linux/sound/soc/qcom/lpass-cpu.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 and
   6 * only version 2 as published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 *
  13 * lpass-cpu.c -- ALSA SoC CPU DAI driver for QTi LPASS
  14 */
  15
  16#include <linux/clk.h>
  17#include <linux/kernel.h>
  18#include <linux/module.h>
  19#include <linux/of.h>
  20#include <linux/of_device.h>
  21#include <linux/platform_device.h>
  22#include <sound/pcm.h>
  23#include <sound/pcm_params.h>
  24#include <linux/regmap.h>
  25#include <sound/soc.h>
  26#include <sound/soc-dai.h>
  27#include "lpass-lpaif-reg.h"
  28#include "lpass.h"
  29
  30static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
  31                unsigned int freq, int dir)
  32{
  33        struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
  34        int ret;
  35
  36        ret = clk_set_rate(drvdata->mi2s_osr_clk[dai->driver->id], freq);
  37        if (ret)
  38                dev_err(dai->dev, "error setting mi2s osrclk to %u: %d\n",
  39                        freq, ret);
  40
  41        return ret;
  42}
  43
  44static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream,
  45                struct snd_soc_dai *dai)
  46{
  47        struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
  48        int ret;
  49
  50        ret = clk_prepare_enable(drvdata->mi2s_osr_clk[dai->driver->id]);
  51        if (ret) {
  52                dev_err(dai->dev, "error in enabling mi2s osr clk: %d\n", ret);
  53                return ret;
  54        }
  55
  56        ret = clk_prepare_enable(drvdata->mi2s_bit_clk[dai->driver->id]);
  57        if (ret) {
  58                dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
  59                clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
  60                return ret;
  61        }
  62
  63        return 0;
  64}
  65
  66static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream,
  67                struct snd_soc_dai *dai)
  68{
  69        struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
  70
  71        clk_disable_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]);
  72
  73        clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
  74}
  75
  76static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
  77                struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
  78{
  79        struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
  80        snd_pcm_format_t format = params_format(params);
  81        unsigned int channels = params_channels(params);
  82        unsigned int rate = params_rate(params);
  83        unsigned int regval;
  84        int bitwidth, ret;
  85
  86        bitwidth = snd_pcm_format_width(format);
  87        if (bitwidth < 0) {
  88                dev_err(dai->dev, "invalid bit width given: %d\n", bitwidth);
  89                return bitwidth;
  90        }
  91
  92        regval = LPAIF_I2SCTL_LOOPBACK_DISABLE |
  93                        LPAIF_I2SCTL_WSSRC_INTERNAL;
  94
  95        switch (bitwidth) {
  96        case 16:
  97                regval |= LPAIF_I2SCTL_BITWIDTH_16;
  98                break;
  99        case 24:
 100                regval |= LPAIF_I2SCTL_BITWIDTH_24;
 101                break;
 102        case 32:
 103                regval |= LPAIF_I2SCTL_BITWIDTH_32;
 104                break;
 105        default:
 106                dev_err(dai->dev, "invalid bitwidth given: %d\n", bitwidth);
 107                return -EINVAL;
 108        }
 109
 110        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 111                switch (channels) {
 112                case 1:
 113                        regval |= LPAIF_I2SCTL_SPKMODE_SD0;
 114                        regval |= LPAIF_I2SCTL_SPKMONO_MONO;
 115                        break;
 116                case 2:
 117                        regval |= LPAIF_I2SCTL_SPKMODE_SD0;
 118                        regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
 119                        break;
 120                case 4:
 121                        regval |= LPAIF_I2SCTL_SPKMODE_QUAD01;
 122                        regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
 123                        break;
 124                case 6:
 125                        regval |= LPAIF_I2SCTL_SPKMODE_6CH;
 126                        regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
 127                        break;
 128                case 8:
 129                        regval |= LPAIF_I2SCTL_SPKMODE_8CH;
 130                        regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
 131                        break;
 132                default:
 133                        dev_err(dai->dev, "invalid channels given: %u\n",
 134                                channels);
 135                        return -EINVAL;
 136                }
 137        } else {
 138                switch (channels) {
 139                case 1:
 140                        regval |= LPAIF_I2SCTL_MICMODE_SD0;
 141                        regval |= LPAIF_I2SCTL_MICMONO_MONO;
 142                        break;
 143                case 2:
 144                        regval |= LPAIF_I2SCTL_MICMODE_SD0;
 145                        regval |= LPAIF_I2SCTL_MICMONO_STEREO;
 146                        break;
 147                case 4:
 148                        regval |= LPAIF_I2SCTL_MICMODE_QUAD01;
 149                        regval |= LPAIF_I2SCTL_MICMONO_STEREO;
 150                        break;
 151                case 6:
 152                        regval |= LPAIF_I2SCTL_MICMODE_6CH;
 153                        regval |= LPAIF_I2SCTL_MICMONO_STEREO;
 154                        break;
 155                case 8:
 156                        regval |= LPAIF_I2SCTL_MICMODE_8CH;
 157                        regval |= LPAIF_I2SCTL_MICMONO_STEREO;
 158                        break;
 159                default:
 160                        dev_err(dai->dev, "invalid channels given: %u\n",
 161                                channels);
 162                        return -EINVAL;
 163                }
 164        }
 165
 166        ret = regmap_write(drvdata->lpaif_map,
 167                           LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
 168                           regval);
 169        if (ret) {
 170                dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret);
 171                return ret;
 172        }
 173
 174        ret = clk_set_rate(drvdata->mi2s_bit_clk[dai->driver->id],
 175                           rate * bitwidth * 2);
 176        if (ret) {
 177                dev_err(dai->dev, "error setting mi2s bitclk to %u: %d\n",
 178                        rate * bitwidth * 2, ret);
 179                return ret;
 180        }
 181
 182        return 0;
 183}
 184
 185static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream,
 186                struct snd_soc_dai *dai)
 187{
 188        struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 189        int ret;
 190
 191        ret = regmap_write(drvdata->lpaif_map,
 192                           LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
 193                           0);
 194        if (ret)
 195                dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret);
 196
 197        return ret;
 198}
 199
 200static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
 201                struct snd_soc_dai *dai)
 202{
 203        struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 204        int ret;
 205        unsigned int val, mask;
 206
 207        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 208                val = LPAIF_I2SCTL_SPKEN_ENABLE;
 209                mask = LPAIF_I2SCTL_SPKEN_MASK;
 210        } else  {
 211                val = LPAIF_I2SCTL_MICEN_ENABLE;
 212                mask = LPAIF_I2SCTL_MICEN_MASK;
 213        }
 214
 215        ret = regmap_update_bits(drvdata->lpaif_map,
 216                        LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
 217                        mask, val);
 218        if (ret)
 219                dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret);
 220
 221        return ret;
 222}
 223
 224static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
 225                int cmd, struct snd_soc_dai *dai)
 226{
 227        struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 228        int ret = -EINVAL;
 229        unsigned int val, mask;
 230
 231        switch (cmd) {
 232        case SNDRV_PCM_TRIGGER_START:
 233        case SNDRV_PCM_TRIGGER_RESUME:
 234        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 235                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 236                        val = LPAIF_I2SCTL_SPKEN_ENABLE;
 237                        mask = LPAIF_I2SCTL_SPKEN_MASK;
 238                } else  {
 239                        val = LPAIF_I2SCTL_MICEN_ENABLE;
 240                        mask = LPAIF_I2SCTL_MICEN_MASK;
 241                }
 242
 243                ret = regmap_update_bits(drvdata->lpaif_map,
 244                                LPAIF_I2SCTL_REG(drvdata->variant,
 245                                                dai->driver->id),
 246                                mask, val);
 247                if (ret)
 248                        dev_err(dai->dev, "error writing to i2sctl reg: %d\n",
 249                                ret);
 250                break;
 251        case SNDRV_PCM_TRIGGER_STOP:
 252        case SNDRV_PCM_TRIGGER_SUSPEND:
 253        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 254                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 255                        val = LPAIF_I2SCTL_SPKEN_DISABLE;
 256                        mask = LPAIF_I2SCTL_SPKEN_MASK;
 257                } else  {
 258                        val = LPAIF_I2SCTL_MICEN_DISABLE;
 259                        mask = LPAIF_I2SCTL_MICEN_MASK;
 260                }
 261
 262                ret = regmap_update_bits(drvdata->lpaif_map,
 263                                LPAIF_I2SCTL_REG(drvdata->variant,
 264                                                dai->driver->id),
 265                                mask, val);
 266                if (ret)
 267                        dev_err(dai->dev, "error writing to i2sctl reg: %d\n",
 268                                ret);
 269                break;
 270        }
 271
 272        return ret;
 273}
 274
 275const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = {
 276        .set_sysclk     = lpass_cpu_daiops_set_sysclk,
 277        .startup        = lpass_cpu_daiops_startup,
 278        .shutdown       = lpass_cpu_daiops_shutdown,
 279        .hw_params      = lpass_cpu_daiops_hw_params,
 280        .hw_free        = lpass_cpu_daiops_hw_free,
 281        .prepare        = lpass_cpu_daiops_prepare,
 282        .trigger        = lpass_cpu_daiops_trigger,
 283};
 284EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops);
 285
 286int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai)
 287{
 288        struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 289        int ret;
 290
 291        /* ensure audio hardware is disabled */
 292        ret = regmap_write(drvdata->lpaif_map,
 293                        LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 0);
 294        if (ret)
 295                dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret);
 296
 297        return ret;
 298}
 299EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe);
 300
 301static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
 302        .name = "lpass-cpu",
 303};
 304
 305static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
 306{
 307        struct lpass_data *drvdata = dev_get_drvdata(dev);
 308        struct lpass_variant *v = drvdata->variant;
 309        int i;
 310
 311        for (i = 0; i < v->i2s_ports; ++i)
 312                if (reg == LPAIF_I2SCTL_REG(v, i))
 313                        return true;
 314
 315        for (i = 0; i < v->irq_ports; ++i) {
 316                if (reg == LPAIF_IRQEN_REG(v, i))
 317                        return true;
 318                if (reg == LPAIF_IRQCLEAR_REG(v, i))
 319                        return true;
 320        }
 321
 322        for (i = 0; i < v->rdma_channels; ++i) {
 323                if (reg == LPAIF_RDMACTL_REG(v, i))
 324                        return true;
 325                if (reg == LPAIF_RDMABASE_REG(v, i))
 326                        return true;
 327                if (reg == LPAIF_RDMABUFF_REG(v, i))
 328                        return true;
 329                if (reg == LPAIF_RDMAPER_REG(v, i))
 330                        return true;
 331        }
 332
 333        for (i = 0; i < v->wrdma_channels; ++i) {
 334                if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start))
 335                        return true;
 336                if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start))
 337                        return true;
 338                if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start))
 339                        return true;
 340                if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start))
 341                        return true;
 342        }
 343
 344        return false;
 345}
 346
 347static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
 348{
 349        struct lpass_data *drvdata = dev_get_drvdata(dev);
 350        struct lpass_variant *v = drvdata->variant;
 351        int i;
 352
 353        for (i = 0; i < v->i2s_ports; ++i)
 354                if (reg == LPAIF_I2SCTL_REG(v, i))
 355                        return true;
 356
 357        for (i = 0; i < v->irq_ports; ++i) {
 358                if (reg == LPAIF_IRQEN_REG(v, i))
 359                        return true;
 360                if (reg == LPAIF_IRQSTAT_REG(v, i))
 361                        return true;
 362        }
 363
 364        for (i = 0; i < v->rdma_channels; ++i) {
 365                if (reg == LPAIF_RDMACTL_REG(v, i))
 366                        return true;
 367                if (reg == LPAIF_RDMABASE_REG(v, i))
 368                        return true;
 369                if (reg == LPAIF_RDMABUFF_REG(v, i))
 370                        return true;
 371                if (reg == LPAIF_RDMACURR_REG(v, i))
 372                        return true;
 373                if (reg == LPAIF_RDMAPER_REG(v, i))
 374                        return true;
 375        }
 376
 377        for (i = 0; i < v->wrdma_channels; ++i) {
 378                if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start))
 379                        return true;
 380                if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start))
 381                        return true;
 382                if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start))
 383                        return true;
 384                if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start))
 385                        return true;
 386                if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start))
 387                        return true;
 388        }
 389
 390        return false;
 391}
 392
 393static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
 394{
 395        struct lpass_data *drvdata = dev_get_drvdata(dev);
 396        struct lpass_variant *v = drvdata->variant;
 397        int i;
 398
 399        for (i = 0; i < v->irq_ports; ++i)
 400                if (reg == LPAIF_IRQSTAT_REG(v, i))
 401                        return true;
 402
 403        for (i = 0; i < v->rdma_channels; ++i)
 404                if (reg == LPAIF_RDMACURR_REG(v, i))
 405                        return true;
 406
 407        for (i = 0; i < v->wrdma_channels; ++i)
 408                if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start))
 409                        return true;
 410
 411        return false;
 412}
 413
 414static struct regmap_config lpass_cpu_regmap_config = {
 415        .reg_bits = 32,
 416        .reg_stride = 4,
 417        .val_bits = 32,
 418        .writeable_reg = lpass_cpu_regmap_writeable,
 419        .readable_reg = lpass_cpu_regmap_readable,
 420        .volatile_reg = lpass_cpu_regmap_volatile,
 421        .cache_type = REGCACHE_FLAT,
 422};
 423
 424int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 425{
 426        struct lpass_data *drvdata;
 427        struct device_node *dsp_of_node;
 428        struct resource *res;
 429        struct lpass_variant *variant;
 430        struct device *dev = &pdev->dev;
 431        const struct of_device_id *match;
 432        int ret, i, dai_id;
 433
 434        dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);
 435        if (dsp_of_node) {
 436                dev_err(&pdev->dev, "DSP exists and holds audio resources\n");
 437                return -EBUSY;
 438        }
 439
 440        drvdata = devm_kzalloc(&pdev->dev, sizeof(struct lpass_data),
 441                        GFP_KERNEL);
 442        if (!drvdata)
 443                return -ENOMEM;
 444        platform_set_drvdata(pdev, drvdata);
 445
 446        match = of_match_device(dev->driver->of_match_table, dev);
 447        if (!match || !match->data)
 448                return -EINVAL;
 449
 450        drvdata->variant = (struct lpass_variant *)match->data;
 451        variant = drvdata->variant;
 452
 453        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif");
 454
 455        drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res);
 456        if (IS_ERR((void const __force *)drvdata->lpaif)) {
 457                dev_err(&pdev->dev, "error mapping reg resource: %ld\n",
 458                                PTR_ERR((void const __force *)drvdata->lpaif));
 459                return PTR_ERR((void const __force *)drvdata->lpaif);
 460        }
 461
 462        lpass_cpu_regmap_config.max_register = LPAIF_WRDMAPER_REG(variant,
 463                                                variant->wrdma_channels +
 464                                                variant->wrdma_channel_start);
 465
 466        drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,
 467                        &lpass_cpu_regmap_config);
 468        if (IS_ERR(drvdata->lpaif_map)) {
 469                dev_err(&pdev->dev, "error initializing regmap: %ld\n",
 470                        PTR_ERR(drvdata->lpaif_map));
 471                return PTR_ERR(drvdata->lpaif_map);
 472        }
 473
 474        if (variant->init)
 475                variant->init(pdev);
 476
 477        for (i = 0; i < variant->num_dai; i++) {
 478                dai_id = variant->dai_driver[i].id;
 479                drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(&pdev->dev,
 480                                             variant->dai_osr_clk_names[i]);
 481                if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) {
 482                        dev_warn(&pdev->dev,
 483                                "%s() error getting optional %s: %ld\n",
 484                                __func__,
 485                                variant->dai_osr_clk_names[i],
 486                                PTR_ERR(drvdata->mi2s_osr_clk[dai_id]));
 487
 488                        drvdata->mi2s_osr_clk[dai_id] = NULL;
 489                }
 490
 491                drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(&pdev->dev,
 492                                                variant->dai_bit_clk_names[i]);
 493                if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {
 494                        dev_err(&pdev->dev,
 495                                "error getting %s: %ld\n",
 496                                variant->dai_bit_clk_names[i],
 497                                PTR_ERR(drvdata->mi2s_bit_clk[dai_id]));
 498                        return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
 499                }
 500        }
 501
 502        drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk");
 503        if (IS_ERR(drvdata->ahbix_clk)) {
 504                dev_err(&pdev->dev, "error getting ahbix-clk: %ld\n",
 505                        PTR_ERR(drvdata->ahbix_clk));
 506                return PTR_ERR(drvdata->ahbix_clk);
 507        }
 508
 509        ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY);
 510        if (ret) {
 511                dev_err(&pdev->dev, "error setting rate on ahbix_clk: %d\n",
 512                        ret);
 513                return ret;
 514        }
 515        dev_dbg(&pdev->dev, "set ahbix_clk rate to %lu\n",
 516                clk_get_rate(drvdata->ahbix_clk));
 517
 518        ret = clk_prepare_enable(drvdata->ahbix_clk);
 519        if (ret) {
 520                dev_err(&pdev->dev, "error enabling ahbix_clk: %d\n", ret);
 521                return ret;
 522        }
 523
 524        ret = devm_snd_soc_register_component(&pdev->dev,
 525                                              &lpass_cpu_comp_driver,
 526                                              variant->dai_driver,
 527                                              variant->num_dai);
 528        if (ret) {
 529                dev_err(&pdev->dev, "error registering cpu driver: %d\n", ret);
 530                goto err_clk;
 531        }
 532
 533        ret = asoc_qcom_lpass_platform_register(pdev);
 534        if (ret) {
 535                dev_err(&pdev->dev, "error registering platform driver: %d\n",
 536                        ret);
 537                goto err_clk;
 538        }
 539
 540        return 0;
 541
 542err_clk:
 543        clk_disable_unprepare(drvdata->ahbix_clk);
 544        return ret;
 545}
 546EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_probe);
 547
 548int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev)
 549{
 550        struct lpass_data *drvdata = platform_get_drvdata(pdev);
 551
 552        if (drvdata->variant->exit)
 553                drvdata->variant->exit(pdev);
 554
 555        clk_disable_unprepare(drvdata->ahbix_clk);
 556
 557        return 0;
 558}
 559EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove);
 560
 561MODULE_DESCRIPTION("QTi LPASS CPU Driver");
 562MODULE_LICENSE("GPL v2");
 563