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