Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[deliverable/linux.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
279 snd_soc_dapm_new_widgets(codec);
280 return 0;
281}
282
283struct pll_ {
91d0c3ec 284 unsigned int pre_div:4; /* prescale - 1 */
0a1bf553
MB
285 unsigned int n:4;
286 unsigned int k;
287};
288
91d0c3ec
MB
289static struct pll_ pll_div;
290
291/* The size in bits of the pll divide multiplied by 10
292 * to allow rounding later */
293#define FIXED_PLL_SIZE ((1 << 24) * 10)
294
295static void pll_factors(unsigned int target, unsigned int source)
296{
297 unsigned long long Kpart;
298 unsigned int K, Ndiv, Nmod;
299
300 Ndiv = target / source;
301 if (Ndiv < 6) {
302 source >>= 1;
303 pll_div.pre_div = 1;
304 Ndiv = target / source;
305 } else
306 pll_div.pre_div = 0;
307
308 if ((Ndiv < 6) || (Ndiv > 12))
309 printk(KERN_WARNING
8b83a193 310 "WM8974 N value %u outwith recommended range!\n",
91d0c3ec
MB
311 Ndiv);
312
313 pll_div.n = Ndiv;
314 Nmod = target % source;
315 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
316
317 do_div(Kpart, source);
318
319 K = Kpart & 0xFFFFFFFF;
320
321 /* Check if we need to round */
322 if ((K % 10) >= 5)
323 K += 5;
324
325 /* Move down to proper range now rounding is done */
326 K /= 10;
327
328 pll_div.k = K;
329}
0a1bf553
MB
330
331static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
332 int pll_id, unsigned int freq_in, unsigned int freq_out)
333{
334 struct snd_soc_codec *codec = codec_dai->codec;
0a1bf553
MB
335 u16 reg;
336
1a55b3f6 337 if (freq_in == 0 || freq_out == 0) {
91d0c3ec 338 /* Clock CODEC directly from MCLK */
1e97f50b
MB
339 reg = snd_soc_read(codec, WM8974_CLOCK);
340 snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
91d0c3ec
MB
341
342 /* Turn off PLL */
1e97f50b
MB
343 reg = snd_soc_read(codec, WM8974_POWER1);
344 snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
0a1bf553
MB
345 return 0;
346 }
347
91d0c3ec
MB
348 pll_factors(freq_out*4, freq_in);
349
1e97f50b
MB
350 snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
351 snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
352 snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
353 snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
354 reg = snd_soc_read(codec, WM8974_POWER1);
355 snd_soc_write(codec, WM8974_POWER1, reg | 0x020);
1a55b3f6 356
91d0c3ec 357 /* Run CODEC from PLL instead of MCLK */
1e97f50b
MB
358 reg = snd_soc_read(codec, WM8974_CLOCK);
359 snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
91d0c3ec
MB
360
361 return 0;
0a1bf553
MB
362}
363
364/*
365 * Configure WM8974 clock dividers.
366 */
367static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
368 int div_id, int div)
369{
370 struct snd_soc_codec *codec = codec_dai->codec;
371 u16 reg;
372
373 switch (div_id) {
374 case WM8974_OPCLKDIV:
1e97f50b
MB
375 reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
376 snd_soc_write(codec, WM8974_GPIO, reg | div);
0a1bf553
MB
377 break;
378 case WM8974_MCLKDIV:
1e97f50b
MB
379 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
380 snd_soc_write(codec, WM8974_CLOCK, reg | div);
0a1bf553
MB
381 break;
382 case WM8974_ADCCLK:
1e97f50b
MB
383 reg = snd_soc_read(codec, WM8974_ADC) & 0x1f7;
384 snd_soc_write(codec, WM8974_ADC, reg | div);
0a1bf553
MB
385 break;
386 case WM8974_DACCLK:
1e97f50b
MB
387 reg = snd_soc_read(codec, WM8974_DAC) & 0x1f7;
388 snd_soc_write(codec, WM8974_DAC, reg | div);
0a1bf553
MB
389 break;
390 case WM8974_BCLKDIV:
1e97f50b
MB
391 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
392 snd_soc_write(codec, WM8974_CLOCK, reg | div);
0a1bf553
MB
393 break;
394 default:
395 return -EINVAL;
396 }
397
398 return 0;
399}
400
401static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
402 unsigned int fmt)
403{
404 struct snd_soc_codec *codec = codec_dai->codec;
405 u16 iface = 0;
1e97f50b 406 u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
0a1bf553
MB
407
408 /* set master/slave audio interface */
409 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
410 case SND_SOC_DAIFMT_CBM_CFM:
411 clk |= 0x0001;
412 break;
413 case SND_SOC_DAIFMT_CBS_CFS:
414 break;
415 default:
416 return -EINVAL;
417 }
418
419 /* interface format */
420 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
421 case SND_SOC_DAIFMT_I2S:
422 iface |= 0x0010;
423 break;
424 case SND_SOC_DAIFMT_RIGHT_J:
425 break;
426 case SND_SOC_DAIFMT_LEFT_J:
427 iface |= 0x0008;
428 break;
429 case SND_SOC_DAIFMT_DSP_A:
430 iface |= 0x00018;
431 break;
432 default:
433 return -EINVAL;
434 }
435
436 /* clock inversion */
437 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
438 case SND_SOC_DAIFMT_NB_NF:
439 break;
440 case SND_SOC_DAIFMT_IB_IF:
441 iface |= 0x0180;
442 break;
443 case SND_SOC_DAIFMT_IB_NF:
444 iface |= 0x0100;
445 break;
446 case SND_SOC_DAIFMT_NB_IF:
447 iface |= 0x0080;
448 break;
449 default:
450 return -EINVAL;
451 }
452
1e97f50b
MB
453 snd_soc_write(codec, WM8974_IFACE, iface);
454 snd_soc_write(codec, WM8974_CLOCK, clk);
0a1bf553
MB
455 return 0;
456}
457
458static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
459 struct snd_pcm_hw_params *params,
460 struct snd_soc_dai *dai)
461{
462 struct snd_soc_codec *codec = dai->codec;
1e97f50b
MB
463 u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
464 u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
0a1bf553
MB
465
466 /* bit size */
467 switch (params_format(params)) {
468 case SNDRV_PCM_FORMAT_S16_LE:
469 break;
470 case SNDRV_PCM_FORMAT_S20_3LE:
471 iface |= 0x0020;
472 break;
473 case SNDRV_PCM_FORMAT_S24_LE:
474 iface |= 0x0040;
475 break;
476 case SNDRV_PCM_FORMAT_S32_LE:
477 iface |= 0x0060;
478 break;
479 }
480
481 /* filter coefficient */
482 switch (params_rate(params)) {
483 case SNDRV_PCM_RATE_8000:
484 adn |= 0x5 << 1;
485 break;
486 case SNDRV_PCM_RATE_11025:
487 adn |= 0x4 << 1;
488 break;
489 case SNDRV_PCM_RATE_16000:
490 adn |= 0x3 << 1;
491 break;
492 case SNDRV_PCM_RATE_22050:
493 adn |= 0x2 << 1;
494 break;
495 case SNDRV_PCM_RATE_32000:
496 adn |= 0x1 << 1;
497 break;
498 case SNDRV_PCM_RATE_44100:
8b83a193 499 case SNDRV_PCM_RATE_48000:
0a1bf553
MB
500 break;
501 }
502
1e97f50b
MB
503 snd_soc_write(codec, WM8974_IFACE, iface);
504 snd_soc_write(codec, WM8974_ADD, adn);
0a1bf553
MB
505 return 0;
506}
507
508static int wm8974_mute(struct snd_soc_dai *dai, int mute)
509{
510 struct snd_soc_codec *codec = dai->codec;
1e97f50b 511 u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
0a1bf553 512
1a55b3f6 513 if (mute)
1e97f50b 514 snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
0a1bf553 515 else
1e97f50b 516 snd_soc_write(codec, WM8974_DAC, mute_reg);
0a1bf553
MB
517 return 0;
518}
519
520/* liam need to make this lower power with dapm */
521static int wm8974_set_bias_level(struct snd_soc_codec *codec,
522 enum snd_soc_bias_level level)
523{
1e97f50b 524 u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
df1ef7a3 525
0a1bf553
MB
526 switch (level) {
527 case SND_SOC_BIAS_ON:
0a1bf553 528 case SND_SOC_BIAS_PREPARE:
df1ef7a3 529 power1 |= 0x1; /* VMID 50k */
1e97f50b 530 snd_soc_write(codec, WM8974_POWER1, power1);
0a1bf553 531 break;
df1ef7a3 532
0a1bf553 533 case SND_SOC_BIAS_STANDBY:
df1ef7a3
MB
534 power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
535
536 if (codec->bias_level == SND_SOC_BIAS_OFF) {
537 /* Initial cap charge at VMID 5k */
1e97f50b 538 snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
df1ef7a3
MB
539 mdelay(100);
540 }
541
542 power1 |= 0x2; /* VMID 500k */
1e97f50b 543 snd_soc_write(codec, WM8974_POWER1, power1);
0a1bf553 544 break;
df1ef7a3 545
0a1bf553 546 case SND_SOC_BIAS_OFF:
1e97f50b
MB
547 snd_soc_write(codec, WM8974_POWER1, 0);
548 snd_soc_write(codec, WM8974_POWER2, 0);
549 snd_soc_write(codec, WM8974_POWER3, 0);
0a1bf553
MB
550 break;
551 }
df1ef7a3 552
0a1bf553
MB
553 codec->bias_level = level;
554 return 0;
555}
556
1a55b3f6 557#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
0a1bf553
MB
558
559#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
560 SNDRV_PCM_FMTBIT_S24_LE)
561
562static struct snd_soc_dai_ops wm8974_ops = {
563 .hw_params = wm8974_pcm_hw_params,
564 .digital_mute = wm8974_mute,
565 .set_fmt = wm8974_set_dai_fmt,
566 .set_clkdiv = wm8974_set_dai_clkdiv,
567 .set_pll = wm8974_set_dai_pll,
568};
569
570struct snd_soc_dai wm8974_dai = {
571 .name = "WM8974 HiFi",
572 .playback = {
573 .stream_name = "Playback",
574 .channels_min = 1,
33d81af4 575 .channels_max = 2, /* Only 1 channel of data */
0a1bf553
MB
576 .rates = WM8974_RATES,
577 .formats = WM8974_FORMATS,},
578 .capture = {
579 .stream_name = "Capture",
580 .channels_min = 1,
33d81af4 581 .channels_max = 2, /* Only 1 channel of data */
0a1bf553
MB
582 .rates = WM8974_RATES,
583 .formats = WM8974_FORMATS,},
584 .ops = &wm8974_ops,
cb11d39e 585 .symmetric_rates = 1,
0a1bf553
MB
586};
587EXPORT_SYMBOL_GPL(wm8974_dai);
588
589static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
590{
591 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
592 struct snd_soc_codec *codec = socdev->card->codec;
593
594 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
595 return 0;
596}
597
598static int wm8974_resume(struct platform_device *pdev)
599{
600 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
601 struct snd_soc_codec *codec = socdev->card->codec;
602 int i;
603 u8 data[2];
604 u16 *cache = codec->reg_cache;
605
606 /* Sync reg_cache with the hardware */
607 for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
608 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
609 data[1] = cache[i] & 0x00ff;
610 codec->hw_write(codec->control_data, data, 2);
611 }
612 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
613 wm8974_set_bias_level(codec, codec->suspend_bias_level);
614 return 0;
615}
616
4fcbbb67 617static int wm8974_probe(struct platform_device *pdev)
0a1bf553 618{
4fcbbb67
MB
619 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
620 struct snd_soc_codec *codec;
0a1bf553
MB
621 int ret = 0;
622
4fcbbb67
MB
623 if (wm8974_codec == NULL) {
624 dev_err(&pdev->dev, "Codec device not registered\n");
625 return -ENODEV;
626 }
0a1bf553 627
4fcbbb67
MB
628 socdev->card->codec = wm8974_codec;
629 codec = wm8974_codec;
0a1bf553
MB
630
631 /* register pcms */
632 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
1a55b3f6 633 if (ret < 0) {
4fcbbb67 634 dev_err(codec->dev, "failed to create pcms: %d\n", ret);
0a1bf553
MB
635 goto pcm_err;
636 }
637
4fcbbb67
MB
638 snd_soc_add_controls(codec, wm8974_snd_controls,
639 ARRAY_SIZE(wm8974_snd_controls));
0a1bf553
MB
640 wm8974_add_widgets(codec);
641 ret = snd_soc_init_card(socdev);
642 if (ret < 0) {
4fcbbb67 643 dev_err(codec->dev, "failed to register card: %d\n", ret);
0a1bf553
MB
644 goto card_err;
645 }
4fcbbb67 646
0a1bf553
MB
647 return ret;
648
649card_err:
650 snd_soc_free_pcms(socdev);
651 snd_soc_dapm_free(socdev);
652pcm_err:
0a1bf553
MB
653 return ret;
654}
655
4fcbbb67
MB
656/* power down chip */
657static int wm8974_remove(struct platform_device *pdev)
658{
659 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
0a1bf553 660
4fcbbb67
MB
661 snd_soc_free_pcms(socdev);
662 snd_soc_dapm_free(socdev);
0a1bf553 663
4fcbbb67
MB
664 return 0;
665}
0a1bf553 666
4fcbbb67
MB
667struct snd_soc_codec_device soc_codec_dev_wm8974 = {
668 .probe = wm8974_probe,
669 .remove = wm8974_remove,
670 .suspend = wm8974_suspend,
671 .resume = wm8974_resume,
672};
673EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
0a1bf553 674
4fcbbb67 675static __devinit int wm8974_register(struct wm8974_priv *wm8974)
0a1bf553 676{
0a1bf553 677 int ret;
4fcbbb67 678 struct snd_soc_codec *codec = &wm8974->codec;
0a1bf553 679
4fcbbb67
MB
680 if (wm8974_codec) {
681 dev_err(codec->dev, "Another WM8974 is registered\n");
682 return -EINVAL;
683 }
0a1bf553 684
4fcbbb67
MB
685 mutex_init(&codec->mutex);
686 INIT_LIST_HEAD(&codec->dapm_widgets);
687 INIT_LIST_HEAD(&codec->dapm_paths);
0a1bf553 688
4fcbbb67
MB
689 codec->private_data = wm8974;
690 codec->name = "WM8974";
691 codec->owner = THIS_MODULE;
4fcbbb67
MB
692 codec->bias_level = SND_SOC_BIAS_OFF;
693 codec->set_bias_level = wm8974_set_bias_level;
694 codec->dai = &wm8974_dai;
695 codec->num_dai = 1;
696 codec->reg_cache_size = WM8974_CACHEREGNUM;
697 codec->reg_cache = &wm8974->reg_cache;
0a1bf553 698
1e97f50b
MB
699 ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
700 if (ret < 0) {
701 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
702 goto err;
703 }
704
4fcbbb67
MB
705 memcpy(codec->reg_cache, wm8974_reg, sizeof(wm8974_reg));
706
707 ret = wm8974_reset(codec);
0a1bf553 708 if (ret < 0) {
4fcbbb67 709 dev_err(codec->dev, "Failed to issue reset\n");
1e97f50b 710 goto err;
0a1bf553
MB
711 }
712
4fcbbb67
MB
713 wm8974_dai.dev = codec->dev;
714
715 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
716
717 wm8974_codec = codec;
718
719 ret = snd_soc_register_codec(codec);
720 if (ret != 0) {
721 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
1e97f50b 722 goto err;
0a1bf553 723 }
0a1bf553 724
4fcbbb67
MB
725 ret = snd_soc_register_dai(&wm8974_dai);
726 if (ret != 0) {
727 dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
1e97f50b 728 goto err_codec;
4fcbbb67 729 }
0a1bf553 730
0a1bf553 731 return 0;
1e97f50b
MB
732
733err_codec:
734 snd_soc_unregister_codec(codec);
735err:
736 kfree(wm8974);
737 return ret;
0a1bf553
MB
738}
739
4fcbbb67 740static __devexit void wm8974_unregister(struct wm8974_priv *wm8974)
0a1bf553 741{
4fcbbb67
MB
742 wm8974_set_bias_level(&wm8974->codec, SND_SOC_BIAS_OFF);
743 snd_soc_unregister_dai(&wm8974_dai);
744 snd_soc_unregister_codec(&wm8974->codec);
745 kfree(wm8974);
746 wm8974_codec = NULL;
0a1bf553
MB
747}
748
4fcbbb67
MB
749static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
750 const struct i2c_device_id *id)
0a1bf553 751{
4fcbbb67 752 struct wm8974_priv *wm8974;
0a1bf553 753 struct snd_soc_codec *codec;
0a1bf553 754
4fcbbb67
MB
755 wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
756 if (wm8974 == NULL)
0a1bf553
MB
757 return -ENOMEM;
758
4fcbbb67
MB
759 codec = &wm8974->codec;
760 codec->hw_write = (hw_write_t)i2c_master_send;
0a1bf553 761
4fcbbb67
MB
762 i2c_set_clientdata(i2c, wm8974);
763 codec->control_data = i2c;
0a1bf553 764
4fcbbb67 765 codec->dev = &i2c->dev;
0a1bf553 766
4fcbbb67
MB
767 return wm8974_register(wm8974);
768}
0a1bf553 769
4fcbbb67
MB
770static __devexit int wm8974_i2c_remove(struct i2c_client *client)
771{
772 struct wm8974_priv *wm8974 = i2c_get_clientdata(client);
773 wm8974_unregister(wm8974);
0a1bf553
MB
774 return 0;
775}
776
4fcbbb67
MB
777static const struct i2c_device_id wm8974_i2c_id[] = {
778 { "wm8974", 0 },
779 { }
780};
781MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
782
783static struct i2c_driver wm8974_i2c_driver = {
784 .driver = {
8b83a193 785 .name = "WM8974",
4fcbbb67
MB
786 .owner = THIS_MODULE,
787 },
788 .probe = wm8974_i2c_probe,
789 .remove = __devexit_p(wm8974_i2c_remove),
790 .id_table = wm8974_i2c_id,
0a1bf553 791};
0a1bf553
MB
792
793static int __init wm8974_modinit(void)
794{
4fcbbb67 795 return i2c_add_driver(&wm8974_i2c_driver);
0a1bf553
MB
796}
797module_init(wm8974_modinit);
798
799static void __exit wm8974_exit(void)
800{
4fcbbb67 801 i2c_del_driver(&wm8974_i2c_driver);
0a1bf553
MB
802}
803module_exit(wm8974_exit);
804
805MODULE_DESCRIPTION("ASoC WM8974 driver");
806MODULE_AUTHOR("Liam Girdwood");
807MODULE_LICENSE("GPL");
This page took 0.094398 seconds and 5 git commands to generate.