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