Commit | Line | Data |
---|---|---|
7eaae41e BS |
1 | /* |
2 | * File: sound/soc/codecs/ad1836.c | |
3 | * Author: Barry Song <Barry.Song@analog.com> | |
4 | * | |
5 | * Created: Aug 04 2009 | |
6 | * Description: Driver for AD1836 sound chip | |
7 | * | |
8 | * Modified: | |
9 | * Copyright 2009 Analog Devices Inc. | |
10 | * | |
11 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | |
12 | * | |
13 | * This program is free software; you can redistribute it and/or modify | |
14 | * it under the terms of the GNU General Public License as published by | |
15 | * the Free Software Foundation; either version 2 of the License, or | |
16 | * (at your option) any later version. | |
17 | */ | |
18 | ||
19 | #include <linux/init.h> | |
5a0e3ad6 | 20 | #include <linux/slab.h> |
7eaae41e | 21 | #include <linux/module.h> |
7eaae41e BS |
22 | #include <linux/kernel.h> |
23 | #include <linux/device.h> | |
24 | #include <sound/core.h> | |
25 | #include <sound/pcm.h> | |
26 | #include <sound/pcm_params.h> | |
27 | #include <sound/initval.h> | |
28 | #include <sound/soc.h> | |
29 | #include <sound/tlv.h> | |
7eaae41e BS |
30 | #include <linux/spi/spi.h> |
31 | #include "ad1836.h" | |
32 | ||
874ce77b LPC |
33 | enum ad1836_type { |
34 | AD1835, | |
35 | AD1836, | |
36 | AD1838, | |
37 | }; | |
38 | ||
7eaae41e BS |
39 | /* codec private data */ |
40 | struct ad1836_priv { | |
f0fba2ad LG |
41 | enum snd_soc_control_type control_type; |
42 | void *control_data; | |
874ce77b | 43 | enum ad1836_type type; |
7eaae41e BS |
44 | }; |
45 | ||
7eaae41e BS |
46 | /* |
47 | * AD1836 volume/mute/de-emphasis etc. controls | |
48 | */ | |
49 | static const char *ad1836_deemp[] = {"None", "44.1kHz", "32kHz", "48kHz"}; | |
50 | ||
51 | static const struct soc_enum ad1836_deemp_enum = | |
52 | SOC_ENUM_SINGLE(AD1836_DAC_CTRL1, 8, 4, ad1836_deemp); | |
53 | ||
90bc11d1 LPC |
54 | #define AD1836_DAC_VOLUME(x) \ |
55 | SOC_DOUBLE_R("DAC" #x " Playback Volume", AD1836_DAC_L_VOL(x), \ | |
56 | AD1836_DAC_R_VOL(x), 0, 0x3FF, 0) | |
57 | ||
58 | #define AD1836_DAC_SWITCH(x) \ | |
59 | SOC_DOUBLE("DAC" #x " Playback Switch", AD1836_DAC_CTRL2, \ | |
60 | AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1) | |
61 | ||
62 | #define AD1836_ADC_SWITCH(x) \ | |
63 | SOC_DOUBLE("ADC" #x " Capture Switch", AD1836_ADC_CTRL2, \ | |
64 | AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1) | |
65 | ||
874ce77b | 66 | static const struct snd_kcontrol_new ad183x_dac_controls[] = { |
90bc11d1 | 67 | AD1836_DAC_VOLUME(1), |
874ce77b | 68 | AD1836_DAC_SWITCH(1), |
90bc11d1 | 69 | AD1836_DAC_VOLUME(2), |
874ce77b | 70 | AD1836_DAC_SWITCH(2), |
90bc11d1 | 71 | AD1836_DAC_VOLUME(3), |
874ce77b LPC |
72 | AD1836_DAC_SWITCH(3), |
73 | AD1836_DAC_VOLUME(4), | |
74 | AD1836_DAC_SWITCH(4), | |
75 | }; | |
76 | ||
77 | static const struct snd_soc_dapm_widget ad183x_dac_dapm_widgets[] = { | |
78 | SND_SOC_DAPM_OUTPUT("DAC1OUT"), | |
79 | SND_SOC_DAPM_OUTPUT("DAC2OUT"), | |
80 | SND_SOC_DAPM_OUTPUT("DAC3OUT"), | |
81 | SND_SOC_DAPM_OUTPUT("DAC4OUT"), | |
82 | }; | |
83 | ||
84 | static const struct snd_soc_dapm_route ad183x_dac_routes[] = { | |
85 | { "DAC1OUT", NULL, "DAC" }, | |
86 | { "DAC2OUT", NULL, "DAC" }, | |
87 | { "DAC3OUT", NULL, "DAC" }, | |
88 | { "DAC4OUT", NULL, "DAC" }, | |
89 | }; | |
7eaae41e | 90 | |
874ce77b | 91 | static const struct snd_kcontrol_new ad183x_adc_controls[] = { |
90bc11d1 LPC |
92 | AD1836_ADC_SWITCH(1), |
93 | AD1836_ADC_SWITCH(2), | |
874ce77b LPC |
94 | AD1836_ADC_SWITCH(3), |
95 | }; | |
7eaae41e | 96 | |
874ce77b LPC |
97 | static const struct snd_soc_dapm_widget ad183x_adc_dapm_widgets[] = { |
98 | SND_SOC_DAPM_INPUT("ADC1IN"), | |
99 | SND_SOC_DAPM_INPUT("ADC2IN"), | |
100 | }; | |
101 | ||
102 | static const struct snd_soc_dapm_route ad183x_adc_routes[] = { | |
103 | { "ADC", NULL, "ADC1IN" }, | |
104 | { "ADC", NULL, "ADC2IN" }, | |
105 | }; | |
7eaae41e | 106 | |
874ce77b | 107 | static const struct snd_kcontrol_new ad183x_controls[] = { |
7eaae41e BS |
108 | /* ADC high-pass filter */ |
109 | SOC_SINGLE("ADC High Pass Filter Switch", AD1836_ADC_CTRL1, | |
110 | AD1836_ADC_HIGHPASS_FILTER, 1, 0), | |
111 | ||
112 | /* DAC de-emphasis */ | |
113 | SOC_ENUM("Playback Deemphasis", ad1836_deemp_enum), | |
114 | }; | |
115 | ||
874ce77b | 116 | static const struct snd_soc_dapm_widget ad183x_dapm_widgets[] = { |
7eaae41e BS |
117 | SND_SOC_DAPM_DAC("DAC", "Playback", AD1836_DAC_CTRL1, |
118 | AD1836_DAC_POWERDOWN, 1), | |
119 | SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), | |
120 | SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1836_ADC_CTRL1, | |
121 | AD1836_ADC_POWERDOWN, 1, NULL, 0), | |
7eaae41e BS |
122 | }; |
123 | ||
874ce77b | 124 | static const struct snd_soc_dapm_route ad183x_dapm_routes[] = { |
7eaae41e BS |
125 | { "DAC", NULL, "ADC_PWR" }, |
126 | { "ADC", NULL, "ADC_PWR" }, | |
7eaae41e BS |
127 | }; |
128 | ||
129 | /* | |
130 | * DAI ops entries | |
131 | */ | |
132 | ||
133 | static int ad1836_set_dai_fmt(struct snd_soc_dai *codec_dai, | |
134 | unsigned int fmt) | |
135 | { | |
136 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | |
137 | /* at present, we support adc aux mode to interface with | |
138 | * blackfin sport tdm mode | |
139 | */ | |
140 | case SND_SOC_DAIFMT_DSP_A: | |
141 | break; | |
142 | default: | |
143 | return -EINVAL; | |
144 | } | |
145 | ||
146 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | |
147 | case SND_SOC_DAIFMT_IB_IF: | |
148 | break; | |
149 | default: | |
150 | return -EINVAL; | |
151 | } | |
152 | ||
153 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | |
154 | /* ALCLK,ABCLK are both output, AD1836 can only be master */ | |
155 | case SND_SOC_DAIFMT_CBM_CFM: | |
156 | break; | |
157 | default: | |
158 | return -EINVAL; | |
159 | } | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
164 | static int ad1836_hw_params(struct snd_pcm_substream *substream, | |
165 | struct snd_pcm_hw_params *params, | |
166 | struct snd_soc_dai *dai) | |
167 | { | |
168 | int word_len = 0; | |
169 | ||
170 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
f0fba2ad | 171 | struct snd_soc_codec *codec = rtd->codec; |
7eaae41e BS |
172 | |
173 | /* bit size */ | |
174 | switch (params_format(params)) { | |
175 | case SNDRV_PCM_FORMAT_S16_LE: | |
176 | word_len = 3; | |
177 | break; | |
178 | case SNDRV_PCM_FORMAT_S20_3LE: | |
179 | word_len = 1; | |
180 | break; | |
181 | case SNDRV_PCM_FORMAT_S24_LE: | |
182 | case SNDRV_PCM_FORMAT_S32_LE: | |
183 | word_len = 0; | |
184 | break; | |
185 | } | |
186 | ||
187 | snd_soc_update_bits(codec, AD1836_DAC_CTRL1, | |
188 | AD1836_DAC_WORD_LEN_MASK, word_len); | |
189 | ||
190 | snd_soc_update_bits(codec, AD1836_ADC_CTRL2, | |
191 | AD1836_ADC_WORD_LEN_MASK, word_len); | |
192 | ||
193 | return 0; | |
194 | } | |
195 | ||
84549d23 | 196 | #ifdef CONFIG_PM |
f0fba2ad | 197 | static int ad1836_soc_suspend(struct snd_soc_codec *codec, |
84549d23 BS |
198 | pm_message_t state) |
199 | { | |
84549d23 | 200 | /* reset clock control mode */ |
2cf03428 LPC |
201 | return snd_soc_update_bits(codec, AD1836_ADC_CTRL2, |
202 | AD1836_ADC_SERFMT_MASK, 0); | |
84549d23 BS |
203 | } |
204 | ||
f0fba2ad | 205 | static int ad1836_soc_resume(struct snd_soc_codec *codec) |
84549d23 | 206 | { |
84549d23 | 207 | /* restore clock control mode */ |
2cf03428 LPC |
208 | return snd_soc_update_bits(codec, AD1836_ADC_CTRL2, |
209 | AD1836_ADC_SERFMT_MASK, AD1836_ADC_AUX); | |
84549d23 BS |
210 | } |
211 | #else | |
212 | #define ad1836_soc_suspend NULL | |
213 | #define ad1836_soc_resume NULL | |
214 | #endif | |
215 | ||
7eaae41e BS |
216 | static struct snd_soc_dai_ops ad1836_dai_ops = { |
217 | .hw_params = ad1836_hw_params, | |
218 | .set_fmt = ad1836_set_dai_fmt, | |
219 | }; | |
220 | ||
874ce77b LPC |
221 | #define AD183X_DAI(_name, num_dacs, num_adcs) \ |
222 | { \ | |
223 | .name = _name "-hifi", \ | |
224 | .playback = { \ | |
225 | .stream_name = "Playback", \ | |
226 | .channels_min = 2, \ | |
227 | .channels_max = (num_dacs) * 2, \ | |
228 | .rates = SNDRV_PCM_RATE_48000, \ | |
229 | .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \ | |
230 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \ | |
231 | }, \ | |
232 | .capture = { \ | |
233 | .stream_name = "Capture", \ | |
234 | .channels_min = 2, \ | |
235 | .channels_max = (num_adcs) * 2, \ | |
236 | .rates = SNDRV_PCM_RATE_48000, \ | |
237 | .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \ | |
238 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \ | |
239 | }, \ | |
240 | .ops = &ad1836_dai_ops, \ | |
241 | } | |
242 | ||
243 | static struct snd_soc_dai_driver ad183x_dais[] = { | |
244 | [AD1835] = AD183X_DAI("ad1835", 4, 1), | |
245 | [AD1836] = AD183X_DAI("ad1836", 3, 2), | |
246 | [AD1838] = AD183X_DAI("ad1838", 3, 1), | |
7eaae41e | 247 | }; |
7eaae41e | 248 | |
f0fba2ad | 249 | static int ad1836_probe(struct snd_soc_codec *codec) |
7eaae41e | 250 | { |
f0fba2ad | 251 | struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec); |
ce6120cc | 252 | struct snd_soc_dapm_context *dapm = &codec->dapm; |
874ce77b | 253 | int num_dacs, num_adcs; |
f0fba2ad | 254 | int ret = 0; |
874ce77b LPC |
255 | int i; |
256 | ||
257 | num_dacs = ad183x_dais[ad1836->type].playback.channels_max / 2; | |
258 | num_adcs = ad183x_dais[ad1836->type].capture.channels_max / 2; | |
7eaae41e | 259 | |
f0fba2ad | 260 | codec->control_data = ad1836->control_data; |
63b62ab0 BS |
261 | ret = snd_soc_codec_set_cache_io(codec, 4, 12, SND_SOC_SPI); |
262 | if (ret < 0) { | |
263 | dev_err(codec->dev, "failed to set cache I/O: %d\n", | |
264 | ret); | |
63b62ab0 BS |
265 | return ret; |
266 | } | |
267 | ||
7eaae41e BS |
268 | /* default setting for ad1836 */ |
269 | /* de-emphasis: 48kHz, power-on dac */ | |
63b62ab0 | 270 | snd_soc_write(codec, AD1836_DAC_CTRL1, 0x300); |
7eaae41e | 271 | /* unmute dac channels */ |
63b62ab0 | 272 | snd_soc_write(codec, AD1836_DAC_CTRL2, 0x0); |
7eaae41e | 273 | /* high-pass filter enable, power-on adc */ |
63b62ab0 | 274 | snd_soc_write(codec, AD1836_ADC_CTRL1, 0x100); |
7eaae41e | 275 | /* unmute adc channles, adc aux mode */ |
63b62ab0 | 276 | snd_soc_write(codec, AD1836_ADC_CTRL2, 0x180); |
7eaae41e | 277 | /* volume */ |
874ce77b LPC |
278 | for (i = 1; i <= num_dacs; ++i) { |
279 | snd_soc_write(codec, AD1836_DAC_L_VOL(i), 0x3FF); | |
280 | snd_soc_write(codec, AD1836_DAC_R_VOL(i), 0x3FF); | |
281 | } | |
282 | ||
283 | if (ad1836->type == AD1836) { | |
284 | /* left/right diff:PGA/MUX */ | |
285 | snd_soc_write(codec, AD1836_ADC_CTRL3, 0x3A); | |
286 | } else { | |
287 | snd_soc_write(codec, AD1836_ADC_CTRL3, 0x00); | |
288 | } | |
289 | ||
290 | ret = snd_soc_add_controls(codec, ad183x_dac_controls, num_dacs * 2); | |
291 | if (ret) | |
292 | return ret; | |
293 | ||
294 | ret = snd_soc_add_controls(codec, ad183x_adc_controls, num_adcs); | |
295 | if (ret) | |
296 | return ret; | |
297 | ||
298 | ret = snd_soc_dapm_new_controls(dapm, ad183x_dac_dapm_widgets, num_dacs); | |
299 | if (ret) | |
300 | return ret; | |
301 | ||
302 | ret = snd_soc_dapm_new_controls(dapm, ad183x_adc_dapm_widgets, num_adcs); | |
303 | if (ret) | |
304 | return ret; | |
305 | ||
306 | ret = snd_soc_dapm_add_routes(dapm, ad183x_dac_routes, num_dacs); | |
307 | if (ret) | |
308 | return ret; | |
309 | ||
310 | ret = snd_soc_dapm_add_routes(dapm, ad183x_adc_routes, num_adcs); | |
311 | if (ret) | |
312 | return ret; | |
7eaae41e | 313 | |
7eaae41e BS |
314 | return ret; |
315 | } | |
316 | ||
317 | /* power down chip */ | |
f0fba2ad | 318 | static int ad1836_remove(struct snd_soc_codec *codec) |
7eaae41e | 319 | { |
f0fba2ad | 320 | /* reset clock control mode */ |
2cf03428 LPC |
321 | return snd_soc_update_bits(codec, AD1836_ADC_CTRL2, |
322 | AD1836_ADC_SERFMT_MASK, 0); | |
7eaae41e BS |
323 | } |
324 | ||
f0fba2ad | 325 | static struct snd_soc_codec_driver soc_codec_dev_ad1836 = { |
7eaae41e BS |
326 | .probe = ad1836_probe, |
327 | .remove = ad1836_remove, | |
84549d23 BS |
328 | .suspend = ad1836_soc_suspend, |
329 | .resume = ad1836_soc_resume, | |
f0fba2ad LG |
330 | .reg_cache_size = AD1836_NUM_REGS, |
331 | .reg_word_size = sizeof(u16), | |
874ce77b LPC |
332 | |
333 | .controls = ad183x_controls, | |
334 | .num_controls = ARRAY_SIZE(ad183x_controls), | |
335 | .dapm_widgets = ad183x_dapm_widgets, | |
336 | .num_dapm_widgets = ARRAY_SIZE(ad183x_dapm_widgets), | |
337 | .dapm_routes = ad183x_dapm_routes, | |
338 | .num_dapm_routes = ARRAY_SIZE(ad183x_dapm_routes), | |
f0fba2ad LG |
339 | }; |
340 | ||
341 | static int __devinit ad1836_spi_probe(struct spi_device *spi) | |
342 | { | |
343 | struct ad1836_priv *ad1836; | |
344 | int ret; | |
345 | ||
346 | ad1836 = kzalloc(sizeof(struct ad1836_priv), GFP_KERNEL); | |
347 | if (ad1836 == NULL) | |
348 | return -ENOMEM; | |
349 | ||
874ce77b LPC |
350 | ad1836->type = spi_get_device_id(spi)->driver_data; |
351 | ||
f0fba2ad LG |
352 | spi_set_drvdata(spi, ad1836); |
353 | ad1836->control_data = spi; | |
354 | ad1836->control_type = SND_SOC_SPI; | |
355 | ||
356 | ret = snd_soc_register_codec(&spi->dev, | |
874ce77b | 357 | &soc_codec_dev_ad1836, &ad183x_dais[ad1836->type], 1); |
f0fba2ad LG |
358 | if (ret < 0) |
359 | kfree(ad1836); | |
360 | return ret; | |
361 | } | |
362 | ||
363 | static int __devexit ad1836_spi_remove(struct spi_device *spi) | |
364 | { | |
365 | snd_soc_unregister_codec(&spi->dev); | |
366 | kfree(spi_get_drvdata(spi)); | |
367 | return 0; | |
368 | } | |
874ce77b LPC |
369 | static const struct spi_device_id ad1836_ids[] = { |
370 | { "ad1835", AD1835 }, | |
371 | { "ad1836", AD1836 }, | |
372 | { "ad1837", AD1835 }, | |
373 | { "ad1838", AD1838 }, | |
374 | { "ad1839", AD1838 }, | |
375 | { }, | |
376 | }; | |
377 | MODULE_DEVICE_TABLE(spi, ad1836_ids); | |
f0fba2ad LG |
378 | |
379 | static struct spi_driver ad1836_spi_driver = { | |
380 | .driver = { | |
381 | .name = "ad1836-codec", | |
382 | .owner = THIS_MODULE, | |
383 | }, | |
384 | .probe = ad1836_spi_probe, | |
385 | .remove = __devexit_p(ad1836_spi_remove), | |
874ce77b | 386 | .id_table = ad1836_ids, |
7eaae41e | 387 | }; |
7eaae41e BS |
388 | |
389 | static int __init ad1836_init(void) | |
390 | { | |
391 | int ret; | |
392 | ||
393 | ret = spi_register_driver(&ad1836_spi_driver); | |
394 | if (ret != 0) { | |
395 | printk(KERN_ERR "Failed to register ad1836 SPI driver: %d\n", | |
396 | ret); | |
397 | } | |
398 | ||
399 | return ret; | |
400 | } | |
401 | module_init(ad1836_init); | |
402 | ||
403 | static void __exit ad1836_exit(void) | |
404 | { | |
405 | spi_unregister_driver(&ad1836_spi_driver); | |
406 | } | |
407 | module_exit(ad1836_exit); | |
408 | ||
409 | MODULE_DESCRIPTION("ASoC ad1836 driver"); | |
410 | MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); | |
411 | MODULE_LICENSE("GPL"); |