linux/sound/soc/omap/omap-dmic.c
<<
>>
Prefs
   1/*
   2 * omap-dmic.c  --  OMAP ASoC DMIC DAI driver
   3 *
   4 * Copyright (C) 2010 - 2011 Texas Instruments
   5 *
   6 * Author: David Lambert <dlambert@ti.com>
   7 *         Misael Lopez Cruz <misael.lopez@ti.com>
   8 *         Liam Girdwood <lrg@ti.com>
   9 *         Peter Ujfalusi <peter.ujfalusi@ti.com>
  10 *
  11 * This program is free software; you can redistribute it and/or
  12 * modify it under the terms of the GNU General Public License
  13 * version 2 as published by the Free Software Foundation.
  14 *
  15 * This program is distributed in the hope that it will be useful, but
  16 * WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18 * General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License
  21 * along with this program; if not, write to the Free Software
  22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  23 * 02110-1301 USA
  24 *
  25 */
  26
  27#include <linux/init.h>
  28#include <linux/module.h>
  29#include <linux/platform_device.h>
  30#include <linux/err.h>
  31#include <linux/clk.h>
  32#include <linux/io.h>
  33#include <linux/slab.h>
  34#include <linux/pm_runtime.h>
  35#include <linux/of_device.h>
  36
  37#include <sound/core.h>
  38#include <sound/pcm.h>
  39#include <sound/pcm_params.h>
  40#include <sound/initval.h>
  41#include <sound/soc.h>
  42#include <sound/dmaengine_pcm.h>
  43
  44#include "omap-dmic.h"
  45
  46struct omap_dmic {
  47        struct device *dev;
  48        void __iomem *io_base;
  49        struct clk *fclk;
  50        int fclk_freq;
  51        int out_freq;
  52        int clk_div;
  53        int sysclk;
  54        int threshold;
  55        u32 ch_enabled;
  56        bool active;
  57        struct mutex mutex;
  58
  59        struct snd_dmaengine_dai_dma_data dma_data;
  60        unsigned int dma_req;
  61};
  62
  63static inline void omap_dmic_write(struct omap_dmic *dmic, u16 reg, u32 val)
  64{
  65        __raw_writel(val, dmic->io_base + reg);
  66}
  67
  68static inline int omap_dmic_read(struct omap_dmic *dmic, u16 reg)
  69{
  70        return __raw_readl(dmic->io_base + reg);
  71}
  72
  73static inline void omap_dmic_start(struct omap_dmic *dmic)
  74{
  75        u32 ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG);
  76
  77        /* Configure DMA controller */
  78        omap_dmic_write(dmic, OMAP_DMIC_DMAENABLE_SET_REG,
  79                        OMAP_DMIC_DMA_ENABLE);
  80
  81        omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, ctrl | dmic->ch_enabled);
  82}
  83
  84static inline void omap_dmic_stop(struct omap_dmic *dmic)
  85{
  86        u32 ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG);
  87        omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG,
  88                        ctrl & ~OMAP_DMIC_UP_ENABLE_MASK);
  89
  90        /* Disable DMA request generation */
  91        omap_dmic_write(dmic, OMAP_DMIC_DMAENABLE_CLR_REG,
  92                        OMAP_DMIC_DMA_ENABLE);
  93
  94}
  95
  96static inline int dmic_is_enabled(struct omap_dmic *dmic)
  97{
  98        return omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG) &
  99                                                OMAP_DMIC_UP_ENABLE_MASK;
 100}
 101
 102static int omap_dmic_dai_startup(struct snd_pcm_substream *substream,
 103                                  struct snd_soc_dai *dai)
 104{
 105        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
 106        int ret = 0;
 107
 108        mutex_lock(&dmic->mutex);
 109
 110        if (!dai->active)
 111                dmic->active = 1;
 112        else
 113                ret = -EBUSY;
 114
 115        mutex_unlock(&dmic->mutex);
 116
 117        snd_soc_dai_set_dma_data(dai, substream, &dmic->dma_data);
 118        return ret;
 119}
 120
 121static void omap_dmic_dai_shutdown(struct snd_pcm_substream *substream,
 122                                    struct snd_soc_dai *dai)
 123{
 124        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
 125
 126        mutex_lock(&dmic->mutex);
 127
 128        if (!dai->active)
 129                dmic->active = 0;
 130
 131        mutex_unlock(&dmic->mutex);
 132}
 133
 134static int omap_dmic_select_divider(struct omap_dmic *dmic, int sample_rate)
 135{
 136        int divider = -EINVAL;
 137
 138        /*
 139         * 192KHz rate is only supported with 19.2MHz/3.84MHz clock
 140         * configuration.
 141         */
 142        if (sample_rate == 192000) {
 143                if (dmic->fclk_freq == 19200000 && dmic->out_freq == 3840000)
 144                        divider = 0x6; /* Divider: 5 (192KHz sampling rate) */
 145                else
 146                        dev_err(dmic->dev,
 147                                "invalid clock configuration for 192KHz\n");
 148
 149                return divider;
 150        }
 151
 152        switch (dmic->out_freq) {
 153        case 1536000:
 154                if (dmic->fclk_freq != 24576000)
 155                        goto div_err;
 156                divider = 0x4; /* Divider: 16 */
 157                break;
 158        case 2400000:
 159                switch (dmic->fclk_freq) {
 160                case 12000000:
 161                        divider = 0x5; /* Divider: 5 */
 162                        break;
 163                case 19200000:
 164                        divider = 0x0; /* Divider: 8 */
 165                        break;
 166                case 24000000:
 167                        divider = 0x2; /* Divider: 10 */
 168                        break;
 169                default:
 170                        goto div_err;
 171                }
 172                break;
 173        case 3072000:
 174                if (dmic->fclk_freq != 24576000)
 175                        goto div_err;
 176                divider = 0x3; /* Divider: 8 */
 177                break;
 178        case 3840000:
 179                if (dmic->fclk_freq != 19200000)
 180                        goto div_err;
 181                divider = 0x1; /* Divider: 5 (96KHz sampling rate) */
 182                break;
 183        default:
 184                dev_err(dmic->dev, "invalid out frequency: %dHz\n",
 185                        dmic->out_freq);
 186                break;
 187        }
 188
 189        return divider;
 190
 191div_err:
 192        dev_err(dmic->dev, "invalid out frequency %dHz for %dHz input\n",
 193                dmic->out_freq, dmic->fclk_freq);
 194        return -EINVAL;
 195}
 196
 197static int omap_dmic_dai_hw_params(struct snd_pcm_substream *substream,
 198                                    struct snd_pcm_hw_params *params,
 199                                    struct snd_soc_dai *dai)
 200{
 201        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
 202        struct snd_dmaengine_dai_dma_data *dma_data;
 203        int channels;
 204
 205        dmic->clk_div = omap_dmic_select_divider(dmic, params_rate(params));
 206        if (dmic->clk_div < 0) {
 207                dev_err(dmic->dev, "no valid divider for %dHz from %dHz\n",
 208                        dmic->out_freq, dmic->fclk_freq);
 209                return -EINVAL;
 210        }
 211
 212        dmic->ch_enabled = 0;
 213        channels = params_channels(params);
 214        switch (channels) {
 215        case 6:
 216                dmic->ch_enabled |= OMAP_DMIC_UP3_ENABLE;
 217        case 4:
 218                dmic->ch_enabled |= OMAP_DMIC_UP2_ENABLE;
 219        case 2:
 220                dmic->ch_enabled |= OMAP_DMIC_UP1_ENABLE;
 221                break;
 222        default:
 223                dev_err(dmic->dev, "invalid number of legacy channels\n");
 224                return -EINVAL;
 225        }
 226
 227        /* packet size is threshold * channels */
 228        dma_data = snd_soc_dai_get_dma_data(dai, substream);
 229        dma_data->maxburst = dmic->threshold * channels;
 230
 231        return 0;
 232}
 233
 234static int omap_dmic_dai_prepare(struct snd_pcm_substream *substream,
 235                                  struct snd_soc_dai *dai)
 236{
 237        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
 238        u32 ctrl;
 239
 240        /* Configure uplink threshold */
 241        omap_dmic_write(dmic, OMAP_DMIC_FIFO_CTRL_REG, dmic->threshold);
 242
 243        ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG);
 244
 245        /* Set dmic out format */
 246        ctrl &= ~(OMAP_DMIC_FORMAT | OMAP_DMIC_POLAR_MASK);
 247        ctrl |= (OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 |
 248                 OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3);
 249
 250        /* Configure dmic clock divider */
 251        ctrl &= ~OMAP_DMIC_CLK_DIV_MASK;
 252        ctrl |= OMAP_DMIC_CLK_DIV(dmic->clk_div);
 253
 254        omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, ctrl);
 255
 256        omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG,
 257                        ctrl | OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 |
 258                        OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3);
 259
 260        return 0;
 261}
 262
 263static int omap_dmic_dai_trigger(struct snd_pcm_substream *substream,
 264                                  int cmd, struct snd_soc_dai *dai)
 265{
 266        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
 267
 268        switch (cmd) {
 269        case SNDRV_PCM_TRIGGER_START:
 270                omap_dmic_start(dmic);
 271                break;
 272        case SNDRV_PCM_TRIGGER_STOP:
 273                omap_dmic_stop(dmic);
 274                break;
 275        default:
 276                break;
 277        }
 278
 279        return 0;
 280}
 281
 282static int omap_dmic_select_fclk(struct omap_dmic *dmic, int clk_id,
 283                                 unsigned int freq)
 284{
 285        struct clk *parent_clk;
 286        char *parent_clk_name;
 287        int ret = 0;
 288
 289        switch (freq) {
 290        case 12000000:
 291        case 19200000:
 292        case 24000000:
 293        case 24576000:
 294                break;
 295        default:
 296                dev_err(dmic->dev, "invalid input frequency: %dHz\n", freq);
 297                dmic->fclk_freq = 0;
 298                return -EINVAL;
 299        }
 300
 301        if (dmic->sysclk == clk_id) {
 302                dmic->fclk_freq = freq;
 303                return 0;
 304        }
 305
 306        /* re-parent not allowed if a stream is ongoing */
 307        if (dmic->active && dmic_is_enabled(dmic)) {
 308                dev_err(dmic->dev, "can't re-parent when DMIC active\n");
 309                return -EBUSY;
 310        }
 311
 312        switch (clk_id) {
 313        case OMAP_DMIC_SYSCLK_PAD_CLKS:
 314                parent_clk_name = "pad_clks_ck";
 315                break;
 316        case OMAP_DMIC_SYSCLK_SLIMBLUS_CLKS:
 317                parent_clk_name = "slimbus_clk";
 318                break;
 319        case OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS:
 320                parent_clk_name = "dmic_sync_mux_ck";
 321                break;
 322        default:
 323                dev_err(dmic->dev, "fclk clk_id (%d) not supported\n", clk_id);
 324                return -EINVAL;
 325        }
 326
 327        parent_clk = clk_get(dmic->dev, parent_clk_name);
 328        if (IS_ERR(parent_clk)) {
 329                dev_err(dmic->dev, "can't get %s\n", parent_clk_name);
 330                return -ENODEV;
 331        }
 332
 333        mutex_lock(&dmic->mutex);
 334        if (dmic->active) {
 335                /* disable clock while reparenting */
 336                pm_runtime_put_sync(dmic->dev);
 337                ret = clk_set_parent(dmic->fclk, parent_clk);
 338                pm_runtime_get_sync(dmic->dev);
 339        } else {
 340                ret = clk_set_parent(dmic->fclk, parent_clk);
 341        }
 342        mutex_unlock(&dmic->mutex);
 343
 344        if (ret < 0) {
 345                dev_err(dmic->dev, "re-parent failed\n");
 346                goto err_busy;
 347        }
 348
 349        dmic->sysclk = clk_id;
 350        dmic->fclk_freq = freq;
 351
 352err_busy:
 353        clk_put(parent_clk);
 354
 355        return ret;
 356}
 357
 358static int omap_dmic_select_outclk(struct omap_dmic *dmic, int clk_id,
 359                                    unsigned int freq)
 360{
 361        int ret = 0;
 362
 363        if (clk_id != OMAP_DMIC_ABE_DMIC_CLK) {
 364                dev_err(dmic->dev, "output clk_id (%d) not supported\n",
 365                        clk_id);
 366                return -EINVAL;
 367        }
 368
 369        switch (freq) {
 370        case 1536000:
 371        case 2400000:
 372        case 3072000:
 373        case 3840000:
 374                dmic->out_freq = freq;
 375                break;
 376        default:
 377                dev_err(dmic->dev, "invalid out frequency: %dHz\n", freq);
 378                dmic->out_freq = 0;
 379                ret = -EINVAL;
 380        }
 381
 382        return ret;
 383}
 384
 385static int omap_dmic_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
 386                                    unsigned int freq, int dir)
 387{
 388        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
 389
 390        if (dir == SND_SOC_CLOCK_IN)
 391                return omap_dmic_select_fclk(dmic, clk_id, freq);
 392        else if (dir == SND_SOC_CLOCK_OUT)
 393                return omap_dmic_select_outclk(dmic, clk_id, freq);
 394
 395        dev_err(dmic->dev, "invalid clock direction (%d)\n", dir);
 396        return -EINVAL;
 397}
 398
 399static const struct snd_soc_dai_ops omap_dmic_dai_ops = {
 400        .startup        = omap_dmic_dai_startup,
 401        .shutdown       = omap_dmic_dai_shutdown,
 402        .hw_params      = omap_dmic_dai_hw_params,
 403        .prepare        = omap_dmic_dai_prepare,
 404        .trigger        = omap_dmic_dai_trigger,
 405        .set_sysclk     = omap_dmic_set_dai_sysclk,
 406};
 407
 408static int omap_dmic_probe(struct snd_soc_dai *dai)
 409{
 410        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
 411
 412        pm_runtime_enable(dmic->dev);
 413
 414        /* Disable lines while request is ongoing */
 415        pm_runtime_get_sync(dmic->dev);
 416        omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, 0x00);
 417        pm_runtime_put_sync(dmic->dev);
 418
 419        /* Configure DMIC threshold value */
 420        dmic->threshold = OMAP_DMIC_THRES_MAX - 3;
 421        return 0;
 422}
 423
 424static int omap_dmic_remove(struct snd_soc_dai *dai)
 425{
 426        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
 427
 428        pm_runtime_disable(dmic->dev);
 429
 430        return 0;
 431}
 432
 433static struct snd_soc_dai_driver omap_dmic_dai = {
 434        .name = "omap-dmic",
 435        .probe = omap_dmic_probe,
 436        .remove = omap_dmic_remove,
 437        .capture = {
 438                .channels_min = 2,
 439                .channels_max = 6,
 440                .rates = SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
 441                .formats = SNDRV_PCM_FMTBIT_S32_LE,
 442                .sig_bits = 24,
 443        },
 444        .ops = &omap_dmic_dai_ops,
 445};
 446
 447static const struct snd_soc_component_driver omap_dmic_component = {
 448        .name           = "omap-dmic",
 449};
 450
 451static int asoc_dmic_probe(struct platform_device *pdev)
 452{
 453        struct omap_dmic *dmic;
 454        struct resource *res;
 455        int ret;
 456
 457        dmic = devm_kzalloc(&pdev->dev, sizeof(struct omap_dmic), GFP_KERNEL);
 458        if (!dmic)
 459                return -ENOMEM;
 460
 461        platform_set_drvdata(pdev, dmic);
 462        dmic->dev = &pdev->dev;
 463        dmic->sysclk = OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS;
 464
 465        mutex_init(&dmic->mutex);
 466
 467        dmic->fclk = clk_get(dmic->dev, "fck");
 468        if (IS_ERR(dmic->fclk)) {
 469                dev_err(dmic->dev, "cant get fck\n");
 470                return -ENODEV;
 471        }
 472
 473        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
 474        if (!res) {
 475                dev_err(dmic->dev, "invalid dma memory resource\n");
 476                ret = -ENODEV;
 477                goto err_put_clk;
 478        }
 479        dmic->dma_data.addr = res->start + OMAP_DMIC_DATA_REG;
 480
 481        res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
 482        if (!res) {
 483                dev_err(dmic->dev, "invalid dma resource\n");
 484                ret = -ENODEV;
 485                goto err_put_clk;
 486        }
 487
 488        dmic->dma_req = res->start;
 489        dmic->dma_data.filter_data = &dmic->dma_req;
 490
 491        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
 492        if (!res) {
 493                dev_err(dmic->dev, "invalid memory resource\n");
 494                ret = -ENODEV;
 495                goto err_put_clk;
 496        }
 497
 498        dmic->io_base = devm_ioremap_resource(&pdev->dev, res);
 499        if (IS_ERR(dmic->io_base))
 500                return PTR_ERR(dmic->io_base);
 501
 502        ret = snd_soc_register_component(&pdev->dev, &omap_dmic_component,
 503                                         &omap_dmic_dai, 1);
 504        if (ret)
 505                goto err_put_clk;
 506
 507        return 0;
 508
 509err_put_clk:
 510        clk_put(dmic->fclk);
 511        return ret;
 512}
 513
 514static int asoc_dmic_remove(struct platform_device *pdev)
 515{
 516        struct omap_dmic *dmic = platform_get_drvdata(pdev);
 517
 518        snd_soc_unregister_component(&pdev->dev);
 519        clk_put(dmic->fclk);
 520
 521        return 0;
 522}
 523
 524static const struct of_device_id omap_dmic_of_match[] = {
 525        { .compatible = "ti,omap4-dmic", },
 526        { }
 527};
 528MODULE_DEVICE_TABLE(of, omap_dmic_of_match);
 529
 530static struct platform_driver asoc_dmic_driver = {
 531        .driver = {
 532                .name = "omap-dmic",
 533                .owner = THIS_MODULE,
 534                .of_match_table = omap_dmic_of_match,
 535        },
 536        .probe = asoc_dmic_probe,
 537        .remove = asoc_dmic_remove,
 538};
 539
 540module_platform_driver(asoc_dmic_driver);
 541
 542MODULE_ALIAS("platform:omap-dmic");
 543MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
 544MODULE_DESCRIPTION("OMAP DMIC ASoC Interface");
 545MODULE_LICENSE("GPL");
 546