1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * wm8731.c -- WM8731 ALSA SoC Audio driver |
4 | * |
5 | * Copyright 2005 Openedhand Ltd. |
6 | * Copyright 2006-12 Wolfson Microelectronics, plc |
7 | * |
8 | * Author: Richard Purdie <richard@openedhand.com> |
9 | * |
10 | * Based on wm8753.c by Liam Girdwood |
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/slab.h> |
19 | #include <linux/regmap.h> |
20 | #include <linux/regulator/consumer.h> |
21 | #include <linux/clk.h> |
22 | #include <sound/core.h> |
23 | #include <sound/pcm.h> |
24 | #include <sound/pcm_params.h> |
25 | #include <sound/soc.h> |
26 | #include <sound/initval.h> |
27 | #include <sound/tlv.h> |
28 | |
29 | #include "wm8731.h" |
30 | |
31 | static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { |
32 | "AVDD" , |
33 | "HPVDD" , |
34 | "DCVDD" , |
35 | "DBVDD" , |
36 | }; |
37 | |
38 | /* |
39 | * wm8731 register cache |
40 | */ |
41 | static const struct reg_default wm8731_reg_defaults[] = { |
42 | { 0, 0x0097 }, |
43 | { 1, 0x0097 }, |
44 | { 2, 0x0079 }, |
45 | { 3, 0x0079 }, |
46 | { 4, 0x000a }, |
47 | { 5, 0x0008 }, |
48 | { 6, 0x009f }, |
49 | { 7, 0x000a }, |
50 | { 8, 0x0000 }, |
51 | { 9, 0x0000 }, |
52 | }; |
53 | |
54 | static bool wm8731_volatile(struct device *dev, unsigned int reg) |
55 | { |
56 | return reg == WM8731_RESET; |
57 | } |
58 | |
59 | #define wm8731_reset(m) regmap_write(m, WM8731_RESET, 0) |
60 | |
61 | static const char *wm8731_input_select[] = {"Line In" , "Mic" }; |
62 | |
63 | static SOC_ENUM_SINGLE_DECL(wm8731_insel_enum, |
64 | WM8731_APANA, 2, wm8731_input_select); |
65 | |
66 | static int wm8731_deemph[] = { 0, 32000, 44100, 48000 }; |
67 | |
68 | static int wm8731_set_deemph(struct snd_soc_component *component) |
69 | { |
70 | struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(c: component); |
71 | int val, i, best; |
72 | |
73 | /* If we're using deemphasis select the nearest available sample |
74 | * rate. |
75 | */ |
76 | if (wm8731->deemph) { |
77 | best = 1; |
78 | for (i = 2; i < ARRAY_SIZE(wm8731_deemph); i++) { |
79 | if (abs(wm8731_deemph[i] - wm8731->playback_fs) < |
80 | abs(wm8731_deemph[best] - wm8731->playback_fs)) |
81 | best = i; |
82 | } |
83 | |
84 | val = best << 1; |
85 | } else { |
86 | best = 0; |
87 | val = 0; |
88 | } |
89 | |
90 | dev_dbg(component->dev, "Set deemphasis %d (%dHz)\n" , |
91 | best, wm8731_deemph[best]); |
92 | |
93 | return snd_soc_component_update_bits(component, WM8731_APDIGI, mask: 0x6, val); |
94 | } |
95 | |
96 | static int wm8731_get_deemph(struct snd_kcontrol *kcontrol, |
97 | struct snd_ctl_elem_value *ucontrol) |
98 | { |
99 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
100 | struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(c: component); |
101 | |
102 | ucontrol->value.integer.value[0] = wm8731->deemph; |
103 | |
104 | return 0; |
105 | } |
106 | |
107 | static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, |
108 | struct snd_ctl_elem_value *ucontrol) |
109 | { |
110 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
111 | struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(c: component); |
112 | unsigned int deemph = ucontrol->value.integer.value[0]; |
113 | int ret = 0; |
114 | |
115 | if (deemph > 1) |
116 | return -EINVAL; |
117 | |
118 | mutex_lock(&wm8731->lock); |
119 | if (wm8731->deemph != deemph) { |
120 | wm8731->deemph = deemph; |
121 | |
122 | wm8731_set_deemph(component); |
123 | |
124 | ret = 1; |
125 | } |
126 | mutex_unlock(lock: &wm8731->lock); |
127 | |
128 | return ret; |
129 | } |
130 | |
131 | static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); |
132 | static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0); |
133 | static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); |
134 | static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 2000, 0); |
135 | |
136 | static const struct snd_kcontrol_new wm8731_snd_controls[] = { |
137 | |
138 | SOC_DOUBLE_R_TLV("Master Playback Volume" , WM8731_LOUT1V, WM8731_ROUT1V, |
139 | 0, 127, 0, out_tlv), |
140 | SOC_DOUBLE_R("Master Playback ZC Switch" , WM8731_LOUT1V, WM8731_ROUT1V, |
141 | 7, 1, 0), |
142 | |
143 | SOC_DOUBLE_R_TLV("Capture Volume" , WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0, |
144 | in_tlv), |
145 | SOC_DOUBLE_R("Line Capture Switch" , WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1), |
146 | |
147 | SOC_SINGLE_TLV("Mic Boost Volume" , WM8731_APANA, 0, 1, 0, mic_tlv), |
148 | SOC_SINGLE("Mic Capture Switch" , WM8731_APANA, 1, 1, 1), |
149 | |
150 | SOC_SINGLE_TLV("Sidetone Playback Volume" , WM8731_APANA, 6, 3, 1, |
151 | sidetone_tlv), |
152 | |
153 | SOC_SINGLE("ADC High Pass Filter Switch" , WM8731_APDIGI, 0, 1, 1), |
154 | SOC_SINGLE("Store DC Offset Switch" , WM8731_APDIGI, 4, 1, 0), |
155 | |
156 | SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch" , 0, |
157 | wm8731_get_deemph, wm8731_put_deemph), |
158 | }; |
159 | |
160 | /* Output Mixer */ |
161 | static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = { |
162 | SOC_DAPM_SINGLE("Line Bypass Switch" , WM8731_APANA, 3, 1, 0), |
163 | SOC_DAPM_SINGLE("Mic Sidetone Switch" , WM8731_APANA, 5, 1, 0), |
164 | SOC_DAPM_SINGLE("HiFi Playback Switch" , WM8731_APANA, 4, 1, 0), |
165 | }; |
166 | |
167 | /* Input mux */ |
168 | static const struct snd_kcontrol_new wm8731_input_mux_controls = |
169 | SOC_DAPM_ENUM("Input Select" , wm8731_insel_enum); |
170 | |
171 | static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { |
172 | SND_SOC_DAPM_SUPPLY("ACTIVE" ,WM8731_ACTIVE, 0, 0, NULL, 0), |
173 | SND_SOC_DAPM_SUPPLY("OSC" , WM8731_PWR, 5, 1, NULL, 0), |
174 | SND_SOC_DAPM_MIXER("Output Mixer" , WM8731_PWR, 4, 1, |
175 | &wm8731_output_mixer_controls[0], |
176 | ARRAY_SIZE(wm8731_output_mixer_controls)), |
177 | SND_SOC_DAPM_DAC("DAC" , "HiFi Playback" , WM8731_PWR, 3, 1), |
178 | SND_SOC_DAPM_OUTPUT("LOUT" ), |
179 | SND_SOC_DAPM_OUTPUT("LHPOUT" ), |
180 | SND_SOC_DAPM_OUTPUT("ROUT" ), |
181 | SND_SOC_DAPM_OUTPUT("RHPOUT" ), |
182 | SND_SOC_DAPM_ADC("ADC" , "HiFi Capture" , WM8731_PWR, 2, 1), |
183 | SND_SOC_DAPM_MUX("Input Mux" , SND_SOC_NOPM, 0, 0, &wm8731_input_mux_controls), |
184 | SND_SOC_DAPM_PGA("Line Input" , WM8731_PWR, 0, 1, NULL, 0), |
185 | SND_SOC_DAPM_MICBIAS("Mic Bias" , WM8731_PWR, 1, 1), |
186 | SND_SOC_DAPM_INPUT("MICIN" ), |
187 | SND_SOC_DAPM_INPUT("RLINEIN" ), |
188 | SND_SOC_DAPM_INPUT("LLINEIN" ), |
189 | }; |
190 | |
191 | static int wm8731_check_osc(struct snd_soc_dapm_widget *source, |
192 | struct snd_soc_dapm_widget *sink) |
193 | { |
194 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: source->dapm); |
195 | struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(c: component); |
196 | |
197 | return wm8731->sysclk_type == WM8731_SYSCLK_XTAL; |
198 | } |
199 | |
200 | static const struct snd_soc_dapm_route wm8731_intercon[] = { |
201 | {"DAC" , NULL, "OSC" , wm8731_check_osc}, |
202 | {"ADC" , NULL, "OSC" , wm8731_check_osc}, |
203 | {"DAC" , NULL, "ACTIVE" }, |
204 | {"ADC" , NULL, "ACTIVE" }, |
205 | |
206 | /* output mixer */ |
207 | {"Output Mixer" , "Line Bypass Switch" , "Line Input" }, |
208 | {"Output Mixer" , "HiFi Playback Switch" , "DAC" }, |
209 | {"Output Mixer" , "Mic Sidetone Switch" , "Mic Bias" }, |
210 | |
211 | /* outputs */ |
212 | {"RHPOUT" , NULL, "Output Mixer" }, |
213 | {"ROUT" , NULL, "Output Mixer" }, |
214 | {"LHPOUT" , NULL, "Output Mixer" }, |
215 | {"LOUT" , NULL, "Output Mixer" }, |
216 | |
217 | /* input mux */ |
218 | {"Input Mux" , "Line In" , "Line Input" }, |
219 | {"Input Mux" , "Mic" , "Mic Bias" }, |
220 | {"ADC" , NULL, "Input Mux" }, |
221 | |
222 | /* inputs */ |
223 | {"Line Input" , NULL, "LLINEIN" }, |
224 | {"Line Input" , NULL, "RLINEIN" }, |
225 | {"Mic Bias" , NULL, "MICIN" }, |
226 | }; |
227 | |
228 | struct _coeff_div { |
229 | u32 mclk; |
230 | u32 rate; |
231 | u16 fs; |
232 | u8 sr:4; |
233 | u8 bosr:1; |
234 | u8 usb:1; |
235 | }; |
236 | |
237 | /* codec mclk clock divider coefficients */ |
238 | static const struct _coeff_div coeff_div[] = { |
239 | /* 48k */ |
240 | {12288000, 48000, 256, 0x0, 0x0, 0x0}, |
241 | {18432000, 48000, 384, 0x0, 0x1, 0x0}, |
242 | {12000000, 48000, 250, 0x0, 0x0, 0x1}, |
243 | |
244 | /* 32k */ |
245 | {12288000, 32000, 384, 0x6, 0x0, 0x0}, |
246 | {18432000, 32000, 576, 0x6, 0x1, 0x0}, |
247 | {12000000, 32000, 375, 0x6, 0x0, 0x1}, |
248 | |
249 | /* 8k */ |
250 | {12288000, 8000, 1536, 0x3, 0x0, 0x0}, |
251 | {18432000, 8000, 2304, 0x3, 0x1, 0x0}, |
252 | {11289600, 8000, 1408, 0xb, 0x0, 0x0}, |
253 | {16934400, 8000, 2112, 0xb, 0x1, 0x0}, |
254 | {12000000, 8000, 1500, 0x3, 0x0, 0x1}, |
255 | |
256 | /* 96k */ |
257 | {12288000, 96000, 128, 0x7, 0x0, 0x0}, |
258 | {18432000, 96000, 192, 0x7, 0x1, 0x0}, |
259 | {12000000, 96000, 125, 0x7, 0x0, 0x1}, |
260 | |
261 | /* 44.1k */ |
262 | {11289600, 44100, 256, 0x8, 0x0, 0x0}, |
263 | {16934400, 44100, 384, 0x8, 0x1, 0x0}, |
264 | {12000000, 44100, 272, 0x8, 0x1, 0x1}, |
265 | |
266 | /* 88.2k */ |
267 | {11289600, 88200, 128, 0xf, 0x0, 0x0}, |
268 | {16934400, 88200, 192, 0xf, 0x1, 0x0}, |
269 | {12000000, 88200, 136, 0xf, 0x1, 0x1}, |
270 | }; |
271 | |
272 | /* rates constraints */ |
273 | static const unsigned int wm8731_rates_12000000[] = { |
274 | 8000, 32000, 44100, 48000, 96000, 88200, |
275 | }; |
276 | |
277 | static const unsigned int wm8731_rates_12288000_18432000[] = { |
278 | 8000, 32000, 48000, 96000, |
279 | }; |
280 | |
281 | static const unsigned int wm8731_rates_11289600_16934400[] = { |
282 | 8000, 44100, 88200, |
283 | }; |
284 | |
285 | static const struct snd_pcm_hw_constraint_list wm8731_constraints_12000000 = { |
286 | .list = wm8731_rates_12000000, |
287 | .count = ARRAY_SIZE(wm8731_rates_12000000), |
288 | }; |
289 | |
290 | static const |
291 | struct snd_pcm_hw_constraint_list wm8731_constraints_12288000_18432000 = { |
292 | .list = wm8731_rates_12288000_18432000, |
293 | .count = ARRAY_SIZE(wm8731_rates_12288000_18432000), |
294 | }; |
295 | |
296 | static const |
297 | struct snd_pcm_hw_constraint_list wm8731_constraints_11289600_16934400 = { |
298 | .list = wm8731_rates_11289600_16934400, |
299 | .count = ARRAY_SIZE(wm8731_rates_11289600_16934400), |
300 | }; |
301 | |
302 | static inline int get_coeff(int mclk, int rate) |
303 | { |
304 | int i; |
305 | |
306 | for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { |
307 | if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) |
308 | return i; |
309 | } |
310 | return 0; |
311 | } |
312 | |
313 | static int wm8731_hw_params(struct snd_pcm_substream *substream, |
314 | struct snd_pcm_hw_params *params, |
315 | struct snd_soc_dai *dai) |
316 | { |
317 | struct snd_soc_component *component = dai->component; |
318 | struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(c: component); |
319 | u16 iface = snd_soc_component_read(component, WM8731_IFACE) & 0xfff3; |
320 | int i = get_coeff(mclk: wm8731->sysclk, rate: params_rate(p: params)); |
321 | u16 srate = (coeff_div[i].sr << 2) | |
322 | (coeff_div[i].bosr << 1) | coeff_div[i].usb; |
323 | |
324 | wm8731->playback_fs = params_rate(p: params); |
325 | |
326 | snd_soc_component_write(component, WM8731_SRATE, val: srate); |
327 | |
328 | /* bit size */ |
329 | switch (params_width(p: params)) { |
330 | case 16: |
331 | break; |
332 | case 20: |
333 | iface |= 0x0004; |
334 | break; |
335 | case 24: |
336 | iface |= 0x0008; |
337 | break; |
338 | case 32: |
339 | iface |= 0x000c; |
340 | break; |
341 | } |
342 | |
343 | wm8731_set_deemph(component); |
344 | |
345 | snd_soc_component_write(component, WM8731_IFACE, val: iface); |
346 | return 0; |
347 | } |
348 | |
349 | static int wm8731_mute(struct snd_soc_dai *dai, int mute, int direction) |
350 | { |
351 | struct snd_soc_component *component = dai->component; |
352 | u16 mute_reg = snd_soc_component_read(component, WM8731_APDIGI) & 0xfff7; |
353 | |
354 | if (mute) |
355 | snd_soc_component_write(component, WM8731_APDIGI, val: mute_reg | 0x8); |
356 | else |
357 | snd_soc_component_write(component, WM8731_APDIGI, val: mute_reg); |
358 | return 0; |
359 | } |
360 | |
361 | static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai, |
362 | int clk_id, unsigned int freq, int dir) |
363 | { |
364 | struct snd_soc_component *component = codec_dai->component; |
365 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); |
366 | struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(c: component); |
367 | |
368 | switch (clk_id) { |
369 | case WM8731_SYSCLK_XTAL: |
370 | case WM8731_SYSCLK_MCLK: |
371 | if (wm8731->mclk && clk_set_rate(clk: wm8731->mclk, rate: freq)) |
372 | return -EINVAL; |
373 | wm8731->sysclk_type = clk_id; |
374 | break; |
375 | default: |
376 | return -EINVAL; |
377 | } |
378 | |
379 | switch (freq) { |
380 | case 0: |
381 | wm8731->constraints = NULL; |
382 | break; |
383 | case 12000000: |
384 | wm8731->constraints = &wm8731_constraints_12000000; |
385 | break; |
386 | case 12288000: |
387 | case 18432000: |
388 | wm8731->constraints = &wm8731_constraints_12288000_18432000; |
389 | break; |
390 | case 16934400: |
391 | case 11289600: |
392 | wm8731->constraints = &wm8731_constraints_11289600_16934400; |
393 | break; |
394 | default: |
395 | return -EINVAL; |
396 | } |
397 | |
398 | wm8731->sysclk = freq; |
399 | |
400 | snd_soc_dapm_sync(dapm); |
401 | |
402 | return 0; |
403 | } |
404 | |
405 | |
406 | static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai, |
407 | unsigned int fmt) |
408 | { |
409 | struct snd_soc_component *component = codec_dai->component; |
410 | u16 iface = 0; |
411 | |
412 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
413 | case SND_SOC_DAIFMT_CBP_CFP: |
414 | iface |= 0x0040; |
415 | break; |
416 | case SND_SOC_DAIFMT_CBC_CFC: |
417 | break; |
418 | default: |
419 | return -EINVAL; |
420 | } |
421 | |
422 | /* interface format */ |
423 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
424 | case SND_SOC_DAIFMT_I2S: |
425 | iface |= 0x0002; |
426 | break; |
427 | case SND_SOC_DAIFMT_RIGHT_J: |
428 | break; |
429 | case SND_SOC_DAIFMT_LEFT_J: |
430 | iface |= 0x0001; |
431 | break; |
432 | case SND_SOC_DAIFMT_DSP_A: |
433 | iface |= 0x0013; |
434 | break; |
435 | case SND_SOC_DAIFMT_DSP_B: |
436 | iface |= 0x0003; |
437 | break; |
438 | default: |
439 | return -EINVAL; |
440 | } |
441 | |
442 | /* clock inversion */ |
443 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
444 | case SND_SOC_DAIFMT_NB_NF: |
445 | break; |
446 | case SND_SOC_DAIFMT_IB_IF: |
447 | iface |= 0x0090; |
448 | break; |
449 | case SND_SOC_DAIFMT_IB_NF: |
450 | iface |= 0x0080; |
451 | break; |
452 | case SND_SOC_DAIFMT_NB_IF: |
453 | iface |= 0x0010; |
454 | break; |
455 | default: |
456 | return -EINVAL; |
457 | } |
458 | |
459 | /* set iface */ |
460 | snd_soc_component_write(component, WM8731_IFACE, val: iface); |
461 | return 0; |
462 | } |
463 | |
464 | static int wm8731_set_bias_level(struct snd_soc_component *component, |
465 | enum snd_soc_bias_level level) |
466 | { |
467 | struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(c: component); |
468 | int ret; |
469 | u16 reg; |
470 | |
471 | switch (level) { |
472 | case SND_SOC_BIAS_ON: |
473 | if (wm8731->mclk) { |
474 | ret = clk_prepare_enable(clk: wm8731->mclk); |
475 | if (ret) |
476 | return ret; |
477 | } |
478 | break; |
479 | case SND_SOC_BIAS_PREPARE: |
480 | break; |
481 | case SND_SOC_BIAS_STANDBY: |
482 | if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { |
483 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), |
484 | consumers: wm8731->supplies); |
485 | if (ret != 0) |
486 | return ret; |
487 | |
488 | regcache_sync(map: wm8731->regmap); |
489 | } |
490 | |
491 | /* Clear PWROFF, gate CLKOUT, everything else as-is */ |
492 | reg = snd_soc_component_read(component, WM8731_PWR) & 0xff7f; |
493 | snd_soc_component_write(component, WM8731_PWR, val: reg | 0x0040); |
494 | break; |
495 | case SND_SOC_BIAS_OFF: |
496 | if (wm8731->mclk) |
497 | clk_disable_unprepare(clk: wm8731->mclk); |
498 | snd_soc_component_write(component, WM8731_PWR, val: 0xffff); |
499 | regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), |
500 | consumers: wm8731->supplies); |
501 | regcache_mark_dirty(map: wm8731->regmap); |
502 | break; |
503 | } |
504 | return 0; |
505 | } |
506 | |
507 | static int wm8731_startup(struct snd_pcm_substream *substream, |
508 | struct snd_soc_dai *dai) |
509 | { |
510 | struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(c: dai->component); |
511 | |
512 | if (wm8731->constraints) |
513 | snd_pcm_hw_constraint_list(runtime: substream->runtime, cond: 0, |
514 | SNDRV_PCM_HW_PARAM_RATE, |
515 | l: wm8731->constraints); |
516 | |
517 | return 0; |
518 | } |
519 | |
520 | #define WM8731_RATES SNDRV_PCM_RATE_8000_96000 |
521 | |
522 | #define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ |
523 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) |
524 | |
525 | static const struct snd_soc_dai_ops wm8731_dai_ops = { |
526 | .startup = wm8731_startup, |
527 | .hw_params = wm8731_hw_params, |
528 | .mute_stream = wm8731_mute, |
529 | .set_sysclk = wm8731_set_dai_sysclk, |
530 | .set_fmt = wm8731_set_dai_fmt, |
531 | .no_capture_mute = 1, |
532 | }; |
533 | |
534 | static struct snd_soc_dai_driver wm8731_dai = { |
535 | .name = "wm8731-hifi" , |
536 | .playback = { |
537 | .stream_name = "Playback" , |
538 | .channels_min = 1, |
539 | .channels_max = 2, |
540 | .rates = WM8731_RATES, |
541 | .formats = WM8731_FORMATS,}, |
542 | .capture = { |
543 | .stream_name = "Capture" , |
544 | .channels_min = 1, |
545 | .channels_max = 2, |
546 | .rates = WM8731_RATES, |
547 | .formats = WM8731_FORMATS,}, |
548 | .ops = &wm8731_dai_ops, |
549 | .symmetric_rate = 1, |
550 | }; |
551 | |
552 | static const struct snd_soc_component_driver soc_component_dev_wm8731 = { |
553 | .set_bias_level = wm8731_set_bias_level, |
554 | .controls = wm8731_snd_controls, |
555 | .num_controls = ARRAY_SIZE(wm8731_snd_controls), |
556 | .dapm_widgets = wm8731_dapm_widgets, |
557 | .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets), |
558 | .dapm_routes = wm8731_intercon, |
559 | .num_dapm_routes = ARRAY_SIZE(wm8731_intercon), |
560 | .suspend_bias_off = 1, |
561 | .idle_bias_on = 1, |
562 | .use_pmdown_time = 1, |
563 | .endianness = 1, |
564 | }; |
565 | |
566 | int wm8731_init(struct device *dev, struct wm8731_priv *wm8731) |
567 | { |
568 | int ret = 0, i; |
569 | |
570 | wm8731->mclk = devm_clk_get(dev, id: "mclk" ); |
571 | if (IS_ERR(ptr: wm8731->mclk)) { |
572 | ret = PTR_ERR(ptr: wm8731->mclk); |
573 | if (ret == -ENOENT) { |
574 | wm8731->mclk = NULL; |
575 | dev_warn(dev, "Assuming static MCLK\n" ); |
576 | } else { |
577 | dev_err(dev, "Failed to get MCLK: %d\n" , ret); |
578 | return ret; |
579 | } |
580 | } |
581 | |
582 | mutex_init(&wm8731->lock); |
583 | |
584 | for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++) |
585 | wm8731->supplies[i].supply = wm8731_supply_names[i]; |
586 | |
587 | ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8731->supplies), |
588 | consumers: wm8731->supplies); |
589 | if (ret != 0) { |
590 | dev_err(dev, "Failed to request supplies: %d\n" , ret); |
591 | return ret; |
592 | } |
593 | |
594 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), |
595 | consumers: wm8731->supplies); |
596 | if (ret != 0) { |
597 | dev_err(dev, "Failed to enable supplies: %d\n" , ret); |
598 | return ret; |
599 | } |
600 | |
601 | ret = wm8731_reset(wm8731->regmap); |
602 | if (ret < 0) { |
603 | dev_err(dev, "Failed to issue reset: %d\n" , ret); |
604 | goto err_regulator_enable; |
605 | } |
606 | |
607 | /* Clear POWEROFF, keep everything else disabled */ |
608 | regmap_write(map: wm8731->regmap, WM8731_PWR, val: 0x7f); |
609 | |
610 | /* Latch the update bits */ |
611 | regmap_update_bits(map: wm8731->regmap, WM8731_LOUT1V, mask: 0x100, val: 0); |
612 | regmap_update_bits(map: wm8731->regmap, WM8731_ROUT1V, mask: 0x100, val: 0); |
613 | regmap_update_bits(map: wm8731->regmap, WM8731_LINVOL, mask: 0x100, val: 0); |
614 | regmap_update_bits(map: wm8731->regmap, WM8731_RINVOL, mask: 0x100, val: 0); |
615 | |
616 | /* Disable bypass path by default */ |
617 | regmap_update_bits(map: wm8731->regmap, WM8731_APANA, mask: 0x8, val: 0); |
618 | |
619 | regcache_mark_dirty(map: wm8731->regmap); |
620 | |
621 | ret = devm_snd_soc_register_component(dev, |
622 | component_driver: &soc_component_dev_wm8731, dai_drv: &wm8731_dai, num_dai: 1); |
623 | if (ret != 0) { |
624 | dev_err(dev, "Failed to register CODEC: %d\n" , ret); |
625 | goto err_regulator_enable; |
626 | } |
627 | |
628 | return 0; |
629 | |
630 | err_regulator_enable: |
631 | /* Regulators will be enabled by bias management */ |
632 | regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), consumers: wm8731->supplies); |
633 | |
634 | return ret; |
635 | } |
636 | EXPORT_SYMBOL_GPL(wm8731_init); |
637 | |
638 | const struct regmap_config wm8731_regmap = { |
639 | .reg_bits = 7, |
640 | .val_bits = 9, |
641 | |
642 | .max_register = WM8731_RESET, |
643 | .volatile_reg = wm8731_volatile, |
644 | |
645 | .cache_type = REGCACHE_MAPLE, |
646 | .reg_defaults = wm8731_reg_defaults, |
647 | .num_reg_defaults = ARRAY_SIZE(wm8731_reg_defaults), |
648 | }; |
649 | EXPORT_SYMBOL_GPL(wm8731_regmap); |
650 | |
651 | MODULE_DESCRIPTION("ASoC WM8731 driver" ); |
652 | MODULE_AUTHOR("Richard Purdie" ); |
653 | MODULE_LICENSE("GPL" ); |
654 | |