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