]>
Commit | Line | Data |
---|---|---|
40e0aa64 RP |
1 | /* |
2 | * wm8731.c -- WM8731 ALSA SoC Audio driver | |
3 | * | |
4 | * Copyright 2005 Openedhand Ltd. | |
5 | * | |
6 | * Author: Richard Purdie <richard@openedhand.com> | |
7 | * | |
8 | * Based on wm8753.c by Liam Girdwood | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Software Foundation. | |
13 | */ | |
14 | ||
15 | #include <linux/module.h> | |
16 | #include <linux/moduleparam.h> | |
17 | #include <linux/init.h> | |
18 | #include <linux/delay.h> | |
19 | #include <linux/pm.h> | |
20 | #include <linux/i2c.h> | |
5a0e3ad6 | 21 | #include <linux/slab.h> |
40e0aa64 | 22 | #include <linux/platform_device.h> |
7dea7c01 | 23 | #include <linux/regulator/consumer.h> |
d2a40355 | 24 | #include <linux/spi/spi.h> |
40e0aa64 RP |
25 | #include <sound/core.h> |
26 | #include <sound/pcm.h> | |
27 | #include <sound/pcm_params.h> | |
28 | #include <sound/soc.h> | |
40e0aa64 | 29 | #include <sound/initval.h> |
d00efa64 | 30 | #include <sound/tlv.h> |
40e0aa64 RP |
31 | |
32 | #include "wm8731.h" | |
33 | ||
7dea7c01 MB |
34 | #define WM8731_NUM_SUPPLIES 4 |
35 | static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { | |
36 | "AVDD", | |
37 | "HPVDD", | |
38 | "DCVDD", | |
39 | "DBVDD", | |
40 | }; | |
41 | ||
b36d61d4 FM |
42 | /* codec private data */ |
43 | struct wm8731_priv { | |
f0fba2ad | 44 | enum snd_soc_control_type control_type; |
7dea7c01 | 45 | struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; |
5998102b | 46 | u16 reg_cache[WM8731_CACHEREGNUM]; |
b36d61d4 | 47 | unsigned int sysclk; |
9745e824 | 48 | int sysclk_type; |
b36d61d4 FM |
49 | }; |
50 | ||
a8035c8f | 51 | |
40e0aa64 RP |
52 | /* |
53 | * wm8731 register cache | |
54 | * We can't read the WM8731 register space when we are | |
55 | * using 2 wire for device control, so we cache them instead. | |
56 | * There is no point in caching the reset register | |
57 | */ | |
58 | static const u16 wm8731_reg[WM8731_CACHEREGNUM] = { | |
17a52fd6 MB |
59 | 0x0097, 0x0097, 0x0079, 0x0079, |
60 | 0x000a, 0x0008, 0x009f, 0x000a, | |
61 | 0x0000, 0x0000 | |
40e0aa64 RP |
62 | }; |
63 | ||
17a52fd6 | 64 | #define wm8731_reset(c) snd_soc_write(c, WM8731_RESET, 0) |
40e0aa64 RP |
65 | |
66 | static const char *wm8731_input_select[] = {"Line In", "Mic"}; | |
67 | static const char *wm8731_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; | |
68 | ||
69 | static const struct soc_enum wm8731_enum[] = { | |
70 | SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select), | |
71 | SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph), | |
72 | }; | |
73 | ||
d00efa64 MB |
74 | static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); |
75 | static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0); | |
76 | static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); | |
77 | ||
40e0aa64 RP |
78 | static const struct snd_kcontrol_new wm8731_snd_controls[] = { |
79 | ||
d00efa64 MB |
80 | SOC_DOUBLE_R_TLV("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V, |
81 | 0, 127, 0, out_tlv), | |
bd903b6e LG |
82 | SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V, |
83 | 7, 1, 0), | |
40e0aa64 | 84 | |
d00efa64 MB |
85 | SOC_DOUBLE_R_TLV("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0, |
86 | in_tlv), | |
40e0aa64 RP |
87 | SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1), |
88 | ||
89 | SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0), | |
ef38ed88 | 90 | SOC_SINGLE("Mic Capture Switch", WM8731_APANA, 1, 1, 1), |
40e0aa64 | 91 | |
d00efa64 MB |
92 | SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1, |
93 | sidetone_tlv), | |
40e0aa64 RP |
94 | |
95 | SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1), | |
96 | SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0), | |
97 | ||
98 | SOC_ENUM("Playback De-emphasis", wm8731_enum[1]), | |
99 | }; | |
100 | ||
40e0aa64 RP |
101 | /* Output Mixer */ |
102 | static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = { | |
103 | SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0), | |
104 | SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0), | |
105 | SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0), | |
106 | }; | |
107 | ||
108 | /* Input mux */ | |
109 | static const struct snd_kcontrol_new wm8731_input_mux_controls = | |
110 | SOC_DAPM_ENUM("Input Select", wm8731_enum[0]); | |
111 | ||
112 | static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { | |
9745e824 | 113 | SND_SOC_DAPM_SUPPLY("OSC", WM8731_PWR, 5, 1, NULL, 0), |
40e0aa64 RP |
114 | SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, |
115 | &wm8731_output_mixer_controls[0], | |
116 | ARRAY_SIZE(wm8731_output_mixer_controls)), | |
117 | SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8731_PWR, 3, 1), | |
118 | SND_SOC_DAPM_OUTPUT("LOUT"), | |
119 | SND_SOC_DAPM_OUTPUT("LHPOUT"), | |
120 | SND_SOC_DAPM_OUTPUT("ROUT"), | |
121 | SND_SOC_DAPM_OUTPUT("RHPOUT"), | |
122 | SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8731_PWR, 2, 1), | |
123 | SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &wm8731_input_mux_controls), | |
124 | SND_SOC_DAPM_PGA("Line Input", WM8731_PWR, 0, 1, NULL, 0), | |
125 | SND_SOC_DAPM_MICBIAS("Mic Bias", WM8731_PWR, 1, 1), | |
126 | SND_SOC_DAPM_INPUT("MICIN"), | |
127 | SND_SOC_DAPM_INPUT("RLINEIN"), | |
128 | SND_SOC_DAPM_INPUT("LLINEIN"), | |
129 | }; | |
130 | ||
9745e824 MB |
131 | static int wm8731_check_osc(struct snd_soc_dapm_widget *source, |
132 | struct snd_soc_dapm_widget *sink) | |
133 | { | |
134 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(source->codec); | |
135 | ||
136 | return wm8731->sysclk_type == WM8731_SYSCLK_MCLK; | |
137 | } | |
138 | ||
a65f0568 | 139 | static const struct snd_soc_dapm_route intercon[] = { |
9745e824 MB |
140 | {"DAC", NULL, "OSC", wm8731_check_osc}, |
141 | {"ADC", NULL, "OSC", wm8731_check_osc}, | |
142 | ||
40e0aa64 RP |
143 | /* output mixer */ |
144 | {"Output Mixer", "Line Bypass Switch", "Line Input"}, | |
145 | {"Output Mixer", "HiFi Playback Switch", "DAC"}, | |
146 | {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"}, | |
147 | ||
148 | /* outputs */ | |
149 | {"RHPOUT", NULL, "Output Mixer"}, | |
150 | {"ROUT", NULL, "Output Mixer"}, | |
151 | {"LHPOUT", NULL, "Output Mixer"}, | |
152 | {"LOUT", NULL, "Output Mixer"}, | |
153 | ||
154 | /* input mux */ | |
155 | {"Input Mux", "Line In", "Line Input"}, | |
156 | {"Input Mux", "Mic", "Mic Bias"}, | |
157 | {"ADC", NULL, "Input Mux"}, | |
158 | ||
159 | /* inputs */ | |
160 | {"Line Input", NULL, "LLINEIN"}, | |
161 | {"Line Input", NULL, "RLINEIN"}, | |
162 | {"Mic Bias", NULL, "MICIN"}, | |
40e0aa64 RP |
163 | }; |
164 | ||
165 | static int wm8731_add_widgets(struct snd_soc_codec *codec) | |
166 | { | |
ce6120cc | 167 | struct snd_soc_dapm_context *dapm = &codec->dapm; |
40e0aa64 | 168 | |
ce6120cc LG |
169 | snd_soc_dapm_new_controls(dapm, wm8731_dapm_widgets, |
170 | ARRAY_SIZE(wm8731_dapm_widgets)); | |
171 | snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); | |
40e0aa64 | 172 | |
40e0aa64 RP |
173 | return 0; |
174 | } | |
175 | ||
176 | struct _coeff_div { | |
177 | u32 mclk; | |
178 | u32 rate; | |
179 | u16 fs; | |
180 | u8 sr:4; | |
181 | u8 bosr:1; | |
182 | u8 usb:1; | |
183 | }; | |
184 | ||
185 | /* codec mclk clock divider coefficients */ | |
186 | static const struct _coeff_div coeff_div[] = { | |
187 | /* 48k */ | |
188 | {12288000, 48000, 256, 0x0, 0x0, 0x0}, | |
189 | {18432000, 48000, 384, 0x0, 0x1, 0x0}, | |
190 | {12000000, 48000, 250, 0x0, 0x0, 0x1}, | |
191 | ||
192 | /* 32k */ | |
193 | {12288000, 32000, 384, 0x6, 0x0, 0x0}, | |
194 | {18432000, 32000, 576, 0x6, 0x1, 0x0}, | |
298a2c75 | 195 | {12000000, 32000, 375, 0x6, 0x0, 0x1}, |
40e0aa64 RP |
196 | |
197 | /* 8k */ | |
198 | {12288000, 8000, 1536, 0x3, 0x0, 0x0}, | |
199 | {18432000, 8000, 2304, 0x3, 0x1, 0x0}, | |
200 | {11289600, 8000, 1408, 0xb, 0x0, 0x0}, | |
201 | {16934400, 8000, 2112, 0xb, 0x1, 0x0}, | |
202 | {12000000, 8000, 1500, 0x3, 0x0, 0x1}, | |
203 | ||
204 | /* 96k */ | |
205 | {12288000, 96000, 128, 0x7, 0x0, 0x0}, | |
206 | {18432000, 96000, 192, 0x7, 0x1, 0x0}, | |
207 | {12000000, 96000, 125, 0x7, 0x0, 0x1}, | |
208 | ||
209 | /* 44.1k */ | |
210 | {11289600, 44100, 256, 0x8, 0x0, 0x0}, | |
211 | {16934400, 44100, 384, 0x8, 0x1, 0x0}, | |
212 | {12000000, 44100, 272, 0x8, 0x1, 0x1}, | |
213 | ||
214 | /* 88.2k */ | |
215 | {11289600, 88200, 128, 0xf, 0x0, 0x0}, | |
216 | {16934400, 88200, 192, 0xf, 0x1, 0x0}, | |
217 | {12000000, 88200, 136, 0xf, 0x1, 0x1}, | |
218 | }; | |
219 | ||
220 | static inline int get_coeff(int mclk, int rate) | |
221 | { | |
222 | int i; | |
223 | ||
224 | for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { | |
225 | if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) | |
226 | return i; | |
227 | } | |
228 | return 0; | |
229 | } | |
230 | ||
b36d61d4 | 231 | static int wm8731_hw_params(struct snd_pcm_substream *substream, |
dee89c4d MB |
232 | struct snd_pcm_hw_params *params, |
233 | struct snd_soc_dai *dai) | |
40e0aa64 | 234 | { |
f0fba2ad | 235 | struct snd_soc_codec *codec = dai->codec; |
b2c812e2 | 236 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); |
17a52fd6 | 237 | u16 iface = snd_soc_read(codec, WM8731_IFACE) & 0xfff3; |
b36d61d4 FM |
238 | int i = get_coeff(wm8731->sysclk, params_rate(params)); |
239 | u16 srate = (coeff_div[i].sr << 2) | | |
240 | (coeff_div[i].bosr << 1) | coeff_div[i].usb; | |
40e0aa64 | 241 | |
17a52fd6 | 242 | snd_soc_write(codec, WM8731_SRATE, srate); |
b36d61d4 FM |
243 | |
244 | /* bit size */ | |
245 | switch (params_format(params)) { | |
246 | case SNDRV_PCM_FORMAT_S16_LE: | |
247 | break; | |
248 | case SNDRV_PCM_FORMAT_S20_3LE: | |
249 | iface |= 0x0004; | |
250 | break; | |
251 | case SNDRV_PCM_FORMAT_S24_LE: | |
252 | iface |= 0x0008; | |
253 | break; | |
254 | } | |
40e0aa64 | 255 | |
17a52fd6 | 256 | snd_soc_write(codec, WM8731_IFACE, iface); |
b36d61d4 | 257 | return 0; |
40e0aa64 RP |
258 | } |
259 | ||
dee89c4d MB |
260 | static int wm8731_pcm_prepare(struct snd_pcm_substream *substream, |
261 | struct snd_soc_dai *dai) | |
40e0aa64 | 262 | { |
f0fba2ad | 263 | struct snd_soc_codec *codec = dai->codec; |
b36d61d4 FM |
264 | |
265 | /* set active */ | |
17a52fd6 | 266 | snd_soc_write(codec, WM8731_ACTIVE, 0x0001); |
b36d61d4 FM |
267 | |
268 | return 0; | |
269 | } | |
270 | ||
dee89c4d MB |
271 | static void wm8731_shutdown(struct snd_pcm_substream *substream, |
272 | struct snd_soc_dai *dai) | |
b36d61d4 | 273 | { |
f0fba2ad | 274 | struct snd_soc_codec *codec = dai->codec; |
b36d61d4 FM |
275 | |
276 | /* deactivate */ | |
277 | if (!codec->active) { | |
278 | udelay(50); | |
17a52fd6 | 279 | snd_soc_write(codec, WM8731_ACTIVE, 0x0); |
b36d61d4 FM |
280 | } |
281 | } | |
282 | ||
e550e17f | 283 | static int wm8731_mute(struct snd_soc_dai *dai, int mute) |
b36d61d4 FM |
284 | { |
285 | struct snd_soc_codec *codec = dai->codec; | |
17a52fd6 | 286 | u16 mute_reg = snd_soc_read(codec, WM8731_APDIGI) & 0xfff7; |
b36d61d4 FM |
287 | |
288 | if (mute) | |
17a52fd6 | 289 | snd_soc_write(codec, WM8731_APDIGI, mute_reg | 0x8); |
b36d61d4 | 290 | else |
17a52fd6 | 291 | snd_soc_write(codec, WM8731_APDIGI, mute_reg); |
b36d61d4 FM |
292 | return 0; |
293 | } | |
294 | ||
e550e17f | 295 | static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai, |
b36d61d4 FM |
296 | int clk_id, unsigned int freq, int dir) |
297 | { | |
298 | struct snd_soc_codec *codec = codec_dai->codec; | |
b2c812e2 | 299 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); |
b36d61d4 | 300 | |
9745e824 MB |
301 | switch (clk_id) { |
302 | case WM8731_SYSCLK_XTAL: | |
303 | case WM8731_SYSCLK_MCLK: | |
304 | wm8731->sysclk_type = clk_id; | |
305 | break; | |
306 | default: | |
307 | return -EINVAL; | |
308 | } | |
309 | ||
b36d61d4 FM |
310 | switch (freq) { |
311 | case 11289600: | |
312 | case 12000000: | |
313 | case 12288000: | |
314 | case 16934400: | |
315 | case 18432000: | |
316 | wm8731->sysclk = freq; | |
9745e824 MB |
317 | break; |
318 | default: | |
319 | return -EINVAL; | |
b36d61d4 | 320 | } |
9745e824 | 321 | |
ce6120cc | 322 | snd_soc_dapm_sync(&codec->dapm); |
9745e824 MB |
323 | |
324 | return 0; | |
b36d61d4 FM |
325 | } |
326 | ||
327 | ||
e550e17f | 328 | static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai, |
b36d61d4 FM |
329 | unsigned int fmt) |
330 | { | |
331 | struct snd_soc_codec *codec = codec_dai->codec; | |
332 | u16 iface = 0; | |
40e0aa64 RP |
333 | |
334 | /* set master/slave audio interface */ | |
b36d61d4 | 335 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
40e0aa64 RP |
336 | case SND_SOC_DAIFMT_CBM_CFM: |
337 | iface |= 0x0040; | |
338 | break; | |
339 | case SND_SOC_DAIFMT_CBS_CFS: | |
340 | break; | |
b36d61d4 FM |
341 | default: |
342 | return -EINVAL; | |
40e0aa64 | 343 | } |
40e0aa64 RP |
344 | |
345 | /* interface format */ | |
b36d61d4 | 346 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
40e0aa64 RP |
347 | case SND_SOC_DAIFMT_I2S: |
348 | iface |= 0x0002; | |
349 | break; | |
350 | case SND_SOC_DAIFMT_RIGHT_J: | |
351 | break; | |
352 | case SND_SOC_DAIFMT_LEFT_J: | |
353 | iface |= 0x0001; | |
354 | break; | |
355 | case SND_SOC_DAIFMT_DSP_A: | |
356 | iface |= 0x0003; | |
357 | break; | |
358 | case SND_SOC_DAIFMT_DSP_B: | |
359 | iface |= 0x0013; | |
360 | break; | |
b36d61d4 FM |
361 | default: |
362 | return -EINVAL; | |
40e0aa64 RP |
363 | } |
364 | ||
365 | /* clock inversion */ | |
b36d61d4 | 366 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
40e0aa64 RP |
367 | case SND_SOC_DAIFMT_NB_NF: |
368 | break; | |
369 | case SND_SOC_DAIFMT_IB_IF: | |
370 | iface |= 0x0090; | |
371 | break; | |
372 | case SND_SOC_DAIFMT_IB_NF: | |
373 | iface |= 0x0080; | |
374 | break; | |
375 | case SND_SOC_DAIFMT_NB_IF: | |
376 | iface |= 0x0010; | |
377 | break; | |
b36d61d4 FM |
378 | default: |
379 | return -EINVAL; | |
40e0aa64 RP |
380 | } |
381 | ||
382 | /* set iface */ | |
17a52fd6 | 383 | snd_soc_write(codec, WM8731_IFACE, iface); |
40e0aa64 RP |
384 | return 0; |
385 | } | |
386 | ||
0be9898a MB |
387 | static int wm8731_set_bias_level(struct snd_soc_codec *codec, |
388 | enum snd_soc_bias_level level) | |
40e0aa64 | 389 | { |
06ae9988 MB |
390 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); |
391 | int i, ret; | |
392 | u8 data[2]; | |
393 | u16 *cache = codec->reg_cache; | |
22d22ee5 | 394 | u16 reg; |
40e0aa64 | 395 | |
0be9898a MB |
396 | switch (level) { |
397 | case SND_SOC_BIAS_ON: | |
40e0aa64 | 398 | break; |
0be9898a | 399 | case SND_SOC_BIAS_PREPARE: |
40e0aa64 | 400 | break; |
0be9898a | 401 | case SND_SOC_BIAS_STANDBY: |
ce6120cc | 402 | if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { |
06ae9988 MB |
403 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), |
404 | wm8731->supplies); | |
405 | if (ret != 0) | |
406 | return ret; | |
407 | ||
408 | /* Sync reg_cache with the hardware */ | |
409 | for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) { | |
410 | if (cache[i] == wm8731_reg[i]) | |
411 | continue; | |
412 | ||
413 | data[0] = (i << 1) | ((cache[i] >> 8) | |
414 | & 0x0001); | |
415 | data[1] = cache[i] & 0x00ff; | |
416 | codec->hw_write(codec->control_data, data, 2); | |
417 | } | |
418 | } | |
419 | ||
22d22ee5 | 420 | /* Clear PWROFF, gate CLKOUT, everything else as-is */ |
17a52fd6 MB |
421 | reg = snd_soc_read(codec, WM8731_PWR) & 0xff7f; |
422 | snd_soc_write(codec, WM8731_PWR, reg | 0x0040); | |
40e0aa64 | 423 | break; |
0be9898a | 424 | case SND_SOC_BIAS_OFF: |
17a52fd6 MB |
425 | snd_soc_write(codec, WM8731_ACTIVE, 0x0); |
426 | snd_soc_write(codec, WM8731_PWR, 0xffff); | |
06ae9988 MB |
427 | regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), |
428 | wm8731->supplies); | |
40e0aa64 RP |
429 | break; |
430 | } | |
ce6120cc | 431 | codec->dapm.bias_level = level; |
40e0aa64 RP |
432 | return 0; |
433 | } | |
434 | ||
e135443e | 435 | #define WM8731_RATES SNDRV_PCM_RATE_8000_96000 |
b36d61d4 FM |
436 | |
437 | #define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ | |
438 | SNDRV_PCM_FMTBIT_S24_LE) | |
439 | ||
6335d055 EM |
440 | static struct snd_soc_dai_ops wm8731_dai_ops = { |
441 | .prepare = wm8731_pcm_prepare, | |
442 | .hw_params = wm8731_hw_params, | |
443 | .shutdown = wm8731_shutdown, | |
444 | .digital_mute = wm8731_mute, | |
445 | .set_sysclk = wm8731_set_dai_sysclk, | |
446 | .set_fmt = wm8731_set_dai_fmt, | |
447 | }; | |
448 | ||
f0fba2ad LG |
449 | static struct snd_soc_dai_driver wm8731_dai = { |
450 | .name = "wm8731-hifi", | |
40e0aa64 RP |
451 | .playback = { |
452 | .stream_name = "Playback", | |
453 | .channels_min = 1, | |
454 | .channels_max = 2, | |
b36d61d4 FM |
455 | .rates = WM8731_RATES, |
456 | .formats = WM8731_FORMATS,}, | |
40e0aa64 RP |
457 | .capture = { |
458 | .stream_name = "Capture", | |
459 | .channels_min = 1, | |
460 | .channels_max = 2, | |
b36d61d4 FM |
461 | .rates = WM8731_RATES, |
462 | .formats = WM8731_FORMATS,}, | |
6335d055 | 463 | .ops = &wm8731_dai_ops, |
4934482d | 464 | .symmetric_rates = 1, |
40e0aa64 | 465 | }; |
40e0aa64 | 466 | |
b3b50b3f | 467 | #ifdef CONFIG_PM |
f0fba2ad | 468 | static int wm8731_suspend(struct snd_soc_codec *codec, pm_message_t state) |
40e0aa64 | 469 | { |
0be9898a | 470 | wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); |
06ae9988 | 471 | |
40e0aa64 RP |
472 | return 0; |
473 | } | |
474 | ||
f0fba2ad | 475 | static int wm8731_resume(struct snd_soc_codec *codec) |
40e0aa64 | 476 | { |
0be9898a | 477 | wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
7dea7c01 | 478 | |
40e0aa64 RP |
479 | return 0; |
480 | } | |
b3b50b3f MB |
481 | #else |
482 | #define wm8731_suspend NULL | |
483 | #define wm8731_resume NULL | |
484 | #endif | |
40e0aa64 | 485 | |
f0fba2ad | 486 | static int wm8731_probe(struct snd_soc_codec *codec) |
40e0aa64 | 487 | { |
f0fba2ad LG |
488 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); |
489 | int ret = 0, i; | |
5998102b | 490 | |
f0fba2ad | 491 | ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8731->control_type); |
17a52fd6 MB |
492 | if (ret < 0) { |
493 | dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); | |
f0fba2ad | 494 | return ret; |
17a52fd6 MB |
495 | } |
496 | ||
7dea7c01 MB |
497 | for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++) |
498 | wm8731->supplies[i].supply = wm8731_supply_names[i]; | |
499 | ||
500 | ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies), | |
501 | wm8731->supplies); | |
502 | if (ret != 0) { | |
503 | dev_err(codec->dev, "Failed to request supplies: %d\n", ret); | |
f0fba2ad | 504 | return ret; |
7dea7c01 MB |
505 | } |
506 | ||
507 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), | |
508 | wm8731->supplies); | |
509 | if (ret != 0) { | |
510 | dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); | |
511 | goto err_regulator_get; | |
512 | } | |
513 | ||
519cf2df MB |
514 | ret = wm8731_reset(codec); |
515 | if (ret < 0) { | |
fe5422fc | 516 | dev_err(codec->dev, "Failed to issue reset: %d\n", ret); |
7dea7c01 | 517 | goto err_regulator_enable; |
519cf2df MB |
518 | } |
519 | ||
5998102b MB |
520 | wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
521 | ||
522 | /* Latch the update bits */ | |
17a52fd6 MB |
523 | snd_soc_update_bits(codec, WM8731_LOUT1V, 0x100, 0); |
524 | snd_soc_update_bits(codec, WM8731_ROUT1V, 0x100, 0); | |
525 | snd_soc_update_bits(codec, WM8731_LINVOL, 0x100, 0); | |
526 | snd_soc_update_bits(codec, WM8731_RINVOL, 0x100, 0); | |
5998102b | 527 | |
ce3bdaa8 | 528 | /* Disable bypass path by default */ |
2062ea52 | 529 | snd_soc_update_bits(codec, WM8731_APANA, 0x8, 0); |
ce3bdaa8 | 530 | |
f0fba2ad LG |
531 | snd_soc_add_controls(codec, wm8731_snd_controls, |
532 | ARRAY_SIZE(wm8731_snd_controls)); | |
533 | wm8731_add_widgets(codec); | |
a8035c8f | 534 | |
06ae9988 MB |
535 | /* Regulators will have been enabled by bias management */ |
536 | regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); | |
537 | ||
a8035c8f | 538 | return 0; |
fe5422fc | 539 | |
7dea7c01 MB |
540 | err_regulator_enable: |
541 | regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); | |
542 | err_regulator_get: | |
543 | regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); | |
f0fba2ad | 544 | |
fe5422fc | 545 | return ret; |
a8035c8f MB |
546 | } |
547 | ||
f0fba2ad LG |
548 | /* power down chip */ |
549 | static int wm8731_remove(struct snd_soc_codec *codec) | |
5998102b | 550 | { |
f0fba2ad LG |
551 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); |
552 | ||
553 | wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); | |
554 | ||
555 | regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); | |
7dea7c01 | 556 | regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); |
f0fba2ad LG |
557 | |
558 | return 0; | |
5998102b | 559 | } |
a8035c8f | 560 | |
f0fba2ad LG |
561 | static struct snd_soc_codec_driver soc_codec_dev_wm8731 = { |
562 | .probe = wm8731_probe, | |
563 | .remove = wm8731_remove, | |
564 | .suspend = wm8731_suspend, | |
565 | .resume = wm8731_resume, | |
566 | .set_bias_level = wm8731_set_bias_level, | |
e5eec34c | 567 | .reg_cache_size = ARRAY_SIZE(wm8731_reg), |
f0fba2ad LG |
568 | .reg_word_size = sizeof(u16), |
569 | .reg_cache_default = wm8731_reg, | |
570 | }; | |
571 | ||
5998102b | 572 | #if defined(CONFIG_SPI_MASTER) |
5998102b MB |
573 | static int __devinit wm8731_spi_probe(struct spi_device *spi) |
574 | { | |
5998102b | 575 | struct wm8731_priv *wm8731; |
f0fba2ad | 576 | int ret; |
5998102b MB |
577 | |
578 | wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL); | |
579 | if (wm8731 == NULL) | |
580 | return -ENOMEM; | |
581 | ||
f0fba2ad LG |
582 | wm8731->control_type = SND_SOC_SPI; |
583 | spi_set_drvdata(spi, wm8731); | |
93b760b7 | 584 | |
f0fba2ad LG |
585 | ret = snd_soc_register_codec(&spi->dev, |
586 | &soc_codec_dev_wm8731, &wm8731_dai, 1); | |
587 | if (ret < 0) | |
588 | kfree(wm8731); | |
589 | return ret; | |
5998102b MB |
590 | } |
591 | ||
592 | static int __devexit wm8731_spi_remove(struct spi_device *spi) | |
593 | { | |
f0fba2ad LG |
594 | snd_soc_unregister_codec(&spi->dev); |
595 | kfree(spi_get_drvdata(spi)); | |
5998102b MB |
596 | return 0; |
597 | } | |
598 | ||
599 | static struct spi_driver wm8731_spi_driver = { | |
600 | .driver = { | |
f0fba2ad | 601 | .name = "wm8731-codec", |
5998102b MB |
602 | .owner = THIS_MODULE, |
603 | }, | |
604 | .probe = wm8731_spi_probe, | |
605 | .remove = __devexit_p(wm8731_spi_remove), | |
606 | }; | |
a8035c8f MB |
607 | #endif /* CONFIG_SPI_MASTER */ |
608 | ||
609 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | |
c6f29811 MB |
610 | static __devinit int wm8731_i2c_probe(struct i2c_client *i2c, |
611 | const struct i2c_device_id *id) | |
a8035c8f | 612 | { |
5998102b | 613 | struct wm8731_priv *wm8731; |
f0fba2ad | 614 | int ret; |
a8035c8f | 615 | |
5998102b MB |
616 | wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL); |
617 | if (wm8731 == NULL) | |
618 | return -ENOMEM; | |
619 | ||
5998102b | 620 | i2c_set_clientdata(i2c, wm8731); |
f0fba2ad | 621 | wm8731->control_type = SND_SOC_I2C; |
a8035c8f | 622 | |
f0fba2ad LG |
623 | ret = snd_soc_register_codec(&i2c->dev, |
624 | &soc_codec_dev_wm8731, &wm8731_dai, 1); | |
625 | if (ret < 0) | |
626 | kfree(wm8731); | |
627 | return ret; | |
a8035c8f MB |
628 | } |
629 | ||
c6f29811 | 630 | static __devexit int wm8731_i2c_remove(struct i2c_client *client) |
a8035c8f | 631 | { |
f0fba2ad LG |
632 | snd_soc_unregister_codec(&client->dev); |
633 | kfree(i2c_get_clientdata(client)); | |
a8035c8f MB |
634 | return 0; |
635 | } | |
636 | ||
637 | static const struct i2c_device_id wm8731_i2c_id[] = { | |
638 | { "wm8731", 0 }, | |
639 | { } | |
640 | }; | |
641 | MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id); | |
642 | ||
643 | static struct i2c_driver wm8731_i2c_driver = { | |
644 | .driver = { | |
f0fba2ad | 645 | .name = "wm8731-codec", |
a8035c8f MB |
646 | .owner = THIS_MODULE, |
647 | }, | |
648 | .probe = wm8731_i2c_probe, | |
c6f29811 | 649 | .remove = __devexit_p(wm8731_i2c_remove), |
a8035c8f MB |
650 | .id_table = wm8731_i2c_id, |
651 | }; | |
652 | #endif | |
653 | ||
c9b3a40f | 654 | static int __init wm8731_modinit(void) |
64089b84 | 655 | { |
f0fba2ad | 656 | int ret = 0; |
5998102b MB |
657 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
658 | ret = i2c_add_driver(&wm8731_i2c_driver); | |
659 | if (ret != 0) { | |
660 | printk(KERN_ERR "Failed to register WM8731 I2C driver: %d\n", | |
661 | ret); | |
662 | } | |
663 | #endif | |
664 | #if defined(CONFIG_SPI_MASTER) | |
665 | ret = spi_register_driver(&wm8731_spi_driver); | |
666 | if (ret != 0) { | |
667 | printk(KERN_ERR "Failed to register WM8731 SPI driver: %d\n", | |
668 | ret); | |
669 | } | |
670 | #endif | |
f0fba2ad | 671 | return ret; |
64089b84 MB |
672 | } |
673 | module_init(wm8731_modinit); | |
674 | ||
675 | static void __exit wm8731_exit(void) | |
676 | { | |
5998102b MB |
677 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
678 | i2c_del_driver(&wm8731_i2c_driver); | |
679 | #endif | |
680 | #if defined(CONFIG_SPI_MASTER) | |
681 | spi_unregister_driver(&wm8731_spi_driver); | |
682 | #endif | |
64089b84 MB |
683 | } |
684 | module_exit(wm8731_exit); | |
685 | ||
40e0aa64 RP |
686 | MODULE_DESCRIPTION("ASoC WM8731 driver"); |
687 | MODULE_AUTHOR("Richard Purdie"); | |
688 | MODULE_LICENSE("GPL"); |