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, ®);
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