1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Ingenic JZ4760 CODEC driver |
4 | // |
5 | // Copyright (C) 2021, Christophe Branchereau <cbranchereau@gmail.com> |
6 | // Copyright (C) 2021, Paul Cercueil <paul@crapouillou.net> |
7 | |
8 | #include <linux/bitfield.h> |
9 | #include <linux/clk.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/iopoll.h> |
12 | #include <linux/module.h> |
13 | #include <linux/regmap.h> |
14 | #include <linux/time64.h> |
15 | |
16 | #include <sound/pcm_params.h> |
17 | #include <sound/soc.h> |
18 | #include <sound/soc-dai.h> |
19 | #include <sound/soc-dapm.h> |
20 | #include <sound/tlv.h> |
21 | |
22 | #define ICDC_RGADW_OFFSET 0x00 |
23 | #define ICDC_RGDATA_OFFSET 0x04 |
24 | |
25 | /* ICDC internal register access control register(RGADW) */ |
26 | #define ICDC_RGADW_RGWR BIT(16) |
27 | #define ICDC_RGADW_RGADDR_MASK GENMASK(14, 8) |
28 | #define ICDC_RGADW_RGDIN_MASK GENMASK(7, 0) |
29 | |
30 | /* ICDC internal register data output register (RGDATA)*/ |
31 | #define ICDC_RGDATA_IRQ BIT(8) |
32 | #define ICDC_RGDATA_RGDOUT_MASK GENMASK(7, 0) |
33 | |
34 | /* Internal register space, accessed through regmap */ |
35 | enum { |
36 | JZ4760_CODEC_REG_SR, |
37 | JZ4760_CODEC_REG_AICR, |
38 | JZ4760_CODEC_REG_CR1, |
39 | JZ4760_CODEC_REG_CR2, |
40 | JZ4760_CODEC_REG_CR3, |
41 | JZ4760_CODEC_REG_CR4, |
42 | JZ4760_CODEC_REG_CCR1, |
43 | JZ4760_CODEC_REG_CCR2, |
44 | JZ4760_CODEC_REG_PMR1, |
45 | JZ4760_CODEC_REG_PMR2, |
46 | JZ4760_CODEC_REG_ICR, |
47 | JZ4760_CODEC_REG_IFR, |
48 | JZ4760_CODEC_REG_GCR1, |
49 | JZ4760_CODEC_REG_GCR2, |
50 | JZ4760_CODEC_REG_GCR3, |
51 | JZ4760_CODEC_REG_GCR4, |
52 | JZ4760_CODEC_REG_GCR5, |
53 | JZ4760_CODEC_REG_GCR6, |
54 | JZ4760_CODEC_REG_GCR7, |
55 | JZ4760_CODEC_REG_GCR8, |
56 | JZ4760_CODEC_REG_GCR9, |
57 | JZ4760_CODEC_REG_AGC1, |
58 | JZ4760_CODEC_REG_AGC2, |
59 | JZ4760_CODEC_REG_AGC3, |
60 | JZ4760_CODEC_REG_AGC4, |
61 | JZ4760_CODEC_REG_AGC5, |
62 | JZ4760_CODEC_REG_MIX1, |
63 | JZ4760_CODEC_REG_MIX2, |
64 | }; |
65 | |
66 | #define REG_AICR_DAC_ADWL_MASK GENMASK(7, 6) |
67 | #define REG_AICR_DAC_SERIAL BIT(3) |
68 | #define REG_AICR_DAC_I2S BIT(1) |
69 | |
70 | #define REG_AICR_ADC_ADWL_MASK GENMASK(5, 4) |
71 | |
72 | #define REG_AICR_ADC_SERIAL BIT(2) |
73 | #define REG_AICR_ADC_I2S BIT(0) |
74 | |
75 | #define REG_CR1_HP_LOAD BIT(7) |
76 | #define REG_CR1_HP_MUTE BIT(5) |
77 | #define REG_CR1_LO_MUTE_OFFSET 4 |
78 | #define REG_CR1_BTL_MUTE_OFFSET 3 |
79 | #define REG_CR1_OUTSEL_OFFSET 0 |
80 | #define REG_CR1_OUTSEL_MASK GENMASK(1, REG_CR1_OUTSEL_OFFSET) |
81 | |
82 | #define REG_CR2_DAC_MONO BIT(7) |
83 | #define REG_CR2_DAC_MUTE BIT(5) |
84 | #define REG_CR2_DAC_NOMAD BIT(1) |
85 | #define REG_CR2_DAC_RIGHT_ONLY BIT(0) |
86 | |
87 | #define REG_CR3_ADC_INSEL_OFFSET 2 |
88 | #define REG_CR3_ADC_INSEL_MASK GENMASK(3, REG_CR3_ADC_INSEL_OFFSET) |
89 | #define REG_CR3_MICSTEREO_OFFSET 1 |
90 | #define REG_CR3_MICDIFF_OFFSET 0 |
91 | |
92 | #define REG_CR4_ADC_HPF_OFFSET 7 |
93 | #define REG_CR4_ADC_RIGHT_ONLY BIT(0) |
94 | |
95 | #define REG_CCR1_CRYSTAL_MASK GENMASK(3, 0) |
96 | |
97 | #define REG_CCR2_DAC_FREQ_MASK GENMASK(7, 4) |
98 | #define REG_CCR2_ADC_FREQ_MASK GENMASK(3, 0) |
99 | |
100 | #define REG_PMR1_SB BIT(7) |
101 | #define REG_PMR1_SB_SLEEP BIT(6) |
102 | #define REG_PMR1_SB_AIP_OFFSET 5 |
103 | #define REG_PMR1_SB_LINE_OFFSET 4 |
104 | #define REG_PMR1_SB_MIC1_OFFSET 3 |
105 | #define REG_PMR1_SB_MIC2_OFFSET 2 |
106 | #define REG_PMR1_SB_BYPASS_OFFSET 1 |
107 | #define REG_PMR1_SB_MICBIAS_OFFSET 0 |
108 | |
109 | #define REG_PMR2_SB_ADC_OFFSET 4 |
110 | #define REG_PMR2_SB_HP_OFFSET 3 |
111 | #define REG_PMR2_SB_BTL_OFFSET 2 |
112 | #define REG_PMR2_SB_LOUT_OFFSET 1 |
113 | #define REG_PMR2_SB_DAC_OFFSET 0 |
114 | |
115 | #define REG_ICR_INT_FORM_MASK GENMASK(7, 6) |
116 | #define REG_ICR_ALL_MASK GENMASK(5, 0) |
117 | #define REG_ICR_JACK_MASK BIT(5) |
118 | #define REG_ICR_SCMC_MASK BIT(4) |
119 | #define REG_ICR_RUP_MASK BIT(3) |
120 | #define REG_ICR_RDO_MASK BIT(2) |
121 | #define REG_ICR_GUP_MASK BIT(1) |
122 | #define REG_ICR_GDO_MASK BIT(0) |
123 | |
124 | #define REG_IFR_ALL_MASK GENMASK(5, 0) |
125 | #define REG_IFR_JACK BIT(6) |
126 | #define REG_IFR_JACK_EVENT BIT(5) |
127 | #define REG_IFR_SCMC BIT(4) |
128 | #define REG_IFR_RUP BIT(3) |
129 | #define REG_IFR_RDO BIT(2) |
130 | #define REG_IFR_GUP BIT(1) |
131 | #define REG_IFR_GDO BIT(0) |
132 | |
133 | #define REG_GCR_GAIN_OFFSET 0 |
134 | #define REG_GCR_GAIN_MAX 0x1f |
135 | |
136 | #define REG_GCR_RL BIT(7) |
137 | |
138 | #define REG_GCR_GIM1_MASK GENMASK(5, 3) |
139 | #define REG_GCR_GIM2_MASK GENMASK(2, 0) |
140 | #define REG_GCR_GIM_GAIN_MAX 7 |
141 | |
142 | #define REG_AGC1_EN BIT(7) |
143 | #define REG_AGC1_TARGET_MASK GENMASK(5, 2) |
144 | |
145 | #define REG_AGC2_NG_THR_MASK GENMASK(6, 4) |
146 | #define REG_AGC2_HOLD_MASK GENMASK(3, 0) |
147 | |
148 | #define REG_AGC3_ATK_MASK GENMASK(7, 4) |
149 | #define REG_AGC3_DCY_MASK GENMASK(3, 0) |
150 | |
151 | #define REG_AGC4_AGC_MAX_MASK GENMASK(4, 0) |
152 | |
153 | #define REG_AGC5_AGC_MIN_MASK GENMASK(4, 0) |
154 | |
155 | #define REG_MIX1_MIX_REC_MASK GENMASK(7, 6) |
156 | #define REG_MIX1_GIMIX_MASK GENMASK(4, 0) |
157 | |
158 | #define REG_MIX2_DAC_MIX_MASK GENMASK(7, 6) |
159 | #define REG_MIX2_GOMIX_MASK GENMASK(4, 0) |
160 | |
161 | /* codec private data */ |
162 | struct jz_codec { |
163 | struct device *dev; |
164 | struct regmap *regmap; |
165 | void __iomem *base; |
166 | struct clk *clk; |
167 | }; |
168 | |
169 | static int jz4760_codec_set_bias_level(struct snd_soc_component *codec, |
170 | enum snd_soc_bias_level level) |
171 | { |
172 | struct jz_codec *jz_codec = snd_soc_component_get_drvdata(c: codec); |
173 | struct regmap *regmap = jz_codec->regmap; |
174 | |
175 | switch (level) { |
176 | case SND_SOC_BIAS_PREPARE: |
177 | /* Reset all interrupt flags. */ |
178 | regmap_write(map: regmap, reg: JZ4760_CODEC_REG_IFR, REG_IFR_ALL_MASK); |
179 | |
180 | regmap_clear_bits(map: regmap, reg: JZ4760_CODEC_REG_PMR1, REG_PMR1_SB); |
181 | msleep(msecs: 250); |
182 | regmap_clear_bits(map: regmap, reg: JZ4760_CODEC_REG_PMR1, REG_PMR1_SB_SLEEP); |
183 | msleep(msecs: 400); |
184 | break; |
185 | case SND_SOC_BIAS_STANDBY: |
186 | regmap_set_bits(map: regmap, reg: JZ4760_CODEC_REG_PMR1, REG_PMR1_SB_SLEEP); |
187 | regmap_set_bits(map: regmap, reg: JZ4760_CODEC_REG_PMR1, REG_PMR1_SB); |
188 | break; |
189 | default: |
190 | break; |
191 | } |
192 | |
193 | return 0; |
194 | } |
195 | |
196 | static int jz4760_codec_startup(struct snd_pcm_substream *substream, |
197 | struct snd_soc_dai *dai) |
198 | { |
199 | struct snd_soc_component *codec = dai->component; |
200 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component: codec); |
201 | int ret = 0; |
202 | |
203 | /* |
204 | * SYSCLK output from the codec to the AIC is required to keep the |
205 | * DMA transfer going during playback when all audible outputs have |
206 | * been disabled. |
207 | */ |
208 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
209 | ret = snd_soc_dapm_force_enable_pin(dapm, pin: "SYSCLK" ); |
210 | return ret; |
211 | } |
212 | |
213 | static void jz4760_codec_shutdown(struct snd_pcm_substream *substream, |
214 | struct snd_soc_dai *dai) |
215 | { |
216 | struct snd_soc_component *codec = dai->component; |
217 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component: codec); |
218 | |
219 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
220 | snd_soc_dapm_disable_pin(dapm, pin: "SYSCLK" ); |
221 | } |
222 | |
223 | |
224 | static int jz4760_codec_pcm_trigger(struct snd_pcm_substream *substream, |
225 | int cmd, struct snd_soc_dai *dai) |
226 | { |
227 | struct snd_soc_component *codec = dai->component; |
228 | int ret = 0; |
229 | |
230 | switch (cmd) { |
231 | case SNDRV_PCM_TRIGGER_START: |
232 | case SNDRV_PCM_TRIGGER_RESUME: |
233 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
234 | if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) |
235 | snd_soc_component_force_bias_level(component: codec, level: SND_SOC_BIAS_ON); |
236 | break; |
237 | case SNDRV_PCM_TRIGGER_STOP: |
238 | case SNDRV_PCM_TRIGGER_SUSPEND: |
239 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
240 | /* do nothing */ |
241 | break; |
242 | default: |
243 | ret = -EINVAL; |
244 | } |
245 | |
246 | return ret; |
247 | } |
248 | |
249 | static int jz4760_codec_mute_stream(struct snd_soc_dai *dai, int mute, int direction) |
250 | { |
251 | struct snd_soc_component *codec = dai->component; |
252 | struct jz_codec *jz_codec = snd_soc_component_get_drvdata(c: codec); |
253 | unsigned int gain_bit = mute ? REG_IFR_GDO : REG_IFR_GUP; |
254 | unsigned int val, reg; |
255 | int change, err; |
256 | |
257 | change = snd_soc_component_update_bits(component: codec, reg: JZ4760_CODEC_REG_CR2, |
258 | REG_CR2_DAC_MUTE, |
259 | val: mute ? REG_CR2_DAC_MUTE : 0); |
260 | if (change == 1) { |
261 | regmap_read(map: jz_codec->regmap, reg: JZ4760_CODEC_REG_PMR2, val: &val); |
262 | |
263 | if (val & BIT(REG_PMR2_SB_DAC_OFFSET)) |
264 | return 1; |
265 | |
266 | err = regmap_read_poll_timeout(jz_codec->regmap, |
267 | JZ4760_CODEC_REG_IFR, |
268 | val, val & gain_bit, |
269 | 1000, 1 * USEC_PER_SEC); |
270 | if (err) { |
271 | dev_err(jz_codec->dev, |
272 | "Timeout while setting digital mute: %d" , err); |
273 | return err; |
274 | } |
275 | |
276 | /* clear GUP/GDO flag */ |
277 | regmap_write(map: jz_codec->regmap, reg: JZ4760_CODEC_REG_IFR, val: gain_bit); |
278 | } |
279 | |
280 | regmap_read(map: jz_codec->regmap, reg: JZ4760_CODEC_REG_CR2, val: ®); |
281 | |
282 | return 0; |
283 | } |
284 | |
285 | /* unit: 0.01dB */ |
286 | static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 100); |
287 | static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0); |
288 | static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 100); |
289 | static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0); |
290 | static const DECLARE_TLV_DB_MINMAX(mixer_tlv, -3100, 0); |
291 | |
292 | /* Unconditional controls. */ |
293 | static const struct snd_kcontrol_new jz4760_codec_snd_controls[] = { |
294 | /* record gain control */ |
295 | SOC_DOUBLE_R_TLV("PCM Capture Volume" , |
296 | JZ4760_CODEC_REG_GCR9, JZ4760_CODEC_REG_GCR8, |
297 | REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 0, adc_tlv), |
298 | |
299 | SOC_DOUBLE_R_TLV("Line In Bypass Playback Volume" , |
300 | JZ4760_CODEC_REG_GCR4, JZ4760_CODEC_REG_GCR3, |
301 | REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv), |
302 | |
303 | SOC_SINGLE_TLV("Mixer Capture Volume" , |
304 | JZ4760_CODEC_REG_MIX1, |
305 | REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv), |
306 | |
307 | SOC_SINGLE_TLV("Mixer Playback Volume" , |
308 | JZ4760_CODEC_REG_MIX2, |
309 | REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv), |
310 | |
311 | SOC_SINGLE("High-Pass Filter Capture Switch" , |
312 | JZ4760_CODEC_REG_CR4, |
313 | REG_CR4_ADC_HPF_OFFSET, 1, 0), |
314 | }; |
315 | |
316 | static const struct snd_kcontrol_new jz4760_codec_pcm_playback_controls[] = { |
317 | { |
318 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
319 | .name = "Volume" , |
320 | .info = snd_soc_info_volsw, |
321 | .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
322 | | SNDRV_CTL_ELEM_ACCESS_READWRITE, |
323 | .tlv.p = dac_tlv, |
324 | .get = snd_soc_dapm_get_volsw, |
325 | .put = snd_soc_dapm_put_volsw, |
326 | .private_value = SOC_DOUBLE_R_VALUE(JZ4760_CODEC_REG_GCR6, |
327 | JZ4760_CODEC_REG_GCR5, |
328 | REG_GCR_GAIN_OFFSET, |
329 | REG_GCR_GAIN_MAX, 1), |
330 | }, |
331 | }; |
332 | |
333 | static const struct snd_kcontrol_new jz4760_codec_hp_playback_controls[] = { |
334 | { |
335 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
336 | .name = "Volume" , |
337 | .info = snd_soc_info_volsw, |
338 | .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
339 | | SNDRV_CTL_ELEM_ACCESS_READWRITE, |
340 | .tlv.p = out_tlv, |
341 | .get = snd_soc_dapm_get_volsw, |
342 | .put = snd_soc_dapm_put_volsw, |
343 | .private_value = SOC_DOUBLE_R_VALUE(JZ4760_CODEC_REG_GCR2, |
344 | JZ4760_CODEC_REG_GCR1, |
345 | REG_GCR_GAIN_OFFSET, |
346 | REG_GCR_GAIN_MAX, 1), |
347 | }, |
348 | }; |
349 | |
350 | static int hpout_event(struct snd_soc_dapm_widget *w, |
351 | struct snd_kcontrol *kcontrol, int event) |
352 | { |
353 | struct snd_soc_component *codec = snd_soc_dapm_to_component(dapm: w->dapm); |
354 | struct jz_codec *jz_codec = snd_soc_component_get_drvdata(c: codec); |
355 | unsigned int val; |
356 | int err; |
357 | |
358 | switch (event) { |
359 | case SND_SOC_DAPM_PRE_PMU: |
360 | /* unmute HP */ |
361 | regmap_clear_bits(map: jz_codec->regmap, reg: JZ4760_CODEC_REG_CR1, |
362 | REG_CR1_HP_MUTE); |
363 | break; |
364 | |
365 | case SND_SOC_DAPM_POST_PMU: |
366 | /* wait for ramp-up complete (RUP) */ |
367 | err = regmap_read_poll_timeout(jz_codec->regmap, |
368 | JZ4760_CODEC_REG_IFR, |
369 | val, val & REG_IFR_RUP, |
370 | 1000, 1 * USEC_PER_SEC); |
371 | if (err) { |
372 | dev_err(jz_codec->dev, "RUP timeout: %d" , err); |
373 | return err; |
374 | } |
375 | |
376 | /* clear RUP flag */ |
377 | regmap_set_bits(map: jz_codec->regmap, reg: JZ4760_CODEC_REG_IFR, |
378 | REG_IFR_RUP); |
379 | |
380 | break; |
381 | |
382 | case SND_SOC_DAPM_POST_PMD: |
383 | /* mute HP */ |
384 | regmap_set_bits(map: jz_codec->regmap, reg: JZ4760_CODEC_REG_CR1, |
385 | REG_CR1_HP_MUTE); |
386 | |
387 | err = regmap_read_poll_timeout(jz_codec->regmap, |
388 | JZ4760_CODEC_REG_IFR, |
389 | val, val & REG_IFR_RDO, |
390 | 1000, 1 * USEC_PER_SEC); |
391 | if (err) { |
392 | dev_err(jz_codec->dev, "RDO timeout: %d" , err); |
393 | return err; |
394 | } |
395 | |
396 | /* clear RDO flag */ |
397 | regmap_set_bits(map: jz_codec->regmap, reg: JZ4760_CODEC_REG_IFR, |
398 | REG_IFR_RDO); |
399 | |
400 | break; |
401 | } |
402 | |
403 | return 0; |
404 | } |
405 | |
406 | static const char * const jz4760_codec_hp_texts[] = { |
407 | "PCM" , "Line In" , "Mic 1" , "Mic 2" |
408 | }; |
409 | |
410 | static const unsigned int jz4760_codec_hp_values[] = { 3, 2, 0, 1 }; |
411 | |
412 | static SOC_VALUE_ENUM_SINGLE_DECL(jz4760_codec_hp_enum, |
413 | JZ4760_CODEC_REG_CR1, |
414 | REG_CR1_OUTSEL_OFFSET, |
415 | REG_CR1_OUTSEL_MASK >> REG_CR1_OUTSEL_OFFSET, |
416 | jz4760_codec_hp_texts, |
417 | jz4760_codec_hp_values); |
418 | static const struct snd_kcontrol_new jz4760_codec_hp_source = |
419 | SOC_DAPM_ENUM("Route" , jz4760_codec_hp_enum); |
420 | |
421 | static const char * const jz4760_codec_cap_texts[] = { |
422 | "Line In" , "Mic 1" , "Mic 2" |
423 | }; |
424 | |
425 | static const unsigned int jz4760_codec_cap_values[] = { 2, 0, 1 }; |
426 | |
427 | static SOC_VALUE_ENUM_SINGLE_DECL(jz4760_codec_cap_enum, |
428 | JZ4760_CODEC_REG_CR3, |
429 | REG_CR3_ADC_INSEL_OFFSET, |
430 | REG_CR3_ADC_INSEL_MASK >> REG_CR3_ADC_INSEL_OFFSET, |
431 | jz4760_codec_cap_texts, |
432 | jz4760_codec_cap_values); |
433 | static const struct snd_kcontrol_new jz4760_codec_cap_source = |
434 | SOC_DAPM_ENUM("Route" , jz4760_codec_cap_enum); |
435 | |
436 | static const struct snd_kcontrol_new jz4760_codec_mic_controls[] = { |
437 | SOC_DAPM_SINGLE("Stereo Capture Switch" , JZ4760_CODEC_REG_CR3, |
438 | REG_CR3_MICSTEREO_OFFSET, 1, 0), |
439 | }; |
440 | |
441 | static const struct snd_kcontrol_new jz4760_codec_line_out_switch = |
442 | SOC_DAPM_SINGLE("Switch" , JZ4760_CODEC_REG_CR1, |
443 | REG_CR1_LO_MUTE_OFFSET, 0, 0); |
444 | static const struct snd_kcontrol_new jz4760_codec_btl_out_switch = |
445 | SOC_DAPM_SINGLE("Switch" , JZ4760_CODEC_REG_CR1, |
446 | REG_CR1_BTL_MUTE_OFFSET, 0, 0); |
447 | |
448 | static const struct snd_soc_dapm_widget jz4760_codec_dapm_widgets[] = { |
449 | SND_SOC_DAPM_PGA_E("HP Out" , JZ4760_CODEC_REG_PMR2, |
450 | REG_PMR2_SB_HP_OFFSET, 1, NULL, 0, hpout_event, |
451 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | |
452 | SND_SOC_DAPM_POST_PMD), |
453 | |
454 | SND_SOC_DAPM_SWITCH("Line Out" , JZ4760_CODEC_REG_PMR2, |
455 | REG_PMR2_SB_LOUT_OFFSET, 1, |
456 | &jz4760_codec_line_out_switch), |
457 | |
458 | SND_SOC_DAPM_SWITCH("BTL Out" , JZ4760_CODEC_REG_PMR2, |
459 | REG_PMR2_SB_BTL_OFFSET, 1, |
460 | &jz4760_codec_btl_out_switch), |
461 | |
462 | SND_SOC_DAPM_PGA("Line In" , JZ4760_CODEC_REG_PMR1, |
463 | REG_PMR1_SB_LINE_OFFSET, 1, NULL, 0), |
464 | |
465 | SND_SOC_DAPM_MUX("Headphones Source" , SND_SOC_NOPM, 0, 0, |
466 | &jz4760_codec_hp_source), |
467 | |
468 | SND_SOC_DAPM_MUX("Capture Source" , SND_SOC_NOPM, 0, 0, |
469 | &jz4760_codec_cap_source), |
470 | |
471 | SND_SOC_DAPM_PGA("Mic 1" , JZ4760_CODEC_REG_PMR1, |
472 | REG_PMR1_SB_MIC1_OFFSET, 1, NULL, 0), |
473 | |
474 | SND_SOC_DAPM_PGA("Mic 2" , JZ4760_CODEC_REG_PMR1, |
475 | REG_PMR1_SB_MIC2_OFFSET, 1, NULL, 0), |
476 | |
477 | SND_SOC_DAPM_PGA("Mic Diff" , JZ4760_CODEC_REG_CR3, |
478 | REG_CR3_MICDIFF_OFFSET, 0, NULL, 0), |
479 | |
480 | SND_SOC_DAPM_MIXER("Mic" , SND_SOC_NOPM, 0, 0, |
481 | jz4760_codec_mic_controls, |
482 | ARRAY_SIZE(jz4760_codec_mic_controls)), |
483 | |
484 | SND_SOC_DAPM_PGA("Line In Bypass" , JZ4760_CODEC_REG_PMR1, |
485 | REG_PMR1_SB_BYPASS_OFFSET, 1, NULL, 0), |
486 | |
487 | SND_SOC_DAPM_ADC("ADC" , "Capture" , JZ4760_CODEC_REG_PMR2, |
488 | REG_PMR2_SB_ADC_OFFSET, 1), |
489 | |
490 | SND_SOC_DAPM_DAC("DAC" , "Playback" , JZ4760_CODEC_REG_PMR2, |
491 | REG_PMR2_SB_DAC_OFFSET, 1), |
492 | |
493 | SND_SOC_DAPM_MIXER("PCM Playback" , SND_SOC_NOPM, 0, 0, |
494 | jz4760_codec_pcm_playback_controls, |
495 | ARRAY_SIZE(jz4760_codec_pcm_playback_controls)), |
496 | |
497 | SND_SOC_DAPM_MIXER("Headphones Playback" , SND_SOC_NOPM, 0, 0, |
498 | jz4760_codec_hp_playback_controls, |
499 | ARRAY_SIZE(jz4760_codec_hp_playback_controls)), |
500 | |
501 | SND_SOC_DAPM_SUPPLY("MICBIAS" , JZ4760_CODEC_REG_PMR1, |
502 | REG_PMR1_SB_MICBIAS_OFFSET, 1, NULL, 0), |
503 | |
504 | SND_SOC_DAPM_INPUT("MIC1P" ), |
505 | SND_SOC_DAPM_INPUT("MIC1N" ), |
506 | SND_SOC_DAPM_INPUT("MIC2P" ), |
507 | SND_SOC_DAPM_INPUT("MIC2N" ), |
508 | |
509 | SND_SOC_DAPM_INPUT("LLINEIN" ), |
510 | SND_SOC_DAPM_INPUT("RLINEIN" ), |
511 | |
512 | SND_SOC_DAPM_OUTPUT("LHPOUT" ), |
513 | SND_SOC_DAPM_OUTPUT("RHPOUT" ), |
514 | |
515 | SND_SOC_DAPM_OUTPUT("LOUT" ), |
516 | SND_SOC_DAPM_OUTPUT("ROUT" ), |
517 | |
518 | SND_SOC_DAPM_OUTPUT("BTLP" ), |
519 | SND_SOC_DAPM_OUTPUT("BTLN" ), |
520 | |
521 | SND_SOC_DAPM_OUTPUT("SYSCLK" ), |
522 | }; |
523 | |
524 | /* Unconditional routes. */ |
525 | static const struct snd_soc_dapm_route jz4760_codec_dapm_routes[] = { |
526 | { "Mic 1" , NULL, "MIC1P" }, |
527 | { "Mic Diff" , NULL, "MIC1N" }, |
528 | { "Mic 1" , NULL, "Mic Diff" }, |
529 | { "Mic 2" , NULL, "MIC2P" }, |
530 | { "Mic Diff" , NULL, "MIC2N" }, |
531 | { "Mic 2" , NULL, "Mic Diff" }, |
532 | |
533 | { "Line In" , NULL, "LLINEIN" }, |
534 | { "Line In" , NULL, "RLINEIN" }, |
535 | |
536 | { "Mic" , "Stereo Capture Switch" , "Mic 1" }, |
537 | { "Mic" , "Stereo Capture Switch" , "Mic 2" }, |
538 | { "Headphones Source" , "Mic 1" , "Mic" }, |
539 | { "Headphones Source" , "Mic 2" , "Mic" }, |
540 | { "Capture Source" , "Mic 1" , "Mic" }, |
541 | { "Capture Source" , "Mic 2" , "Mic" }, |
542 | |
543 | { "Capture Source" , "Line In" , "Line In" }, |
544 | { "Capture Source" , "Mic 1" , "Mic 1" }, |
545 | { "Capture Source" , "Mic 2" , "Mic 2" }, |
546 | { "ADC" , NULL, "Capture Source" }, |
547 | |
548 | { "Line In Bypass" , NULL, "Line In" }, |
549 | |
550 | { "Headphones Source" , "Mic 1" , "Mic 1" }, |
551 | { "Headphones Source" , "Mic 2" , "Mic 2" }, |
552 | { "Headphones Source" , "Line In" , "Line In Bypass" }, |
553 | { "Headphones Source" , "PCM" , "Headphones Playback" }, |
554 | { "HP Out" , NULL, "Headphones Source" }, |
555 | |
556 | { "LHPOUT" , NULL, "HP Out" }, |
557 | { "RHPOUT" , NULL, "HP Out" }, |
558 | { "Line Out" , "Switch" , "HP Out" }, |
559 | |
560 | { "LOUT" , NULL, "Line Out" }, |
561 | { "ROUT" , NULL, "Line Out" }, |
562 | { "BTL Out" , "Switch" , "Line Out" }, |
563 | |
564 | { "BTLP" , NULL, "BTL Out" }, |
565 | { "BTLN" , NULL, "BTL Out" }, |
566 | |
567 | { "PCM Playback" , "Volume" , "DAC" }, |
568 | { "Headphones Playback" , "Volume" , "PCM Playback" }, |
569 | |
570 | { "SYSCLK" , NULL, "DAC" }, |
571 | }; |
572 | |
573 | static void jz4760_codec_codec_init_regs(struct snd_soc_component *codec) |
574 | { |
575 | struct jz_codec *jz_codec = snd_soc_component_get_drvdata(c: codec); |
576 | struct regmap *regmap = jz_codec->regmap; |
577 | |
578 | /* Collect updates for later sending. */ |
579 | regcache_cache_only(map: regmap, enable: true); |
580 | |
581 | /* default Amp output to PCM */ |
582 | regmap_set_bits(map: regmap, reg: JZ4760_CODEC_REG_CR1, REG_CR1_OUTSEL_MASK); |
583 | |
584 | /* Disable stereo mic */ |
585 | regmap_clear_bits(map: regmap, reg: JZ4760_CODEC_REG_CR3, |
586 | BIT(REG_CR3_MICSTEREO_OFFSET)); |
587 | |
588 | /* Set mic 1 as default source for ADC */ |
589 | regmap_clear_bits(map: regmap, reg: JZ4760_CODEC_REG_CR3, |
590 | REG_CR3_ADC_INSEL_MASK); |
591 | |
592 | /* ADC/DAC: serial + i2s */ |
593 | regmap_set_bits(map: regmap, reg: JZ4760_CODEC_REG_AICR, |
594 | REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S | |
595 | REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S); |
596 | |
597 | /* The generated IRQ is a high level */ |
598 | regmap_clear_bits(map: regmap, reg: JZ4760_CODEC_REG_ICR, REG_ICR_INT_FORM_MASK); |
599 | regmap_update_bits(map: regmap, reg: JZ4760_CODEC_REG_ICR, REG_ICR_ALL_MASK, |
600 | REG_ICR_JACK_MASK | REG_ICR_RUP_MASK | |
601 | REG_ICR_RDO_MASK | REG_ICR_GUP_MASK | |
602 | REG_ICR_GDO_MASK); |
603 | |
604 | /* 12M oscillator */ |
605 | regmap_clear_bits(map: regmap, reg: JZ4760_CODEC_REG_CCR1, REG_CCR1_CRYSTAL_MASK); |
606 | |
607 | /* 0: 16ohm/220uF, 1: 10kohm/1uF */ |
608 | regmap_clear_bits(map: regmap, reg: JZ4760_CODEC_REG_CR1, REG_CR1_HP_LOAD); |
609 | |
610 | /* default to NOMAD */ |
611 | regmap_set_bits(map: jz_codec->regmap, reg: JZ4760_CODEC_REG_CR2, |
612 | REG_CR2_DAC_NOMAD); |
613 | |
614 | /* disable automatic gain */ |
615 | regmap_clear_bits(map: regmap, reg: JZ4760_CODEC_REG_AGC1, REG_AGC1_EN); |
616 | |
617 | /* Independent L/R DAC gain control */ |
618 | regmap_clear_bits(map: regmap, reg: JZ4760_CODEC_REG_GCR5, |
619 | REG_GCR_RL); |
620 | |
621 | /* Send collected updates. */ |
622 | regcache_cache_only(map: regmap, enable: false); |
623 | regcache_sync(map: regmap); |
624 | } |
625 | |
626 | static int jz4760_codec_codec_probe(struct snd_soc_component *codec) |
627 | { |
628 | struct jz_codec *jz_codec = snd_soc_component_get_drvdata(c: codec); |
629 | |
630 | clk_prepare_enable(clk: jz_codec->clk); |
631 | |
632 | jz4760_codec_codec_init_regs(codec); |
633 | |
634 | return 0; |
635 | } |
636 | |
637 | static void jz4760_codec_codec_remove(struct snd_soc_component *codec) |
638 | { |
639 | struct jz_codec *jz_codec = snd_soc_component_get_drvdata(c: codec); |
640 | |
641 | clk_disable_unprepare(clk: jz_codec->clk); |
642 | } |
643 | |
644 | static const struct snd_soc_component_driver jz4760_codec_soc_codec_dev = { |
645 | .probe = jz4760_codec_codec_probe, |
646 | .remove = jz4760_codec_codec_remove, |
647 | .set_bias_level = jz4760_codec_set_bias_level, |
648 | .controls = jz4760_codec_snd_controls, |
649 | .num_controls = ARRAY_SIZE(jz4760_codec_snd_controls), |
650 | .dapm_widgets = jz4760_codec_dapm_widgets, |
651 | .num_dapm_widgets = ARRAY_SIZE(jz4760_codec_dapm_widgets), |
652 | .dapm_routes = jz4760_codec_dapm_routes, |
653 | .num_dapm_routes = ARRAY_SIZE(jz4760_codec_dapm_routes), |
654 | .suspend_bias_off = 1, |
655 | .use_pmdown_time = 1, |
656 | }; |
657 | |
658 | static const unsigned int jz4760_codec_sample_rates[] = { |
659 | 96000, 48000, 44100, 32000, |
660 | 24000, 22050, 16000, 12000, |
661 | 11025, 9600, 8000, |
662 | }; |
663 | |
664 | static int jz4760_codec_hw_params(struct snd_pcm_substream *substream, |
665 | struct snd_pcm_hw_params *params, |
666 | struct snd_soc_dai *dai) |
667 | { |
668 | struct jz_codec *codec = snd_soc_component_get_drvdata(c: dai->component); |
669 | unsigned int rate, bit_width; |
670 | |
671 | switch (params_format(p: params)) { |
672 | case SNDRV_PCM_FORMAT_S16_LE: |
673 | bit_width = 0; |
674 | break; |
675 | case SNDRV_PCM_FORMAT_S18_3LE: |
676 | bit_width = 1; |
677 | break; |
678 | case SNDRV_PCM_FORMAT_S20_3LE: |
679 | bit_width = 2; |
680 | break; |
681 | case SNDRV_PCM_FORMAT_S24_3LE: |
682 | bit_width = 3; |
683 | break; |
684 | default: |
685 | return -EINVAL; |
686 | } |
687 | |
688 | for (rate = 0; rate < ARRAY_SIZE(jz4760_codec_sample_rates); rate++) { |
689 | if (jz4760_codec_sample_rates[rate] == params_rate(p: params)) |
690 | break; |
691 | } |
692 | |
693 | if (rate == ARRAY_SIZE(jz4760_codec_sample_rates)) |
694 | return -EINVAL; |
695 | |
696 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
697 | regmap_update_bits(map: codec->regmap, reg: JZ4760_CODEC_REG_AICR, |
698 | REG_AICR_DAC_ADWL_MASK, |
699 | FIELD_PREP(REG_AICR_DAC_ADWL_MASK, bit_width)); |
700 | regmap_update_bits(map: codec->regmap, reg: JZ4760_CODEC_REG_CCR2, |
701 | REG_CCR2_DAC_FREQ_MASK, |
702 | FIELD_PREP(REG_CCR2_DAC_FREQ_MASK, rate)); |
703 | } else { |
704 | regmap_update_bits(map: codec->regmap, reg: JZ4760_CODEC_REG_AICR, |
705 | REG_AICR_ADC_ADWL_MASK, |
706 | FIELD_PREP(REG_AICR_ADC_ADWL_MASK, bit_width)); |
707 | regmap_update_bits(map: codec->regmap, reg: JZ4760_CODEC_REG_CCR2, |
708 | REG_CCR2_ADC_FREQ_MASK, |
709 | FIELD_PREP(REG_CCR2_ADC_FREQ_MASK, rate)); |
710 | } |
711 | |
712 | return 0; |
713 | } |
714 | |
715 | static const struct snd_soc_dai_ops jz4760_codec_dai_ops = { |
716 | .startup = jz4760_codec_startup, |
717 | .shutdown = jz4760_codec_shutdown, |
718 | .hw_params = jz4760_codec_hw_params, |
719 | .trigger = jz4760_codec_pcm_trigger, |
720 | .mute_stream = jz4760_codec_mute_stream, |
721 | .no_capture_mute = 1, |
722 | }; |
723 | |
724 | #define JZ_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ |
725 | SNDRV_PCM_FMTBIT_S18_3LE | \ |
726 | SNDRV_PCM_FMTBIT_S20_3LE | \ |
727 | SNDRV_PCM_FMTBIT_S24_3LE) |
728 | |
729 | static struct snd_soc_dai_driver jz4760_codec_dai = { |
730 | .name = "jz4760-hifi" , |
731 | .playback = { |
732 | .stream_name = "Playback" , |
733 | .channels_min = 2, |
734 | .channels_max = 2, |
735 | .rates = SNDRV_PCM_RATE_8000_96000, |
736 | .formats = JZ_CODEC_FORMATS, |
737 | }, |
738 | .capture = { |
739 | .stream_name = "Capture" , |
740 | .channels_min = 2, |
741 | .channels_max = 2, |
742 | .rates = SNDRV_PCM_RATE_8000_96000, |
743 | .formats = JZ_CODEC_FORMATS, |
744 | }, |
745 | .ops = &jz4760_codec_dai_ops, |
746 | }; |
747 | |
748 | static bool jz4760_codec_volatile(struct device *dev, unsigned int reg) |
749 | { |
750 | return reg == JZ4760_CODEC_REG_SR || reg == JZ4760_CODEC_REG_IFR; |
751 | } |
752 | |
753 | static bool jz4760_codec_writeable(struct device *dev, unsigned int reg) |
754 | { |
755 | switch (reg) { |
756 | case JZ4760_CODEC_REG_SR: |
757 | return false; |
758 | default: |
759 | return true; |
760 | } |
761 | } |
762 | |
763 | static int jz4760_codec_io_wait(struct jz_codec *codec) |
764 | { |
765 | u32 reg; |
766 | |
767 | return readl_poll_timeout(codec->base + ICDC_RGADW_OFFSET, reg, |
768 | !(reg & ICDC_RGADW_RGWR), |
769 | 1000, 1 * USEC_PER_SEC); |
770 | } |
771 | |
772 | static int jz4760_codec_reg_read(void *context, unsigned int reg, |
773 | unsigned int *val) |
774 | { |
775 | struct jz_codec *codec = context; |
776 | unsigned int i; |
777 | u32 tmp; |
778 | int ret; |
779 | |
780 | ret = jz4760_codec_io_wait(codec); |
781 | if (ret) |
782 | return ret; |
783 | |
784 | tmp = readl(addr: codec->base + ICDC_RGADW_OFFSET); |
785 | tmp &= ~ICDC_RGADW_RGADDR_MASK; |
786 | tmp |= FIELD_PREP(ICDC_RGADW_RGADDR_MASK, reg); |
787 | writel(val: tmp, addr: codec->base + ICDC_RGADW_OFFSET); |
788 | |
789 | /* wait 6+ cycles */ |
790 | for (i = 0; i < 6; i++) |
791 | *val = readl(addr: codec->base + ICDC_RGDATA_OFFSET) & |
792 | ICDC_RGDATA_RGDOUT_MASK; |
793 | |
794 | return 0; |
795 | } |
796 | |
797 | static int jz4760_codec_reg_write(void *context, unsigned int reg, |
798 | unsigned int val) |
799 | { |
800 | struct jz_codec *codec = context; |
801 | int ret; |
802 | |
803 | ret = jz4760_codec_io_wait(codec); |
804 | if (ret) |
805 | return ret; |
806 | |
807 | writel(ICDC_RGADW_RGWR | FIELD_PREP(ICDC_RGADW_RGADDR_MASK, reg) | val, |
808 | addr: codec->base + ICDC_RGADW_OFFSET); |
809 | |
810 | ret = jz4760_codec_io_wait(codec); |
811 | if (ret) |
812 | return ret; |
813 | |
814 | return 0; |
815 | } |
816 | |
817 | static const u8 jz4760_codec_reg_defaults[] = { |
818 | 0x00, 0xFC, 0x1B, 0x20, 0x00, 0x80, 0x00, 0x00, |
819 | 0xFF, 0x1F, 0x3F, 0x00, 0x06, 0x06, 0x06, 0x06, |
820 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x07, 0x44, |
821 | 0x1F, 0x00, 0x00, 0x00 |
822 | }; |
823 | |
824 | static struct regmap_config jz4760_codec_regmap_config = { |
825 | .reg_bits = 7, |
826 | .val_bits = 8, |
827 | |
828 | .max_register = JZ4760_CODEC_REG_MIX2, |
829 | .volatile_reg = jz4760_codec_volatile, |
830 | .writeable_reg = jz4760_codec_writeable, |
831 | |
832 | .reg_read = jz4760_codec_reg_read, |
833 | .reg_write = jz4760_codec_reg_write, |
834 | |
835 | .reg_defaults_raw = jz4760_codec_reg_defaults, |
836 | .num_reg_defaults_raw = ARRAY_SIZE(jz4760_codec_reg_defaults), |
837 | .cache_type = REGCACHE_FLAT, |
838 | }; |
839 | |
840 | static int jz4760_codec_probe(struct platform_device *pdev) |
841 | { |
842 | struct device *dev = &pdev->dev; |
843 | struct jz_codec *codec; |
844 | int ret; |
845 | |
846 | codec = devm_kzalloc(dev, size: sizeof(*codec), GFP_KERNEL); |
847 | if (!codec) |
848 | return -ENOMEM; |
849 | |
850 | codec->dev = dev; |
851 | |
852 | codec->base = devm_platform_ioremap_resource(pdev, index: 0); |
853 | if (IS_ERR(ptr: codec->base)) |
854 | return PTR_ERR(ptr: codec->base); |
855 | |
856 | codec->regmap = devm_regmap_init(dev, NULL, codec, |
857 | &jz4760_codec_regmap_config); |
858 | if (IS_ERR(ptr: codec->regmap)) |
859 | return PTR_ERR(ptr: codec->regmap); |
860 | |
861 | codec->clk = devm_clk_get(dev, id: "aic" ); |
862 | if (IS_ERR(ptr: codec->clk)) |
863 | return PTR_ERR(ptr: codec->clk); |
864 | |
865 | platform_set_drvdata(pdev, data: codec); |
866 | |
867 | ret = devm_snd_soc_register_component(dev, component_driver: &jz4760_codec_soc_codec_dev, |
868 | dai_drv: &jz4760_codec_dai, num_dai: 1); |
869 | if (ret) { |
870 | dev_err(dev, "Failed to register codec: %d\n" , ret); |
871 | return ret; |
872 | } |
873 | |
874 | return 0; |
875 | } |
876 | |
877 | static const struct of_device_id jz4760_codec_of_matches[] = { |
878 | { .compatible = "ingenic,jz4760-codec" , }, |
879 | { /* sentinel */ } |
880 | }; |
881 | MODULE_DEVICE_TABLE(of, jz4760_codec_of_matches); |
882 | |
883 | static struct platform_driver jz4760_codec_driver = { |
884 | .probe = jz4760_codec_probe, |
885 | .driver = { |
886 | .name = "jz4760-codec" , |
887 | .of_match_table = jz4760_codec_of_matches, |
888 | }, |
889 | }; |
890 | module_platform_driver(jz4760_codec_driver); |
891 | |
892 | MODULE_DESCRIPTION("JZ4760 SoC internal codec driver" ); |
893 | MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>" ); |
894 | MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>" ); |
895 | MODULE_LICENSE("GPL v2" ); |
896 | |