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