Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * omap-dmic.c  --  OMAP ASoC DMIC DAI driver
0004  *
0005  * Copyright (C) 2010 - 2011 Texas Instruments
0006  *
0007  * Author: David Lambert <dlambert@ti.com>
0008  *     Misael Lopez Cruz <misael.lopez@ti.com>
0009  *     Liam Girdwood <lrg@ti.com>
0010  *     Peter Ujfalusi <peter.ujfalusi@ti.com>
0011  */
0012 
0013 #include <linux/init.h>
0014 #include <linux/module.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/err.h>
0017 #include <linux/clk.h>
0018 #include <linux/io.h>
0019 #include <linux/slab.h>
0020 #include <linux/pm_runtime.h>
0021 #include <linux/of_device.h>
0022 
0023 #include <sound/core.h>
0024 #include <sound/pcm.h>
0025 #include <sound/pcm_params.h>
0026 #include <sound/initval.h>
0027 #include <sound/soc.h>
0028 #include <sound/dmaengine_pcm.h>
0029 
0030 #include "omap-dmic.h"
0031 #include "sdma-pcm.h"
0032 
0033 struct omap_dmic {
0034     struct device *dev;
0035     void __iomem *io_base;
0036     struct clk *fclk;
0037     struct pm_qos_request pm_qos_req;
0038     int latency;
0039     int fclk_freq;
0040     int out_freq;
0041     int clk_div;
0042     int sysclk;
0043     int threshold;
0044     u32 ch_enabled;
0045     bool active;
0046     struct mutex mutex;
0047 
0048     struct snd_dmaengine_dai_dma_data dma_data;
0049 };
0050 
0051 static inline void omap_dmic_write(struct omap_dmic *dmic, u16 reg, u32 val)
0052 {
0053     writel_relaxed(val, dmic->io_base + reg);
0054 }
0055 
0056 static inline int omap_dmic_read(struct omap_dmic *dmic, u16 reg)
0057 {
0058     return readl_relaxed(dmic->io_base + reg);
0059 }
0060 
0061 static inline void omap_dmic_start(struct omap_dmic *dmic)
0062 {
0063     u32 ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG);
0064 
0065     /* Configure DMA controller */
0066     omap_dmic_write(dmic, OMAP_DMIC_DMAENABLE_SET_REG,
0067             OMAP_DMIC_DMA_ENABLE);
0068 
0069     omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, ctrl | dmic->ch_enabled);
0070 }
0071 
0072 static inline void omap_dmic_stop(struct omap_dmic *dmic)
0073 {
0074     u32 ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG);
0075     omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG,
0076             ctrl & ~OMAP_DMIC_UP_ENABLE_MASK);
0077 
0078     /* Disable DMA request generation */
0079     omap_dmic_write(dmic, OMAP_DMIC_DMAENABLE_CLR_REG,
0080             OMAP_DMIC_DMA_ENABLE);
0081 
0082 }
0083 
0084 static inline int dmic_is_enabled(struct omap_dmic *dmic)
0085 {
0086     return omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG) &
0087                         OMAP_DMIC_UP_ENABLE_MASK;
0088 }
0089 
0090 static int omap_dmic_dai_startup(struct snd_pcm_substream *substream,
0091                   struct snd_soc_dai *dai)
0092 {
0093     struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
0094     int ret = 0;
0095 
0096     mutex_lock(&dmic->mutex);
0097 
0098     if (!snd_soc_dai_active(dai))
0099         dmic->active = 1;
0100     else
0101         ret = -EBUSY;
0102 
0103     mutex_unlock(&dmic->mutex);
0104 
0105     return ret;
0106 }
0107 
0108 static void omap_dmic_dai_shutdown(struct snd_pcm_substream *substream,
0109                     struct snd_soc_dai *dai)
0110 {
0111     struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
0112 
0113     mutex_lock(&dmic->mutex);
0114 
0115     cpu_latency_qos_remove_request(&dmic->pm_qos_req);
0116 
0117     if (!snd_soc_dai_active(dai))
0118         dmic->active = 0;
0119 
0120     mutex_unlock(&dmic->mutex);
0121 }
0122 
0123 static int omap_dmic_select_divider(struct omap_dmic *dmic, int sample_rate)
0124 {
0125     int divider = -EINVAL;
0126 
0127     /*
0128      * 192KHz rate is only supported with 19.2MHz/3.84MHz clock
0129      * configuration.
0130      */
0131     if (sample_rate == 192000) {
0132         if (dmic->fclk_freq == 19200000 && dmic->out_freq == 3840000)
0133             divider = 0x6; /* Divider: 5 (192KHz sampling rate) */
0134         else
0135             dev_err(dmic->dev,
0136                 "invalid clock configuration for 192KHz\n");
0137 
0138         return divider;
0139     }
0140 
0141     switch (dmic->out_freq) {
0142     case 1536000:
0143         if (dmic->fclk_freq != 24576000)
0144             goto div_err;
0145         divider = 0x4; /* Divider: 16 */
0146         break;
0147     case 2400000:
0148         switch (dmic->fclk_freq) {
0149         case 12000000:
0150             divider = 0x5; /* Divider: 5 */
0151             break;
0152         case 19200000:
0153             divider = 0x0; /* Divider: 8 */
0154             break;
0155         case 24000000:
0156             divider = 0x2; /* Divider: 10 */
0157             break;
0158         default:
0159             goto div_err;
0160         }
0161         break;
0162     case 3072000:
0163         if (dmic->fclk_freq != 24576000)
0164             goto div_err;
0165         divider = 0x3; /* Divider: 8 */
0166         break;
0167     case 3840000:
0168         if (dmic->fclk_freq != 19200000)
0169             goto div_err;
0170         divider = 0x1; /* Divider: 5 (96KHz sampling rate) */
0171         break;
0172     default:
0173         dev_err(dmic->dev, "invalid out frequency: %dHz\n",
0174             dmic->out_freq);
0175         break;
0176     }
0177 
0178     return divider;
0179 
0180 div_err:
0181     dev_err(dmic->dev, "invalid out frequency %dHz for %dHz input\n",
0182         dmic->out_freq, dmic->fclk_freq);
0183     return -EINVAL;
0184 }
0185 
0186 static int omap_dmic_dai_hw_params(struct snd_pcm_substream *substream,
0187                     struct snd_pcm_hw_params *params,
0188                     struct snd_soc_dai *dai)
0189 {
0190     struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
0191     struct snd_dmaengine_dai_dma_data *dma_data;
0192     int channels;
0193 
0194     dmic->clk_div = omap_dmic_select_divider(dmic, params_rate(params));
0195     if (dmic->clk_div < 0) {
0196         dev_err(dmic->dev, "no valid divider for %dHz from %dHz\n",
0197             dmic->out_freq, dmic->fclk_freq);
0198         return -EINVAL;
0199     }
0200 
0201     dmic->ch_enabled = 0;
0202     channels = params_channels(params);
0203     switch (channels) {
0204     case 6:
0205         dmic->ch_enabled |= OMAP_DMIC_UP3_ENABLE;
0206         fallthrough;
0207     case 4:
0208         dmic->ch_enabled |= OMAP_DMIC_UP2_ENABLE;
0209         fallthrough;
0210     case 2:
0211         dmic->ch_enabled |= OMAP_DMIC_UP1_ENABLE;
0212         break;
0213     default:
0214         dev_err(dmic->dev, "invalid number of legacy channels\n");
0215         return -EINVAL;
0216     }
0217 
0218     /* packet size is threshold * channels */
0219     dma_data = snd_soc_dai_get_dma_data(dai, substream);
0220     dma_data->maxburst = dmic->threshold * channels;
0221     dmic->latency = (OMAP_DMIC_THRES_MAX - dmic->threshold) * USEC_PER_SEC /
0222             params_rate(params);
0223 
0224     return 0;
0225 }
0226 
0227 static int omap_dmic_dai_prepare(struct snd_pcm_substream *substream,
0228                   struct snd_soc_dai *dai)
0229 {
0230     struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
0231     u32 ctrl;
0232 
0233     if (cpu_latency_qos_request_active(&dmic->pm_qos_req))
0234         cpu_latency_qos_update_request(&dmic->pm_qos_req,
0235                            dmic->latency);
0236 
0237     /* Configure uplink threshold */
0238     omap_dmic_write(dmic, OMAP_DMIC_FIFO_CTRL_REG, dmic->threshold);
0239 
0240     ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG);
0241 
0242     /* Set dmic out format */
0243     ctrl &= ~(OMAP_DMIC_FORMAT | OMAP_DMIC_POLAR_MASK);
0244     ctrl |= (OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 |
0245          OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3);
0246 
0247     /* Configure dmic clock divider */
0248     ctrl &= ~OMAP_DMIC_CLK_DIV_MASK;
0249     ctrl |= OMAP_DMIC_CLK_DIV(dmic->clk_div);
0250 
0251     omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, ctrl);
0252 
0253     omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG,
0254             ctrl | OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 |
0255             OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3);
0256 
0257     return 0;
0258 }
0259 
0260 static int omap_dmic_dai_trigger(struct snd_pcm_substream *substream,
0261                   int cmd, struct snd_soc_dai *dai)
0262 {
0263     struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
0264 
0265     switch (cmd) {
0266     case SNDRV_PCM_TRIGGER_START:
0267         omap_dmic_start(dmic);
0268         break;
0269     case SNDRV_PCM_TRIGGER_STOP:
0270         omap_dmic_stop(dmic);
0271         break;
0272     default:
0273         break;
0274     }
0275 
0276     return 0;
0277 }
0278 
0279 static int omap_dmic_select_fclk(struct omap_dmic *dmic, int clk_id,
0280                  unsigned int freq)
0281 {
0282     struct clk *parent_clk, *mux;
0283     char *parent_clk_name;
0284     int ret = 0;
0285 
0286     switch (freq) {
0287     case 12000000:
0288     case 19200000:
0289     case 24000000:
0290     case 24576000:
0291         break;
0292     default:
0293         dev_err(dmic->dev, "invalid input frequency: %dHz\n", freq);
0294         dmic->fclk_freq = 0;
0295         return -EINVAL;
0296     }
0297 
0298     if (dmic->sysclk == clk_id) {
0299         dmic->fclk_freq = freq;
0300         return 0;
0301     }
0302 
0303     /* re-parent not allowed if a stream is ongoing */
0304     if (dmic->active && dmic_is_enabled(dmic)) {
0305         dev_err(dmic->dev, "can't re-parent when DMIC active\n");
0306         return -EBUSY;
0307     }
0308 
0309     switch (clk_id) {
0310     case OMAP_DMIC_SYSCLK_PAD_CLKS:
0311         parent_clk_name = "pad_clks_ck";
0312         break;
0313     case OMAP_DMIC_SYSCLK_SLIMBLUS_CLKS:
0314         parent_clk_name = "slimbus_clk";
0315         break;
0316     case OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS:
0317         parent_clk_name = "dmic_sync_mux_ck";
0318         break;
0319     default:
0320         dev_err(dmic->dev, "fclk clk_id (%d) not supported\n", clk_id);
0321         return -EINVAL;
0322     }
0323 
0324     parent_clk = clk_get(dmic->dev, parent_clk_name);
0325     if (IS_ERR(parent_clk)) {
0326         dev_err(dmic->dev, "can't get %s\n", parent_clk_name);
0327         return -ENODEV;
0328     }
0329 
0330     mux = clk_get_parent(dmic->fclk);
0331     if (IS_ERR(mux)) {
0332         dev_err(dmic->dev, "can't get fck mux parent\n");
0333         clk_put(parent_clk);
0334         return -ENODEV;
0335     }
0336 
0337     mutex_lock(&dmic->mutex);
0338     if (dmic->active) {
0339         /* disable clock while reparenting */
0340         pm_runtime_put_sync(dmic->dev);
0341         ret = clk_set_parent(mux, parent_clk);
0342         pm_runtime_get_sync(dmic->dev);
0343     } else {
0344         ret = clk_set_parent(mux, parent_clk);
0345     }
0346     mutex_unlock(&dmic->mutex);
0347 
0348     if (ret < 0) {
0349         dev_err(dmic->dev, "re-parent failed\n");
0350         goto err_busy;
0351     }
0352 
0353     dmic->sysclk = clk_id;
0354     dmic->fclk_freq = freq;
0355 
0356 err_busy:
0357     clk_put(mux);
0358     clk_put(parent_clk);
0359 
0360     return ret;
0361 }
0362 
0363 static int omap_dmic_select_outclk(struct omap_dmic *dmic, int clk_id,
0364                     unsigned int freq)
0365 {
0366     int ret = 0;
0367 
0368     if (clk_id != OMAP_DMIC_ABE_DMIC_CLK) {
0369         dev_err(dmic->dev, "output clk_id (%d) not supported\n",
0370             clk_id);
0371         return -EINVAL;
0372     }
0373 
0374     switch (freq) {
0375     case 1536000:
0376     case 2400000:
0377     case 3072000:
0378     case 3840000:
0379         dmic->out_freq = freq;
0380         break;
0381     default:
0382         dev_err(dmic->dev, "invalid out frequency: %dHz\n", freq);
0383         dmic->out_freq = 0;
0384         ret = -EINVAL;
0385     }
0386 
0387     return ret;
0388 }
0389 
0390 static int omap_dmic_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
0391                     unsigned int freq, int dir)
0392 {
0393     struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
0394 
0395     if (dir == SND_SOC_CLOCK_IN)
0396         return omap_dmic_select_fclk(dmic, clk_id, freq);
0397     else if (dir == SND_SOC_CLOCK_OUT)
0398         return omap_dmic_select_outclk(dmic, clk_id, freq);
0399 
0400     dev_err(dmic->dev, "invalid clock direction (%d)\n", dir);
0401     return -EINVAL;
0402 }
0403 
0404 static const struct snd_soc_dai_ops omap_dmic_dai_ops = {
0405     .startup    = omap_dmic_dai_startup,
0406     .shutdown   = omap_dmic_dai_shutdown,
0407     .hw_params  = omap_dmic_dai_hw_params,
0408     .prepare    = omap_dmic_dai_prepare,
0409     .trigger    = omap_dmic_dai_trigger,
0410     .set_sysclk = omap_dmic_set_dai_sysclk,
0411 };
0412 
0413 static int omap_dmic_probe(struct snd_soc_dai *dai)
0414 {
0415     struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
0416 
0417     pm_runtime_enable(dmic->dev);
0418 
0419     /* Disable lines while request is ongoing */
0420     pm_runtime_get_sync(dmic->dev);
0421     omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, 0x00);
0422     pm_runtime_put_sync(dmic->dev);
0423 
0424     /* Configure DMIC threshold value */
0425     dmic->threshold = OMAP_DMIC_THRES_MAX - 3;
0426 
0427     snd_soc_dai_init_dma_data(dai, NULL, &dmic->dma_data);
0428 
0429     return 0;
0430 }
0431 
0432 static int omap_dmic_remove(struct snd_soc_dai *dai)
0433 {
0434     struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
0435 
0436     pm_runtime_disable(dmic->dev);
0437 
0438     return 0;
0439 }
0440 
0441 static struct snd_soc_dai_driver omap_dmic_dai = {
0442     .name = "omap-dmic",
0443     .probe = omap_dmic_probe,
0444     .remove = omap_dmic_remove,
0445     .capture = {
0446         .channels_min = 2,
0447         .channels_max = 6,
0448         .rates = SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
0449         .formats = SNDRV_PCM_FMTBIT_S32_LE,
0450         .sig_bits = 24,
0451     },
0452     .ops = &omap_dmic_dai_ops,
0453 };
0454 
0455 static const struct snd_soc_component_driver omap_dmic_component = {
0456     .name           = "omap-dmic",
0457     .legacy_dai_naming  = 1,
0458 };
0459 
0460 static int asoc_dmic_probe(struct platform_device *pdev)
0461 {
0462     struct omap_dmic *dmic;
0463     struct resource *res;
0464     int ret;
0465 
0466     dmic = devm_kzalloc(&pdev->dev, sizeof(struct omap_dmic), GFP_KERNEL);
0467     if (!dmic)
0468         return -ENOMEM;
0469 
0470     platform_set_drvdata(pdev, dmic);
0471     dmic->dev = &pdev->dev;
0472     dmic->sysclk = OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS;
0473 
0474     mutex_init(&dmic->mutex);
0475 
0476     dmic->fclk = devm_clk_get(dmic->dev, "fck");
0477     if (IS_ERR(dmic->fclk)) {
0478         dev_err(dmic->dev, "can't get fck\n");
0479         return -ENODEV;
0480     }
0481 
0482     res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
0483     if (!res) {
0484         dev_err(dmic->dev, "invalid dma memory resource\n");
0485         return -ENODEV;
0486     }
0487     dmic->dma_data.addr = res->start + OMAP_DMIC_DATA_REG;
0488 
0489     dmic->dma_data.filter_data = "up_link";
0490 
0491     res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
0492     dmic->io_base = devm_ioremap_resource(&pdev->dev, res);
0493     if (IS_ERR(dmic->io_base))
0494         return PTR_ERR(dmic->io_base);
0495 
0496 
0497     ret = devm_snd_soc_register_component(&pdev->dev,
0498                           &omap_dmic_component,
0499                           &omap_dmic_dai, 1);
0500     if (ret)
0501         return ret;
0502 
0503     ret = sdma_pcm_platform_register(&pdev->dev, NULL, "up_link");
0504     if (ret)
0505         return ret;
0506 
0507     return 0;
0508 }
0509 
0510 static const struct of_device_id omap_dmic_of_match[] = {
0511     { .compatible = "ti,omap4-dmic", },
0512     { }
0513 };
0514 MODULE_DEVICE_TABLE(of, omap_dmic_of_match);
0515 
0516 static struct platform_driver asoc_dmic_driver = {
0517     .driver = {
0518         .name = "omap-dmic",
0519         .of_match_table = omap_dmic_of_match,
0520     },
0521     .probe = asoc_dmic_probe,
0522 };
0523 
0524 module_platform_driver(asoc_dmic_driver);
0525 
0526 MODULE_ALIAS("platform:omap-dmic");
0527 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
0528 MODULE_DESCRIPTION("OMAP DMIC ASoC Interface");
0529 MODULE_LICENSE("GPL");