]>
Commit | Line | Data |
---|---|---|
0e15bdfd BX |
1 | /* |
2 | * Copyright (C) 2017 Sanechips Technology Co., Ltd. | |
3 | * Copyright 2017 Linaro Ltd. | |
4 | * | |
5 | * Author: Baoyou Xie <baoyou.xie@linaro.org> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | */ | |
11 | ||
12 | #include <linux/gpio/consumer.h> | |
13 | #include <linux/i2c.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/regmap.h> | |
16 | #include <sound/pcm.h> | |
17 | #include <sound/pcm_params.h> | |
18 | #include <sound/soc.h> | |
19 | #include <sound/soc-dai.h> | |
20 | #include <sound/tlv.h> | |
21 | ||
22 | #define AUD96P22_RESET 0x00 | |
23 | #define RST_DAC_DPZ BIT(0) | |
24 | #define RST_ADC_DPZ BIT(1) | |
25 | #define AUD96P22_I2S1_CONFIG_0 0x03 | |
26 | #define I2S1_MS_MODE BIT(3) | |
27 | #define I2S1_MODE_MASK 0x7 | |
28 | #define I2S1_MODE_RIGHT_J 0x0 | |
29 | #define I2S1_MODE_I2S 0x1 | |
30 | #define I2S1_MODE_LEFT_J 0x2 | |
31 | #define AUD96P22_PD_0 0x15 | |
32 | #define AUD96P22_PD_1 0x16 | |
33 | #define AUD96P22_PD_3 0x18 | |
34 | #define AUD96P22_PD_4 0x19 | |
35 | #define AUD96P22_MUTE_0 0x1d | |
36 | #define AUD96P22_MUTE_2 0x1f | |
37 | #define AUD96P22_MUTE_4 0x21 | |
38 | #define AUD96P22_RECVOL_0 0x24 | |
39 | #define AUD96P22_RECVOL_1 0x25 | |
40 | #define AUD96P22_PGA1VOL_0 0x26 | |
41 | #define AUD96P22_PGA1VOL_1 0x27 | |
42 | #define AUD96P22_LMVOL_0 0x34 | |
43 | #define AUD96P22_LMVOL_1 0x35 | |
44 | #define AUD96P22_HS1VOL_0 0x38 | |
45 | #define AUD96P22_HS1VOL_1 0x39 | |
46 | #define AUD96P22_PGA1SEL_0 0x47 | |
47 | #define AUD96P22_PGA1SEL_1 0x48 | |
48 | #define AUD96P22_LDR1SEL_0 0x59 | |
49 | #define AUD96P22_LDR1SEL_1 0x60 | |
50 | #define AUD96P22_LDR2SEL_0 0x5d | |
51 | #define AUD96P22_REG_MAX 0xfb | |
52 | ||
53 | struct aud96p22_priv { | |
54 | struct regmap *regmap; | |
55 | }; | |
56 | ||
57 | static int aud96p22_adc_event(struct snd_soc_dapm_widget *w, | |
58 | struct snd_kcontrol *kcontrol, int event) | |
59 | { | |
60 | struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); | |
61 | struct aud96p22_priv *priv = snd_soc_codec_get_drvdata(codec); | |
62 | struct regmap *regmap = priv->regmap; | |
63 | ||
64 | if (event != SND_SOC_DAPM_POST_PMU) | |
65 | return -EINVAL; | |
66 | ||
67 | /* Assert/de-assert the bit to reset ADC data path */ | |
68 | regmap_update_bits(regmap, AUD96P22_RESET, RST_ADC_DPZ, 0); | |
69 | regmap_update_bits(regmap, AUD96P22_RESET, RST_ADC_DPZ, RST_ADC_DPZ); | |
70 | ||
71 | return 0; | |
72 | } | |
73 | ||
74 | static int aud96p22_dac_event(struct snd_soc_dapm_widget *w, | |
75 | struct snd_kcontrol *kcontrol, int event) | |
76 | { | |
77 | struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); | |
78 | struct aud96p22_priv *priv = snd_soc_codec_get_drvdata(codec); | |
79 | struct regmap *regmap = priv->regmap; | |
80 | ||
81 | if (event != SND_SOC_DAPM_POST_PMU) | |
82 | return -EINVAL; | |
83 | ||
84 | /* Assert/de-assert the bit to reset DAC data path */ | |
85 | regmap_update_bits(regmap, AUD96P22_RESET, RST_DAC_DPZ, 0); | |
86 | regmap_update_bits(regmap, AUD96P22_RESET, RST_DAC_DPZ, RST_DAC_DPZ); | |
87 | ||
88 | return 0; | |
89 | } | |
90 | ||
91 | static const DECLARE_TLV_DB_SCALE(lm_tlv, -11550, 50, 0); | |
92 | static const DECLARE_TLV_DB_SCALE(hs_tlv, -3900, 300, 0); | |
93 | static const DECLARE_TLV_DB_SCALE(rec_tlv, -9550, 50, 0); | |
94 | static const DECLARE_TLV_DB_SCALE(pga_tlv, -1800, 100, 0); | |
95 | ||
96 | static const struct snd_kcontrol_new aud96p22_snd_controls[] = { | |
97 | /* Volume control */ | |
98 | SOC_DOUBLE_R_TLV("Master Playback Volume", AUD96P22_LMVOL_0, | |
99 | AUD96P22_LMVOL_1, 0, 0xff, 0, lm_tlv), | |
100 | SOC_DOUBLE_R_TLV("Headphone Volume", AUD96P22_HS1VOL_0, | |
101 | AUD96P22_HS1VOL_1, 0, 0xf, 0, hs_tlv), | |
102 | SOC_DOUBLE_R_TLV("Master Capture Volume", AUD96P22_RECVOL_0, | |
103 | AUD96P22_RECVOL_1, 0, 0xff, 0, rec_tlv), | |
104 | SOC_DOUBLE_R_TLV("Analogue Capture Volume", AUD96P22_PGA1VOL_0, | |
105 | AUD96P22_PGA1VOL_1, 0, 0x37, 0, pga_tlv), | |
106 | ||
107 | /* Mute control */ | |
108 | SOC_DOUBLE("Master Playback Switch", AUD96P22_MUTE_2, 0, 1, 1, 1), | |
109 | SOC_DOUBLE("Headphone Switch", AUD96P22_MUTE_2, 4, 5, 1, 1), | |
110 | SOC_DOUBLE("Line Out Switch", AUD96P22_MUTE_4, 0, 1, 1, 1), | |
111 | SOC_DOUBLE("Speaker Switch", AUD96P22_MUTE_4, 2, 3, 1, 1), | |
112 | SOC_DOUBLE("Master Capture Switch", AUD96P22_MUTE_0, 0, 1, 1, 1), | |
113 | SOC_DOUBLE("Analogue Capture Switch", AUD96P22_MUTE_0, 2, 3, 1, 1), | |
114 | }; | |
115 | ||
116 | /* Input mux kcontrols */ | |
117 | static const unsigned int ain_mux_values[] = { | |
118 | 0, 1, 3, 4, 5, | |
119 | }; | |
120 | ||
121 | static const char * const ainl_mux_texts[] = { | |
122 | "AINL1 differential", | |
123 | "AINL1 single-ended", | |
124 | "AINL3 single-ended", | |
125 | "AINL2 differential", | |
126 | "AINL2 single-ended", | |
127 | }; | |
128 | ||
129 | static const char * const ainr_mux_texts[] = { | |
130 | "AINR1 differential", | |
131 | "AINR1 single-ended", | |
132 | "AINR3 single-ended", | |
133 | "AINR2 differential", | |
134 | "AINR2 single-ended", | |
135 | }; | |
136 | ||
137 | static SOC_VALUE_ENUM_SINGLE_DECL(ainl_mux_enum, AUD96P22_PGA1SEL_0, | |
138 | 0, 0x7, ainl_mux_texts, ain_mux_values); | |
139 | static SOC_VALUE_ENUM_SINGLE_DECL(ainr_mux_enum, AUD96P22_PGA1SEL_1, | |
140 | 0, 0x7, ainr_mux_texts, ain_mux_values); | |
141 | ||
142 | static const struct snd_kcontrol_new ainl_mux_kcontrol = | |
143 | SOC_DAPM_ENUM("AINL Mux", ainl_mux_enum); | |
144 | static const struct snd_kcontrol_new ainr_mux_kcontrol = | |
145 | SOC_DAPM_ENUM("AINR Mux", ainr_mux_enum); | |
146 | ||
147 | /* Output mixer kcontrols */ | |
148 | static const struct snd_kcontrol_new ld1_left_kcontrols[] = { | |
149 | SOC_DAPM_SINGLE("DACL LD1L Switch", AUD96P22_LDR1SEL_0, 0, 1, 0), | |
150 | SOC_DAPM_SINGLE("AINL LD1L Switch", AUD96P22_LDR1SEL_0, 1, 1, 0), | |
151 | SOC_DAPM_SINGLE("AINR LD1L Switch", AUD96P22_LDR1SEL_0, 2, 1, 0), | |
152 | }; | |
153 | ||
154 | static const struct snd_kcontrol_new ld1_right_kcontrols[] = { | |
155 | SOC_DAPM_SINGLE("DACR LD1R Switch", AUD96P22_LDR1SEL_1, 8, 1, 0), | |
156 | SOC_DAPM_SINGLE("AINR LD1R Switch", AUD96P22_LDR1SEL_1, 9, 1, 0), | |
157 | SOC_DAPM_SINGLE("AINL LD1R Switch", AUD96P22_LDR1SEL_1, 10, 1, 0), | |
158 | }; | |
159 | ||
160 | static const struct snd_kcontrol_new ld2_kcontrols[] = { | |
161 | SOC_DAPM_SINGLE("DACL LD2 Switch", AUD96P22_LDR2SEL_0, 0, 1, 0), | |
162 | SOC_DAPM_SINGLE("AINL LD2 Switch", AUD96P22_LDR2SEL_0, 1, 1, 0), | |
163 | SOC_DAPM_SINGLE("DACR LD2 Switch", AUD96P22_LDR2SEL_0, 2, 1, 0), | |
164 | }; | |
165 | ||
166 | static const struct snd_soc_dapm_widget aud96p22_dapm_widgets[] = { | |
167 | /* Overall power bit */ | |
168 | SND_SOC_DAPM_SUPPLY("POWER", AUD96P22_PD_0, 0, 0, NULL, 0), | |
169 | ||
170 | /* Input pins */ | |
171 | SND_SOC_DAPM_INPUT("AINL1P"), | |
172 | SND_SOC_DAPM_INPUT("AINL2P"), | |
173 | SND_SOC_DAPM_INPUT("AINL3"), | |
174 | SND_SOC_DAPM_INPUT("AINL1N"), | |
175 | SND_SOC_DAPM_INPUT("AINL2N"), | |
176 | SND_SOC_DAPM_INPUT("AINR2N"), | |
177 | SND_SOC_DAPM_INPUT("AINR1N"), | |
178 | SND_SOC_DAPM_INPUT("AINR3"), | |
179 | SND_SOC_DAPM_INPUT("AINR2P"), | |
180 | SND_SOC_DAPM_INPUT("AINR1P"), | |
181 | ||
182 | /* Input muxes */ | |
183 | SND_SOC_DAPM_MUX("AINLMUX", AUD96P22_PD_1, 2, 0, &ainl_mux_kcontrol), | |
184 | SND_SOC_DAPM_MUX("AINRMUX", AUD96P22_PD_1, 3, 0, &ainr_mux_kcontrol), | |
185 | ||
186 | /* ADCs */ | |
187 | SND_SOC_DAPM_ADC_E("ADCL", "Capture Left", AUD96P22_PD_1, 0, 0, | |
188 | aud96p22_adc_event, SND_SOC_DAPM_POST_PMU), | |
189 | SND_SOC_DAPM_ADC_E("ADCR", "Capture Right", AUD96P22_PD_1, 1, 0, | |
190 | aud96p22_adc_event, SND_SOC_DAPM_POST_PMU), | |
191 | ||
192 | /* DACs */ | |
193 | SND_SOC_DAPM_DAC_E("DACL", "Playback Left", AUD96P22_PD_3, 0, 0, | |
194 | aud96p22_dac_event, SND_SOC_DAPM_POST_PMU), | |
195 | SND_SOC_DAPM_DAC_E("DACR", "Playback Right", AUD96P22_PD_3, 1, 0, | |
196 | aud96p22_dac_event, SND_SOC_DAPM_POST_PMU), | |
197 | ||
198 | /* Output mixers */ | |
199 | SND_SOC_DAPM_MIXER("LD1L", AUD96P22_PD_3, 6, 0, ld1_left_kcontrols, | |
200 | ARRAY_SIZE(ld1_left_kcontrols)), | |
201 | SND_SOC_DAPM_MIXER("LD1R", AUD96P22_PD_3, 7, 0, ld1_right_kcontrols, | |
202 | ARRAY_SIZE(ld1_right_kcontrols)), | |
203 | SND_SOC_DAPM_MIXER("LD2", AUD96P22_PD_4, 2, 0, ld2_kcontrols, | |
204 | ARRAY_SIZE(ld2_kcontrols)), | |
205 | ||
206 | /* Headset power switch */ | |
207 | SND_SOC_DAPM_SUPPLY("HS1L", AUD96P22_PD_3, 4, 0, NULL, 0), | |
208 | SND_SOC_DAPM_SUPPLY("HS1R", AUD96P22_PD_3, 5, 0, NULL, 0), | |
209 | ||
210 | /* Output pins */ | |
211 | SND_SOC_DAPM_OUTPUT("HSOUTL"), | |
212 | SND_SOC_DAPM_OUTPUT("LINEOUTL"), | |
213 | SND_SOC_DAPM_OUTPUT("LINEOUTMP"), | |
214 | SND_SOC_DAPM_OUTPUT("LINEOUTMN"), | |
215 | SND_SOC_DAPM_OUTPUT("LINEOUTR"), | |
216 | SND_SOC_DAPM_OUTPUT("HSOUTR"), | |
217 | }; | |
218 | ||
219 | static const struct snd_soc_dapm_route aud96p22_dapm_routes[] = { | |
220 | { "AINLMUX", "AINL1 differential", "AINL1N" }, | |
221 | { "AINLMUX", "AINL1 single-ended", "AINL1P" }, | |
222 | { "AINLMUX", "AINL3 single-ended", "AINL3" }, | |
223 | { "AINLMUX", "AINL2 differential", "AINL2N" }, | |
224 | { "AINLMUX", "AINL2 single-ended", "AINL2P" }, | |
225 | ||
226 | { "AINRMUX", "AINR1 differential", "AINR1N" }, | |
227 | { "AINRMUX", "AINR1 single-ended", "AINR1P" }, | |
228 | { "AINRMUX", "AINR3 single-ended", "AINR3" }, | |
229 | { "AINRMUX", "AINR2 differential", "AINR2N" }, | |
230 | { "AINRMUX", "AINR2 single-ended", "AINR2P" }, | |
231 | ||
232 | { "ADCL", NULL, "AINLMUX" }, | |
233 | { "ADCR", NULL, "AINRMUX" }, | |
234 | ||
235 | { "ADCL", NULL, "POWER" }, | |
236 | { "ADCR", NULL, "POWER" }, | |
237 | { "DACL", NULL, "POWER" }, | |
238 | { "DACR", NULL, "POWER" }, | |
239 | ||
240 | { "LD1L", "DACL LD1L Switch", "DACL" }, | |
241 | { "LD1L", "AINL LD1L Switch", "AINLMUX" }, | |
242 | { "LD1L", "AINR LD1L Switch", "AINRMUX" }, | |
243 | ||
244 | { "LD1R", "DACR LD1R Switch", "DACR" }, | |
245 | { "LD1R", "AINR LD1R Switch", "AINRMUX" }, | |
246 | { "LD1R", "AINL LD1R Switch", "AINLMUX" }, | |
247 | ||
248 | { "LD2", "DACL LD2 Switch", "DACL" }, | |
249 | { "LD2", "AINL LD2 Switch", "AINLMUX" }, | |
250 | { "LD2", "DACR LD2 Switch", "DACR" }, | |
251 | ||
252 | { "HSOUTL", NULL, "LD1L" }, | |
253 | { "HSOUTR", NULL, "LD1R" }, | |
254 | { "HSOUTL", NULL, "HS1L" }, | |
255 | { "HSOUTR", NULL, "HS1R" }, | |
256 | ||
257 | { "LINEOUTL", NULL, "LD1L" }, | |
258 | { "LINEOUTR", NULL, "LD1R" }, | |
259 | ||
260 | { "LINEOUTMP", NULL, "LD2" }, | |
261 | { "LINEOUTMN", NULL, "LD2" }, | |
262 | }; | |
263 | ||
a180ba45 | 264 | static const struct snd_soc_codec_driver aud96p22_driver = { |
0e15bdfd BX |
265 | .component_driver = { |
266 | .controls = aud96p22_snd_controls, | |
267 | .num_controls = ARRAY_SIZE(aud96p22_snd_controls), | |
268 | .dapm_widgets = aud96p22_dapm_widgets, | |
269 | .num_dapm_widgets = ARRAY_SIZE(aud96p22_dapm_widgets), | |
270 | .dapm_routes = aud96p22_dapm_routes, | |
271 | .num_dapm_routes = ARRAY_SIZE(aud96p22_dapm_routes), | |
272 | }, | |
273 | }; | |
274 | ||
275 | static int aud96p22_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) | |
276 | { | |
277 | struct aud96p22_priv *priv = snd_soc_codec_get_drvdata(dai->codec); | |
278 | struct regmap *regmap = priv->regmap; | |
279 | unsigned int val; | |
280 | ||
281 | /* Master/slave mode */ | |
282 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | |
283 | case SND_SOC_DAIFMT_CBS_CFS: | |
284 | val = 0; | |
285 | break; | |
286 | case SND_SOC_DAIFMT_CBM_CFM: | |
287 | val = I2S1_MS_MODE; | |
288 | break; | |
289 | default: | |
290 | return -EINVAL; | |
291 | } | |
292 | ||
293 | regmap_update_bits(regmap, AUD96P22_I2S1_CONFIG_0, I2S1_MS_MODE, val); | |
294 | ||
295 | /* Audio format */ | |
296 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | |
297 | case SND_SOC_DAIFMT_RIGHT_J: | |
298 | val = I2S1_MODE_RIGHT_J; | |
299 | break; | |
300 | case SND_SOC_DAIFMT_I2S: | |
301 | val = I2S1_MODE_I2S; | |
302 | break; | |
303 | case SND_SOC_DAIFMT_LEFT_J: | |
304 | val = I2S1_MODE_LEFT_J; | |
305 | break; | |
306 | default: | |
307 | return -EINVAL; | |
308 | } | |
309 | ||
310 | regmap_update_bits(regmap, AUD96P22_I2S1_CONFIG_0, I2S1_MODE_MASK, val); | |
311 | ||
312 | return 0; | |
313 | } | |
314 | ||
315 | static struct snd_soc_dai_ops aud96p22_dai_ops = { | |
316 | .set_fmt = aud96p22_set_fmt, | |
317 | }; | |
318 | ||
319 | #define AUD96P22_RATES SNDRV_PCM_RATE_8000_192000 | |
320 | #define AUD96P22_FORMATS (\ | |
321 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ | |
322 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE) | |
323 | ||
324 | static struct snd_soc_dai_driver aud96p22_dai = { | |
325 | .name = "aud96p22-dai", | |
326 | .playback = { | |
327 | .stream_name = "Playback", | |
328 | .channels_min = 1, | |
329 | .channels_max = 2, | |
330 | .rates = AUD96P22_RATES, | |
331 | .formats = AUD96P22_FORMATS, | |
332 | }, | |
333 | .capture = { | |
334 | .stream_name = "Capture", | |
335 | .channels_min = 1, | |
336 | .channels_max = 2, | |
337 | .rates = AUD96P22_RATES, | |
338 | .formats = AUD96P22_FORMATS, | |
339 | }, | |
340 | .ops = &aud96p22_dai_ops, | |
341 | }; | |
342 | ||
343 | static const struct regmap_config aud96p22_regmap = { | |
344 | .reg_bits = 8, | |
345 | .val_bits = 8, | |
346 | .max_register = AUD96P22_REG_MAX, | |
347 | .cache_type = REGCACHE_RBTREE, | |
348 | }; | |
349 | ||
350 | static int aud96p22_i2c_probe(struct i2c_client *i2c, | |
351 | const struct i2c_device_id *id) | |
352 | { | |
353 | struct device *dev = &i2c->dev; | |
354 | struct aud96p22_priv *priv; | |
355 | int ret; | |
356 | ||
357 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
358 | if (priv == NULL) | |
359 | return -ENOMEM; | |
360 | ||
361 | priv->regmap = devm_regmap_init_i2c(i2c, &aud96p22_regmap); | |
362 | if (IS_ERR(priv->regmap)) { | |
363 | ret = PTR_ERR(priv->regmap); | |
364 | dev_err(dev, "failed to init i2c regmap: %d\n", ret); | |
365 | return ret; | |
366 | } | |
367 | ||
368 | i2c_set_clientdata(i2c, priv); | |
369 | ||
370 | ret = snd_soc_register_codec(dev, &aud96p22_driver, &aud96p22_dai, 1); | |
371 | if (ret) { | |
372 | dev_err(dev, "failed to register codec: %d\n", ret); | |
373 | return ret; | |
374 | } | |
375 | ||
376 | return 0; | |
377 | } | |
378 | ||
379 | static int aud96p22_i2c_remove(struct i2c_client *i2c) | |
380 | { | |
381 | snd_soc_unregister_codec(&i2c->dev); | |
382 | return 0; | |
383 | } | |
384 | ||
385 | const struct of_device_id aud96p22_dt_ids[] = { | |
386 | { .compatible = "zte,zx-aud96p22", }, | |
387 | { } | |
388 | }; | |
389 | MODULE_DEVICE_TABLE(of, aud96p22_dt_ids); | |
390 | ||
391 | static struct i2c_driver aud96p22_i2c_driver = { | |
392 | .driver = { | |
393 | .name = "zx_aud96p22", | |
394 | .of_match_table = aud96p22_dt_ids, | |
395 | }, | |
396 | .probe = aud96p22_i2c_probe, | |
397 | .remove = aud96p22_i2c_remove, | |
398 | }; | |
399 | module_i2c_driver(aud96p22_i2c_driver); | |
400 | ||
401 | MODULE_DESCRIPTION("ZTE ASoC AUD96P22 CODEC driver"); | |
402 | MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>"); | |
403 | MODULE_LICENSE("GPL v2"); |