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