1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // JZ4725B CODEC driver |
4 | // |
5 | // Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net> |
6 | |
7 | #include <linux/kernel.h> |
8 | #include <linux/module.h> |
9 | #include <linux/platform_device.h> |
10 | #include <linux/slab.h> |
11 | #include <linux/io.h> |
12 | #include <linux/iopoll.h> |
13 | #include <linux/regmap.h> |
14 | #include <linux/clk.h> |
15 | |
16 | #include <linux/delay.h> |
17 | |
18 | #include <sound/core.h> |
19 | #include <sound/pcm.h> |
20 | #include <sound/pcm_params.h> |
21 | #include <sound/initval.h> |
22 | #include <sound/soc.h> |
23 | #include <sound/tlv.h> |
24 | |
25 | #define ICDC_RGADW_OFFSET 0x00 |
26 | #define ICDC_RGDATA_OFFSET 0x04 |
27 | |
28 | /* ICDC internal register access control register(RGADW) */ |
29 | #define ICDC_RGADW_RGWR BIT(16) |
30 | |
31 | #define ICDC_RGADW_RGADDR_OFFSET 8 |
32 | #define ICDC_RGADW_RGADDR_MASK GENMASK(14, ICDC_RGADW_RGADDR_OFFSET) |
33 | |
34 | #define ICDC_RGADW_RGDIN_OFFSET 0 |
35 | #define ICDC_RGADW_RGDIN_MASK GENMASK(7, ICDC_RGADW_RGDIN_OFFSET) |
36 | |
37 | /* ICDC internal register data output register (RGDATA)*/ |
38 | #define ICDC_RGDATA_IRQ BIT(8) |
39 | |
40 | #define ICDC_RGDATA_RGDOUT_OFFSET 0 |
41 | #define ICDC_RGDATA_RGDOUT_MASK GENMASK(7, ICDC_RGDATA_RGDOUT_OFFSET) |
42 | |
43 | /* JZ internal register space */ |
44 | enum { |
45 | JZ4725B_CODEC_REG_AICR, |
46 | JZ4725B_CODEC_REG_CR1, |
47 | JZ4725B_CODEC_REG_CR2, |
48 | JZ4725B_CODEC_REG_CCR1, |
49 | JZ4725B_CODEC_REG_CCR2, |
50 | JZ4725B_CODEC_REG_PMR1, |
51 | JZ4725B_CODEC_REG_PMR2, |
52 | JZ4725B_CODEC_REG_CRR, |
53 | JZ4725B_CODEC_REG_ICR, |
54 | JZ4725B_CODEC_REG_IFR, |
55 | JZ4725B_CODEC_REG_CGR1, |
56 | JZ4725B_CODEC_REG_CGR2, |
57 | JZ4725B_CODEC_REG_CGR3, |
58 | JZ4725B_CODEC_REG_CGR4, |
59 | JZ4725B_CODEC_REG_CGR5, |
60 | JZ4725B_CODEC_REG_CGR6, |
61 | JZ4725B_CODEC_REG_CGR7, |
62 | JZ4725B_CODEC_REG_CGR8, |
63 | JZ4725B_CODEC_REG_CGR9, |
64 | JZ4725B_CODEC_REG_CGR10, |
65 | JZ4725B_CODEC_REG_TR1, |
66 | JZ4725B_CODEC_REG_TR2, |
67 | JZ4725B_CODEC_REG_CR3, |
68 | JZ4725B_CODEC_REG_AGC1, |
69 | JZ4725B_CODEC_REG_AGC2, |
70 | JZ4725B_CODEC_REG_AGC3, |
71 | JZ4725B_CODEC_REG_AGC4, |
72 | JZ4725B_CODEC_REG_AGC5, |
73 | }; |
74 | |
75 | #define REG_AICR_CONFIG1_OFFSET 0 |
76 | #define REG_AICR_CONFIG1_MASK (0xf << REG_AICR_CONFIG1_OFFSET) |
77 | |
78 | #define REG_CR1_SB_MICBIAS_OFFSET 7 |
79 | #define REG_CR1_MONO_OFFSET 6 |
80 | #define REG_CR1_DAC_MUTE_OFFSET 5 |
81 | #define REG_CR1_HP_DIS_OFFSET 4 |
82 | #define REG_CR1_DACSEL_OFFSET 3 |
83 | #define REG_CR1_BYPASS_OFFSET 2 |
84 | |
85 | #define REG_CR2_DAC_DEEMP_OFFSET 7 |
86 | #define REG_CR2_DAC_ADWL_OFFSET 5 |
87 | #define REG_CR2_DAC_ADWL_MASK (0x3 << REG_CR2_DAC_ADWL_OFFSET) |
88 | #define REG_CR2_ADC_ADWL_OFFSET 3 |
89 | #define REG_CR2_ADC_ADWL_MASK (0x3 << REG_CR2_ADC_ADWL_OFFSET) |
90 | #define REG_CR2_ADC_HPF_OFFSET 2 |
91 | |
92 | #define REG_CR3_SB_MIC1_OFFSET 7 |
93 | #define REG_CR3_SB_MIC2_OFFSET 6 |
94 | #define REG_CR3_SIDETONE1_OFFSET 5 |
95 | #define REG_CR3_SIDETONE2_OFFSET 4 |
96 | #define REG_CR3_MICDIFF_OFFSET 3 |
97 | #define REG_CR3_MICSTEREO_OFFSET 2 |
98 | #define REG_CR3_INSEL_OFFSET 0 |
99 | #define REG_CR3_INSEL_MASK (0x3 << REG_CR3_INSEL_OFFSET) |
100 | |
101 | #define REG_CCR1_CONFIG4_OFFSET 0 |
102 | #define REG_CCR1_CONFIG4_MASK (0xf << REG_CCR1_CONFIG4_OFFSET) |
103 | |
104 | #define REG_CCR2_DFREQ_OFFSET 4 |
105 | #define REG_CCR2_DFREQ_MASK (0xf << REG_CCR2_DFREQ_OFFSET) |
106 | #define REG_CCR2_AFREQ_OFFSET 0 |
107 | #define REG_CCR2_AFREQ_MASK (0xf << REG_CCR2_AFREQ_OFFSET) |
108 | |
109 | #define REG_PMR1_SB_DAC_OFFSET 7 |
110 | #define REG_PMR1_SB_OUT_OFFSET 6 |
111 | #define REG_PMR1_SB_MIX_OFFSET 5 |
112 | #define REG_PMR1_SB_ADC_OFFSET 4 |
113 | #define REG_PMR1_SB_LIN_OFFSET 3 |
114 | #define REG_PMR1_SB_IND_OFFSET 0 |
115 | |
116 | #define REG_PMR2_LRGI_OFFSET 7 |
117 | #define REG_PMR2_RLGI_OFFSET 6 |
118 | #define REG_PMR2_LRGOD_OFFSET 5 |
119 | #define REG_PMR2_RLGOD_OFFSET 4 |
120 | #define REG_PMR2_GIM_OFFSET 3 |
121 | #define REG_PMR2_SB_MC_OFFSET 2 |
122 | #define REG_PMR2_SB_OFFSET 1 |
123 | #define REG_PMR2_SB_SLEEP_OFFSET 0 |
124 | |
125 | #define REG_IFR_RAMP_UP_DONE_OFFSET 3 |
126 | #define REG_IFR_RAMP_DOWN_DONE_OFFSET 2 |
127 | |
128 | #define REG_CGR1_GODL_OFFSET 4 |
129 | #define REG_CGR1_GODL_MASK (0xf << REG_CGR1_GODL_OFFSET) |
130 | #define REG_CGR1_GODR_OFFSET 0 |
131 | #define REG_CGR1_GODR_MASK (0xf << REG_CGR1_GODR_OFFSET) |
132 | |
133 | #define REG_CGR2_GO1R_OFFSET 0 |
134 | #define REG_CGR2_GO1R_MASK (0x1f << REG_CGR2_GO1R_OFFSET) |
135 | |
136 | #define REG_CGR3_GO1L_OFFSET 0 |
137 | #define REG_CGR3_GO1L_MASK (0x1f << REG_CGR3_GO1L_OFFSET) |
138 | |
139 | #define REG_CGR4_GO2R_OFFSET 0 |
140 | #define REG_CGR4_GO2R_MASK (0x1f << REG_CGR4_GO2R_OFFSET) |
141 | |
142 | #define REG_CGR5_GO2L_OFFSET 0 |
143 | #define REG_CGR5_GO2L_MASK (0x1f << REG_CGR5_GO2L_OFFSET) |
144 | |
145 | #define REG_CGR6_GO3R_OFFSET 0 |
146 | #define REG_CGR6_GO3R_MASK (0x1f << REG_CGR6_GO3R_OFFSET) |
147 | |
148 | #define REG_CGR7_GO3L_OFFSET 0 |
149 | #define REG_CGR7_GO3L_MASK (0x1f << REG_CGR7_GO3L_OFFSET) |
150 | |
151 | #define REG_CGR8_GOR_OFFSET 0 |
152 | #define REG_CGR8_GOR_MASK (0x1f << REG_CGR8_GOR_OFFSET) |
153 | |
154 | #define REG_CGR9_GOL_OFFSET 0 |
155 | #define REG_CGR9_GOL_MASK (0x1f << REG_CGR9_GOL_OFFSET) |
156 | |
157 | #define REG_CGR10_GIL_OFFSET 0 |
158 | #define REG_CGR10_GIR_OFFSET 4 |
159 | |
160 | struct jz_icdc { |
161 | struct regmap *regmap; |
162 | void __iomem *base; |
163 | struct clk *clk; |
164 | }; |
165 | |
166 | static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(jz4725b_adc_tlv, 0, 150, 0); |
167 | static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(jz4725b_dac_tlv, -2250, 150, 0); |
168 | static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(jz4725b_mix_tlv, |
169 | 0, 11, TLV_DB_SCALE_ITEM(-2250, 0, 0), |
170 | 12, 31, TLV_DB_SCALE_ITEM(-2250, 150, 0), |
171 | ); |
172 | |
173 | static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(jz4725b_out_tlv, |
174 | 0, 11, TLV_DB_SCALE_ITEM(-3350, 200, 0), |
175 | 12, 23, TLV_DB_SCALE_ITEM(-1050, 100, 0), |
176 | 24, 31, TLV_DB_SCALE_ITEM( 100, 50, 0), |
177 | ); |
178 | static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(jz4725b_mic_boost_tlv, 0, 2000, 0); |
179 | |
180 | static const char * const jz4725b_mic_mode_texts[] = { |
181 | "Single Ended" , "Differential" , |
182 | }; |
183 | |
184 | static const struct soc_enum jz4725b_mic_mode_enum = |
185 | SOC_ENUM_SINGLE(JZ4725B_CODEC_REG_CR3, REG_CR3_MICDIFF_OFFSET, |
186 | 2, jz4725b_mic_mode_texts); |
187 | |
188 | static const struct snd_kcontrol_new jz4725b_codec_controls[] = { |
189 | SOC_DOUBLE_TLV("DAC Playback Volume" , |
190 | JZ4725B_CODEC_REG_CGR1, |
191 | REG_CGR1_GODL_OFFSET, |
192 | REG_CGR1_GODR_OFFSET, |
193 | 0xf, 1, jz4725b_dac_tlv), |
194 | SOC_DOUBLE_TLV("Master Capture Volume" , |
195 | JZ4725B_CODEC_REG_CGR10, |
196 | REG_CGR10_GIL_OFFSET, |
197 | REG_CGR10_GIR_OFFSET, |
198 | 0xf, 0, jz4725b_adc_tlv), |
199 | SOC_DOUBLE_R_TLV("Mixer Line In Bypass Playback Volume" , |
200 | JZ4725B_CODEC_REG_CGR3, |
201 | JZ4725B_CODEC_REG_CGR2, |
202 | REG_CGR2_GO1R_OFFSET, |
203 | 0x1f, 1, jz4725b_mix_tlv), |
204 | SOC_DOUBLE_R_TLV("Mixer Mic 1 Bypass Playback Volume" , |
205 | JZ4725B_CODEC_REG_CGR5, |
206 | JZ4725B_CODEC_REG_CGR4, |
207 | REG_CGR4_GO2R_OFFSET, |
208 | 0x1f, 1, jz4725b_mix_tlv), |
209 | SOC_DOUBLE_R_TLV("Mixer Mic 2 Bypass Playback Volume" , |
210 | JZ4725B_CODEC_REG_CGR7, |
211 | JZ4725B_CODEC_REG_CGR6, |
212 | REG_CGR6_GO3R_OFFSET, |
213 | 0x1f, 1, jz4725b_mix_tlv), |
214 | |
215 | SOC_DOUBLE_R_TLV("Master Playback Volume" , |
216 | JZ4725B_CODEC_REG_CGR9, |
217 | JZ4725B_CODEC_REG_CGR8, |
218 | REG_CGR8_GOR_OFFSET, |
219 | 0x1f, 1, jz4725b_out_tlv), |
220 | |
221 | SOC_SINGLE("DAC Playback Switch" , JZ4725B_CODEC_REG_CR1, |
222 | REG_CR1_DAC_MUTE_OFFSET, 1, 1), |
223 | |
224 | SOC_SINGLE("Deemphasize Filter Playback Switch" , |
225 | JZ4725B_CODEC_REG_CR2, |
226 | REG_CR2_DAC_DEEMP_OFFSET, 1, 0), |
227 | |
228 | SOC_SINGLE("High-Pass Filter Capture Switch" , |
229 | JZ4725B_CODEC_REG_CR2, |
230 | REG_CR2_ADC_HPF_OFFSET, 1, 0), |
231 | |
232 | SOC_ENUM("Mic Mode Capture Switch" , jz4725b_mic_mode_enum), |
233 | |
234 | SOC_SINGLE_TLV("Mic1 Boost Capture Volume" , |
235 | JZ4725B_CODEC_REG_PMR2, |
236 | REG_PMR2_GIM_OFFSET, |
237 | 1, 0, jz4725b_mic_boost_tlv), |
238 | }; |
239 | |
240 | static const char * const jz4725b_codec_adc_src_texts[] = { |
241 | "Mic 1" , "Mic 2" , "Line In" , "Mixer" , |
242 | }; |
243 | static const unsigned int jz4725b_codec_adc_src_values[] = { 0, 1, 2, 3, }; |
244 | static SOC_VALUE_ENUM_SINGLE_DECL(jz4725b_codec_adc_src_enum, |
245 | JZ4725B_CODEC_REG_CR3, |
246 | REG_CR3_INSEL_OFFSET, |
247 | REG_CR3_INSEL_MASK, |
248 | jz4725b_codec_adc_src_texts, |
249 | jz4725b_codec_adc_src_values); |
250 | static const struct snd_kcontrol_new jz4725b_codec_adc_src_ctrl = |
251 | SOC_DAPM_ENUM("ADC Source Capture Route" , jz4725b_codec_adc_src_enum); |
252 | |
253 | static const struct snd_kcontrol_new jz4725b_codec_mixer_controls[] = { |
254 | SOC_DAPM_SINGLE("Line In Bypass Playback Switch" , JZ4725B_CODEC_REG_CR1, |
255 | REG_CR1_BYPASS_OFFSET, 1, 0), |
256 | SOC_DAPM_SINGLE("Mic 1 Bypass Playback Switch" , JZ4725B_CODEC_REG_CR3, |
257 | REG_CR3_SIDETONE1_OFFSET, 1, 0), |
258 | SOC_DAPM_SINGLE("Mic 2 Bypass Playback Switch" , JZ4725B_CODEC_REG_CR3, |
259 | REG_CR3_SIDETONE2_OFFSET, 1, 0), |
260 | }; |
261 | |
262 | static int jz4725b_out_stage_enable(struct snd_soc_dapm_widget *w, |
263 | struct snd_kcontrol *kcontrol, |
264 | int event) |
265 | { |
266 | struct snd_soc_component *codec = snd_soc_dapm_to_component(dapm: w->dapm); |
267 | struct jz_icdc *icdc = snd_soc_component_get_drvdata(c: codec); |
268 | struct regmap *map = icdc->regmap; |
269 | unsigned int val; |
270 | |
271 | switch (event) { |
272 | case SND_SOC_DAPM_PRE_PMU: |
273 | return regmap_clear_bits(map, reg: JZ4725B_CODEC_REG_IFR, |
274 | BIT(REG_IFR_RAMP_UP_DONE_OFFSET)); |
275 | case SND_SOC_DAPM_POST_PMU: |
276 | return regmap_read_poll_timeout(map, JZ4725B_CODEC_REG_IFR, |
277 | val, val & BIT(REG_IFR_RAMP_UP_DONE_OFFSET), |
278 | 100000, 500000); |
279 | case SND_SOC_DAPM_PRE_PMD: |
280 | return regmap_clear_bits(map, reg: JZ4725B_CODEC_REG_IFR, |
281 | BIT(REG_IFR_RAMP_DOWN_DONE_OFFSET)); |
282 | case SND_SOC_DAPM_POST_PMD: |
283 | return regmap_read_poll_timeout(map, JZ4725B_CODEC_REG_IFR, |
284 | val, val & BIT(REG_IFR_RAMP_DOWN_DONE_OFFSET), |
285 | 100000, 500000); |
286 | default: |
287 | return -EINVAL; |
288 | } |
289 | } |
290 | |
291 | static const struct snd_soc_dapm_widget jz4725b_codec_dapm_widgets[] = { |
292 | /* DAC */ |
293 | SND_SOC_DAPM_DAC("DAC" , "Playback" , |
294 | JZ4725B_CODEC_REG_PMR1, REG_PMR1_SB_DAC_OFFSET, 1), |
295 | |
296 | /* ADC */ |
297 | SND_SOC_DAPM_ADC("ADC" , "Capture" , |
298 | JZ4725B_CODEC_REG_PMR1, REG_PMR1_SB_ADC_OFFSET, 1), |
299 | |
300 | SND_SOC_DAPM_MUX("ADC Source Capture Route" , SND_SOC_NOPM, 0, 0, |
301 | &jz4725b_codec_adc_src_ctrl), |
302 | |
303 | /* Mixer */ |
304 | SND_SOC_DAPM_MIXER("Mixer" , JZ4725B_CODEC_REG_PMR1, |
305 | REG_PMR1_SB_MIX_OFFSET, 1, |
306 | jz4725b_codec_mixer_controls, |
307 | ARRAY_SIZE(jz4725b_codec_mixer_controls)), |
308 | SND_SOC_DAPM_MIXER("DAC to Mixer" , JZ4725B_CODEC_REG_CR1, |
309 | REG_CR1_DACSEL_OFFSET, 0, NULL, 0), |
310 | |
311 | SND_SOC_DAPM_MIXER("Line In" , JZ4725B_CODEC_REG_PMR1, |
312 | REG_PMR1_SB_LIN_OFFSET, 1, NULL, 0), |
313 | SND_SOC_DAPM_MIXER("HP Out" , JZ4725B_CODEC_REG_CR1, |
314 | REG_CR1_HP_DIS_OFFSET, 1, NULL, 0), |
315 | |
316 | SND_SOC_DAPM_MIXER("Mic 1" , JZ4725B_CODEC_REG_CR3, |
317 | REG_CR3_SB_MIC1_OFFSET, 1, NULL, 0), |
318 | SND_SOC_DAPM_MIXER("Mic 2" , JZ4725B_CODEC_REG_CR3, |
319 | REG_CR3_SB_MIC2_OFFSET, 1, NULL, 0), |
320 | |
321 | SND_SOC_DAPM_MIXER_E("Out Stage" , JZ4725B_CODEC_REG_PMR1, |
322 | REG_PMR1_SB_OUT_OFFSET, 1, NULL, 0, |
323 | jz4725b_out_stage_enable, |
324 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | |
325 | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), |
326 | SND_SOC_DAPM_MIXER("Mixer to ADC" , JZ4725B_CODEC_REG_PMR1, |
327 | REG_PMR1_SB_IND_OFFSET, 1, NULL, 0), |
328 | |
329 | SND_SOC_DAPM_SUPPLY("Mic Bias" , JZ4725B_CODEC_REG_CR1, |
330 | REG_CR1_SB_MICBIAS_OFFSET, 1, NULL, 0), |
331 | |
332 | /* Pins */ |
333 | SND_SOC_DAPM_INPUT("MIC1P" ), |
334 | SND_SOC_DAPM_INPUT("MIC1N" ), |
335 | SND_SOC_DAPM_INPUT("MIC2P" ), |
336 | SND_SOC_DAPM_INPUT("MIC2N" ), |
337 | |
338 | SND_SOC_DAPM_INPUT("LLINEIN" ), |
339 | SND_SOC_DAPM_INPUT("RLINEIN" ), |
340 | |
341 | SND_SOC_DAPM_OUTPUT("LHPOUT" ), |
342 | SND_SOC_DAPM_OUTPUT("RHPOUT" ), |
343 | }; |
344 | |
345 | static const struct snd_soc_dapm_route jz4725b_codec_dapm_routes[] = { |
346 | {"Mic 1" , NULL, "MIC1P" }, |
347 | {"Mic 1" , NULL, "MIC1N" }, |
348 | {"Mic 2" , NULL, "MIC2P" }, |
349 | {"Mic 2" , NULL, "MIC2N" }, |
350 | |
351 | {"Line In" , NULL, "LLINEIN" }, |
352 | {"Line In" , NULL, "RLINEIN" }, |
353 | |
354 | {"Mixer" , "Mic 1 Bypass Playback Switch" , "Mic 1" }, |
355 | {"Mixer" , "Mic 2 Bypass Playback Switch" , "Mic 2" }, |
356 | {"Mixer" , "Line In Bypass Playback Switch" , "Line In" }, |
357 | {"DAC to Mixer" , NULL, "DAC" }, |
358 | {"Mixer" , NULL, "DAC to Mixer" }, |
359 | |
360 | {"Mixer to ADC" , NULL, "Mixer" }, |
361 | {"ADC Source Capture Route" , "Mixer" , "Mixer to ADC" }, |
362 | {"ADC Source Capture Route" , "Line In" , "Line In" }, |
363 | {"ADC Source Capture Route" , "Mic 1" , "Mic 1" }, |
364 | {"ADC Source Capture Route" , "Mic 2" , "Mic 2" }, |
365 | {"ADC" , NULL, "ADC Source Capture Route" }, |
366 | |
367 | {"Out Stage" , NULL, "Mixer" }, |
368 | {"HP Out" , NULL, "Out Stage" }, |
369 | {"LHPOUT" , NULL, "HP Out" }, |
370 | {"RHPOUT" , NULL, "HP Out" }, |
371 | }; |
372 | |
373 | static int jz4725b_codec_set_bias_level(struct snd_soc_component *component, |
374 | enum snd_soc_bias_level level) |
375 | { |
376 | struct jz_icdc *icdc = snd_soc_component_get_drvdata(c: component); |
377 | struct regmap *map = icdc->regmap; |
378 | |
379 | switch (level) { |
380 | case SND_SOC_BIAS_ON: |
381 | regmap_clear_bits(map, reg: JZ4725B_CODEC_REG_PMR2, |
382 | BIT(REG_PMR2_SB_SLEEP_OFFSET)); |
383 | break; |
384 | case SND_SOC_BIAS_PREPARE: |
385 | /* Enable sound hardware */ |
386 | regmap_clear_bits(map, reg: JZ4725B_CODEC_REG_PMR2, |
387 | BIT(REG_PMR2_SB_OFFSET)); |
388 | msleep(msecs: 224); |
389 | break; |
390 | case SND_SOC_BIAS_STANDBY: |
391 | regmap_set_bits(map, reg: JZ4725B_CODEC_REG_PMR2, |
392 | BIT(REG_PMR2_SB_SLEEP_OFFSET)); |
393 | break; |
394 | case SND_SOC_BIAS_OFF: |
395 | regmap_set_bits(map, reg: JZ4725B_CODEC_REG_PMR2, |
396 | BIT(REG_PMR2_SB_OFFSET)); |
397 | break; |
398 | } |
399 | |
400 | return 0; |
401 | } |
402 | |
403 | static int jz4725b_codec_dev_probe(struct snd_soc_component *component) |
404 | { |
405 | struct jz_icdc *icdc = snd_soc_component_get_drvdata(c: component); |
406 | struct regmap *map = icdc->regmap; |
407 | |
408 | clk_prepare_enable(clk: icdc->clk); |
409 | |
410 | /* Write CONFIGn (n=1 to 8) bits. |
411 | * The value 0x0f is specified in the datasheet as a requirement. |
412 | */ |
413 | regmap_write(map, reg: JZ4725B_CODEC_REG_AICR, |
414 | val: 0xf << REG_AICR_CONFIG1_OFFSET); |
415 | regmap_write(map, reg: JZ4725B_CODEC_REG_CCR1, |
416 | val: 0x0 << REG_CCR1_CONFIG4_OFFSET); |
417 | |
418 | return 0; |
419 | } |
420 | |
421 | static void jz4725b_codec_dev_remove(struct snd_soc_component *component) |
422 | { |
423 | struct jz_icdc *icdc = snd_soc_component_get_drvdata(c: component); |
424 | |
425 | clk_disable_unprepare(clk: icdc->clk); |
426 | } |
427 | |
428 | static const struct snd_soc_component_driver jz4725b_codec = { |
429 | .probe = jz4725b_codec_dev_probe, |
430 | .remove = jz4725b_codec_dev_remove, |
431 | .set_bias_level = jz4725b_codec_set_bias_level, |
432 | .controls = jz4725b_codec_controls, |
433 | .num_controls = ARRAY_SIZE(jz4725b_codec_controls), |
434 | .dapm_widgets = jz4725b_codec_dapm_widgets, |
435 | .num_dapm_widgets = ARRAY_SIZE(jz4725b_codec_dapm_widgets), |
436 | .dapm_routes = jz4725b_codec_dapm_routes, |
437 | .num_dapm_routes = ARRAY_SIZE(jz4725b_codec_dapm_routes), |
438 | .suspend_bias_off = 1, |
439 | .use_pmdown_time = 1, |
440 | }; |
441 | |
442 | static const unsigned int jz4725b_codec_sample_rates[] = { |
443 | 96000, 48000, 44100, 32000, |
444 | 24000, 22050, 16000, 12000, |
445 | 11025, 9600, 8000, |
446 | }; |
447 | |
448 | static int jz4725b_codec_hw_params(struct snd_pcm_substream *substream, |
449 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) |
450 | { |
451 | struct jz_icdc *icdc = snd_soc_component_get_drvdata(c: dai->component); |
452 | unsigned int rate, bit_width; |
453 | |
454 | switch (params_format(p: params)) { |
455 | case SNDRV_PCM_FORMAT_S16_LE: |
456 | bit_width = 0; |
457 | break; |
458 | case SNDRV_PCM_FORMAT_S18_3LE: |
459 | bit_width = 1; |
460 | break; |
461 | case SNDRV_PCM_FORMAT_S20_3LE: |
462 | bit_width = 2; |
463 | break; |
464 | case SNDRV_PCM_FORMAT_S24_3LE: |
465 | bit_width = 3; |
466 | break; |
467 | default: |
468 | return -EINVAL; |
469 | } |
470 | |
471 | for (rate = 0; rate < ARRAY_SIZE(jz4725b_codec_sample_rates); rate++) { |
472 | if (jz4725b_codec_sample_rates[rate] == params_rate(p: params)) |
473 | break; |
474 | } |
475 | |
476 | if (rate == ARRAY_SIZE(jz4725b_codec_sample_rates)) |
477 | return -EINVAL; |
478 | |
479 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
480 | regmap_update_bits(map: icdc->regmap, |
481 | reg: JZ4725B_CODEC_REG_CR2, |
482 | REG_CR2_DAC_ADWL_MASK, |
483 | val: bit_width << REG_CR2_DAC_ADWL_OFFSET); |
484 | |
485 | regmap_update_bits(map: icdc->regmap, |
486 | reg: JZ4725B_CODEC_REG_CCR2, |
487 | REG_CCR2_DFREQ_MASK, |
488 | val: rate << REG_CCR2_DFREQ_OFFSET); |
489 | } else { |
490 | regmap_update_bits(map: icdc->regmap, |
491 | reg: JZ4725B_CODEC_REG_CR2, |
492 | REG_CR2_ADC_ADWL_MASK, |
493 | val: bit_width << REG_CR2_ADC_ADWL_OFFSET); |
494 | |
495 | regmap_update_bits(map: icdc->regmap, |
496 | reg: JZ4725B_CODEC_REG_CCR2, |
497 | REG_CCR2_AFREQ_MASK, |
498 | val: rate << REG_CCR2_AFREQ_OFFSET); |
499 | } |
500 | |
501 | return 0; |
502 | } |
503 | |
504 | static const struct snd_soc_dai_ops jz4725b_codec_dai_ops = { |
505 | .hw_params = jz4725b_codec_hw_params, |
506 | }; |
507 | |
508 | #define JZ_ICDC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ |
509 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE) |
510 | |
511 | static struct snd_soc_dai_driver jz4725b_codec_dai = { |
512 | .name = "jz4725b-hifi" , |
513 | .playback = { |
514 | .stream_name = "Playback" , |
515 | .channels_min = 2, |
516 | .channels_max = 2, |
517 | .rates = SNDRV_PCM_RATE_8000_96000, |
518 | .formats = JZ_ICDC_FORMATS, |
519 | }, |
520 | .capture = { |
521 | .stream_name = "Capture" , |
522 | .channels_min = 2, |
523 | .channels_max = 2, |
524 | .rates = SNDRV_PCM_RATE_8000_96000, |
525 | .formats = JZ_ICDC_FORMATS, |
526 | }, |
527 | .ops = &jz4725b_codec_dai_ops, |
528 | }; |
529 | |
530 | static bool jz4725b_codec_volatile(struct device *dev, unsigned int reg) |
531 | { |
532 | return reg == JZ4725B_CODEC_REG_IFR; |
533 | } |
534 | |
535 | static bool jz4725b_codec_can_access_reg(struct device *dev, unsigned int reg) |
536 | { |
537 | return (reg != JZ4725B_CODEC_REG_TR1) && (reg != JZ4725B_CODEC_REG_TR2); |
538 | } |
539 | |
540 | static int jz4725b_codec_io_wait(struct jz_icdc *icdc) |
541 | { |
542 | u32 reg; |
543 | |
544 | return readl_poll_timeout(icdc->base + ICDC_RGADW_OFFSET, reg, |
545 | !(reg & ICDC_RGADW_RGWR), 1000, 10000); |
546 | } |
547 | |
548 | static int jz4725b_codec_reg_read(void *context, unsigned int reg, |
549 | unsigned int *val) |
550 | { |
551 | struct jz_icdc *icdc = context; |
552 | unsigned int i; |
553 | u32 tmp; |
554 | int ret; |
555 | |
556 | ret = jz4725b_codec_io_wait(icdc); |
557 | if (ret) |
558 | return ret; |
559 | |
560 | tmp = readl(addr: icdc->base + ICDC_RGADW_OFFSET); |
561 | tmp = (tmp & ~ICDC_RGADW_RGADDR_MASK) |
562 | | (reg << ICDC_RGADW_RGADDR_OFFSET); |
563 | writel(val: tmp, addr: icdc->base + ICDC_RGADW_OFFSET); |
564 | |
565 | /* wait 6+ cycles */ |
566 | for (i = 0; i < 6; i++) |
567 | *val = readl(addr: icdc->base + ICDC_RGDATA_OFFSET) & |
568 | ICDC_RGDATA_RGDOUT_MASK; |
569 | |
570 | return 0; |
571 | } |
572 | |
573 | static int jz4725b_codec_reg_write(void *context, unsigned int reg, |
574 | unsigned int val) |
575 | { |
576 | struct jz_icdc *icdc = context; |
577 | int ret; |
578 | |
579 | ret = jz4725b_codec_io_wait(icdc); |
580 | if (ret) |
581 | return ret; |
582 | |
583 | writel(ICDC_RGADW_RGWR | (reg << ICDC_RGADW_RGADDR_OFFSET) | val, |
584 | addr: icdc->base + ICDC_RGADW_OFFSET); |
585 | |
586 | ret = jz4725b_codec_io_wait(icdc); |
587 | if (ret) |
588 | return ret; |
589 | |
590 | return 0; |
591 | } |
592 | |
593 | static const u8 jz4725b_codec_reg_defaults[] = { |
594 | 0x0c, 0xaa, 0x78, 0x00, 0x00, 0xff, 0x03, 0x51, |
595 | 0x3f, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, |
596 | 0x04, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0xc0, 0x34, |
597 | 0x07, 0x44, 0x1f, 0x00, |
598 | }; |
599 | |
600 | static const struct regmap_config jz4725b_codec_regmap_config = { |
601 | .reg_bits = 7, |
602 | .val_bits = 8, |
603 | |
604 | .max_register = JZ4725B_CODEC_REG_AGC5, |
605 | .volatile_reg = jz4725b_codec_volatile, |
606 | .readable_reg = jz4725b_codec_can_access_reg, |
607 | .writeable_reg = jz4725b_codec_can_access_reg, |
608 | |
609 | .reg_read = jz4725b_codec_reg_read, |
610 | .reg_write = jz4725b_codec_reg_write, |
611 | |
612 | .reg_defaults_raw = jz4725b_codec_reg_defaults, |
613 | .num_reg_defaults_raw = ARRAY_SIZE(jz4725b_codec_reg_defaults), |
614 | .cache_type = REGCACHE_FLAT, |
615 | }; |
616 | |
617 | static int jz4725b_codec_probe(struct platform_device *pdev) |
618 | { |
619 | struct device *dev = &pdev->dev; |
620 | struct jz_icdc *icdc; |
621 | int ret; |
622 | |
623 | icdc = devm_kzalloc(dev, size: sizeof(*icdc), GFP_KERNEL); |
624 | if (!icdc) |
625 | return -ENOMEM; |
626 | |
627 | icdc->base = devm_platform_ioremap_resource(pdev, index: 0); |
628 | if (IS_ERR(ptr: icdc->base)) |
629 | return PTR_ERR(ptr: icdc->base); |
630 | |
631 | icdc->regmap = devm_regmap_init(dev, NULL, icdc, |
632 | &jz4725b_codec_regmap_config); |
633 | if (IS_ERR(ptr: icdc->regmap)) |
634 | return PTR_ERR(ptr: icdc->regmap); |
635 | |
636 | icdc->clk = devm_clk_get(dev: &pdev->dev, id: "aic" ); |
637 | if (IS_ERR(ptr: icdc->clk)) |
638 | return PTR_ERR(ptr: icdc->clk); |
639 | |
640 | platform_set_drvdata(pdev, data: icdc); |
641 | |
642 | ret = devm_snd_soc_register_component(dev, component_driver: &jz4725b_codec, |
643 | dai_drv: &jz4725b_codec_dai, num_dai: 1); |
644 | if (ret) |
645 | dev_err(dev, "Failed to register codec\n" ); |
646 | |
647 | return ret; |
648 | } |
649 | |
650 | static const struct of_device_id jz4725b_codec_of_matches[] = { |
651 | { .compatible = "ingenic,jz4725b-codec" , }, |
652 | { } |
653 | }; |
654 | MODULE_DEVICE_TABLE(of, jz4725b_codec_of_matches); |
655 | |
656 | static struct platform_driver jz4725b_codec_driver = { |
657 | .probe = jz4725b_codec_probe, |
658 | .driver = { |
659 | .name = "jz4725b-codec" , |
660 | .of_match_table = jz4725b_codec_of_matches, |
661 | }, |
662 | }; |
663 | module_platform_driver(jz4725b_codec_driver); |
664 | |
665 | MODULE_DESCRIPTION("JZ4725B SoC internal codec driver" ); |
666 | MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>" ); |
667 | MODULE_LICENSE("GPL v2" ); |
668 | |