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