1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 2014 Emilio López <emilio@elopez.com.ar> |
4 | * Copyright 2014 Jon Smirl <jonsmirl@gmail.com> |
5 | * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com> |
6 | * Copyright 2015 Adam Sampson <ats@offog.org> |
7 | * Copyright 2016 Chen-Yu Tsai <wens@csie.org> |
8 | * |
9 | * Based on the Allwinner SDK driver, released under the GPL. |
10 | */ |
11 | |
12 | #include <linux/init.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/module.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/delay.h> |
17 | #include <linux/slab.h> |
18 | #include <linux/clk.h> |
19 | #include <linux/regmap.h> |
20 | #include <linux/reset.h> |
21 | #include <linux/gpio/consumer.h> |
22 | |
23 | #include <sound/core.h> |
24 | #include <sound/pcm.h> |
25 | #include <sound/pcm_params.h> |
26 | #include <sound/soc.h> |
27 | #include <sound/tlv.h> |
28 | #include <sound/initval.h> |
29 | #include <sound/dmaengine_pcm.h> |
30 | |
31 | /* Codec DAC digital controls and FIFO registers */ |
32 | #define SUN4I_CODEC_DAC_DPC (0x00) |
33 | #define SUN4I_CODEC_DAC_DPC_EN_DA (31) |
34 | #define SUN4I_CODEC_DAC_DPC_DVOL (12) |
35 | #define SUN4I_CODEC_DAC_FIFOC (0x04) |
36 | #define SUN4I_CODEC_DAC_FIFOC_DAC_FS (29) |
37 | #define SUN4I_CODEC_DAC_FIFOC_FIR_VERSION (28) |
38 | #define SUN4I_CODEC_DAC_FIFOC_SEND_LASAT (26) |
39 | #define SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE (24) |
40 | #define SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT (21) |
41 | #define SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL (8) |
42 | #define SUN4I_CODEC_DAC_FIFOC_MONO_EN (6) |
43 | #define SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS (5) |
44 | #define SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN (4) |
45 | #define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH (0) |
46 | #define SUN4I_CODEC_DAC_FIFOS (0x08) |
47 | #define SUN4I_CODEC_DAC_TXDATA (0x0c) |
48 | |
49 | /* Codec DAC side analog signal controls */ |
50 | #define SUN4I_CODEC_DAC_ACTL (0x10) |
51 | #define SUN4I_CODEC_DAC_ACTL_DACAENR (31) |
52 | #define SUN4I_CODEC_DAC_ACTL_DACAENL (30) |
53 | #define SUN4I_CODEC_DAC_ACTL_MIXEN (29) |
54 | #define SUN4I_CODEC_DAC_ACTL_LNG (26) |
55 | #define SUN4I_CODEC_DAC_ACTL_FMG (23) |
56 | #define SUN4I_CODEC_DAC_ACTL_MICG (20) |
57 | #define SUN4I_CODEC_DAC_ACTL_LLNS (19) |
58 | #define SUN4I_CODEC_DAC_ACTL_RLNS (18) |
59 | #define SUN4I_CODEC_DAC_ACTL_LFMS (17) |
60 | #define SUN4I_CODEC_DAC_ACTL_RFMS (16) |
61 | #define SUN4I_CODEC_DAC_ACTL_LDACLMIXS (15) |
62 | #define SUN4I_CODEC_DAC_ACTL_RDACRMIXS (14) |
63 | #define SUN4I_CODEC_DAC_ACTL_LDACRMIXS (13) |
64 | #define SUN4I_CODEC_DAC_ACTL_MIC1LS (12) |
65 | #define SUN4I_CODEC_DAC_ACTL_MIC1RS (11) |
66 | #define SUN4I_CODEC_DAC_ACTL_MIC2LS (10) |
67 | #define SUN4I_CODEC_DAC_ACTL_MIC2RS (9) |
68 | #define SUN4I_CODEC_DAC_ACTL_DACPAS (8) |
69 | #define SUN4I_CODEC_DAC_ACTL_MIXPAS (7) |
70 | #define SUN4I_CODEC_DAC_ACTL_PA_MUTE (6) |
71 | #define SUN4I_CODEC_DAC_ACTL_PA_VOL (0) |
72 | #define SUN4I_CODEC_DAC_TUNE (0x14) |
73 | #define SUN4I_CODEC_DAC_DEBUG (0x18) |
74 | |
75 | /* Codec ADC digital controls and FIFO registers */ |
76 | #define SUN4I_CODEC_ADC_FIFOC (0x1c) |
77 | #define SUN4I_CODEC_ADC_FIFOC_ADC_FS (29) |
78 | #define SUN4I_CODEC_ADC_FIFOC_EN_AD (28) |
79 | #define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE (24) |
80 | #define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL (8) |
81 | #define SUN4I_CODEC_ADC_FIFOC_MONO_EN (7) |
82 | #define SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS (6) |
83 | #define SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN (4) |
84 | #define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH (0) |
85 | #define SUN4I_CODEC_ADC_FIFOS (0x20) |
86 | #define SUN4I_CODEC_ADC_RXDATA (0x24) |
87 | |
88 | /* Codec ADC side analog signal controls */ |
89 | #define SUN4I_CODEC_ADC_ACTL (0x28) |
90 | #define SUN4I_CODEC_ADC_ACTL_ADC_R_EN (31) |
91 | #define SUN4I_CODEC_ADC_ACTL_ADC_L_EN (30) |
92 | #define SUN4I_CODEC_ADC_ACTL_PREG1EN (29) |
93 | #define SUN4I_CODEC_ADC_ACTL_PREG2EN (28) |
94 | #define SUN4I_CODEC_ADC_ACTL_VMICEN (27) |
95 | #define SUN4I_CODEC_ADC_ACTL_PREG1 (25) |
96 | #define SUN4I_CODEC_ADC_ACTL_PREG2 (23) |
97 | #define SUN4I_CODEC_ADC_ACTL_VADCG (20) |
98 | #define SUN4I_CODEC_ADC_ACTL_ADCIS (17) |
99 | #define SUN4I_CODEC_ADC_ACTL_LNPREG (13) |
100 | #define SUN4I_CODEC_ADC_ACTL_PA_EN (4) |
101 | #define SUN4I_CODEC_ADC_ACTL_DDE (3) |
102 | #define SUN4I_CODEC_ADC_DEBUG (0x2c) |
103 | |
104 | /* FIFO counters */ |
105 | #define SUN4I_CODEC_DAC_TXCNT (0x30) |
106 | #define SUN4I_CODEC_ADC_RXCNT (0x34) |
107 | |
108 | /* Calibration register (sun7i only) */ |
109 | #define SUN7I_CODEC_AC_DAC_CAL (0x38) |
110 | |
111 | /* Microphone controls (sun7i only) */ |
112 | #define SUN7I_CODEC_AC_MIC_PHONE_CAL (0x3c) |
113 | |
114 | #define SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG1 (29) |
115 | #define SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG2 (26) |
116 | |
117 | /* |
118 | * sun6i specific registers |
119 | * |
120 | * sun6i shares the same digital control and FIFO registers as sun4i, |
121 | * but only the DAC digital controls are at the same offset. The others |
122 | * have been moved around to accommodate extra analog controls. |
123 | */ |
124 | |
125 | /* Codec DAC digital controls and FIFO registers */ |
126 | #define SUN6I_CODEC_ADC_FIFOC (0x10) |
127 | #define SUN6I_CODEC_ADC_FIFOC_EN_AD (28) |
128 | #define SUN6I_CODEC_ADC_FIFOS (0x14) |
129 | #define SUN6I_CODEC_ADC_RXDATA (0x18) |
130 | |
131 | /* Output mixer and gain controls */ |
132 | #define SUN6I_CODEC_OM_DACA_CTRL (0x20) |
133 | #define SUN6I_CODEC_OM_DACA_CTRL_DACAREN (31) |
134 | #define SUN6I_CODEC_OM_DACA_CTRL_DACALEN (30) |
135 | #define SUN6I_CODEC_OM_DACA_CTRL_RMIXEN (29) |
136 | #define SUN6I_CODEC_OM_DACA_CTRL_LMIXEN (28) |
137 | #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1 (23) |
138 | #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2 (22) |
139 | #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONE (21) |
140 | #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONEP (20) |
141 | #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR (19) |
142 | #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR (18) |
143 | #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL (17) |
144 | #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1 (16) |
145 | #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2 (15) |
146 | #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONE (14) |
147 | #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONEN (13) |
148 | #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL (12) |
149 | #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL (11) |
150 | #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR (10) |
151 | #define SUN6I_CODEC_OM_DACA_CTRL_RHPIS (9) |
152 | #define SUN6I_CODEC_OM_DACA_CTRL_LHPIS (8) |
153 | #define SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE (7) |
154 | #define SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE (6) |
155 | #define SUN6I_CODEC_OM_DACA_CTRL_HPVOL (0) |
156 | #define SUN6I_CODEC_OM_PA_CTRL (0x24) |
157 | #define SUN6I_CODEC_OM_PA_CTRL_HPPAEN (31) |
158 | #define SUN6I_CODEC_OM_PA_CTRL_HPCOM_CTL (29) |
159 | #define SUN6I_CODEC_OM_PA_CTRL_COMPTEN (28) |
160 | #define SUN6I_CODEC_OM_PA_CTRL_MIC1G (15) |
161 | #define SUN6I_CODEC_OM_PA_CTRL_MIC2G (12) |
162 | #define SUN6I_CODEC_OM_PA_CTRL_LINEING (9) |
163 | #define SUN6I_CODEC_OM_PA_CTRL_PHONEG (6) |
164 | #define SUN6I_CODEC_OM_PA_CTRL_PHONEPG (3) |
165 | #define SUN6I_CODEC_OM_PA_CTRL_PHONENG (0) |
166 | |
167 | /* Microphone, line out and phone out controls */ |
168 | #define SUN6I_CODEC_MIC_CTRL (0x28) |
169 | #define SUN6I_CODEC_MIC_CTRL_HBIASEN (31) |
170 | #define SUN6I_CODEC_MIC_CTRL_MBIASEN (30) |
171 | #define SUN6I_CODEC_MIC_CTRL_MIC1AMPEN (28) |
172 | #define SUN6I_CODEC_MIC_CTRL_MIC1BOOST (25) |
173 | #define SUN6I_CODEC_MIC_CTRL_MIC2AMPEN (24) |
174 | #define SUN6I_CODEC_MIC_CTRL_MIC2BOOST (21) |
175 | #define SUN6I_CODEC_MIC_CTRL_MIC2SLT (20) |
176 | #define SUN6I_CODEC_MIC_CTRL_LINEOUTLEN (19) |
177 | #define SUN6I_CODEC_MIC_CTRL_LINEOUTREN (18) |
178 | #define SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC (17) |
179 | #define SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC (16) |
180 | #define SUN6I_CODEC_MIC_CTRL_LINEOUTVC (11) |
181 | #define SUN6I_CODEC_MIC_CTRL_PHONEPREG (8) |
182 | |
183 | /* ADC mixer controls */ |
184 | #define SUN6I_CODEC_ADC_ACTL (0x2c) |
185 | #define SUN6I_CODEC_ADC_ACTL_ADCREN (31) |
186 | #define SUN6I_CODEC_ADC_ACTL_ADCLEN (30) |
187 | #define SUN6I_CODEC_ADC_ACTL_ADCRG (27) |
188 | #define SUN6I_CODEC_ADC_ACTL_ADCLG (24) |
189 | #define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1 (13) |
190 | #define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2 (12) |
191 | #define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONE (11) |
192 | #define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONEP (10) |
193 | #define SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR (9) |
194 | #define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR (8) |
195 | #define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL (7) |
196 | #define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1 (6) |
197 | #define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2 (5) |
198 | #define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONE (4) |
199 | #define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONEN (3) |
200 | #define SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL (2) |
201 | #define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL (1) |
202 | #define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR (0) |
203 | |
204 | /* Analog performance tuning controls */ |
205 | #define SUN6I_CODEC_ADDA_TUNE (0x30) |
206 | |
207 | /* Calibration controls */ |
208 | #define SUN6I_CODEC_CALIBRATION (0x34) |
209 | |
210 | /* FIFO counters */ |
211 | #define SUN6I_CODEC_DAC_TXCNT (0x40) |
212 | #define SUN6I_CODEC_ADC_RXCNT (0x44) |
213 | |
214 | /* headset jack detection and button support registers */ |
215 | #define SUN6I_CODEC_HMIC_CTL (0x50) |
216 | #define SUN6I_CODEC_HMIC_DATA (0x54) |
217 | |
218 | /* TODO sun6i DAP (Digital Audio Processing) bits */ |
219 | |
220 | /* FIFO counters moved on A23 */ |
221 | #define SUN8I_A23_CODEC_DAC_TXCNT (0x1c) |
222 | #define SUN8I_A23_CODEC_ADC_RXCNT (0x20) |
223 | |
224 | /* TX FIFO moved on H3 */ |
225 | #define SUN8I_H3_CODEC_DAC_TXDATA (0x20) |
226 | #define SUN8I_H3_CODEC_DAC_DBG (0x48) |
227 | #define SUN8I_H3_CODEC_ADC_DBG (0x4c) |
228 | |
229 | /* TODO H3 DAP (Digital Audio Processing) bits */ |
230 | |
231 | struct sun4i_codec { |
232 | struct device *dev; |
233 | struct regmap *regmap; |
234 | struct clk *clk_apb; |
235 | struct clk *clk_module; |
236 | struct reset_control *rst; |
237 | struct gpio_desc *gpio_pa; |
238 | |
239 | /* ADC_FIFOC register is at different offset on different SoCs */ |
240 | struct regmap_field *reg_adc_fifoc; |
241 | |
242 | struct snd_dmaengine_dai_dma_data capture_dma_data; |
243 | struct snd_dmaengine_dai_dma_data playback_dma_data; |
244 | }; |
245 | |
246 | static void sun4i_codec_start_playback(struct sun4i_codec *scodec) |
247 | { |
248 | /* Flush TX FIFO */ |
249 | regmap_set_bits(map: scodec->regmap, SUN4I_CODEC_DAC_FIFOC, |
250 | BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH)); |
251 | |
252 | /* Enable DAC DRQ */ |
253 | regmap_set_bits(map: scodec->regmap, SUN4I_CODEC_DAC_FIFOC, |
254 | BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN)); |
255 | } |
256 | |
257 | static void sun4i_codec_stop_playback(struct sun4i_codec *scodec) |
258 | { |
259 | /* Disable DAC DRQ */ |
260 | regmap_clear_bits(map: scodec->regmap, SUN4I_CODEC_DAC_FIFOC, |
261 | BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN)); |
262 | } |
263 | |
264 | static void sun4i_codec_start_capture(struct sun4i_codec *scodec) |
265 | { |
266 | /* Enable ADC DRQ */ |
267 | regmap_field_set_bits(field: scodec->reg_adc_fifoc, |
268 | BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN)); |
269 | } |
270 | |
271 | static void sun4i_codec_stop_capture(struct sun4i_codec *scodec) |
272 | { |
273 | /* Disable ADC DRQ */ |
274 | regmap_field_clear_bits(field: scodec->reg_adc_fifoc, |
275 | BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN)); |
276 | } |
277 | |
278 | static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd, |
279 | struct snd_soc_dai *dai) |
280 | { |
281 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
282 | struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card: rtd->card); |
283 | |
284 | switch (cmd) { |
285 | case SNDRV_PCM_TRIGGER_START: |
286 | case SNDRV_PCM_TRIGGER_RESUME: |
287 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
288 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
289 | sun4i_codec_start_playback(scodec); |
290 | else |
291 | sun4i_codec_start_capture(scodec); |
292 | break; |
293 | |
294 | case SNDRV_PCM_TRIGGER_STOP: |
295 | case SNDRV_PCM_TRIGGER_SUSPEND: |
296 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
297 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
298 | sun4i_codec_stop_playback(scodec); |
299 | else |
300 | sun4i_codec_stop_capture(scodec); |
301 | break; |
302 | |
303 | default: |
304 | return -EINVAL; |
305 | } |
306 | |
307 | return 0; |
308 | } |
309 | |
310 | static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream, |
311 | struct snd_soc_dai *dai) |
312 | { |
313 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
314 | struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card: rtd->card); |
315 | |
316 | |
317 | /* Flush RX FIFO */ |
318 | regmap_field_set_bits(field: scodec->reg_adc_fifoc, |
319 | BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH)); |
320 | |
321 | |
322 | /* Set RX FIFO trigger level */ |
323 | regmap_field_update_bits(field: scodec->reg_adc_fifoc, |
324 | mask: 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL, |
325 | val: 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL); |
326 | |
327 | /* |
328 | * FIXME: Undocumented in the datasheet, but |
329 | * Allwinner's code mentions that it is |
330 | * related to microphone gain |
331 | */ |
332 | if (of_device_is_compatible(device: scodec->dev->of_node, |
333 | "allwinner,sun4i-a10-codec" ) || |
334 | of_device_is_compatible(device: scodec->dev->of_node, |
335 | "allwinner,sun7i-a20-codec" )) { |
336 | regmap_update_bits(map: scodec->regmap, SUN4I_CODEC_ADC_ACTL, |
337 | mask: 0x3 << 25, |
338 | val: 0x1 << 25); |
339 | } |
340 | |
341 | if (of_device_is_compatible(device: scodec->dev->of_node, |
342 | "allwinner,sun7i-a20-codec" )) |
343 | /* FIXME: Undocumented bits */ |
344 | regmap_update_bits(map: scodec->regmap, SUN4I_CODEC_DAC_TUNE, |
345 | mask: 0x3 << 8, |
346 | val: 0x1 << 8); |
347 | |
348 | return 0; |
349 | } |
350 | |
351 | static int sun4i_codec_prepare_playback(struct snd_pcm_substream *substream, |
352 | struct snd_soc_dai *dai) |
353 | { |
354 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
355 | struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card: rtd->card); |
356 | u32 val; |
357 | |
358 | /* Flush the TX FIFO */ |
359 | regmap_set_bits(map: scodec->regmap, SUN4I_CODEC_DAC_FIFOC, |
360 | BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH)); |
361 | |
362 | /* Set TX FIFO Empty Trigger Level */ |
363 | regmap_update_bits(map: scodec->regmap, SUN4I_CODEC_DAC_FIFOC, |
364 | mask: 0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL, |
365 | val: 0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL); |
366 | |
367 | if (substream->runtime->rate > 32000) |
368 | /* Use 64 bits FIR filter */ |
369 | val = 0; |
370 | else |
371 | /* Use 32 bits FIR filter */ |
372 | val = BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION); |
373 | |
374 | regmap_update_bits(map: scodec->regmap, SUN4I_CODEC_DAC_FIFOC, |
375 | BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION), |
376 | val); |
377 | |
378 | /* Send zeros when we have an underrun */ |
379 | regmap_clear_bits(map: scodec->regmap, SUN4I_CODEC_DAC_FIFOC, |
380 | BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT)); |
381 | |
382 | return 0; |
383 | }; |
384 | |
385 | static int sun4i_codec_prepare(struct snd_pcm_substream *substream, |
386 | struct snd_soc_dai *dai) |
387 | { |
388 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
389 | return sun4i_codec_prepare_playback(substream, dai); |
390 | |
391 | return sun4i_codec_prepare_capture(substream, dai); |
392 | } |
393 | |
394 | static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params) |
395 | { |
396 | unsigned int rate = params_rate(p: params); |
397 | |
398 | switch (rate) { |
399 | case 176400: |
400 | case 88200: |
401 | case 44100: |
402 | case 33075: |
403 | case 22050: |
404 | case 14700: |
405 | case 11025: |
406 | case 7350: |
407 | return 22579200; |
408 | |
409 | case 192000: |
410 | case 96000: |
411 | case 48000: |
412 | case 32000: |
413 | case 24000: |
414 | case 16000: |
415 | case 12000: |
416 | case 8000: |
417 | return 24576000; |
418 | |
419 | default: |
420 | return 0; |
421 | } |
422 | } |
423 | |
424 | static int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params) |
425 | { |
426 | unsigned int rate = params_rate(p: params); |
427 | |
428 | switch (rate) { |
429 | case 192000: |
430 | case 176400: |
431 | return 6; |
432 | |
433 | case 96000: |
434 | case 88200: |
435 | return 7; |
436 | |
437 | case 48000: |
438 | case 44100: |
439 | return 0; |
440 | |
441 | case 32000: |
442 | case 33075: |
443 | return 1; |
444 | |
445 | case 24000: |
446 | case 22050: |
447 | return 2; |
448 | |
449 | case 16000: |
450 | case 14700: |
451 | return 3; |
452 | |
453 | case 12000: |
454 | case 11025: |
455 | return 4; |
456 | |
457 | case 8000: |
458 | case 7350: |
459 | return 5; |
460 | |
461 | default: |
462 | return -EINVAL; |
463 | } |
464 | } |
465 | |
466 | static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec, |
467 | struct snd_pcm_hw_params *params, |
468 | unsigned int hwrate) |
469 | { |
470 | /* Set ADC sample rate */ |
471 | regmap_field_update_bits(field: scodec->reg_adc_fifoc, |
472 | mask: 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS, |
473 | val: hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS); |
474 | |
475 | /* Set the number of channels we want to use */ |
476 | if (params_channels(p: params) == 1) |
477 | regmap_field_set_bits(field: scodec->reg_adc_fifoc, |
478 | BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN)); |
479 | else |
480 | regmap_field_clear_bits(field: scodec->reg_adc_fifoc, |
481 | BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN)); |
482 | |
483 | /* Set the number of sample bits to either 16 or 24 bits */ |
484 | if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) { |
485 | regmap_field_set_bits(field: scodec->reg_adc_fifoc, |
486 | BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS)); |
487 | |
488 | regmap_field_clear_bits(field: scodec->reg_adc_fifoc, |
489 | BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE)); |
490 | |
491 | scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
492 | } else { |
493 | regmap_field_clear_bits(field: scodec->reg_adc_fifoc, |
494 | BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS)); |
495 | |
496 | /* Fill most significant bits with valid data MSB */ |
497 | regmap_field_set_bits(field: scodec->reg_adc_fifoc, |
498 | BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE)); |
499 | |
500 | scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
501 | } |
502 | |
503 | return 0; |
504 | } |
505 | |
506 | static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec, |
507 | struct snd_pcm_hw_params *params, |
508 | unsigned int hwrate) |
509 | { |
510 | u32 val; |
511 | |
512 | /* Set DAC sample rate */ |
513 | regmap_update_bits(map: scodec->regmap, SUN4I_CODEC_DAC_FIFOC, |
514 | mask: 7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS, |
515 | val: hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS); |
516 | |
517 | /* Set the number of channels we want to use */ |
518 | if (params_channels(p: params) == 1) |
519 | val = BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN); |
520 | else |
521 | val = 0; |
522 | |
523 | regmap_update_bits(map: scodec->regmap, SUN4I_CODEC_DAC_FIFOC, |
524 | BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN), |
525 | val); |
526 | |
527 | /* Set the number of sample bits to either 16 or 24 bits */ |
528 | if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) { |
529 | regmap_set_bits(map: scodec->regmap, SUN4I_CODEC_DAC_FIFOC, |
530 | BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS)); |
531 | |
532 | /* Set TX FIFO mode to padding the LSBs with 0 */ |
533 | regmap_clear_bits(map: scodec->regmap, SUN4I_CODEC_DAC_FIFOC, |
534 | BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE)); |
535 | |
536 | scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
537 | } else { |
538 | regmap_clear_bits(map: scodec->regmap, SUN4I_CODEC_DAC_FIFOC, |
539 | BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS)); |
540 | |
541 | /* Set TX FIFO mode to repeat the MSB */ |
542 | regmap_set_bits(map: scodec->regmap, SUN4I_CODEC_DAC_FIFOC, |
543 | BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE)); |
544 | |
545 | scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
546 | } |
547 | |
548 | return 0; |
549 | } |
550 | |
551 | static int sun4i_codec_hw_params(struct snd_pcm_substream *substream, |
552 | struct snd_pcm_hw_params *params, |
553 | struct snd_soc_dai *dai) |
554 | { |
555 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
556 | struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card: rtd->card); |
557 | unsigned long clk_freq; |
558 | int ret, hwrate; |
559 | |
560 | clk_freq = sun4i_codec_get_mod_freq(params); |
561 | if (!clk_freq) |
562 | return -EINVAL; |
563 | |
564 | ret = clk_set_rate(clk: scodec->clk_module, rate: clk_freq); |
565 | if (ret) |
566 | return ret; |
567 | |
568 | hwrate = sun4i_codec_get_hw_rate(params); |
569 | if (hwrate < 0) |
570 | return hwrate; |
571 | |
572 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
573 | return sun4i_codec_hw_params_playback(scodec, params, |
574 | hwrate); |
575 | |
576 | return sun4i_codec_hw_params_capture(scodec, params, |
577 | hwrate); |
578 | } |
579 | |
580 | |
581 | static unsigned int sun4i_codec_src_rates[] = { |
582 | 8000, 11025, 12000, 16000, 22050, 24000, 32000, |
583 | 44100, 48000, 96000, 192000 |
584 | }; |
585 | |
586 | |
587 | static struct snd_pcm_hw_constraint_list sun4i_codec_constraints = { |
588 | .count = ARRAY_SIZE(sun4i_codec_src_rates), |
589 | .list = sun4i_codec_src_rates, |
590 | }; |
591 | |
592 | |
593 | static int sun4i_codec_startup(struct snd_pcm_substream *substream, |
594 | struct snd_soc_dai *dai) |
595 | { |
596 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
597 | struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card: rtd->card); |
598 | |
599 | snd_pcm_hw_constraint_list(runtime: substream->runtime, cond: 0, |
600 | SNDRV_PCM_HW_PARAM_RATE, l: &sun4i_codec_constraints); |
601 | |
602 | /* |
603 | * Stop issuing DRQ when we have room for less than 16 samples |
604 | * in our TX FIFO |
605 | */ |
606 | regmap_set_bits(map: scodec->regmap, SUN4I_CODEC_DAC_FIFOC, |
607 | bits: 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT); |
608 | |
609 | return clk_prepare_enable(clk: scodec->clk_module); |
610 | } |
611 | |
612 | static void sun4i_codec_shutdown(struct snd_pcm_substream *substream, |
613 | struct snd_soc_dai *dai) |
614 | { |
615 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
616 | struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card: rtd->card); |
617 | |
618 | clk_disable_unprepare(clk: scodec->clk_module); |
619 | } |
620 | |
621 | static const struct snd_soc_dai_ops sun4i_codec_dai_ops = { |
622 | .startup = sun4i_codec_startup, |
623 | .shutdown = sun4i_codec_shutdown, |
624 | .trigger = sun4i_codec_trigger, |
625 | .hw_params = sun4i_codec_hw_params, |
626 | .prepare = sun4i_codec_prepare, |
627 | }; |
628 | |
629 | static struct snd_soc_dai_driver sun4i_codec_dai = { |
630 | .name = "Codec" , |
631 | .ops = &sun4i_codec_dai_ops, |
632 | .playback = { |
633 | .stream_name = "Codec Playback" , |
634 | .channels_min = 1, |
635 | .channels_max = 2, |
636 | .rate_min = 8000, |
637 | .rate_max = 192000, |
638 | .rates = SNDRV_PCM_RATE_CONTINUOUS, |
639 | .formats = SNDRV_PCM_FMTBIT_S16_LE | |
640 | SNDRV_PCM_FMTBIT_S32_LE, |
641 | .sig_bits = 24, |
642 | }, |
643 | .capture = { |
644 | .stream_name = "Codec Capture" , |
645 | .channels_min = 1, |
646 | .channels_max = 2, |
647 | .rate_min = 8000, |
648 | .rate_max = 48000, |
649 | .rates = SNDRV_PCM_RATE_CONTINUOUS, |
650 | .formats = SNDRV_PCM_FMTBIT_S16_LE | |
651 | SNDRV_PCM_FMTBIT_S32_LE, |
652 | .sig_bits = 24, |
653 | }, |
654 | }; |
655 | |
656 | /*** sun4i Codec ***/ |
657 | static const struct snd_kcontrol_new sun4i_codec_pa_mute = |
658 | SOC_DAPM_SINGLE("Switch" , SUN4I_CODEC_DAC_ACTL, |
659 | SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0); |
660 | |
661 | static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1); |
662 | static DECLARE_TLV_DB_SCALE(sun4i_codec_linein_loopback_gain_scale, -150, 150, |
663 | 0); |
664 | static DECLARE_TLV_DB_SCALE(sun4i_codec_linein_preamp_gain_scale, -1200, 300, |
665 | 0); |
666 | static DECLARE_TLV_DB_SCALE(sun4i_codec_fmin_loopback_gain_scale, -450, 150, |
667 | 0); |
668 | static DECLARE_TLV_DB_SCALE(sun4i_codec_micin_loopback_gain_scale, -450, 150, |
669 | 0); |
670 | static DECLARE_TLV_DB_RANGE(sun4i_codec_micin_preamp_gain_scale, |
671 | 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), |
672 | 1, 7, TLV_DB_SCALE_ITEM(3500, 300, 0)); |
673 | static DECLARE_TLV_DB_RANGE(sun7i_codec_micin_preamp_gain_scale, |
674 | 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), |
675 | 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0)); |
676 | |
677 | static const struct snd_kcontrol_new sun4i_codec_controls[] = { |
678 | SOC_SINGLE_TLV("Power Amplifier Volume" , SUN4I_CODEC_DAC_ACTL, |
679 | SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0, |
680 | sun4i_codec_pa_volume_scale), |
681 | SOC_SINGLE_TLV("Line Playback Volume" , SUN4I_CODEC_DAC_ACTL, |
682 | SUN4I_CODEC_DAC_ACTL_LNG, 1, 0, |
683 | sun4i_codec_linein_loopback_gain_scale), |
684 | SOC_SINGLE_TLV("Line Boost Volume" , SUN4I_CODEC_ADC_ACTL, |
685 | SUN4I_CODEC_ADC_ACTL_LNPREG, 7, 0, |
686 | sun4i_codec_linein_preamp_gain_scale), |
687 | SOC_SINGLE_TLV("FM Playback Volume" , SUN4I_CODEC_DAC_ACTL, |
688 | SUN4I_CODEC_DAC_ACTL_FMG, 3, 0, |
689 | sun4i_codec_fmin_loopback_gain_scale), |
690 | SOC_SINGLE_TLV("Mic Playback Volume" , SUN4I_CODEC_DAC_ACTL, |
691 | SUN4I_CODEC_DAC_ACTL_MICG, 7, 0, |
692 | sun4i_codec_micin_loopback_gain_scale), |
693 | SOC_SINGLE_TLV("Mic1 Boost Volume" , SUN4I_CODEC_ADC_ACTL, |
694 | SUN4I_CODEC_ADC_ACTL_PREG1, 3, 0, |
695 | sun4i_codec_micin_preamp_gain_scale), |
696 | SOC_SINGLE_TLV("Mic2 Boost Volume" , SUN4I_CODEC_ADC_ACTL, |
697 | SUN4I_CODEC_ADC_ACTL_PREG2, 3, 0, |
698 | sun4i_codec_micin_preamp_gain_scale), |
699 | }; |
700 | |
701 | static const struct snd_kcontrol_new sun7i_codec_controls[] = { |
702 | SOC_SINGLE_TLV("Power Amplifier Volume" , SUN4I_CODEC_DAC_ACTL, |
703 | SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0, |
704 | sun4i_codec_pa_volume_scale), |
705 | SOC_SINGLE_TLV("Line Playback Volume" , SUN4I_CODEC_DAC_ACTL, |
706 | SUN4I_CODEC_DAC_ACTL_LNG, 1, 0, |
707 | sun4i_codec_linein_loopback_gain_scale), |
708 | SOC_SINGLE_TLV("Line Boost Volume" , SUN4I_CODEC_ADC_ACTL, |
709 | SUN4I_CODEC_ADC_ACTL_LNPREG, 7, 0, |
710 | sun4i_codec_linein_preamp_gain_scale), |
711 | SOC_SINGLE_TLV("FM Playback Volume" , SUN4I_CODEC_DAC_ACTL, |
712 | SUN4I_CODEC_DAC_ACTL_FMG, 3, 0, |
713 | sun4i_codec_fmin_loopback_gain_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), |
723 | }; |
724 | |
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, |
728 | 1, 0), |
729 | SOC_DAPM_SINGLE("Right Mixer Right DAC Playback Switch" , |
730 | SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_RDACRMIXS, |
731 | 1, 0), |
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("Line Playback Switch" , SUN4I_CODEC_DAC_ACTL, |
736 | SUN4I_CODEC_DAC_ACTL_LLNS, |
737 | SUN4I_CODEC_DAC_ACTL_RLNS, 1, 0), |
738 | SOC_DAPM_DOUBLE("FM Playback Switch" , SUN4I_CODEC_DAC_ACTL, |
739 | SUN4I_CODEC_DAC_ACTL_LFMS, |
740 | SUN4I_CODEC_DAC_ACTL_RFMS, 1, 0), |
741 | SOC_DAPM_DOUBLE("Mic1 Playback Switch" , SUN4I_CODEC_DAC_ACTL, |
742 | SUN4I_CODEC_DAC_ACTL_MIC1LS, |
743 | SUN4I_CODEC_DAC_ACTL_MIC1RS, 1, 0), |
744 | SOC_DAPM_DOUBLE("Mic2 Playback Switch" , SUN4I_CODEC_DAC_ACTL, |
745 | SUN4I_CODEC_DAC_ACTL_MIC2LS, |
746 | SUN4I_CODEC_DAC_ACTL_MIC2RS, 1, 0), |
747 | }; |
748 | |
749 | static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = { |
750 | SOC_DAPM_SINGLE("DAC Playback Switch" , SUN4I_CODEC_DAC_ACTL, |
751 | SUN4I_CODEC_DAC_ACTL_DACPAS, 1, 0), |
752 | SOC_DAPM_SINGLE("Mixer Playback Switch" , SUN4I_CODEC_DAC_ACTL, |
753 | SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0), |
754 | }; |
755 | |
756 | static const struct snd_soc_dapm_widget sun4i_codec_codec_dapm_widgets[] = { |
757 | /* Digital parts of the ADCs */ |
758 | SND_SOC_DAPM_SUPPLY("ADC" , SUN4I_CODEC_ADC_FIFOC, |
759 | SUN4I_CODEC_ADC_FIFOC_EN_AD, 0, |
760 | NULL, 0), |
761 | |
762 | /* Digital parts of the DACs */ |
763 | SND_SOC_DAPM_SUPPLY("DAC" , SUN4I_CODEC_DAC_DPC, |
764 | SUN4I_CODEC_DAC_DPC_EN_DA, 0, |
765 | NULL, 0), |
766 | |
767 | /* Analog parts of the ADCs */ |
768 | SND_SOC_DAPM_ADC("Left ADC" , "Codec Capture" , SUN4I_CODEC_ADC_ACTL, |
769 | SUN4I_CODEC_ADC_ACTL_ADC_L_EN, 0), |
770 | SND_SOC_DAPM_ADC("Right ADC" , "Codec Capture" , SUN4I_CODEC_ADC_ACTL, |
771 | SUN4I_CODEC_ADC_ACTL_ADC_R_EN, 0), |
772 | |
773 | /* Analog parts of the DACs */ |
774 | SND_SOC_DAPM_DAC("Left DAC" , "Codec Playback" , SUN4I_CODEC_DAC_ACTL, |
775 | SUN4I_CODEC_DAC_ACTL_DACAENL, 0), |
776 | SND_SOC_DAPM_DAC("Right DAC" , "Codec Playback" , SUN4I_CODEC_DAC_ACTL, |
777 | SUN4I_CODEC_DAC_ACTL_DACAENR, 0), |
778 | |
779 | /* Mixers */ |
780 | SND_SOC_DAPM_MIXER("Left Mixer" , SND_SOC_NOPM, 0, 0, |
781 | sun4i_codec_mixer_controls, |
782 | ARRAY_SIZE(sun4i_codec_mixer_controls)), |
783 | SND_SOC_DAPM_MIXER("Right Mixer" , SND_SOC_NOPM, 0, 0, |
784 | sun4i_codec_mixer_controls, |
785 | ARRAY_SIZE(sun4i_codec_mixer_controls)), |
786 | |
787 | /* Global Mixer Enable */ |
788 | SND_SOC_DAPM_SUPPLY("Mixer Enable" , SUN4I_CODEC_DAC_ACTL, |
789 | SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0), |
790 | |
791 | /* VMIC */ |
792 | SND_SOC_DAPM_SUPPLY("VMIC" , SUN4I_CODEC_ADC_ACTL, |
793 | SUN4I_CODEC_ADC_ACTL_VMICEN, 0, NULL, 0), |
794 | |
795 | /* Mic Pre-Amplifiers */ |
796 | SND_SOC_DAPM_PGA("MIC1 Pre-Amplifier" , SUN4I_CODEC_ADC_ACTL, |
797 | SUN4I_CODEC_ADC_ACTL_PREG1EN, 0, NULL, 0), |
798 | SND_SOC_DAPM_PGA("MIC2 Pre-Amplifier" , SUN4I_CODEC_ADC_ACTL, |
799 | SUN4I_CODEC_ADC_ACTL_PREG2EN, 0, NULL, 0), |
800 | |
801 | /* Power Amplifier */ |
802 | SND_SOC_DAPM_MIXER("Power Amplifier" , SUN4I_CODEC_ADC_ACTL, |
803 | SUN4I_CODEC_ADC_ACTL_PA_EN, 0, |
804 | sun4i_codec_pa_mixer_controls, |
805 | ARRAY_SIZE(sun4i_codec_pa_mixer_controls)), |
806 | SND_SOC_DAPM_SWITCH("Power Amplifier Mute" , SND_SOC_NOPM, 0, 0, |
807 | &sun4i_codec_pa_mute), |
808 | |
809 | SND_SOC_DAPM_INPUT("Line Right" ), |
810 | SND_SOC_DAPM_INPUT("Line Left" ), |
811 | SND_SOC_DAPM_INPUT("FM Right" ), |
812 | SND_SOC_DAPM_INPUT("FM Left" ), |
813 | SND_SOC_DAPM_INPUT("Mic1" ), |
814 | SND_SOC_DAPM_INPUT("Mic2" ), |
815 | |
816 | SND_SOC_DAPM_OUTPUT("HP Right" ), |
817 | SND_SOC_DAPM_OUTPUT("HP Left" ), |
818 | }; |
819 | |
820 | static const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = { |
821 | /* Left ADC / DAC Routes */ |
822 | { "Left ADC" , NULL, "ADC" }, |
823 | { "Left DAC" , NULL, "DAC" }, |
824 | |
825 | /* Right ADC / DAC Routes */ |
826 | { "Right ADC" , NULL, "ADC" }, |
827 | { "Right DAC" , NULL, "DAC" }, |
828 | |
829 | /* Right Mixer Routes */ |
830 | { "Right Mixer" , NULL, "Mixer Enable" }, |
831 | { "Right Mixer" , "Right Mixer Left DAC Playback Switch" , "Left DAC" }, |
832 | { "Right Mixer" , "Right Mixer Right DAC Playback Switch" , "Right DAC" }, |
833 | { "Right Mixer" , "Line Playback Switch" , "Line Right" }, |
834 | { "Right Mixer" , "FM Playback Switch" , "FM Right" }, |
835 | { "Right Mixer" , "Mic1 Playback Switch" , "MIC1 Pre-Amplifier" }, |
836 | { "Right Mixer" , "Mic2 Playback Switch" , "MIC2 Pre-Amplifier" }, |
837 | |
838 | /* Left Mixer Routes */ |
839 | { "Left Mixer" , NULL, "Mixer Enable" }, |
840 | { "Left Mixer" , "Left Mixer Left DAC Playback Switch" , "Left DAC" }, |
841 | { "Left Mixer" , "Line Playback Switch" , "Line Left" }, |
842 | { "Left Mixer" , "FM Playback Switch" , "FM Left" }, |
843 | { "Left Mixer" , "Mic1 Playback Switch" , "MIC1 Pre-Amplifier" }, |
844 | { "Left Mixer" , "Mic2 Playback Switch" , "MIC2 Pre-Amplifier" }, |
845 | |
846 | /* Power Amplifier Routes */ |
847 | { "Power Amplifier" , "Mixer Playback Switch" , "Left Mixer" }, |
848 | { "Power Amplifier" , "Mixer Playback Switch" , "Right Mixer" }, |
849 | { "Power Amplifier" , "DAC Playback Switch" , "Left DAC" }, |
850 | { "Power Amplifier" , "DAC Playback Switch" , "Right DAC" }, |
851 | |
852 | /* Headphone Output Routes */ |
853 | { "Power Amplifier Mute" , "Switch" , "Power Amplifier" }, |
854 | { "HP Right" , NULL, "Power Amplifier Mute" }, |
855 | { "HP Left" , NULL, "Power Amplifier Mute" }, |
856 | |
857 | /* Mic1 Routes */ |
858 | { "Left ADC" , NULL, "MIC1 Pre-Amplifier" }, |
859 | { "Right ADC" , NULL, "MIC1 Pre-Amplifier" }, |
860 | { "MIC1 Pre-Amplifier" , NULL, "Mic1" }, |
861 | { "Mic1" , NULL, "VMIC" }, |
862 | |
863 | /* Mic2 Routes */ |
864 | { "Left ADC" , NULL, "MIC2 Pre-Amplifier" }, |
865 | { "Right ADC" , NULL, "MIC2 Pre-Amplifier" }, |
866 | { "MIC2 Pre-Amplifier" , NULL, "Mic2" }, |
867 | { "Mic2" , NULL, "VMIC" }, |
868 | }; |
869 | |
870 | static const struct snd_soc_component_driver sun4i_codec_codec = { |
871 | .controls = sun4i_codec_controls, |
872 | .num_controls = ARRAY_SIZE(sun4i_codec_controls), |
873 | .dapm_widgets = sun4i_codec_codec_dapm_widgets, |
874 | .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets), |
875 | .dapm_routes = sun4i_codec_codec_dapm_routes, |
876 | .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes), |
877 | .idle_bias_on = 1, |
878 | .use_pmdown_time = 1, |
879 | .endianness = 1, |
880 | }; |
881 | |
882 | static const struct snd_soc_component_driver sun7i_codec_codec = { |
883 | .controls = sun7i_codec_controls, |
884 | .num_controls = ARRAY_SIZE(sun7i_codec_controls), |
885 | .dapm_widgets = sun4i_codec_codec_dapm_widgets, |
886 | .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets), |
887 | .dapm_routes = sun4i_codec_codec_dapm_routes, |
888 | .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes), |
889 | .idle_bias_on = 1, |
890 | .use_pmdown_time = 1, |
891 | .endianness = 1, |
892 | }; |
893 | |
894 | /*** sun6i Codec ***/ |
895 | |
896 | /* mixer controls */ |
897 | static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = { |
898 | SOC_DAPM_DOUBLE("DAC Playback Switch" , |
899 | SUN6I_CODEC_OM_DACA_CTRL, |
900 | SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL, |
901 | SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR, 1, 0), |
902 | SOC_DAPM_DOUBLE("DAC Reversed Playback Switch" , |
903 | SUN6I_CODEC_OM_DACA_CTRL, |
904 | SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR, |
905 | SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0), |
906 | SOC_DAPM_DOUBLE("Line In Playback Switch" , |
907 | SUN6I_CODEC_OM_DACA_CTRL, |
908 | SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL, |
909 | SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR, 1, 0), |
910 | SOC_DAPM_DOUBLE("Mic1 Playback Switch" , |
911 | SUN6I_CODEC_OM_DACA_CTRL, |
912 | SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1, |
913 | SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1, 1, 0), |
914 | SOC_DAPM_DOUBLE("Mic2 Playback Switch" , |
915 | SUN6I_CODEC_OM_DACA_CTRL, |
916 | SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2, |
917 | SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2, 1, 0), |
918 | }; |
919 | |
920 | /* ADC mixer controls */ |
921 | static const struct snd_kcontrol_new sun6i_codec_adc_mixer_controls[] = { |
922 | SOC_DAPM_DOUBLE("Mixer Capture Switch" , |
923 | SUN6I_CODEC_ADC_ACTL, |
924 | SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL, |
925 | SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR, 1, 0), |
926 | SOC_DAPM_DOUBLE("Mixer Reversed Capture Switch" , |
927 | SUN6I_CODEC_ADC_ACTL, |
928 | SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR, |
929 | SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL, 1, 0), |
930 | SOC_DAPM_DOUBLE("Line In Capture Switch" , |
931 | SUN6I_CODEC_ADC_ACTL, |
932 | SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL, |
933 | SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR, 1, 0), |
934 | SOC_DAPM_DOUBLE("Mic1 Capture Switch" , |
935 | SUN6I_CODEC_ADC_ACTL, |
936 | SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1, |
937 | SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1, 1, 0), |
938 | SOC_DAPM_DOUBLE("Mic2 Capture Switch" , |
939 | SUN6I_CODEC_ADC_ACTL, |
940 | SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2, |
941 | SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2, 1, 0), |
942 | }; |
943 | |
944 | /* headphone controls */ |
945 | static const char * const sun6i_codec_hp_src_enum_text[] = { |
946 | "DAC" , "Mixer" , |
947 | }; |
948 | |
949 | static SOC_ENUM_DOUBLE_DECL(sun6i_codec_hp_src_enum, |
950 | SUN6I_CODEC_OM_DACA_CTRL, |
951 | SUN6I_CODEC_OM_DACA_CTRL_LHPIS, |
952 | SUN6I_CODEC_OM_DACA_CTRL_RHPIS, |
953 | sun6i_codec_hp_src_enum_text); |
954 | |
955 | static const struct snd_kcontrol_new sun6i_codec_hp_src[] = { |
956 | SOC_DAPM_ENUM("Headphone Source Playback Route" , |
957 | sun6i_codec_hp_src_enum), |
958 | }; |
959 | |
960 | /* microphone controls */ |
961 | static const char * const sun6i_codec_mic2_src_enum_text[] = { |
962 | "Mic2" , "Mic3" , |
963 | }; |
964 | |
965 | static SOC_ENUM_SINGLE_DECL(sun6i_codec_mic2_src_enum, |
966 | SUN6I_CODEC_MIC_CTRL, |
967 | SUN6I_CODEC_MIC_CTRL_MIC2SLT, |
968 | sun6i_codec_mic2_src_enum_text); |
969 | |
970 | static const struct snd_kcontrol_new sun6i_codec_mic2_src[] = { |
971 | SOC_DAPM_ENUM("Mic2 Amplifier Source Route" , |
972 | sun6i_codec_mic2_src_enum), |
973 | }; |
974 | |
975 | /* line out controls */ |
976 | static const char * const sun6i_codec_lineout_src_enum_text[] = { |
977 | "Stereo" , "Mono Differential" , |
978 | }; |
979 | |
980 | static SOC_ENUM_DOUBLE_DECL(sun6i_codec_lineout_src_enum, |
981 | SUN6I_CODEC_MIC_CTRL, |
982 | SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC, |
983 | SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC, |
984 | sun6i_codec_lineout_src_enum_text); |
985 | |
986 | static const struct snd_kcontrol_new sun6i_codec_lineout_src[] = { |
987 | SOC_DAPM_ENUM("Line Out Source Playback Route" , |
988 | sun6i_codec_lineout_src_enum), |
989 | }; |
990 | |
991 | /* volume / mute controls */ |
992 | static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0); |
993 | static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1); |
994 | static const DECLARE_TLV_DB_SCALE(sun6i_codec_out_mixer_pregain_scale, |
995 | -450, 150, 0); |
996 | static const DECLARE_TLV_DB_RANGE(sun6i_codec_lineout_vol_scale, |
997 | 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), |
998 | 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0), |
999 | ); |
1000 | static const DECLARE_TLV_DB_RANGE(sun6i_codec_mic_gain_scale, |
1001 | 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), |
1002 | 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0), |
1003 | ); |
1004 | |
1005 | static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = { |
1006 | SOC_SINGLE_TLV("DAC Playback Volume" , SUN4I_CODEC_DAC_DPC, |
1007 | SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1, |
1008 | sun6i_codec_dvol_scale), |
1009 | SOC_SINGLE_TLV("Headphone Playback Volume" , |
1010 | SUN6I_CODEC_OM_DACA_CTRL, |
1011 | SUN6I_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0, |
1012 | sun6i_codec_hp_vol_scale), |
1013 | SOC_SINGLE_TLV("Line Out Playback Volume" , |
1014 | SUN6I_CODEC_MIC_CTRL, |
1015 | SUN6I_CODEC_MIC_CTRL_LINEOUTVC, 0x1f, 0, |
1016 | sun6i_codec_lineout_vol_scale), |
1017 | SOC_DOUBLE("Headphone Playback Switch" , |
1018 | SUN6I_CODEC_OM_DACA_CTRL, |
1019 | SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE, |
1020 | SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0), |
1021 | SOC_DOUBLE("Line Out Playback Switch" , |
1022 | SUN6I_CODEC_MIC_CTRL, |
1023 | SUN6I_CODEC_MIC_CTRL_LINEOUTLEN, |
1024 | SUN6I_CODEC_MIC_CTRL_LINEOUTREN, 1, 0), |
1025 | /* Mixer pre-gains */ |
1026 | SOC_SINGLE_TLV("Line In Playback Volume" , |
1027 | SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_LINEING, |
1028 | 0x7, 0, sun6i_codec_out_mixer_pregain_scale), |
1029 | SOC_SINGLE_TLV("Mic1 Playback Volume" , |
1030 | SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC1G, |
1031 | 0x7, 0, sun6i_codec_out_mixer_pregain_scale), |
1032 | SOC_SINGLE_TLV("Mic2 Playback Volume" , |
1033 | SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC2G, |
1034 | 0x7, 0, sun6i_codec_out_mixer_pregain_scale), |
1035 | |
1036 | /* Microphone Amp boost gains */ |
1037 | SOC_SINGLE_TLV("Mic1 Boost Volume" , SUN6I_CODEC_MIC_CTRL, |
1038 | SUN6I_CODEC_MIC_CTRL_MIC1BOOST, 0x7, 0, |
1039 | sun6i_codec_mic_gain_scale), |
1040 | SOC_SINGLE_TLV("Mic2 Boost Volume" , SUN6I_CODEC_MIC_CTRL, |
1041 | SUN6I_CODEC_MIC_CTRL_MIC2BOOST, 0x7, 0, |
1042 | sun6i_codec_mic_gain_scale), |
1043 | SOC_DOUBLE_TLV("ADC Capture Volume" , |
1044 | SUN6I_CODEC_ADC_ACTL, SUN6I_CODEC_ADC_ACTL_ADCLG, |
1045 | SUN6I_CODEC_ADC_ACTL_ADCRG, 0x7, 0, |
1046 | sun6i_codec_out_mixer_pregain_scale), |
1047 | }; |
1048 | |
1049 | static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = { |
1050 | /* Microphone inputs */ |
1051 | SND_SOC_DAPM_INPUT("MIC1" ), |
1052 | SND_SOC_DAPM_INPUT("MIC2" ), |
1053 | SND_SOC_DAPM_INPUT("MIC3" ), |
1054 | |
1055 | /* Microphone Bias */ |
1056 | SND_SOC_DAPM_SUPPLY("HBIAS" , SUN6I_CODEC_MIC_CTRL, |
1057 | SUN6I_CODEC_MIC_CTRL_HBIASEN, 0, NULL, 0), |
1058 | SND_SOC_DAPM_SUPPLY("MBIAS" , SUN6I_CODEC_MIC_CTRL, |
1059 | SUN6I_CODEC_MIC_CTRL_MBIASEN, 0, NULL, 0), |
1060 | |
1061 | /* Mic input path */ |
1062 | SND_SOC_DAPM_MUX("Mic2 Amplifier Source Route" , |
1063 | SND_SOC_NOPM, 0, 0, sun6i_codec_mic2_src), |
1064 | SND_SOC_DAPM_PGA("Mic1 Amplifier" , SUN6I_CODEC_MIC_CTRL, |
1065 | SUN6I_CODEC_MIC_CTRL_MIC1AMPEN, 0, NULL, 0), |
1066 | SND_SOC_DAPM_PGA("Mic2 Amplifier" , SUN6I_CODEC_MIC_CTRL, |
1067 | SUN6I_CODEC_MIC_CTRL_MIC2AMPEN, 0, NULL, 0), |
1068 | |
1069 | /* Line In */ |
1070 | SND_SOC_DAPM_INPUT("LINEIN" ), |
1071 | |
1072 | /* Digital parts of the ADCs */ |
1073 | SND_SOC_DAPM_SUPPLY("ADC Enable" , SUN6I_CODEC_ADC_FIFOC, |
1074 | SUN6I_CODEC_ADC_FIFOC_EN_AD, 0, |
1075 | NULL, 0), |
1076 | |
1077 | /* Analog parts of the ADCs */ |
1078 | SND_SOC_DAPM_ADC("Left ADC" , "Codec Capture" , SUN6I_CODEC_ADC_ACTL, |
1079 | SUN6I_CODEC_ADC_ACTL_ADCLEN, 0), |
1080 | SND_SOC_DAPM_ADC("Right ADC" , "Codec Capture" , SUN6I_CODEC_ADC_ACTL, |
1081 | SUN6I_CODEC_ADC_ACTL_ADCREN, 0), |
1082 | |
1083 | /* ADC Mixers */ |
1084 | SOC_MIXER_ARRAY("Left ADC Mixer" , SND_SOC_NOPM, 0, 0, |
1085 | sun6i_codec_adc_mixer_controls), |
1086 | SOC_MIXER_ARRAY("Right ADC Mixer" , SND_SOC_NOPM, 0, 0, |
1087 | sun6i_codec_adc_mixer_controls), |
1088 | |
1089 | /* Digital parts of the DACs */ |
1090 | SND_SOC_DAPM_SUPPLY("DAC Enable" , SUN4I_CODEC_DAC_DPC, |
1091 | SUN4I_CODEC_DAC_DPC_EN_DA, 0, |
1092 | NULL, 0), |
1093 | |
1094 | /* Analog parts of the DACs */ |
1095 | SND_SOC_DAPM_DAC("Left DAC" , "Codec Playback" , |
1096 | SUN6I_CODEC_OM_DACA_CTRL, |
1097 | SUN6I_CODEC_OM_DACA_CTRL_DACALEN, 0), |
1098 | SND_SOC_DAPM_DAC("Right DAC" , "Codec Playback" , |
1099 | SUN6I_CODEC_OM_DACA_CTRL, |
1100 | SUN6I_CODEC_OM_DACA_CTRL_DACAREN, 0), |
1101 | |
1102 | /* Mixers */ |
1103 | SOC_MIXER_ARRAY("Left Mixer" , SUN6I_CODEC_OM_DACA_CTRL, |
1104 | SUN6I_CODEC_OM_DACA_CTRL_LMIXEN, 0, |
1105 | sun6i_codec_mixer_controls), |
1106 | SOC_MIXER_ARRAY("Right Mixer" , SUN6I_CODEC_OM_DACA_CTRL, |
1107 | SUN6I_CODEC_OM_DACA_CTRL_RMIXEN, 0, |
1108 | sun6i_codec_mixer_controls), |
1109 | |
1110 | /* Headphone output path */ |
1111 | SND_SOC_DAPM_MUX("Headphone Source Playback Route" , |
1112 | SND_SOC_NOPM, 0, 0, sun6i_codec_hp_src), |
1113 | SND_SOC_DAPM_OUT_DRV("Headphone Amp" , SUN6I_CODEC_OM_PA_CTRL, |
1114 | SUN6I_CODEC_OM_PA_CTRL_HPPAEN, 0, NULL, 0), |
1115 | SND_SOC_DAPM_SUPPLY("HPCOM Protection" , SUN6I_CODEC_OM_PA_CTRL, |
1116 | SUN6I_CODEC_OM_PA_CTRL_COMPTEN, 0, NULL, 0), |
1117 | SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM" , SUN6I_CODEC_OM_PA_CTRL, |
1118 | SUN6I_CODEC_OM_PA_CTRL_HPCOM_CTL, 0x3, 0x3, 0), |
1119 | SND_SOC_DAPM_OUTPUT("HP" ), |
1120 | |
1121 | /* Line Out path */ |
1122 | SND_SOC_DAPM_MUX("Line Out Source Playback Route" , |
1123 | SND_SOC_NOPM, 0, 0, sun6i_codec_lineout_src), |
1124 | SND_SOC_DAPM_OUTPUT("LINEOUT" ), |
1125 | }; |
1126 | |
1127 | static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = { |
1128 | /* DAC Routes */ |
1129 | { "Left DAC" , NULL, "DAC Enable" }, |
1130 | { "Right DAC" , NULL, "DAC Enable" }, |
1131 | |
1132 | /* Microphone Routes */ |
1133 | { "Mic1 Amplifier" , NULL, "MIC1" }, |
1134 | { "Mic2 Amplifier Source Route" , "Mic2" , "MIC2" }, |
1135 | { "Mic2 Amplifier Source Route" , "Mic3" , "MIC3" }, |
1136 | { "Mic2 Amplifier" , NULL, "Mic2 Amplifier Source Route" }, |
1137 | |
1138 | /* Left Mixer Routes */ |
1139 | { "Left Mixer" , "DAC Playback Switch" , "Left DAC" }, |
1140 | { "Left Mixer" , "DAC Reversed Playback Switch" , "Right DAC" }, |
1141 | { "Left Mixer" , "Line In Playback Switch" , "LINEIN" }, |
1142 | { "Left Mixer" , "Mic1 Playback Switch" , "Mic1 Amplifier" }, |
1143 | { "Left Mixer" , "Mic2 Playback Switch" , "Mic2 Amplifier" }, |
1144 | |
1145 | /* Right Mixer Routes */ |
1146 | { "Right Mixer" , "DAC Playback Switch" , "Right DAC" }, |
1147 | { "Right Mixer" , "DAC Reversed Playback Switch" , "Left DAC" }, |
1148 | { "Right Mixer" , "Line In Playback Switch" , "LINEIN" }, |
1149 | { "Right Mixer" , "Mic1 Playback Switch" , "Mic1 Amplifier" }, |
1150 | { "Right Mixer" , "Mic2 Playback Switch" , "Mic2 Amplifier" }, |
1151 | |
1152 | /* Left ADC Mixer Routes */ |
1153 | { "Left ADC Mixer" , "Mixer Capture Switch" , "Left Mixer" }, |
1154 | { "Left ADC Mixer" , "Mixer Reversed Capture Switch" , "Right Mixer" }, |
1155 | { "Left ADC Mixer" , "Line In Capture Switch" , "LINEIN" }, |
1156 | { "Left ADC Mixer" , "Mic1 Capture Switch" , "Mic1 Amplifier" }, |
1157 | { "Left ADC Mixer" , "Mic2 Capture Switch" , "Mic2 Amplifier" }, |
1158 | |
1159 | /* Right ADC Mixer Routes */ |
1160 | { "Right ADC Mixer" , "Mixer Capture Switch" , "Right Mixer" }, |
1161 | { "Right ADC Mixer" , "Mixer Reversed Capture Switch" , "Left Mixer" }, |
1162 | { "Right ADC Mixer" , "Line In Capture Switch" , "LINEIN" }, |
1163 | { "Right ADC Mixer" , "Mic1 Capture Switch" , "Mic1 Amplifier" }, |
1164 | { "Right ADC Mixer" , "Mic2 Capture Switch" , "Mic2 Amplifier" }, |
1165 | |
1166 | /* Headphone Routes */ |
1167 | { "Headphone Source Playback Route" , "DAC" , "Left DAC" }, |
1168 | { "Headphone Source Playback Route" , "DAC" , "Right DAC" }, |
1169 | { "Headphone Source Playback Route" , "Mixer" , "Left Mixer" }, |
1170 | { "Headphone Source Playback Route" , "Mixer" , "Right Mixer" }, |
1171 | { "Headphone Amp" , NULL, "Headphone Source Playback Route" }, |
1172 | { "HP" , NULL, "Headphone Amp" }, |
1173 | { "HPCOM" , NULL, "HPCOM Protection" }, |
1174 | |
1175 | /* Line Out Routes */ |
1176 | { "Line Out Source Playback Route" , "Stereo" , "Left Mixer" }, |
1177 | { "Line Out Source Playback Route" , "Stereo" , "Right Mixer" }, |
1178 | { "Line Out Source Playback Route" , "Mono Differential" , "Left Mixer" }, |
1179 | { "Line Out Source Playback Route" , "Mono Differential" , "Right Mixer" }, |
1180 | { "LINEOUT" , NULL, "Line Out Source Playback Route" }, |
1181 | |
1182 | /* ADC Routes */ |
1183 | { "Left ADC" , NULL, "ADC Enable" }, |
1184 | { "Right ADC" , NULL, "ADC Enable" }, |
1185 | { "Left ADC" , NULL, "Left ADC Mixer" }, |
1186 | { "Right ADC" , NULL, "Right ADC Mixer" }, |
1187 | }; |
1188 | |
1189 | static const struct snd_soc_component_driver sun6i_codec_codec = { |
1190 | .controls = sun6i_codec_codec_widgets, |
1191 | .num_controls = ARRAY_SIZE(sun6i_codec_codec_widgets), |
1192 | .dapm_widgets = sun6i_codec_codec_dapm_widgets, |
1193 | .num_dapm_widgets = ARRAY_SIZE(sun6i_codec_codec_dapm_widgets), |
1194 | .dapm_routes = sun6i_codec_codec_dapm_routes, |
1195 | .num_dapm_routes = ARRAY_SIZE(sun6i_codec_codec_dapm_routes), |
1196 | .idle_bias_on = 1, |
1197 | .use_pmdown_time = 1, |
1198 | .endianness = 1, |
1199 | }; |
1200 | |
1201 | /* sun8i A23 codec */ |
1202 | static const struct snd_kcontrol_new sun8i_a23_codec_codec_controls[] = { |
1203 | SOC_SINGLE_TLV("DAC Playback Volume" , SUN4I_CODEC_DAC_DPC, |
1204 | SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1, |
1205 | sun6i_codec_dvol_scale), |
1206 | }; |
1207 | |
1208 | static const struct snd_soc_dapm_widget sun8i_a23_codec_codec_widgets[] = { |
1209 | /* Digital parts of the ADCs */ |
1210 | SND_SOC_DAPM_SUPPLY("ADC Enable" , SUN6I_CODEC_ADC_FIFOC, |
1211 | SUN6I_CODEC_ADC_FIFOC_EN_AD, 0, NULL, 0), |
1212 | /* Digital parts of the DACs */ |
1213 | SND_SOC_DAPM_SUPPLY("DAC Enable" , SUN4I_CODEC_DAC_DPC, |
1214 | SUN4I_CODEC_DAC_DPC_EN_DA, 0, NULL, 0), |
1215 | |
1216 | }; |
1217 | |
1218 | static const struct snd_soc_component_driver sun8i_a23_codec_codec = { |
1219 | .controls = sun8i_a23_codec_codec_controls, |
1220 | .num_controls = ARRAY_SIZE(sun8i_a23_codec_codec_controls), |
1221 | .dapm_widgets = sun8i_a23_codec_codec_widgets, |
1222 | .num_dapm_widgets = ARRAY_SIZE(sun8i_a23_codec_codec_widgets), |
1223 | .idle_bias_on = 1, |
1224 | .use_pmdown_time = 1, |
1225 | .endianness = 1, |
1226 | }; |
1227 | |
1228 | static const struct snd_soc_component_driver sun4i_codec_component = { |
1229 | .name = "sun4i-codec" , |
1230 | .legacy_dai_naming = 1, |
1231 | #ifdef CONFIG_DEBUG_FS |
1232 | .debugfs_prefix = "cpu" , |
1233 | #endif |
1234 | }; |
1235 | |
1236 | #define SUN4I_CODEC_RATES SNDRV_PCM_RATE_CONTINUOUS |
1237 | #define SUN4I_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ |
1238 | SNDRV_PCM_FMTBIT_S32_LE) |
1239 | |
1240 | static int sun4i_codec_dai_probe(struct snd_soc_dai *dai) |
1241 | { |
1242 | struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai); |
1243 | struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card); |
1244 | |
1245 | snd_soc_dai_init_dma_data(dai, playback: &scodec->playback_dma_data, |
1246 | capture: &scodec->capture_dma_data); |
1247 | |
1248 | return 0; |
1249 | } |
1250 | |
1251 | static const struct snd_soc_dai_ops dummy_dai_ops = { |
1252 | .probe = sun4i_codec_dai_probe, |
1253 | }; |
1254 | |
1255 | static struct snd_soc_dai_driver dummy_cpu_dai = { |
1256 | .name = "sun4i-codec-cpu-dai" , |
1257 | .playback = { |
1258 | .stream_name = "Playback" , |
1259 | .channels_min = 1, |
1260 | .channels_max = 2, |
1261 | .rates = SUN4I_CODEC_RATES, |
1262 | .formats = SUN4I_CODEC_FORMATS, |
1263 | .sig_bits = 24, |
1264 | }, |
1265 | .capture = { |
1266 | .stream_name = "Capture" , |
1267 | .channels_min = 1, |
1268 | .channels_max = 2, |
1269 | .rates = SUN4I_CODEC_RATES, |
1270 | .formats = SUN4I_CODEC_FORMATS, |
1271 | .sig_bits = 24, |
1272 | }, |
1273 | .ops = &dummy_dai_ops, |
1274 | }; |
1275 | |
1276 | static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev, |
1277 | int *num_links) |
1278 | { |
1279 | struct snd_soc_dai_link *link = devm_kzalloc(dev, size: sizeof(*link), |
1280 | GFP_KERNEL); |
1281 | struct snd_soc_dai_link_component *dlc = devm_kzalloc(dev, |
1282 | size: 3 * sizeof(*dlc), GFP_KERNEL); |
1283 | if (!link || !dlc) |
1284 | return NULL; |
1285 | |
1286 | link->cpus = &dlc[0]; |
1287 | link->codecs = &dlc[1]; |
1288 | link->platforms = &dlc[2]; |
1289 | |
1290 | link->num_cpus = 1; |
1291 | link->num_codecs = 1; |
1292 | link->num_platforms = 1; |
1293 | |
1294 | link->name = "cdc" ; |
1295 | link->stream_name = "CDC PCM" ; |
1296 | link->codecs->dai_name = "Codec" ; |
1297 | link->cpus->dai_name = dev_name(dev); |
1298 | link->codecs->name = dev_name(dev); |
1299 | link->platforms->name = dev_name(dev); |
1300 | link->dai_fmt = SND_SOC_DAIFMT_I2S; |
1301 | |
1302 | *num_links = 1; |
1303 | |
1304 | return link; |
1305 | }; |
1306 | |
1307 | static int sun4i_codec_spk_event(struct snd_soc_dapm_widget *w, |
1308 | struct snd_kcontrol *k, int event) |
1309 | { |
1310 | struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card: w->dapm->card); |
1311 | |
1312 | gpiod_set_value_cansleep(desc: scodec->gpio_pa, |
1313 | value: !!SND_SOC_DAPM_EVENT_ON(event)); |
1314 | |
1315 | if (SND_SOC_DAPM_EVENT_ON(event)) { |
1316 | /* |
1317 | * Need a delay to wait for DAC to push the data. 700ms seems |
1318 | * to be the best compromise not to feel this delay while |
1319 | * playing a sound. |
1320 | */ |
1321 | msleep(msecs: 700); |
1322 | } |
1323 | |
1324 | return 0; |
1325 | } |
1326 | |
1327 | static const struct snd_soc_dapm_widget sun4i_codec_card_dapm_widgets[] = { |
1328 | SND_SOC_DAPM_SPK("Speaker" , sun4i_codec_spk_event), |
1329 | }; |
1330 | |
1331 | static const struct snd_soc_dapm_route sun4i_codec_card_dapm_routes[] = { |
1332 | { "Speaker" , NULL, "HP Right" }, |
1333 | { "Speaker" , NULL, "HP Left" }, |
1334 | }; |
1335 | |
1336 | static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) |
1337 | { |
1338 | struct snd_soc_card *card; |
1339 | |
1340 | card = devm_kzalloc(dev, size: sizeof(*card), GFP_KERNEL); |
1341 | if (!card) |
1342 | return ERR_PTR(error: -ENOMEM); |
1343 | |
1344 | card->dai_link = sun4i_codec_create_link(dev, num_links: &card->num_links); |
1345 | if (!card->dai_link) |
1346 | return ERR_PTR(error: -ENOMEM); |
1347 | |
1348 | card->dev = dev; |
1349 | card->owner = THIS_MODULE; |
1350 | card->name = "sun4i-codec" ; |
1351 | card->dapm_widgets = sun4i_codec_card_dapm_widgets; |
1352 | card->num_dapm_widgets = ARRAY_SIZE(sun4i_codec_card_dapm_widgets); |
1353 | card->dapm_routes = sun4i_codec_card_dapm_routes; |
1354 | card->num_dapm_routes = ARRAY_SIZE(sun4i_codec_card_dapm_routes); |
1355 | |
1356 | return card; |
1357 | }; |
1358 | |
1359 | static const struct snd_soc_dapm_widget sun6i_codec_card_dapm_widgets[] = { |
1360 | SND_SOC_DAPM_HP("Headphone" , NULL), |
1361 | SND_SOC_DAPM_LINE("Line In" , NULL), |
1362 | SND_SOC_DAPM_LINE("Line Out" , NULL), |
1363 | SND_SOC_DAPM_MIC("Headset Mic" , NULL), |
1364 | SND_SOC_DAPM_MIC("Mic" , NULL), |
1365 | SND_SOC_DAPM_SPK("Speaker" , sun4i_codec_spk_event), |
1366 | }; |
1367 | |
1368 | static struct snd_soc_card *sun6i_codec_create_card(struct device *dev) |
1369 | { |
1370 | struct snd_soc_card *card; |
1371 | int ret; |
1372 | |
1373 | card = devm_kzalloc(dev, size: sizeof(*card), GFP_KERNEL); |
1374 | if (!card) |
1375 | return ERR_PTR(error: -ENOMEM); |
1376 | |
1377 | card->dai_link = sun4i_codec_create_link(dev, num_links: &card->num_links); |
1378 | if (!card->dai_link) |
1379 | return ERR_PTR(error: -ENOMEM); |
1380 | |
1381 | card->dev = dev; |
1382 | card->owner = THIS_MODULE; |
1383 | card->name = "A31 Audio Codec" ; |
1384 | card->dapm_widgets = sun6i_codec_card_dapm_widgets; |
1385 | card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets); |
1386 | card->fully_routed = true; |
1387 | |
1388 | ret = snd_soc_of_parse_audio_routing(card, propname: "allwinner,audio-routing" ); |
1389 | if (ret) |
1390 | dev_warn(dev, "failed to parse audio-routing: %d\n" , ret); |
1391 | |
1392 | return card; |
1393 | }; |
1394 | |
1395 | /* Connect digital side enables to analog side widgets */ |
1396 | static const struct snd_soc_dapm_route sun8i_codec_card_routes[] = { |
1397 | /* ADC Routes */ |
1398 | { "Left ADC" , NULL, "ADC Enable" }, |
1399 | { "Right ADC" , NULL, "ADC Enable" }, |
1400 | { "Codec Capture" , NULL, "Left ADC" }, |
1401 | { "Codec Capture" , NULL, "Right ADC" }, |
1402 | |
1403 | /* DAC Routes */ |
1404 | { "Left DAC" , NULL, "DAC Enable" }, |
1405 | { "Right DAC" , NULL, "DAC Enable" }, |
1406 | { "Left DAC" , NULL, "Codec Playback" }, |
1407 | { "Right DAC" , NULL, "Codec Playback" }, |
1408 | }; |
1409 | |
1410 | static struct snd_soc_aux_dev aux_dev = { |
1411 | .dlc = COMP_EMPTY(), |
1412 | }; |
1413 | |
1414 | static struct snd_soc_card *sun8i_a23_codec_create_card(struct device *dev) |
1415 | { |
1416 | struct snd_soc_card *card; |
1417 | int ret; |
1418 | |
1419 | card = devm_kzalloc(dev, size: sizeof(*card), GFP_KERNEL); |
1420 | if (!card) |
1421 | return ERR_PTR(error: -ENOMEM); |
1422 | |
1423 | aux_dev.dlc.of_node = of_parse_phandle(np: dev->of_node, |
1424 | phandle_name: "allwinner,codec-analog-controls" , |
1425 | index: 0); |
1426 | if (!aux_dev.dlc.of_node) { |
1427 | dev_err(dev, "Can't find analog controls for codec.\n" ); |
1428 | return ERR_PTR(error: -EINVAL); |
1429 | } |
1430 | |
1431 | card->dai_link = sun4i_codec_create_link(dev, num_links: &card->num_links); |
1432 | if (!card->dai_link) |
1433 | return ERR_PTR(error: -ENOMEM); |
1434 | |
1435 | card->dev = dev; |
1436 | card->owner = THIS_MODULE; |
1437 | card->name = "A23 Audio Codec" ; |
1438 | card->dapm_widgets = sun6i_codec_card_dapm_widgets; |
1439 | card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets); |
1440 | card->dapm_routes = sun8i_codec_card_routes; |
1441 | card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes); |
1442 | card->aux_dev = &aux_dev; |
1443 | card->num_aux_devs = 1; |
1444 | card->fully_routed = true; |
1445 | |
1446 | ret = snd_soc_of_parse_audio_routing(card, propname: "allwinner,audio-routing" ); |
1447 | if (ret) |
1448 | dev_warn(dev, "failed to parse audio-routing: %d\n" , ret); |
1449 | |
1450 | return card; |
1451 | }; |
1452 | |
1453 | static struct snd_soc_card *sun8i_h3_codec_create_card(struct device *dev) |
1454 | { |
1455 | struct snd_soc_card *card; |
1456 | int ret; |
1457 | |
1458 | card = devm_kzalloc(dev, size: sizeof(*card), GFP_KERNEL); |
1459 | if (!card) |
1460 | return ERR_PTR(error: -ENOMEM); |
1461 | |
1462 | aux_dev.dlc.of_node = of_parse_phandle(np: dev->of_node, |
1463 | phandle_name: "allwinner,codec-analog-controls" , |
1464 | index: 0); |
1465 | if (!aux_dev.dlc.of_node) { |
1466 | dev_err(dev, "Can't find analog controls for codec.\n" ); |
1467 | return ERR_PTR(error: -EINVAL); |
1468 | } |
1469 | |
1470 | card->dai_link = sun4i_codec_create_link(dev, num_links: &card->num_links); |
1471 | if (!card->dai_link) |
1472 | return ERR_PTR(error: -ENOMEM); |
1473 | |
1474 | card->dev = dev; |
1475 | card->owner = THIS_MODULE; |
1476 | card->name = "H3 Audio Codec" ; |
1477 | card->dapm_widgets = sun6i_codec_card_dapm_widgets; |
1478 | card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets); |
1479 | card->dapm_routes = sun8i_codec_card_routes; |
1480 | card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes); |
1481 | card->aux_dev = &aux_dev; |
1482 | card->num_aux_devs = 1; |
1483 | card->fully_routed = true; |
1484 | |
1485 | ret = snd_soc_of_parse_audio_routing(card, propname: "allwinner,audio-routing" ); |
1486 | if (ret) |
1487 | dev_warn(dev, "failed to parse audio-routing: %d\n" , ret); |
1488 | |
1489 | return card; |
1490 | }; |
1491 | |
1492 | static struct snd_soc_card *sun8i_v3s_codec_create_card(struct device *dev) |
1493 | { |
1494 | struct snd_soc_card *card; |
1495 | int ret; |
1496 | |
1497 | card = devm_kzalloc(dev, size: sizeof(*card), GFP_KERNEL); |
1498 | if (!card) |
1499 | return ERR_PTR(error: -ENOMEM); |
1500 | |
1501 | aux_dev.dlc.of_node = of_parse_phandle(np: dev->of_node, |
1502 | phandle_name: "allwinner,codec-analog-controls" , |
1503 | index: 0); |
1504 | if (!aux_dev.dlc.of_node) { |
1505 | dev_err(dev, "Can't find analog controls for codec.\n" ); |
1506 | return ERR_PTR(error: -EINVAL); |
1507 | } |
1508 | |
1509 | card->dai_link = sun4i_codec_create_link(dev, num_links: &card->num_links); |
1510 | if (!card->dai_link) |
1511 | return ERR_PTR(error: -ENOMEM); |
1512 | |
1513 | card->dev = dev; |
1514 | card->owner = THIS_MODULE; |
1515 | card->name = "V3s Audio Codec" ; |
1516 | card->dapm_widgets = sun6i_codec_card_dapm_widgets; |
1517 | card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets); |
1518 | card->dapm_routes = sun8i_codec_card_routes; |
1519 | card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes); |
1520 | card->aux_dev = &aux_dev; |
1521 | card->num_aux_devs = 1; |
1522 | card->fully_routed = true; |
1523 | |
1524 | ret = snd_soc_of_parse_audio_routing(card, propname: "allwinner,audio-routing" ); |
1525 | if (ret) |
1526 | dev_warn(dev, "failed to parse audio-routing: %d\n" , ret); |
1527 | |
1528 | return card; |
1529 | }; |
1530 | |
1531 | static const struct regmap_config sun4i_codec_regmap_config = { |
1532 | .reg_bits = 32, |
1533 | .reg_stride = 4, |
1534 | .val_bits = 32, |
1535 | .max_register = SUN4I_CODEC_ADC_RXCNT, |
1536 | }; |
1537 | |
1538 | static const struct regmap_config sun6i_codec_regmap_config = { |
1539 | .reg_bits = 32, |
1540 | .reg_stride = 4, |
1541 | .val_bits = 32, |
1542 | .max_register = SUN6I_CODEC_HMIC_DATA, |
1543 | }; |
1544 | |
1545 | static const struct regmap_config sun7i_codec_regmap_config = { |
1546 | .reg_bits = 32, |
1547 | .reg_stride = 4, |
1548 | .val_bits = 32, |
1549 | .max_register = SUN7I_CODEC_AC_MIC_PHONE_CAL, |
1550 | }; |
1551 | |
1552 | static const struct regmap_config sun8i_a23_codec_regmap_config = { |
1553 | .reg_bits = 32, |
1554 | .reg_stride = 4, |
1555 | .val_bits = 32, |
1556 | .max_register = SUN8I_A23_CODEC_ADC_RXCNT, |
1557 | }; |
1558 | |
1559 | static const struct regmap_config sun8i_h3_codec_regmap_config = { |
1560 | .reg_bits = 32, |
1561 | .reg_stride = 4, |
1562 | .val_bits = 32, |
1563 | .max_register = SUN8I_H3_CODEC_ADC_DBG, |
1564 | }; |
1565 | |
1566 | static const struct regmap_config sun8i_v3s_codec_regmap_config = { |
1567 | .reg_bits = 32, |
1568 | .reg_stride = 4, |
1569 | .val_bits = 32, |
1570 | .max_register = SUN8I_H3_CODEC_ADC_DBG, |
1571 | }; |
1572 | |
1573 | struct sun4i_codec_quirks { |
1574 | const struct regmap_config *regmap_config; |
1575 | const struct snd_soc_component_driver *codec; |
1576 | struct snd_soc_card * (*create_card)(struct device *dev); |
1577 | struct reg_field reg_adc_fifoc; /* used for regmap_field */ |
1578 | unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */ |
1579 | unsigned int reg_adc_rxdata; /* RX FIFO offset for DMA config */ |
1580 | bool has_reset; |
1581 | }; |
1582 | |
1583 | static const struct sun4i_codec_quirks sun4i_codec_quirks = { |
1584 | .regmap_config = &sun4i_codec_regmap_config, |
1585 | .codec = &sun4i_codec_codec, |
1586 | .create_card = sun4i_codec_create_card, |
1587 | .reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31), |
1588 | .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, |
1589 | .reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA, |
1590 | }; |
1591 | |
1592 | static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = { |
1593 | .regmap_config = &sun6i_codec_regmap_config, |
1594 | .codec = &sun6i_codec_codec, |
1595 | .create_card = sun6i_codec_create_card, |
1596 | .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31), |
1597 | .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, |
1598 | .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, |
1599 | .has_reset = true, |
1600 | }; |
1601 | |
1602 | static const struct sun4i_codec_quirks sun7i_codec_quirks = { |
1603 | .regmap_config = &sun7i_codec_regmap_config, |
1604 | .codec = &sun7i_codec_codec, |
1605 | .create_card = sun4i_codec_create_card, |
1606 | .reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31), |
1607 | .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, |
1608 | .reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA, |
1609 | }; |
1610 | |
1611 | static const struct sun4i_codec_quirks sun8i_a23_codec_quirks = { |
1612 | .regmap_config = &sun8i_a23_codec_regmap_config, |
1613 | .codec = &sun8i_a23_codec_codec, |
1614 | .create_card = sun8i_a23_codec_create_card, |
1615 | .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31), |
1616 | .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, |
1617 | .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, |
1618 | .has_reset = true, |
1619 | }; |
1620 | |
1621 | static const struct sun4i_codec_quirks sun8i_h3_codec_quirks = { |
1622 | .regmap_config = &sun8i_h3_codec_regmap_config, |
1623 | /* |
1624 | * TODO Share the codec structure with A23 for now. |
1625 | * This should be split out when adding digital audio |
1626 | * processing support for the H3. |
1627 | */ |
1628 | .codec = &sun8i_a23_codec_codec, |
1629 | .create_card = sun8i_h3_codec_create_card, |
1630 | .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31), |
1631 | .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA, |
1632 | .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, |
1633 | .has_reset = true, |
1634 | }; |
1635 | |
1636 | static const struct sun4i_codec_quirks sun8i_v3s_codec_quirks = { |
1637 | .regmap_config = &sun8i_v3s_codec_regmap_config, |
1638 | /* |
1639 | * TODO The codec structure should be split out, like |
1640 | * H3, when adding digital audio processing support. |
1641 | */ |
1642 | .codec = &sun8i_a23_codec_codec, |
1643 | .create_card = sun8i_v3s_codec_create_card, |
1644 | .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31), |
1645 | .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA, |
1646 | .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, |
1647 | .has_reset = true, |
1648 | }; |
1649 | |
1650 | static const struct of_device_id sun4i_codec_of_match[] = { |
1651 | { |
1652 | .compatible = "allwinner,sun4i-a10-codec" , |
1653 | .data = &sun4i_codec_quirks, |
1654 | }, |
1655 | { |
1656 | .compatible = "allwinner,sun6i-a31-codec" , |
1657 | .data = &sun6i_a31_codec_quirks, |
1658 | }, |
1659 | { |
1660 | .compatible = "allwinner,sun7i-a20-codec" , |
1661 | .data = &sun7i_codec_quirks, |
1662 | }, |
1663 | { |
1664 | .compatible = "allwinner,sun8i-a23-codec" , |
1665 | .data = &sun8i_a23_codec_quirks, |
1666 | }, |
1667 | { |
1668 | .compatible = "allwinner,sun8i-h3-codec" , |
1669 | .data = &sun8i_h3_codec_quirks, |
1670 | }, |
1671 | { |
1672 | .compatible = "allwinner,sun8i-v3s-codec" , |
1673 | .data = &sun8i_v3s_codec_quirks, |
1674 | }, |
1675 | {} |
1676 | }; |
1677 | MODULE_DEVICE_TABLE(of, sun4i_codec_of_match); |
1678 | |
1679 | static int sun4i_codec_probe(struct platform_device *pdev) |
1680 | { |
1681 | struct snd_soc_card *card; |
1682 | struct sun4i_codec *scodec; |
1683 | const struct sun4i_codec_quirks *quirks; |
1684 | struct resource *res; |
1685 | void __iomem *base; |
1686 | int ret; |
1687 | |
1688 | scodec = devm_kzalloc(dev: &pdev->dev, size: sizeof(*scodec), GFP_KERNEL); |
1689 | if (!scodec) |
1690 | return -ENOMEM; |
1691 | |
1692 | scodec->dev = &pdev->dev; |
1693 | |
1694 | base = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res); |
1695 | if (IS_ERR(ptr: base)) |
1696 | return PTR_ERR(ptr: base); |
1697 | |
1698 | quirks = of_device_get_match_data(dev: &pdev->dev); |
1699 | if (quirks == NULL) { |
1700 | dev_err(&pdev->dev, "Failed to determine the quirks to use\n" ); |
1701 | return -ENODEV; |
1702 | } |
1703 | |
1704 | scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base, |
1705 | quirks->regmap_config); |
1706 | if (IS_ERR(ptr: scodec->regmap)) { |
1707 | dev_err(&pdev->dev, "Failed to create our regmap\n" ); |
1708 | return PTR_ERR(ptr: scodec->regmap); |
1709 | } |
1710 | |
1711 | /* Get the clocks from the DT */ |
1712 | scodec->clk_apb = devm_clk_get(dev: &pdev->dev, id: "apb" ); |
1713 | if (IS_ERR(ptr: scodec->clk_apb)) { |
1714 | dev_err(&pdev->dev, "Failed to get the APB clock\n" ); |
1715 | return PTR_ERR(ptr: scodec->clk_apb); |
1716 | } |
1717 | |
1718 | scodec->clk_module = devm_clk_get(dev: &pdev->dev, id: "codec" ); |
1719 | if (IS_ERR(ptr: scodec->clk_module)) { |
1720 | dev_err(&pdev->dev, "Failed to get the module clock\n" ); |
1721 | return PTR_ERR(ptr: scodec->clk_module); |
1722 | } |
1723 | |
1724 | if (quirks->has_reset) { |
1725 | scodec->rst = devm_reset_control_get_exclusive(dev: &pdev->dev, |
1726 | NULL); |
1727 | if (IS_ERR(ptr: scodec->rst)) { |
1728 | dev_err(&pdev->dev, "Failed to get reset control\n" ); |
1729 | return PTR_ERR(ptr: scodec->rst); |
1730 | } |
1731 | } |
1732 | |
1733 | scodec->gpio_pa = devm_gpiod_get_optional(dev: &pdev->dev, con_id: "allwinner,pa" , |
1734 | flags: GPIOD_OUT_LOW); |
1735 | if (IS_ERR(ptr: scodec->gpio_pa)) { |
1736 | ret = PTR_ERR(ptr: scodec->gpio_pa); |
1737 | dev_err_probe(dev: &pdev->dev, err: ret, fmt: "Failed to get pa gpio\n" ); |
1738 | return ret; |
1739 | } |
1740 | |
1741 | /* reg_field setup */ |
1742 | scodec->reg_adc_fifoc = devm_regmap_field_alloc(dev: &pdev->dev, |
1743 | regmap: scodec->regmap, |
1744 | reg_field: quirks->reg_adc_fifoc); |
1745 | if (IS_ERR(ptr: scodec->reg_adc_fifoc)) { |
1746 | ret = PTR_ERR(ptr: scodec->reg_adc_fifoc); |
1747 | dev_err(&pdev->dev, "Failed to create regmap fields: %d\n" , |
1748 | ret); |
1749 | return ret; |
1750 | } |
1751 | |
1752 | /* Enable the bus clock */ |
1753 | if (clk_prepare_enable(clk: scodec->clk_apb)) { |
1754 | dev_err(&pdev->dev, "Failed to enable the APB clock\n" ); |
1755 | return -EINVAL; |
1756 | } |
1757 | |
1758 | /* Deassert the reset control */ |
1759 | if (scodec->rst) { |
1760 | ret = reset_control_deassert(rstc: scodec->rst); |
1761 | if (ret) { |
1762 | dev_err(&pdev->dev, |
1763 | "Failed to deassert the reset control\n" ); |
1764 | goto err_clk_disable; |
1765 | } |
1766 | } |
1767 | |
1768 | /* DMA configuration for TX FIFO */ |
1769 | scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata; |
1770 | scodec->playback_dma_data.maxburst = 8; |
1771 | scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
1772 | |
1773 | /* DMA configuration for RX FIFO */ |
1774 | scodec->capture_dma_data.addr = res->start + quirks->reg_adc_rxdata; |
1775 | scodec->capture_dma_data.maxburst = 8; |
1776 | scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
1777 | |
1778 | ret = devm_snd_soc_register_component(dev: &pdev->dev, component_driver: quirks->codec, |
1779 | dai_drv: &sun4i_codec_dai, num_dai: 1); |
1780 | if (ret) { |
1781 | dev_err(&pdev->dev, "Failed to register our codec\n" ); |
1782 | goto err_assert_reset; |
1783 | } |
1784 | |
1785 | ret = devm_snd_soc_register_component(dev: &pdev->dev, |
1786 | component_driver: &sun4i_codec_component, |
1787 | dai_drv: &dummy_cpu_dai, num_dai: 1); |
1788 | if (ret) { |
1789 | dev_err(&pdev->dev, "Failed to register our DAI\n" ); |
1790 | goto err_assert_reset; |
1791 | } |
1792 | |
1793 | ret = devm_snd_dmaengine_pcm_register(dev: &pdev->dev, NULL, flags: 0); |
1794 | if (ret) { |
1795 | dev_err(&pdev->dev, "Failed to register against DMAEngine\n" ); |
1796 | goto err_assert_reset; |
1797 | } |
1798 | |
1799 | card = quirks->create_card(&pdev->dev); |
1800 | if (IS_ERR(ptr: card)) { |
1801 | ret = PTR_ERR(ptr: card); |
1802 | dev_err(&pdev->dev, "Failed to create our card\n" ); |
1803 | goto err_assert_reset; |
1804 | } |
1805 | |
1806 | snd_soc_card_set_drvdata(card, data: scodec); |
1807 | |
1808 | ret = snd_soc_register_card(card); |
1809 | if (ret) { |
1810 | dev_err_probe(dev: &pdev->dev, err: ret, fmt: "Failed to register our card\n" ); |
1811 | goto err_assert_reset; |
1812 | } |
1813 | |
1814 | return 0; |
1815 | |
1816 | err_assert_reset: |
1817 | if (scodec->rst) |
1818 | reset_control_assert(rstc: scodec->rst); |
1819 | err_clk_disable: |
1820 | clk_disable_unprepare(clk: scodec->clk_apb); |
1821 | return ret; |
1822 | } |
1823 | |
1824 | static void sun4i_codec_remove(struct platform_device *pdev) |
1825 | { |
1826 | struct snd_soc_card *card = platform_get_drvdata(pdev); |
1827 | struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card); |
1828 | |
1829 | snd_soc_unregister_card(card); |
1830 | if (scodec->rst) |
1831 | reset_control_assert(rstc: scodec->rst); |
1832 | clk_disable_unprepare(clk: scodec->clk_apb); |
1833 | } |
1834 | |
1835 | static struct platform_driver sun4i_codec_driver = { |
1836 | .driver = { |
1837 | .name = "sun4i-codec" , |
1838 | .of_match_table = sun4i_codec_of_match, |
1839 | }, |
1840 | .probe = sun4i_codec_probe, |
1841 | .remove_new = sun4i_codec_remove, |
1842 | }; |
1843 | module_platform_driver(sun4i_codec_driver); |
1844 | |
1845 | MODULE_DESCRIPTION("Allwinner A10 codec driver" ); |
1846 | MODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>" ); |
1847 | MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>" ); |
1848 | MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>" ); |
1849 | MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>" ); |
1850 | MODULE_LICENSE("GPL" ); |
1851 | |