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#include <sound/omap-pcm.h>
  44
  45#include "omap-dmic.h"
  46
  47struct omap_dmic {
  48        struct device *dev;
  49        void __iomem *io_base;
  50        struct clk *fclk;
  51        int fclk_freq;
  52        int out_freq;
  53        int clk_div;
  54        int sysclk;
  55        int threshold;
  56        u32 ch_enabled;
  57        bool active;
  58        struct mutex mutex;
  59
  60        struct snd_dmaengine_dai_dma_data dma_data;
  61};
  62
  63static inline void omap_dmic_write(struct omap_dmic *dmic, u16 reg, u32 val)
  64{
  65        writel_relaxed(val, dmic->io_base + reg);
  66}
  67
  68static inline int omap_dmic_read(struct omap_dmic *dmic, u16 reg)
  69{
  70        return readl_relaxed(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        return ret;
 118}
 119
 120static void omap_dmic_dai_shutdown(struct snd_pcm_substream *substream,
 121                                    struct snd_soc_dai *dai)
 122{
 123        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
 124
 125        mutex_lock(&dmic->mutex);
 126
 127        if (!dai->active)
 128                dmic->active = 0;
 129
 130        mutex_unlock(&dmic->mutex);
 131}
 132
 133static int omap_dmic_select_divider(struct omap_dmic *dmic, int sample_rate)
 134{
 135        int divider = -EINVAL;
 136
 137        /*
 138         * 192KHz rate is only supported with 19.2MHz/3.84MHz clock
 139         * configuration.
 140         */
 141        if (sample_rate == 192000) {
 142                if (dmic->fclk_freq == 19200000 && dmic->out_freq == 3840000)
 143                        divider = 0x6; /* Divider: 5 (192KHz sampling rate) */
 144                else
 145                        dev_err(dmic->dev,
 146                                "invalid clock configuration for 192KHz\n");
 147
 148                return divider;
 149        }
 150
 151        switch (dmic->out_freq) {
 152        case 1536000:
 153                if (dmic->fclk_freq != 24576000)
 154                        goto div_err;
 155                divider = 0x4; /* Divider: 16 */
 156                break;
 157        case 2400000:
 158                switch (dmic->fclk_freq) {
 159                case 12000000:
 160                        divider = 0x5; /* Divider: 5 */
 161                        break;
 162                case 19200000:
 163                        divider = 0x0; /* Divider: 8 */
 164                        break;
 165                case 24000000:
 166                        divider = 0x2; /* Divider: 10 */
 167                        break;
 168                default:
 169                        goto div_err;
 170                }
 171                break;
 172        case 3072000:
 173                if (dmic->fclk_freq != 24576000)
 174                        goto div_err;
 175                divider = 0x3; /* Divider: 8 */
 176                break;
 177        case 3840000:
 178                if (dmic->fclk_freq != 19200000)
 179                        goto div_err;
 180                divider = 0x1; /* Divider: 5 (96KHz sampling rate) */
 181                break;
 182        default:
 183                dev_err(dmic->dev, "invalid out frequency: %dHz\n",
 184                        dmic->out_freq);
 185                break;
 186        }
 187
 188        return divider;
 189
 190div_err:
 191        dev_err(dmic->dev, "invalid out frequency %dHz for %dHz input\n",
 192                dmic->out_freq, dmic->fclk_freq);
 193        return -EINVAL;
 194}
 195
 196static int omap_dmic_dai_hw_params(struct snd_pcm_substream *substream,
 197                                    struct snd_pcm_hw_params *params,
 198                                    struct snd_soc_dai *dai)
 199{
 200        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
 201        struct snd_dmaengine_dai_dma_data *dma_data;
 202        int channels;
 203
 204        dmic->clk_div = omap_dmic_select_divider(dmic, params_rate(params));
 205        if (dmic->clk_div < 0) {
 206                dev_err(dmic->dev, "no valid divider for %dHz from %dHz\n",
 207                        dmic->out_freq, dmic->fclk_freq);
 208                return -EINVAL;
 209        }
 210
 211        dmic->ch_enabled = 0;
 212        channels = params_channels(params);
 213        switch (channels) {
 214        case 6:
 215                dmic->ch_enabled |= OMAP_DMIC_UP3_ENABLE;
 216        case 4:
 217                dmic->ch_enabled |= OMAP_DMIC_UP2_ENABLE;
 218        case 2:
 219                dmic->ch_enabled |= OMAP_DMIC_UP1_ENABLE;
 220                break;
 221        default:
 222                dev_err(dmic->dev, "invalid number of legacy channels\n");
 223                return -EINVAL;
 224        }
 225
 226        /* packet size is threshold * channels */
 227        dma_data = snd_soc_dai_get_dma_data(dai, substream);
 228        dma_data->maxburst = dmic->threshold * channels;
 229
 230        return 0;
 231}
 232
 233static int omap_dmic_dai_prepare(struct snd_pcm_substream *substream,
 234                                  struct snd_soc_dai *dai)
 235{
 236        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
 237        u32 ctrl;
 238
 239        /* Configure uplink threshold */
 240        omap_dmic_write(dmic, OMAP_DMIC_FIFO_CTRL_REG, dmic->threshold);
 241
 242        ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG);
 243
 244        /* Set dmic out format */
 245        ctrl &= ~(OMAP_DMIC_FORMAT | OMAP_DMIC_POLAR_MASK);
 246        ctrl |= (OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 |
 247                 OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3);
 248
 249        /* Configure dmic clock divider */
 250        ctrl &= ~OMAP_DMIC_CLK_DIV_MASK;
 251        ctrl |= OMAP_DMIC_CLK_DIV(dmic->clk_div);
 252
 253        omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, ctrl);
 254
 255        omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG,
 256                        ctrl | OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 |
 257                        OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3);
 258
 259        return 0;
 260}
 261
 262static int omap_dmic_dai_trigger(struct snd_pcm_substream *substream,
 263                                  int cmd, struct snd_soc_dai *dai)
 264{
 265        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
 266
 267        switch (cmd) {
 268        case SNDRV_PCM_TRIGGER_START:
 269                omap_dmic_start(dmic);
 270                break;
 271        case SNDRV_PCM_TRIGGER_STOP:
 272                omap_dmic_stop(dmic);
 273                break;
 274        default:
 275                break;
 276        }
 277
 278        return 0;
 279}
 280
 281static int omap_dmic_select_fclk(struct omap_dmic *dmic, int clk_id,
 282                                 unsigned int freq)
 283{
 284        struct clk *parent_clk;
 285        char *parent_clk_name;
 286        int ret = 0;
 287
 288        switch (freq) {
 289        case 12000000:
 290        case 19200000:
 291        case 24000000:
 292        case 24576000:
 293                break;
 294        default:
 295                dev_err(dmic->dev, "invalid input frequency: %dHz\n", freq);
 296                dmic->fclk_freq = 0;
 297                return -EINVAL;
 298        }
 299
 300        if (dmic->sysclk == clk_id) {
 301                dmic->fclk_freq = freq;
 302                return 0;
 303        }
 304
 305        /* re-parent not allowed if a stream is ongoing */
 306        if (dmic->active && dmic_is_enabled(dmic)) {
 307                dev_err(dmic->dev, "can't re-parent when DMIC active\n");
 308                return -EBUSY;
 309        }
 310
 311        switch (clk_id) {
 312        case OMAP_DMIC_SYSCLK_PAD_CLKS:
 313                parent_clk_name = "pad_clks_ck";
 314                break;
 315        case OMAP_DMIC_SYSCLK_SLIMBLUS_CLKS:
 316                parent_clk_name = "slimbus_clk";
 317                break;
 318        case OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS:
 319                parent_clk_name = "dmic_sync_mux_ck";
 320                break;
 321        default:
 322                dev_err(dmic->dev, "fclk clk_id (%d) not supported\n", clk_id);
 323                return -EINVAL;
 324        }
 325
 326        parent_clk = clk_get(dmic->dev, parent_clk_name);
 327        if (IS_ERR(parent_clk)) {
 328                dev_err(dmic->dev, "can't get %s\n", parent_clk_name);
 329                return -ENODEV;
 330        }
 331
 332        mutex_lock(&dmic->mutex);
 333        if (dmic->active) {
 334                /* disable clock while reparenting */
 335                pm_runtime_put_sync(dmic->dev);
 336                ret = clk_set_parent(dmic->fclk, parent_clk);
 337                pm_runtime_get_sync(dmic->dev);
 338        } else {
 339                ret = clk_set_parent(dmic->fclk, parent_clk);
 340        }
 341        mutex_unlock(&dmic->mutex);
 342
 343        if (ret < 0) {
 344                dev_err(dmic->dev, "re-parent failed\n");
 345                goto err_busy;
 346        }
 347
 348        dmic->sysclk = clk_id;
 349        dmic->fclk_freq = freq;
 350
 351err_busy:
 352        clk_put(parent_clk);
 353
 354        return ret;
 355}
 356
 357static int omap_dmic_select_outclk(struct omap_dmic *dmic, int clk_id,
 358                                    unsigned int freq)
 359{
 360        int ret = 0;
 361
 362        if (clk_id != OMAP_DMIC_ABE_DMIC_CLK) {
 363                dev_err(dmic->dev, "output clk_id (%d) not supported\n",
 364                        clk_id);
 365                return -EINVAL;
 366        }
 367
 368        switch (freq) {
 369        case 1536000:
 370        case 2400000:
 371        case 3072000:
 372        case 3840000:
 373                dmic->out_freq = freq;
 374                break;
 375        default:
 376                dev_err(dmic->dev, "invalid out frequency: %dHz\n", freq);
 377                dmic->out_freq = 0;
 378                ret = -EINVAL;
 379        }
 380
 381        return ret;
 382}
 383
 384static int omap_dmic_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
 385                                    unsigned int freq, int dir)
 386{
 387        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
 388
 389        if (dir == SND_SOC_CLOCK_IN)
 390                return omap_dmic_select_fclk(dmic, clk_id, freq);
 391        else if (dir == SND_SOC_CLOCK_OUT)
 392                return omap_dmic_select_outclk(dmic, clk_id, freq);
 393
 394        dev_err(dmic->dev, "invalid clock direction (%d)\n", dir);
 395        return -EINVAL;
 396}
 397
 398static const struct snd_soc_dai_ops omap_dmic_dai_ops = {
 399        .startup        = omap_dmic_dai_startup,
 400        .shutdown       = omap_dmic_dai_shutdown,
 401        .hw_params      = omap_dmic_dai_hw_params,
 402        .prepare        = omap_dmic_dai_prepare,
 403        .trigger        = omap_dmic_dai_trigger,
 404        .set_sysclk     = omap_dmic_set_dai_sysclk,
 405};
 406
 407static int omap_dmic_probe(struct snd_soc_dai *dai)
 408{
 409        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
 410
 411        pm_runtime_enable(dmic->dev);
 412
 413        /* Disable lines while request is ongoing */
 414        pm_runtime_get_sync(dmic->dev);
 415        omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, 0x00);
 416        pm_runtime_put_sync(dmic->dev);
 417
 418        /* Configure DMIC threshold value */
 419        dmic->threshold = OMAP_DMIC_THRES_MAX - 3;
 420
 421        snd_soc_dai_init_dma_data(dai, NULL, &dmic->dma_data);
 422
 423        return 0;
 424}
 425
 426static int omap_dmic_remove(struct snd_soc_dai *dai)
 427{
 428        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
 429
 430        pm_runtime_disable(dmic->dev);
 431
 432        return 0;
 433}
 434
 435static struct snd_soc_dai_driver omap_dmic_dai = {
 436        .name = "omap-dmic",
 437        .probe = omap_dmic_probe,
 438        .remove = omap_dmic_remove,
 439        .capture = {
 440                .channels_min = 2,
 441                .channels_max = 6,
 442                .rates = SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
 443                .formats = SNDRV_PCM_FMTBIT_S32_LE,
 444                .sig_bits = 24,
 445        },
 446        .ops = &omap_dmic_dai_ops,
 447};
 448
 449static const struct snd_soc_component_driver omap_dmic_component = {
 450        .name           = "omap-dmic",
 451};
 452
 453static int asoc_dmic_probe(struct platform_device *pdev)
 454{
 455        struct omap_dmic *dmic;
 456        struct resource *res;
 457        int ret;
 458
 459        dmic = devm_kzalloc(&pdev->dev, sizeof(struct omap_dmic), GFP_KERNEL);
 460        if (!dmic)
 461                return -ENOMEM;
 462
 463        platform_set_drvdata(pdev, dmic);
 464        dmic->dev = &pdev->dev;
 465        dmic->sysclk = OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS;
 466
 467        mutex_init(&dmic->mutex);
 468
 469        dmic->fclk = devm_clk_get(dmic->dev, "fck");
 470        if (IS_ERR(dmic->fclk)) {
 471                dev_err(dmic->dev, "cant get fck\n");
 472                return -ENODEV;
 473        }
 474
 475        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
 476        if (!res) {
 477                dev_err(dmic->dev, "invalid dma memory resource\n");
 478                return -ENODEV;
 479        }
 480        dmic->dma_data.addr = res->start + OMAP_DMIC_DATA_REG;
 481
 482        dmic->dma_data.filter_data = "up_link";
 483
 484        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
 485        dmic->io_base = devm_ioremap_resource(&pdev->dev, res);
 486        if (IS_ERR(dmic->io_base))
 487                return PTR_ERR(dmic->io_base);
 488
 489
 490        ret = devm_snd_soc_register_component(&pdev->dev,
 491                                              &omap_dmic_component,
 492                                              &omap_dmic_dai, 1);
 493        if (ret)
 494                return ret;
 495
 496        ret = omap_pcm_platform_register(&pdev->dev);
 497        if (ret)
 498                return ret;
 499
 500        return 0;
 501}
 502
 503static const struct of_device_id omap_dmic_of_match[] = {
 504        { .compatible = "ti,omap4-dmic", },
 505        { }
 506};
 507MODULE_DEVICE_TABLE(of, omap_dmic_of_match);
 508
 509static struct platform_driver asoc_dmic_driver = {
 510        .driver = {
 511                .name = "omap-dmic",
 512                .of_match_table = omap_dmic_of_match,
 513        },
 514        .probe = asoc_dmic_probe,
 515};
 516
 517module_platform_driver(asoc_dmic_driver);
 518
 519MODULE_ALIAS("platform:omap-dmic");
 520MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
 521MODULE_DESCRIPTION("OMAP DMIC ASoC Interface");
 522MODULE_LICENSE("GPL");
 523