1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * This driver supports the digital controls for the internal codec |
4 | * found in Allwinner's A33 SoCs. |
5 | * |
6 | * (C) Copyright 2010-2016 |
7 | * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com> |
8 | * huangxin <huangxin@Reuuimllatech.com> |
9 | * Mylène Josserand <mylene.josserand@free-electrons.com> |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/delay.h> |
14 | #include <linux/clk.h> |
15 | #include <linux/io.h> |
16 | #include <linux/of.h> |
17 | #include <linux/pm_runtime.h> |
18 | #include <linux/regmap.h> |
19 | #include <linux/log2.h> |
20 | |
21 | #include <sound/pcm_params.h> |
22 | #include <sound/soc.h> |
23 | #include <sound/soc-dapm.h> |
24 | #include <sound/tlv.h> |
25 | |
26 | #define SUN8I_SYSCLK_CTL 0x00c |
27 | #define SUN8I_SYSCLK_CTL_AIF1CLK_ENA 11 |
28 | #define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL (0x2 << 8) |
29 | #define SUN8I_SYSCLK_CTL_AIF2CLK_ENA 7 |
30 | #define SUN8I_SYSCLK_CTL_AIF2CLK_SRC_PLL (0x2 << 4) |
31 | #define SUN8I_SYSCLK_CTL_SYSCLK_ENA 3 |
32 | #define SUN8I_SYSCLK_CTL_SYSCLK_SRC 0 |
33 | #define SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF1CLK (0x0 << 0) |
34 | #define SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF2CLK (0x1 << 0) |
35 | #define SUN8I_MOD_CLK_ENA 0x010 |
36 | #define SUN8I_MOD_CLK_ENA_AIF1 15 |
37 | #define SUN8I_MOD_CLK_ENA_AIF2 14 |
38 | #define SUN8I_MOD_CLK_ENA_AIF3 13 |
39 | #define SUN8I_MOD_CLK_ENA_ADC 3 |
40 | #define SUN8I_MOD_CLK_ENA_DAC 2 |
41 | #define SUN8I_MOD_RST_CTL 0x014 |
42 | #define SUN8I_MOD_RST_CTL_AIF1 15 |
43 | #define SUN8I_MOD_RST_CTL_AIF2 14 |
44 | #define SUN8I_MOD_RST_CTL_AIF3 13 |
45 | #define SUN8I_MOD_RST_CTL_ADC 3 |
46 | #define SUN8I_MOD_RST_CTL_DAC 2 |
47 | #define SUN8I_SYS_SR_CTRL 0x018 |
48 | #define SUN8I_SYS_SR_CTRL_AIF1_FS 12 |
49 | #define SUN8I_SYS_SR_CTRL_AIF2_FS 8 |
50 | #define SUN8I_AIF_CLK_CTRL(n) (0x040 * (1 + (n))) |
51 | #define SUN8I_AIF_CLK_CTRL_MSTR_MOD 15 |
52 | #define SUN8I_AIF_CLK_CTRL_CLK_INV 13 |
53 | #define SUN8I_AIF_CLK_CTRL_BCLK_DIV 9 |
54 | #define SUN8I_AIF_CLK_CTRL_LRCK_DIV 6 |
55 | #define SUN8I_AIF_CLK_CTRL_WORD_SIZ 4 |
56 | #define SUN8I_AIF_CLK_CTRL_DATA_FMT 2 |
57 | #define SUN8I_AIF1_ADCDAT_CTRL 0x044 |
58 | #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA 15 |
59 | #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA 14 |
60 | #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_SRC 10 |
61 | #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_SRC 8 |
62 | #define SUN8I_AIF1_DACDAT_CTRL 0x048 |
63 | #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA 15 |
64 | #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA 14 |
65 | #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_SRC 10 |
66 | #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_SRC 8 |
67 | #define SUN8I_AIF1_MXR_SRC 0x04c |
68 | #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L 15 |
69 | #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL 14 |
70 | #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL 13 |
71 | #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR 12 |
72 | #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R 11 |
73 | #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR 10 |
74 | #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR 9 |
75 | #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL 8 |
76 | #define SUN8I_AIF1_VOL_CTRL1 0x050 |
77 | #define SUN8I_AIF1_VOL_CTRL1_AD0L_VOL 8 |
78 | #define SUN8I_AIF1_VOL_CTRL1_AD0R_VOL 0 |
79 | #define SUN8I_AIF1_VOL_CTRL3 0x058 |
80 | #define SUN8I_AIF1_VOL_CTRL3_DA0L_VOL 8 |
81 | #define SUN8I_AIF1_VOL_CTRL3_DA0R_VOL 0 |
82 | #define SUN8I_AIF2_ADCDAT_CTRL 0x084 |
83 | #define SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCL_ENA 15 |
84 | #define SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCR_ENA 14 |
85 | #define SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCL_SRC 10 |
86 | #define SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCR_SRC 8 |
87 | #define SUN8I_AIF2_DACDAT_CTRL 0x088 |
88 | #define SUN8I_AIF2_DACDAT_CTRL_AIF2_DACL_ENA 15 |
89 | #define SUN8I_AIF2_DACDAT_CTRL_AIF2_DACR_ENA 14 |
90 | #define SUN8I_AIF2_DACDAT_CTRL_AIF2_DACL_SRC 10 |
91 | #define SUN8I_AIF2_DACDAT_CTRL_AIF2_DACR_SRC 8 |
92 | #define SUN8I_AIF2_MXR_SRC 0x08c |
93 | #define SUN8I_AIF2_MXR_SRC_ADCL_MXR_SRC_AIF1DA0L 15 |
94 | #define SUN8I_AIF2_MXR_SRC_ADCL_MXR_SRC_AIF1DA1L 14 |
95 | #define SUN8I_AIF2_MXR_SRC_ADCL_MXR_SRC_AIF2DACR 13 |
96 | #define SUN8I_AIF2_MXR_SRC_ADCL_MXR_SRC_ADCL 12 |
97 | #define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF1DA0R 11 |
98 | #define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF1DA1R 10 |
99 | #define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF2DACL 9 |
100 | #define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_ADCR 8 |
101 | #define SUN8I_AIF2_VOL_CTRL1 0x090 |
102 | #define SUN8I_AIF2_VOL_CTRL1_ADCL_VOL 8 |
103 | #define SUN8I_AIF2_VOL_CTRL1_ADCR_VOL 0 |
104 | #define SUN8I_AIF2_VOL_CTRL2 0x098 |
105 | #define SUN8I_AIF2_VOL_CTRL2_DACL_VOL 8 |
106 | #define SUN8I_AIF2_VOL_CTRL2_DACR_VOL 0 |
107 | #define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF1 (0x0 << 0) |
108 | #define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF2 (0x1 << 0) |
109 | #define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF1CLK (0x2 << 0) |
110 | #define SUN8I_AIF3_PATH_CTRL 0x0cc |
111 | #define SUN8I_AIF3_PATH_CTRL_AIF3_ADC_SRC 10 |
112 | #define SUN8I_AIF3_PATH_CTRL_AIF2_DAC_SRC 8 |
113 | #define SUN8I_AIF3_PATH_CTRL_AIF3_PINS_TRI 7 |
114 | #define SUN8I_ADC_DIG_CTRL 0x100 |
115 | #define SUN8I_ADC_DIG_CTRL_ENAD 15 |
116 | #define SUN8I_ADC_DIG_CTRL_ADOUT_DTS 2 |
117 | #define SUN8I_ADC_DIG_CTRL_ADOUT_DLY 1 |
118 | #define SUN8I_ADC_VOL_CTRL 0x104 |
119 | #define SUN8I_ADC_VOL_CTRL_ADCL_VOL 8 |
120 | #define SUN8I_ADC_VOL_CTRL_ADCR_VOL 0 |
121 | #define SUN8I_DAC_DIG_CTRL 0x120 |
122 | #define SUN8I_DAC_DIG_CTRL_ENDA 15 |
123 | #define SUN8I_DAC_VOL_CTRL 0x124 |
124 | #define SUN8I_DAC_VOL_CTRL_DACL_VOL 8 |
125 | #define SUN8I_DAC_VOL_CTRL_DACR_VOL 0 |
126 | #define SUN8I_DAC_MXR_SRC 0x130 |
127 | #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L 15 |
128 | #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L 14 |
129 | #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL 13 |
130 | #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL 12 |
131 | #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R 11 |
132 | #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R 10 |
133 | #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR 9 |
134 | #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR 8 |
135 | |
136 | #define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_MASK GENMASK(9, 8) |
137 | #define SUN8I_SYSCLK_CTL_AIF2CLK_SRC_MASK GENMASK(5, 4) |
138 | #define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK GENMASK(15, 12) |
139 | #define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK GENMASK(11, 8) |
140 | #define SUN8I_AIF_CLK_CTRL_CLK_INV_MASK GENMASK(14, 13) |
141 | #define SUN8I_AIF_CLK_CTRL_BCLK_DIV_MASK GENMASK(12, 9) |
142 | #define SUN8I_AIF_CLK_CTRL_LRCK_DIV_MASK GENMASK(8, 6) |
143 | #define SUN8I_AIF_CLK_CTRL_WORD_SIZ_MASK GENMASK(5, 4) |
144 | #define SUN8I_AIF_CLK_CTRL_DATA_FMT_MASK GENMASK(3, 2) |
145 | #define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_MASK GENMASK(1, 0) |
146 | |
147 | #define SUN8I_CODEC_PASSTHROUGH_SAMPLE_RATE 48000 |
148 | |
149 | #define SUN8I_CODEC_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 |\ |
150 | SNDRV_PCM_FMTBIT_S16_LE |\ |
151 | SNDRV_PCM_FMTBIT_S20_LE |\ |
152 | SNDRV_PCM_FMTBIT_S24_LE |\ |
153 | SNDRV_PCM_FMTBIT_S20_3LE|\ |
154 | SNDRV_PCM_FMTBIT_S24_3LE) |
155 | |
156 | #define SUN8I_CODEC_PCM_RATES (SNDRV_PCM_RATE_8000_48000|\ |
157 | SNDRV_PCM_RATE_88200 |\ |
158 | SNDRV_PCM_RATE_96000 |\ |
159 | SNDRV_PCM_RATE_176400 |\ |
160 | SNDRV_PCM_RATE_192000 |\ |
161 | SNDRV_PCM_RATE_KNOT) |
162 | |
163 | enum { |
164 | SUN8I_CODEC_AIF1, |
165 | SUN8I_CODEC_AIF2, |
166 | SUN8I_CODEC_AIF3, |
167 | SUN8I_CODEC_NAIFS |
168 | }; |
169 | |
170 | struct sun8i_codec_aif { |
171 | unsigned int lrck_div_order; |
172 | unsigned int sample_rate; |
173 | unsigned int slots; |
174 | unsigned int slot_width; |
175 | unsigned int active_streams : 2; |
176 | unsigned int open_streams : 2; |
177 | }; |
178 | |
179 | struct sun8i_codec_quirks { |
180 | bool legacy_widgets : 1; |
181 | bool lrck_inversion : 1; |
182 | }; |
183 | |
184 | struct sun8i_codec { |
185 | struct regmap *regmap; |
186 | struct clk *clk_module; |
187 | const struct sun8i_codec_quirks *quirks; |
188 | struct sun8i_codec_aif aifs[SUN8I_CODEC_NAIFS]; |
189 | unsigned int sysclk_rate; |
190 | int sysclk_refcnt; |
191 | }; |
192 | |
193 | static struct snd_soc_dai_driver sun8i_codec_dais[]; |
194 | |
195 | static int sun8i_codec_runtime_resume(struct device *dev) |
196 | { |
197 | struct sun8i_codec *scodec = dev_get_drvdata(dev); |
198 | int ret; |
199 | |
200 | regcache_cache_only(map: scodec->regmap, enable: false); |
201 | |
202 | ret = regcache_sync(map: scodec->regmap); |
203 | if (ret) { |
204 | dev_err(dev, "Failed to sync regmap cache\n" ); |
205 | return ret; |
206 | } |
207 | |
208 | return 0; |
209 | } |
210 | |
211 | static int sun8i_codec_runtime_suspend(struct device *dev) |
212 | { |
213 | struct sun8i_codec *scodec = dev_get_drvdata(dev); |
214 | |
215 | regcache_cache_only(map: scodec->regmap, enable: true); |
216 | regcache_mark_dirty(map: scodec->regmap); |
217 | |
218 | return 0; |
219 | } |
220 | |
221 | static int sun8i_codec_get_hw_rate(unsigned int sample_rate) |
222 | { |
223 | switch (sample_rate) { |
224 | case 7350: |
225 | case 8000: |
226 | return 0x0; |
227 | case 11025: |
228 | return 0x1; |
229 | case 12000: |
230 | return 0x2; |
231 | case 14700: |
232 | case 16000: |
233 | return 0x3; |
234 | case 22050: |
235 | return 0x4; |
236 | case 24000: |
237 | return 0x5; |
238 | case 29400: |
239 | case 32000: |
240 | return 0x6; |
241 | case 44100: |
242 | return 0x7; |
243 | case 48000: |
244 | return 0x8; |
245 | case 88200: |
246 | case 96000: |
247 | return 0x9; |
248 | case 176400: |
249 | case 192000: |
250 | return 0xa; |
251 | default: |
252 | return -EINVAL; |
253 | } |
254 | } |
255 | |
256 | static int sun8i_codec_update_sample_rate(struct sun8i_codec *scodec) |
257 | { |
258 | unsigned int max_rate = 0; |
259 | int hw_rate, i; |
260 | |
261 | for (i = SUN8I_CODEC_AIF1; i < SUN8I_CODEC_NAIFS; ++i) { |
262 | struct sun8i_codec_aif *aif = &scodec->aifs[i]; |
263 | |
264 | if (aif->active_streams) |
265 | max_rate = max(max_rate, aif->sample_rate); |
266 | } |
267 | |
268 | /* Set the sample rate for ADC->DAC passthrough when no AIF is active. */ |
269 | if (!max_rate) |
270 | max_rate = SUN8I_CODEC_PASSTHROUGH_SAMPLE_RATE; |
271 | |
272 | hw_rate = sun8i_codec_get_hw_rate(sample_rate: max_rate); |
273 | if (hw_rate < 0) |
274 | return hw_rate; |
275 | |
276 | regmap_update_bits(map: scodec->regmap, SUN8I_SYS_SR_CTRL, |
277 | SUN8I_SYS_SR_CTRL_AIF1_FS_MASK, |
278 | val: hw_rate << SUN8I_SYS_SR_CTRL_AIF1_FS); |
279 | |
280 | return 0; |
281 | } |
282 | |
283 | static int sun8i_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
284 | { |
285 | struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai); |
286 | u32 dsp_format, format, invert, value; |
287 | |
288 | /* clock masters */ |
289 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
290 | case SND_SOC_DAIFMT_CBC_CFC: /* Codec slave, DAI master */ |
291 | value = 0x1; |
292 | break; |
293 | case SND_SOC_DAIFMT_CBP_CFP: /* Codec Master, DAI slave */ |
294 | value = 0x0; |
295 | break; |
296 | default: |
297 | return -EINVAL; |
298 | } |
299 | |
300 | if (dai->id == SUN8I_CODEC_AIF3) { |
301 | /* AIF3 only supports master mode. */ |
302 | if (value) |
303 | return -EINVAL; |
304 | |
305 | /* Use the AIF2 BCLK and LRCK for AIF3. */ |
306 | regmap_update_bits(map: scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id), |
307 | SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_MASK, |
308 | SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF2); |
309 | } else { |
310 | regmap_update_bits(map: scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id), |
311 | BIT(SUN8I_AIF_CLK_CTRL_MSTR_MOD), |
312 | val: value << SUN8I_AIF_CLK_CTRL_MSTR_MOD); |
313 | } |
314 | |
315 | /* DAI format */ |
316 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
317 | case SND_SOC_DAIFMT_I2S: |
318 | format = 0x0; |
319 | break; |
320 | case SND_SOC_DAIFMT_LEFT_J: |
321 | format = 0x1; |
322 | break; |
323 | case SND_SOC_DAIFMT_RIGHT_J: |
324 | format = 0x2; |
325 | break; |
326 | case SND_SOC_DAIFMT_DSP_A: |
327 | format = 0x3; |
328 | dsp_format = 0x0; /* Set LRCK_INV to 0 */ |
329 | break; |
330 | case SND_SOC_DAIFMT_DSP_B: |
331 | format = 0x3; |
332 | dsp_format = 0x1; /* Set LRCK_INV to 1 */ |
333 | break; |
334 | default: |
335 | return -EINVAL; |
336 | } |
337 | |
338 | if (dai->id == SUN8I_CODEC_AIF3) { |
339 | /* AIF3 only supports DSP mode. */ |
340 | if (format != 3) |
341 | return -EINVAL; |
342 | } else { |
343 | regmap_update_bits(map: scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id), |
344 | SUN8I_AIF_CLK_CTRL_DATA_FMT_MASK, |
345 | val: format << SUN8I_AIF_CLK_CTRL_DATA_FMT); |
346 | } |
347 | |
348 | /* clock inversion */ |
349 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
350 | case SND_SOC_DAIFMT_NB_NF: /* Normal */ |
351 | invert = 0x0; |
352 | break; |
353 | case SND_SOC_DAIFMT_NB_IF: /* Inverted LRCK */ |
354 | invert = 0x1; |
355 | break; |
356 | case SND_SOC_DAIFMT_IB_NF: /* Inverted BCLK */ |
357 | invert = 0x2; |
358 | break; |
359 | case SND_SOC_DAIFMT_IB_IF: /* Both inverted */ |
360 | invert = 0x3; |
361 | break; |
362 | default: |
363 | return -EINVAL; |
364 | } |
365 | |
366 | if (format == 0x3) { |
367 | /* Inverted LRCK is not available in DSP mode. */ |
368 | if (invert & BIT(0)) |
369 | return -EINVAL; |
370 | |
371 | /* Instead, the bit selects between DSP A/B formats. */ |
372 | invert |= dsp_format; |
373 | } else { |
374 | /* |
375 | * It appears that the DAI and the codec in the A33 SoC don't |
376 | * share the same polarity for the LRCK signal when they mean |
377 | * 'normal' and 'inverted' in the datasheet. |
378 | * |
379 | * Since the DAI here is our regular i2s driver that have been |
380 | * tested with way more codecs than just this one, it means |
381 | * that the codec probably gets it backward, and we have to |
382 | * invert the value here. |
383 | */ |
384 | invert ^= scodec->quirks->lrck_inversion; |
385 | } |
386 | |
387 | regmap_update_bits(map: scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id), |
388 | SUN8I_AIF_CLK_CTRL_CLK_INV_MASK, |
389 | val: invert << SUN8I_AIF_CLK_CTRL_CLK_INV); |
390 | |
391 | return 0; |
392 | } |
393 | |
394 | static int sun8i_codec_set_tdm_slot(struct snd_soc_dai *dai, |
395 | unsigned int tx_mask, unsigned int rx_mask, |
396 | int slots, int slot_width) |
397 | { |
398 | struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai); |
399 | struct sun8i_codec_aif *aif = &scodec->aifs[dai->id]; |
400 | |
401 | if (slot_width && !is_power_of_2(n: slot_width)) |
402 | return -EINVAL; |
403 | |
404 | aif->slots = slots; |
405 | aif->slot_width = slot_width; |
406 | |
407 | return 0; |
408 | } |
409 | |
410 | static const unsigned int sun8i_codec_rates[] = { |
411 | 7350, 8000, 11025, 12000, 14700, 16000, 22050, 24000, |
412 | 29400, 32000, 44100, 48000, 88200, 96000, 176400, 192000, |
413 | }; |
414 | |
415 | static const struct snd_pcm_hw_constraint_list sun8i_codec_all_rates = { |
416 | .list = sun8i_codec_rates, |
417 | .count = ARRAY_SIZE(sun8i_codec_rates), |
418 | }; |
419 | |
420 | static const struct snd_pcm_hw_constraint_list sun8i_codec_22M_rates = { |
421 | .list = sun8i_codec_rates, |
422 | .count = ARRAY_SIZE(sun8i_codec_rates), |
423 | .mask = 0x5555, |
424 | }; |
425 | |
426 | static const struct snd_pcm_hw_constraint_list sun8i_codec_24M_rates = { |
427 | .list = sun8i_codec_rates, |
428 | .count = ARRAY_SIZE(sun8i_codec_rates), |
429 | .mask = 0xaaaa, |
430 | }; |
431 | |
432 | static int sun8i_codec_startup(struct snd_pcm_substream *substream, |
433 | struct snd_soc_dai *dai) |
434 | { |
435 | struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai); |
436 | const struct snd_pcm_hw_constraint_list *list; |
437 | |
438 | /* hw_constraints is not relevant for codec2codec DAIs. */ |
439 | if (dai->id != SUN8I_CODEC_AIF1) |
440 | return 0; |
441 | |
442 | if (!scodec->sysclk_refcnt) |
443 | list = &sun8i_codec_all_rates; |
444 | else if (scodec->sysclk_rate == 22579200) |
445 | list = &sun8i_codec_22M_rates; |
446 | else if (scodec->sysclk_rate == 24576000) |
447 | list = &sun8i_codec_24M_rates; |
448 | else |
449 | return -EINVAL; |
450 | |
451 | return snd_pcm_hw_constraint_list(runtime: substream->runtime, cond: 0, |
452 | SNDRV_PCM_HW_PARAM_RATE, l: list); |
453 | } |
454 | |
455 | struct sun8i_codec_clk_div { |
456 | u8 div; |
457 | u8 val; |
458 | }; |
459 | |
460 | static const struct sun8i_codec_clk_div sun8i_codec_bclk_div[] = { |
461 | { .div = 1, .val = 0 }, |
462 | { .div = 2, .val = 1 }, |
463 | { .div = 4, .val = 2 }, |
464 | { .div = 6, .val = 3 }, |
465 | { .div = 8, .val = 4 }, |
466 | { .div = 12, .val = 5 }, |
467 | { .div = 16, .val = 6 }, |
468 | { .div = 24, .val = 7 }, |
469 | { .div = 32, .val = 8 }, |
470 | { .div = 48, .val = 9 }, |
471 | { .div = 64, .val = 10 }, |
472 | { .div = 96, .val = 11 }, |
473 | { .div = 128, .val = 12 }, |
474 | { .div = 192, .val = 13 }, |
475 | }; |
476 | |
477 | static int sun8i_codec_get_bclk_div(unsigned int sysclk_rate, |
478 | unsigned int lrck_div_order, |
479 | unsigned int sample_rate) |
480 | { |
481 | unsigned int div = sysclk_rate / sample_rate >> lrck_div_order; |
482 | int i; |
483 | |
484 | for (i = 0; i < ARRAY_SIZE(sun8i_codec_bclk_div); i++) { |
485 | const struct sun8i_codec_clk_div *bdiv = &sun8i_codec_bclk_div[i]; |
486 | |
487 | if (bdiv->div == div) |
488 | return bdiv->val; |
489 | } |
490 | |
491 | return -EINVAL; |
492 | } |
493 | |
494 | static int sun8i_codec_get_lrck_div_order(unsigned int slots, |
495 | unsigned int slot_width) |
496 | { |
497 | unsigned int div = slots * slot_width; |
498 | |
499 | if (div < 16 || div > 256) |
500 | return -EINVAL; |
501 | |
502 | return order_base_2(div); |
503 | } |
504 | |
505 | static unsigned int sun8i_codec_get_sysclk_rate(unsigned int sample_rate) |
506 | { |
507 | return (sample_rate % 4000) ? 22579200 : 24576000; |
508 | } |
509 | |
510 | static int sun8i_codec_hw_params(struct snd_pcm_substream *substream, |
511 | struct snd_pcm_hw_params *params, |
512 | struct snd_soc_dai *dai) |
513 | { |
514 | struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai); |
515 | struct sun8i_codec_aif *aif = &scodec->aifs[dai->id]; |
516 | unsigned int sample_rate = params_rate(p: params); |
517 | unsigned int slots = aif->slots ?: params_channels(p: params); |
518 | unsigned int slot_width = aif->slot_width ?: params_width(p: params); |
519 | unsigned int sysclk_rate = sun8i_codec_get_sysclk_rate(sample_rate); |
520 | int bclk_div, lrck_div_order, ret, word_size; |
521 | u32 clk_reg; |
522 | |
523 | /* word size */ |
524 | switch (params_width(p: params)) { |
525 | case 8: |
526 | word_size = 0x0; |
527 | break; |
528 | case 16: |
529 | word_size = 0x1; |
530 | break; |
531 | case 20: |
532 | word_size = 0x2; |
533 | break; |
534 | case 24: |
535 | word_size = 0x3; |
536 | break; |
537 | default: |
538 | return -EINVAL; |
539 | } |
540 | |
541 | regmap_update_bits(map: scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id), |
542 | SUN8I_AIF_CLK_CTRL_WORD_SIZ_MASK, |
543 | val: word_size << SUN8I_AIF_CLK_CTRL_WORD_SIZ); |
544 | |
545 | /* LRCK divider (BCLK/LRCK ratio) */ |
546 | lrck_div_order = sun8i_codec_get_lrck_div_order(slots, slot_width); |
547 | if (lrck_div_order < 0) |
548 | return lrck_div_order; |
549 | |
550 | if (dai->id == SUN8I_CODEC_AIF2 || dai->id == SUN8I_CODEC_AIF3) { |
551 | /* AIF2 and AIF3 share AIF2's BCLK and LRCK generation circuitry. */ |
552 | int partner = (SUN8I_CODEC_AIF2 + SUN8I_CODEC_AIF3) - dai->id; |
553 | const struct sun8i_codec_aif *partner_aif = &scodec->aifs[partner]; |
554 | const char *partner_name = sun8i_codec_dais[partner].name; |
555 | |
556 | if (partner_aif->open_streams && |
557 | (lrck_div_order != partner_aif->lrck_div_order || |
558 | sample_rate != partner_aif->sample_rate)) { |
559 | dev_err(dai->dev, |
560 | "%s sample and bit rates must match %s when both are used\n" , |
561 | dai->name, partner_name); |
562 | return -EBUSY; |
563 | } |
564 | |
565 | clk_reg = SUN8I_AIF_CLK_CTRL(SUN8I_CODEC_AIF2); |
566 | } else { |
567 | clk_reg = SUN8I_AIF_CLK_CTRL(dai->id); |
568 | } |
569 | |
570 | regmap_update_bits(map: scodec->regmap, reg: clk_reg, |
571 | SUN8I_AIF_CLK_CTRL_LRCK_DIV_MASK, |
572 | val: (lrck_div_order - 4) << SUN8I_AIF_CLK_CTRL_LRCK_DIV); |
573 | |
574 | /* BCLK divider (SYSCLK/BCLK ratio) */ |
575 | bclk_div = sun8i_codec_get_bclk_div(sysclk_rate, lrck_div_order, sample_rate); |
576 | if (bclk_div < 0) |
577 | return bclk_div; |
578 | |
579 | regmap_update_bits(map: scodec->regmap, reg: clk_reg, |
580 | SUN8I_AIF_CLK_CTRL_BCLK_DIV_MASK, |
581 | val: bclk_div << SUN8I_AIF_CLK_CTRL_BCLK_DIV); |
582 | |
583 | /* |
584 | * SYSCLK rate |
585 | * |
586 | * Clock rate protection is reference counted; but hw_params may be |
587 | * called many times per substream, without matching calls to hw_free. |
588 | * Protect the clock rate once per AIF, on the first hw_params call |
589 | * for the first substream. clk_set_rate() will allow clock rate |
590 | * changes on subsequent calls if only one AIF has open streams. |
591 | */ |
592 | ret = (aif->open_streams ? clk_set_rate : clk_set_rate_exclusive)(scodec->clk_module, |
593 | sysclk_rate); |
594 | if (ret == -EBUSY) |
595 | dev_err(dai->dev, |
596 | "%s sample rate (%u Hz) conflicts with other audio streams\n" , |
597 | dai->name, sample_rate); |
598 | if (ret < 0) |
599 | return ret; |
600 | |
601 | if (!aif->open_streams) |
602 | scodec->sysclk_refcnt++; |
603 | scodec->sysclk_rate = sysclk_rate; |
604 | |
605 | aif->lrck_div_order = lrck_div_order; |
606 | aif->sample_rate = sample_rate; |
607 | aif->open_streams |= BIT(substream->stream); |
608 | |
609 | return sun8i_codec_update_sample_rate(scodec); |
610 | } |
611 | |
612 | static int sun8i_codec_hw_free(struct snd_pcm_substream *substream, |
613 | struct snd_soc_dai *dai) |
614 | { |
615 | struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai); |
616 | struct sun8i_codec_aif *aif = &scodec->aifs[dai->id]; |
617 | |
618 | /* Drop references when the last substream for the AIF is freed. */ |
619 | if (aif->open_streams != BIT(substream->stream)) |
620 | goto done; |
621 | |
622 | clk_rate_exclusive_put(clk: scodec->clk_module); |
623 | scodec->sysclk_refcnt--; |
624 | aif->lrck_div_order = 0; |
625 | aif->sample_rate = 0; |
626 | |
627 | done: |
628 | aif->open_streams &= ~BIT(substream->stream); |
629 | return 0; |
630 | } |
631 | |
632 | static const struct snd_soc_dai_ops sun8i_codec_dai_ops = { |
633 | .set_fmt = sun8i_codec_set_fmt, |
634 | .set_tdm_slot = sun8i_codec_set_tdm_slot, |
635 | .startup = sun8i_codec_startup, |
636 | .hw_params = sun8i_codec_hw_params, |
637 | .hw_free = sun8i_codec_hw_free, |
638 | }; |
639 | |
640 | static struct snd_soc_dai_driver sun8i_codec_dais[] = { |
641 | { |
642 | .name = "sun8i-codec-aif1" , |
643 | .id = SUN8I_CODEC_AIF1, |
644 | .ops = &sun8i_codec_dai_ops, |
645 | /* capture capabilities */ |
646 | .capture = { |
647 | .stream_name = "AIF1 Capture" , |
648 | .channels_min = 1, |
649 | .channels_max = 2, |
650 | .rates = SUN8I_CODEC_PCM_RATES, |
651 | .formats = SUN8I_CODEC_PCM_FORMATS, |
652 | .sig_bits = 24, |
653 | }, |
654 | /* playback capabilities */ |
655 | .playback = { |
656 | .stream_name = "AIF1 Playback" , |
657 | .channels_min = 1, |
658 | .channels_max = 2, |
659 | .rates = SUN8I_CODEC_PCM_RATES, |
660 | .formats = SUN8I_CODEC_PCM_FORMATS, |
661 | }, |
662 | .symmetric_rate = true, |
663 | .symmetric_channels = true, |
664 | .symmetric_sample_bits = true, |
665 | }, |
666 | { |
667 | .name = "sun8i-codec-aif2" , |
668 | .id = SUN8I_CODEC_AIF2, |
669 | .ops = &sun8i_codec_dai_ops, |
670 | /* capture capabilities */ |
671 | .capture = { |
672 | .stream_name = "AIF2 Capture" , |
673 | .channels_min = 1, |
674 | .channels_max = 2, |
675 | .rates = SUN8I_CODEC_PCM_RATES, |
676 | .formats = SUN8I_CODEC_PCM_FORMATS, |
677 | .sig_bits = 24, |
678 | }, |
679 | /* playback capabilities */ |
680 | .playback = { |
681 | .stream_name = "AIF2 Playback" , |
682 | .channels_min = 1, |
683 | .channels_max = 2, |
684 | .rates = SUN8I_CODEC_PCM_RATES, |
685 | .formats = SUN8I_CODEC_PCM_FORMATS, |
686 | }, |
687 | .symmetric_rate = true, |
688 | .symmetric_channels = true, |
689 | .symmetric_sample_bits = true, |
690 | }, |
691 | { |
692 | .name = "sun8i-codec-aif3" , |
693 | .id = SUN8I_CODEC_AIF3, |
694 | .ops = &sun8i_codec_dai_ops, |
695 | /* capture capabilities */ |
696 | .capture = { |
697 | .stream_name = "AIF3 Capture" , |
698 | .channels_min = 1, |
699 | .channels_max = 1, |
700 | .rates = SUN8I_CODEC_PCM_RATES, |
701 | .formats = SUN8I_CODEC_PCM_FORMATS, |
702 | .sig_bits = 24, |
703 | }, |
704 | /* playback capabilities */ |
705 | .playback = { |
706 | .stream_name = "AIF3 Playback" , |
707 | .channels_min = 1, |
708 | .channels_max = 1, |
709 | .rates = SUN8I_CODEC_PCM_RATES, |
710 | .formats = SUN8I_CODEC_PCM_FORMATS, |
711 | }, |
712 | .symmetric_rate = true, |
713 | .symmetric_channels = true, |
714 | .symmetric_sample_bits = true, |
715 | }, |
716 | }; |
717 | |
718 | static const DECLARE_TLV_DB_SCALE(sun8i_codec_vol_scale, -12000, 75, 1); |
719 | |
720 | static const struct snd_kcontrol_new sun8i_codec_controls[] = { |
721 | SOC_DOUBLE_TLV("AIF1 AD0 Capture Volume" , |
722 | SUN8I_AIF1_VOL_CTRL1, |
723 | SUN8I_AIF1_VOL_CTRL1_AD0L_VOL, |
724 | SUN8I_AIF1_VOL_CTRL1_AD0R_VOL, |
725 | 0xc0, 0, sun8i_codec_vol_scale), |
726 | SOC_DOUBLE_TLV("AIF1 DA0 Playback Volume" , |
727 | SUN8I_AIF1_VOL_CTRL3, |
728 | SUN8I_AIF1_VOL_CTRL3_DA0L_VOL, |
729 | SUN8I_AIF1_VOL_CTRL3_DA0R_VOL, |
730 | 0xc0, 0, sun8i_codec_vol_scale), |
731 | SOC_DOUBLE_TLV("AIF2 ADC Capture Volume" , |
732 | SUN8I_AIF2_VOL_CTRL1, |
733 | SUN8I_AIF2_VOL_CTRL1_ADCL_VOL, |
734 | SUN8I_AIF2_VOL_CTRL1_ADCR_VOL, |
735 | 0xc0, 0, sun8i_codec_vol_scale), |
736 | SOC_DOUBLE_TLV("AIF2 DAC Playback Volume" , |
737 | SUN8I_AIF2_VOL_CTRL2, |
738 | SUN8I_AIF2_VOL_CTRL2_DACL_VOL, |
739 | SUN8I_AIF2_VOL_CTRL2_DACR_VOL, |
740 | 0xc0, 0, sun8i_codec_vol_scale), |
741 | SOC_DOUBLE_TLV("ADC Capture Volume" , |
742 | SUN8I_ADC_VOL_CTRL, |
743 | SUN8I_ADC_VOL_CTRL_ADCL_VOL, |
744 | SUN8I_ADC_VOL_CTRL_ADCR_VOL, |
745 | 0xc0, 0, sun8i_codec_vol_scale), |
746 | SOC_DOUBLE_TLV("DAC Playback Volume" , |
747 | SUN8I_DAC_VOL_CTRL, |
748 | SUN8I_DAC_VOL_CTRL_DACL_VOL, |
749 | SUN8I_DAC_VOL_CTRL_DACR_VOL, |
750 | 0xc0, 0, sun8i_codec_vol_scale), |
751 | }; |
752 | |
753 | static int sun8i_codec_aif_event(struct snd_soc_dapm_widget *w, |
754 | struct snd_kcontrol *kcontrol, int event) |
755 | { |
756 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: w->dapm); |
757 | struct sun8i_codec *scodec = snd_soc_component_get_drvdata(c: component); |
758 | struct sun8i_codec_aif *aif = &scodec->aifs[w->sname[3] - '1']; |
759 | int stream = w->id == snd_soc_dapm_aif_out; |
760 | |
761 | if (SND_SOC_DAPM_EVENT_ON(event)) |
762 | aif->active_streams |= BIT(stream); |
763 | else |
764 | aif->active_streams &= ~BIT(stream); |
765 | |
766 | return sun8i_codec_update_sample_rate(scodec); |
767 | } |
768 | |
769 | static const char *const sun8i_aif_stereo_mux_enum_values[] = { |
770 | "Stereo" , "Reverse Stereo" , "Sum Mono" , "Mix Mono" |
771 | }; |
772 | |
773 | static SOC_ENUM_DOUBLE_DECL(sun8i_aif1_ad0_stereo_mux_enum, |
774 | SUN8I_AIF1_ADCDAT_CTRL, |
775 | SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_SRC, |
776 | SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_SRC, |
777 | sun8i_aif_stereo_mux_enum_values); |
778 | |
779 | static const struct snd_kcontrol_new sun8i_aif1_ad0_stereo_mux_control = |
780 | SOC_DAPM_ENUM("AIF1 AD0 Stereo Capture Route" , |
781 | sun8i_aif1_ad0_stereo_mux_enum); |
782 | |
783 | static SOC_ENUM_DOUBLE_DECL(sun8i_aif2_adc_stereo_mux_enum, |
784 | SUN8I_AIF2_ADCDAT_CTRL, |
785 | SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCL_SRC, |
786 | SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCR_SRC, |
787 | sun8i_aif_stereo_mux_enum_values); |
788 | |
789 | static const struct snd_kcontrol_new sun8i_aif2_adc_stereo_mux_control = |
790 | SOC_DAPM_ENUM("AIF2 ADC Stereo Capture Route" , |
791 | sun8i_aif2_adc_stereo_mux_enum); |
792 | |
793 | static const char *const sun8i_aif3_adc_mux_enum_values[] = { |
794 | "None" , "AIF2 ADCL" , "AIF2 ADCR" |
795 | }; |
796 | |
797 | static SOC_ENUM_SINGLE_DECL(sun8i_aif3_adc_mux_enum, |
798 | SUN8I_AIF3_PATH_CTRL, |
799 | SUN8I_AIF3_PATH_CTRL_AIF3_ADC_SRC, |
800 | sun8i_aif3_adc_mux_enum_values); |
801 | |
802 | static const struct snd_kcontrol_new sun8i_aif3_adc_mux_control = |
803 | SOC_DAPM_ENUM("AIF3 ADC Source Capture Route" , |
804 | sun8i_aif3_adc_mux_enum); |
805 | |
806 | static const struct snd_kcontrol_new sun8i_aif1_ad0_mixer_controls[] = { |
807 | SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch" , |
808 | SUN8I_AIF1_MXR_SRC, |
809 | SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L, |
810 | SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R, 1, 0), |
811 | SOC_DAPM_DOUBLE("AIF2 Digital ADC Capture Switch" , |
812 | SUN8I_AIF1_MXR_SRC, |
813 | SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL, |
814 | SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR, 1, 0), |
815 | SOC_DAPM_DOUBLE("AIF1 Data Digital ADC Capture Switch" , |
816 | SUN8I_AIF1_MXR_SRC, |
817 | SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL, |
818 | SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR, 1, 0), |
819 | SOC_DAPM_DOUBLE("AIF2 Inv Digital ADC Capture Switch" , |
820 | SUN8I_AIF1_MXR_SRC, |
821 | SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR, |
822 | SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0), |
823 | }; |
824 | |
825 | static const struct snd_kcontrol_new sun8i_aif2_adc_mixer_controls[] = { |
826 | SOC_DAPM_DOUBLE("AIF2 ADC Mixer AIF1 DA0 Capture Switch" , |
827 | SUN8I_AIF2_MXR_SRC, |
828 | SUN8I_AIF2_MXR_SRC_ADCL_MXR_SRC_AIF1DA0L, |
829 | SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF1DA0R, 1, 0), |
830 | SOC_DAPM_DOUBLE("AIF2 ADC Mixer AIF1 DA1 Capture Switch" , |
831 | SUN8I_AIF2_MXR_SRC, |
832 | SUN8I_AIF2_MXR_SRC_ADCL_MXR_SRC_AIF1DA1L, |
833 | SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF1DA1R, 1, 0), |
834 | SOC_DAPM_DOUBLE("AIF2 ADC Mixer AIF2 DAC Rev Capture Switch" , |
835 | SUN8I_AIF2_MXR_SRC, |
836 | SUN8I_AIF2_MXR_SRC_ADCL_MXR_SRC_AIF2DACR, |
837 | SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF2DACL, 1, 0), |
838 | SOC_DAPM_DOUBLE("AIF2 ADC Mixer ADC Capture Switch" , |
839 | SUN8I_AIF2_MXR_SRC, |
840 | SUN8I_AIF2_MXR_SRC_ADCL_MXR_SRC_ADCL, |
841 | SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_ADCR, 1, 0), |
842 | }; |
843 | |
844 | static const char *const sun8i_aif2_dac_mux_enum_values[] = { |
845 | "AIF2" , "AIF3+2" , "AIF2+3" |
846 | }; |
847 | |
848 | static SOC_ENUM_SINGLE_DECL(sun8i_aif2_dac_mux_enum, |
849 | SUN8I_AIF3_PATH_CTRL, |
850 | SUN8I_AIF3_PATH_CTRL_AIF2_DAC_SRC, |
851 | sun8i_aif2_dac_mux_enum_values); |
852 | |
853 | static const struct snd_kcontrol_new sun8i_aif2_dac_mux_control = |
854 | SOC_DAPM_ENUM("AIF2 DAC Source Playback Route" , |
855 | sun8i_aif2_dac_mux_enum); |
856 | |
857 | static SOC_ENUM_DOUBLE_DECL(sun8i_aif1_da0_stereo_mux_enum, |
858 | SUN8I_AIF1_DACDAT_CTRL, |
859 | SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_SRC, |
860 | SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_SRC, |
861 | sun8i_aif_stereo_mux_enum_values); |
862 | |
863 | static const struct snd_kcontrol_new sun8i_aif1_da0_stereo_mux_control = |
864 | SOC_DAPM_ENUM("AIF1 DA0 Stereo Playback Route" , |
865 | sun8i_aif1_da0_stereo_mux_enum); |
866 | |
867 | static SOC_ENUM_DOUBLE_DECL(sun8i_aif2_dac_stereo_mux_enum, |
868 | SUN8I_AIF2_DACDAT_CTRL, |
869 | SUN8I_AIF2_DACDAT_CTRL_AIF2_DACL_SRC, |
870 | SUN8I_AIF2_DACDAT_CTRL_AIF2_DACR_SRC, |
871 | sun8i_aif_stereo_mux_enum_values); |
872 | |
873 | static const struct snd_kcontrol_new sun8i_aif2_dac_stereo_mux_control = |
874 | SOC_DAPM_ENUM("AIF2 DAC Stereo Playback Route" , |
875 | sun8i_aif2_dac_stereo_mux_enum); |
876 | |
877 | static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = { |
878 | SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital DAC Playback Switch" , |
879 | SUN8I_DAC_MXR_SRC, |
880 | SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L, |
881 | SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R, 1, 0), |
882 | SOC_DAPM_DOUBLE("AIF1 Slot 1 Digital DAC Playback Switch" , |
883 | SUN8I_DAC_MXR_SRC, |
884 | SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L, |
885 | SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R, 1, 0), |
886 | SOC_DAPM_DOUBLE("AIF2 Digital DAC Playback Switch" , SUN8I_DAC_MXR_SRC, |
887 | SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL, |
888 | SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR, 1, 0), |
889 | SOC_DAPM_DOUBLE("ADC Digital DAC Playback Switch" , SUN8I_DAC_MXR_SRC, |
890 | SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL, |
891 | SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR, 1, 0), |
892 | }; |
893 | |
894 | static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { |
895 | /* System Clocks */ |
896 | SND_SOC_DAPM_CLOCK_SUPPLY("mod" ), |
897 | |
898 | SND_SOC_DAPM_SUPPLY("AIF1CLK" , |
899 | SUN8I_SYSCLK_CTL, |
900 | SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0), |
901 | SND_SOC_DAPM_SUPPLY("AIF2CLK" , |
902 | SUN8I_SYSCLK_CTL, |
903 | SUN8I_SYSCLK_CTL_AIF2CLK_ENA, 0, NULL, 0), |
904 | SND_SOC_DAPM_SUPPLY("SYSCLK" , |
905 | SUN8I_SYSCLK_CTL, |
906 | SUN8I_SYSCLK_CTL_SYSCLK_ENA, 0, NULL, 0), |
907 | |
908 | /* Module Clocks */ |
909 | SND_SOC_DAPM_SUPPLY("CLK AIF1" , |
910 | SUN8I_MOD_CLK_ENA, |
911 | SUN8I_MOD_CLK_ENA_AIF1, 0, NULL, 0), |
912 | SND_SOC_DAPM_SUPPLY("CLK AIF2" , |
913 | SUN8I_MOD_CLK_ENA, |
914 | SUN8I_MOD_CLK_ENA_AIF2, 0, NULL, 0), |
915 | SND_SOC_DAPM_SUPPLY("CLK AIF3" , |
916 | SUN8I_MOD_CLK_ENA, |
917 | SUN8I_MOD_CLK_ENA_AIF3, 0, NULL, 0), |
918 | SND_SOC_DAPM_SUPPLY("CLK ADC" , |
919 | SUN8I_MOD_CLK_ENA, |
920 | SUN8I_MOD_CLK_ENA_ADC, 0, NULL, 0), |
921 | SND_SOC_DAPM_SUPPLY("CLK DAC" , |
922 | SUN8I_MOD_CLK_ENA, |
923 | SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0), |
924 | |
925 | /* Module Resets */ |
926 | SND_SOC_DAPM_SUPPLY("RST AIF1" , |
927 | SUN8I_MOD_RST_CTL, |
928 | SUN8I_MOD_RST_CTL_AIF1, 0, NULL, 0), |
929 | SND_SOC_DAPM_SUPPLY("RST AIF2" , |
930 | SUN8I_MOD_RST_CTL, |
931 | SUN8I_MOD_RST_CTL_AIF2, 0, NULL, 0), |
932 | SND_SOC_DAPM_SUPPLY("RST AIF3" , |
933 | SUN8I_MOD_RST_CTL, |
934 | SUN8I_MOD_RST_CTL_AIF3, 0, NULL, 0), |
935 | SND_SOC_DAPM_SUPPLY("RST ADC" , |
936 | SUN8I_MOD_RST_CTL, |
937 | SUN8I_MOD_RST_CTL_ADC, 0, NULL, 0), |
938 | SND_SOC_DAPM_SUPPLY("RST DAC" , |
939 | SUN8I_MOD_RST_CTL, |
940 | SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0), |
941 | |
942 | /* Module Supplies */ |
943 | SND_SOC_DAPM_SUPPLY("ADC" , |
944 | SUN8I_ADC_DIG_CTRL, |
945 | SUN8I_ADC_DIG_CTRL_ENAD, 0, NULL, 0), |
946 | SND_SOC_DAPM_SUPPLY("DAC" , |
947 | SUN8I_DAC_DIG_CTRL, |
948 | SUN8I_DAC_DIG_CTRL_ENDA, 0, NULL, 0), |
949 | |
950 | /* AIF "ADC" Outputs */ |
951 | SND_SOC_DAPM_AIF_OUT_E("AIF1 AD0L" , "AIF1 Capture" , 0, |
952 | SUN8I_AIF1_ADCDAT_CTRL, |
953 | SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0, |
954 | sun8i_codec_aif_event, |
955 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
956 | SND_SOC_DAPM_AIF_OUT("AIF1 AD0R" , "AIF1 Capture" , 1, |
957 | SUN8I_AIF1_ADCDAT_CTRL, |
958 | SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0), |
959 | |
960 | SND_SOC_DAPM_AIF_OUT_E("AIF2 ADCL" , "AIF2 Capture" , 0, |
961 | SUN8I_AIF2_ADCDAT_CTRL, |
962 | SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCL_ENA, 0, |
963 | sun8i_codec_aif_event, |
964 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
965 | SND_SOC_DAPM_AIF_OUT("AIF2 ADCR" , "AIF2 Capture" , 1, |
966 | SUN8I_AIF2_ADCDAT_CTRL, |
967 | SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCR_ENA, 0), |
968 | |
969 | SND_SOC_DAPM_AIF_OUT_E("AIF3 ADC" , "AIF3 Capture" , 0, |
970 | SND_SOC_NOPM, 0, 0, |
971 | sun8i_codec_aif_event, |
972 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
973 | |
974 | /* AIF "ADC" Mono/Stereo Muxes */ |
975 | SND_SOC_DAPM_MUX("AIF1 AD0L Stereo Mux" , SND_SOC_NOPM, 0, 0, |
976 | &sun8i_aif1_ad0_stereo_mux_control), |
977 | SND_SOC_DAPM_MUX("AIF1 AD0R Stereo Mux" , SND_SOC_NOPM, 0, 0, |
978 | &sun8i_aif1_ad0_stereo_mux_control), |
979 | |
980 | SND_SOC_DAPM_MUX("AIF2 ADCL Stereo Mux" , SND_SOC_NOPM, 0, 0, |
981 | &sun8i_aif2_adc_stereo_mux_control), |
982 | SND_SOC_DAPM_MUX("AIF2 ADCR Stereo Mux" , SND_SOC_NOPM, 0, 0, |
983 | &sun8i_aif2_adc_stereo_mux_control), |
984 | |
985 | /* AIF "ADC" Output Muxes */ |
986 | SND_SOC_DAPM_MUX("AIF3 ADC Source Capture Route" , SND_SOC_NOPM, 0, 0, |
987 | &sun8i_aif3_adc_mux_control), |
988 | |
989 | /* AIF "ADC" Mixers */ |
990 | SOC_MIXER_ARRAY("AIF1 AD0L Mixer" , SND_SOC_NOPM, 0, 0, |
991 | sun8i_aif1_ad0_mixer_controls), |
992 | SOC_MIXER_ARRAY("AIF1 AD0R Mixer" , SND_SOC_NOPM, 0, 0, |
993 | sun8i_aif1_ad0_mixer_controls), |
994 | |
995 | SOC_MIXER_ARRAY("AIF2 ADCL Mixer" , SND_SOC_NOPM, 0, 0, |
996 | sun8i_aif2_adc_mixer_controls), |
997 | SOC_MIXER_ARRAY("AIF2 ADCR Mixer" , SND_SOC_NOPM, 0, 0, |
998 | sun8i_aif2_adc_mixer_controls), |
999 | |
1000 | /* AIF "DAC" Input Muxes */ |
1001 | SND_SOC_DAPM_MUX("AIF2 DACL Source" , SND_SOC_NOPM, 0, 0, |
1002 | &sun8i_aif2_dac_mux_control), |
1003 | SND_SOC_DAPM_MUX("AIF2 DACR Source" , SND_SOC_NOPM, 0, 0, |
1004 | &sun8i_aif2_dac_mux_control), |
1005 | |
1006 | /* AIF "DAC" Mono/Stereo Muxes */ |
1007 | SND_SOC_DAPM_MUX("AIF1 DA0L Stereo Mux" , SND_SOC_NOPM, 0, 0, |
1008 | &sun8i_aif1_da0_stereo_mux_control), |
1009 | SND_SOC_DAPM_MUX("AIF1 DA0R Stereo Mux" , SND_SOC_NOPM, 0, 0, |
1010 | &sun8i_aif1_da0_stereo_mux_control), |
1011 | |
1012 | SND_SOC_DAPM_MUX("AIF2 DACL Stereo Mux" , SND_SOC_NOPM, 0, 0, |
1013 | &sun8i_aif2_dac_stereo_mux_control), |
1014 | SND_SOC_DAPM_MUX("AIF2 DACR Stereo Mux" , SND_SOC_NOPM, 0, 0, |
1015 | &sun8i_aif2_dac_stereo_mux_control), |
1016 | |
1017 | /* AIF "DAC" Inputs */ |
1018 | SND_SOC_DAPM_AIF_IN_E("AIF1 DA0L" , "AIF1 Playback" , 0, |
1019 | SUN8I_AIF1_DACDAT_CTRL, |
1020 | SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0, |
1021 | sun8i_codec_aif_event, |
1022 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
1023 | SND_SOC_DAPM_AIF_IN("AIF1 DA0R" , "AIF1 Playback" , 1, |
1024 | SUN8I_AIF1_DACDAT_CTRL, |
1025 | SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0), |
1026 | |
1027 | SND_SOC_DAPM_AIF_IN_E("AIF2 DACL" , "AIF2 Playback" , 0, |
1028 | SUN8I_AIF2_DACDAT_CTRL, |
1029 | SUN8I_AIF2_DACDAT_CTRL_AIF2_DACL_ENA, 0, |
1030 | sun8i_codec_aif_event, |
1031 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
1032 | SND_SOC_DAPM_AIF_IN("AIF2 DACR" , "AIF2 Playback" , 1, |
1033 | SUN8I_AIF2_DACDAT_CTRL, |
1034 | SUN8I_AIF2_DACDAT_CTRL_AIF2_DACR_ENA, 0), |
1035 | |
1036 | SND_SOC_DAPM_AIF_IN_E("AIF3 DAC" , "AIF3 Playback" , 0, |
1037 | SND_SOC_NOPM, 0, 0, |
1038 | sun8i_codec_aif_event, |
1039 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
1040 | |
1041 | /* ADC Inputs (connected to analog codec DAPM context) */ |
1042 | SND_SOC_DAPM_ADC("ADCL" , NULL, SND_SOC_NOPM, 0, 0), |
1043 | SND_SOC_DAPM_ADC("ADCR" , NULL, SND_SOC_NOPM, 0, 0), |
1044 | |
1045 | /* DAC Outputs (connected to analog codec DAPM context) */ |
1046 | SND_SOC_DAPM_DAC("DACL" , NULL, SND_SOC_NOPM, 0, 0), |
1047 | SND_SOC_DAPM_DAC("DACR" , NULL, SND_SOC_NOPM, 0, 0), |
1048 | |
1049 | /* DAC Mixers */ |
1050 | SOC_MIXER_ARRAY("DACL Mixer" , SND_SOC_NOPM, 0, 0, |
1051 | sun8i_dac_mixer_controls), |
1052 | SOC_MIXER_ARRAY("DACR Mixer" , SND_SOC_NOPM, 0, 0, |
1053 | sun8i_dac_mixer_controls), |
1054 | }; |
1055 | |
1056 | static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { |
1057 | /* Clock Routes */ |
1058 | { "AIF1CLK" , NULL, "mod" }, |
1059 | |
1060 | { "SYSCLK" , NULL, "AIF1CLK" }, |
1061 | |
1062 | { "CLK AIF1" , NULL, "AIF1CLK" }, |
1063 | { "CLK AIF1" , NULL, "SYSCLK" }, |
1064 | { "RST AIF1" , NULL, "CLK AIF1" }, |
1065 | { "AIF1 AD0L" , NULL, "RST AIF1" }, |
1066 | { "AIF1 AD0R" , NULL, "RST AIF1" }, |
1067 | { "AIF1 DA0L" , NULL, "RST AIF1" }, |
1068 | { "AIF1 DA0R" , NULL, "RST AIF1" }, |
1069 | |
1070 | { "CLK AIF2" , NULL, "AIF2CLK" }, |
1071 | { "CLK AIF2" , NULL, "SYSCLK" }, |
1072 | { "RST AIF2" , NULL, "CLK AIF2" }, |
1073 | { "AIF2 ADCL" , NULL, "RST AIF2" }, |
1074 | { "AIF2 ADCR" , NULL, "RST AIF2" }, |
1075 | { "AIF2 DACL" , NULL, "RST AIF2" }, |
1076 | { "AIF2 DACR" , NULL, "RST AIF2" }, |
1077 | |
1078 | { "CLK AIF3" , NULL, "AIF1CLK" }, |
1079 | { "CLK AIF3" , NULL, "SYSCLK" }, |
1080 | { "RST AIF3" , NULL, "CLK AIF3" }, |
1081 | { "AIF3 ADC" , NULL, "RST AIF3" }, |
1082 | { "AIF3 DAC" , NULL, "RST AIF3" }, |
1083 | |
1084 | { "CLK ADC" , NULL, "SYSCLK" }, |
1085 | { "RST ADC" , NULL, "CLK ADC" }, |
1086 | { "ADC" , NULL, "RST ADC" }, |
1087 | { "ADCL" , NULL, "ADC" }, |
1088 | { "ADCR" , NULL, "ADC" }, |
1089 | |
1090 | { "CLK DAC" , NULL, "SYSCLK" }, |
1091 | { "RST DAC" , NULL, "CLK DAC" }, |
1092 | { "DAC" , NULL, "RST DAC" }, |
1093 | { "DACL" , NULL, "DAC" }, |
1094 | { "DACR" , NULL, "DAC" }, |
1095 | |
1096 | /* AIF "ADC" Output Routes */ |
1097 | { "AIF1 AD0L" , NULL, "AIF1 AD0L Stereo Mux" }, |
1098 | { "AIF1 AD0R" , NULL, "AIF1 AD0R Stereo Mux" }, |
1099 | |
1100 | { "AIF2 ADCL" , NULL, "AIF2 ADCL Stereo Mux" }, |
1101 | { "AIF2 ADCR" , NULL, "AIF2 ADCR Stereo Mux" }, |
1102 | |
1103 | { "AIF3 ADC" , NULL, "AIF3 ADC Source Capture Route" }, |
1104 | |
1105 | /* AIF "ADC" Mono/Stereo Mux Routes */ |
1106 | { "AIF1 AD0L Stereo Mux" , "Stereo" , "AIF1 AD0L Mixer" }, |
1107 | { "AIF1 AD0L Stereo Mux" , "Reverse Stereo" , "AIF1 AD0R Mixer" }, |
1108 | { "AIF1 AD0L Stereo Mux" , "Sum Mono" , "AIF1 AD0L Mixer" }, |
1109 | { "AIF1 AD0L Stereo Mux" , "Sum Mono" , "AIF1 AD0R Mixer" }, |
1110 | { "AIF1 AD0L Stereo Mux" , "Mix Mono" , "AIF1 AD0L Mixer" }, |
1111 | { "AIF1 AD0L Stereo Mux" , "Mix Mono" , "AIF1 AD0R Mixer" }, |
1112 | |
1113 | { "AIF1 AD0R Stereo Mux" , "Stereo" , "AIF1 AD0R Mixer" }, |
1114 | { "AIF1 AD0R Stereo Mux" , "Reverse Stereo" , "AIF1 AD0L Mixer" }, |
1115 | { "AIF1 AD0R Stereo Mux" , "Sum Mono" , "AIF1 AD0L Mixer" }, |
1116 | { "AIF1 AD0R Stereo Mux" , "Sum Mono" , "AIF1 AD0R Mixer" }, |
1117 | { "AIF1 AD0R Stereo Mux" , "Mix Mono" , "AIF1 AD0L Mixer" }, |
1118 | { "AIF1 AD0R Stereo Mux" , "Mix Mono" , "AIF1 AD0R Mixer" }, |
1119 | |
1120 | { "AIF2 ADCL Stereo Mux" , "Stereo" , "AIF2 ADCL Mixer" }, |
1121 | { "AIF2 ADCL Stereo Mux" , "Reverse Stereo" , "AIF2 ADCR Mixer" }, |
1122 | { "AIF2 ADCL Stereo Mux" , "Sum Mono" , "AIF2 ADCL Mixer" }, |
1123 | { "AIF2 ADCL Stereo Mux" , "Sum Mono" , "AIF2 ADCR Mixer" }, |
1124 | { "AIF2 ADCL Stereo Mux" , "Mix Mono" , "AIF2 ADCL Mixer" }, |
1125 | { "AIF2 ADCL Stereo Mux" , "Mix Mono" , "AIF2 ADCR Mixer" }, |
1126 | |
1127 | { "AIF2 ADCR Stereo Mux" , "Stereo" , "AIF2 ADCR Mixer" }, |
1128 | { "AIF2 ADCR Stereo Mux" , "Reverse Stereo" , "AIF2 ADCL Mixer" }, |
1129 | { "AIF2 ADCR Stereo Mux" , "Sum Mono" , "AIF2 ADCL Mixer" }, |
1130 | { "AIF2 ADCR Stereo Mux" , "Sum Mono" , "AIF2 ADCR Mixer" }, |
1131 | { "AIF2 ADCR Stereo Mux" , "Mix Mono" , "AIF2 ADCL Mixer" }, |
1132 | { "AIF2 ADCR Stereo Mux" , "Mix Mono" , "AIF2 ADCR Mixer" }, |
1133 | |
1134 | /* AIF "ADC" Output Mux Routes */ |
1135 | { "AIF3 ADC Source Capture Route" , "AIF2 ADCL" , "AIF2 ADCL Mixer" }, |
1136 | { "AIF3 ADC Source Capture Route" , "AIF2 ADCR" , "AIF2 ADCR Mixer" }, |
1137 | |
1138 | /* AIF "ADC" Mixer Routes */ |
1139 | { "AIF1 AD0L Mixer" , "AIF1 Slot 0 Digital ADC Capture Switch" , "AIF1 DA0L Stereo Mux" }, |
1140 | { "AIF1 AD0L Mixer" , "AIF2 Digital ADC Capture Switch" , "AIF2 DACL Source" }, |
1141 | { "AIF1 AD0L Mixer" , "AIF1 Data Digital ADC Capture Switch" , "ADCL" }, |
1142 | { "AIF1 AD0L Mixer" , "AIF2 Inv Digital ADC Capture Switch" , "AIF2 DACR Source" }, |
1143 | |
1144 | { "AIF1 AD0R Mixer" , "AIF1 Slot 0 Digital ADC Capture Switch" , "AIF1 DA0R Stereo Mux" }, |
1145 | { "AIF1 AD0R Mixer" , "AIF2 Digital ADC Capture Switch" , "AIF2 DACR Source" }, |
1146 | { "AIF1 AD0R Mixer" , "AIF1 Data Digital ADC Capture Switch" , "ADCR" }, |
1147 | { "AIF1 AD0R Mixer" , "AIF2 Inv Digital ADC Capture Switch" , "AIF2 DACL Source" }, |
1148 | |
1149 | { "AIF2 ADCL Mixer" , "AIF2 ADC Mixer AIF1 DA0 Capture Switch" , "AIF1 DA0L Stereo Mux" }, |
1150 | { "AIF2 ADCL Mixer" , "AIF2 ADC Mixer AIF2 DAC Rev Capture Switch" , "AIF2 DACR Source" }, |
1151 | { "AIF2 ADCL Mixer" , "AIF2 ADC Mixer ADC Capture Switch" , "ADCL" }, |
1152 | |
1153 | { "AIF2 ADCR Mixer" , "AIF2 ADC Mixer AIF1 DA0 Capture Switch" , "AIF1 DA0R Stereo Mux" }, |
1154 | { "AIF2 ADCR Mixer" , "AIF2 ADC Mixer AIF2 DAC Rev Capture Switch" , "AIF2 DACL Source" }, |
1155 | { "AIF2 ADCR Mixer" , "AIF2 ADC Mixer ADC Capture Switch" , "ADCR" }, |
1156 | |
1157 | /* AIF "DAC" Input Mux Routes */ |
1158 | { "AIF2 DACL Source" , "AIF2" , "AIF2 DACL Stereo Mux" }, |
1159 | { "AIF2 DACL Source" , "AIF3+2" , "AIF3 DAC" }, |
1160 | { "AIF2 DACL Source" , "AIF2+3" , "AIF2 DACL Stereo Mux" }, |
1161 | |
1162 | { "AIF2 DACR Source" , "AIF2" , "AIF2 DACR Stereo Mux" }, |
1163 | { "AIF2 DACR Source" , "AIF3+2" , "AIF2 DACR Stereo Mux" }, |
1164 | { "AIF2 DACR Source" , "AIF2+3" , "AIF3 DAC" }, |
1165 | |
1166 | /* AIF "DAC" Mono/Stereo Mux Routes */ |
1167 | { "AIF1 DA0L Stereo Mux" , "Stereo" , "AIF1 DA0L" }, |
1168 | { "AIF1 DA0L Stereo Mux" , "Reverse Stereo" , "AIF1 DA0R" }, |
1169 | { "AIF1 DA0L Stereo Mux" , "Sum Mono" , "AIF1 DA0L" }, |
1170 | { "AIF1 DA0L Stereo Mux" , "Sum Mono" , "AIF1 DA0R" }, |
1171 | { "AIF1 DA0L Stereo Mux" , "Mix Mono" , "AIF1 DA0L" }, |
1172 | { "AIF1 DA0L Stereo Mux" , "Mix Mono" , "AIF1 DA0R" }, |
1173 | |
1174 | { "AIF1 DA0R Stereo Mux" , "Stereo" , "AIF1 DA0R" }, |
1175 | { "AIF1 DA0R Stereo Mux" , "Reverse Stereo" , "AIF1 DA0L" }, |
1176 | { "AIF1 DA0R Stereo Mux" , "Sum Mono" , "AIF1 DA0L" }, |
1177 | { "AIF1 DA0R Stereo Mux" , "Sum Mono" , "AIF1 DA0R" }, |
1178 | { "AIF1 DA0R Stereo Mux" , "Mix Mono" , "AIF1 DA0L" }, |
1179 | { "AIF1 DA0R Stereo Mux" , "Mix Mono" , "AIF1 DA0R" }, |
1180 | |
1181 | { "AIF2 DACL Stereo Mux" , "Stereo" , "AIF2 DACL" }, |
1182 | { "AIF2 DACL Stereo Mux" , "Reverse Stereo" , "AIF2 DACR" }, |
1183 | { "AIF2 DACL Stereo Mux" , "Sum Mono" , "AIF2 DACL" }, |
1184 | { "AIF2 DACL Stereo Mux" , "Sum Mono" , "AIF2 DACR" }, |
1185 | { "AIF2 DACL Stereo Mux" , "Mix Mono" , "AIF2 DACL" }, |
1186 | { "AIF2 DACL Stereo Mux" , "Mix Mono" , "AIF2 DACR" }, |
1187 | |
1188 | { "AIF2 DACR Stereo Mux" , "Stereo" , "AIF2 DACR" }, |
1189 | { "AIF2 DACR Stereo Mux" , "Reverse Stereo" , "AIF2 DACL" }, |
1190 | { "AIF2 DACR Stereo Mux" , "Sum Mono" , "AIF2 DACL" }, |
1191 | { "AIF2 DACR Stereo Mux" , "Sum Mono" , "AIF2 DACR" }, |
1192 | { "AIF2 DACR Stereo Mux" , "Mix Mono" , "AIF2 DACL" }, |
1193 | { "AIF2 DACR Stereo Mux" , "Mix Mono" , "AIF2 DACR" }, |
1194 | |
1195 | /* DAC Output Routes */ |
1196 | { "DACL" , NULL, "DACL Mixer" }, |
1197 | { "DACR" , NULL, "DACR Mixer" }, |
1198 | |
1199 | /* DAC Mixer Routes */ |
1200 | { "DACL Mixer" , "AIF1 Slot 0 Digital DAC Playback Switch" , "AIF1 DA0L Stereo Mux" }, |
1201 | { "DACL Mixer" , "AIF2 Digital DAC Playback Switch" , "AIF2 DACL Source" }, |
1202 | { "DACL Mixer" , "ADC Digital DAC Playback Switch" , "ADCL" }, |
1203 | |
1204 | { "DACR Mixer" , "AIF1 Slot 0 Digital DAC Playback Switch" , "AIF1 DA0R Stereo Mux" }, |
1205 | { "DACR Mixer" , "AIF2 Digital DAC Playback Switch" , "AIF2 DACR Source" }, |
1206 | { "DACR Mixer" , "ADC Digital DAC Playback Switch" , "ADCR" }, |
1207 | }; |
1208 | |
1209 | static const struct snd_soc_dapm_widget sun8i_codec_legacy_widgets[] = { |
1210 | /* Legacy ADC Inputs (connected to analog codec DAPM context) */ |
1211 | SND_SOC_DAPM_ADC("AIF1 Slot 0 Left ADC" , NULL, SND_SOC_NOPM, 0, 0), |
1212 | SND_SOC_DAPM_ADC("AIF1 Slot 0 Right ADC" , NULL, SND_SOC_NOPM, 0, 0), |
1213 | |
1214 | /* Legacy DAC Outputs (connected to analog codec DAPM context) */ |
1215 | SND_SOC_DAPM_DAC("AIF1 Slot 0 Left" , NULL, SND_SOC_NOPM, 0, 0), |
1216 | SND_SOC_DAPM_DAC("AIF1 Slot 0 Right" , NULL, SND_SOC_NOPM, 0, 0), |
1217 | }; |
1218 | |
1219 | static const struct snd_soc_dapm_route sun8i_codec_legacy_routes[] = { |
1220 | /* Legacy ADC Routes */ |
1221 | { "ADCL" , NULL, "AIF1 Slot 0 Left ADC" }, |
1222 | { "ADCR" , NULL, "AIF1 Slot 0 Right ADC" }, |
1223 | |
1224 | /* Legacy DAC Routes */ |
1225 | { "AIF1 Slot 0 Left" , NULL, "DACL" }, |
1226 | { "AIF1 Slot 0 Right" , NULL, "DACR" }, |
1227 | }; |
1228 | |
1229 | static int sun8i_codec_component_probe(struct snd_soc_component *component) |
1230 | { |
1231 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); |
1232 | struct sun8i_codec *scodec = snd_soc_component_get_drvdata(c: component); |
1233 | int ret; |
1234 | |
1235 | /* Add widgets for backward compatibility with old device trees. */ |
1236 | if (scodec->quirks->legacy_widgets) { |
1237 | ret = snd_soc_dapm_new_controls(dapm, widget: sun8i_codec_legacy_widgets, |
1238 | ARRAY_SIZE(sun8i_codec_legacy_widgets)); |
1239 | if (ret) |
1240 | return ret; |
1241 | |
1242 | ret = snd_soc_dapm_add_routes(dapm, route: sun8i_codec_legacy_routes, |
1243 | ARRAY_SIZE(sun8i_codec_legacy_routes)); |
1244 | if (ret) |
1245 | return ret; |
1246 | } |
1247 | |
1248 | /* |
1249 | * AIF1CLK and AIF2CLK share a pair of clock parents: PLL_AUDIO ("mod") |
1250 | * and MCLK (from the CPU DAI connected to AIF1). MCLK's parent is also |
1251 | * PLL_AUDIO, so using it adds no additional flexibility. Use PLL_AUDIO |
1252 | * directly to simplify the clock tree. |
1253 | */ |
1254 | regmap_update_bits(map: scodec->regmap, SUN8I_SYSCLK_CTL, |
1255 | SUN8I_SYSCLK_CTL_AIF1CLK_SRC_MASK | |
1256 | SUN8I_SYSCLK_CTL_AIF2CLK_SRC_MASK, |
1257 | SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL | |
1258 | SUN8I_SYSCLK_CTL_AIF2CLK_SRC_PLL); |
1259 | |
1260 | /* Use AIF1CLK as the SYSCLK parent since AIF1 is used most often. */ |
1261 | regmap_update_bits(map: scodec->regmap, SUN8I_SYSCLK_CTL, |
1262 | BIT(SUN8I_SYSCLK_CTL_SYSCLK_SRC), |
1263 | SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF1CLK); |
1264 | |
1265 | /* Program the default sample rate. */ |
1266 | sun8i_codec_update_sample_rate(scodec); |
1267 | |
1268 | return 0; |
1269 | } |
1270 | |
1271 | static const struct snd_soc_component_driver sun8i_soc_component = { |
1272 | .controls = sun8i_codec_controls, |
1273 | .num_controls = ARRAY_SIZE(sun8i_codec_controls), |
1274 | .dapm_widgets = sun8i_codec_dapm_widgets, |
1275 | .num_dapm_widgets = ARRAY_SIZE(sun8i_codec_dapm_widgets), |
1276 | .dapm_routes = sun8i_codec_dapm_routes, |
1277 | .num_dapm_routes = ARRAY_SIZE(sun8i_codec_dapm_routes), |
1278 | .probe = sun8i_codec_component_probe, |
1279 | .idle_bias_on = 1, |
1280 | .endianness = 1, |
1281 | }; |
1282 | |
1283 | static const struct regmap_config sun8i_codec_regmap_config = { |
1284 | .reg_bits = 32, |
1285 | .reg_stride = 4, |
1286 | .val_bits = 32, |
1287 | .max_register = SUN8I_DAC_MXR_SRC, |
1288 | |
1289 | .cache_type = REGCACHE_FLAT, |
1290 | }; |
1291 | |
1292 | static int sun8i_codec_probe(struct platform_device *pdev) |
1293 | { |
1294 | struct sun8i_codec *scodec; |
1295 | void __iomem *base; |
1296 | int ret; |
1297 | |
1298 | scodec = devm_kzalloc(dev: &pdev->dev, size: sizeof(*scodec), GFP_KERNEL); |
1299 | if (!scodec) |
1300 | return -ENOMEM; |
1301 | |
1302 | scodec->clk_module = devm_clk_get(dev: &pdev->dev, id: "mod" ); |
1303 | if (IS_ERR(ptr: scodec->clk_module)) { |
1304 | dev_err(&pdev->dev, "Failed to get the module clock\n" ); |
1305 | return PTR_ERR(ptr: scodec->clk_module); |
1306 | } |
1307 | |
1308 | base = devm_platform_ioremap_resource(pdev, index: 0); |
1309 | if (IS_ERR(ptr: base)) { |
1310 | dev_err(&pdev->dev, "Failed to map the registers\n" ); |
1311 | return PTR_ERR(ptr: base); |
1312 | } |
1313 | |
1314 | scodec->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "bus" , base, |
1315 | &sun8i_codec_regmap_config); |
1316 | if (IS_ERR(ptr: scodec->regmap)) { |
1317 | dev_err(&pdev->dev, "Failed to create our regmap\n" ); |
1318 | return PTR_ERR(ptr: scodec->regmap); |
1319 | } |
1320 | |
1321 | scodec->quirks = of_device_get_match_data(dev: &pdev->dev); |
1322 | |
1323 | platform_set_drvdata(pdev, data: scodec); |
1324 | |
1325 | pm_runtime_enable(dev: &pdev->dev); |
1326 | if (!pm_runtime_enabled(dev: &pdev->dev)) { |
1327 | ret = sun8i_codec_runtime_resume(dev: &pdev->dev); |
1328 | if (ret) |
1329 | goto err_pm_disable; |
1330 | } |
1331 | |
1332 | ret = devm_snd_soc_register_component(dev: &pdev->dev, component_driver: &sun8i_soc_component, |
1333 | dai_drv: sun8i_codec_dais, |
1334 | ARRAY_SIZE(sun8i_codec_dais)); |
1335 | if (ret) { |
1336 | dev_err(&pdev->dev, "Failed to register codec\n" ); |
1337 | goto err_suspend; |
1338 | } |
1339 | |
1340 | return ret; |
1341 | |
1342 | err_suspend: |
1343 | if (!pm_runtime_status_suspended(dev: &pdev->dev)) |
1344 | sun8i_codec_runtime_suspend(dev: &pdev->dev); |
1345 | |
1346 | err_pm_disable: |
1347 | pm_runtime_disable(dev: &pdev->dev); |
1348 | |
1349 | return ret; |
1350 | } |
1351 | |
1352 | static void sun8i_codec_remove(struct platform_device *pdev) |
1353 | { |
1354 | pm_runtime_disable(dev: &pdev->dev); |
1355 | if (!pm_runtime_status_suspended(dev: &pdev->dev)) |
1356 | sun8i_codec_runtime_suspend(dev: &pdev->dev); |
1357 | } |
1358 | |
1359 | static const struct sun8i_codec_quirks sun8i_a33_quirks = { |
1360 | .legacy_widgets = true, |
1361 | .lrck_inversion = true, |
1362 | }; |
1363 | |
1364 | static const struct sun8i_codec_quirks sun50i_a64_quirks = { |
1365 | }; |
1366 | |
1367 | static const struct of_device_id sun8i_codec_of_match[] = { |
1368 | { .compatible = "allwinner,sun8i-a33-codec" , .data = &sun8i_a33_quirks }, |
1369 | { .compatible = "allwinner,sun50i-a64-codec" , .data = &sun50i_a64_quirks }, |
1370 | {} |
1371 | }; |
1372 | MODULE_DEVICE_TABLE(of, sun8i_codec_of_match); |
1373 | |
1374 | static const struct dev_pm_ops sun8i_codec_pm_ops = { |
1375 | SET_RUNTIME_PM_OPS(sun8i_codec_runtime_suspend, |
1376 | sun8i_codec_runtime_resume, NULL) |
1377 | }; |
1378 | |
1379 | static struct platform_driver sun8i_codec_driver = { |
1380 | .driver = { |
1381 | .name = "sun8i-codec" , |
1382 | .of_match_table = sun8i_codec_of_match, |
1383 | .pm = &sun8i_codec_pm_ops, |
1384 | }, |
1385 | .probe = sun8i_codec_probe, |
1386 | .remove_new = sun8i_codec_remove, |
1387 | }; |
1388 | module_platform_driver(sun8i_codec_driver); |
1389 | |
1390 | MODULE_DESCRIPTION("Allwinner A33 (sun8i) codec driver" ); |
1391 | MODULE_AUTHOR("Mylène Josserand <mylene.josserand@free-electrons.com>" ); |
1392 | MODULE_LICENSE("GPL" ); |
1393 | MODULE_ALIAS("platform:sun8i-codec" ); |
1394 | |