]>
Commit | Line | Data |
---|---|---|
992bee40 IL |
1 | /* |
2 | * wm8741.c -- WM8741 ALSA SoC Audio driver | |
3 | * | |
656baaeb | 4 | * Copyright 2010-1 Wolfson Microelectronics plc |
992bee40 IL |
5 | * |
6 | * Author: Ian Lartey <ian@opensource.wolfsonmicro.com> | |
7 | * | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License version 2 as | |
11 | * published by the Free Software Foundation. | |
12 | */ | |
13 | ||
14 | #include <linux/module.h> | |
15 | #include <linux/moduleparam.h> | |
16 | #include <linux/init.h> | |
17 | #include <linux/delay.h> | |
18 | #include <linux/pm.h> | |
19 | #include <linux/i2c.h> | |
39e9b8d2 | 20 | #include <linux/spi/spi.h> |
fe98c0cf | 21 | #include <linux/regmap.h> |
992bee40 IL |
22 | #include <linux/regulator/consumer.h> |
23 | #include <linux/slab.h> | |
80080ec5 | 24 | #include <linux/of_device.h> |
992bee40 IL |
25 | #include <sound/core.h> |
26 | #include <sound/pcm.h> | |
27 | #include <sound/pcm_params.h> | |
28 | #include <sound/soc.h> | |
992bee40 IL |
29 | #include <sound/initval.h> |
30 | #include <sound/tlv.h> | |
31 | ||
32 | #include "wm8741.h" | |
33 | ||
992bee40 IL |
34 | #define WM8741_NUM_SUPPLIES 2 |
35 | static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = { | |
36 | "AVDD", | |
37 | "DVDD", | |
38 | }; | |
39 | ||
3fe4a5ee | 40 | #define WM8741_NUM_RATES 6 |
992bee40 IL |
41 | |
42 | /* codec private data */ | |
43 | struct wm8741_priv { | |
c354b54c | 44 | struct wm8741_platform_data pdata; |
fe98c0cf | 45 | struct regmap *regmap; |
992bee40 IL |
46 | struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES]; |
47 | unsigned int sysclk; | |
70bad2c7 | 48 | const struct snd_pcm_hw_constraint_list *sysclk_constraints; |
992bee40 IL |
49 | }; |
50 | ||
fe98c0cf MB |
51 | static const struct reg_default wm8741_reg_defaults[] = { |
52 | { 0, 0x0000 }, /* R0 - DACLLSB Attenuation */ | |
53 | { 1, 0x0000 }, /* R1 - DACLMSB Attenuation */ | |
54 | { 2, 0x0000 }, /* R2 - DACRLSB Attenuation */ | |
55 | { 3, 0x0000 }, /* R3 - DACRMSB Attenuation */ | |
56 | { 4, 0x0000 }, /* R4 - Volume Control */ | |
57 | { 5, 0x000A }, /* R5 - Format Control */ | |
58 | { 6, 0x0000 }, /* R6 - Filter Control */ | |
59 | { 7, 0x0000 }, /* R7 - Mode Control 1 */ | |
60 | { 8, 0x0002 }, /* R8 - Mode Control 2 */ | |
61 | { 32, 0x0002 }, /* R32 - ADDITONAL_CONTROL_1 */ | |
992bee40 IL |
62 | }; |
63 | ||
fe98c0cf MB |
64 | static bool wm8741_readable(struct device *dev, unsigned int reg) |
65 | { | |
66 | switch (reg) { | |
67 | case WM8741_DACLLSB_ATTENUATION: | |
68 | case WM8741_DACLMSB_ATTENUATION: | |
69 | case WM8741_DACRLSB_ATTENUATION: | |
70 | case WM8741_DACRMSB_ATTENUATION: | |
71 | case WM8741_VOLUME_CONTROL: | |
72 | case WM8741_FORMAT_CONTROL: | |
73 | case WM8741_FILTER_CONTROL: | |
74 | case WM8741_MODE_CONTROL_1: | |
75 | case WM8741_MODE_CONTROL_2: | |
76 | case WM8741_ADDITIONAL_CONTROL_1: | |
77 | return true; | |
78 | default: | |
79 | return false; | |
80 | } | |
81 | } | |
992bee40 IL |
82 | |
83 | static int wm8741_reset(struct snd_soc_codec *codec) | |
84 | { | |
85 | return snd_soc_write(codec, WM8741_RESET, 0); | |
86 | } | |
87 | ||
88 | static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0); | |
89 | static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0); | |
90 | ||
c354b54c | 91 | static const struct snd_kcontrol_new wm8741_snd_controls_stereo[] = { |
992bee40 IL |
92 | SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, |
93 | WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine), | |
94 | SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION, | |
95 | WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv), | |
96 | }; | |
97 | ||
c354b54c SS |
98 | static const struct snd_kcontrol_new wm8741_snd_controls_mono_left[] = { |
99 | SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, | |
100 | 1, 255, 1, dac_tlv_fine), | |
101 | SOC_SINGLE_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION, | |
102 | 0, 511, 1, dac_tlv), | |
103 | }; | |
104 | ||
105 | static const struct snd_kcontrol_new wm8741_snd_controls_mono_right[] = { | |
106 | SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACRLSB_ATTENUATION, | |
107 | 1, 255, 1, dac_tlv_fine), | |
108 | SOC_SINGLE_TLV("Playback Volume", WM8741_DACRMSB_ATTENUATION, | |
109 | 0, 511, 1, dac_tlv), | |
110 | }; | |
111 | ||
992bee40 IL |
112 | static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = { |
113 | SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0), | |
114 | SND_SOC_DAPM_DAC("DACR", "Playback", SND_SOC_NOPM, 0, 0), | |
115 | SND_SOC_DAPM_OUTPUT("VOUTLP"), | |
116 | SND_SOC_DAPM_OUTPUT("VOUTLN"), | |
117 | SND_SOC_DAPM_OUTPUT("VOUTRP"), | |
118 | SND_SOC_DAPM_OUTPUT("VOUTRN"), | |
119 | }; | |
120 | ||
0e62780f | 121 | static const struct snd_soc_dapm_route wm8741_dapm_routes[] = { |
992bee40 IL |
122 | { "VOUTLP", NULL, "DACL" }, |
123 | { "VOUTLN", NULL, "DACL" }, | |
124 | { "VOUTRP", NULL, "DACR" }, | |
125 | { "VOUTRN", NULL, "DACR" }, | |
126 | }; | |
127 | ||
992bee40 IL |
128 | static struct { |
129 | int value; | |
130 | int ratio; | |
131 | } lrclk_ratios[WM8741_NUM_RATES] = { | |
3fe4a5ee IL |
132 | { 1, 128 }, |
133 | { 2, 192 }, | |
134 | { 3, 256 }, | |
135 | { 4, 384 }, | |
136 | { 5, 512 }, | |
137 | { 6, 768 }, | |
138 | }; | |
139 | ||
70bad2c7 | 140 | static const unsigned int rates_11289[] = { |
8787041d | 141 | 44100, 88200, |
3fe4a5ee IL |
142 | }; |
143 | ||
70bad2c7 | 144 | static const struct snd_pcm_hw_constraint_list constraints_11289 = { |
3fe4a5ee IL |
145 | .count = ARRAY_SIZE(rates_11289), |
146 | .list = rates_11289, | |
147 | }; | |
148 | ||
70bad2c7 | 149 | static const unsigned int rates_12288[] = { |
3fe4a5ee IL |
150 | 32000, 48000, 96000, |
151 | }; | |
152 | ||
70bad2c7 | 153 | static const struct snd_pcm_hw_constraint_list constraints_12288 = { |
3fe4a5ee IL |
154 | .count = ARRAY_SIZE(rates_12288), |
155 | .list = rates_12288, | |
156 | }; | |
157 | ||
70bad2c7 | 158 | static const unsigned int rates_16384[] = { |
3fe4a5ee IL |
159 | 32000, |
160 | }; | |
161 | ||
70bad2c7 | 162 | static const struct snd_pcm_hw_constraint_list constraints_16384 = { |
3fe4a5ee IL |
163 | .count = ARRAY_SIZE(rates_16384), |
164 | .list = rates_16384, | |
165 | }; | |
166 | ||
70bad2c7 | 167 | static const unsigned int rates_16934[] = { |
8787041d | 168 | 44100, 88200, |
3fe4a5ee IL |
169 | }; |
170 | ||
70bad2c7 | 171 | static const struct snd_pcm_hw_constraint_list constraints_16934 = { |
3fe4a5ee IL |
172 | .count = ARRAY_SIZE(rates_16934), |
173 | .list = rates_16934, | |
174 | }; | |
175 | ||
70bad2c7 | 176 | static const unsigned int rates_18432[] = { |
3fe4a5ee IL |
177 | 48000, 96000, |
178 | }; | |
179 | ||
70bad2c7 | 180 | static const struct snd_pcm_hw_constraint_list constraints_18432 = { |
3fe4a5ee IL |
181 | .count = ARRAY_SIZE(rates_18432), |
182 | .list = rates_18432, | |
183 | }; | |
184 | ||
70bad2c7 | 185 | static const unsigned int rates_22579[] = { |
8787041d | 186 | 44100, 88200, 176400 |
3fe4a5ee IL |
187 | }; |
188 | ||
70bad2c7 | 189 | static const struct snd_pcm_hw_constraint_list constraints_22579 = { |
3fe4a5ee IL |
190 | .count = ARRAY_SIZE(rates_22579), |
191 | .list = rates_22579, | |
192 | }; | |
193 | ||
70bad2c7 | 194 | static const unsigned int rates_24576[] = { |
3fe4a5ee IL |
195 | 32000, 48000, 96000, 192000 |
196 | }; | |
197 | ||
70bad2c7 | 198 | static const struct snd_pcm_hw_constraint_list constraints_24576 = { |
3fe4a5ee IL |
199 | .count = ARRAY_SIZE(rates_24576), |
200 | .list = rates_24576, | |
201 | }; | |
202 | ||
70bad2c7 | 203 | static const unsigned int rates_36864[] = { |
8787041d | 204 | 48000, 96000, 192000 |
3fe4a5ee IL |
205 | }; |
206 | ||
70bad2c7 | 207 | static const struct snd_pcm_hw_constraint_list constraints_36864 = { |
3fe4a5ee IL |
208 | .count = ARRAY_SIZE(rates_36864), |
209 | .list = rates_36864, | |
992bee40 IL |
210 | }; |
211 | ||
212 | ||
213 | static int wm8741_startup(struct snd_pcm_substream *substream, | |
214 | struct snd_soc_dai *dai) | |
215 | { | |
216 | struct snd_soc_codec *codec = dai->codec; | |
217 | struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); | |
218 | ||
219 | /* The set of sample rates that can be supported depends on the | |
220 | * MCLK supplied to the CODEC - enforce this. | |
221 | */ | |
222 | if (!wm8741->sysclk) { | |
223 | dev_err(codec->dev, | |
224 | "No MCLK configured, call set_sysclk() on init\n"); | |
225 | return -EINVAL; | |
226 | } | |
227 | ||
228 | snd_pcm_hw_constraint_list(substream->runtime, 0, | |
229 | SNDRV_PCM_HW_PARAM_RATE, | |
3fe4a5ee | 230 | wm8741->sysclk_constraints); |
992bee40 IL |
231 | |
232 | return 0; | |
233 | } | |
234 | ||
235 | static int wm8741_hw_params(struct snd_pcm_substream *substream, | |
236 | struct snd_pcm_hw_params *params, | |
237 | struct snd_soc_dai *dai) | |
238 | { | |
e6968a17 | 239 | struct snd_soc_codec *codec = dai->codec; |
992bee40 IL |
240 | struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); |
241 | u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1FC; | |
242 | int i; | |
243 | ||
244 | /* Find a supported LRCLK ratio */ | |
245 | for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) { | |
246 | if (wm8741->sysclk / params_rate(params) == | |
247 | lrclk_ratios[i].ratio) | |
248 | break; | |
249 | } | |
250 | ||
251 | /* Should never happen, should be handled by constraints */ | |
252 | if (i == ARRAY_SIZE(lrclk_ratios)) { | |
253 | dev_err(codec->dev, "MCLK/fs ratio %d unsupported\n", | |
254 | wm8741->sysclk / params_rate(params)); | |
255 | return -EINVAL; | |
256 | } | |
257 | ||
258 | /* bit size */ | |
34967ad2 MB |
259 | switch (params_width(params)) { |
260 | case 16: | |
992bee40 | 261 | break; |
34967ad2 | 262 | case 20: |
992bee40 IL |
263 | iface |= 0x0001; |
264 | break; | |
34967ad2 | 265 | case 24: |
992bee40 IL |
266 | iface |= 0x0002; |
267 | break; | |
34967ad2 | 268 | case 32: |
992bee40 IL |
269 | iface |= 0x0003; |
270 | break; | |
271 | default: | |
272 | dev_dbg(codec->dev, "wm8741_hw_params: Unsupported bit size param = %d", | |
34967ad2 | 273 | params_width(params)); |
992bee40 IL |
274 | return -EINVAL; |
275 | } | |
276 | ||
277 | dev_dbg(codec->dev, "wm8741_hw_params: bit size param = %d", | |
34967ad2 | 278 | params_width(params)); |
992bee40 IL |
279 | |
280 | snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface); | |
281 | return 0; | |
282 | } | |
283 | ||
284 | static int wm8741_set_dai_sysclk(struct snd_soc_dai *codec_dai, | |
285 | int clk_id, unsigned int freq, int dir) | |
286 | { | |
287 | struct snd_soc_codec *codec = codec_dai->codec; | |
288 | struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); | |
992bee40 IL |
289 | |
290 | dev_dbg(codec->dev, "wm8741_set_dai_sysclk info: freq=%dHz\n", freq); | |
291 | ||
3fe4a5ee IL |
292 | switch (freq) { |
293 | case 11289600: | |
294 | wm8741->sysclk_constraints = &constraints_11289; | |
295 | wm8741->sysclk = freq; | |
296 | return 0; | |
297 | ||
298 | case 12288000: | |
299 | wm8741->sysclk_constraints = &constraints_12288; | |
300 | wm8741->sysclk = freq; | |
301 | return 0; | |
302 | ||
303 | case 16384000: | |
304 | wm8741->sysclk_constraints = &constraints_16384; | |
305 | wm8741->sysclk = freq; | |
306 | return 0; | |
307 | ||
308 | case 16934400: | |
309 | wm8741->sysclk_constraints = &constraints_16934; | |
310 | wm8741->sysclk = freq; | |
311 | return 0; | |
312 | ||
313 | case 18432000: | |
314 | wm8741->sysclk_constraints = &constraints_18432; | |
315 | wm8741->sysclk = freq; | |
316 | return 0; | |
317 | ||
318 | case 22579200: | |
319 | case 33868800: | |
320 | wm8741->sysclk_constraints = &constraints_22579; | |
321 | wm8741->sysclk = freq; | |
322 | return 0; | |
323 | ||
324 | case 24576000: | |
325 | wm8741->sysclk_constraints = &constraints_24576; | |
326 | wm8741->sysclk = freq; | |
327 | return 0; | |
328 | ||
329 | case 36864000: | |
330 | wm8741->sysclk_constraints = &constraints_36864; | |
331 | wm8741->sysclk = freq; | |
332 | return 0; | |
992bee40 | 333 | } |
3fe4a5ee | 334 | return -EINVAL; |
992bee40 IL |
335 | } |
336 | ||
337 | static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai, | |
338 | unsigned int fmt) | |
339 | { | |
340 | struct snd_soc_codec *codec = codec_dai->codec; | |
341 | u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1C3; | |
342 | ||
343 | /* check master/slave audio interface */ | |
344 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | |
345 | case SND_SOC_DAIFMT_CBS_CFS: | |
346 | break; | |
347 | default: | |
348 | return -EINVAL; | |
349 | } | |
350 | ||
351 | /* interface format */ | |
352 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | |
353 | case SND_SOC_DAIFMT_I2S: | |
354 | iface |= 0x0008; | |
355 | break; | |
356 | case SND_SOC_DAIFMT_RIGHT_J: | |
357 | break; | |
358 | case SND_SOC_DAIFMT_LEFT_J: | |
359 | iface |= 0x0004; | |
360 | break; | |
361 | case SND_SOC_DAIFMT_DSP_A: | |
3a340104 | 362 | iface |= 0x000C; |
992bee40 IL |
363 | break; |
364 | case SND_SOC_DAIFMT_DSP_B: | |
3a340104 | 365 | iface |= 0x001C; |
992bee40 IL |
366 | break; |
367 | default: | |
368 | return -EINVAL; | |
369 | } | |
370 | ||
371 | /* clock inversion */ | |
372 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | |
373 | case SND_SOC_DAIFMT_NB_NF: | |
374 | break; | |
375 | case SND_SOC_DAIFMT_IB_IF: | |
376 | iface |= 0x0010; | |
377 | break; | |
378 | case SND_SOC_DAIFMT_IB_NF: | |
379 | iface |= 0x0020; | |
380 | break; | |
381 | case SND_SOC_DAIFMT_NB_IF: | |
382 | iface |= 0x0030; | |
383 | break; | |
384 | default: | |
385 | return -EINVAL; | |
386 | } | |
387 | ||
388 | ||
389 | dev_dbg(codec->dev, "wm8741_set_dai_fmt: Format=%x, Clock Inv=%x\n", | |
390 | fmt & SND_SOC_DAIFMT_FORMAT_MASK, | |
391 | ((fmt & SND_SOC_DAIFMT_INV_MASK))); | |
392 | ||
393 | snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface); | |
394 | return 0; | |
395 | } | |
396 | ||
397 | #define WM8741_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ | |
398 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \ | |
399 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \ | |
400 | SNDRV_PCM_RATE_192000) | |
401 | ||
402 | #define WM8741_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ | |
403 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) | |
404 | ||
85e7652d | 405 | static const struct snd_soc_dai_ops wm8741_dai_ops = { |
992bee40 IL |
406 | .startup = wm8741_startup, |
407 | .hw_params = wm8741_hw_params, | |
408 | .set_sysclk = wm8741_set_dai_sysclk, | |
409 | .set_fmt = wm8741_set_dai_fmt, | |
410 | }; | |
411 | ||
f0fba2ad | 412 | static struct snd_soc_dai_driver wm8741_dai = { |
30e2d368 | 413 | .name = "wm8741", |
992bee40 IL |
414 | .playback = { |
415 | .stream_name = "Playback", | |
c354b54c | 416 | .channels_min = 2, |
992bee40 IL |
417 | .channels_max = 2, |
418 | .rates = WM8741_RATES, | |
419 | .formats = WM8741_FORMATS, | |
420 | }, | |
421 | .ops = &wm8741_dai_ops, | |
422 | }; | |
992bee40 IL |
423 | |
424 | #ifdef CONFIG_PM | |
f0fba2ad | 425 | static int wm8741_resume(struct snd_soc_codec *codec) |
992bee40 | 426 | { |
df3431b7 | 427 | snd_soc_cache_sync(codec); |
992bee40 IL |
428 | return 0; |
429 | } | |
430 | #else | |
992bee40 IL |
431 | #define wm8741_resume NULL |
432 | #endif | |
433 | ||
c354b54c SS |
434 | static int wm8741_configure(struct snd_soc_codec *codec) |
435 | { | |
436 | struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); | |
437 | ||
438 | /* Configure differential mode */ | |
439 | switch (wm8741->pdata.diff_mode) { | |
440 | case WM8741_DIFF_MODE_STEREO: | |
441 | case WM8741_DIFF_MODE_STEREO_REVERSED: | |
442 | case WM8741_DIFF_MODE_MONO_LEFT: | |
443 | case WM8741_DIFF_MODE_MONO_RIGHT: | |
444 | snd_soc_update_bits(codec, WM8741_MODE_CONTROL_2, | |
445 | WM8741_DIFF_MASK, | |
446 | wm8741->pdata.diff_mode << WM8741_DIFF_SHIFT); | |
447 | break; | |
448 | default: | |
449 | return -EINVAL; | |
450 | } | |
451 | ||
452 | /* Change some default settings - latch VU */ | |
453 | snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION, | |
454 | WM8741_UPDATELL, WM8741_UPDATELL); | |
455 | snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION, | |
456 | WM8741_UPDATELM, WM8741_UPDATELM); | |
457 | snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION, | |
458 | WM8741_UPDATERL, WM8741_UPDATERL); | |
459 | snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION, | |
460 | WM8741_UPDATERM, WM8741_UPDATERM); | |
461 | ||
462 | return 0; | |
463 | } | |
464 | ||
465 | static int wm8741_add_controls(struct snd_soc_codec *codec) | |
466 | { | |
467 | struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); | |
468 | ||
469 | switch (wm8741->pdata.diff_mode) { | |
470 | case WM8741_DIFF_MODE_STEREO: | |
471 | case WM8741_DIFF_MODE_STEREO_REVERSED: | |
472 | snd_soc_add_codec_controls(codec, | |
473 | wm8741_snd_controls_stereo, | |
474 | ARRAY_SIZE(wm8741_snd_controls_stereo)); | |
475 | break; | |
476 | case WM8741_DIFF_MODE_MONO_LEFT: | |
477 | snd_soc_add_codec_controls(codec, | |
478 | wm8741_snd_controls_mono_left, | |
479 | ARRAY_SIZE(wm8741_snd_controls_mono_left)); | |
480 | break; | |
481 | case WM8741_DIFF_MODE_MONO_RIGHT: | |
482 | snd_soc_add_codec_controls(codec, | |
483 | wm8741_snd_controls_mono_right, | |
484 | ARRAY_SIZE(wm8741_snd_controls_mono_right)); | |
485 | break; | |
486 | default: | |
487 | return -EINVAL; | |
488 | } | |
489 | ||
490 | return 0; | |
491 | } | |
492 | ||
f0fba2ad | 493 | static int wm8741_probe(struct snd_soc_codec *codec) |
992bee40 | 494 | { |
f0fba2ad | 495 | struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); |
992bee40 | 496 | int ret = 0; |
398575db MB |
497 | |
498 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8741->supplies), | |
499 | wm8741->supplies); | |
500 | if (ret != 0) { | |
501 | dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); | |
502 | goto err_get; | |
503 | } | |
992bee40 | 504 | |
f0fba2ad | 505 | ret = wm8741_reset(codec); |
992bee40 | 506 | if (ret < 0) { |
f0fba2ad | 507 | dev_err(codec->dev, "Failed to issue reset\n"); |
398575db | 508 | goto err_enable; |
992bee40 IL |
509 | } |
510 | ||
c354b54c SS |
511 | ret = wm8741_configure(codec); |
512 | if (ret < 0) { | |
513 | dev_err(codec->dev, "Failed to change default settings\n"); | |
514 | goto err_enable; | |
515 | } | |
516 | ||
517 | ret = wm8741_add_controls(codec); | |
518 | if (ret < 0) { | |
519 | dev_err(codec->dev, "Failed to add controls\n"); | |
520 | goto err_enable; | |
521 | } | |
f0fba2ad | 522 | |
f0fba2ad | 523 | dev_dbg(codec->dev, "Successful registration\n"); |
992bee40 | 524 | return ret; |
398575db MB |
525 | |
526 | err_enable: | |
527 | regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies); | |
528 | err_get: | |
398575db MB |
529 | return ret; |
530 | } | |
531 | ||
532 | static int wm8741_remove(struct snd_soc_codec *codec) | |
533 | { | |
534 | struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); | |
535 | ||
536 | regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies); | |
398575db MB |
537 | |
538 | return 0; | |
992bee40 IL |
539 | } |
540 | ||
f0fba2ad | 541 | static struct snd_soc_codec_driver soc_codec_dev_wm8741 = { |
992bee40 | 542 | .probe = wm8741_probe, |
398575db | 543 | .remove = wm8741_remove, |
992bee40 | 544 | .resume = wm8741_resume, |
0e62780f | 545 | |
0e62780f MB |
546 | .dapm_widgets = wm8741_dapm_widgets, |
547 | .num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets), | |
548 | .dapm_routes = wm8741_dapm_routes, | |
549 | .num_dapm_routes = ARRAY_SIZE(wm8741_dapm_routes), | |
992bee40 | 550 | }; |
992bee40 | 551 | |
80080ec5 MB |
552 | static const struct of_device_id wm8741_of_match[] = { |
553 | { .compatible = "wlf,wm8741", }, | |
554 | { } | |
555 | }; | |
556 | MODULE_DEVICE_TABLE(of, wm8741_of_match); | |
557 | ||
fe98c0cf MB |
558 | static const struct regmap_config wm8741_regmap = { |
559 | .reg_bits = 7, | |
560 | .val_bits = 9, | |
561 | .max_register = WM8741_MAX_REGISTER, | |
562 | ||
563 | .reg_defaults = wm8741_reg_defaults, | |
564 | .num_reg_defaults = ARRAY_SIZE(wm8741_reg_defaults), | |
565 | .cache_type = REGCACHE_RBTREE, | |
566 | ||
567 | .readable_reg = wm8741_readable, | |
568 | }; | |
569 | ||
c354b54c SS |
570 | static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741) |
571 | { | |
572 | const struct wm8741_platform_data *pdata = dev_get_platdata(dev); | |
573 | u32 diff_mode; | |
574 | ||
575 | if (dev->of_node) { | |
576 | if (of_property_read_u32(dev->of_node, "diff-mode", &diff_mode) | |
577 | >= 0) | |
578 | wm8741->pdata.diff_mode = diff_mode; | |
579 | } else { | |
580 | if (pdata != NULL) | |
581 | memcpy(&wm8741->pdata, pdata, sizeof(wm8741->pdata)); | |
582 | } | |
583 | ||
584 | return 0; | |
585 | } | |
586 | ||
26090a83 | 587 | #if IS_ENABLED(CONFIG_I2C) |
f0fba2ad LG |
588 | static int wm8741_i2c_probe(struct i2c_client *i2c, |
589 | const struct i2c_device_id *id) | |
992bee40 | 590 | { |
f0fba2ad | 591 | struct wm8741_priv *wm8741; |
d9780550 | 592 | int ret, i; |
992bee40 | 593 | |
5aefb306 MB |
594 | wm8741 = devm_kzalloc(&i2c->dev, sizeof(struct wm8741_priv), |
595 | GFP_KERNEL); | |
f0fba2ad LG |
596 | if (wm8741 == NULL) |
597 | return -ENOMEM; | |
992bee40 | 598 | |
d9780550 MB |
599 | for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++) |
600 | wm8741->supplies[i].supply = wm8741_supply_names[i]; | |
601 | ||
602 | ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8741->supplies), | |
603 | wm8741->supplies); | |
604 | if (ret != 0) { | |
fe98c0cf MB |
605 | dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); |
606 | return ret; | |
607 | } | |
608 | ||
fd64c455 | 609 | wm8741->regmap = devm_regmap_init_i2c(i2c, &wm8741_regmap); |
fe98c0cf MB |
610 | if (IS_ERR(wm8741->regmap)) { |
611 | ret = PTR_ERR(wm8741->regmap); | |
612 | dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret); | |
613 | return ret; | |
d9780550 MB |
614 | } |
615 | ||
2d52d172 | 616 | ret = wm8741_set_pdata(&i2c->dev, wm8741); |
c354b54c SS |
617 | if (ret != 0) { |
618 | dev_err(&i2c->dev, "Failed to set pdata: %d\n", ret); | |
619 | return ret; | |
620 | } | |
621 | ||
f0fba2ad | 622 | i2c_set_clientdata(i2c, wm8741); |
992bee40 | 623 | |
398575db MB |
624 | ret = snd_soc_register_codec(&i2c->dev, |
625 | &soc_codec_dev_wm8741, &wm8741_dai, 1); | |
992bee40 | 626 | |
992bee40 IL |
627 | return ret; |
628 | } | |
629 | ||
f0fba2ad | 630 | static int wm8741_i2c_remove(struct i2c_client *client) |
992bee40 | 631 | { |
f0fba2ad | 632 | snd_soc_unregister_codec(&client->dev); |
992bee40 IL |
633 | return 0; |
634 | } | |
635 | ||
636 | static const struct i2c_device_id wm8741_i2c_id[] = { | |
637 | { "wm8741", 0 }, | |
638 | { } | |
639 | }; | |
640 | MODULE_DEVICE_TABLE(i2c, wm8741_i2c_id); | |
641 | ||
992bee40 IL |
642 | static struct i2c_driver wm8741_i2c_driver = { |
643 | .driver = { | |
0473e61b | 644 | .name = "wm8741", |
992bee40 | 645 | .owner = THIS_MODULE, |
80080ec5 | 646 | .of_match_table = wm8741_of_match, |
992bee40 IL |
647 | }, |
648 | .probe = wm8741_i2c_probe, | |
f0fba2ad | 649 | .remove = wm8741_i2c_remove, |
992bee40 IL |
650 | .id_table = wm8741_i2c_id, |
651 | }; | |
652 | #endif | |
653 | ||
39e9b8d2 | 654 | #if defined(CONFIG_SPI_MASTER) |
7a79e94e | 655 | static int wm8741_spi_probe(struct spi_device *spi) |
39e9b8d2 MB |
656 | { |
657 | struct wm8741_priv *wm8741; | |
d9780550 | 658 | int ret, i; |
39e9b8d2 | 659 | |
5aefb306 MB |
660 | wm8741 = devm_kzalloc(&spi->dev, sizeof(struct wm8741_priv), |
661 | GFP_KERNEL); | |
39e9b8d2 MB |
662 | if (wm8741 == NULL) |
663 | return -ENOMEM; | |
664 | ||
d9780550 MB |
665 | for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++) |
666 | wm8741->supplies[i].supply = wm8741_supply_names[i]; | |
667 | ||
fe98c0cf | 668 | ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(wm8741->supplies), |
d9780550 MB |
669 | wm8741->supplies); |
670 | if (ret != 0) { | |
671 | dev_err(&spi->dev, "Failed to request supplies: %d\n", ret); | |
fe98c0cf MB |
672 | return ret; |
673 | } | |
674 | ||
fd64c455 | 675 | wm8741->regmap = devm_regmap_init_spi(spi, &wm8741_regmap); |
fe98c0cf MB |
676 | if (IS_ERR(wm8741->regmap)) { |
677 | ret = PTR_ERR(wm8741->regmap); | |
678 | dev_err(&spi->dev, "Failed to init regmap: %d\n", ret); | |
679 | return ret; | |
d9780550 MB |
680 | } |
681 | ||
2d52d172 | 682 | ret = wm8741_set_pdata(&spi->dev, wm8741); |
c354b54c SS |
683 | if (ret != 0) { |
684 | dev_err(&spi->dev, "Failed to set pdata: %d\n", ret); | |
685 | return ret; | |
686 | } | |
687 | ||
39e9b8d2 MB |
688 | spi_set_drvdata(spi, wm8741); |
689 | ||
690 | ret = snd_soc_register_codec(&spi->dev, | |
691 | &soc_codec_dev_wm8741, &wm8741_dai, 1); | |
39e9b8d2 MB |
692 | return ret; |
693 | } | |
694 | ||
7a79e94e | 695 | static int wm8741_spi_remove(struct spi_device *spi) |
39e9b8d2 MB |
696 | { |
697 | snd_soc_unregister_codec(&spi->dev); | |
39e9b8d2 MB |
698 | return 0; |
699 | } | |
700 | ||
701 | static struct spi_driver wm8741_spi_driver = { | |
702 | .driver = { | |
703 | .name = "wm8741", | |
704 | .owner = THIS_MODULE, | |
80080ec5 | 705 | .of_match_table = wm8741_of_match, |
39e9b8d2 MB |
706 | }, |
707 | .probe = wm8741_spi_probe, | |
7a79e94e | 708 | .remove = wm8741_spi_remove, |
39e9b8d2 MB |
709 | }; |
710 | #endif /* CONFIG_SPI_MASTER */ | |
711 | ||
992bee40 IL |
712 | static int __init wm8741_modinit(void) |
713 | { | |
f0fba2ad LG |
714 | int ret = 0; |
715 | ||
26090a83 | 716 | #if IS_ENABLED(CONFIG_I2C) |
992bee40 | 717 | ret = i2c_add_driver(&wm8741_i2c_driver); |
3fe4a5ee | 718 | if (ret != 0) |
f0fba2ad | 719 | pr_err("Failed to register WM8741 I2C driver: %d\n", ret); |
992bee40 | 720 | #endif |
39e9b8d2 MB |
721 | #if defined(CONFIG_SPI_MASTER) |
722 | ret = spi_register_driver(&wm8741_spi_driver); | |
723 | if (ret != 0) { | |
724 | printk(KERN_ERR "Failed to register wm8741 SPI driver: %d\n", | |
725 | ret); | |
726 | } | |
727 | #endif | |
f0fba2ad LG |
728 | |
729 | return ret; | |
992bee40 IL |
730 | } |
731 | module_init(wm8741_modinit); | |
732 | ||
733 | static void __exit wm8741_exit(void) | |
734 | { | |
39e9b8d2 MB |
735 | #if defined(CONFIG_SPI_MASTER) |
736 | spi_unregister_driver(&wm8741_spi_driver); | |
737 | #endif | |
26090a83 | 738 | #if IS_ENABLED(CONFIG_I2C) |
992bee40 IL |
739 | i2c_del_driver(&wm8741_i2c_driver); |
740 | #endif | |
741 | } | |
742 | module_exit(wm8741_exit); | |
743 | ||
744 | MODULE_DESCRIPTION("ASoC WM8741 driver"); | |
745 | MODULE_AUTHOR("Ian Lartey <ian@opensource.wolfsonmicro.com>"); | |
746 | MODULE_LICENSE("GPL"); |