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