]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - sound/soc/codecs/wm8974.c
ASoC: Refresh WM8974 PLL configuration
[mirror_ubuntu-bionic-kernel.git] / sound / soc / codecs / wm8974.c
CommitLineData
0a1bf553
MB
1/*
2 * wm8974.c -- WM8974 ALSA Soc Audio driver
3 *
4 * Copyright 2006 Wolfson Microelectronics PLC.
5 *
4fcbbb67 6 * Author: Liam Girdwood <linux@wolfsonmicro.com>
0a1bf553
MB
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/version.h>
16#include <linux/kernel.h>
17#include <linux/init.h>
18#include <linux/delay.h>
19#include <linux/pm.h>
20#include <linux/i2c.h>
21#include <linux/platform_device.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/soc-dapm.h>
27#include <sound/initval.h>
28
29#include "wm8974.h"
30
0a1bf553 31static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
1a55b3f6
MB
32 0x0000, 0x0000, 0x0000, 0x0000,
33 0x0050, 0x0000, 0x0140, 0x0000,
34 0x0000, 0x0000, 0x0000, 0x00ff,
35 0x0000, 0x0000, 0x0100, 0x00ff,
36 0x0000, 0x0000, 0x012c, 0x002c,
37 0x002c, 0x002c, 0x002c, 0x0000,
38 0x0032, 0x0000, 0x0000, 0x0000,
39 0x0000, 0x0000, 0x0000, 0x0000,
40 0x0038, 0x000b, 0x0032, 0x0000,
41 0x0008, 0x000c, 0x0093, 0x00e9,
42 0x0000, 0x0000, 0x0000, 0x0000,
43 0x0003, 0x0010, 0x0000, 0x0000,
44 0x0000, 0x0002, 0x0000, 0x0000,
45 0x0000, 0x0000, 0x0039, 0x0000,
46 0x0000,
0a1bf553
MB
47};
48
df1ef7a3
MB
49#define WM8974_POWER1_BIASEN 0x08
50#define WM8974_POWER1_BUFIOEN 0x10
51
4fcbbb67
MB
52struct wm8974_priv {
53 struct snd_soc_codec codec;
54 u16 reg_cache[WM8974_CACHEREGNUM];
55};
56
57static struct snd_soc_codec *wm8974_codec;
58
0a1bf553
MB
59/*
60 * read wm8974 register cache
61 */
1a55b3f6 62static inline unsigned int wm8974_read_reg_cache(struct snd_soc_codec *codec,
0a1bf553
MB
63 unsigned int reg)
64{
65 u16 *cache = codec->reg_cache;
66 if (reg == WM8974_RESET)
67 return 0;
68 if (reg >= WM8974_CACHEREGNUM)
69 return -1;
70 return cache[reg];
71}
72
73/*
74 * write wm8974 register cache
75 */
76static inline void wm8974_write_reg_cache(struct snd_soc_codec *codec,
77 u16 reg, unsigned int value)
78{
79 u16 *cache = codec->reg_cache;
80 if (reg >= WM8974_CACHEREGNUM)
81 return;
82 cache[reg] = value;
83}
84
85/*
86 * write to the WM8974 register space
87 */
88static int wm8974_write(struct snd_soc_codec *codec, unsigned int reg,
89 unsigned int value)
90{
91 u8 data[2];
92
93 /* data is
94 * D15..D9 WM8974 register offset
95 * D8...D0 register data
96 */
97 data[0] = (reg << 1) | ((value >> 8) & 0x0001);
98 data[1] = value & 0x00ff;
99
1a55b3f6 100 wm8974_write_reg_cache(codec, reg, value);
0a1bf553
MB
101 if (codec->hw_write(codec->control_data, data, 2) == 2)
102 return 0;
103 else
104 return -EIO;
105}
106
107#define wm8974_reset(c) wm8974_write(c, WM8974_RESET, 0)
108
109static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
110static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
111static const char *wm8974_eqmode[] = {"Capture", "Playback" };
112static const char *wm8974_bw[] = {"Narrow", "Wide" };
113static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
114static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
115static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
116static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
117static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
118static const char *wm8974_alc[] = {"ALC", "Limiter" };
119
120static const struct soc_enum wm8974_enum[] = {
121 SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
122 SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
123 SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp),
124 SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode),
125
126 SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1),
127 SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw),
128 SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2),
129 SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw),
130
131 SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3),
132 SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw),
133 SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4),
134 SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw),
135
136 SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5),
137 SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc),
138};
139
140static const struct snd_kcontrol_new wm8974_snd_controls[] = {
141
142SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
143
144SOC_ENUM("DAC Companding", wm8974_enum[1]),
145SOC_ENUM("ADC Companding", wm8974_enum[0]),
146
147SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
148SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
149
150SOC_SINGLE("PCM Volume", WM8974_DACVOL, 0, 127, 0),
151
152SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
153SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
154SOC_SINGLE("ADC Inversion Switch", WM8974_COMP, 0, 1, 0),
155
156SOC_SINGLE("Capture Volume", WM8974_ADCVOL, 0, 127, 0),
157
158SOC_ENUM("Equaliser Function", wm8974_enum[3]),
159SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
160SOC_SINGLE("EQ1 Volume", WM8974_EQ1, 0, 31, 1),
161
162SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
163SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
164SOC_SINGLE("EQ2 Volume", WM8974_EQ2, 0, 31, 1),
165
166SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
167SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
168SOC_SINGLE("EQ3 Volume", WM8974_EQ3, 0, 31, 1),
169
170SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
171SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
172SOC_SINGLE("EQ4 Volume", WM8974_EQ4, 0, 31, 1),
173
174SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
175SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
176SOC_SINGLE("EQ5 Volume", WM8974_EQ5, 0, 31, 1),
177
178SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0),
179SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0),
180SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0),
181
182SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0),
183SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0),
184
185SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0),
186SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0),
187SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0),
188
189SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0),
190SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0),
191SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0),
192
193SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
194SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0),
195SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0),
196
197SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0),
198SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0),
199
200SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0),
201SOC_SINGLE("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0),
202
203SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0),
204SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1),
205SOC_SINGLE("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0),
206
207SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
208SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 0),
209};
210
0a1bf553
MB
211/* Speaker Output Mixer */
212static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
213SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
214SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
215SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1),
216};
217
218/* Mono Output Mixer */
219static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
220SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
221SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
222SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 1),
223};
224
225/* AUX Input boost vol */
226static const struct snd_kcontrol_new wm8974_aux_boost_controls =
227SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
228
229/* Mic Input boost vol */
230static const struct snd_kcontrol_new wm8974_mic_boost_controls =
231SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
232
233/* Capture boost switch */
234static const struct snd_kcontrol_new wm8974_capture_boost_controls =
235SOC_DAPM_SINGLE("Capture Boost Switch", WM8974_INPPGA, 6, 1, 0);
236
237/* Aux In to PGA */
238static const struct snd_kcontrol_new wm8974_aux_capture_boost_controls =
239SOC_DAPM_SINGLE("Aux Capture Boost Switch", WM8974_INPPGA, 2, 1, 0);
240
241/* Mic P In to PGA */
242static const struct snd_kcontrol_new wm8974_micp_capture_boost_controls =
243SOC_DAPM_SINGLE("Mic P Capture Boost Switch", WM8974_INPPGA, 0, 1, 0);
244
245/* Mic N In to PGA */
246static const struct snd_kcontrol_new wm8974_micn_capture_boost_controls =
247SOC_DAPM_SINGLE("Mic N Capture Boost Switch", WM8974_INPPGA, 1, 1, 0);
248
249static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
250SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
251 &wm8974_speaker_mixer_controls[0],
252 ARRAY_SIZE(wm8974_speaker_mixer_controls)),
253SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
254 &wm8974_mono_mixer_controls[0],
255 ARRAY_SIZE(wm8974_mono_mixer_controls)),
256SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
257SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER3, 0, 0),
258SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
259SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
260SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
261SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
262SND_SOC_DAPM_PGA("Mic PGA", WM8974_POWER2, 2, 0, NULL, 0),
263
264SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
265 &wm8974_aux_boost_controls, 1),
266SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
267 &wm8974_mic_boost_controls, 1),
268SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
269 &wm8974_capture_boost_controls),
270
271SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0, NULL, 0),
272
273SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
274
275SND_SOC_DAPM_INPUT("MICN"),
276SND_SOC_DAPM_INPUT("MICP"),
277SND_SOC_DAPM_INPUT("AUX"),
278SND_SOC_DAPM_OUTPUT("MONOOUT"),
279SND_SOC_DAPM_OUTPUT("SPKOUTP"),
280SND_SOC_DAPM_OUTPUT("SPKOUTN"),
281};
282
283static const struct snd_soc_dapm_route audio_map[] = {
284 /* Mono output mixer */
285 {"Mono Mixer", "PCM Playback Switch", "DAC"},
286 {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
287 {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
288
289 /* Speaker output mixer */
290 {"Speaker Mixer", "PCM Playback Switch", "DAC"},
291 {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
292 {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
293
294 /* Outputs */
295 {"Mono Out", NULL, "Mono Mixer"},
296 {"MONOOUT", NULL, "Mono Out"},
297 {"SpkN Out", NULL, "Speaker Mixer"},
298 {"SpkP Out", NULL, "Speaker Mixer"},
299 {"SPKOUTN", NULL, "SpkN Out"},
300 {"SPKOUTP", NULL, "SpkP Out"},
301
302 /* Boost Mixer */
303 {"Boost Mixer", NULL, "ADC"},
304 {"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
305 {"Aux Boost", "Aux Volume", "Boost Mixer"},
306 {"Capture Boost", "Capture Switch", "Boost Mixer"},
307 {"Mic Boost", "Mic Volume", "Boost Mixer"},
308
309 /* Inputs */
310 {"MICP", NULL, "Mic Boost"},
311 {"MICN", NULL, "Mic PGA"},
312 {"Mic PGA", NULL, "Capture Boost"},
313 {"AUX", NULL, "Aux Input"},
314};
315
316static int wm8974_add_widgets(struct snd_soc_codec *codec)
317{
318 snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets,
319 ARRAY_SIZE(wm8974_dapm_widgets));
320
321 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
322
323 snd_soc_dapm_new_widgets(codec);
324 return 0;
325}
326
327struct pll_ {
91d0c3ec 328 unsigned int pre_div:4; /* prescale - 1 */
0a1bf553
MB
329 unsigned int n:4;
330 unsigned int k;
331};
332
91d0c3ec
MB
333static struct pll_ pll_div;
334
335/* The size in bits of the pll divide multiplied by 10
336 * to allow rounding later */
337#define FIXED_PLL_SIZE ((1 << 24) * 10)
338
339static void pll_factors(unsigned int target, unsigned int source)
340{
341 unsigned long long Kpart;
342 unsigned int K, Ndiv, Nmod;
343
344 Ndiv = target / source;
345 if (Ndiv < 6) {
346 source >>= 1;
347 pll_div.pre_div = 1;
348 Ndiv = target / source;
349 } else
350 pll_div.pre_div = 0;
351
352 if ((Ndiv < 6) || (Ndiv > 12))
353 printk(KERN_WARNING
354 "WM8974 N value %u outwith recommended range!d\n",
355 Ndiv);
356
357 pll_div.n = Ndiv;
358 Nmod = target % source;
359 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
360
361 do_div(Kpart, source);
362
363 K = Kpart & 0xFFFFFFFF;
364
365 /* Check if we need to round */
366 if ((K % 10) >= 5)
367 K += 5;
368
369 /* Move down to proper range now rounding is done */
370 K /= 10;
371
372 pll_div.k = K;
373}
0a1bf553
MB
374
375static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
376 int pll_id, unsigned int freq_in, unsigned int freq_out)
377{
378 struct snd_soc_codec *codec = codec_dai->codec;
0a1bf553
MB
379 u16 reg;
380
1a55b3f6 381 if (freq_in == 0 || freq_out == 0) {
91d0c3ec
MB
382 /* Clock CODEC directly from MCLK */
383 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
384 wm8974_write(codec, WM8974_CLOCK, reg & 0x0ff);
385
386 /* Turn off PLL */
0a1bf553
MB
387 reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
388 wm8974_write(codec, WM8974_POWER1, reg & 0x1df);
389 return 0;
390 }
391
91d0c3ec
MB
392 pll_factors(freq_out*4, freq_in);
393
394 wm8974_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
395 wm8974_write(codec, WM8974_PLLK1, pll_div.k >> 18);
396 wm8974_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
397 wm8974_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
398 reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
399 wm8974_write(codec, WM8974_POWER1, reg | 0x020);
1a55b3f6 400
91d0c3ec
MB
401 /* Run CODEC from PLL instead of MCLK */
402 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
403 wm8974_write(codec, WM8974_CLOCK, reg | 0x100);
404
405 return 0;
0a1bf553
MB
406}
407
408/*
409 * Configure WM8974 clock dividers.
410 */
411static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
412 int div_id, int div)
413{
414 struct snd_soc_codec *codec = codec_dai->codec;
415 u16 reg;
416
417 switch (div_id) {
418 case WM8974_OPCLKDIV:
1a55b3f6 419 reg = wm8974_read_reg_cache(codec, WM8974_GPIO) & 0x1cf;
0a1bf553
MB
420 wm8974_write(codec, WM8974_GPIO, reg | div);
421 break;
422 case WM8974_MCLKDIV:
423 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x11f;
424 wm8974_write(codec, WM8974_CLOCK, reg | div);
425 break;
426 case WM8974_ADCCLK:
427 reg = wm8974_read_reg_cache(codec, WM8974_ADC) & 0x1f7;
428 wm8974_write(codec, WM8974_ADC, reg | div);
429 break;
430 case WM8974_DACCLK:
431 reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0x1f7;
432 wm8974_write(codec, WM8974_DAC, reg | div);
433 break;
434 case WM8974_BCLKDIV:
435 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1e3;
436 wm8974_write(codec, WM8974_CLOCK, reg | div);
437 break;
438 default:
439 return -EINVAL;
440 }
441
442 return 0;
443}
444
445static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
446 unsigned int fmt)
447{
448 struct snd_soc_codec *codec = codec_dai->codec;
449 u16 iface = 0;
450 u16 clk = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1fe;
451
452 /* set master/slave audio interface */
453 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
454 case SND_SOC_DAIFMT_CBM_CFM:
455 clk |= 0x0001;
456 break;
457 case SND_SOC_DAIFMT_CBS_CFS:
458 break;
459 default:
460 return -EINVAL;
461 }
462
463 /* interface format */
464 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
465 case SND_SOC_DAIFMT_I2S:
466 iface |= 0x0010;
467 break;
468 case SND_SOC_DAIFMT_RIGHT_J:
469 break;
470 case SND_SOC_DAIFMT_LEFT_J:
471 iface |= 0x0008;
472 break;
473 case SND_SOC_DAIFMT_DSP_A:
474 iface |= 0x00018;
475 break;
476 default:
477 return -EINVAL;
478 }
479
480 /* clock inversion */
481 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
482 case SND_SOC_DAIFMT_NB_NF:
483 break;
484 case SND_SOC_DAIFMT_IB_IF:
485 iface |= 0x0180;
486 break;
487 case SND_SOC_DAIFMT_IB_NF:
488 iface |= 0x0100;
489 break;
490 case SND_SOC_DAIFMT_NB_IF:
491 iface |= 0x0080;
492 break;
493 default:
494 return -EINVAL;
495 }
496
497 wm8974_write(codec, WM8974_IFACE, iface);
498 wm8974_write(codec, WM8974_CLOCK, clk);
499 return 0;
500}
501
502static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
503 struct snd_pcm_hw_params *params,
504 struct snd_soc_dai *dai)
505{
506 struct snd_soc_codec *codec = dai->codec;
507 u16 iface = wm8974_read_reg_cache(codec, WM8974_IFACE) & 0x19f;
508 u16 adn = wm8974_read_reg_cache(codec, WM8974_ADD) & 0x1f1;
509
510 /* bit size */
511 switch (params_format(params)) {
512 case SNDRV_PCM_FORMAT_S16_LE:
513 break;
514 case SNDRV_PCM_FORMAT_S20_3LE:
515 iface |= 0x0020;
516 break;
517 case SNDRV_PCM_FORMAT_S24_LE:
518 iface |= 0x0040;
519 break;
520 case SNDRV_PCM_FORMAT_S32_LE:
521 iface |= 0x0060;
522 break;
523 }
524
525 /* filter coefficient */
526 switch (params_rate(params)) {
527 case SNDRV_PCM_RATE_8000:
528 adn |= 0x5 << 1;
529 break;
530 case SNDRV_PCM_RATE_11025:
531 adn |= 0x4 << 1;
532 break;
533 case SNDRV_PCM_RATE_16000:
534 adn |= 0x3 << 1;
535 break;
536 case SNDRV_PCM_RATE_22050:
537 adn |= 0x2 << 1;
538 break;
539 case SNDRV_PCM_RATE_32000:
540 adn |= 0x1 << 1;
541 break;
542 case SNDRV_PCM_RATE_44100:
543 break;
544 }
545
546 wm8974_write(codec, WM8974_IFACE, iface);
547 wm8974_write(codec, WM8974_ADD, adn);
548 return 0;
549}
550
551static int wm8974_mute(struct snd_soc_dai *dai, int mute)
552{
553 struct snd_soc_codec *codec = dai->codec;
554 u16 mute_reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0xffbf;
555
1a55b3f6 556 if (mute)
0a1bf553
MB
557 wm8974_write(codec, WM8974_DAC, mute_reg | 0x40);
558 else
559 wm8974_write(codec, WM8974_DAC, mute_reg);
560 return 0;
561}
562
563/* liam need to make this lower power with dapm */
564static int wm8974_set_bias_level(struct snd_soc_codec *codec,
565 enum snd_soc_bias_level level)
566{
df1ef7a3
MB
567 u16 power1 = wm8974_read_reg_cache(codec, WM8974_POWER1) & ~0x3;
568
0a1bf553
MB
569 switch (level) {
570 case SND_SOC_BIAS_ON:
0a1bf553 571 case SND_SOC_BIAS_PREPARE:
df1ef7a3
MB
572 power1 |= 0x1; /* VMID 50k */
573 wm8974_write(codec, WM8974_POWER1, power1);
0a1bf553 574 break;
df1ef7a3 575
0a1bf553 576 case SND_SOC_BIAS_STANDBY:
df1ef7a3
MB
577 power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
578
579 if (codec->bias_level == SND_SOC_BIAS_OFF) {
580 /* Initial cap charge at VMID 5k */
581 wm8974_write(codec, WM8974_POWER1, power1 | 0x3);
582 mdelay(100);
583 }
584
585 power1 |= 0x2; /* VMID 500k */
586 wm8974_write(codec, WM8974_POWER1, power1);
0a1bf553 587 break;
df1ef7a3 588
0a1bf553 589 case SND_SOC_BIAS_OFF:
df1ef7a3
MB
590 wm8974_write(codec, WM8974_POWER1, 0);
591 wm8974_write(codec, WM8974_POWER2, 0);
592 wm8974_write(codec, WM8974_POWER3, 0);
0a1bf553
MB
593 break;
594 }
df1ef7a3 595
0a1bf553
MB
596 codec->bias_level = level;
597 return 0;
598}
599
1a55b3f6 600#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
0a1bf553
MB
601
602#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
603 SNDRV_PCM_FMTBIT_S24_LE)
604
605static struct snd_soc_dai_ops wm8974_ops = {
606 .hw_params = wm8974_pcm_hw_params,
607 .digital_mute = wm8974_mute,
608 .set_fmt = wm8974_set_dai_fmt,
609 .set_clkdiv = wm8974_set_dai_clkdiv,
610 .set_pll = wm8974_set_dai_pll,
611};
612
613struct snd_soc_dai wm8974_dai = {
614 .name = "WM8974 HiFi",
615 .playback = {
616 .stream_name = "Playback",
617 .channels_min = 1,
33d81af4 618 .channels_max = 2, /* Only 1 channel of data */
0a1bf553
MB
619 .rates = WM8974_RATES,
620 .formats = WM8974_FORMATS,},
621 .capture = {
622 .stream_name = "Capture",
623 .channels_min = 1,
33d81af4 624 .channels_max = 2, /* Only 1 channel of data */
0a1bf553
MB
625 .rates = WM8974_RATES,
626 .formats = WM8974_FORMATS,},
627 .ops = &wm8974_ops,
628};
629EXPORT_SYMBOL_GPL(wm8974_dai);
630
631static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
632{
633 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
634 struct snd_soc_codec *codec = socdev->card->codec;
635
636 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
637 return 0;
638}
639
640static int wm8974_resume(struct platform_device *pdev)
641{
642 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
643 struct snd_soc_codec *codec = socdev->card->codec;
644 int i;
645 u8 data[2];
646 u16 *cache = codec->reg_cache;
647
648 /* Sync reg_cache with the hardware */
649 for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
650 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
651 data[1] = cache[i] & 0x00ff;
652 codec->hw_write(codec->control_data, data, 2);
653 }
654 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
655 wm8974_set_bias_level(codec, codec->suspend_bias_level);
656 return 0;
657}
658
4fcbbb67 659static int wm8974_probe(struct platform_device *pdev)
0a1bf553 660{
4fcbbb67
MB
661 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
662 struct snd_soc_codec *codec;
0a1bf553
MB
663 int ret = 0;
664
4fcbbb67
MB
665 if (wm8974_codec == NULL) {
666 dev_err(&pdev->dev, "Codec device not registered\n");
667 return -ENODEV;
668 }
0a1bf553 669
4fcbbb67
MB
670 socdev->card->codec = wm8974_codec;
671 codec = wm8974_codec;
0a1bf553
MB
672
673 /* register pcms */
674 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
1a55b3f6 675 if (ret < 0) {
4fcbbb67 676 dev_err(codec->dev, "failed to create pcms: %d\n", ret);
0a1bf553
MB
677 goto pcm_err;
678 }
679
4fcbbb67
MB
680 snd_soc_add_controls(codec, wm8974_snd_controls,
681 ARRAY_SIZE(wm8974_snd_controls));
0a1bf553
MB
682 wm8974_add_widgets(codec);
683 ret = snd_soc_init_card(socdev);
684 if (ret < 0) {
4fcbbb67 685 dev_err(codec->dev, "failed to register card: %d\n", ret);
0a1bf553
MB
686 goto card_err;
687 }
4fcbbb67 688
0a1bf553
MB
689 return ret;
690
691card_err:
692 snd_soc_free_pcms(socdev);
693 snd_soc_dapm_free(socdev);
694pcm_err:
0a1bf553
MB
695 return ret;
696}
697
4fcbbb67
MB
698/* power down chip */
699static int wm8974_remove(struct platform_device *pdev)
700{
701 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
0a1bf553 702
4fcbbb67
MB
703 snd_soc_free_pcms(socdev);
704 snd_soc_dapm_free(socdev);
0a1bf553 705
4fcbbb67
MB
706 return 0;
707}
0a1bf553 708
4fcbbb67
MB
709struct snd_soc_codec_device soc_codec_dev_wm8974 = {
710 .probe = wm8974_probe,
711 .remove = wm8974_remove,
712 .suspend = wm8974_suspend,
713 .resume = wm8974_resume,
714};
715EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
0a1bf553 716
4fcbbb67 717static __devinit int wm8974_register(struct wm8974_priv *wm8974)
0a1bf553 718{
0a1bf553 719 int ret;
4fcbbb67 720 struct snd_soc_codec *codec = &wm8974->codec;
0a1bf553 721
4fcbbb67
MB
722 if (wm8974_codec) {
723 dev_err(codec->dev, "Another WM8974 is registered\n");
724 return -EINVAL;
725 }
0a1bf553 726
4fcbbb67
MB
727 mutex_init(&codec->mutex);
728 INIT_LIST_HEAD(&codec->dapm_widgets);
729 INIT_LIST_HEAD(&codec->dapm_paths);
0a1bf553 730
4fcbbb67
MB
731 codec->private_data = wm8974;
732 codec->name = "WM8974";
733 codec->owner = THIS_MODULE;
734 codec->read = wm8974_read_reg_cache;
735 codec->write = wm8974_write;
736 codec->bias_level = SND_SOC_BIAS_OFF;
737 codec->set_bias_level = wm8974_set_bias_level;
738 codec->dai = &wm8974_dai;
739 codec->num_dai = 1;
740 codec->reg_cache_size = WM8974_CACHEREGNUM;
741 codec->reg_cache = &wm8974->reg_cache;
0a1bf553 742
4fcbbb67
MB
743 memcpy(codec->reg_cache, wm8974_reg, sizeof(wm8974_reg));
744
745 ret = wm8974_reset(codec);
0a1bf553 746 if (ret < 0) {
4fcbbb67
MB
747 dev_err(codec->dev, "Failed to issue reset\n");
748 return ret;
0a1bf553
MB
749 }
750
4fcbbb67
MB
751 wm8974_dai.dev = codec->dev;
752
753 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
754
755 wm8974_codec = codec;
756
757 ret = snd_soc_register_codec(codec);
758 if (ret != 0) {
759 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
760 return ret;
0a1bf553 761 }
0a1bf553 762
4fcbbb67
MB
763 ret = snd_soc_register_dai(&wm8974_dai);
764 if (ret != 0) {
765 dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
766 snd_soc_unregister_codec(codec);
767 return ret;
768 }
0a1bf553 769
0a1bf553
MB
770 return 0;
771}
772
4fcbbb67 773static __devexit void wm8974_unregister(struct wm8974_priv *wm8974)
0a1bf553 774{
4fcbbb67
MB
775 wm8974_set_bias_level(&wm8974->codec, SND_SOC_BIAS_OFF);
776 snd_soc_unregister_dai(&wm8974_dai);
777 snd_soc_unregister_codec(&wm8974->codec);
778 kfree(wm8974);
779 wm8974_codec = NULL;
0a1bf553
MB
780}
781
4fcbbb67
MB
782static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
783 const struct i2c_device_id *id)
0a1bf553 784{
4fcbbb67 785 struct wm8974_priv *wm8974;
0a1bf553 786 struct snd_soc_codec *codec;
0a1bf553 787
4fcbbb67
MB
788 wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
789 if (wm8974 == NULL)
0a1bf553
MB
790 return -ENOMEM;
791
4fcbbb67
MB
792 codec = &wm8974->codec;
793 codec->hw_write = (hw_write_t)i2c_master_send;
0a1bf553 794
4fcbbb67
MB
795 i2c_set_clientdata(i2c, wm8974);
796 codec->control_data = i2c;
0a1bf553 797
4fcbbb67 798 codec->dev = &i2c->dev;
0a1bf553 799
4fcbbb67
MB
800 return wm8974_register(wm8974);
801}
0a1bf553 802
4fcbbb67
MB
803static __devexit int wm8974_i2c_remove(struct i2c_client *client)
804{
805 struct wm8974_priv *wm8974 = i2c_get_clientdata(client);
806 wm8974_unregister(wm8974);
0a1bf553
MB
807 return 0;
808}
809
4fcbbb67
MB
810static const struct i2c_device_id wm8974_i2c_id[] = {
811 { "wm8974", 0 },
812 { }
813};
814MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
815
816static struct i2c_driver wm8974_i2c_driver = {
817 .driver = {
818 .name = "WM8974 I2C Codec",
819 .owner = THIS_MODULE,
820 },
821 .probe = wm8974_i2c_probe,
822 .remove = __devexit_p(wm8974_i2c_remove),
823 .id_table = wm8974_i2c_id,
0a1bf553 824};
0a1bf553
MB
825
826static int __init wm8974_modinit(void)
827{
4fcbbb67 828 return i2c_add_driver(&wm8974_i2c_driver);
0a1bf553
MB
829}
830module_init(wm8974_modinit);
831
832static void __exit wm8974_exit(void)
833{
4fcbbb67 834 i2c_del_driver(&wm8974_i2c_driver);
0a1bf553
MB
835}
836module_exit(wm8974_exit);
837
838MODULE_DESCRIPTION("ASoC WM8974 driver");
839MODULE_AUTHOR("Liam Girdwood");
840MODULE_LICENSE("GPL");