2 * Copyright 2014 Emilio López <emilio@elopez.com.ar>
3 * Copyright 2014 Jon Smirl <jonsmirl@gmail.com>
4 * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
5 * Copyright 2015 Adam Sampson <ats@offog.org>
6 * Copyright 2016 Chen-Yu Tsai <wens@csie.org>
8 * Based on the Allwinner SDK driver, released under the GPL.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include <linux/init.h>
22 #include <linux/kernel.h>
23 #include <linux/module.h>
24 #include <linux/platform_device.h>
25 #include <linux/delay.h>
26 #include <linux/slab.h>
28 #include <linux/of_address.h>
29 #include <linux/of_device.h>
30 #include <linux/of_platform.h>
31 #include <linux/clk.h>
32 #include <linux/regmap.h>
33 #include <linux/reset.h>
34 #include <linux/gpio/consumer.h>
36 #include <sound/core.h>
37 #include <sound/pcm.h>
38 #include <sound/pcm_params.h>
39 #include <sound/soc.h>
40 #include <sound/tlv.h>
41 #include <sound/initval.h>
42 #include <sound/dmaengine_pcm.h>
44 /* Codec DAC digital controls and FIFO registers */
45 #define SUN4I_CODEC_DAC_DPC (0x00)
46 #define SUN4I_CODEC_DAC_DPC_EN_DA (31)
47 #define SUN4I_CODEC_DAC_DPC_DVOL (12)
48 #define SUN4I_CODEC_DAC_FIFOC (0x04)
49 #define SUN4I_CODEC_DAC_FIFOC_DAC_FS (29)
50 #define SUN4I_CODEC_DAC_FIFOC_FIR_VERSION (28)
51 #define SUN4I_CODEC_DAC_FIFOC_SEND_LASAT (26)
52 #define SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE (24)
53 #define SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT (21)
54 #define SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL (8)
55 #define SUN4I_CODEC_DAC_FIFOC_MONO_EN (6)
56 #define SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS (5)
57 #define SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN (4)
58 #define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH (0)
59 #define SUN4I_CODEC_DAC_FIFOS (0x08)
60 #define SUN4I_CODEC_DAC_TXDATA (0x0c)
62 /* Codec DAC side analog signal controls */
63 #define SUN4I_CODEC_DAC_ACTL (0x10)
64 #define SUN4I_CODEC_DAC_ACTL_DACAENR (31)
65 #define SUN4I_CODEC_DAC_ACTL_DACAENL (30)
66 #define SUN4I_CODEC_DAC_ACTL_MIXEN (29)
67 #define SUN4I_CODEC_DAC_ACTL_MICG (20)
68 #define SUN4I_CODEC_DAC_ACTL_LDACLMIXS (15)
69 #define SUN4I_CODEC_DAC_ACTL_RDACRMIXS (14)
70 #define SUN4I_CODEC_DAC_ACTL_LDACRMIXS (13)
71 #define SUN4I_CODEC_DAC_ACTL_MIC1LS (12)
72 #define SUN4I_CODEC_DAC_ACTL_MIC1RS (11)
73 #define SUN4I_CODEC_DAC_ACTL_MIC2LS (10)
74 #define SUN4I_CODEC_DAC_ACTL_MIC2RS (9)
75 #define SUN4I_CODEC_DAC_ACTL_DACPAS (8)
76 #define SUN4I_CODEC_DAC_ACTL_MIXPAS (7)
77 #define SUN4I_CODEC_DAC_ACTL_PA_MUTE (6)
78 #define SUN4I_CODEC_DAC_ACTL_PA_VOL (0)
79 #define SUN4I_CODEC_DAC_TUNE (0x14)
80 #define SUN4I_CODEC_DAC_DEBUG (0x18)
82 /* Codec ADC digital controls and FIFO registers */
83 #define SUN4I_CODEC_ADC_FIFOC (0x1c)
84 #define SUN4I_CODEC_ADC_FIFOC_ADC_FS (29)
85 #define SUN4I_CODEC_ADC_FIFOC_EN_AD (28)
86 #define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE (24)
87 #define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL (8)
88 #define SUN4I_CODEC_ADC_FIFOC_MONO_EN (7)
89 #define SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS (6)
90 #define SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN (4)
91 #define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH (0)
92 #define SUN4I_CODEC_ADC_FIFOS (0x20)
93 #define SUN4I_CODEC_ADC_RXDATA (0x24)
95 /* Codec ADC side analog signal controls */
96 #define SUN4I_CODEC_ADC_ACTL (0x28)
97 #define SUN4I_CODEC_ADC_ACTL_ADC_R_EN (31)
98 #define SUN4I_CODEC_ADC_ACTL_ADC_L_EN (30)
99 #define SUN4I_CODEC_ADC_ACTL_PREG1EN (29)
100 #define SUN4I_CODEC_ADC_ACTL_PREG2EN (28)
101 #define SUN4I_CODEC_ADC_ACTL_VMICEN (27)
102 #define SUN4I_CODEC_ADC_ACTL_PREG1 (25)
103 #define SUN4I_CODEC_ADC_ACTL_PREG2 (23)
104 #define SUN4I_CODEC_ADC_ACTL_VADCG (20)
105 #define SUN4I_CODEC_ADC_ACTL_ADCIS (17)
106 #define SUN4I_CODEC_ADC_ACTL_PA_EN (4)
107 #define SUN4I_CODEC_ADC_ACTL_DDE (3)
108 #define SUN4I_CODEC_ADC_DEBUG (0x2c)
111 #define SUN4I_CODEC_DAC_TXCNT (0x30)
112 #define SUN4I_CODEC_ADC_RXCNT (0x34)
114 /* Calibration register (sun7i only) */
115 #define SUN7I_CODEC_AC_DAC_CAL (0x38)
117 /* Microphone controls (sun7i only) */
118 #define SUN7I_CODEC_AC_MIC_PHONE_CAL (0x3c)
120 #define SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG1 (29)
121 #define SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG2 (26)
124 * sun6i specific registers
126 * sun6i shares the same digital control and FIFO registers as sun4i,
127 * but only the DAC digital controls are at the same offset. The others
128 * have been moved around to accommodate extra analog controls.
131 /* Codec DAC digital controls and FIFO registers */
132 #define SUN6I_CODEC_ADC_FIFOC (0x10)
133 #define SUN6I_CODEC_ADC_FIFOC_EN_AD (28)
134 #define SUN6I_CODEC_ADC_FIFOS (0x14)
135 #define SUN6I_CODEC_ADC_RXDATA (0x18)
137 /* Output mixer and gain controls */
138 #define SUN6I_CODEC_OM_DACA_CTRL (0x20)
139 #define SUN6I_CODEC_OM_DACA_CTRL_DACAREN (31)
140 #define SUN6I_CODEC_OM_DACA_CTRL_DACALEN (30)
141 #define SUN6I_CODEC_OM_DACA_CTRL_RMIXEN (29)
142 #define SUN6I_CODEC_OM_DACA_CTRL_LMIXEN (28)
143 #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1 (23)
144 #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2 (22)
145 #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONE (21)
146 #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONEP (20)
147 #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR (19)
148 #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR (18)
149 #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL (17)
150 #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1 (16)
151 #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2 (15)
152 #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONE (14)
153 #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONEN (13)
154 #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL (12)
155 #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL (11)
156 #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR (10)
157 #define SUN6I_CODEC_OM_DACA_CTRL_RHPIS (9)
158 #define SUN6I_CODEC_OM_DACA_CTRL_LHPIS (8)
159 #define SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE (7)
160 #define SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE (6)
161 #define SUN6I_CODEC_OM_DACA_CTRL_HPVOL (0)
162 #define SUN6I_CODEC_OM_PA_CTRL (0x24)
163 #define SUN6I_CODEC_OM_PA_CTRL_HPPAEN (31)
164 #define SUN6I_CODEC_OM_PA_CTRL_HPCOM_CTL (29)
165 #define SUN6I_CODEC_OM_PA_CTRL_COMPTEN (28)
166 #define SUN6I_CODEC_OM_PA_CTRL_MIC1G (15)
167 #define SUN6I_CODEC_OM_PA_CTRL_MIC2G (12)
168 #define SUN6I_CODEC_OM_PA_CTRL_LINEING (9)
169 #define SUN6I_CODEC_OM_PA_CTRL_PHONEG (6)
170 #define SUN6I_CODEC_OM_PA_CTRL_PHONEPG (3)
171 #define SUN6I_CODEC_OM_PA_CTRL_PHONENG (0)
173 /* Microphone, line out and phone out controls */
174 #define SUN6I_CODEC_MIC_CTRL (0x28)
175 #define SUN6I_CODEC_MIC_CTRL_HBIASEN (31)
176 #define SUN6I_CODEC_MIC_CTRL_MBIASEN (30)
177 #define SUN6I_CODEC_MIC_CTRL_MIC1AMPEN (28)
178 #define SUN6I_CODEC_MIC_CTRL_MIC1BOOST (25)
179 #define SUN6I_CODEC_MIC_CTRL_MIC2AMPEN (24)
180 #define SUN6I_CODEC_MIC_CTRL_MIC2BOOST (21)
181 #define SUN6I_CODEC_MIC_CTRL_MIC2SLT (20)
182 #define SUN6I_CODEC_MIC_CTRL_LINEOUTLEN (19)
183 #define SUN6I_CODEC_MIC_CTRL_LINEOUTREN (18)
184 #define SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC (17)
185 #define SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC (16)
186 #define SUN6I_CODEC_MIC_CTRL_LINEOUTVC (11)
187 #define SUN6I_CODEC_MIC_CTRL_PHONEPREG (8)
189 /* ADC mixer controls */
190 #define SUN6I_CODEC_ADC_ACTL (0x2c)
191 #define SUN6I_CODEC_ADC_ACTL_ADCREN (31)
192 #define SUN6I_CODEC_ADC_ACTL_ADCLEN (30)
193 #define SUN6I_CODEC_ADC_ACTL_ADCRG (27)
194 #define SUN6I_CODEC_ADC_ACTL_ADCLG (24)
195 #define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1 (13)
196 #define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2 (12)
197 #define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONE (11)
198 #define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONEP (10)
199 #define SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR (9)
200 #define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR (8)
201 #define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL (7)
202 #define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1 (6)
203 #define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2 (5)
204 #define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONE (4)
205 #define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONEN (3)
206 #define SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL (2)
207 #define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL (1)
208 #define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR (0)
210 /* Analog performance tuning controls */
211 #define SUN6I_CODEC_ADDA_TUNE (0x30)
213 /* Calibration controls */
214 #define SUN6I_CODEC_CALIBRATION (0x34)
217 #define SUN6I_CODEC_DAC_TXCNT (0x40)
218 #define SUN6I_CODEC_ADC_RXCNT (0x44)
220 /* headset jack detection and button support registers */
221 #define SUN6I_CODEC_HMIC_CTL (0x50)
222 #define SUN6I_CODEC_HMIC_DATA (0x54)
224 /* TODO sun6i DAP (Digital Audio Processing) bits */
226 /* FIFO counters moved on A23 */
227 #define SUN8I_A23_CODEC_DAC_TXCNT (0x1c)
228 #define SUN8I_A23_CODEC_ADC_RXCNT (0x20)
230 /* TX FIFO moved on H3 */
231 #define SUN8I_H3_CODEC_DAC_TXDATA (0x20)
232 #define SUN8I_H3_CODEC_DAC_DBG (0x48)
233 #define SUN8I_H3_CODEC_ADC_DBG (0x4c)
235 /* TODO H3 DAP (Digital Audio Processing) bits */
239 struct regmap *regmap;
241 struct clk *clk_module;
242 struct reset_control *rst;
243 struct gpio_desc *gpio_pa;
245 /* ADC_FIFOC register is at different offset on different SoCs */
246 struct regmap_field *reg_adc_fifoc;
248 struct snd_dmaengine_dai_dma_data capture_dma_data;
249 struct snd_dmaengine_dai_dma_data playback_dma_data;
252 static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
255 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
256 BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
257 BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
260 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
261 BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
262 BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
265 static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
267 /* Disable DAC DRQ */
268 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
269 BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
273 static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
276 regmap_field_update_bits(scodec->reg_adc_fifoc,
277 BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN),
278 BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
281 static void sun4i_codec_stop_capture(struct sun4i_codec *scodec)
283 /* Disable ADC DRQ */
284 regmap_field_update_bits(scodec->reg_adc_fifoc,
285 BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0);
288 static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
289 struct snd_soc_dai *dai)
291 struct snd_soc_pcm_runtime *rtd = substream->private_data;
292 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
295 case SNDRV_PCM_TRIGGER_START:
296 case SNDRV_PCM_TRIGGER_RESUME:
297 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
298 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
299 sun4i_codec_start_playback(scodec);
301 sun4i_codec_start_capture(scodec);
304 case SNDRV_PCM_TRIGGER_STOP:
305 case SNDRV_PCM_TRIGGER_SUSPEND:
306 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
307 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
308 sun4i_codec_stop_playback(scodec);
310 sun4i_codec_stop_capture(scodec);
320 static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
321 struct snd_soc_dai *dai)
323 struct snd_soc_pcm_runtime *rtd = substream->private_data;
324 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
328 regmap_field_update_bits(scodec->reg_adc_fifoc,
329 BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH),
330 BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH));
333 /* Set RX FIFO trigger level */
334 regmap_field_update_bits(scodec->reg_adc_fifoc,
335 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
336 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL);
339 * FIXME: Undocumented in the datasheet, but
340 * Allwinner's code mentions that it is related
341 * related to microphone gain
343 if (of_device_is_compatible(scodec->dev->of_node,
344 "allwinner,sun4i-a10-codec") ||
345 of_device_is_compatible(scodec->dev->of_node,
346 "allwinner,sun7i-a20-codec")) {
347 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
352 if (of_device_is_compatible(scodec->dev->of_node,
353 "allwinner,sun7i-a20-codec"))
354 /* FIXME: Undocumented bits */
355 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_TUNE,
362 static int sun4i_codec_prepare_playback(struct snd_pcm_substream *substream,
363 struct snd_soc_dai *dai)
365 struct snd_soc_pcm_runtime *rtd = substream->private_data;
366 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
369 /* Flush the TX FIFO */
370 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
371 BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
372 BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
374 /* Set TX FIFO Empty Trigger Level */
375 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
376 0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL,
377 0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL);
379 if (substream->runtime->rate > 32000)
380 /* Use 64 bits FIR filter */
383 /* Use 32 bits FIR filter */
384 val = BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION);
386 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
387 BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION),
390 /* Send zeros when we have an underrun */
391 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
392 BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT),
398 static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
399 struct snd_soc_dai *dai)
401 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
402 return sun4i_codec_prepare_playback(substream, dai);
404 return sun4i_codec_prepare_capture(substream, dai);
407 static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params)
409 unsigned int rate = params_rate(params);
437 static int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
439 unsigned int rate = params_rate(params);
479 static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec,
480 struct snd_pcm_hw_params *params,
483 /* Set ADC sample rate */
484 regmap_field_update_bits(scodec->reg_adc_fifoc,
485 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS,
486 hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS);
488 /* Set the number of channels we want to use */
489 if (params_channels(params) == 1)
490 regmap_field_update_bits(scodec->reg_adc_fifoc,
491 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN),
492 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
494 regmap_field_update_bits(scodec->reg_adc_fifoc,
495 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN),
498 /* Set the number of sample bits to either 16 or 24 bits */
499 if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {
500 regmap_field_update_bits(scodec->reg_adc_fifoc,
501 BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS),
502 BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS));
504 regmap_field_update_bits(scodec->reg_adc_fifoc,
505 BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE),
508 scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
510 regmap_field_update_bits(scodec->reg_adc_fifoc,
511 BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS),
514 /* Fill most significant bits with valid data MSB */
515 regmap_field_update_bits(scodec->reg_adc_fifoc,
516 BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE),
517 BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
519 scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
525 static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
526 struct snd_pcm_hw_params *params,
531 /* Set DAC sample rate */
532 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
533 7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS,
534 hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS);
536 /* Set the number of channels we want to use */
537 if (params_channels(params) == 1)
538 val = BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN);
542 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
543 BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN),
546 /* Set the number of sample bits to either 16 or 24 bits */
547 if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {
548 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
549 BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
550 BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
552 /* Set TX FIFO mode to padding the LSBs with 0 */
553 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
554 BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
557 scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
559 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
560 BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
563 /* Set TX FIFO mode to repeat the MSB */
564 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
565 BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
566 BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
568 scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
574 static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
575 struct snd_pcm_hw_params *params,
576 struct snd_soc_dai *dai)
578 struct snd_soc_pcm_runtime *rtd = substream->private_data;
579 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
580 unsigned long clk_freq;
583 clk_freq = sun4i_codec_get_mod_freq(params);
587 ret = clk_set_rate(scodec->clk_module, clk_freq);
591 hwrate = sun4i_codec_get_hw_rate(params);
595 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
596 return sun4i_codec_hw_params_playback(scodec, params,
599 return sun4i_codec_hw_params_capture(scodec, params,
604 static unsigned int sun4i_codec_src_rates[] = {
605 8000, 11025, 12000, 16000, 22050, 24000, 32000,
606 44100, 48000, 96000, 192000
610 static struct snd_pcm_hw_constraint_list sun4i_codec_constraints = {
611 .count = ARRAY_SIZE(sun4i_codec_src_rates),
612 .list = sun4i_codec_src_rates,
616 static int sun4i_codec_startup(struct snd_pcm_substream *substream,
617 struct snd_soc_dai *dai)
619 struct snd_soc_pcm_runtime *rtd = substream->private_data;
620 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
622 snd_pcm_hw_constraint_list(substream->runtime, 0,
623 SNDRV_PCM_HW_PARAM_RATE, &sun4i_codec_constraints);
626 * Stop issuing DRQ when we have room for less than 16 samples
629 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
630 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT,
631 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT);
633 return clk_prepare_enable(scodec->clk_module);
636 static void sun4i_codec_shutdown(struct snd_pcm_substream *substream,
637 struct snd_soc_dai *dai)
639 struct snd_soc_pcm_runtime *rtd = substream->private_data;
640 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
642 clk_disable_unprepare(scodec->clk_module);
645 static const struct snd_soc_dai_ops sun4i_codec_dai_ops = {
646 .startup = sun4i_codec_startup,
647 .shutdown = sun4i_codec_shutdown,
648 .trigger = sun4i_codec_trigger,
649 .hw_params = sun4i_codec_hw_params,
650 .prepare = sun4i_codec_prepare,
653 static struct snd_soc_dai_driver sun4i_codec_dai = {
655 .ops = &sun4i_codec_dai_ops,
657 .stream_name = "Codec Playback",
662 .rates = SNDRV_PCM_RATE_CONTINUOUS,
663 .formats = SNDRV_PCM_FMTBIT_S16_LE |
664 SNDRV_PCM_FMTBIT_S32_LE,
668 .stream_name = "Codec Capture",
673 .rates = SNDRV_PCM_RATE_CONTINUOUS,
674 .formats = SNDRV_PCM_FMTBIT_S16_LE |
675 SNDRV_PCM_FMTBIT_S32_LE,
680 /*** sun4i Codec ***/
681 static const struct snd_kcontrol_new sun4i_codec_pa_mute =
682 SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL,
683 SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0);
685 static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1);
686 static DECLARE_TLV_DB_SCALE(sun4i_codec_micin_loopback_gain_scale, -450, 150,
688 static DECLARE_TLV_DB_RANGE(sun4i_codec_micin_preamp_gain_scale,
689 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
690 1, 7, TLV_DB_SCALE_ITEM(3500, 300, 0));
691 static DECLARE_TLV_DB_RANGE(sun7i_codec_micin_preamp_gain_scale,
692 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
693 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0));
695 static const struct snd_kcontrol_new sun4i_codec_controls[] = {
696 SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL,
697 SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,
698 sun4i_codec_pa_volume_scale),
699 SOC_SINGLE_TLV("Mic Playback Volume", SUN4I_CODEC_DAC_ACTL,
700 SUN4I_CODEC_DAC_ACTL_MICG, 7, 0,
701 sun4i_codec_micin_loopback_gain_scale),
702 SOC_SINGLE_TLV("Mic1 Boost Volume", SUN4I_CODEC_ADC_ACTL,
703 SUN4I_CODEC_ADC_ACTL_PREG1, 3, 0,
704 sun4i_codec_micin_preamp_gain_scale),
705 SOC_SINGLE_TLV("Mic2 Boost Volume", SUN4I_CODEC_ADC_ACTL,
706 SUN4I_CODEC_ADC_ACTL_PREG2, 3, 0,
707 sun4i_codec_micin_preamp_gain_scale),
710 static const struct snd_kcontrol_new sun7i_codec_controls[] = {
711 SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL,
712 SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,
713 sun4i_codec_pa_volume_scale),
714 SOC_SINGLE_TLV("Mic Playback Volume", SUN4I_CODEC_DAC_ACTL,
715 SUN4I_CODEC_DAC_ACTL_MICG, 7, 0,
716 sun4i_codec_micin_loopback_gain_scale),
717 SOC_SINGLE_TLV("Mic1 Boost Volume", SUN7I_CODEC_AC_MIC_PHONE_CAL,
718 SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG1, 7, 0,
719 sun7i_codec_micin_preamp_gain_scale),
720 SOC_SINGLE_TLV("Mic2 Boost Volume", SUN7I_CODEC_AC_MIC_PHONE_CAL,
721 SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG2, 7, 0,
722 sun7i_codec_micin_preamp_gain_scale),
725 static const struct snd_kcontrol_new sun4i_codec_mixer_controls[] = {
726 SOC_DAPM_SINGLE("Left Mixer Left DAC Playback Switch",
727 SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_LDACLMIXS,
729 SOC_DAPM_SINGLE("Right Mixer Right DAC Playback Switch",
730 SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_RDACRMIXS,
732 SOC_DAPM_SINGLE("Right Mixer Left DAC Playback Switch",
733 SUN4I_CODEC_DAC_ACTL,
734 SUN4I_CODEC_DAC_ACTL_LDACRMIXS, 1, 0),
735 SOC_DAPM_DOUBLE("Mic1 Playback Switch", SUN4I_CODEC_DAC_ACTL,
736 SUN4I_CODEC_DAC_ACTL_MIC1LS,
737 SUN4I_CODEC_DAC_ACTL_MIC1RS, 1, 0),
738 SOC_DAPM_DOUBLE("Mic2 Playback Switch", SUN4I_CODEC_DAC_ACTL,
739 SUN4I_CODEC_DAC_ACTL_MIC2LS,
740 SUN4I_CODEC_DAC_ACTL_MIC2RS, 1, 0),
743 static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = {
744 SOC_DAPM_SINGLE("DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
745 SUN4I_CODEC_DAC_ACTL_DACPAS, 1, 0),
746 SOC_DAPM_SINGLE("Mixer Playback Switch", SUN4I_CODEC_DAC_ACTL,
747 SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0),
750 static const struct snd_soc_dapm_widget sun4i_codec_codec_dapm_widgets[] = {
751 /* Digital parts of the ADCs */
752 SND_SOC_DAPM_SUPPLY("ADC", SUN4I_CODEC_ADC_FIFOC,
753 SUN4I_CODEC_ADC_FIFOC_EN_AD, 0,
756 /* Digital parts of the DACs */
757 SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC,
758 SUN4I_CODEC_DAC_DPC_EN_DA, 0,
761 /* Analog parts of the ADCs */
762 SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,
763 SUN4I_CODEC_ADC_ACTL_ADC_L_EN, 0),
764 SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,
765 SUN4I_CODEC_ADC_ACTL_ADC_R_EN, 0),
767 /* Analog parts of the DACs */
768 SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
769 SUN4I_CODEC_DAC_ACTL_DACAENL, 0),
770 SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
771 SUN4I_CODEC_DAC_ACTL_DACAENR, 0),
774 SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
775 sun4i_codec_mixer_controls,
776 ARRAY_SIZE(sun4i_codec_mixer_controls)),
777 SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
778 sun4i_codec_mixer_controls,
779 ARRAY_SIZE(sun4i_codec_mixer_controls)),
781 /* Global Mixer Enable */
782 SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL,
783 SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0),
786 SND_SOC_DAPM_SUPPLY("VMIC", SUN4I_CODEC_ADC_ACTL,
787 SUN4I_CODEC_ADC_ACTL_VMICEN, 0, NULL, 0),
789 /* Mic Pre-Amplifiers */
790 SND_SOC_DAPM_PGA("MIC1 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,
791 SUN4I_CODEC_ADC_ACTL_PREG1EN, 0, NULL, 0),
792 SND_SOC_DAPM_PGA("MIC2 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,
793 SUN4I_CODEC_ADC_ACTL_PREG2EN, 0, NULL, 0),
795 /* Power Amplifier */
796 SND_SOC_DAPM_MIXER("Power Amplifier", SUN4I_CODEC_ADC_ACTL,
797 SUN4I_CODEC_ADC_ACTL_PA_EN, 0,
798 sun4i_codec_pa_mixer_controls,
799 ARRAY_SIZE(sun4i_codec_pa_mixer_controls)),
800 SND_SOC_DAPM_SWITCH("Power Amplifier Mute", SND_SOC_NOPM, 0, 0,
801 &sun4i_codec_pa_mute),
803 SND_SOC_DAPM_INPUT("Mic1"),
804 SND_SOC_DAPM_INPUT("Mic2"),
806 SND_SOC_DAPM_OUTPUT("HP Right"),
807 SND_SOC_DAPM_OUTPUT("HP Left"),
810 static const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = {
811 /* Left ADC / DAC Routes */
812 { "Left ADC", NULL, "ADC" },
813 { "Left DAC", NULL, "DAC" },
815 /* Right ADC / DAC Routes */
816 { "Right ADC", NULL, "ADC" },
817 { "Right DAC", NULL, "DAC" },
819 /* Right Mixer Routes */
820 { "Right Mixer", NULL, "Mixer Enable" },
821 { "Right Mixer", "Right Mixer Left DAC Playback Switch", "Left DAC" },
822 { "Right Mixer", "Right Mixer Right DAC Playback Switch", "Right DAC" },
823 { "Right Mixer", "Mic1 Playback Switch", "MIC1 Pre-Amplifier" },
824 { "Right Mixer", "Mic2 Playback Switch", "MIC2 Pre-Amplifier" },
826 /* Left Mixer Routes */
827 { "Left Mixer", NULL, "Mixer Enable" },
828 { "Left Mixer", "Left Mixer Left DAC Playback Switch", "Left DAC" },
829 { "Left Mixer", "Mic1 Playback Switch", "MIC1 Pre-Amplifier" },
830 { "Left Mixer", "Mic2 Playback Switch", "MIC2 Pre-Amplifier" },
832 /* Power Amplifier Routes */
833 { "Power Amplifier", "Mixer Playback Switch", "Left Mixer" },
834 { "Power Amplifier", "Mixer Playback Switch", "Right Mixer" },
835 { "Power Amplifier", "DAC Playback Switch", "Left DAC" },
836 { "Power Amplifier", "DAC Playback Switch", "Right DAC" },
838 /* Headphone Output Routes */
839 { "Power Amplifier Mute", "Switch", "Power Amplifier" },
840 { "HP Right", NULL, "Power Amplifier Mute" },
841 { "HP Left", NULL, "Power Amplifier Mute" },
844 { "Left ADC", NULL, "MIC1 Pre-Amplifier" },
845 { "Right ADC", NULL, "MIC1 Pre-Amplifier" },
846 { "MIC1 Pre-Amplifier", NULL, "Mic1"},
847 { "Mic1", NULL, "VMIC" },
850 { "Left ADC", NULL, "MIC2 Pre-Amplifier" },
851 { "Right ADC", NULL, "MIC2 Pre-Amplifier" },
852 { "MIC2 Pre-Amplifier", NULL, "Mic2"},
853 { "Mic2", NULL, "VMIC" },
856 static const struct snd_soc_component_driver sun4i_codec_codec = {
857 .controls = sun4i_codec_controls,
858 .num_controls = ARRAY_SIZE(sun4i_codec_controls),
859 .dapm_widgets = sun4i_codec_codec_dapm_widgets,
860 .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets),
861 .dapm_routes = sun4i_codec_codec_dapm_routes,
862 .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes),
864 .use_pmdown_time = 1,
866 .non_legacy_dai_naming = 1,
869 static const struct snd_soc_component_driver sun7i_codec_codec = {
870 .controls = sun7i_codec_controls,
871 .num_controls = ARRAY_SIZE(sun7i_codec_controls),
872 .dapm_widgets = sun4i_codec_codec_dapm_widgets,
873 .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets),
874 .dapm_routes = sun4i_codec_codec_dapm_routes,
875 .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes),
877 .use_pmdown_time = 1,
879 .non_legacy_dai_naming = 1,
882 /*** sun6i Codec ***/
885 static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {
886 SOC_DAPM_DOUBLE("DAC Playback Switch",
887 SUN6I_CODEC_OM_DACA_CTRL,
888 SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL,
889 SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR, 1, 0),
890 SOC_DAPM_DOUBLE("DAC Reversed Playback Switch",
891 SUN6I_CODEC_OM_DACA_CTRL,
892 SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR,
893 SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0),
894 SOC_DAPM_DOUBLE("Line In Playback Switch",
895 SUN6I_CODEC_OM_DACA_CTRL,
896 SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL,
897 SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR, 1, 0),
898 SOC_DAPM_DOUBLE("Mic1 Playback Switch",
899 SUN6I_CODEC_OM_DACA_CTRL,
900 SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1,
901 SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1, 1, 0),
902 SOC_DAPM_DOUBLE("Mic2 Playback Switch",
903 SUN6I_CODEC_OM_DACA_CTRL,
904 SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2,
905 SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2, 1, 0),
908 /* ADC mixer controls */
909 static const struct snd_kcontrol_new sun6i_codec_adc_mixer_controls[] = {
910 SOC_DAPM_DOUBLE("Mixer Capture Switch",
911 SUN6I_CODEC_ADC_ACTL,
912 SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL,
913 SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR, 1, 0),
914 SOC_DAPM_DOUBLE("Mixer Reversed Capture Switch",
915 SUN6I_CODEC_ADC_ACTL,
916 SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR,
917 SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL, 1, 0),
918 SOC_DAPM_DOUBLE("Line In Capture Switch",
919 SUN6I_CODEC_ADC_ACTL,
920 SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL,
921 SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR, 1, 0),
922 SOC_DAPM_DOUBLE("Mic1 Capture Switch",
923 SUN6I_CODEC_ADC_ACTL,
924 SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1,
925 SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1, 1, 0),
926 SOC_DAPM_DOUBLE("Mic2 Capture Switch",
927 SUN6I_CODEC_ADC_ACTL,
928 SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2,
929 SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2, 1, 0),
932 /* headphone controls */
933 static const char * const sun6i_codec_hp_src_enum_text[] = {
937 static SOC_ENUM_DOUBLE_DECL(sun6i_codec_hp_src_enum,
938 SUN6I_CODEC_OM_DACA_CTRL,
939 SUN6I_CODEC_OM_DACA_CTRL_LHPIS,
940 SUN6I_CODEC_OM_DACA_CTRL_RHPIS,
941 sun6i_codec_hp_src_enum_text);
943 static const struct snd_kcontrol_new sun6i_codec_hp_src[] = {
944 SOC_DAPM_ENUM("Headphone Source Playback Route",
945 sun6i_codec_hp_src_enum),
948 /* microphone controls */
949 static const char * const sun6i_codec_mic2_src_enum_text[] = {
953 static SOC_ENUM_SINGLE_DECL(sun6i_codec_mic2_src_enum,
954 SUN6I_CODEC_MIC_CTRL,
955 SUN6I_CODEC_MIC_CTRL_MIC2SLT,
956 sun6i_codec_mic2_src_enum_text);
958 static const struct snd_kcontrol_new sun6i_codec_mic2_src[] = {
959 SOC_DAPM_ENUM("Mic2 Amplifier Source Route",
960 sun6i_codec_mic2_src_enum),
963 /* line out controls */
964 static const char * const sun6i_codec_lineout_src_enum_text[] = {
965 "Stereo", "Mono Differential",
968 static SOC_ENUM_DOUBLE_DECL(sun6i_codec_lineout_src_enum,
969 SUN6I_CODEC_MIC_CTRL,
970 SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC,
971 SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC,
972 sun6i_codec_lineout_src_enum_text);
974 static const struct snd_kcontrol_new sun6i_codec_lineout_src[] = {
975 SOC_DAPM_ENUM("Line Out Source Playback Route",
976 sun6i_codec_lineout_src_enum),
979 /* volume / mute controls */
980 static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0);
981 static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1);
982 static const DECLARE_TLV_DB_SCALE(sun6i_codec_out_mixer_pregain_scale,
984 static const DECLARE_TLV_DB_RANGE(sun6i_codec_lineout_vol_scale,
985 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
986 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
988 static const DECLARE_TLV_DB_RANGE(sun6i_codec_mic_gain_scale,
989 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
990 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
993 static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
994 SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
995 SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,
996 sun6i_codec_dvol_scale),
997 SOC_SINGLE_TLV("Headphone Playback Volume",
998 SUN6I_CODEC_OM_DACA_CTRL,
999 SUN6I_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0,
1000 sun6i_codec_hp_vol_scale),
1001 SOC_SINGLE_TLV("Line Out Playback Volume",
1002 SUN6I_CODEC_MIC_CTRL,
1003 SUN6I_CODEC_MIC_CTRL_LINEOUTVC, 0x1f, 0,
1004 sun6i_codec_lineout_vol_scale),
1005 SOC_DOUBLE("Headphone Playback Switch",
1006 SUN6I_CODEC_OM_DACA_CTRL,
1007 SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE,
1008 SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0),
1009 SOC_DOUBLE("Line Out Playback Switch",
1010 SUN6I_CODEC_MIC_CTRL,
1011 SUN6I_CODEC_MIC_CTRL_LINEOUTLEN,
1012 SUN6I_CODEC_MIC_CTRL_LINEOUTREN, 1, 0),
1013 /* Mixer pre-gains */
1014 SOC_SINGLE_TLV("Line In Playback Volume",
1015 SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_LINEING,
1016 0x7, 0, sun6i_codec_out_mixer_pregain_scale),
1017 SOC_SINGLE_TLV("Mic1 Playback Volume",
1018 SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC1G,
1019 0x7, 0, sun6i_codec_out_mixer_pregain_scale),
1020 SOC_SINGLE_TLV("Mic2 Playback Volume",
1021 SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC2G,
1022 0x7, 0, sun6i_codec_out_mixer_pregain_scale),
1024 /* Microphone Amp boost gains */
1025 SOC_SINGLE_TLV("Mic1 Boost Volume", SUN6I_CODEC_MIC_CTRL,
1026 SUN6I_CODEC_MIC_CTRL_MIC1BOOST, 0x7, 0,
1027 sun6i_codec_mic_gain_scale),
1028 SOC_SINGLE_TLV("Mic2 Boost Volume", SUN6I_CODEC_MIC_CTRL,
1029 SUN6I_CODEC_MIC_CTRL_MIC2BOOST, 0x7, 0,
1030 sun6i_codec_mic_gain_scale),
1031 SOC_DOUBLE_TLV("ADC Capture Volume",
1032 SUN6I_CODEC_ADC_ACTL, SUN6I_CODEC_ADC_ACTL_ADCLG,
1033 SUN6I_CODEC_ADC_ACTL_ADCRG, 0x7, 0,
1034 sun6i_codec_out_mixer_pregain_scale),
1037 static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
1038 /* Microphone inputs */
1039 SND_SOC_DAPM_INPUT("MIC1"),
1040 SND_SOC_DAPM_INPUT("MIC2"),
1041 SND_SOC_DAPM_INPUT("MIC3"),
1043 /* Microphone Bias */
1044 SND_SOC_DAPM_SUPPLY("HBIAS", SUN6I_CODEC_MIC_CTRL,
1045 SUN6I_CODEC_MIC_CTRL_HBIASEN, 0, NULL, 0),
1046 SND_SOC_DAPM_SUPPLY("MBIAS", SUN6I_CODEC_MIC_CTRL,
1047 SUN6I_CODEC_MIC_CTRL_MBIASEN, 0, NULL, 0),
1049 /* Mic input path */
1050 SND_SOC_DAPM_MUX("Mic2 Amplifier Source Route",
1051 SND_SOC_NOPM, 0, 0, sun6i_codec_mic2_src),
1052 SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN6I_CODEC_MIC_CTRL,
1053 SUN6I_CODEC_MIC_CTRL_MIC1AMPEN, 0, NULL, 0),
1054 SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN6I_CODEC_MIC_CTRL,
1055 SUN6I_CODEC_MIC_CTRL_MIC2AMPEN, 0, NULL, 0),
1058 SND_SOC_DAPM_INPUT("LINEIN"),
1060 /* Digital parts of the ADCs */
1061 SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC,
1062 SUN6I_CODEC_ADC_FIFOC_EN_AD, 0,
1065 /* Analog parts of the ADCs */
1066 SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL,
1067 SUN6I_CODEC_ADC_ACTL_ADCLEN, 0),
1068 SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL,
1069 SUN6I_CODEC_ADC_ACTL_ADCREN, 0),
1072 SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
1073 sun6i_codec_adc_mixer_controls),
1074 SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
1075 sun6i_codec_adc_mixer_controls),
1077 /* Digital parts of the DACs */
1078 SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
1079 SUN4I_CODEC_DAC_DPC_EN_DA, 0,
1082 /* Analog parts of the DACs */
1083 SND_SOC_DAPM_DAC("Left DAC", "Codec Playback",
1084 SUN6I_CODEC_OM_DACA_CTRL,
1085 SUN6I_CODEC_OM_DACA_CTRL_DACALEN, 0),
1086 SND_SOC_DAPM_DAC("Right DAC", "Codec Playback",
1087 SUN6I_CODEC_OM_DACA_CTRL,
1088 SUN6I_CODEC_OM_DACA_CTRL_DACAREN, 0),
1091 SOC_MIXER_ARRAY("Left Mixer", SUN6I_CODEC_OM_DACA_CTRL,
1092 SUN6I_CODEC_OM_DACA_CTRL_LMIXEN, 0,
1093 sun6i_codec_mixer_controls),
1094 SOC_MIXER_ARRAY("Right Mixer", SUN6I_CODEC_OM_DACA_CTRL,
1095 SUN6I_CODEC_OM_DACA_CTRL_RMIXEN, 0,
1096 sun6i_codec_mixer_controls),
1098 /* Headphone output path */
1099 SND_SOC_DAPM_MUX("Headphone Source Playback Route",
1100 SND_SOC_NOPM, 0, 0, sun6i_codec_hp_src),
1101 SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN6I_CODEC_OM_PA_CTRL,
1102 SUN6I_CODEC_OM_PA_CTRL_HPPAEN, 0, NULL, 0),
1103 SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN6I_CODEC_OM_PA_CTRL,
1104 SUN6I_CODEC_OM_PA_CTRL_COMPTEN, 0, NULL, 0),
1105 SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN6I_CODEC_OM_PA_CTRL,
1106 SUN6I_CODEC_OM_PA_CTRL_HPCOM_CTL, 0x3, 0x3, 0),
1107 SND_SOC_DAPM_OUTPUT("HP"),
1110 SND_SOC_DAPM_MUX("Line Out Source Playback Route",
1111 SND_SOC_NOPM, 0, 0, sun6i_codec_lineout_src),
1112 SND_SOC_DAPM_OUTPUT("LINEOUT"),
1115 static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
1117 { "Left DAC", NULL, "DAC Enable" },
1118 { "Right DAC", NULL, "DAC Enable" },
1120 /* Microphone Routes */
1121 { "Mic1 Amplifier", NULL, "MIC1"},
1122 { "Mic2 Amplifier Source Route", "Mic2", "MIC2" },
1123 { "Mic2 Amplifier Source Route", "Mic3", "MIC3" },
1124 { "Mic2 Amplifier", NULL, "Mic2 Amplifier Source Route"},
1126 /* Left Mixer Routes */
1127 { "Left Mixer", "DAC Playback Switch", "Left DAC" },
1128 { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
1129 { "Left Mixer", "Line In Playback Switch", "LINEIN" },
1130 { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
1131 { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
1133 /* Right Mixer Routes */
1134 { "Right Mixer", "DAC Playback Switch", "Right DAC" },
1135 { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
1136 { "Right Mixer", "Line In Playback Switch", "LINEIN" },
1137 { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
1138 { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
1140 /* Left ADC Mixer Routes */
1141 { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
1142 { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
1143 { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
1144 { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
1145 { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
1147 /* Right ADC Mixer Routes */
1148 { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
1149 { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
1150 { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
1151 { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
1152 { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
1154 /* Headphone Routes */
1155 { "Headphone Source Playback Route", "DAC", "Left DAC" },
1156 { "Headphone Source Playback Route", "DAC", "Right DAC" },
1157 { "Headphone Source Playback Route", "Mixer", "Left Mixer" },
1158 { "Headphone Source Playback Route", "Mixer", "Right Mixer" },
1159 { "Headphone Amp", NULL, "Headphone Source Playback Route" },
1160 { "HP", NULL, "Headphone Amp" },
1161 { "HPCOM", NULL, "HPCOM Protection" },
1163 /* Line Out Routes */
1164 { "Line Out Source Playback Route", "Stereo", "Left Mixer" },
1165 { "Line Out Source Playback Route", "Stereo", "Right Mixer" },
1166 { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
1167 { "Line Out Source Playback Route", "Mono Differential", "Right Mixer" },
1168 { "LINEOUT", NULL, "Line Out Source Playback Route" },
1171 { "Left ADC", NULL, "ADC Enable" },
1172 { "Right ADC", NULL, "ADC Enable" },
1173 { "Left ADC", NULL, "Left ADC Mixer" },
1174 { "Right ADC", NULL, "Right ADC Mixer" },
1177 static const struct snd_soc_component_driver sun6i_codec_codec = {
1178 .controls = sun6i_codec_codec_widgets,
1179 .num_controls = ARRAY_SIZE(sun6i_codec_codec_widgets),
1180 .dapm_widgets = sun6i_codec_codec_dapm_widgets,
1181 .num_dapm_widgets = ARRAY_SIZE(sun6i_codec_codec_dapm_widgets),
1182 .dapm_routes = sun6i_codec_codec_dapm_routes,
1183 .num_dapm_routes = ARRAY_SIZE(sun6i_codec_codec_dapm_routes),
1185 .use_pmdown_time = 1,
1187 .non_legacy_dai_naming = 1,
1190 /* sun8i A23 codec */
1191 static const struct snd_kcontrol_new sun8i_a23_codec_codec_controls[] = {
1192 SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
1193 SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,
1194 sun6i_codec_dvol_scale),
1197 static const struct snd_soc_dapm_widget sun8i_a23_codec_codec_widgets[] = {
1198 /* Digital parts of the ADCs */
1199 SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC,
1200 SUN6I_CODEC_ADC_FIFOC_EN_AD, 0, NULL, 0),
1201 /* Digital parts of the DACs */
1202 SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
1203 SUN4I_CODEC_DAC_DPC_EN_DA, 0, NULL, 0),
1207 static const struct snd_soc_component_driver sun8i_a23_codec_codec = {
1208 .controls = sun8i_a23_codec_codec_controls,
1209 .num_controls = ARRAY_SIZE(sun8i_a23_codec_codec_controls),
1210 .dapm_widgets = sun8i_a23_codec_codec_widgets,
1211 .num_dapm_widgets = ARRAY_SIZE(sun8i_a23_codec_codec_widgets),
1213 .use_pmdown_time = 1,
1215 .non_legacy_dai_naming = 1,
1218 static const struct snd_soc_component_driver sun4i_codec_component = {
1219 .name = "sun4i-codec",
1222 #define SUN4I_CODEC_RATES SNDRV_PCM_RATE_CONTINUOUS
1223 #define SUN4I_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
1224 SNDRV_PCM_FMTBIT_S32_LE)
1226 static int sun4i_codec_dai_probe(struct snd_soc_dai *dai)
1228 struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai);
1229 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
1231 snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data,
1232 &scodec->capture_dma_data);
1237 static struct snd_soc_dai_driver dummy_cpu_dai = {
1238 .name = "sun4i-codec-cpu-dai",
1239 .probe = sun4i_codec_dai_probe,
1241 .stream_name = "Playback",
1244 .rates = SUN4I_CODEC_RATES,
1245 .formats = SUN4I_CODEC_FORMATS,
1249 .stream_name = "Capture",
1252 .rates = SUN4I_CODEC_RATES,
1253 .formats = SUN4I_CODEC_FORMATS,
1258 static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
1261 struct snd_soc_dai_link *link = devm_kzalloc(dev, sizeof(*link),
1267 link->stream_name = "CDC PCM";
1268 link->codec_dai_name = "Codec";
1269 link->cpu_dai_name = dev_name(dev);
1270 link->codec_name = dev_name(dev);
1271 link->platform_name = dev_name(dev);
1272 link->dai_fmt = SND_SOC_DAIFMT_I2S;
1279 static int sun4i_codec_spk_event(struct snd_soc_dapm_widget *w,
1280 struct snd_kcontrol *k, int event)
1282 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(w->dapm->card);
1284 gpiod_set_value_cansleep(scodec->gpio_pa,
1285 !!SND_SOC_DAPM_EVENT_ON(event));
1290 static const struct snd_soc_dapm_widget sun4i_codec_card_dapm_widgets[] = {
1291 SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event),
1294 static const struct snd_soc_dapm_route sun4i_codec_card_dapm_routes[] = {
1295 { "Speaker", NULL, "HP Right" },
1296 { "Speaker", NULL, "HP Left" },
1299 static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
1301 struct snd_soc_card *card;
1303 card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
1305 return ERR_PTR(-ENOMEM);
1307 card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
1308 if (!card->dai_link)
1309 return ERR_PTR(-ENOMEM);
1312 card->name = "sun4i-codec";
1313 card->dapm_widgets = sun4i_codec_card_dapm_widgets;
1314 card->num_dapm_widgets = ARRAY_SIZE(sun4i_codec_card_dapm_widgets);
1315 card->dapm_routes = sun4i_codec_card_dapm_routes;
1316 card->num_dapm_routes = ARRAY_SIZE(sun4i_codec_card_dapm_routes);
1321 static const struct snd_soc_dapm_widget sun6i_codec_card_dapm_widgets[] = {
1322 SND_SOC_DAPM_HP("Headphone", NULL),
1323 SND_SOC_DAPM_LINE("Line In", NULL),
1324 SND_SOC_DAPM_LINE("Line Out", NULL),
1325 SND_SOC_DAPM_MIC("Headset Mic", NULL),
1326 SND_SOC_DAPM_MIC("Mic", NULL),
1327 SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event),
1330 static struct snd_soc_card *sun6i_codec_create_card(struct device *dev)
1332 struct snd_soc_card *card;
1335 card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
1337 return ERR_PTR(-ENOMEM);
1339 card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
1340 if (!card->dai_link)
1341 return ERR_PTR(-ENOMEM);
1344 card->name = "A31 Audio Codec";
1345 card->dapm_widgets = sun6i_codec_card_dapm_widgets;
1346 card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets);
1347 card->fully_routed = true;
1349 ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
1351 dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
1356 /* Connect digital side enables to analog side widgets */
1357 static const struct snd_soc_dapm_route sun8i_codec_card_routes[] = {
1359 { "Left ADC", NULL, "ADC Enable" },
1360 { "Right ADC", NULL, "ADC Enable" },
1361 { "Codec Capture", NULL, "Left ADC" },
1362 { "Codec Capture", NULL, "Right ADC" },
1365 { "Left DAC", NULL, "DAC Enable" },
1366 { "Right DAC", NULL, "DAC Enable" },
1367 { "Left DAC", NULL, "Codec Playback" },
1368 { "Right DAC", NULL, "Codec Playback" },
1371 static struct snd_soc_aux_dev aux_dev = {
1372 .name = "Codec Analog Controls",
1375 static struct snd_soc_card *sun8i_a23_codec_create_card(struct device *dev)
1377 struct snd_soc_card *card;
1380 card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
1382 return ERR_PTR(-ENOMEM);
1384 aux_dev.codec_of_node = of_parse_phandle(dev->of_node,
1385 "allwinner,codec-analog-controls",
1387 if (!aux_dev.codec_of_node) {
1388 dev_err(dev, "Can't find analog controls for codec.\n");
1389 return ERR_PTR(-EINVAL);
1392 card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
1393 if (!card->dai_link)
1394 return ERR_PTR(-ENOMEM);
1397 card->name = "A23 Audio Codec";
1398 card->dapm_widgets = sun6i_codec_card_dapm_widgets;
1399 card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets);
1400 card->dapm_routes = sun8i_codec_card_routes;
1401 card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes);
1402 card->aux_dev = &aux_dev;
1403 card->num_aux_devs = 1;
1404 card->fully_routed = true;
1406 ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
1408 dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
1413 static struct snd_soc_card *sun8i_h3_codec_create_card(struct device *dev)
1415 struct snd_soc_card *card;
1418 card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
1420 return ERR_PTR(-ENOMEM);
1422 aux_dev.codec_of_node = of_parse_phandle(dev->of_node,
1423 "allwinner,codec-analog-controls",
1425 if (!aux_dev.codec_of_node) {
1426 dev_err(dev, "Can't find analog controls for codec.\n");
1427 return ERR_PTR(-EINVAL);
1430 card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
1431 if (!card->dai_link)
1432 return ERR_PTR(-ENOMEM);
1435 card->name = "H3 Audio Codec";
1436 card->dapm_widgets = sun6i_codec_card_dapm_widgets;
1437 card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets);
1438 card->dapm_routes = sun8i_codec_card_routes;
1439 card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes);
1440 card->aux_dev = &aux_dev;
1441 card->num_aux_devs = 1;
1442 card->fully_routed = true;
1444 ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
1446 dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
1451 static struct snd_soc_card *sun8i_v3s_codec_create_card(struct device *dev)
1453 struct snd_soc_card *card;
1456 card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
1458 return ERR_PTR(-ENOMEM);
1460 aux_dev.codec_of_node = of_parse_phandle(dev->of_node,
1461 "allwinner,codec-analog-controls",
1463 if (!aux_dev.codec_of_node) {
1464 dev_err(dev, "Can't find analog controls for codec.\n");
1465 return ERR_PTR(-EINVAL);
1468 card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
1469 if (!card->dai_link)
1470 return ERR_PTR(-ENOMEM);
1473 card->name = "V3s Audio Codec";
1474 card->dapm_widgets = sun6i_codec_card_dapm_widgets;
1475 card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets);
1476 card->dapm_routes = sun8i_codec_card_routes;
1477 card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes);
1478 card->aux_dev = &aux_dev;
1479 card->num_aux_devs = 1;
1480 card->fully_routed = true;
1482 ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
1484 dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
1489 static const struct regmap_config sun4i_codec_regmap_config = {
1493 .max_register = SUN4I_CODEC_ADC_RXCNT,
1496 static const struct regmap_config sun6i_codec_regmap_config = {
1500 .max_register = SUN6I_CODEC_HMIC_DATA,
1503 static const struct regmap_config sun7i_codec_regmap_config = {
1507 .max_register = SUN7I_CODEC_AC_MIC_PHONE_CAL,
1510 static const struct regmap_config sun8i_a23_codec_regmap_config = {
1514 .max_register = SUN8I_A23_CODEC_ADC_RXCNT,
1517 static const struct regmap_config sun8i_h3_codec_regmap_config = {
1521 .max_register = SUN8I_H3_CODEC_ADC_DBG,
1524 static const struct regmap_config sun8i_v3s_codec_regmap_config = {
1528 .max_register = SUN8I_H3_CODEC_ADC_DBG,
1531 struct sun4i_codec_quirks {
1532 const struct regmap_config *regmap_config;
1533 const struct snd_soc_component_driver *codec;
1534 struct snd_soc_card * (*create_card)(struct device *dev);
1535 struct reg_field reg_adc_fifoc; /* used for regmap_field */
1536 unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */
1537 unsigned int reg_adc_rxdata; /* RX FIFO offset for DMA config */
1541 static const struct sun4i_codec_quirks sun4i_codec_quirks = {
1542 .regmap_config = &sun4i_codec_regmap_config,
1543 .codec = &sun4i_codec_codec,
1544 .create_card = sun4i_codec_create_card,
1545 .reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31),
1546 .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
1547 .reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA,
1550 static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = {
1551 .regmap_config = &sun6i_codec_regmap_config,
1552 .codec = &sun6i_codec_codec,
1553 .create_card = sun6i_codec_create_card,
1554 .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
1555 .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
1556 .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
1560 static const struct sun4i_codec_quirks sun7i_codec_quirks = {
1561 .regmap_config = &sun7i_codec_regmap_config,
1562 .codec = &sun7i_codec_codec,
1563 .create_card = sun4i_codec_create_card,
1564 .reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31),
1565 .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
1566 .reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA,
1569 static const struct sun4i_codec_quirks sun8i_a23_codec_quirks = {
1570 .regmap_config = &sun8i_a23_codec_regmap_config,
1571 .codec = &sun8i_a23_codec_codec,
1572 .create_card = sun8i_a23_codec_create_card,
1573 .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
1574 .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
1575 .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
1579 static const struct sun4i_codec_quirks sun8i_h3_codec_quirks = {
1580 .regmap_config = &sun8i_h3_codec_regmap_config,
1582 * TODO Share the codec structure with A23 for now.
1583 * This should be split out when adding digital audio
1584 * processing support for the H3.
1586 .codec = &sun8i_a23_codec_codec,
1587 .create_card = sun8i_h3_codec_create_card,
1588 .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
1589 .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA,
1590 .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
1594 static const struct sun4i_codec_quirks sun8i_v3s_codec_quirks = {
1595 .regmap_config = &sun8i_v3s_codec_regmap_config,
1597 * TODO The codec structure should be split out, like
1598 * H3, when adding digital audio processing support.
1600 .codec = &sun8i_a23_codec_codec,
1601 .create_card = sun8i_v3s_codec_create_card,
1602 .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
1603 .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA,
1604 .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
1608 static const struct of_device_id sun4i_codec_of_match[] = {
1610 .compatible = "allwinner,sun4i-a10-codec",
1611 .data = &sun4i_codec_quirks,
1614 .compatible = "allwinner,sun6i-a31-codec",
1615 .data = &sun6i_a31_codec_quirks,
1618 .compatible = "allwinner,sun7i-a20-codec",
1619 .data = &sun7i_codec_quirks,
1622 .compatible = "allwinner,sun8i-a23-codec",
1623 .data = &sun8i_a23_codec_quirks,
1626 .compatible = "allwinner,sun8i-h3-codec",
1627 .data = &sun8i_h3_codec_quirks,
1630 .compatible = "allwinner,sun8i-v3s-codec",
1631 .data = &sun8i_v3s_codec_quirks,
1635 MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
1637 static int sun4i_codec_probe(struct platform_device *pdev)
1639 struct snd_soc_card *card;
1640 struct sun4i_codec *scodec;
1641 const struct sun4i_codec_quirks *quirks;
1642 struct resource *res;
1646 scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
1650 scodec->dev = &pdev->dev;
1652 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1653 base = devm_ioremap_resource(&pdev->dev, res);
1655 dev_err(&pdev->dev, "Failed to map the registers\n");
1656 return PTR_ERR(base);
1659 quirks = of_device_get_match_data(&pdev->dev);
1660 if (quirks == NULL) {
1661 dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
1665 scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
1666 quirks->regmap_config);
1667 if (IS_ERR(scodec->regmap)) {
1668 dev_err(&pdev->dev, "Failed to create our regmap\n");
1669 return PTR_ERR(scodec->regmap);
1672 /* Get the clocks from the DT */
1673 scodec->clk_apb = devm_clk_get(&pdev->dev, "apb");
1674 if (IS_ERR(scodec->clk_apb)) {
1675 dev_err(&pdev->dev, "Failed to get the APB clock\n");
1676 return PTR_ERR(scodec->clk_apb);
1679 scodec->clk_module = devm_clk_get(&pdev->dev, "codec");
1680 if (IS_ERR(scodec->clk_module)) {
1681 dev_err(&pdev->dev, "Failed to get the module clock\n");
1682 return PTR_ERR(scodec->clk_module);
1685 if (quirks->has_reset) {
1686 scodec->rst = devm_reset_control_get_exclusive(&pdev->dev,
1688 if (IS_ERR(scodec->rst)) {
1689 dev_err(&pdev->dev, "Failed to get reset control\n");
1690 return PTR_ERR(scodec->rst);
1694 scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa",
1696 if (IS_ERR(scodec->gpio_pa)) {
1697 ret = PTR_ERR(scodec->gpio_pa);
1698 if (ret != -EPROBE_DEFER)
1699 dev_err(&pdev->dev, "Failed to get pa gpio: %d\n", ret);
1703 /* reg_field setup */
1704 scodec->reg_adc_fifoc = devm_regmap_field_alloc(&pdev->dev,
1706 quirks->reg_adc_fifoc);
1707 if (IS_ERR(scodec->reg_adc_fifoc)) {
1708 ret = PTR_ERR(scodec->reg_adc_fifoc);
1709 dev_err(&pdev->dev, "Failed to create regmap fields: %d\n",
1714 /* Enable the bus clock */
1715 if (clk_prepare_enable(scodec->clk_apb)) {
1716 dev_err(&pdev->dev, "Failed to enable the APB clock\n");
1720 /* Deassert the reset control */
1722 ret = reset_control_deassert(scodec->rst);
1725 "Failed to deassert the reset control\n");
1726 goto err_clk_disable;
1730 /* DMA configuration for TX FIFO */
1731 scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata;
1732 scodec->playback_dma_data.maxburst = 8;
1733 scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
1735 /* DMA configuration for RX FIFO */
1736 scodec->capture_dma_data.addr = res->start + quirks->reg_adc_rxdata;
1737 scodec->capture_dma_data.maxburst = 8;
1738 scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
1740 ret = devm_snd_soc_register_component(&pdev->dev, quirks->codec,
1741 &sun4i_codec_dai, 1);
1743 dev_err(&pdev->dev, "Failed to register our codec\n");
1744 goto err_assert_reset;
1747 ret = devm_snd_soc_register_component(&pdev->dev,
1748 &sun4i_codec_component,
1751 dev_err(&pdev->dev, "Failed to register our DAI\n");
1752 goto err_assert_reset;
1755 ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
1757 dev_err(&pdev->dev, "Failed to register against DMAEngine\n");
1758 goto err_assert_reset;
1761 card = quirks->create_card(&pdev->dev);
1763 ret = PTR_ERR(card);
1764 dev_err(&pdev->dev, "Failed to create our card\n");
1765 goto err_assert_reset;
1768 snd_soc_card_set_drvdata(card, scodec);
1770 ret = snd_soc_register_card(card);
1772 dev_err(&pdev->dev, "Failed to register our card\n");
1773 goto err_assert_reset;
1780 reset_control_assert(scodec->rst);
1782 clk_disable_unprepare(scodec->clk_apb);
1786 static int sun4i_codec_remove(struct platform_device *pdev)
1788 struct snd_soc_card *card = platform_get_drvdata(pdev);
1789 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
1791 snd_soc_unregister_card(card);
1793 reset_control_assert(scodec->rst);
1794 clk_disable_unprepare(scodec->clk_apb);
1799 static struct platform_driver sun4i_codec_driver = {
1801 .name = "sun4i-codec",
1802 .of_match_table = sun4i_codec_of_match,
1804 .probe = sun4i_codec_probe,
1805 .remove = sun4i_codec_remove,
1807 module_platform_driver(sun4i_codec_driver);
1809 MODULE_DESCRIPTION("Allwinner A10 codec driver");
1810 MODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>");
1811 MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
1812 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
1813 MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
1814 MODULE_LICENSE("GPL");