~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/sunxi/sun50i-dmic.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-or-later
  2 //
  3 // This driver supports the DMIC in Allwinner's H6 SoCs.
  4 //
  5 // Copyright 2021 Ban Tao <fengzheng923@gmail.com>
  6 
  7 #include <linux/clk.h>
  8 #include <linux/device.h>
  9 #include <linux/mod_devicetable.h>
 10 #include <linux/module.h>
 11 #include <linux/platform_device.h>
 12 #include <linux/pm_runtime.h>
 13 #include <linux/reset.h>
 14 #include <sound/dmaengine_pcm.h>
 15 #include <sound/pcm_params.h>
 16 #include <sound/soc.h>
 17 #include <sound/tlv.h>
 18 
 19 #define SUN50I_DMIC_EN_CTL                      (0x00)
 20         #define SUN50I_DMIC_EN_CTL_GLOBE                        BIT(8)
 21         #define SUN50I_DMIC_EN_CTL_CHAN(v)                      ((v) << 0)
 22         #define SUN50I_DMIC_EN_CTL_CHAN_MASK                    GENMASK(7, 0)
 23 #define SUN50I_DMIC_SR                          (0x04)
 24         #define SUN50I_DMIC_SR_SAMPLE_RATE(v)                   ((v) << 0)
 25         #define SUN50I_DMIC_SR_SAMPLE_RATE_MASK                 GENMASK(2, 0)
 26 #define SUN50I_DMIC_CTL                         (0x08)
 27         #define SUN50I_DMIC_CTL_OVERSAMPLE_RATE                 BIT(0)
 28 #define SUN50I_DMIC_DATA                        (0x10)
 29 #define SUN50I_DMIC_INTC                        (0x14)
 30         #define SUN50I_DMIC_FIFO_DRQ_EN                         BIT(2)
 31 #define SUN50I_DMIC_INT_STA                     (0x18)
 32         #define SUN50I_DMIC_INT_STA_OVERRUN_IRQ_PENDING         BIT(1)
 33         #define SUN50I_DMIC_INT_STA_DATA_IRQ_PENDING            BIT(0)
 34 #define SUN50I_DMIC_RXFIFO_CTL                  (0x1c)
 35         #define SUN50I_DMIC_RXFIFO_CTL_FLUSH                    BIT(31)
 36         #define SUN50I_DMIC_RXFIFO_CTL_MODE_MASK                BIT(9)
 37         #define SUN50I_DMIC_RXFIFO_CTL_MODE_LSB                 (0 << 9)
 38         #define SUN50I_DMIC_RXFIFO_CTL_MODE_MSB                 (1 << 9)
 39         #define SUN50I_DMIC_RXFIFO_CTL_SAMPLE_MASK              BIT(8)
 40         #define SUN50I_DMIC_RXFIFO_CTL_SAMPLE_16                (0 << 8)
 41         #define SUN50I_DMIC_RXFIFO_CTL_SAMPLE_24                (1 << 8)
 42 #define SUN50I_DMIC_CH_NUM                      (0x24)
 43         #define SUN50I_DMIC_CH_NUM_N(v)                         ((v) << 0)
 44         #define SUN50I_DMIC_CH_NUM_N_MASK                       GENMASK(2, 0)
 45 #define SUN50I_DMIC_CNT                         (0x2c)
 46         #define SUN50I_DMIC_CNT_N                               (1 << 0)
 47 #define SUN50I_DMIC_D0D1_VOL_CTR                (0x30)
 48         #define SUN50I_DMIC_D0D1_VOL_CTR_0R                     (0)
 49         #define SUN50I_DMIC_D0D1_VOL_CTR_0L                     (8)
 50         #define SUN50I_DMIC_D0D1_VOL_CTR_1R                     (16)
 51         #define SUN50I_DMIC_D0D1_VOL_CTR_1L                     (24)
 52 #define SUN50I_DMIC_D2D3_VOL_CTR                (0x34)
 53         #define SUN50I_DMIC_D2D3_VOL_CTR_2R                     (0)
 54         #define SUN50I_DMIC_D2D3_VOL_CTR_2L                     (8)
 55         #define SUN50I_DMIC_D2D3_VOL_CTR_3R                     (16)
 56         #define SUN50I_DMIC_D2D3_VOL_CTR_3L                     (24)
 57 
 58 #define SUN50I_DMIC_HPF_CTRL                    (0x38)
 59 #define SUN50I_DMIC_VERSION                     (0x50)
 60 
 61 struct sun50i_dmic_dev {
 62         struct clk *dmic_clk;
 63         struct clk *bus_clk;
 64         struct reset_control *rst;
 65         struct regmap *regmap;
 66         struct snd_dmaengine_dai_dma_data dma_params_rx;
 67 };
 68 
 69 struct dmic_rate {
 70         unsigned int samplerate;
 71         unsigned int rate_bit;
 72 };
 73 
 74 static const struct dmic_rate dmic_rate_s[] = {
 75         {48000, 0x0},
 76         {44100, 0x0},
 77         {32000, 0x1},
 78         {24000, 0x2},
 79         {22050, 0x2},
 80         {16000, 0x3},
 81         {12000, 0x4},
 82         {11025, 0x4},
 83         {8000,  0x5},
 84 };
 85 
 86 static int sun50i_dmic_startup(struct snd_pcm_substream *substream,
 87                                struct snd_soc_dai *cpu_dai)
 88 {
 89         struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 90         struct sun50i_dmic_dev *host = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
 91 
 92         /* only support capture */
 93         if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
 94                 return -EINVAL;
 95 
 96         regmap_update_bits(host->regmap, SUN50I_DMIC_RXFIFO_CTL,
 97                         SUN50I_DMIC_RXFIFO_CTL_FLUSH,
 98                         SUN50I_DMIC_RXFIFO_CTL_FLUSH);
 99         regmap_write(host->regmap, SUN50I_DMIC_CNT, SUN50I_DMIC_CNT_N);
100 
101         return 0;
102 }
103 
104 static int sun50i_dmic_hw_params(struct snd_pcm_substream *substream,
105                                  struct snd_pcm_hw_params *params,
106                                  struct snd_soc_dai *cpu_dai)
107 {
108         int i = 0;
109         unsigned long rate = params_rate(params);
110         unsigned int mclk = 0;
111         unsigned int channels = params_channels(params);
112         unsigned int chan_en = (1 << channels) - 1;
113         struct sun50i_dmic_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
114 
115         /* DMIC num is N+1 */
116         regmap_update_bits(host->regmap, SUN50I_DMIC_CH_NUM,
117                            SUN50I_DMIC_CH_NUM_N_MASK,
118                            SUN50I_DMIC_CH_NUM_N(channels - 1));
119         regmap_write(host->regmap, SUN50I_DMIC_HPF_CTRL, chan_en);
120         regmap_update_bits(host->regmap, SUN50I_DMIC_EN_CTL,
121                            SUN50I_DMIC_EN_CTL_CHAN_MASK,
122                            SUN50I_DMIC_EN_CTL_CHAN(chan_en));
123 
124         switch (params_format(params)) {
125         case SNDRV_PCM_FORMAT_S16_LE:
126                 regmap_update_bits(host->regmap, SUN50I_DMIC_RXFIFO_CTL,
127                                    SUN50I_DMIC_RXFIFO_CTL_SAMPLE_MASK,
128                                    SUN50I_DMIC_RXFIFO_CTL_SAMPLE_16);
129                 break;
130         case SNDRV_PCM_FORMAT_S24_LE:
131                 regmap_update_bits(host->regmap, SUN50I_DMIC_RXFIFO_CTL,
132                                    SUN50I_DMIC_RXFIFO_CTL_SAMPLE_MASK,
133                                    SUN50I_DMIC_RXFIFO_CTL_SAMPLE_24);
134                 break;
135         default:
136                 dev_err(cpu_dai->dev, "Invalid format!\n");
137                 return -EINVAL;
138         }
139         /* The hardware supports FIFO mode 1 for 24-bit samples */
140         regmap_update_bits(host->regmap, SUN50I_DMIC_RXFIFO_CTL,
141                            SUN50I_DMIC_RXFIFO_CTL_MODE_MASK,
142                            SUN50I_DMIC_RXFIFO_CTL_MODE_MSB);
143 
144         switch (rate) {
145         case 11025:
146         case 22050:
147         case 44100:
148                 mclk = 22579200;
149                 break;
150         case 8000:
151         case 12000:
152         case 16000:
153         case 24000:
154         case 32000:
155         case 48000:
156                 mclk = 24576000;
157                 break;
158         default:
159                 dev_err(cpu_dai->dev, "Invalid rate!\n");
160                 return -EINVAL;
161         }
162 
163         if (clk_set_rate(host->dmic_clk, mclk)) {
164                 dev_err(cpu_dai->dev, "mclk : %u not support\n", mclk);
165                 return -EINVAL;
166         }
167 
168         for (i = 0; i < ARRAY_SIZE(dmic_rate_s); i++) {
169                 if (dmic_rate_s[i].samplerate == rate) {
170                         regmap_update_bits(host->regmap, SUN50I_DMIC_SR,
171                                            SUN50I_DMIC_SR_SAMPLE_RATE_MASK,
172                                            SUN50I_DMIC_SR_SAMPLE_RATE(dmic_rate_s[i].rate_bit));
173                         break;
174                 }
175         }
176 
177         switch (params_physical_width(params)) {
178         case 16:
179                 host->dma_params_rx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
180                 break;
181         case 32:
182                 host->dma_params_rx.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
183                 break;
184         default:
185                 dev_err(cpu_dai->dev, "Unsupported physical sample width: %d\n",
186                         params_physical_width(params));
187                 return -EINVAL;
188         }
189 
190         /* oversamplerate adjust */
191         if (params_rate(params) >= 24000)
192                 regmap_update_bits(host->regmap, SUN50I_DMIC_CTL,
193                                    SUN50I_DMIC_CTL_OVERSAMPLE_RATE,
194                                    SUN50I_DMIC_CTL_OVERSAMPLE_RATE);
195         else
196                 regmap_update_bits(host->regmap, SUN50I_DMIC_CTL,
197                                    SUN50I_DMIC_CTL_OVERSAMPLE_RATE, 0);
198 
199         return 0;
200 }
201 
202 static int sun50i_dmic_trigger(struct snd_pcm_substream *substream, int cmd,
203                                struct snd_soc_dai *dai)
204 {
205         int ret = 0;
206         struct sun50i_dmic_dev *host = snd_soc_dai_get_drvdata(dai);
207 
208         if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
209                 return -EINVAL;
210 
211         switch (cmd) {
212         case SNDRV_PCM_TRIGGER_START:
213         case SNDRV_PCM_TRIGGER_RESUME:
214         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
215                 /* DRQ ENABLE */
216                 regmap_update_bits(host->regmap, SUN50I_DMIC_INTC,
217                                    SUN50I_DMIC_FIFO_DRQ_EN,
218                                    SUN50I_DMIC_FIFO_DRQ_EN);
219                 /* Global enable */
220                 regmap_update_bits(host->regmap, SUN50I_DMIC_EN_CTL,
221                                    SUN50I_DMIC_EN_CTL_GLOBE,
222                                    SUN50I_DMIC_EN_CTL_GLOBE);
223                 break;
224         case SNDRV_PCM_TRIGGER_STOP:
225         case SNDRV_PCM_TRIGGER_SUSPEND:
226         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
227                 /* DRQ DISABLE */
228                 regmap_update_bits(host->regmap, SUN50I_DMIC_INTC,
229                                    SUN50I_DMIC_FIFO_DRQ_EN, 0);
230                 /* Global disable */
231                 regmap_update_bits(host->regmap, SUN50I_DMIC_EN_CTL,
232                                    SUN50I_DMIC_EN_CTL_GLOBE, 0);
233                 break;
234         default:
235                 ret = -EINVAL;
236                 break;
237         }
238         return ret;
239 }
240 
241 static int sun50i_dmic_soc_dai_probe(struct snd_soc_dai *dai)
242 {
243         struct sun50i_dmic_dev *host = snd_soc_dai_get_drvdata(dai);
244 
245         snd_soc_dai_init_dma_data(dai, NULL, &host->dma_params_rx);
246 
247         return 0;
248 }
249 
250 static const struct snd_soc_dai_ops sun50i_dmic_dai_ops = {
251         .probe          = sun50i_dmic_soc_dai_probe,
252         .startup        = sun50i_dmic_startup,
253         .trigger        = sun50i_dmic_trigger,
254         .hw_params      = sun50i_dmic_hw_params,
255 };
256 
257 static const struct regmap_config sun50i_dmic_regmap_config = {
258         .reg_bits = 32,
259         .reg_stride = 4,
260         .val_bits = 32,
261         .max_register = SUN50I_DMIC_VERSION,
262         .cache_type = REGCACHE_NONE,
263 };
264 
265 #define SUN50I_DMIC_RATES (SNDRV_PCM_RATE_8000_48000)
266 #define SUN50I_DMIC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
267 
268 static struct snd_soc_dai_driver sun50i_dmic_dai = {
269         .capture = {
270                 .channels_min = 1,
271                 .channels_max = 8,
272                 .rates = SUN50I_DMIC_RATES,
273                 .formats = SUN50I_DMIC_FORMATS,
274                 .sig_bits = 21,
275         },
276         .ops = &sun50i_dmic_dai_ops,
277         .name = "dmic",
278 };
279 
280 static const struct of_device_id sun50i_dmic_of_match[] = {
281         {
282                 .compatible = "allwinner,sun50i-h6-dmic",
283         },
284         { /* sentinel */ }
285 };
286 MODULE_DEVICE_TABLE(of, sun50i_dmic_of_match);
287 
288 static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(sun50i_dmic_vol_scale, -12000, 75, 1);
289 
290 static const struct snd_kcontrol_new sun50i_dmic_controls[] = {
291 
292         SOC_DOUBLE_TLV("DMIC Channel 0 Capture Volume", SUN50I_DMIC_D0D1_VOL_CTR,
293                        SUN50I_DMIC_D0D1_VOL_CTR_0L, SUN50I_DMIC_D0D1_VOL_CTR_0R,
294                        0xFF, 0, sun50i_dmic_vol_scale),
295         SOC_DOUBLE_TLV("DMIC Channel 1 Capture Volume", SUN50I_DMIC_D0D1_VOL_CTR,
296                        SUN50I_DMIC_D0D1_VOL_CTR_1L, SUN50I_DMIC_D0D1_VOL_CTR_1R,
297                        0xFF, 0, sun50i_dmic_vol_scale),
298         SOC_DOUBLE_TLV("DMIC Channel 2 Capture Volume", SUN50I_DMIC_D2D3_VOL_CTR,
299                        SUN50I_DMIC_D2D3_VOL_CTR_2L, SUN50I_DMIC_D2D3_VOL_CTR_2R,
300                        0xFF, 0, sun50i_dmic_vol_scale),
301         SOC_DOUBLE_TLV("DMIC Channel 3 Capture Volume", SUN50I_DMIC_D2D3_VOL_CTR,
302                        SUN50I_DMIC_D2D3_VOL_CTR_3L, SUN50I_DMIC_D2D3_VOL_CTR_3R,
303                        0xFF, 0, sun50i_dmic_vol_scale),
304 
305 
306 };
307 
308 static const struct snd_soc_component_driver sun50i_dmic_component = {
309         .name           = "sun50i-dmic",
310         .controls       = sun50i_dmic_controls,
311         .num_controls   = ARRAY_SIZE(sun50i_dmic_controls),
312 };
313 
314 static int sun50i_dmic_runtime_suspend(struct device *dev)
315 {
316         struct sun50i_dmic_dev *host  = dev_get_drvdata(dev);
317 
318         clk_disable_unprepare(host->dmic_clk);
319         clk_disable_unprepare(host->bus_clk);
320 
321         return 0;
322 }
323 
324 static int sun50i_dmic_runtime_resume(struct device *dev)
325 {
326         struct sun50i_dmic_dev *host  = dev_get_drvdata(dev);
327         int ret;
328 
329         ret = clk_prepare_enable(host->dmic_clk);
330         if (ret)
331                 return ret;
332 
333         ret = clk_prepare_enable(host->bus_clk);
334         if (ret) {
335                 clk_disable_unprepare(host->dmic_clk);
336                 return ret;
337         }
338 
339         return 0;
340 }
341 
342 static int sun50i_dmic_probe(struct platform_device *pdev)
343 {
344         struct sun50i_dmic_dev *host;
345         struct resource *res;
346         int ret;
347         void __iomem *base;
348 
349         host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
350         if (!host)
351                 return -ENOMEM;
352 
353         /* Get the addresses */
354         base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
355         if (IS_ERR(base))
356                 return dev_err_probe(&pdev->dev, PTR_ERR(base),
357                                      "get resource failed.\n");
358 
359         host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
360                                              &sun50i_dmic_regmap_config);
361 
362         /* Clocks */
363         host->bus_clk = devm_clk_get(&pdev->dev, "bus");
364         if (IS_ERR(host->bus_clk))
365                 return dev_err_probe(&pdev->dev, PTR_ERR(host->bus_clk),
366                                      "failed to get bus clock.\n");
367 
368         host->dmic_clk = devm_clk_get(&pdev->dev, "mod");
369         if (IS_ERR(host->dmic_clk))
370                 return dev_err_probe(&pdev->dev, PTR_ERR(host->dmic_clk),
371                                      "failed to get dmic clock.\n");
372 
373         host->dma_params_rx.addr = res->start + SUN50I_DMIC_DATA;
374         host->dma_params_rx.maxburst = 8;
375 
376         platform_set_drvdata(pdev, host);
377 
378         host->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
379         if (IS_ERR(host->rst))
380                 return dev_err_probe(&pdev->dev, PTR_ERR(host->rst),
381                                      "Failed to get reset.\n");
382         reset_control_deassert(host->rst);
383 
384         ret = devm_snd_soc_register_component(&pdev->dev, &sun50i_dmic_component,
385                                               &sun50i_dmic_dai, 1);
386         if (ret)
387                 return dev_err_probe(&pdev->dev, ret,
388                                      "failed to register component.\n");
389 
390         pm_runtime_enable(&pdev->dev);
391         if (!pm_runtime_enabled(&pdev->dev)) {
392                 ret = sun50i_dmic_runtime_resume(&pdev->dev);
393                 if (ret)
394                         goto err_disable_runtime_pm;
395         }
396 
397         ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
398         if (ret)
399                 goto err_suspend;
400 
401         return 0;
402 err_suspend:
403         if (!pm_runtime_status_suspended(&pdev->dev))
404                 sun50i_dmic_runtime_suspend(&pdev->dev);
405 err_disable_runtime_pm:
406         pm_runtime_disable(&pdev->dev);
407         return ret;
408 }
409 
410 static void sun50i_dmic_remove(struct platform_device *pdev)
411 {
412         pm_runtime_disable(&pdev->dev);
413         if (!pm_runtime_status_suspended(&pdev->dev))
414                 sun50i_dmic_runtime_suspend(&pdev->dev);
415 }
416 
417 static const struct dev_pm_ops sun50i_dmic_pm = {
418         SET_RUNTIME_PM_OPS(sun50i_dmic_runtime_suspend,
419                            sun50i_dmic_runtime_resume, NULL)
420 };
421 
422 static struct platform_driver sun50i_dmic_driver = {
423         .driver         = {
424                 .name   = "sun50i-dmic",
425                 .of_match_table = sun50i_dmic_of_match,
426                 .pm     = &sun50i_dmic_pm,
427         },
428         .probe          = sun50i_dmic_probe,
429         .remove_new     = sun50i_dmic_remove,
430 };
431 
432 module_platform_driver(sun50i_dmic_driver);
433 
434 MODULE_DESCRIPTION("Allwinner sun50i DMIC SoC Interface");
435 MODULE_AUTHOR("Ban Tao <fengzheng923@gmail.com>");
436 MODULE_LICENSE("GPL");
437 MODULE_ALIAS("platform:sun50i-dmic");
438 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php