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