ASoC: WM8903: Create default platform data structure
[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 *
9a185b9a 6 * Author: Liam Girdwood <Liam.Girdwood@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>
5a0e3ad6 20#include <linux/slab.h>
0a1bf553
MB
21#include <sound/core.h>
22#include <sound/pcm.h>
23#include <sound/pcm_params.h>
24#include <sound/soc.h>
0a1bf553 25#include <sound/initval.h>
a5f8d2f1 26#include <sound/tlv.h>
0a1bf553
MB
27
28#include "wm8974.h"
29
0a1bf553 30static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
1a55b3f6
MB
31 0x0000, 0x0000, 0x0000, 0x0000,
32 0x0050, 0x0000, 0x0140, 0x0000,
33 0x0000, 0x0000, 0x0000, 0x00ff,
34 0x0000, 0x0000, 0x0100, 0x00ff,
35 0x0000, 0x0000, 0x012c, 0x002c,
36 0x002c, 0x002c, 0x002c, 0x0000,
37 0x0032, 0x0000, 0x0000, 0x0000,
38 0x0000, 0x0000, 0x0000, 0x0000,
39 0x0038, 0x000b, 0x0032, 0x0000,
40 0x0008, 0x000c, 0x0093, 0x00e9,
41 0x0000, 0x0000, 0x0000, 0x0000,
42 0x0003, 0x0010, 0x0000, 0x0000,
43 0x0000, 0x0002, 0x0000, 0x0000,
44 0x0000, 0x0000, 0x0039, 0x0000,
45 0x0000,
0a1bf553
MB
46};
47
df1ef7a3 48#define WM8974_POWER1_BIASEN 0x08
48c03ce7 49#define WM8974_POWER1_BUFIOEN 0x04
df1ef7a3 50
4fcbbb67 51struct wm8974_priv {
f0fba2ad 52 enum snd_soc_control_type control_type;
4fcbbb67
MB
53};
54
1e97f50b 55#define wm8974_reset(c) snd_soc_write(c, WM8974_RESET, 0)
0a1bf553
MB
56
57static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
58static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
59static const char *wm8974_eqmode[] = {"Capture", "Playback" };
60static const char *wm8974_bw[] = {"Narrow", "Wide" };
61static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
62static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
63static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
64static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
65static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
66static const char *wm8974_alc[] = {"ALC", "Limiter" };
67
68static const struct soc_enum wm8974_enum[] = {
69 SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
70 SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
71 SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp),
72 SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode),
73
74 SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1),
75 SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw),
76 SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2),
77 SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw),
78
79 SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3),
80 SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw),
81 SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4),
82 SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw),
83
84 SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5),
85 SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc),
86};
87
8a123ee2
MB
88static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" };
89
90static const struct soc_enum wm8974_auxmode =
91 SOC_ENUM_SINGLE(WM8974_INPUT, 3, 2, wm8974_auxmode_text);
92
a5f8d2f1
MB
93static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
94static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
95static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
96static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
97
0a1bf553
MB
98static const struct snd_kcontrol_new wm8974_snd_controls[] = {
99
100SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
101
102SOC_ENUM("DAC Companding", wm8974_enum[1]),
103SOC_ENUM("ADC Companding", wm8974_enum[0]),
104
105SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
106SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
107
a5f8d2f1 108SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv),
0a1bf553
MB
109
110SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
111SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
25cbf465 112SOC_SINGLE("ADC Inversion Switch", WM8974_ADC, 0, 1, 0),
0a1bf553 113
a5f8d2f1 114SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL, 0, 255, 0, digital_tlv),
0a1bf553
MB
115
116SOC_ENUM("Equaliser Function", wm8974_enum[3]),
117SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
a5f8d2f1 118SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1, 0, 24, 1, eq_tlv),
0a1bf553
MB
119
120SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
121SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
a5f8d2f1 122SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2, 0, 24, 1, eq_tlv),
0a1bf553
MB
123
124SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
125SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
a5f8d2f1 126SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3, 0, 24, 1, eq_tlv),
0a1bf553
MB
127
128SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
129SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
a5f8d2f1 130SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4, 0, 24, 1, eq_tlv),
0a1bf553
MB
131
132SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
133SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
a5f8d2f1 134SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5, 0, 24, 1, eq_tlv),
0a1bf553
MB
135
136SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0),
137SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0),
138SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0),
139
140SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0),
141SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0),
142
143SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0),
144SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0),
145SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0),
146
147SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0),
148SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0),
149SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0),
150
151SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
152SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0),
153SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0),
154
155SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0),
156SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0),
157
158SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0),
a5f8d2f1 159SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0, inpga_tlv),
0a1bf553
MB
160
161SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0),
162SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1),
8a123ee2
MB
163SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0, spk_tlv),
164
165SOC_ENUM("Aux Mode", wm8974_auxmode),
0a1bf553
MB
166
167SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
8a123ee2 168SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1),
b2c3e923
GL
169
170/* DAC / ADC oversampling */
171SOC_SINGLE("DAC 128x Oversampling Switch", WM8974_DAC, 8, 1, 0),
172SOC_SINGLE("ADC 128x Oversampling Switch", WM8974_ADC, 8, 1, 0),
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),
759512fb 179SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 0),
0a1bf553
MB
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 227
48dd231b 228SND_SOC_DAPM_SUPPLY("Mic Bias", WM8974_POWER1, 4, 0, NULL, 0),
0a1bf553
MB
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{
ce6120cc 274 struct snd_soc_dapm_context *dapm = &codec->dapm;
0a1bf553 275
ce6120cc
LG
276 snd_soc_dapm_new_controls(dapm, wm8974_dapm_widgets,
277 ARRAY_SIZE(wm8974_dapm_widgets));
278 snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
0a1bf553 279
0a1bf553
MB
280 return 0;
281}
282
283struct pll_ {
c36b2fc7 284 unsigned int pre_div:1;
0a1bf553
MB
285 unsigned int n:4;
286 unsigned int k;
287};
288
91d0c3ec
MB
289/* The size in bits of the pll divide multiplied by 10
290 * to allow rounding later */
291#define FIXED_PLL_SIZE ((1 << 24) * 10)
292
c36b2fc7
MB
293static void pll_factors(struct pll_ *pll_div,
294 unsigned int target, unsigned int source)
91d0c3ec
MB
295{
296 unsigned long long Kpart;
297 unsigned int K, Ndiv, Nmod;
298
c36b2fc7
MB
299 /* There is a fixed divide by 4 in the output path */
300 target *= 4;
301
91d0c3ec
MB
302 Ndiv = target / source;
303 if (Ndiv < 6) {
c36b2fc7
MB
304 source /= 2;
305 pll_div->pre_div = 1;
91d0c3ec
MB
306 Ndiv = target / source;
307 } else
c36b2fc7 308 pll_div->pre_div = 0;
91d0c3ec
MB
309
310 if ((Ndiv < 6) || (Ndiv > 12))
311 printk(KERN_WARNING
8b83a193 312 "WM8974 N value %u outwith recommended range!\n",
91d0c3ec
MB
313 Ndiv);
314
c36b2fc7 315 pll_div->n = Ndiv;
91d0c3ec
MB
316 Nmod = target % source;
317 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
318
319 do_div(Kpart, source);
320
321 K = Kpart & 0xFFFFFFFF;
322
323 /* Check if we need to round */
324 if ((K % 10) >= 5)
325 K += 5;
326
327 /* Move down to proper range now rounding is done */
328 K /= 10;
329
c36b2fc7 330 pll_div->k = K;
91d0c3ec 331}
0a1bf553 332
85488037
MB
333static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
334 int source, unsigned int freq_in, unsigned int freq_out)
0a1bf553
MB
335{
336 struct snd_soc_codec *codec = codec_dai->codec;
c36b2fc7 337 struct pll_ pll_div;
0a1bf553
MB
338 u16 reg;
339
1a55b3f6 340 if (freq_in == 0 || freq_out == 0) {
91d0c3ec 341 /* Clock CODEC directly from MCLK */
1e97f50b
MB
342 reg = snd_soc_read(codec, WM8974_CLOCK);
343 snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
91d0c3ec
MB
344
345 /* Turn off PLL */
1e97f50b
MB
346 reg = snd_soc_read(codec, WM8974_POWER1);
347 snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
0a1bf553
MB
348 return 0;
349 }
350
c36b2fc7 351 pll_factors(&pll_div, freq_out, freq_in);
91d0c3ec 352
1e97f50b
MB
353 snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
354 snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
355 snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
356 snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
357 reg = snd_soc_read(codec, WM8974_POWER1);
358 snd_soc_write(codec, WM8974_POWER1, reg | 0x020);
1a55b3f6 359
91d0c3ec 360 /* Run CODEC from PLL instead of MCLK */
1e97f50b
MB
361 reg = snd_soc_read(codec, WM8974_CLOCK);
362 snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
91d0c3ec
MB
363
364 return 0;
0a1bf553
MB
365}
366
367/*
368 * Configure WM8974 clock dividers.
369 */
370static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
371 int div_id, int div)
372{
373 struct snd_soc_codec *codec = codec_dai->codec;
374 u16 reg;
375
376 switch (div_id) {
377 case WM8974_OPCLKDIV:
1e97f50b
MB
378 reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
379 snd_soc_write(codec, WM8974_GPIO, reg | div);
0a1bf553
MB
380 break;
381 case WM8974_MCLKDIV:
1e97f50b
MB
382 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
383 snd_soc_write(codec, WM8974_CLOCK, reg | div);
0a1bf553 384 break;
0a1bf553 385 case WM8974_BCLKDIV:
1e97f50b
MB
386 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
387 snd_soc_write(codec, WM8974_CLOCK, reg | div);
0a1bf553
MB
388 break;
389 default:
390 return -EINVAL;
391 }
392
393 return 0;
394}
395
396static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
397 unsigned int fmt)
398{
399 struct snd_soc_codec *codec = codec_dai->codec;
400 u16 iface = 0;
1e97f50b 401 u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
0a1bf553
MB
402
403 /* set master/slave audio interface */
404 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
405 case SND_SOC_DAIFMT_CBM_CFM:
406 clk |= 0x0001;
407 break;
408 case SND_SOC_DAIFMT_CBS_CFS:
409 break;
410 default:
411 return -EINVAL;
412 }
413
414 /* interface format */
415 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
416 case SND_SOC_DAIFMT_I2S:
417 iface |= 0x0010;
418 break;
419 case SND_SOC_DAIFMT_RIGHT_J:
420 break;
421 case SND_SOC_DAIFMT_LEFT_J:
422 iface |= 0x0008;
423 break;
424 case SND_SOC_DAIFMT_DSP_A:
425 iface |= 0x00018;
426 break;
427 default:
428 return -EINVAL;
429 }
430
431 /* clock inversion */
432 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
433 case SND_SOC_DAIFMT_NB_NF:
434 break;
435 case SND_SOC_DAIFMT_IB_IF:
436 iface |= 0x0180;
437 break;
438 case SND_SOC_DAIFMT_IB_NF:
439 iface |= 0x0100;
440 break;
441 case SND_SOC_DAIFMT_NB_IF:
442 iface |= 0x0080;
443 break;
444 default:
445 return -EINVAL;
446 }
447
1e97f50b
MB
448 snd_soc_write(codec, WM8974_IFACE, iface);
449 snd_soc_write(codec, WM8974_CLOCK, clk);
0a1bf553
MB
450 return 0;
451}
452
453static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
454 struct snd_pcm_hw_params *params,
455 struct snd_soc_dai *dai)
456{
457 struct snd_soc_codec *codec = dai->codec;
1e97f50b
MB
458 u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
459 u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
0a1bf553
MB
460
461 /* bit size */
462 switch (params_format(params)) {
463 case SNDRV_PCM_FORMAT_S16_LE:
464 break;
465 case SNDRV_PCM_FORMAT_S20_3LE:
466 iface |= 0x0020;
467 break;
468 case SNDRV_PCM_FORMAT_S24_LE:
469 iface |= 0x0040;
470 break;
471 case SNDRV_PCM_FORMAT_S32_LE:
472 iface |= 0x0060;
473 break;
474 }
475
476 /* filter coefficient */
477 switch (params_rate(params)) {
b3172f22 478 case 8000:
0a1bf553
MB
479 adn |= 0x5 << 1;
480 break;
b3172f22 481 case 11025:
0a1bf553
MB
482 adn |= 0x4 << 1;
483 break;
b3172f22 484 case 16000:
0a1bf553
MB
485 adn |= 0x3 << 1;
486 break;
b3172f22 487 case 22050:
0a1bf553
MB
488 adn |= 0x2 << 1;
489 break;
b3172f22 490 case 32000:
0a1bf553
MB
491 adn |= 0x1 << 1;
492 break;
b3172f22
GL
493 case 44100:
494 case 48000:
0a1bf553
MB
495 break;
496 }
497
1e97f50b
MB
498 snd_soc_write(codec, WM8974_IFACE, iface);
499 snd_soc_write(codec, WM8974_ADD, adn);
0a1bf553
MB
500 return 0;
501}
502
503static int wm8974_mute(struct snd_soc_dai *dai, int mute)
504{
505 struct snd_soc_codec *codec = dai->codec;
1e97f50b 506 u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
0a1bf553 507
1a55b3f6 508 if (mute)
1e97f50b 509 snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
0a1bf553 510 else
1e97f50b 511 snd_soc_write(codec, WM8974_DAC, mute_reg);
0a1bf553
MB
512 return 0;
513}
514
515/* liam need to make this lower power with dapm */
516static int wm8974_set_bias_level(struct snd_soc_codec *codec,
517 enum snd_soc_bias_level level)
518{
1e97f50b 519 u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
df1ef7a3 520
0a1bf553
MB
521 switch (level) {
522 case SND_SOC_BIAS_ON:
0a1bf553 523 case SND_SOC_BIAS_PREPARE:
df1ef7a3 524 power1 |= 0x1; /* VMID 50k */
1e97f50b 525 snd_soc_write(codec, WM8974_POWER1, power1);
0a1bf553 526 break;
df1ef7a3 527
0a1bf553 528 case SND_SOC_BIAS_STANDBY:
df1ef7a3
MB
529 power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
530
ce6120cc 531 if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
0bad3d84
AL
532 snd_soc_cache_sync(codec);
533
df1ef7a3 534 /* Initial cap charge at VMID 5k */
1e97f50b 535 snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
df1ef7a3
MB
536 mdelay(100);
537 }
538
539 power1 |= 0x2; /* VMID 500k */
1e97f50b 540 snd_soc_write(codec, WM8974_POWER1, power1);
0a1bf553 541 break;
df1ef7a3 542
0a1bf553 543 case SND_SOC_BIAS_OFF:
1e97f50b
MB
544 snd_soc_write(codec, WM8974_POWER1, 0);
545 snd_soc_write(codec, WM8974_POWER2, 0);
546 snd_soc_write(codec, WM8974_POWER3, 0);
0a1bf553
MB
547 break;
548 }
df1ef7a3 549
ce6120cc 550 codec->dapm.bias_level = level;
0a1bf553
MB
551 return 0;
552}
553
1a55b3f6 554#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
0a1bf553
MB
555
556#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
557 SNDRV_PCM_FMTBIT_S24_LE)
558
85e7652d 559static const struct snd_soc_dai_ops wm8974_ops = {
0a1bf553
MB
560 .hw_params = wm8974_pcm_hw_params,
561 .digital_mute = wm8974_mute,
562 .set_fmt = wm8974_set_dai_fmt,
563 .set_clkdiv = wm8974_set_dai_clkdiv,
564 .set_pll = wm8974_set_dai_pll,
565};
566
f0fba2ad
LG
567static struct snd_soc_dai_driver wm8974_dai = {
568 .name = "wm8974-hifi",
0a1bf553
MB
569 .playback = {
570 .stream_name = "Playback",
571 .channels_min = 1,
33d81af4 572 .channels_max = 2, /* Only 1 channel of data */
0a1bf553
MB
573 .rates = WM8974_RATES,
574 .formats = WM8974_FORMATS,},
575 .capture = {
576 .stream_name = "Capture",
577 .channels_min = 1,
33d81af4 578 .channels_max = 2, /* Only 1 channel of data */
0a1bf553
MB
579 .rates = WM8974_RATES,
580 .formats = WM8974_FORMATS,},
581 .ops = &wm8974_ops,
cb11d39e 582 .symmetric_rates = 1,
0a1bf553 583};
0a1bf553 584
84b315ee 585static int wm8974_suspend(struct snd_soc_codec *codec)
0a1bf553 586{
0a1bf553
MB
587 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
588 return 0;
589}
590
f0fba2ad 591static int wm8974_resume(struct snd_soc_codec *codec)
0a1bf553 592{
0a1bf553 593 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
0a1bf553
MB
594 return 0;
595}
596
f0fba2ad 597static int wm8974_probe(struct snd_soc_codec *codec)
0a1bf553 598{
0a1bf553
MB
599 int ret = 0;
600
f0fba2ad
LG
601 ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
602 if (ret < 0) {
603 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
604 return ret;
4fcbbb67 605 }
0a1bf553 606
f0fba2ad 607 ret = wm8974_reset(codec);
1a55b3f6 608 if (ret < 0) {
f0fba2ad
LG
609 dev_err(codec->dev, "Failed to issue reset\n");
610 return ret;
0a1bf553
MB
611 }
612
f0fba2ad 613 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
4fcbbb67
MB
614 snd_soc_add_controls(codec, wm8974_snd_controls,
615 ARRAY_SIZE(wm8974_snd_controls));
0a1bf553 616 wm8974_add_widgets(codec);
4fcbbb67 617
0a1bf553 618 return ret;
0a1bf553
MB
619}
620
4fcbbb67 621/* power down chip */
f0fba2ad 622static int wm8974_remove(struct snd_soc_codec *codec)
4fcbbb67 623{
f0fba2ad 624 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
4fcbbb67
MB
625 return 0;
626}
0a1bf553 627
f0fba2ad 628static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
4fcbbb67
MB
629 .probe = wm8974_probe,
630 .remove = wm8974_remove,
631 .suspend = wm8974_suspend,
632 .resume = wm8974_resume,
f0fba2ad
LG
633 .set_bias_level = wm8974_set_bias_level,
634 .reg_cache_size = ARRAY_SIZE(wm8974_reg),
635 .reg_word_size = sizeof(u16),
636 .reg_cache_default = wm8974_reg,
4fcbbb67 637};
0a1bf553 638
f0fba2ad 639#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
4fcbbb67
MB
640static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
641 const struct i2c_device_id *id)
0a1bf553 642{
4fcbbb67 643 struct wm8974_priv *wm8974;
f0fba2ad 644 int ret;
0a1bf553 645
4fcbbb67
MB
646 wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
647 if (wm8974 == NULL)
0a1bf553
MB
648 return -ENOMEM;
649
4fcbbb67 650 i2c_set_clientdata(i2c, wm8974);
0a1bf553 651
f0fba2ad
LG
652 ret = snd_soc_register_codec(&i2c->dev,
653 &soc_codec_dev_wm8974, &wm8974_dai, 1);
654 if (ret < 0)
655 kfree(wm8974);
656 return ret;
4fcbbb67 657}
0a1bf553 658
4fcbbb67
MB
659static __devexit int wm8974_i2c_remove(struct i2c_client *client)
660{
f0fba2ad
LG
661 snd_soc_unregister_codec(&client->dev);
662 kfree(i2c_get_clientdata(client));
0a1bf553
MB
663 return 0;
664}
665
4fcbbb67
MB
666static const struct i2c_device_id wm8974_i2c_id[] = {
667 { "wm8974", 0 },
668 { }
669};
670MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
671
672static struct i2c_driver wm8974_i2c_driver = {
673 .driver = {
f0fba2ad 674 .name = "wm8974-codec",
4fcbbb67
MB
675 .owner = THIS_MODULE,
676 },
677 .probe = wm8974_i2c_probe,
678 .remove = __devexit_p(wm8974_i2c_remove),
679 .id_table = wm8974_i2c_id,
0a1bf553 680};
f0fba2ad 681#endif
0a1bf553
MB
682
683static int __init wm8974_modinit(void)
684{
f0fba2ad
LG
685 int ret = 0;
686#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
687 ret = i2c_add_driver(&wm8974_i2c_driver);
688 if (ret != 0) {
689 printk(KERN_ERR "Failed to register wm8974 I2C driver: %d\n",
690 ret);
691 }
692#endif
693 return ret;
0a1bf553
MB
694}
695module_init(wm8974_modinit);
696
697static void __exit wm8974_exit(void)
698{
f0fba2ad 699#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
4fcbbb67 700 i2c_del_driver(&wm8974_i2c_driver);
f0fba2ad 701#endif
0a1bf553
MB
702}
703module_exit(wm8974_exit);
704
705MODULE_DESCRIPTION("ASoC WM8974 driver");
706MODULE_AUTHOR("Liam Girdwood");
707MODULE_LICENSE("GPL");
This page took 0.182082 seconds and 5 git commands to generate.