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