1 /*
2 * Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
3 *
4 * Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
5 * the the people's Republic of China and other countries.
6 * All Allwinner Technology Co.,Ltd. trademarks are used with permission.
7 *
8 * DISCLAIMER
9 * THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
10 * IF YOU NEED TO INTEGRATE THIRD PARTY’S TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
11 * IN ALLWINNERS’SDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
12 * ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
13 * ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
14 * COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
15 * YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTY’S TECHNOLOGY.
16 *
17 *
18 * THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
19 * PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
20 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
21 * THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
22 * OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23 * IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 #include <hal_timer.h>
33 #include <stdio.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #include <sound/snd_core.h>
40 #include <sound/snd_pcm.h>
41 #include <sound/snd_dma.h>
42 #include <sound/dma_wrap.h>
43 #include <hal_dma.h>
44 #include <hal_cmd.h>
45 
46 #include "sunxi-pcm.h"
47 #include "sunxi-dmic.h"
48 
49 static const struct dmic_rate dmic_rate_s[] = {
50     {44100, 0x0},
51     {48000, 0x0},
52     {22050, 0x2},
53     /* KNOT support */
54     {24000, 0x2},
55     {11025, 0x4},
56     {12000, 0x4},
57     {32000, 0x1},
58     {16000, 0x3},
59     {8000, 0x5},
60 };
61 
62 /*
63  * Configure DMA , Chan enable & Global enable
64  */
sunxi_dmic_enable(struct snd_platform * platform,bool enable)65 static void sunxi_dmic_enable(struct snd_platform *platform, bool enable)
66 {
67     struct sunxi_dmic_info *sunxi_dmic = platform->private_data;
68 
69     snd_print("\n");
70     if (enable) {
71         snd_platform_update_bits(platform, SUNXI_DMIC_INTC,
72                 (0x1 << FIFO_DRQ_EN), (0x1 << FIFO_DRQ_EN));
73 
74         snd_platform_update_bits(platform, SUNXI_DMIC_EN,
75                 (0xFF<<DATA_CH_EN),
76                 ((sunxi_dmic->chanmap)<<DATA_CH_EN));
77 
78         snd_platform_update_bits(platform, SUNXI_DMIC_EN,
79                 (0x1 << GLOBE_EN), (0x1 << GLOBE_EN));
80     } else {
81         snd_platform_update_bits(platform, SUNXI_DMIC_EN,
82                 (0x1 << GLOBE_EN), (0x0 << GLOBE_EN));
83         snd_platform_update_bits(platform, SUNXI_DMIC_EN,
84                 (0xFF << DATA_CH_EN),
85                 (0x0 << DATA_CH_EN));
86         snd_platform_update_bits(platform, SUNXI_DMIC_INTC,
87                 (0x1 << FIFO_DRQ_EN),
88                 (0x0 << FIFO_DRQ_EN));
89     }
90 }
91 
sunxi_dmic_init(struct snd_platform * platform)92 static void sunxi_dmic_init(struct snd_platform *platform)
93 {
94     snd_print("\n");
95     snd_platform_write(platform,
96             SUNXI_DMIC_CH_MAP, DMIC_CHANMAP_DEFAULT);
97     snd_platform_update_bits(platform, SUNXI_DMIC_CTR,
98             (0x7<<DMICDFEN), (0x7<<DMICDFEN));
99 
100     /* set the vol */
101     snd_platform_write(platform, SUNXI_DMIC_DATA0_1_VOL, DMIC_DEFAULT_VOL);
102     snd_platform_write(platform, SUNXI_DMIC_DATA2_3_VOL, DMIC_DEFAULT_VOL);
103 }
104 
sunxi_dmic_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_dai * dai)105 static int sunxi_dmic_hw_params(struct snd_pcm_substream *substream,
106         struct snd_pcm_hw_params *params, struct snd_dai *dai)
107 {
108     struct snd_platform *platform = dai->component;
109     struct sunxi_dmic_info *sunxi_dmic = platform->private_data;
110     int i;
111 
112     snd_print("\n");
113     /* if clk rst */
114     sunxi_dmic_init(platform);
115 
116     /* sample resolution & sample fifo format */
117     switch (params_format(params)) {
118     case SNDRV_PCM_FORMAT_S16_LE:
119         snd_platform_update_bits(platform, SUNXI_DMIC_FIFO_CTR,
120                 (0x1 << DMIC_SAMPLE_RESOLUTION),
121                 (0x0 << DMIC_SAMPLE_RESOLUTION));
122         snd_platform_update_bits(platform, SUNXI_DMIC_FIFO_CTR,
123                 (0x1 << DMIC_FIFO_MODE),
124                 (0x1 << DMIC_FIFO_MODE));
125         break;
126     case SNDRV_PCM_FORMAT_S24_LE:
127         snd_platform_update_bits(platform, SUNXI_DMIC_FIFO_CTR,
128                 (0x1 << DMIC_SAMPLE_RESOLUTION),
129                 (0x1 << DMIC_SAMPLE_RESOLUTION));
130         snd_platform_update_bits(platform, SUNXI_DMIC_FIFO_CTR,
131                 (0x1 << DMIC_FIFO_MODE),
132                 (0x0 << DMIC_FIFO_MODE));
133         break;
134     default:
135         snd_err( "Invalid format set\n");
136         return -EINVAL;
137     }
138 
139     for (i = 0; i < ARRAY_SIZE(dmic_rate_s); i++) {
140         if (dmic_rate_s[i].samplerate == params_rate(params)) {
141             snd_platform_update_bits(platform, SUNXI_DMIC_SR,
142             (7<<DMIC_SR), (dmic_rate_s[i].rate_bit<<DMIC_SR));
143             break;
144         }
145     }
146 
147     /* oversamplerate adjust */
148     if (params_rate(params) >= 24000) {
149         snd_platform_update_bits(platform, SUNXI_DMIC_CTR,
150             (1<<DMIC_OVERSAMPLE_RATE), (1<<DMIC_OVERSAMPLE_RATE));
151     } else {
152         snd_platform_update_bits(platform, SUNXI_DMIC_CTR,
153             (1<<DMIC_OVERSAMPLE_RATE), (0<<DMIC_OVERSAMPLE_RATE));
154     }
155 
156     sunxi_dmic->chanmap = (1<<params_channels(params)) - 1;
157 
158     snd_platform_write(platform, SUNXI_DMIC_HPF_CTRL, sunxi_dmic->chanmap);
159 
160     /* DMIC num is M+1 */
161     snd_platform_update_bits(platform, SUNXI_DMIC_CH_NUM,
162         (7<<DMIC_CH_NUM), ((params_channels(params)-1)<<DMIC_CH_NUM));
163 
164     return 0;
165 }
166 
sunxi_dmic_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_dai * dai)167 static int sunxi_dmic_trigger(struct snd_pcm_substream *substream,
168                 int cmd, struct snd_dai *dai)
169 {
170     int ret = 0;
171     struct snd_platform *platform = dai->component;
172 
173     snd_print("\n");
174     switch (cmd) {
175     case    SNDRV_PCM_TRIGGER_START:
176     case    SNDRV_PCM_TRIGGER_RESUME:
177     case    SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
178         sunxi_dmic_enable(platform, true);
179         break;
180     case    SNDRV_PCM_TRIGGER_STOP:
181     case    SNDRV_PCM_TRIGGER_SUSPEND:
182     case    SNDRV_PCM_TRIGGER_PAUSE_PUSH:
183         sunxi_dmic_enable(platform, false);
184         break;
185     default:
186         ret = -EINVAL;
187         break;
188     }
189 
190     return ret;
191 }
192 
193 /*
194  * Reset & Flush FIFO
195  */
sunxi_dmic_prepare(struct snd_pcm_substream * substream,struct snd_dai * dai)196 static int sunxi_dmic_prepare(struct snd_pcm_substream *substream,
197             struct snd_dai *dai)
198 {
199     struct snd_platform *platform = dai->component;
200 
201     snd_print("\n");
202     snd_platform_update_bits(platform, SUNXI_DMIC_FIFO_CTR,
203             (1<<DMIC_FIFO_FLUSH), (1<<DMIC_FIFO_FLUSH));
204 
205     snd_platform_write(platform, SUNXI_DMIC_INTS,
206         (1<<FIFO_OVERRUN_IRQ_PENDING) | (1<<FIFO_DATA_IRQ_PENDING));
207 
208     snd_platform_write(platform, SUNXI_DMIC_CNT, 0x0);
209 
210     return 0;
211 }
212 
sunxi_dmic_startup(struct snd_pcm_substream * substream,struct snd_dai * dai)213 static int sunxi_dmic_startup(struct snd_pcm_substream *substream,
214             struct snd_dai *dai)
215 {
216     struct snd_platform *platform = dai->component;
217     struct sunxi_dmic_info *sunxi_dmic = platform->private_data;
218 
219     snd_print("\n");
220     dai->capture_dma_data = &sunxi_dmic->capture_dma_param;
221 
222     return 0;
223 }
224 
sunxi_dmic_shutdown(struct snd_pcm_substream * substream,struct snd_dai * dai)225 static void sunxi_dmic_shutdown(struct snd_pcm_substream *substream,
226             struct snd_dai *dai)
227 {
228     snd_print("\n");
229 }
230 
sunxi_dmic_set_sysclk(struct snd_dai * dai,int clk_id,unsigned int freq,int dir)231 static int sunxi_dmic_set_sysclk(struct snd_dai *dai, int clk_id,
232                         unsigned int freq, int dir)
233 {
234     struct snd_platform *platform = dai->component;
235     struct sunxi_dmic_info *sunxi_dmic = platform->private_data;
236 
237     snd_print("\n");
238     if (hal_clk_set_rate(sunxi_dmic->pllclk, freq)) {
239         snd_err("set pllclk %u failed\n", freq);
240         return -EINVAL;
241     }
242 
243     return 0;
244 }
245 
246 /* Dmic module init status */
sunxi_dmic_dai_probe(struct snd_dai * dai)247 static int sunxi_dmic_dai_probe(struct snd_dai *dai)
248 {
249     struct snd_platform *platform = dai->component;
250 
251     snd_print("\n");
252     dai->capture_dma_data = 0;
253     sunxi_dmic_init(platform);
254 
255     return 0;
256 }
257 
258 static struct snd_dai_ops sunxi_dmic_dai_ops = {
259     .startup = sunxi_dmic_startup,
260     .trigger = sunxi_dmic_trigger,
261     .prepare = sunxi_dmic_prepare,
262     .hw_params = sunxi_dmic_hw_params,
263     .set_sysclk = sunxi_dmic_set_sysclk,
264     .shutdown = sunxi_dmic_shutdown,
265 };
266 
267 static struct snd_dai sunxi_dmic_dai = {
268     .id     = 1,
269     .name       = "sunxi-dmic-cpudai",
270     .capture    = {
271         .stream_name    = "Capture",
272         .channels_min   = 1,
273         .channels_max   = 8,
274         .rates      = SUNXI_DMIC_RATES,
275         .formats    = SNDRV_PCM_FMTBIT_S16_LE
276                 | SNDRV_PCM_FMTBIT_S24_LE
277                 | SNDRV_PCM_FMTBIT_S32_LE,
278         .rate_min   = 8000,
279         .rate_max   = 48000,
280     },
281     .probe      = sunxi_dmic_dai_probe,
282     .ops        = &sunxi_dmic_dai_ops,
283 };
284 
sunxi_dmic_clk_init(struct snd_platform * platform)285 static int sunxi_dmic_clk_init(struct snd_platform *platform)
286 {
287     struct sunxi_dmic_info *sunxi_dmic = platform->private_data;
288     hal_reset_type_t reset_type = HAL_SUNXI_RESET;
289     hal_clk_type_t clk_type = HAL_SUNXI_CCU;
290     int ret;
291 
292     snd_print("\n");
293     sunxi_dmic->pllclk = hal_clock_get(clk_type, SUNXI_DMIC_CLK_PLL_AUDIO);
294     sunxi_dmic->moduleclk = hal_clock_get(clk_type, SUNXI_DMIC_CLK_DMIC);
295     sunxi_dmic->busclk = hal_clock_get(clk_type, SUNXI_DMIC_CLK_BUS);
296     sunxi_dmic->rstclk = hal_reset_control_get(reset_type, SUNXI_DMIC_CLK_RST);
297 
298     ret = hal_clk_set_parent(sunxi_dmic->moduleclk, sunxi_dmic->pllclk);
299     if (ret != HAL_CLK_STATUS_OK) {
300         snd_err("sunxi_dmic clk_set_parent failed.\n");
301         goto err_dmic_moduleclk_set_parent;
302     }
303 
304     ret = hal_reset_control_deassert(sunxi_dmic->rstclk);
305     if (ret != HAL_CLK_STATUS_OK) {
306         snd_err("dmic clk_deassert rstclk failed.\n");
307         goto err_dmic_rstclk_deassert;
308     }
309 
310     ret = hal_clock_enable(sunxi_dmic->busclk);
311     if (ret != HAL_CLK_STATUS_OK) {
312         snd_err("dmic clk_enable busclk failed.\n");
313         goto err_dmic_busclk_enable;
314     }
315     ret = hal_clock_enable(sunxi_dmic->pllclk);
316     if (ret != HAL_CLK_STATUS_OK) {
317         snd_err("dmic clk_enable pllclk failed.\n");
318         goto err_dmic_pllclk_enable;
319     }
320     ret = hal_clock_enable(sunxi_dmic->moduleclk);
321     if (ret != HAL_CLK_STATUS_OK) {
322         snd_err("dmic clk_enable moduleclk failed.\n");
323         goto err_dmic_moduleclk_enable;
324     }
325 
326     return 0;
327 
328 err_dmic_moduleclk_enable:
329     hal_clock_disable(sunxi_dmic->pllclk);
330 err_dmic_pllclk_enable:
331 err_dmic_busclk_enable:
332 err_dmic_rstclk_deassert:
333 err_dmic_moduleclk_set_parent:
334     snd_free(sunxi_dmic);
335     return ret;
336 }
337 
sunxi_dmic_gpio_init(bool enable)338 static int sunxi_dmic_gpio_init(bool enable)
339 {
340     snd_print("\n");
341     if (enable) {
342         /*CLK*/
343         hal_gpio_pinmux_set_function(g_dmic_gpio.clk.gpio,
344                     g_dmic_gpio.clk.mux);
345         /*DATA0*/
346         hal_gpio_pinmux_set_function(g_dmic_gpio.din0.gpio,
347                     g_dmic_gpio.din0.mux);
348         /*DATA1*/
349         hal_gpio_pinmux_set_function(g_dmic_gpio.din1.gpio,
350                     g_dmic_gpio.din1.mux);
351         /*DATA2*/
352         hal_gpio_pinmux_set_function(g_dmic_gpio.din2.gpio,
353                     g_dmic_gpio.din2.mux);
354         /*DATA3*/
355         hal_gpio_pinmux_set_function(g_dmic_gpio.din3.gpio,
356                     g_dmic_gpio.din3.mux);
357     } else {
358         /*CLK*/
359         hal_gpio_pinmux_set_function(g_dmic_gpio.clk.gpio,
360                     GPIO_MUXSEL_DISABLED);
361         /*DATA0*/
362         hal_gpio_pinmux_set_function(g_dmic_gpio.din0.gpio,
363                     GPIO_MUXSEL_DISABLED);
364         /*DATA1*/
365         hal_gpio_pinmux_set_function(g_dmic_gpio.din1.gpio,
366                     GPIO_MUXSEL_DISABLED);
367         /*DATA2*/
368         hal_gpio_pinmux_set_function(g_dmic_gpio.din2.gpio,
369                     GPIO_MUXSEL_DISABLED);
370         /*DATA3*/
371         hal_gpio_pinmux_set_function(g_dmic_gpio.din3.gpio,
372                     GPIO_MUXSEL_DISABLED);
373     }
374 
375     return 0;
376 }
377 
sunxi_dmic_platform_probe(struct snd_platform * platform)378 static int sunxi_dmic_platform_probe(struct snd_platform *platform)
379 {
380     struct sunxi_dmic_info *sunxi_dmic;
381 
382     snd_print("\n");
383     sunxi_dmic = snd_malloc(sizeof(struct sunxi_dmic_info));
384     if (!sunxi_dmic) {
385         snd_err("no memory\n");
386         return -ENOMEM;
387     }
388     platform->private_data = (void *)sunxi_dmic;
389     platform->cpu_dai->component = platform;
390 
391     /* mem base */
392     platform->mem_base = (void *)SUNXI_DMIC_MEMBASE;
393 
394     /* clk */
395     sunxi_dmic_clk_init(platform);
396 
397     /* pinctrl */
398     sunxi_dmic_gpio_init(true);
399 
400     /* dma config */
401     sunxi_dmic->capture_dma_param.src_maxburst = 8;
402     sunxi_dmic->capture_dma_param.dst_maxburst = 8;
403     sunxi_dmic->capture_dma_param.dma_addr =
404             (dma_addr_t)platform->mem_base + SUNXI_DMIC_DATA;
405     sunxi_dmic->capture_dma_param.dma_drq_type_num = DRQSRC_DMIC;
406 
407     return 0;
408 }
409 
sunxi_dmic_platform_remove(struct snd_platform * platform)410 static int sunxi_dmic_platform_remove(struct snd_platform *platform)
411 {
412     struct sunxi_dmic_info *sunxi_dmic;
413 
414     snd_print("\n");
415     sunxi_dmic = platform->private_data;
416     if (!sunxi_dmic)
417         return 0;
418 
419     hal_clock_disable(sunxi_dmic->busclk);
420     hal_clock_disable(sunxi_dmic->moduleclk);
421     hal_clock_disable(sunxi_dmic->pllclk);
422     hal_clock_put(sunxi_dmic->busclk);
423     hal_clock_put(sunxi_dmic->moduleclk);
424     hal_clock_put(sunxi_dmic->pllclk);
425 
426     hal_reset_control_assert(sunxi_dmic->rstclk);
427     hal_reset_control_put(sunxi_dmic->rstclk);
428 
429     sunxi_dmic_gpio_init(false);
430 
431     snd_free(sunxi_dmic);
432     platform->private_data = NULL;
433 
434     return 0;
435 }
436 
snd_platform_dmic_register(struct snd_platform * platform,int unused)437 int snd_platform_dmic_register(struct snd_platform *platform, int unused)
438 {
439     int ret;
440 
441     snd_print("\n");
442     platform->name = snd_malloc(DMIC_NAME_LEN);
443     if (!platform->name) {
444         snd_err("no memory\n");
445         return -ENOMEM;
446     }
447     snprintf(platform->name, DMIC_NAME_LEN, "dmic-cpudai");
448     platform->ops = &sunxi_pcm_ops;
449     platform->pcm_new = sunxi_pcm_new;
450     platform->pcm_free = sunxi_pcm_free;
451 
452     platform->cpu_dai = snd_malloc(sizeof(struct snd_dai));
453     if (!platform->cpu_dai) {
454         snd_err("cpu_dai malloc failed.\n");
455         ret = -ENOMEM;
456         goto err_cpu_dai_malloc;
457     }
458     memcpy(platform->cpu_dai, &sunxi_dmic_dai, sizeof(struct snd_dai));
459 
460     platform->probe = sunxi_dmic_platform_probe;
461     platform->remove = sunxi_dmic_platform_remove;
462 
463     return 0;
464 
465 err_cpu_dai_malloc:
466     snd_free(platform->name);
467     platform->name = NULL;
468     return ret;
469 }
470 
471 #ifdef SUNXI_DMIC_DEBUG_REG
472 #define REG_LABEL(constant)     {#constant, constant}
473 #define REG_LABEL_END           {NULL, 0}
474 static struct dmic_label {
475     const char *name;
476     const unsigned int address;
477     /*int value;*/
478 } reg_labels[] = {
479     REG_LABEL(SUNXI_DMIC_EN),
480     REG_LABEL(SUNXI_DMIC_SR),
481     REG_LABEL(SUNXI_DMIC_CTR),
482     REG_LABEL(SUNXI_DMIC_INTC),
483     REG_LABEL(SUNXI_DMIC_INTS),
484     REG_LABEL(SUNXI_DMIC_FIFO_CTR),
485     REG_LABEL(SUNXI_DMIC_FIFO_STA),
486     REG_LABEL(SUNXI_DMIC_CH_NUM),
487     REG_LABEL(SUNXI_DMIC_CH_MAP),
488     REG_LABEL(SUNXI_DMIC_CNT),
489     REG_LABEL(SUNXI_DMIC_DATA0_1_VOL),
490     REG_LABEL(SUNXI_DMIC_DATA2_3_VOL),
491     REG_LABEL(SUNXI_DMIC_HPF_CTRL),
492     REG_LABEL(SUNXI_DMIC_HPF_COEF),
493     REG_LABEL(SUNXI_DMIC_HPF_GAIN),
494     REG_LABEL_END,
495 };
496 
497 /* for debug */
498 #include <console.h>
cmd_dmic_dump(void)499 int cmd_dmic_dump(void)
500 {
501     int dmic_num = 0;
502     void *membase;
503     int i = 0;
504 
505     membase = (void *)SUNXI_DMIC_MEMBASE ;
506 
507     while (reg_labels[i].name != NULL) {
508         printf("%-20s[0x%03x]: 0x%-10x\n",
509             reg_labels[i].name,
510             reg_labels[i].address,
511             snd_readl(membase + reg_labels[i].address));
512         i++;
513     }
514 }
515 FINSH_FUNCTION_EXPORT_CMD(cmd_dmic_dump, dmic, dmic dump reg);
516 #endif /* SUNXI_DMIC_DEBUG_REG */
517