diff options
Diffstat (limited to 'sound/soc/codecs/wm8737.c')
-rw-r--r-- | sound/soc/codecs/wm8737.c | 754 |
1 files changed, 754 insertions, 0 deletions
diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c new file mode 100644 index 000000000000..30c67d06a904 --- /dev/null +++ b/sound/soc/codecs/wm8737.c | |||
@@ -0,0 +1,754 @@ | |||
1 | /* | ||
2 | * wm8737.c -- WM8737 ALSA SoC Audio driver | ||
3 | * | ||
4 | * Copyright 2010 Wolfson Microelectronics plc | ||
5 | * | ||
6 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/moduleparam.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/pm.h> | ||
18 | #include <linux/i2c.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/regulator/consumer.h> | ||
21 | #include <linux/spi/spi.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <sound/core.h> | ||
24 | #include <sound/pcm.h> | ||
25 | #include <sound/pcm_params.h> | ||
26 | #include <sound/soc.h> | ||
27 | #include <sound/soc-dapm.h> | ||
28 | #include <sound/initval.h> | ||
29 | #include <sound/tlv.h> | ||
30 | |||
31 | #include "wm8737.h" | ||
32 | |||
33 | #define WM8737_NUM_SUPPLIES 4 | ||
34 | static const char *wm8737_supply_names[WM8737_NUM_SUPPLIES] = { | ||
35 | "DCVDD", | ||
36 | "DBVDD", | ||
37 | "AVDD", | ||
38 | "MVDD", | ||
39 | }; | ||
40 | |||
41 | /* codec private data */ | ||
42 | struct wm8737_priv { | ||
43 | enum snd_soc_control_type control_type; | ||
44 | struct regulator_bulk_data supplies[WM8737_NUM_SUPPLIES]; | ||
45 | unsigned int mclk; | ||
46 | }; | ||
47 | |||
48 | static const u16 wm8737_reg[WM8737_REGISTER_COUNT] = { | ||
49 | 0x00C3, /* R0 - Left PGA volume */ | ||
50 | 0x00C3, /* R1 - Right PGA volume */ | ||
51 | 0x0007, /* R2 - AUDIO path L */ | ||
52 | 0x0007, /* R3 - AUDIO path R */ | ||
53 | 0x0000, /* R4 - 3D Enhance */ | ||
54 | 0x0000, /* R5 - ADC Control */ | ||
55 | 0x0000, /* R6 - Power Management */ | ||
56 | 0x000A, /* R7 - Audio Format */ | ||
57 | 0x0000, /* R8 - Clocking */ | ||
58 | 0x000F, /* R9 - MIC Preamp Control */ | ||
59 | 0x0003, /* R10 - Misc Bias Control */ | ||
60 | 0x0000, /* R11 - Noise Gate */ | ||
61 | 0x007C, /* R12 - ALC1 */ | ||
62 | 0x0000, /* R13 - ALC2 */ | ||
63 | 0x0032, /* R14 - ALC3 */ | ||
64 | }; | ||
65 | |||
66 | static int wm8737_reset(struct snd_soc_codec *codec) | ||
67 | { | ||
68 | return snd_soc_write(codec, WM8737_RESET, 0); | ||
69 | } | ||
70 | |||
71 | static const unsigned int micboost_tlv[] = { | ||
72 | TLV_DB_RANGE_HEAD(4), | ||
73 | 0, 0, TLV_DB_SCALE_ITEM(1300, 0, 0), | ||
74 | 1, 1, TLV_DB_SCALE_ITEM(1800, 0, 0), | ||
75 | 2, 2, TLV_DB_SCALE_ITEM(2800, 0, 0), | ||
76 | 3, 3, TLV_DB_SCALE_ITEM(3300, 0, 0), | ||
77 | }; | ||
78 | static const DECLARE_TLV_DB_SCALE(pga_tlv, -9750, 50, 1); | ||
79 | static const DECLARE_TLV_DB_SCALE(adc_tlv, -600, 600, 0); | ||
80 | static const DECLARE_TLV_DB_SCALE(ng_tlv, -7800, 600, 0); | ||
81 | static const DECLARE_TLV_DB_SCALE(alc_max_tlv, -1200, 600, 0); | ||
82 | static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -1800, 100, 0); | ||
83 | |||
84 | static const char *micbias_enum_text[] = { | ||
85 | "25%", | ||
86 | "50%", | ||
87 | "75%", | ||
88 | "100%", | ||
89 | }; | ||
90 | |||
91 | static const struct soc_enum micbias_enum = | ||
92 | SOC_ENUM_SINGLE(WM8737_MIC_PREAMP_CONTROL, 0, 4, micbias_enum_text); | ||
93 | |||
94 | static const char *low_cutoff_text[] = { | ||
95 | "Low", "High" | ||
96 | }; | ||
97 | |||
98 | static const struct soc_enum low_3d = | ||
99 | SOC_ENUM_SINGLE(WM8737_3D_ENHANCE, 6, 2, low_cutoff_text); | ||
100 | |||
101 | static const char *high_cutoff_text[] = { | ||
102 | "High", "Low" | ||
103 | }; | ||
104 | |||
105 | static const struct soc_enum high_3d = | ||
106 | SOC_ENUM_SINGLE(WM8737_3D_ENHANCE, 5, 2, high_cutoff_text); | ||
107 | |||
108 | static const char *alc_fn_text[] = { | ||
109 | "Disabled", "Right", "Left", "Stereo" | ||
110 | }; | ||
111 | |||
112 | static const struct soc_enum alc_fn = | ||
113 | SOC_ENUM_SINGLE(WM8737_ALC1, 7, 4, alc_fn_text); | ||
114 | |||
115 | static const char *alc_hold_text[] = { | ||
116 | "0", "2.67ms", "5.33ms", "10.66ms", "21.32ms", "42.64ms", "85.28ms", | ||
117 | "170.56ms", "341.12ms", "682.24ms", "1.364s", "2.728s", "5.458s", | ||
118 | "10.916s", "21.832s", "43.691s" | ||
119 | }; | ||
120 | |||
121 | static const struct soc_enum alc_hold = | ||
122 | SOC_ENUM_SINGLE(WM8737_ALC2, 0, 16, alc_hold_text); | ||
123 | |||
124 | static const char *alc_atk_text[] = { | ||
125 | "8.4ms", "16.8ms", "33.6ms", "67.2ms", "134.4ms", "268.8ms", "537.6ms", | ||
126 | "1.075s", "2.15s", "4.3s", "8.6s" | ||
127 | }; | ||
128 | |||
129 | static const struct soc_enum alc_atk = | ||
130 | SOC_ENUM_SINGLE(WM8737_ALC3, 0, 11, alc_atk_text); | ||
131 | |||
132 | static const char *alc_dcy_text[] = { | ||
133 | "33.6ms", "67.2ms", "134.4ms", "268.8ms", "537.6ms", "1.075s", "2.15s", | ||
134 | "4.3s", "8.6s", "17.2s", "34.41s" | ||
135 | }; | ||
136 | |||
137 | static const struct soc_enum alc_dcy = | ||
138 | SOC_ENUM_SINGLE(WM8737_ALC3, 4, 11, alc_dcy_text); | ||
139 | |||
140 | static const struct snd_kcontrol_new wm8737_snd_controls[] = { | ||
141 | SOC_DOUBLE_R_TLV("Mic Boost Volume", WM8737_AUDIO_PATH_L, WM8737_AUDIO_PATH_R, | ||
142 | 6, 3, 0, micboost_tlv), | ||
143 | SOC_DOUBLE_R("Mic Boost Switch", WM8737_AUDIO_PATH_L, WM8737_AUDIO_PATH_R, | ||
144 | 4, 1, 0), | ||
145 | SOC_DOUBLE("Mic ZC Switch", WM8737_AUDIO_PATH_L, WM8737_AUDIO_PATH_R, | ||
146 | 3, 1, 0), | ||
147 | |||
148 | SOC_DOUBLE_R_TLV("Capture Volume", WM8737_LEFT_PGA_VOLUME, | ||
149 | WM8737_RIGHT_PGA_VOLUME, 0, 255, 0, pga_tlv), | ||
150 | SOC_DOUBLE("Capture ZC Switch", WM8737_AUDIO_PATH_L, WM8737_AUDIO_PATH_R, | ||
151 | 2, 1, 0), | ||
152 | |||
153 | SOC_DOUBLE("INPUT1 DC Bias Switch", WM8737_MISC_BIAS_CONTROL, 0, 1, 1, 0), | ||
154 | |||
155 | SOC_ENUM("Mic PGA Bias", micbias_enum), | ||
156 | SOC_SINGLE("ADC Low Power Switch", WM8737_ADC_CONTROL, 2, 1, 0), | ||
157 | SOC_SINGLE("High Pass Filter Switch", WM8737_ADC_CONTROL, 0, 1, 1), | ||
158 | SOC_DOUBLE("Polarity Invert Switch", WM8737_ADC_CONTROL, 5, 6, 1, 0), | ||
159 | |||
160 | SOC_SINGLE("3D Switch", WM8737_3D_ENHANCE, 0, 1, 0), | ||
161 | SOC_SINGLE("3D Depth", WM8737_3D_ENHANCE, 1, 15, 0), | ||
162 | SOC_ENUM("3D Low Cut-off", low_3d), | ||
163 | SOC_ENUM("3D High Cut-off", low_3d), | ||
164 | SOC_SINGLE_TLV("3D ADC Volume", WM8737_3D_ENHANCE, 7, 1, 1, adc_tlv), | ||
165 | |||
166 | SOC_SINGLE("Noise Gate Switch", WM8737_NOISE_GATE, 0, 1, 0), | ||
167 | SOC_SINGLE_TLV("Noise Gate Threshold Volume", WM8737_NOISE_GATE, 2, 7, 0, | ||
168 | ng_tlv), | ||
169 | |||
170 | SOC_ENUM("ALC", alc_fn), | ||
171 | SOC_SINGLE_TLV("ALC Max Gain Volume", WM8737_ALC1, 4, 7, 0, alc_max_tlv), | ||
172 | SOC_SINGLE_TLV("ALC Target Volume", WM8737_ALC1, 0, 15, 0, alc_target_tlv), | ||
173 | SOC_ENUM("ALC Hold Time", alc_hold), | ||
174 | SOC_SINGLE("ALC ZC Switch", WM8737_ALC2, 4, 1, 0), | ||
175 | SOC_ENUM("ALC Attack Time", alc_atk), | ||
176 | SOC_ENUM("ALC Decay Time", alc_dcy), | ||
177 | }; | ||
178 | |||
179 | static const char *linsel_text[] = { | ||
180 | "LINPUT1", "LINPUT2", "LINPUT3", "LINPUT1 DC", | ||
181 | }; | ||
182 | |||
183 | static const struct soc_enum linsel_enum = | ||
184 | SOC_ENUM_SINGLE(WM8737_AUDIO_PATH_L, 7, 4, linsel_text); | ||
185 | |||
186 | static const struct snd_kcontrol_new linsel_mux = | ||
187 | SOC_DAPM_ENUM("LINSEL", linsel_enum); | ||
188 | |||
189 | |||
190 | static const char *rinsel_text[] = { | ||
191 | "RINPUT1", "RINPUT2", "RINPUT3", "RINPUT1 DC", | ||
192 | }; | ||
193 | |||
194 | static const struct soc_enum rinsel_enum = | ||
195 | SOC_ENUM_SINGLE(WM8737_AUDIO_PATH_R, 7, 4, rinsel_text); | ||
196 | |||
197 | static const struct snd_kcontrol_new rinsel_mux = | ||
198 | SOC_DAPM_ENUM("RINSEL", rinsel_enum); | ||
199 | |||
200 | static const char *bypass_text[] = { | ||
201 | "Direct", "Preamp" | ||
202 | }; | ||
203 | |||
204 | static const struct soc_enum lbypass_enum = | ||
205 | SOC_ENUM_SINGLE(WM8737_MIC_PREAMP_CONTROL, 2, 2, bypass_text); | ||
206 | |||
207 | static const struct snd_kcontrol_new lbypass_mux = | ||
208 | SOC_DAPM_ENUM("Left Bypass", lbypass_enum); | ||
209 | |||
210 | |||
211 | static const struct soc_enum rbypass_enum = | ||
212 | SOC_ENUM_SINGLE(WM8737_MIC_PREAMP_CONTROL, 3, 2, bypass_text); | ||
213 | |||
214 | static const struct snd_kcontrol_new rbypass_mux = | ||
215 | SOC_DAPM_ENUM("Left Bypass", rbypass_enum); | ||
216 | |||
217 | static const struct snd_soc_dapm_widget wm8737_dapm_widgets[] = { | ||
218 | SND_SOC_DAPM_INPUT("LINPUT1"), | ||
219 | SND_SOC_DAPM_INPUT("LINPUT2"), | ||
220 | SND_SOC_DAPM_INPUT("LINPUT3"), | ||
221 | SND_SOC_DAPM_INPUT("RINPUT1"), | ||
222 | SND_SOC_DAPM_INPUT("RINPUT2"), | ||
223 | SND_SOC_DAPM_INPUT("RINPUT3"), | ||
224 | SND_SOC_DAPM_INPUT("LACIN"), | ||
225 | SND_SOC_DAPM_INPUT("RACIN"), | ||
226 | |||
227 | SND_SOC_DAPM_MUX("LINSEL", SND_SOC_NOPM, 0, 0, &linsel_mux), | ||
228 | SND_SOC_DAPM_MUX("RINSEL", SND_SOC_NOPM, 0, 0, &rinsel_mux), | ||
229 | |||
230 | SND_SOC_DAPM_MUX("Left Preamp Mux", SND_SOC_NOPM, 0, 0, &lbypass_mux), | ||
231 | SND_SOC_DAPM_MUX("Right Preamp Mux", SND_SOC_NOPM, 0, 0, &rbypass_mux), | ||
232 | |||
233 | SND_SOC_DAPM_PGA("PGAL", WM8737_POWER_MANAGEMENT, 5, 0, NULL, 0), | ||
234 | SND_SOC_DAPM_PGA("PGAR", WM8737_POWER_MANAGEMENT, 4, 0, NULL, 0), | ||
235 | |||
236 | SND_SOC_DAPM_DAC("ADCL", NULL, WM8737_POWER_MANAGEMENT, 3, 0), | ||
237 | SND_SOC_DAPM_DAC("ADCR", NULL, WM8737_POWER_MANAGEMENT, 2, 0), | ||
238 | |||
239 | SND_SOC_DAPM_AIF_OUT("AIF", "Capture", 0, WM8737_POWER_MANAGEMENT, 6, 0), | ||
240 | }; | ||
241 | |||
242 | static const struct snd_soc_dapm_route intercon[] = { | ||
243 | { "LINSEL", "LINPUT1", "LINPUT1" }, | ||
244 | { "LINSEL", "LINPUT2", "LINPUT2" }, | ||
245 | { "LINSEL", "LINPUT3", "LINPUT3" }, | ||
246 | { "LINSEL", "LINPUT1 DC", "LINPUT1" }, | ||
247 | |||
248 | { "RINSEL", "RINPUT1", "RINPUT1" }, | ||
249 | { "RINSEL", "RINPUT2", "RINPUT2" }, | ||
250 | { "RINSEL", "RINPUT3", "RINPUT3" }, | ||
251 | { "RINSEL", "RINPUT1 DC", "RINPUT1" }, | ||
252 | |||
253 | { "Left Preamp Mux", "Preamp", "LINSEL" }, | ||
254 | { "Left Preamp Mux", "Direct", "LACIN" }, | ||
255 | |||
256 | { "Right Preamp Mux", "Preamp", "RINSEL" }, | ||
257 | { "Right Preamp Mux", "Direct", "RACIN" }, | ||
258 | |||
259 | { "PGAL", NULL, "Left Preamp Mux" }, | ||
260 | { "PGAR", NULL, "Right Preamp Mux" }, | ||
261 | |||
262 | { "ADCL", NULL, "PGAL" }, | ||
263 | { "ADCR", NULL, "PGAR" }, | ||
264 | |||
265 | { "AIF", NULL, "ADCL" }, | ||
266 | { "AIF", NULL, "ADCR" }, | ||
267 | }; | ||
268 | |||
269 | static int wm8737_add_widgets(struct snd_soc_codec *codec) | ||
270 | { | ||
271 | struct snd_soc_dapm_context *dapm = &codec->dapm; | ||
272 | |||
273 | snd_soc_dapm_new_controls(dapm, wm8737_dapm_widgets, | ||
274 | ARRAY_SIZE(wm8737_dapm_widgets)); | ||
275 | snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); | ||
276 | |||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | /* codec mclk clock divider coefficients */ | ||
281 | static const struct { | ||
282 | u32 mclk; | ||
283 | u32 rate; | ||
284 | u8 usb; | ||
285 | u8 sr; | ||
286 | } coeff_div[] = { | ||
287 | { 12288000, 8000, 0, 0x4 }, | ||
288 | { 12288000, 12000, 0, 0x8 }, | ||
289 | { 12288000, 16000, 0, 0xa }, | ||
290 | { 12288000, 24000, 0, 0x1c }, | ||
291 | { 12288000, 32000, 0, 0xc }, | ||
292 | { 12288000, 48000, 0, 0 }, | ||
293 | { 12288000, 96000, 0, 0xe }, | ||
294 | |||
295 | { 11289600, 8000, 0, 0x14 }, | ||
296 | { 11289600, 11025, 0, 0x18 }, | ||
297 | { 11289600, 22050, 0, 0x1a }, | ||
298 | { 11289600, 44100, 0, 0x10 }, | ||
299 | { 11289600, 88200, 0, 0x1e }, | ||
300 | |||
301 | { 18432000, 8000, 0, 0x5 }, | ||
302 | { 18432000, 12000, 0, 0x9 }, | ||
303 | { 18432000, 16000, 0, 0xb }, | ||
304 | { 18432000, 24000, 0, 0x1b }, | ||
305 | { 18432000, 32000, 0, 0xd }, | ||
306 | { 18432000, 48000, 0, 0x1 }, | ||
307 | { 18432000, 96000, 0, 0x1f }, | ||
308 | |||
309 | { 16934400, 8000, 0, 0x15 }, | ||
310 | { 16934400, 11025, 0, 0x19 }, | ||
311 | { 16934400, 22050, 0, 0x1b }, | ||
312 | { 16934400, 44100, 0, 0x11 }, | ||
313 | { 16934400, 88200, 0, 0x1f }, | ||
314 | |||
315 | { 12000000, 8000, 1, 0x4 }, | ||
316 | { 12000000, 11025, 1, 0x19 }, | ||
317 | { 12000000, 12000, 1, 0x8 }, | ||
318 | { 12000000, 16000, 1, 0xa }, | ||
319 | { 12000000, 22050, 1, 0x1b }, | ||
320 | { 12000000, 24000, 1, 0x1c }, | ||
321 | { 12000000, 32000, 1, 0xc }, | ||
322 | { 12000000, 44100, 1, 0x11 }, | ||
323 | { 12000000, 48000, 1, 0x0 }, | ||
324 | { 12000000, 88200, 1, 0x1f }, | ||
325 | { 12000000, 96000, 1, 0xe }, | ||
326 | }; | ||
327 | |||
328 | static int wm8737_hw_params(struct snd_pcm_substream *substream, | ||
329 | struct snd_pcm_hw_params *params, | ||
330 | struct snd_soc_dai *dai) | ||
331 | { | ||
332 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
333 | struct snd_soc_codec *codec = rtd->codec; | ||
334 | struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec); | ||
335 | int i; | ||
336 | u16 clocking = 0; | ||
337 | u16 af = 0; | ||
338 | |||
339 | for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { | ||
340 | if (coeff_div[i].rate != params_rate(params)) | ||
341 | continue; | ||
342 | |||
343 | if (coeff_div[i].mclk == wm8737->mclk) | ||
344 | break; | ||
345 | |||
346 | if (coeff_div[i].mclk == wm8737->mclk * 2) { | ||
347 | clocking |= WM8737_CLKDIV2; | ||
348 | break; | ||
349 | } | ||
350 | } | ||
351 | |||
352 | if (i == ARRAY_SIZE(coeff_div)) { | ||
353 | dev_err(codec->dev, "%dHz MCLK can't support %dHz\n", | ||
354 | wm8737->mclk, params_rate(params)); | ||
355 | return -EINVAL; | ||
356 | } | ||
357 | |||
358 | clocking |= coeff_div[i].usb | (coeff_div[i].sr << WM8737_SR_SHIFT); | ||
359 | |||
360 | switch (params_format(params)) { | ||
361 | case SNDRV_PCM_FORMAT_S16_LE: | ||
362 | break; | ||
363 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
364 | af |= 0x8; | ||
365 | break; | ||
366 | case SNDRV_PCM_FORMAT_S24_LE: | ||
367 | af |= 0x10; | ||
368 | break; | ||
369 | case SNDRV_PCM_FORMAT_S32_LE: | ||
370 | af |= 0x18; | ||
371 | break; | ||
372 | default: | ||
373 | return -EINVAL; | ||
374 | } | ||
375 | |||
376 | snd_soc_update_bits(codec, WM8737_AUDIO_FORMAT, WM8737_WL_MASK, af); | ||
377 | snd_soc_update_bits(codec, WM8737_CLOCKING, | ||
378 | WM8737_USB_MODE | WM8737_CLKDIV2 | WM8737_SR_MASK, | ||
379 | clocking); | ||
380 | |||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | static int wm8737_set_dai_sysclk(struct snd_soc_dai *codec_dai, | ||
385 | int clk_id, unsigned int freq, int dir) | ||
386 | { | ||
387 | struct snd_soc_codec *codec = codec_dai->codec; | ||
388 | struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec); | ||
389 | int i; | ||
390 | |||
391 | for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { | ||
392 | if (freq == coeff_div[i].mclk || | ||
393 | freq == coeff_div[i].mclk * 2) { | ||
394 | wm8737->mclk = freq; | ||
395 | return 0; | ||
396 | } | ||
397 | } | ||
398 | |||
399 | dev_err(codec->dev, "MCLK rate %dHz not supported\n", freq); | ||
400 | |||
401 | return -EINVAL; | ||
402 | } | ||
403 | |||
404 | |||
405 | static int wm8737_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
406 | unsigned int fmt) | ||
407 | { | ||
408 | struct snd_soc_codec *codec = codec_dai->codec; | ||
409 | u16 af = 0; | ||
410 | |||
411 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
412 | case SND_SOC_DAIFMT_CBM_CFM: | ||
413 | af |= WM8737_MS; | ||
414 | break; | ||
415 | case SND_SOC_DAIFMT_CBS_CFS: | ||
416 | break; | ||
417 | default: | ||
418 | return -EINVAL; | ||
419 | } | ||
420 | |||
421 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
422 | case SND_SOC_DAIFMT_I2S: | ||
423 | af |= 0x2; | ||
424 | break; | ||
425 | case SND_SOC_DAIFMT_RIGHT_J: | ||
426 | break; | ||
427 | case SND_SOC_DAIFMT_LEFT_J: | ||
428 | af |= 0x1; | ||
429 | break; | ||
430 | case SND_SOC_DAIFMT_DSP_A: | ||
431 | af |= 0x3; | ||
432 | break; | ||
433 | case SND_SOC_DAIFMT_DSP_B: | ||
434 | af |= 0x13; | ||
435 | break; | ||
436 | default: | ||
437 | return -EINVAL; | ||
438 | } | ||
439 | |||
440 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
441 | case SND_SOC_DAIFMT_NB_NF: | ||
442 | break; | ||
443 | case SND_SOC_DAIFMT_NB_IF: | ||
444 | af |= WM8737_LRP; | ||
445 | break; | ||
446 | default: | ||
447 | return -EINVAL; | ||
448 | } | ||
449 | |||
450 | snd_soc_update_bits(codec, WM8737_AUDIO_FORMAT, | ||
451 | WM8737_FORMAT_MASK | WM8737_LRP | WM8737_MS, af); | ||
452 | |||
453 | return 0; | ||
454 | } | ||
455 | |||
456 | static int wm8737_set_bias_level(struct snd_soc_codec *codec, | ||
457 | enum snd_soc_bias_level level) | ||
458 | { | ||
459 | struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec); | ||
460 | int ret; | ||
461 | |||
462 | switch (level) { | ||
463 | case SND_SOC_BIAS_ON: | ||
464 | break; | ||
465 | |||
466 | case SND_SOC_BIAS_PREPARE: | ||
467 | /* VMID at 2*75k */ | ||
468 | snd_soc_update_bits(codec, WM8737_MISC_BIAS_CONTROL, | ||
469 | WM8737_VMIDSEL_MASK, 0); | ||
470 | break; | ||
471 | |||
472 | case SND_SOC_BIAS_STANDBY: | ||
473 | if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { | ||
474 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8737->supplies), | ||
475 | wm8737->supplies); | ||
476 | if (ret != 0) { | ||
477 | dev_err(codec->dev, | ||
478 | "Failed to enable supplies: %d\n", | ||
479 | ret); | ||
480 | return ret; | ||
481 | } | ||
482 | |||
483 | snd_soc_cache_sync(codec); | ||
484 | |||
485 | /* Fast VMID ramp at 2*2.5k */ | ||
486 | snd_soc_update_bits(codec, WM8737_MISC_BIAS_CONTROL, | ||
487 | WM8737_VMIDSEL_MASK, 0x4); | ||
488 | |||
489 | /* Bring VMID up */ | ||
490 | snd_soc_update_bits(codec, WM8737_POWER_MANAGEMENT, | ||
491 | WM8737_VMID_MASK | | ||
492 | WM8737_VREF_MASK, | ||
493 | WM8737_VMID_MASK | | ||
494 | WM8737_VREF_MASK); | ||
495 | |||
496 | msleep(500); | ||
497 | } | ||
498 | |||
499 | /* VMID at 2*300k */ | ||
500 | snd_soc_update_bits(codec, WM8737_MISC_BIAS_CONTROL, | ||
501 | WM8737_VMIDSEL_MASK, 2); | ||
502 | |||
503 | break; | ||
504 | |||
505 | case SND_SOC_BIAS_OFF: | ||
506 | snd_soc_update_bits(codec, WM8737_POWER_MANAGEMENT, | ||
507 | WM8737_VMID_MASK | WM8737_VREF_MASK, 0); | ||
508 | |||
509 | regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), | ||
510 | wm8737->supplies); | ||
511 | break; | ||
512 | } | ||
513 | |||
514 | codec->dapm.bias_level = level; | ||
515 | return 0; | ||
516 | } | ||
517 | |||
518 | #define WM8737_RATES SNDRV_PCM_RATE_8000_96000 | ||
519 | |||
520 | #define WM8737_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ | ||
521 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) | ||
522 | |||
523 | static struct snd_soc_dai_ops wm8737_dai_ops = { | ||
524 | .hw_params = wm8737_hw_params, | ||
525 | .set_sysclk = wm8737_set_dai_sysclk, | ||
526 | .set_fmt = wm8737_set_dai_fmt, | ||
527 | }; | ||
528 | |||
529 | static struct snd_soc_dai_driver wm8737_dai = { | ||
530 | .name = "wm8737", | ||
531 | .capture = { | ||
532 | .stream_name = "Capture", | ||
533 | .channels_min = 2, /* Mono modes not yet supported */ | ||
534 | .channels_max = 2, | ||
535 | .rates = WM8737_RATES, | ||
536 | .formats = WM8737_FORMATS, | ||
537 | }, | ||
538 | .ops = &wm8737_dai_ops, | ||
539 | }; | ||
540 | |||
541 | #ifdef CONFIG_PM | ||
542 | static int wm8737_suspend(struct snd_soc_codec *codec, pm_message_t state) | ||
543 | { | ||
544 | wm8737_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | static int wm8737_resume(struct snd_soc_codec *codec) | ||
549 | { | ||
550 | wm8737_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
551 | return 0; | ||
552 | } | ||
553 | #else | ||
554 | #define wm8737_suspend NULL | ||
555 | #define wm8737_resume NULL | ||
556 | #endif | ||
557 | |||
558 | static int wm8737_probe(struct snd_soc_codec *codec) | ||
559 | { | ||
560 | struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec); | ||
561 | int ret, i; | ||
562 | |||
563 | ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8737->control_type); | ||
564 | if (ret != 0) { | ||
565 | dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); | ||
566 | return ret; | ||
567 | } | ||
568 | |||
569 | for (i = 0; i < ARRAY_SIZE(wm8737->supplies); i++) | ||
570 | wm8737->supplies[i].supply = wm8737_supply_names[i]; | ||
571 | |||
572 | ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8737->supplies), | ||
573 | wm8737->supplies); | ||
574 | if (ret != 0) { | ||
575 | dev_err(codec->dev, "Failed to request supplies: %d\n", ret); | ||
576 | return ret; | ||
577 | } | ||
578 | |||
579 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8737->supplies), | ||
580 | wm8737->supplies); | ||
581 | if (ret != 0) { | ||
582 | dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); | ||
583 | goto err_get; | ||
584 | } | ||
585 | |||
586 | ret = wm8737_reset(codec); | ||
587 | if (ret < 0) { | ||
588 | dev_err(codec->dev, "Failed to issue reset\n"); | ||
589 | goto err_enable; | ||
590 | } | ||
591 | |||
592 | snd_soc_update_bits(codec, WM8737_LEFT_PGA_VOLUME, WM8737_LVU, | ||
593 | WM8737_LVU); | ||
594 | snd_soc_update_bits(codec, WM8737_RIGHT_PGA_VOLUME, WM8737_RVU, | ||
595 | WM8737_RVU); | ||
596 | |||
597 | wm8737_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
598 | |||
599 | /* Bias level configuration will have done an extra enable */ | ||
600 | regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies); | ||
601 | |||
602 | snd_soc_add_controls(codec, wm8737_snd_controls, | ||
603 | ARRAY_SIZE(wm8737_snd_controls)); | ||
604 | wm8737_add_widgets(codec); | ||
605 | |||
606 | return 0; | ||
607 | |||
608 | err_enable: | ||
609 | regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies); | ||
610 | err_get: | ||
611 | regulator_bulk_free(ARRAY_SIZE(wm8737->supplies), wm8737->supplies); | ||
612 | |||
613 | return ret; | ||
614 | } | ||
615 | |||
616 | static int wm8737_remove(struct snd_soc_codec *codec) | ||
617 | { | ||
618 | struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec); | ||
619 | |||
620 | wm8737_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
621 | regulator_bulk_free(ARRAY_SIZE(wm8737->supplies), wm8737->supplies); | ||
622 | return 0; | ||
623 | } | ||
624 | |||
625 | static struct snd_soc_codec_driver soc_codec_dev_wm8737 = { | ||
626 | .probe = wm8737_probe, | ||
627 | .remove = wm8737_remove, | ||
628 | .suspend = wm8737_suspend, | ||
629 | .resume = wm8737_resume, | ||
630 | .set_bias_level = wm8737_set_bias_level, | ||
631 | |||
632 | .reg_cache_size = WM8737_REGISTER_COUNT - 1, /* Skip reset */ | ||
633 | .reg_word_size = sizeof(u16), | ||
634 | .reg_cache_default = wm8737_reg, | ||
635 | }; | ||
636 | |||
637 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
638 | static __devinit int wm8737_i2c_probe(struct i2c_client *i2c, | ||
639 | const struct i2c_device_id *id) | ||
640 | { | ||
641 | struct wm8737_priv *wm8737; | ||
642 | int ret; | ||
643 | |||
644 | wm8737 = kzalloc(sizeof(struct wm8737_priv), GFP_KERNEL); | ||
645 | if (wm8737 == NULL) | ||
646 | return -ENOMEM; | ||
647 | |||
648 | i2c_set_clientdata(i2c, wm8737); | ||
649 | wm8737->control_type = SND_SOC_I2C; | ||
650 | |||
651 | ret = snd_soc_register_codec(&i2c->dev, | ||
652 | &soc_codec_dev_wm8737, &wm8737_dai, 1); | ||
653 | if (ret < 0) | ||
654 | kfree(wm8737); | ||
655 | return ret; | ||
656 | |||
657 | } | ||
658 | |||
659 | static __devexit int wm8737_i2c_remove(struct i2c_client *client) | ||
660 | { | ||
661 | snd_soc_unregister_codec(&client->dev); | ||
662 | kfree(i2c_get_clientdata(client)); | ||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | static const struct i2c_device_id wm8737_i2c_id[] = { | ||
667 | { "wm8737", 0 }, | ||
668 | { } | ||
669 | }; | ||
670 | MODULE_DEVICE_TABLE(i2c, wm8737_i2c_id); | ||
671 | |||
672 | static struct i2c_driver wm8737_i2c_driver = { | ||
673 | .driver = { | ||
674 | .name = "wm8737", | ||
675 | .owner = THIS_MODULE, | ||
676 | }, | ||
677 | .probe = wm8737_i2c_probe, | ||
678 | .remove = __devexit_p(wm8737_i2c_remove), | ||
679 | .id_table = wm8737_i2c_id, | ||
680 | }; | ||
681 | #endif | ||
682 | |||
683 | #if defined(CONFIG_SPI_MASTER) | ||
684 | static int __devinit wm8737_spi_probe(struct spi_device *spi) | ||
685 | { | ||
686 | struct wm8737_priv *wm8737; | ||
687 | int ret; | ||
688 | |||
689 | wm8737 = kzalloc(sizeof(struct wm8737_priv), GFP_KERNEL); | ||
690 | if (wm8737 == NULL) | ||
691 | return -ENOMEM; | ||
692 | |||
693 | wm8737->control_type = SND_SOC_SPI; | ||
694 | spi_set_drvdata(spi, wm8737); | ||
695 | |||
696 | ret = snd_soc_register_codec(&spi->dev, | ||
697 | &soc_codec_dev_wm8737, &wm8737_dai, 1); | ||
698 | if (ret < 0) | ||
699 | kfree(wm8737); | ||
700 | return ret; | ||
701 | } | ||
702 | |||
703 | static int __devexit wm8737_spi_remove(struct spi_device *spi) | ||
704 | { | ||
705 | snd_soc_unregister_codec(&spi->dev); | ||
706 | kfree(spi_get_drvdata(spi)); | ||
707 | return 0; | ||
708 | } | ||
709 | |||
710 | static struct spi_driver wm8737_spi_driver = { | ||
711 | .driver = { | ||
712 | .name = "wm8737", | ||
713 | .owner = THIS_MODULE, | ||
714 | }, | ||
715 | .probe = wm8737_spi_probe, | ||
716 | .remove = __devexit_p(wm8737_spi_remove), | ||
717 | }; | ||
718 | #endif /* CONFIG_SPI_MASTER */ | ||
719 | |||
720 | static int __init wm8737_modinit(void) | ||
721 | { | ||
722 | int ret; | ||
723 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
724 | ret = i2c_add_driver(&wm8737_i2c_driver); | ||
725 | if (ret != 0) { | ||
726 | printk(KERN_ERR "Failed to register WM8737 I2C driver: %d\n", | ||
727 | ret); | ||
728 | } | ||
729 | #endif | ||
730 | #if defined(CONFIG_SPI_MASTER) | ||
731 | ret = spi_register_driver(&wm8737_spi_driver); | ||
732 | if (ret != 0) { | ||
733 | printk(KERN_ERR "Failed to register WM8737 SPI driver: %d\n", | ||
734 | ret); | ||
735 | } | ||
736 | #endif | ||
737 | return 0; | ||
738 | } | ||
739 | module_init(wm8737_modinit); | ||
740 | |||
741 | static void __exit wm8737_exit(void) | ||
742 | { | ||
743 | #if defined(CONFIG_SPI_MASTER) | ||
744 | spi_unregister_driver(&wm8737_spi_driver); | ||
745 | #endif | ||
746 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
747 | i2c_del_driver(&wm8737_i2c_driver); | ||
748 | #endif | ||
749 | } | ||
750 | module_exit(wm8737_exit); | ||
751 | |||
752 | MODULE_DESCRIPTION("ASoC WM8737 driver"); | ||
753 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); | ||
754 | MODULE_LICENSE("GPL"); | ||