blob: 6b60120f59a68e9d6a9a5d03c9f6236ba9c81b30 [file] [log] [blame]
Paul Cercueil2159a682019-12-24 01:27:08 +01001// SPDX-License-Identifier: GPL-2.0
2//
3// Ingenic JZ4770 CODEC driver
4//
5// Copyright (C) 2012, Maarten ter Huurne <maarten@treewalker.org>
6// Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net>
7
8#include <linux/clk.h>
9#include <linux/delay.h>
10#include <linux/iopoll.h>
11#include <linux/module.h>
12#include <linux/regmap.h>
13#include <linux/time64.h>
14
15#include <sound/pcm_params.h>
16#include <sound/soc.h>
17#include <sound/soc-dai.h>
18#include <sound/soc-dapm.h>
19#include <sound/tlv.h>
20
21#define ICDC_RGADW_OFFSET 0x00
22#define ICDC_RGDATA_OFFSET 0x04
23
24/* ICDC internal register access control register(RGADW) */
25#define ICDC_RGADW_RGWR BIT(16)
26
27#define ICDC_RGADW_RGADDR_OFFSET 8
28#define ICDC_RGADW_RGADDR_MASK GENMASK(14, ICDC_RGADW_RGADDR_OFFSET)
29
30#define ICDC_RGADW_RGDIN_OFFSET 0
31#define ICDC_RGADW_RGDIN_MASK GENMASK(7, ICDC_RGADW_RGDIN_OFFSET)
32
33/* ICDC internal register data output register (RGDATA)*/
34#define ICDC_RGDATA_IRQ BIT(8)
35
36#define ICDC_RGDATA_RGDOUT_OFFSET 0
37#define ICDC_RGDATA_RGDOUT_MASK GENMASK(7, ICDC_RGDATA_RGDOUT_OFFSET)
38
39/* Internal register space, accessed through regmap */
40enum {
41 JZ4770_CODEC_REG_SR,
42 JZ4770_CODEC_REG_AICR_DAC,
43 JZ4770_CODEC_REG_AICR_ADC,
44 JZ4770_CODEC_REG_CR_LO,
45 JZ4770_CODEC_REG_CR_HP,
46
47 JZ4770_CODEC_REG_MISSING_REG1,
48
49 JZ4770_CODEC_REG_CR_DAC,
50 JZ4770_CODEC_REG_CR_MIC,
51 JZ4770_CODEC_REG_CR_LI,
52 JZ4770_CODEC_REG_CR_ADC,
53 JZ4770_CODEC_REG_CR_MIX,
54 JZ4770_CODEC_REG_CR_VIC,
55 JZ4770_CODEC_REG_CCR,
56 JZ4770_CODEC_REG_FCR_DAC,
57 JZ4770_CODEC_REG_FCR_ADC,
58 JZ4770_CODEC_REG_ICR,
59 JZ4770_CODEC_REG_IMR,
60 JZ4770_CODEC_REG_IFR,
61 JZ4770_CODEC_REG_GCR_HPL,
62 JZ4770_CODEC_REG_GCR_HPR,
63 JZ4770_CODEC_REG_GCR_LIBYL,
64 JZ4770_CODEC_REG_GCR_LIBYR,
65 JZ4770_CODEC_REG_GCR_DACL,
66 JZ4770_CODEC_REG_GCR_DACR,
67 JZ4770_CODEC_REG_GCR_MIC1,
68 JZ4770_CODEC_REG_GCR_MIC2,
69 JZ4770_CODEC_REG_GCR_ADCL,
70 JZ4770_CODEC_REG_GCR_ADCR,
71
72 JZ4770_CODEC_REG_MISSING_REG2,
73
74 JZ4770_CODEC_REG_GCR_MIXADC,
75 JZ4770_CODEC_REG_GCR_MIXDAC,
76 JZ4770_CODEC_REG_AGC1,
77 JZ4770_CODEC_REG_AGC2,
78 JZ4770_CODEC_REG_AGC3,
79 JZ4770_CODEC_REG_AGC4,
80 JZ4770_CODEC_REG_AGC5,
81};
82
83#define REG_AICR_DAC_ADWL_OFFSET 6
84#define REG_AICR_DAC_ADWL_MASK (0x3 << REG_AICR_DAC_ADWL_OFFSET)
85#define REG_AICR_DAC_SERIAL BIT(1)
86#define REG_AICR_DAC_I2S BIT(0)
87
88#define REG_AICR_ADC_ADWL_OFFSET 6
89#define REG_AICR_ADC_ADWL_MASK (0x3 << REG_AICR_ADC_ADWL_OFFSET)
90#define REG_AICR_ADC_SERIAL BIT(1)
91#define REG_AICR_ADC_I2S BIT(0)
92
93#define REG_CR_LO_MUTE_OFFSET 7
94#define REG_CR_LO_SB_OFFSET 4
95#define REG_CR_LO_SEL_OFFSET 0
96#define REG_CR_LO_SEL_MASK (0x3 << REG_CR_LO_SEL_OFFSET)
97
98#define REG_CR_HP_MUTE BIT(7)
99#define REG_CR_HP_LOAD BIT(6)
100#define REG_CR_HP_SB_OFFSET 4
Paul Cercueile648e3f2020-12-07 12:53:37 +0000101#define REG_CR_HP_SB_HPCM_OFFSET 3
Paul Cercueil2159a682019-12-24 01:27:08 +0100102#define REG_CR_HP_SEL_OFFSET 0
103#define REG_CR_HP_SEL_MASK (0x3 << REG_CR_HP_SEL_OFFSET)
104
105#define REG_CR_DAC_MUTE BIT(7)
106#define REG_CR_DAC_MONO BIT(6)
107#define REG_CR_DAC_LEFT_ONLY BIT(5)
108#define REG_CR_DAC_SB_OFFSET 4
109#define REG_CR_DAC_LRSWAP BIT(3)
110
111#define REG_CR_MIC_STEREO_OFFSET 7
112#define REG_CR_MIC_IDIFF_OFFSET 6
113#define REG_CR_MIC_SB_MIC2_OFFSET 5
114#define REG_CR_MIC_SB_MIC1_OFFSET 4
115#define REG_CR_MIC_BIAS_V0_OFFSET 1
116#define REG_CR_MIC_BIAS_SB_OFFSET 0
117
118#define REG_CR_LI_LIBY_OFFSET 4
119#define REG_CR_LI_SB_OFFSET 0
120
121#define REG_CR_ADC_DMIC_SEL BIT(7)
122#define REG_CR_ADC_MONO BIT(6)
123#define REG_CR_ADC_LEFT_ONLY BIT(5)
124#define REG_CR_ADC_SB_OFFSET 4
125#define REG_CR_ADC_LRSWAP BIT(3)
126#define REG_CR_ADC_IN_SEL_OFFSET 0
127#define REG_CR_ADC_IN_SEL_MASK (0x3 << REG_CR_ADC_IN_SEL_OFFSET)
128
129#define REG_CR_VIC_SB_SLEEP BIT(1)
130#define REG_CR_VIC_SB BIT(0)
131
132#define REG_CCR_CRYSTAL_OFFSET 0
133#define REG_CCR_CRYSTAL_MASK (0xf << REG_CCR_CRYSTAL_OFFSET)
134
135#define REG_FCR_DAC_FREQ_OFFSET 0
136#define REG_FCR_DAC_FREQ_MASK (0xf << REG_FCR_DAC_FREQ_OFFSET)
137
138#define REG_FCR_ADC_FREQ_OFFSET 0
139#define REG_FCR_ADC_FREQ_MASK (0xf << REG_FCR_ADC_FREQ_OFFSET)
140
141#define REG_ICR_INT_FORM_OFFSET 6
142#define REG_ICR_INT_FORM_MASK (0x3 << REG_ICR_INT_FORM_OFFSET)
143
144#define REG_IMR_ALL_MASK (0x7f)
145#define REG_IMR_SCLR_MASK BIT(6)
146#define REG_IMR_JACK_MASK BIT(5)
147#define REG_IMR_SCMC_MASK BIT(4)
148#define REG_IMR_RUP_MASK BIT(3)
149#define REG_IMR_RDO_MASK BIT(2)
150#define REG_IMR_GUP_MASK BIT(1)
151#define REG_IMR_GDO_MASK BIT(0)
152
153#define REG_IFR_ALL_MASK (0x7f)
154#define REG_IFR_SCLR BIT(6)
155#define REG_IFR_JACK BIT(5)
156#define REG_IFR_SCMC BIT(4)
157#define REG_IFR_RUP BIT(3)
158#define REG_IFR_RDO BIT(2)
159#define REG_IFR_GUP BIT(1)
160#define REG_IFR_GDO BIT(0)
161
162#define REG_GCR_HPL_LRGO BIT(7)
163
164#define REG_GCR_DACL_RLGOD BIT(7)
165
166#define REG_GCR_GAIN_OFFSET 0
167#define REG_GCR_GAIN_MAX 0x1f
168
169#define REG_GCR_MIC_GAIN_OFFSET 0
170#define REG_GCR_MIC_GAIN_MAX 5
171
172#define REG_GCR_ADC_GAIN_OFFSET 0
173#define REG_GCR_ADC_GAIN_MAX 23
174
175#define REG_AGC1_EN BIT(7)
176
177/* codec private data */
178struct jz_codec {
179 struct device *dev;
180 struct regmap *regmap;
181 void __iomem *base;
182 struct clk *clk;
183};
184
185static int jz4770_codec_set_bias_level(struct snd_soc_component *codec,
186 enum snd_soc_bias_level level)
187{
188 struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
189 struct regmap *regmap = jz_codec->regmap;
190
191 switch (level) {
192 case SND_SOC_BIAS_PREPARE:
Christophe Branchereaua346c772020-12-07 12:53:34 +0000193 /* Reset all interrupt flags. */
194 regmap_write(regmap, JZ4770_CODEC_REG_IFR, REG_IFR_ALL_MASK);
195
Paul Cercueilad13c832020-12-07 12:53:33 +0000196 regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
197 REG_CR_VIC_SB);
Paul Cercueil2159a682019-12-24 01:27:08 +0100198 msleep(250);
Paul Cercueilad13c832020-12-07 12:53:33 +0000199 regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
200 REG_CR_VIC_SB_SLEEP);
Paul Cercueil2159a682019-12-24 01:27:08 +0100201 msleep(400);
202 break;
203 case SND_SOC_BIAS_STANDBY:
Paul Cercueilad13c832020-12-07 12:53:33 +0000204 regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
205 REG_CR_VIC_SB_SLEEP);
206 regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_VIC,
207 REG_CR_VIC_SB);
Gustavo A. R. Silvadf561f662020-08-23 17:36:59 -0500208 fallthrough;
Paul Cercueil2159a682019-12-24 01:27:08 +0100209 default:
210 break;
211 }
212
213 return 0;
214}
215
216static int jz4770_codec_startup(struct snd_pcm_substream *substream,
217 struct snd_soc_dai *dai)
218{
219 struct snd_soc_component *codec = dai->component;
220 struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec);
221
222 /*
223 * SYSCLK output from the codec to the AIC is required to keep the
224 * DMA transfer going during playback when all audible outputs have
225 * been disabled.
226 */
227 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
228 snd_soc_dapm_force_enable_pin(dapm, "SYSCLK");
229
230 return 0;
231}
232
233static void jz4770_codec_shutdown(struct snd_pcm_substream *substream,
234 struct snd_soc_dai *dai)
235{
236 struct snd_soc_component *codec = dai->component;
237 struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec);
238
239 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
240 snd_soc_dapm_disable_pin(dapm, "SYSCLK");
241}
242
243
244static int jz4770_codec_pcm_trigger(struct snd_pcm_substream *substream,
245 int cmd, struct snd_soc_dai *dai)
246{
247 struct snd_soc_component *codec = dai->component;
248 int ret = 0;
249
250 switch (cmd) {
251 case SNDRV_PCM_TRIGGER_START:
252 case SNDRV_PCM_TRIGGER_RESUME:
253 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
254 if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
255 snd_soc_component_force_bias_level(codec,
256 SND_SOC_BIAS_ON);
257 break;
258 case SNDRV_PCM_TRIGGER_STOP:
259 case SNDRV_PCM_TRIGGER_SUSPEND:
260 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
261 /* do nothing */
262 break;
263 default:
264 ret = -EINVAL;
265 }
266
267 return ret;
268}
269
Kuninori Morimoto54b59272020-07-09 10:56:20 +0900270static int jz4770_codec_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
Paul Cercueil2159a682019-12-24 01:27:08 +0100271{
272 struct snd_soc_component *codec = dai->component;
273 struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
274 unsigned int gain_bit = mute ? REG_IFR_GDO : REG_IFR_GUP;
275 unsigned int val;
276 int change, err;
277
278 change = snd_soc_component_update_bits(codec, JZ4770_CODEC_REG_CR_DAC,
279 REG_CR_DAC_MUTE,
280 mute ? REG_CR_DAC_MUTE : 0);
281 if (change == 1) {
282 regmap_read(jz_codec->regmap, JZ4770_CODEC_REG_CR_DAC, &val);
283
284 if (val & BIT(REG_CR_DAC_SB_OFFSET))
285 return 1;
286
287 err = regmap_read_poll_timeout(jz_codec->regmap,
288 JZ4770_CODEC_REG_IFR,
289 val, val & gain_bit,
Christophe Branchereau6b4da532020-12-07 12:53:35 +0000290 1000, 1 * USEC_PER_SEC);
Paul Cercueil2159a682019-12-24 01:27:08 +0100291 if (err) {
292 dev_err(jz_codec->dev,
293 "Timeout while setting digital mute: %d", err);
294 return err;
295 }
296
297 /* clear GUP/GDO flag */
Paul Cercueilad13c832020-12-07 12:53:33 +0000298 regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR,
299 gain_bit);
Paul Cercueil2159a682019-12-24 01:27:08 +0100300 }
301
302 return 0;
303}
304
305/* unit: 0.01dB */
306static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 0);
307static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0);
308static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 600);
Paul Cercueil2159a682019-12-24 01:27:08 +0100309static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0);
310
311/* Unconditional controls. */
312static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = {
313 /* record gain control */
314 SOC_DOUBLE_R_TLV("PCM Capture Volume",
315 JZ4770_CODEC_REG_GCR_ADCL, JZ4770_CODEC_REG_GCR_ADCR,
316 REG_GCR_ADC_GAIN_OFFSET, REG_GCR_ADC_GAIN_MAX,
317 0, adc_tlv),
318
319 SOC_DOUBLE_R_TLV("Line In Bypass Playback Volume",
320 JZ4770_CODEC_REG_GCR_LIBYL, JZ4770_CODEC_REG_GCR_LIBYR,
321 REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv),
322};
323
324static const struct snd_kcontrol_new jz4770_codec_pcm_playback_controls[] = {
325 {
326 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
327 .name = "Volume",
328 .info = snd_soc_info_volsw,
329 .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ
330 | SNDRV_CTL_ELEM_ACCESS_READWRITE,
331 .tlv.p = dac_tlv,
332 .get = snd_soc_dapm_get_volsw,
333 .put = snd_soc_dapm_put_volsw,
334 /*
335 * NOTE: DACR/DACL are inversed; the gain value written to DACR
336 * seems to affect the left channel, and the gain value written
337 * to DACL seems to affect the right channel.
338 */
339 .private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_DACR,
340 JZ4770_CODEC_REG_GCR_DACL,
341 REG_GCR_GAIN_OFFSET,
342 REG_GCR_GAIN_MAX, 1),
343 },
344};
345
346static const struct snd_kcontrol_new jz4770_codec_hp_playback_controls[] = {
347 {
348 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
349 .name = "Volume",
350 .info = snd_soc_info_volsw,
351 .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ
352 | SNDRV_CTL_ELEM_ACCESS_READWRITE,
353 .tlv.p = out_tlv,
354 .get = snd_soc_dapm_get_volsw,
355 .put = snd_soc_dapm_put_volsw,
356 /* HPR/HPL inversed for the same reason as above */
357 .private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_HPR,
358 JZ4770_CODEC_REG_GCR_HPL,
359 REG_GCR_GAIN_OFFSET,
360 REG_GCR_GAIN_MAX, 1),
361 },
362};
363
364static int hpout_event(struct snd_soc_dapm_widget *w,
365 struct snd_kcontrol *kcontrol, int event)
366{
367 struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
368 struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
369 unsigned int val;
370 int err;
371
372 switch (event) {
373 case SND_SOC_DAPM_PRE_PMU:
Paul Cercueil4f293df2020-12-07 12:53:36 +0000374 /* unmute HP */
Paul Cercueilad13c832020-12-07 12:53:33 +0000375 regmap_clear_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP,
Paul Cercueil4f293df2020-12-07 12:53:36 +0000376 REG_CR_HP_MUTE);
Paul Cercueil2159a682019-12-24 01:27:08 +0100377 break;
378
379 case SND_SOC_DAPM_POST_PMU:
380 /* wait for ramp-up complete (RUP) */
381 err = regmap_read_poll_timeout(jz_codec->regmap,
382 JZ4770_CODEC_REG_IFR,
383 val, val & REG_IFR_RUP,
Christophe Branchereau6b4da532020-12-07 12:53:35 +0000384 1000, 1 * USEC_PER_SEC);
Paul Cercueil2159a682019-12-24 01:27:08 +0100385 if (err) {
386 dev_err(jz_codec->dev, "RUP timeout: %d", err);
387 return err;
388 }
389
390 /* clear RUP flag */
Paul Cercueilad13c832020-12-07 12:53:33 +0000391 regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR,
392 REG_IFR_RUP);
Paul Cercueil2159a682019-12-24 01:27:08 +0100393
394 break;
395
396 case SND_SOC_DAPM_POST_PMD:
Paul Cercueil4f293df2020-12-07 12:53:36 +0000397 /* mute HP */
Paul Cercueilad13c832020-12-07 12:53:33 +0000398 regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP,
Paul Cercueil4f293df2020-12-07 12:53:36 +0000399 REG_CR_HP_MUTE);
Paul Cercueil2159a682019-12-24 01:27:08 +0100400
401 err = regmap_read_poll_timeout(jz_codec->regmap,
402 JZ4770_CODEC_REG_IFR,
403 val, val & REG_IFR_RDO,
Christophe Branchereau6b4da532020-12-07 12:53:35 +0000404 1000, 1 * USEC_PER_SEC);
Paul Cercueil2159a682019-12-24 01:27:08 +0100405 if (err) {
406 dev_err(jz_codec->dev, "RDO timeout: %d", err);
407 return err;
408 }
409
410 /* clear RDO flag */
Paul Cercueilad13c832020-12-07 12:53:33 +0000411 regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR,
412 REG_IFR_RDO);
Paul Cercueil2159a682019-12-24 01:27:08 +0100413
414 break;
415 }
416
417 return 0;
418}
419
420static int adc_poweron_event(struct snd_soc_dapm_widget *w,
421 struct snd_kcontrol *kcontrol, int event)
422{
423 if (event == SND_SOC_DAPM_POST_PMU)
424 msleep(1000);
425
426 return 0;
427}
428
429static const char * const jz4770_codec_hp_texts[] = {
430 "PCM", "Line In", "Mic 1", "Mic 2"
431};
432static const unsigned int jz4770_codec_hp_values[] = { 3, 2, 0, 1 };
433static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_hp_enum,
434 JZ4770_CODEC_REG_CR_HP,
435 REG_CR_HP_SEL_OFFSET,
436 REG_CR_HP_SEL_MASK,
437 jz4770_codec_hp_texts,
438 jz4770_codec_hp_values);
439static const struct snd_kcontrol_new jz4770_codec_hp_source =
440 SOC_DAPM_ENUM("Route", jz4770_codec_hp_enum);
441
442static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_lo_enum,
443 JZ4770_CODEC_REG_CR_LO,
444 REG_CR_LO_SEL_OFFSET,
445 REG_CR_LO_SEL_MASK,
446 jz4770_codec_hp_texts,
447 jz4770_codec_hp_values);
448static const struct snd_kcontrol_new jz4770_codec_lo_source =
449 SOC_DAPM_ENUM("Route", jz4770_codec_lo_enum);
450
451static const char * const jz4770_codec_cap_texts[] = {
452 "Line In", "Mic 1", "Mic 2"
453};
454static const unsigned int jz4770_codec_cap_values[] = { 2, 0, 1 };
455static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_cap_enum,
456 JZ4770_CODEC_REG_CR_ADC,
457 REG_CR_ADC_IN_SEL_OFFSET,
458 REG_CR_ADC_IN_SEL_MASK,
459 jz4770_codec_cap_texts,
460 jz4770_codec_cap_values);
461static const struct snd_kcontrol_new jz4770_codec_cap_source =
462 SOC_DAPM_ENUM("Route", jz4770_codec_cap_enum);
463
464static const struct snd_kcontrol_new jz4770_codec_mic_controls[] = {
465 SOC_DAPM_SINGLE("Stereo Capture Switch", JZ4770_CODEC_REG_CR_MIC,
466 REG_CR_MIC_STEREO_OFFSET, 1, 0),
467};
468
469static const struct snd_soc_dapm_widget jz4770_codec_dapm_widgets[] = {
470 SND_SOC_DAPM_PGA_E("HP Out", JZ4770_CODEC_REG_CR_HP,
471 REG_CR_HP_SB_OFFSET, 1, NULL, 0, hpout_event,
472 SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
473 SND_SOC_DAPM_POST_PMD),
474
475 SND_SOC_DAPM_PGA("Line Out", JZ4770_CODEC_REG_CR_LO,
476 REG_CR_LO_SB_OFFSET, 1, NULL, 0),
477
478 SND_SOC_DAPM_PGA("Line Out Switch 2", JZ4770_CODEC_REG_CR_LO,
479 REG_CR_LO_MUTE_OFFSET, 1, NULL, 0),
480
481 SND_SOC_DAPM_PGA("Line In", JZ4770_CODEC_REG_CR_LI,
482 REG_CR_LI_SB_OFFSET, 1, NULL, 0),
483
484 SND_SOC_DAPM_MUX("Headphones Source", SND_SOC_NOPM, 0, 0,
485 &jz4770_codec_hp_source),
486 SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0,
487 &jz4770_codec_cap_source),
488 SND_SOC_DAPM_MUX("Line Out Source", SND_SOC_NOPM, 0, 0,
489 &jz4770_codec_lo_source),
490
491 SND_SOC_DAPM_PGA("Mic 1", JZ4770_CODEC_REG_CR_MIC,
492 REG_CR_MIC_SB_MIC1_OFFSET, 1, NULL, 0),
493 SND_SOC_DAPM_PGA("Mic 2", JZ4770_CODEC_REG_CR_MIC,
494 REG_CR_MIC_SB_MIC2_OFFSET, 1, NULL, 0),
495
496 SND_SOC_DAPM_PGA("Mic Diff", JZ4770_CODEC_REG_CR_MIC,
497 REG_CR_MIC_IDIFF_OFFSET, 0, NULL, 0),
498
499 SND_SOC_DAPM_MIXER("Mic", SND_SOC_NOPM, 0, 0,
500 jz4770_codec_mic_controls,
501 ARRAY_SIZE(jz4770_codec_mic_controls)),
502
503 SND_SOC_DAPM_PGA("Line In Bypass", JZ4770_CODEC_REG_CR_LI,
504 REG_CR_LI_LIBY_OFFSET, 1, NULL, 0),
505
506 SND_SOC_DAPM_ADC_E("ADC", "HiFi Capture", JZ4770_CODEC_REG_CR_ADC,
507 REG_CR_ADC_SB_OFFSET, 1, adc_poweron_event,
508 SND_SOC_DAPM_POST_PMU),
509 SND_SOC_DAPM_DAC("DAC", "HiFi Playback", JZ4770_CODEC_REG_CR_DAC,
510 REG_CR_DAC_SB_OFFSET, 1),
511
512 SND_SOC_DAPM_MIXER("PCM Playback", SND_SOC_NOPM, 0, 0,
513 jz4770_codec_pcm_playback_controls,
514 ARRAY_SIZE(jz4770_codec_pcm_playback_controls)),
515 SND_SOC_DAPM_MIXER("Headphones Playback", SND_SOC_NOPM, 0, 0,
516 jz4770_codec_hp_playback_controls,
517 ARRAY_SIZE(jz4770_codec_hp_playback_controls)),
518
519 SND_SOC_DAPM_SUPPLY("MICBIAS", JZ4770_CODEC_REG_CR_MIC,
520 REG_CR_MIC_BIAS_SB_OFFSET, 1, NULL, 0),
521
Paul Cercueile648e3f2020-12-07 12:53:37 +0000522 SND_SOC_DAPM_SUPPLY("Cap-less", JZ4770_CODEC_REG_CR_HP,
523 REG_CR_HP_SB_HPCM_OFFSET, 1, NULL, 0),
524
Paul Cercueil2159a682019-12-24 01:27:08 +0100525 SND_SOC_DAPM_INPUT("MIC1P"),
526 SND_SOC_DAPM_INPUT("MIC1N"),
527 SND_SOC_DAPM_INPUT("MIC2P"),
528 SND_SOC_DAPM_INPUT("MIC2N"),
529
530 SND_SOC_DAPM_OUTPUT("LOUT"),
531 SND_SOC_DAPM_OUTPUT("ROUT"),
532
533 SND_SOC_DAPM_OUTPUT("LHPOUT"),
534 SND_SOC_DAPM_OUTPUT("RHPOUT"),
535
536 SND_SOC_DAPM_INPUT("LLINEIN"),
537 SND_SOC_DAPM_INPUT("RLINEIN"),
538
539 SND_SOC_DAPM_OUTPUT("SYSCLK"),
540};
541
542/* Unconditional routes. */
543static const struct snd_soc_dapm_route jz4770_codec_dapm_routes[] = {
544 { "Mic 1", NULL, "MIC1P" },
545 { "Mic Diff", NULL, "MIC1N" },
546 { "Mic 1", NULL, "Mic Diff" },
547 { "Mic 2", NULL, "MIC2P" },
548 { "Mic Diff", NULL, "MIC2N" },
549 { "Mic 2", NULL, "Mic Diff" },
550
551 { "Line In", NULL, "LLINEIN" },
552 { "Line In", NULL, "RLINEIN" },
553
554 { "Mic", "Stereo Capture Switch", "Mic 1" },
555 { "Mic", "Stereo Capture Switch", "Mic 2" },
556 { "Headphones Source", "Mic 1", "Mic" },
557 { "Headphones Source", "Mic 2", "Mic" },
558 { "Capture Source", "Mic 1", "Mic" },
559 { "Capture Source", "Mic 2", "Mic" },
560
561 { "Headphones Source", "Mic 1", "Mic 1" },
562 { "Headphones Source", "Mic 2", "Mic 2" },
563 { "Headphones Source", "Line In", "Line In Bypass" },
564 { "Headphones Source", "PCM", "Headphones Playback" },
565 { "HP Out", NULL, "Headphones Source" },
566
567 { "Capture Source", "Line In", "Line In" },
568 { "Capture Source", "Mic 1", "Mic 1" },
569 { "Capture Source", "Mic 2", "Mic 2" },
570 { "ADC", NULL, "Capture Source" },
571
572 { "Line In Bypass", NULL, "Line In" },
573 { "Line Out Source", "Line In", "Line In Bypass" },
574 { "Line Out Source", "PCM", "PCM Playback" },
575
576 { "LHPOUT", NULL, "HP Out"},
577 { "RHPOUT", NULL, "HP Out"},
578
579 { "Line Out", NULL, "Line Out Source" },
580 { "Line Out Switch 2", NULL, "Line Out" },
581
582 { "LOUT", NULL, "Line Out Switch 2"},
583 { "ROUT", NULL, "Line Out Switch 2"},
584
585 { "PCM Playback", "Volume", "DAC" },
586 { "Headphones Playback", "Volume", "PCM Playback" },
587
588 { "SYSCLK", NULL, "DAC" },
589};
590
591static void jz4770_codec_codec_init_regs(struct snd_soc_component *codec)
592{
593 struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
594 struct regmap *regmap = jz_codec->regmap;
595
596 /* Collect updates for later sending. */
597 regcache_cache_only(regmap, true);
598
599 /* default HP output to PCM */
Paul Cercueilad13c832020-12-07 12:53:33 +0000600 regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_HP, REG_CR_HP_SEL_MASK);
Paul Cercueil2159a682019-12-24 01:27:08 +0100601
602 /* default line output to PCM */
Paul Cercueilad13c832020-12-07 12:53:33 +0000603 regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_LO, REG_CR_LO_SEL_MASK);
Paul Cercueil2159a682019-12-24 01:27:08 +0100604
605 /* Disable stereo mic */
Paul Cercueilad13c832020-12-07 12:53:33 +0000606 regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_MIC,
607 BIT(REG_CR_MIC_STEREO_OFFSET));
Paul Cercueil2159a682019-12-24 01:27:08 +0100608
609 /* Set mic 1 as default source for ADC */
Paul Cercueilad13c832020-12-07 12:53:33 +0000610 regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_ADC,
611 REG_CR_ADC_IN_SEL_MASK);
Paul Cercueil2159a682019-12-24 01:27:08 +0100612
613 /* ADC/DAC: serial + i2s */
Paul Cercueilad13c832020-12-07 12:53:33 +0000614 regmap_set_bits(regmap, JZ4770_CODEC_REG_AICR_ADC,
615 REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S);
616 regmap_set_bits(regmap, JZ4770_CODEC_REG_AICR_DAC,
617 REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S);
Paul Cercueil2159a682019-12-24 01:27:08 +0100618
619 /* The generated IRQ is a high level */
Paul Cercueilad13c832020-12-07 12:53:33 +0000620 regmap_clear_bits(regmap, JZ4770_CODEC_REG_ICR, REG_ICR_INT_FORM_MASK);
Paul Cercueil2159a682019-12-24 01:27:08 +0100621 regmap_update_bits(regmap, JZ4770_CODEC_REG_IMR, REG_IMR_ALL_MASK,
622 REG_IMR_JACK_MASK | REG_IMR_RUP_MASK |
623 REG_IMR_RDO_MASK | REG_IMR_GUP_MASK |
624 REG_IMR_GDO_MASK);
625
626 /* 12M oscillator */
Paul Cercueilad13c832020-12-07 12:53:33 +0000627 regmap_clear_bits(regmap, JZ4770_CODEC_REG_CCR, REG_CCR_CRYSTAL_MASK);
Paul Cercueil2159a682019-12-24 01:27:08 +0100628
629 /* 0: 16ohm/220uF, 1: 10kohm/1uF */
Paul Cercueilad13c832020-12-07 12:53:33 +0000630 regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_HP, REG_CR_HP_LOAD);
Paul Cercueil2159a682019-12-24 01:27:08 +0100631
632 /* disable automatic gain */
Paul Cercueilad13c832020-12-07 12:53:33 +0000633 regmap_clear_bits(regmap, JZ4770_CODEC_REG_AGC1, REG_AGC1_EN);
Paul Cercueil2159a682019-12-24 01:27:08 +0100634
635 /* Disable DAC lrswap */
Paul Cercueilad13c832020-12-07 12:53:33 +0000636 regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_DAC, REG_CR_DAC_LRSWAP);
Paul Cercueil2159a682019-12-24 01:27:08 +0100637
638 /* Independent L/R DAC gain control */
Paul Cercueilad13c832020-12-07 12:53:33 +0000639 regmap_clear_bits(regmap, JZ4770_CODEC_REG_GCR_DACL,
640 REG_GCR_DACL_RLGOD);
Paul Cercueil2159a682019-12-24 01:27:08 +0100641
642 /* Disable ADC lrswap */
Paul Cercueilad13c832020-12-07 12:53:33 +0000643 regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_ADC, REG_CR_ADC_LRSWAP);
Paul Cercueil2159a682019-12-24 01:27:08 +0100644
645 /* default to cap-less mode(0) */
Paul Cercueile648e3f2020-12-07 12:53:37 +0000646 regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_HP,
647 BIT(REG_CR_HP_SB_HPCM_OFFSET));
Paul Cercueil2159a682019-12-24 01:27:08 +0100648
649 /* Send collected updates. */
650 regcache_cache_only(regmap, false);
651 regcache_sync(regmap);
Paul Cercueil2159a682019-12-24 01:27:08 +0100652}
653
654static int jz4770_codec_codec_probe(struct snd_soc_component *codec)
655{
656 struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
657
658 clk_prepare_enable(jz_codec->clk);
659
660 jz4770_codec_codec_init_regs(codec);
661
662 return 0;
663}
664
665static void jz4770_codec_codec_remove(struct snd_soc_component *codec)
666{
667 struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec);
668
669 clk_disable_unprepare(jz_codec->clk);
670}
671
672static const struct snd_soc_component_driver jz4770_codec_soc_codec_dev = {
673 .probe = jz4770_codec_codec_probe,
674 .remove = jz4770_codec_codec_remove,
675 .set_bias_level = jz4770_codec_set_bias_level,
676 .controls = jz4770_codec_snd_controls,
677 .num_controls = ARRAY_SIZE(jz4770_codec_snd_controls),
678 .dapm_widgets = jz4770_codec_dapm_widgets,
679 .num_dapm_widgets = ARRAY_SIZE(jz4770_codec_dapm_widgets),
680 .dapm_routes = jz4770_codec_dapm_routes,
681 .num_dapm_routes = ARRAY_SIZE(jz4770_codec_dapm_routes),
682 .suspend_bias_off = 1,
683 .use_pmdown_time = 1,
684};
685
686static const unsigned int jz4770_codec_sample_rates[] = {
687 96000, 48000, 44100, 32000,
688 24000, 22050, 16000, 12000,
689 11025, 9600, 8000,
690};
691
692static int jz4770_codec_hw_params(struct snd_pcm_substream *substream,
693 struct snd_pcm_hw_params *params,
694 struct snd_soc_dai *dai)
695{
696 struct jz_codec *codec = snd_soc_component_get_drvdata(dai->component);
697 unsigned int rate, bit_width;
698
699 switch (params_format(params)) {
700 case SNDRV_PCM_FORMAT_S16_LE:
701 bit_width = 0;
702 break;
703 case SNDRV_PCM_FORMAT_S18_3LE:
704 bit_width = 1;
705 break;
706 case SNDRV_PCM_FORMAT_S20_3LE:
707 bit_width = 2;
708 break;
709 case SNDRV_PCM_FORMAT_S24_3LE:
710 bit_width = 3;
711 break;
712 default:
713 return -EINVAL;
714 }
715
716 for (rate = 0; rate < ARRAY_SIZE(jz4770_codec_sample_rates); rate++) {
717 if (jz4770_codec_sample_rates[rate] == params_rate(params))
718 break;
719 }
720
721 if (rate == ARRAY_SIZE(jz4770_codec_sample_rates))
722 return -EINVAL;
723
724 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
725 regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_DAC,
726 REG_AICR_DAC_ADWL_MASK,
727 bit_width << REG_AICR_DAC_ADWL_OFFSET);
728 regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_DAC,
729 REG_FCR_DAC_FREQ_MASK,
730 rate << REG_FCR_DAC_FREQ_OFFSET);
731 } else {
732 regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_ADC,
733 REG_AICR_ADC_ADWL_MASK,
734 bit_width << REG_AICR_ADC_ADWL_OFFSET);
735 regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_ADC,
736 REG_FCR_ADC_FREQ_MASK,
737 rate << REG_FCR_ADC_FREQ_OFFSET);
738 }
739
740 return 0;
741}
742
743static const struct snd_soc_dai_ops jz4770_codec_dai_ops = {
744 .startup = jz4770_codec_startup,
745 .shutdown = jz4770_codec_shutdown,
746 .hw_params = jz4770_codec_hw_params,
747 .trigger = jz4770_codec_pcm_trigger,
Kuninori Morimoto54b59272020-07-09 10:56:20 +0900748 .mute_stream = jz4770_codec_mute_stream,
749 .no_capture_mute = 1,
Paul Cercueil2159a682019-12-24 01:27:08 +0100750};
751
752#define JZ_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
753 SNDRV_PCM_FMTBIT_S18_3LE | \
754 SNDRV_PCM_FMTBIT_S20_3LE | \
755 SNDRV_PCM_FMTBIT_S24_3LE)
756
757static struct snd_soc_dai_driver jz4770_codec_dai = {
758 .name = "jz4770-hifi",
759 .playback = {
760 .stream_name = "Playback",
761 .channels_min = 2,
762 .channels_max = 2,
763 .rates = SNDRV_PCM_RATE_8000_96000,
764 .formats = JZ_CODEC_FORMATS,
765 },
766 .capture = {
767 .stream_name = "Capture",
768 .channels_min = 2,
769 .channels_max = 2,
770 .rates = SNDRV_PCM_RATE_8000_96000,
771 .formats = JZ_CODEC_FORMATS,
772 },
773 .ops = &jz4770_codec_dai_ops,
774};
775
776static bool jz4770_codec_volatile(struct device *dev, unsigned int reg)
777{
778 return reg == JZ4770_CODEC_REG_SR || reg == JZ4770_CODEC_REG_IFR;
779}
780
781static bool jz4770_codec_readable(struct device *dev, unsigned int reg)
782{
783 switch (reg) {
784 case JZ4770_CODEC_REG_MISSING_REG1:
785 case JZ4770_CODEC_REG_MISSING_REG2:
786 return false;
787 default:
788 return true;
789 }
790}
791
792static bool jz4770_codec_writeable(struct device *dev, unsigned int reg)
793{
794 switch (reg) {
795 case JZ4770_CODEC_REG_SR:
796 case JZ4770_CODEC_REG_MISSING_REG1:
797 case JZ4770_CODEC_REG_MISSING_REG2:
798 return false;
799 default:
800 return true;
801 }
802}
803
804static int jz4770_codec_io_wait(struct jz_codec *codec)
805{
806 u32 reg;
807
808 return readl_poll_timeout(codec->base + ICDC_RGADW_OFFSET, reg,
809 !(reg & ICDC_RGADW_RGWR),
Christophe Branchereau6b4da532020-12-07 12:53:35 +0000810 1000, 1 * USEC_PER_SEC);
Paul Cercueil2159a682019-12-24 01:27:08 +0100811}
812
813static int jz4770_codec_reg_read(void *context, unsigned int reg,
814 unsigned int *val)
815{
816 struct jz_codec *codec = context;
817 unsigned int i;
818 u32 tmp;
819 int ret;
820
821 ret = jz4770_codec_io_wait(codec);
822 if (ret)
823 return ret;
824
825 tmp = readl(codec->base + ICDC_RGADW_OFFSET);
826 tmp = (tmp & ~ICDC_RGADW_RGADDR_MASK)
827 | (reg << ICDC_RGADW_RGADDR_OFFSET);
828 writel(tmp, codec->base + ICDC_RGADW_OFFSET);
829
830 /* wait 6+ cycles */
831 for (i = 0; i < 6; i++)
832 *val = readl(codec->base + ICDC_RGDATA_OFFSET) &
833 ICDC_RGDATA_RGDOUT_MASK;
834
835 return 0;
836}
837
838static int jz4770_codec_reg_write(void *context, unsigned int reg,
839 unsigned int val)
840{
841 struct jz_codec *codec = context;
842 int ret;
843
844 ret = jz4770_codec_io_wait(codec);
845 if (ret)
846 return ret;
847
848 writel(ICDC_RGADW_RGWR | (reg << ICDC_RGADW_RGADDR_OFFSET) | val,
849 codec->base + ICDC_RGADW_OFFSET);
850
851 ret = jz4770_codec_io_wait(codec);
852 if (ret)
853 return ret;
854
855 return 0;
856}
857
858static const u8 jz4770_codec_reg_defaults[] = {
859 0x00, 0xC3, 0xC3, 0x90, 0x98, 0xFF, 0x90, 0xB1,
860 0x11, 0x10, 0x00, 0x03, 0x00, 0x00, 0x40, 0x00,
861 0xFF, 0x00, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00,
862 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x34,
863 0x07, 0x44, 0x1F, 0x00
864};
865
866static struct regmap_config jz4770_codec_regmap_config = {
867 .reg_bits = 7,
868 .val_bits = 8,
869
870 .max_register = JZ4770_CODEC_REG_AGC5,
871 .volatile_reg = jz4770_codec_volatile,
872 .readable_reg = jz4770_codec_readable,
873 .writeable_reg = jz4770_codec_writeable,
874
875 .reg_read = jz4770_codec_reg_read,
876 .reg_write = jz4770_codec_reg_write,
877
878 .reg_defaults_raw = jz4770_codec_reg_defaults,
879 .num_reg_defaults_raw = ARRAY_SIZE(jz4770_codec_reg_defaults),
880 .cache_type = REGCACHE_FLAT,
881};
882
883static int jz4770_codec_probe(struct platform_device *pdev)
884{
885 struct device *dev = &pdev->dev;
886 struct jz_codec *codec;
887 int ret;
888
889 codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL);
890 if (!codec)
891 return -ENOMEM;
892
893 codec->dev = dev;
894
895 codec->base = devm_platform_ioremap_resource(pdev, 0);
Tang Bincc2d7422021-03-07 15:21:33 +0800896 if (IS_ERR(codec->base))
897 return PTR_ERR(codec->base);
Paul Cercueil2159a682019-12-24 01:27:08 +0100898
899 codec->regmap = devm_regmap_init(dev, NULL, codec,
900 &jz4770_codec_regmap_config);
901 if (IS_ERR(codec->regmap))
902 return PTR_ERR(codec->regmap);
903
904 codec->clk = devm_clk_get(dev, "aic");
905 if (IS_ERR(codec->clk))
906 return PTR_ERR(codec->clk);
907
908 platform_set_drvdata(pdev, codec);
909
910 ret = devm_snd_soc_register_component(dev, &jz4770_codec_soc_codec_dev,
911 &jz4770_codec_dai, 1);
912 if (ret) {
913 dev_err(dev, "Failed to register codec: %d\n", ret);
914 return ret;
915 }
916
917 return 0;
918}
919
920static const struct of_device_id jz4770_codec_of_matches[] = {
921 { .compatible = "ingenic,jz4770-codec", },
922 { /* sentinel */ }
923};
924MODULE_DEVICE_TABLE(of, jz4770_codec_of_matches);
925
926static struct platform_driver jz4770_codec_driver = {
927 .probe = jz4770_codec_probe,
928 .driver = {
929 .name = "jz4770-codec",
Paul Cercueile6825ba2020-05-23 14:54:55 +0200930 .of_match_table = jz4770_codec_of_matches,
Paul Cercueil2159a682019-12-24 01:27:08 +0100931 },
932};
933module_platform_driver(jz4770_codec_driver);
934
935MODULE_DESCRIPTION("JZ4770 SoC internal codec driver");
936MODULE_AUTHOR("Maarten ter Huurne <maarten@treewalker.org>");
937MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
938MODULE_LICENSE("GPL v2");