blob: cff34be61f88791403fd3caed1a27a358f4d2095 [file] [log] [blame]
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +01001/*
2 * wm8804.c -- WM8804 S/PDIF transceiver driver
3 *
Mark Brown656baae2012-05-23 12:39:07 +01004 * Copyright 2010-11 Wolfson Microelectronics plc
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +01005 *
6 * Author: Dimitris Papastamos <dp@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>
Charles Keepax7e5ee1c2015-04-07 11:34:50 +010016#include <linux/gpio/consumer.h>
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +010017#include <linux/delay.h>
18#include <linux/pm.h>
Mark Brownd2dd0542011-08-29 14:23:05 +010019#include <linux/of_device.h>
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +010020#include <linux/regulator/consumer.h>
21#include <linux/slab.h>
22#include <sound/core.h>
23#include <sound/pcm.h>
24#include <sound/pcm_params.h>
25#include <sound/soc.h>
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +010026#include <sound/initval.h>
27#include <sound/tlv.h>
Sapthagiri Baratam5631f182015-04-07 12:55:09 +010028#include <sound/soc-dapm.h>
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +010029
30#include "wm8804.h"
31
32#define WM8804_NUM_SUPPLIES 2
33static const char *wm8804_supply_names[WM8804_NUM_SUPPLIES] = {
34 "PVDD",
35 "DVDD"
36};
37
Mark Brown891271c2011-12-29 19:58:06 +000038static const struct reg_default wm8804_reg_defaults[] = {
39 { 3, 0x21 }, /* R3 - PLL1 */
40 { 4, 0xFD }, /* R4 - PLL2 */
41 { 5, 0x36 }, /* R5 - PLL3 */
42 { 6, 0x07 }, /* R6 - PLL4 */
43 { 7, 0x16 }, /* R7 - PLL5 */
44 { 8, 0x18 }, /* R8 - PLL6 */
45 { 9, 0xFF }, /* R9 - SPDMODE */
46 { 10, 0x00 }, /* R10 - INTMASK */
47 { 18, 0x00 }, /* R18 - SPDTX1 */
48 { 19, 0x00 }, /* R19 - SPDTX2 */
49 { 20, 0x00 }, /* R20 - SPDTX3 */
50 { 21, 0x71 }, /* R21 - SPDTX4 */
51 { 22, 0x0B }, /* R22 - SPDTX5 */
52 { 23, 0x70 }, /* R23 - GPO0 */
53 { 24, 0x57 }, /* R24 - GPO1 */
54 { 26, 0x42 }, /* R26 - GPO2 */
55 { 27, 0x06 }, /* R27 - AIFTX */
56 { 28, 0x06 }, /* R28 - AIFRX */
57 { 29, 0x80 }, /* R29 - SPDRX1 */
58 { 30, 0x07 }, /* R30 - PWRDN */
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +010059};
60
61struct wm8804_priv {
Mark Brown891271c2011-12-29 19:58:06 +000062 struct regmap *regmap;
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +010063 struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES];
64 struct notifier_block disable_nb[WM8804_NUM_SUPPLIES];
Daniel Matuschek06109f42014-05-29 15:08:03 +010065 int mclk_div;
Charles Keepax7e5ee1c2015-04-07 11:34:50 +010066
67 struct gpio_desc *reset;
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +010068
Sapthagiri Baratam5631f182015-04-07 12:55:09 +010069 int aif_pwr;
70};
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +010071
72static int txsrc_put(struct snd_kcontrol *kcontrol,
73 struct snd_ctl_elem_value *ucontrol);
74
Sapthagiri Baratam5631f182015-04-07 12:55:09 +010075static int wm8804_aif_event(struct snd_soc_dapm_widget *w,
76 struct snd_kcontrol *kcontrol, int event);
77
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +010078/*
79 * We can't use the same notifier block for more than one supply and
80 * there's no way I can see to get from a callback to the caller
81 * except container_of().
82 */
83#define WM8804_REGULATOR_EVENT(n) \
84static int wm8804_regulator_event_##n(struct notifier_block *nb, \
85 unsigned long event, void *data) \
86{ \
87 struct wm8804_priv *wm8804 = container_of(nb, struct wm8804_priv, \
88 disable_nb[n]); \
89 if (event & REGULATOR_EVENT_DISABLE) { \
Mark Brown891271c2011-12-29 19:58:06 +000090 regcache_mark_dirty(wm8804->regmap); \
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +010091 } \
92 return 0; \
93}
94
95WM8804_REGULATOR_EVENT(0)
96WM8804_REGULATOR_EVENT(1)
97
98static const char *txsrc_text[] = { "S/PDIF RX", "AIF" };
Sapthagiri Baratam5631f182015-04-07 12:55:09 +010099static const SOC_ENUM_SINGLE_DECL(txsrc, WM8804_SPDTX4, 6, txsrc_text);
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100100
Sapthagiri Baratam5631f182015-04-07 12:55:09 +0100101static const struct snd_kcontrol_new wm8804_tx_source_mux[] = {
102 SOC_DAPM_ENUM_EXT("Input Source", txsrc,
103 snd_soc_dapm_get_enum_double, txsrc_put),
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100104};
105
Sapthagiri Baratam5631f182015-04-07 12:55:09 +0100106static const struct snd_soc_dapm_widget wm8804_dapm_widgets[] = {
107SND_SOC_DAPM_OUTPUT("SPDIF Out"),
108SND_SOC_DAPM_INPUT("SPDIF In"),
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100109
Sapthagiri Baratam5631f182015-04-07 12:55:09 +0100110SND_SOC_DAPM_PGA("SPDIFTX", WM8804_PWRDN, 2, 1, NULL, 0),
111SND_SOC_DAPM_PGA("SPDIFRX", WM8804_PWRDN, 1, 1, NULL, 0),
112
113SND_SOC_DAPM_MUX("Tx Source", SND_SOC_NOPM, 6, 0, wm8804_tx_source_mux),
114
115SND_SOC_DAPM_AIF_OUT_E("AIFTX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event,
116 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
117SND_SOC_DAPM_AIF_IN_E("AIFRX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event,
118 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
119};
120
121static const struct snd_soc_dapm_route wm8804_dapm_routes[] = {
122 { "AIFRX", NULL, "Playback" },
123 { "Tx Source", "AIF", "AIFRX" },
124
125 { "SPDIFRX", NULL, "SPDIF In" },
126 { "Tx Source", "S/PDIF RX", "SPDIFRX" },
127
128 { "SPDIFTX", NULL, "Tx Source" },
129 { "SPDIF Out", NULL, "SPDIFTX" },
130
131 { "AIFTX", NULL, "SPDIFRX" },
132 { "Capture", NULL, "AIFTX" },
133};
134
135static int wm8804_aif_event(struct snd_soc_dapm_widget *w,
136 struct snd_kcontrol *kcontrol, int event)
137{
138 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
139 struct wm8804_priv *wm8804 = snd_soc_codec_get_drvdata(codec);
140
141 switch (event) {
142 case SND_SOC_DAPM_POST_PMU:
143 /* power up the aif */
144 if (!wm8804->aif_pwr)
145 snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0x0);
146 wm8804->aif_pwr++;
147 break;
148 case SND_SOC_DAPM_POST_PMD:
149 /* power down only both paths are disabled */
150 wm8804->aif_pwr--;
151 if (!wm8804->aif_pwr)
152 snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0x10);
153 break;
154 }
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100155
156 return 0;
157}
158
159static int txsrc_put(struct snd_kcontrol *kcontrol,
160 struct snd_ctl_elem_value *ucontrol)
161{
Sapthagiri Baratam5631f182015-04-07 12:55:09 +0100162 struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
163 struct snd_soc_dapm_context *dapm = &codec->dapm;
164 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
165 unsigned int val = ucontrol->value.enumerated.item[0] << e->shift_l;
166 unsigned int mask = 1 << e->shift_l;
167 unsigned int txpwr;
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100168
Sapthagiri Baratam5631f182015-04-07 12:55:09 +0100169 if (val != 0 && val != mask)
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100170 return -EINVAL;
171
Sapthagiri Baratam5631f182015-04-07 12:55:09 +0100172 snd_soc_dapm_mutex_lock(dapm);
173
174 if (snd_soc_test_bits(codec, e->reg, mask, val)) {
175 /* save the current power state of the transmitter */
176 txpwr = snd_soc_read(codec, WM8804_PWRDN) & 0x4;
177
178 /* power down the transmitter */
179 snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x4);
180
181 /* set the tx source */
182 snd_soc_update_bits(codec, e->reg, mask, val);
183
184 /* restore the transmitter's configuration */
185 snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, txpwr);
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100186 }
187
Sapthagiri Baratam5631f182015-04-07 12:55:09 +0100188 snd_soc_dapm_mutex_unlock(dapm);
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100189
190 return 0;
191}
192
Mark Brown891271c2011-12-29 19:58:06 +0000193static bool wm8804_volatile(struct device *dev, unsigned int reg)
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100194{
195 switch (reg) {
196 case WM8804_RST_DEVID1:
197 case WM8804_DEVID2:
198 case WM8804_DEVREV:
199 case WM8804_INTSTAT:
200 case WM8804_SPDSTAT:
201 case WM8804_RXCHAN1:
202 case WM8804_RXCHAN2:
203 case WM8804_RXCHAN3:
204 case WM8804_RXCHAN4:
205 case WM8804_RXCHAN5:
Mark Brown891271c2011-12-29 19:58:06 +0000206 return true;
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100207 default:
Mark Brown891271c2011-12-29 19:58:06 +0000208 return false;
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100209 }
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100210}
211
Charles Keepax7e5ee1c2015-04-07 11:34:50 +0100212static int wm8804_soft_reset(struct wm8804_priv *wm8804)
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100213{
Charles Keepax6f2c9342015-02-22 16:43:22 +0000214 return regmap_write(wm8804->regmap, WM8804_RST_DEVID1, 0x0);
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100215}
216
217static int wm8804_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
218{
219 struct snd_soc_codec *codec;
220 u16 format, master, bcp, lrp;
221
222 codec = dai->codec;
223
224 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
225 case SND_SOC_DAIFMT_I2S:
226 format = 0x2;
227 break;
228 case SND_SOC_DAIFMT_RIGHT_J:
229 format = 0x0;
230 break;
231 case SND_SOC_DAIFMT_LEFT_J:
232 format = 0x1;
233 break;
234 case SND_SOC_DAIFMT_DSP_A:
235 case SND_SOC_DAIFMT_DSP_B:
236 format = 0x3;
237 break;
238 default:
239 dev_err(dai->dev, "Unknown dai format\n");
240 return -EINVAL;
241 }
242
243 /* set data format */
244 snd_soc_update_bits(codec, WM8804_AIFTX, 0x3, format);
245 snd_soc_update_bits(codec, WM8804_AIFRX, 0x3, format);
246
247 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
248 case SND_SOC_DAIFMT_CBM_CFM:
249 master = 1;
250 break;
251 case SND_SOC_DAIFMT_CBS_CFS:
252 master = 0;
253 break;
254 default:
255 dev_err(dai->dev, "Unknown master/slave configuration\n");
256 return -EINVAL;
257 }
258
259 /* set master/slave mode */
260 snd_soc_update_bits(codec, WM8804_AIFRX, 0x40, master << 6);
261
262 bcp = lrp = 0;
263 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
264 case SND_SOC_DAIFMT_NB_NF:
265 break;
266 case SND_SOC_DAIFMT_IB_IF:
267 bcp = lrp = 1;
268 break;
269 case SND_SOC_DAIFMT_IB_NF:
270 bcp = 1;
271 break;
272 case SND_SOC_DAIFMT_NB_IF:
273 lrp = 1;
274 break;
275 default:
276 dev_err(dai->dev, "Unknown polarity configuration\n");
277 return -EINVAL;
278 }
279
280 /* set frame inversion */
281 snd_soc_update_bits(codec, WM8804_AIFTX, 0x10 | 0x20,
282 (bcp << 4) | (lrp << 5));
283 snd_soc_update_bits(codec, WM8804_AIFRX, 0x10 | 0x20,
284 (bcp << 4) | (lrp << 5));
285 return 0;
286}
287
288static int wm8804_hw_params(struct snd_pcm_substream *substream,
289 struct snd_pcm_hw_params *params,
290 struct snd_soc_dai *dai)
291{
292 struct snd_soc_codec *codec;
293 u16 blen;
294
295 codec = dai->codec;
296
Mark Brown16cfd482014-07-31 12:52:37 +0100297 switch (params_width(params)) {
298 case 16:
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100299 blen = 0x0;
300 break;
Mark Brown16cfd482014-07-31 12:52:37 +0100301 case 20:
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100302 blen = 0x1;
303 break;
Mark Brown16cfd482014-07-31 12:52:37 +0100304 case 24:
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100305 blen = 0x2;
306 break;
307 default:
308 dev_err(dai->dev, "Unsupported word length: %u\n",
Mark Brown16cfd482014-07-31 12:52:37 +0100309 params_width(params));
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100310 return -EINVAL;
311 }
312
313 /* set word length */
314 snd_soc_update_bits(codec, WM8804_AIFTX, 0xc, blen << 2);
315 snd_soc_update_bits(codec, WM8804_AIFRX, 0xc, blen << 2);
316
317 return 0;
318}
319
320struct pll_div {
321 u32 prescale:1;
322 u32 mclkdiv:1;
323 u32 freqmode:2;
324 u32 n:4;
325 u32 k:22;
326};
327
328/* PLL rate to output rate divisions */
329static struct {
330 unsigned int div;
331 unsigned int freqmode;
332 unsigned int mclkdiv;
333} post_table[] = {
334 { 2, 0, 0 },
335 { 4, 0, 1 },
336 { 4, 1, 0 },
337 { 8, 1, 1 },
338 { 8, 2, 0 },
339 { 16, 2, 1 },
340 { 12, 3, 0 },
341 { 24, 3, 1 }
342};
343
344#define FIXED_PLL_SIZE ((1ULL << 22) * 10)
345static int pll_factors(struct pll_div *pll_div, unsigned int target,
Daniel Matuschek06109f42014-05-29 15:08:03 +0100346 unsigned int source, unsigned int mclk_div)
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100347{
348 u64 Kpart;
349 unsigned long int K, Ndiv, Nmod, tmp;
350 int i;
351
352 /*
353 * Scale the output frequency up; the PLL should run in the
354 * region of 90-100MHz.
355 */
356 for (i = 0; i < ARRAY_SIZE(post_table); i++) {
357 tmp = target * post_table[i].div;
Daniel Matuschek06109f42014-05-29 15:08:03 +0100358 if ((tmp >= 90000000 && tmp <= 100000000) &&
359 (mclk_div == post_table[i].mclkdiv)) {
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100360 pll_div->freqmode = post_table[i].freqmode;
361 pll_div->mclkdiv = post_table[i].mclkdiv;
362 target *= post_table[i].div;
363 break;
364 }
365 }
366
367 if (i == ARRAY_SIZE(post_table)) {
368 pr_err("%s: Unable to scale output frequency: %uHz\n",
369 __func__, target);
370 return -EINVAL;
371 }
372
373 pll_div->prescale = 0;
374 Ndiv = target / source;
375 if (Ndiv < 5) {
376 source >>= 1;
377 pll_div->prescale = 1;
378 Ndiv = target / source;
379 }
380
381 if (Ndiv < 5 || Ndiv > 13) {
382 pr_err("%s: WM8804 N value is not within the recommended range: %lu\n",
383 __func__, Ndiv);
384 return -EINVAL;
385 }
386 pll_div->n = Ndiv;
387
388 Nmod = target % source;
389 Kpart = FIXED_PLL_SIZE * (u64)Nmod;
390
391 do_div(Kpart, source);
392
393 K = Kpart & 0xffffffff;
394 if ((K % 10) >= 5)
395 K += 5;
396 K /= 10;
397 pll_div->k = K;
398
399 return 0;
400}
401
402static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id,
403 int source, unsigned int freq_in,
404 unsigned int freq_out)
405{
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100406 struct snd_soc_codec *codec;
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100407
408 codec = dai->codec;
Dimitris Papastamos86ce6c92010-10-04 09:31:42 +0100409 if (!freq_in || !freq_out) {
410 /* disable the PLL */
Dimitris Papastamos6c20c802010-10-04 09:37:48 +0100411 snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0x1);
Dimitris Papastamos86ce6c92010-10-04 09:31:42 +0100412 return 0;
413 } else {
414 int ret;
415 struct pll_div pll_div;
Daniel Matuschek06109f42014-05-29 15:08:03 +0100416 struct wm8804_priv *wm8804;
Dimitris Papastamos86ce6c92010-10-04 09:31:42 +0100417
Daniel Matuschek06109f42014-05-29 15:08:03 +0100418 wm8804 = snd_soc_codec_get_drvdata(codec);
419
420 ret = pll_factors(&pll_div, freq_out, freq_in,
421 wm8804->mclk_div);
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100422 if (ret)
423 return ret;
Dimitris Papastamos86ce6c92010-10-04 09:31:42 +0100424
425 /* power down the PLL before reprogramming it */
Dimitris Papastamos6c20c802010-10-04 09:37:48 +0100426 snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0x1);
Dimitris Papastamos86ce6c92010-10-04 09:31:42 +0100427
Dimitris Papastamos86ce6c92010-10-04 09:31:42 +0100428 /* set PLLN and PRESCALE */
429 snd_soc_update_bits(codec, WM8804_PLL4, 0xf | 0x10,
430 pll_div.n | (pll_div.prescale << 4));
431 /* set mclkdiv and freqmode */
432 snd_soc_update_bits(codec, WM8804_PLL5, 0x3 | 0x8,
433 pll_div.freqmode | (pll_div.mclkdiv << 3));
434 /* set PLLK */
435 snd_soc_write(codec, WM8804_PLL1, pll_div.k & 0xff);
436 snd_soc_write(codec, WM8804_PLL2, (pll_div.k >> 8) & 0xff);
437 snd_soc_write(codec, WM8804_PLL3, pll_div.k >> 16);
438
439 /* power up the PLL */
Dimitris Papastamos6c20c802010-10-04 09:37:48 +0100440 snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0);
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100441 }
442
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100443 return 0;
444}
445
446static int wm8804_set_sysclk(struct snd_soc_dai *dai,
447 int clk_id, unsigned int freq, int dir)
448{
449 struct snd_soc_codec *codec;
450
451 codec = dai->codec;
452
453 switch (clk_id) {
454 case WM8804_TX_CLKSRC_MCLK:
455 if ((freq >= 10000000 && freq <= 14400000)
456 || (freq >= 16280000 && freq <= 27000000))
457 snd_soc_update_bits(codec, WM8804_PLL6, 0x80, 0x80);
458 else {
459 dev_err(dai->dev, "OSCCLOCK is not within the "
460 "recommended range: %uHz\n", freq);
461 return -EINVAL;
462 }
463 break;
464 case WM8804_TX_CLKSRC_PLL:
465 snd_soc_update_bits(codec, WM8804_PLL6, 0x80, 0);
466 break;
467 case WM8804_CLKOUT_SRC_CLK1:
468 snd_soc_update_bits(codec, WM8804_PLL6, 0x8, 0);
469 break;
470 case WM8804_CLKOUT_SRC_OSCCLK:
471 snd_soc_update_bits(codec, WM8804_PLL6, 0x8, 0x8);
472 break;
473 default:
474 dev_err(dai->dev, "Unknown clock source: %d\n", clk_id);
475 return -EINVAL;
476 }
477
478 return 0;
479}
480
481static int wm8804_set_clkdiv(struct snd_soc_dai *dai,
482 int div_id, int div)
483{
484 struct snd_soc_codec *codec;
Daniel Matuschek06109f42014-05-29 15:08:03 +0100485 struct wm8804_priv *wm8804;
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100486
487 codec = dai->codec;
488 switch (div_id) {
489 case WM8804_CLKOUT_DIV:
490 snd_soc_update_bits(codec, WM8804_PLL5, 0x30,
491 (div & 0x3) << 4);
492 break;
Daniel Matuschek06109f42014-05-29 15:08:03 +0100493 case WM8804_MCLK_DIV:
494 wm8804 = snd_soc_codec_get_drvdata(codec);
495 wm8804->mclk_div = div;
496 break;
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100497 default:
498 dev_err(dai->dev, "Unknown clock divider: %d\n", div_id);
499 return -EINVAL;
500 }
501 return 0;
502}
503
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100504static int wm8804_set_bias_level(struct snd_soc_codec *codec,
505 enum snd_soc_bias_level level)
506{
507 int ret;
508 struct wm8804_priv *wm8804;
509
510 wm8804 = snd_soc_codec_get_drvdata(codec);
511 switch (level) {
512 case SND_SOC_BIAS_ON:
513 break;
514 case SND_SOC_BIAS_PREPARE:
515 /* power up the OSC and the PLL */
516 snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0);
517 break;
518 case SND_SOC_BIAS_STANDBY:
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200519 if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100520 ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies),
521 wm8804->supplies);
522 if (ret) {
523 dev_err(codec->dev,
524 "Failed to enable supplies: %d\n",
525 ret);
526 return ret;
527 }
Mark Brown891271c2011-12-29 19:58:06 +0000528 regcache_sync(wm8804->regmap);
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100529 }
530 /* power down the OSC and the PLL */
531 snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0x9);
532 break;
533 case SND_SOC_BIAS_OFF:
534 /* power down the OSC and the PLL */
535 snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0x9);
536 regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies),
537 wm8804->supplies);
538 break;
539 }
540
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200541 codec->dapm.bias_level = level;
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100542 return 0;
543}
544
Lars-Peter Clausen85e76522011-11-23 11:40:40 +0100545static const struct snd_soc_dai_ops wm8804_dai_ops = {
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100546 .hw_params = wm8804_hw_params,
547 .set_fmt = wm8804_set_fmt,
548 .set_sysclk = wm8804_set_sysclk,
549 .set_clkdiv = wm8804_set_clkdiv,
550 .set_pll = wm8804_set_pll
551};
552
553#define WM8804_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
554 SNDRV_PCM_FMTBIT_S24_LE)
555
Mark Brown3115ae12011-06-08 18:07:49 +0100556#define WM8804_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
557 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
558 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
559 SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
560
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100561static struct snd_soc_dai_driver wm8804_dai = {
Dimitris Papastamoscb13c6b2010-10-01 09:12:14 +0100562 .name = "wm8804-spdif",
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100563 .playback = {
564 .stream_name = "Playback",
565 .channels_min = 2,
566 .channels_max = 2,
Mark Brown3115ae12011-06-08 18:07:49 +0100567 .rates = WM8804_RATES,
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100568 .formats = WM8804_FORMATS,
569 },
570 .capture = {
571 .stream_name = "Capture",
572 .channels_min = 2,
573 .channels_max = 2,
Mark Brown3115ae12011-06-08 18:07:49 +0100574 .rates = WM8804_RATES,
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100575 .formats = WM8804_FORMATS,
576 },
577 .ops = &wm8804_dai_ops,
578 .symmetric_rates = 1
579};
580
Krzysztof Kozlowskic0b36382015-01-05 10:18:26 +0100581static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = {
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100582 .set_bias_level = wm8804_set_bias_level,
Axel Lineb3032f2012-01-27 18:02:09 +0800583 .idle_bias_off = true,
Mark Brown5a374522011-12-08 16:52:19 +0800584
Sapthagiri Baratam5631f182015-04-07 12:55:09 +0100585 .dapm_widgets = wm8804_dapm_widgets,
586 .num_dapm_widgets = ARRAY_SIZE(wm8804_dapm_widgets),
587 .dapm_routes = wm8804_dapm_routes,
588 .num_dapm_routes = ARRAY_SIZE(wm8804_dapm_routes),
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100589};
590
Charles Keepaxbbed2972015-02-22 16:43:21 +0000591const struct regmap_config wm8804_regmap_config = {
Mark Brown891271c2011-12-29 19:58:06 +0000592 .reg_bits = 8,
593 .val_bits = 8,
594
595 .max_register = WM8804_MAX_REGISTER,
596 .volatile_reg = wm8804_volatile,
597
598 .cache_type = REGCACHE_RBTREE,
599 .reg_defaults = wm8804_reg_defaults,
600 .num_reg_defaults = ARRAY_SIZE(wm8804_reg_defaults),
601};
Charles Keepaxbbed2972015-02-22 16:43:21 +0000602EXPORT_SYMBOL_GPL(wm8804_regmap_config);
Mark Brown891271c2011-12-29 19:58:06 +0000603
Charles Keepaxbbed2972015-02-22 16:43:21 +0000604int wm8804_probe(struct device *dev, struct regmap *regmap)
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100605{
606 struct wm8804_priv *wm8804;
Charles Keepax6f2c9342015-02-22 16:43:22 +0000607 unsigned int id1, id2;
608 int i, ret;
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100609
Charles Keepaxbbed2972015-02-22 16:43:21 +0000610 wm8804 = devm_kzalloc(dev, sizeof(*wm8804), GFP_KERNEL);
Dan Carpenterfe3e2e72010-10-09 21:31:31 +0200611 if (!wm8804)
612 return -ENOMEM;
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100613
Charles Keepaxbbed2972015-02-22 16:43:21 +0000614 dev_set_drvdata(dev, wm8804);
Mark Brown891271c2011-12-29 19:58:06 +0000615
Charles Keepaxbbed2972015-02-22 16:43:21 +0000616 wm8804->regmap = regmap;
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100617
Charles Keepax7e5ee1c2015-04-07 11:34:50 +0100618 wm8804->reset = devm_gpiod_get_optional(dev, "wlf,reset",
619 GPIOD_OUT_LOW);
620 if (IS_ERR(wm8804->reset)) {
621 ret = PTR_ERR(wm8804->reset);
622 dev_err(dev, "Failed to get reset line: %d\n", ret);
623 return ret;
624 }
625
Charles Keepax6f2c9342015-02-22 16:43:22 +0000626 for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++)
627 wm8804->supplies[i].supply = wm8804_supply_names[i];
628
629 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8804->supplies),
630 wm8804->supplies);
631 if (ret) {
632 dev_err(dev, "Failed to request supplies: %d\n", ret);
633 return ret;
634 }
635
636 wm8804->disable_nb[0].notifier_call = wm8804_regulator_event_0;
637 wm8804->disable_nb[1].notifier_call = wm8804_regulator_event_1;
638
639 /* This should really be moved into the regulator core */
640 for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) {
Charles Keepax0be96532015-03-05 15:39:21 +0000641 struct regulator *regulator = wm8804->supplies[i].consumer;
642
643 ret = devm_regulator_register_notifier(regulator,
644 &wm8804->disable_nb[i]);
Charles Keepax6f2c9342015-02-22 16:43:22 +0000645 if (ret != 0) {
646 dev_err(dev,
647 "Failed to register regulator notifier: %d\n",
648 ret);
Charles Keepaxfcf638f2015-03-05 15:39:22 +0000649 return ret;
Charles Keepax6f2c9342015-02-22 16:43:22 +0000650 }
651 }
652
653 ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies),
654 wm8804->supplies);
655 if (ret) {
656 dev_err(dev, "Failed to enable supplies: %d\n", ret);
Charles Keepaxfcf638f2015-03-05 15:39:22 +0000657 return ret;
Charles Keepax6f2c9342015-02-22 16:43:22 +0000658 }
659
Charles Keepax7e5ee1c2015-04-07 11:34:50 +0100660 if (wm8804->reset)
661 gpiod_set_value_cansleep(wm8804->reset, 1);
662
Charles Keepax6f2c9342015-02-22 16:43:22 +0000663 ret = regmap_read(regmap, WM8804_RST_DEVID1, &id1);
664 if (ret < 0) {
665 dev_err(dev, "Failed to read device ID: %d\n", ret);
666 goto err_reg_enable;
667 }
668
669 ret = regmap_read(regmap, WM8804_DEVID2, &id2);
670 if (ret < 0) {
671 dev_err(dev, "Failed to read device ID: %d\n", ret);
672 goto err_reg_enable;
673 }
674
675 id2 = (id2 << 8) | id1;
676
677 if (id2 != 0x8805) {
678 dev_err(dev, "Invalid device ID: %#x\n", id2);
679 ret = -EINVAL;
680 goto err_reg_enable;
681 }
682
683 ret = regmap_read(regmap, WM8804_DEVREV, &id1);
684 if (ret < 0) {
685 dev_err(dev, "Failed to read device revision: %d\n",
686 ret);
687 goto err_reg_enable;
688 }
689 dev_info(dev, "revision %c\n", id1 + 'A');
690
Charles Keepax7e5ee1c2015-04-07 11:34:50 +0100691 if (!wm8804->reset) {
692 ret = wm8804_soft_reset(wm8804);
693 if (ret < 0) {
694 dev_err(dev, "Failed to issue reset: %d\n", ret);
695 goto err_reg_enable;
696 }
Charles Keepax6f2c9342015-02-22 16:43:22 +0000697 }
698
Charles Keepaxfcf638f2015-03-05 15:39:22 +0000699 ret = snd_soc_register_codec(dev, &soc_codec_dev_wm8804,
700 &wm8804_dai, 1);
701 if (ret < 0) {
702 dev_err(dev, "Failed to register CODEC: %d\n", ret);
703 goto err_reg_enable;
704 }
705
706 return 0;
Charles Keepax6f2c9342015-02-22 16:43:22 +0000707
708err_reg_enable:
709 regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies);
710 return ret;
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100711}
Charles Keepaxbbed2972015-02-22 16:43:21 +0000712EXPORT_SYMBOL_GPL(wm8804_probe);
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100713
Charles Keepaxbbed2972015-02-22 16:43:21 +0000714void wm8804_remove(struct device *dev)
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100715{
Charles Keepaxbbed2972015-02-22 16:43:21 +0000716 snd_soc_unregister_codec(dev);
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100717}
Charles Keepaxbbed2972015-02-22 16:43:21 +0000718EXPORT_SYMBOL_GPL(wm8804_remove);
Dimitris Papastamos33cf45c2010-09-29 11:14:56 +0100719
720MODULE_DESCRIPTION("ASoC WM8804 driver");
721MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>");
722MODULE_LICENSE("GPL");