]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - sound/soc/codecs/wm8974.c
ASoC: Add source argument to PLL configuration
[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>
15#include <linux/version.h>
16#include <linux/kernel.h>
17#include <linux/init.h>
18#include <linux/delay.h>
19#include <linux/pm.h>
20#include <linux/i2c.h>
21#include <linux/platform_device.h>
22#include <sound/core.h>
23#include <sound/pcm.h>
24#include <sound/pcm_params.h>
25#include <sound/soc.h>
26#include <sound/soc-dapm.h>
27#include <sound/initval.h>
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
MB
50#define WM8974_POWER1_BIASEN 0x08
51#define WM8974_POWER1_BUFIOEN 0x10
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),
0a1bf553
MB
174};
175
0a1bf553
MB
176/* Speaker Output Mixer */
177static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
178SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
179SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
180SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1),
181};
182
183/* Mono Output Mixer */
184static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
185SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
186SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
8a123ee2
MB
187SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0),
188};
189
190/* Boost mixer */
191static const struct snd_kcontrol_new wm8974_boost_mixer[] = {
192SOC_DAPM_SINGLE("Aux Switch", WM8974_INPPGA, 6, 1, 0),
193};
194
195/* Input PGA */
196static const struct snd_kcontrol_new wm8974_inpga[] = {
197SOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0),
198SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0),
199SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0),
0a1bf553
MB
200};
201
202/* AUX Input boost vol */
203static const struct snd_kcontrol_new wm8974_aux_boost_controls =
204SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
205
206/* Mic Input boost vol */
207static const struct snd_kcontrol_new wm8974_mic_boost_controls =
208SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
209
0a1bf553
MB
210static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
211SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
212 &wm8974_speaker_mixer_controls[0],
213 ARRAY_SIZE(wm8974_speaker_mixer_controls)),
214SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
215 &wm8974_mono_mixer_controls[0],
216 ARRAY_SIZE(wm8974_mono_mixer_controls)),
217SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
8a123ee2 218SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0),
0a1bf553
MB
219SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
220SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
221SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
222SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
0a1bf553 223
8a123ee2
MB
224SND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga,
225 ARRAY_SIZE(wm8974_inpga)),
226SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0,
227 wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)),
0a1bf553
MB
228
229SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
230
231SND_SOC_DAPM_INPUT("MICN"),
232SND_SOC_DAPM_INPUT("MICP"),
233SND_SOC_DAPM_INPUT("AUX"),
234SND_SOC_DAPM_OUTPUT("MONOOUT"),
235SND_SOC_DAPM_OUTPUT("SPKOUTP"),
236SND_SOC_DAPM_OUTPUT("SPKOUTN"),
237};
238
239static const struct snd_soc_dapm_route audio_map[] = {
240 /* Mono output mixer */
241 {"Mono Mixer", "PCM Playback Switch", "DAC"},
242 {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
243 {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
244
245 /* Speaker output mixer */
246 {"Speaker Mixer", "PCM Playback Switch", "DAC"},
247 {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
248 {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
249
250 /* Outputs */
251 {"Mono Out", NULL, "Mono Mixer"},
252 {"MONOOUT", NULL, "Mono Out"},
253 {"SpkN Out", NULL, "Speaker Mixer"},
254 {"SpkP Out", NULL, "Speaker Mixer"},
255 {"SPKOUTN", NULL, "SpkN Out"},
256 {"SPKOUTP", NULL, "SpkP Out"},
257
258 /* Boost Mixer */
8a123ee2
MB
259 {"ADC", NULL, "Boost Mixer"},
260 {"Boost Mixer", "Aux Switch", "Aux Input"},
261 {"Boost Mixer", NULL, "Input PGA"},
262 {"Boost Mixer", NULL, "MICP"},
263
264 /* Input PGA */
265 {"Input PGA", "Aux Switch", "Aux Input"},
266 {"Input PGA", "MicN Switch", "MICN"},
267 {"Input PGA", "MicP Switch", "MICP"},
0a1bf553
MB
268
269 /* Inputs */
8a123ee2 270 {"Aux Input", NULL, "AUX"},
0a1bf553
MB
271};
272
273static int wm8974_add_widgets(struct snd_soc_codec *codec)
274{
275 snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets,
276 ARRAY_SIZE(wm8974_dapm_widgets));
277
278 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
279
280 snd_soc_dapm_new_widgets(codec);
281 return 0;
282}
283
284struct pll_ {
91d0c3ec 285 unsigned int pre_div:4; /* prescale - 1 */
0a1bf553
MB
286 unsigned int n:4;
287 unsigned int k;
288};
289
91d0c3ec
MB
290static struct pll_ pll_div;
291
292/* The size in bits of the pll divide multiplied by 10
293 * to allow rounding later */
294#define FIXED_PLL_SIZE ((1 << 24) * 10)
295
296static void pll_factors(unsigned int target, unsigned int source)
297{
298 unsigned long long Kpart;
299 unsigned int K, Ndiv, Nmod;
300
301 Ndiv = target / source;
302 if (Ndiv < 6) {
303 source >>= 1;
304 pll_div.pre_div = 1;
305 Ndiv = target / source;
306 } else
307 pll_div.pre_div = 0;
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
314 pll_div.n = Ndiv;
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
329 pll_div.k = K;
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;
0a1bf553
MB
336 u16 reg;
337
1a55b3f6 338 if (freq_in == 0 || freq_out == 0) {
91d0c3ec 339 /* Clock CODEC directly from MCLK */
1e97f50b
MB
340 reg = snd_soc_read(codec, WM8974_CLOCK);
341 snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
91d0c3ec
MB
342
343 /* Turn off PLL */
1e97f50b
MB
344 reg = snd_soc_read(codec, WM8974_POWER1);
345 snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
0a1bf553
MB
346 return 0;
347 }
348
91d0c3ec
MB
349 pll_factors(freq_out*4, freq_in);
350
1e97f50b
MB
351 snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
352 snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
353 snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
354 snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
355 reg = snd_soc_read(codec, WM8974_POWER1);
356 snd_soc_write(codec, WM8974_POWER1, reg | 0x020);
1a55b3f6 357
91d0c3ec 358 /* Run CODEC from PLL instead of MCLK */
1e97f50b
MB
359 reg = snd_soc_read(codec, WM8974_CLOCK);
360 snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
91d0c3ec
MB
361
362 return 0;
0a1bf553
MB
363}
364
365/*
366 * Configure WM8974 clock dividers.
367 */
368static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
369 int div_id, int div)
370{
371 struct snd_soc_codec *codec = codec_dai->codec;
372 u16 reg;
373
374 switch (div_id) {
375 case WM8974_OPCLKDIV:
1e97f50b
MB
376 reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
377 snd_soc_write(codec, WM8974_GPIO, reg | div);
0a1bf553
MB
378 break;
379 case WM8974_MCLKDIV:
1e97f50b
MB
380 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
381 snd_soc_write(codec, WM8974_CLOCK, reg | div);
0a1bf553
MB
382 break;
383 case WM8974_ADCCLK:
1e97f50b
MB
384 reg = snd_soc_read(codec, WM8974_ADC) & 0x1f7;
385 snd_soc_write(codec, WM8974_ADC, reg | div);
0a1bf553
MB
386 break;
387 case WM8974_DACCLK:
1e97f50b
MB
388 reg = snd_soc_read(codec, WM8974_DAC) & 0x1f7;
389 snd_soc_write(codec, WM8974_DAC, reg | div);
0a1bf553
MB
390 break;
391 case WM8974_BCLKDIV:
1e97f50b
MB
392 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
393 snd_soc_write(codec, WM8974_CLOCK, reg | div);
0a1bf553
MB
394 break;
395 default:
396 return -EINVAL;
397 }
398
399 return 0;
400}
401
402static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
403 unsigned int fmt)
404{
405 struct snd_soc_codec *codec = codec_dai->codec;
406 u16 iface = 0;
1e97f50b 407 u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
0a1bf553
MB
408
409 /* set master/slave audio interface */
410 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
411 case SND_SOC_DAIFMT_CBM_CFM:
412 clk |= 0x0001;
413 break;
414 case SND_SOC_DAIFMT_CBS_CFS:
415 break;
416 default:
417 return -EINVAL;
418 }
419
420 /* interface format */
421 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
422 case SND_SOC_DAIFMT_I2S:
423 iface |= 0x0010;
424 break;
425 case SND_SOC_DAIFMT_RIGHT_J:
426 break;
427 case SND_SOC_DAIFMT_LEFT_J:
428 iface |= 0x0008;
429 break;
430 case SND_SOC_DAIFMT_DSP_A:
431 iface |= 0x00018;
432 break;
433 default:
434 return -EINVAL;
435 }
436
437 /* clock inversion */
438 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
439 case SND_SOC_DAIFMT_NB_NF:
440 break;
441 case SND_SOC_DAIFMT_IB_IF:
442 iface |= 0x0180;
443 break;
444 case SND_SOC_DAIFMT_IB_NF:
445 iface |= 0x0100;
446 break;
447 case SND_SOC_DAIFMT_NB_IF:
448 iface |= 0x0080;
449 break;
450 default:
451 return -EINVAL;
452 }
453
1e97f50b
MB
454 snd_soc_write(codec, WM8974_IFACE, iface);
455 snd_soc_write(codec, WM8974_CLOCK, clk);
0a1bf553
MB
456 return 0;
457}
458
459static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
460 struct snd_pcm_hw_params *params,
461 struct snd_soc_dai *dai)
462{
463 struct snd_soc_codec *codec = dai->codec;
1e97f50b
MB
464 u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
465 u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
0a1bf553
MB
466
467 /* bit size */
468 switch (params_format(params)) {
469 case SNDRV_PCM_FORMAT_S16_LE:
470 break;
471 case SNDRV_PCM_FORMAT_S20_3LE:
472 iface |= 0x0020;
473 break;
474 case SNDRV_PCM_FORMAT_S24_LE:
475 iface |= 0x0040;
476 break;
477 case SNDRV_PCM_FORMAT_S32_LE:
478 iface |= 0x0060;
479 break;
480 }
481
482 /* filter coefficient */
483 switch (params_rate(params)) {
484 case SNDRV_PCM_RATE_8000:
485 adn |= 0x5 << 1;
486 break;
487 case SNDRV_PCM_RATE_11025:
488 adn |= 0x4 << 1;
489 break;
490 case SNDRV_PCM_RATE_16000:
491 adn |= 0x3 << 1;
492 break;
493 case SNDRV_PCM_RATE_22050:
494 adn |= 0x2 << 1;
495 break;
496 case SNDRV_PCM_RATE_32000:
497 adn |= 0x1 << 1;
498 break;
499 case SNDRV_PCM_RATE_44100:
8b83a193 500 case SNDRV_PCM_RATE_48000:
0a1bf553
MB
501 break;
502 }
503
1e97f50b
MB
504 snd_soc_write(codec, WM8974_IFACE, iface);
505 snd_soc_write(codec, WM8974_ADD, adn);
0a1bf553
MB
506 return 0;
507}
508
509static int wm8974_mute(struct snd_soc_dai *dai, int mute)
510{
511 struct snd_soc_codec *codec = dai->codec;
1e97f50b 512 u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
0a1bf553 513
1a55b3f6 514 if (mute)
1e97f50b 515 snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
0a1bf553 516 else
1e97f50b 517 snd_soc_write(codec, WM8974_DAC, mute_reg);
0a1bf553
MB
518 return 0;
519}
520
521/* liam need to make this lower power with dapm */
522static int wm8974_set_bias_level(struct snd_soc_codec *codec,
523 enum snd_soc_bias_level level)
524{
1e97f50b 525 u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
df1ef7a3 526
0a1bf553
MB
527 switch (level) {
528 case SND_SOC_BIAS_ON:
0a1bf553 529 case SND_SOC_BIAS_PREPARE:
df1ef7a3 530 power1 |= 0x1; /* VMID 50k */
1e97f50b 531 snd_soc_write(codec, WM8974_POWER1, power1);
0a1bf553 532 break;
df1ef7a3 533
0a1bf553 534 case SND_SOC_BIAS_STANDBY:
df1ef7a3
MB
535 power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
536
537 if (codec->bias_level == SND_SOC_BIAS_OFF) {
538 /* Initial cap charge at VMID 5k */
1e97f50b 539 snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
df1ef7a3
MB
540 mdelay(100);
541 }
542
543 power1 |= 0x2; /* VMID 500k */
1e97f50b 544 snd_soc_write(codec, WM8974_POWER1, power1);
0a1bf553 545 break;
df1ef7a3 546
0a1bf553 547 case SND_SOC_BIAS_OFF:
1e97f50b
MB
548 snd_soc_write(codec, WM8974_POWER1, 0);
549 snd_soc_write(codec, WM8974_POWER2, 0);
550 snd_soc_write(codec, WM8974_POWER3, 0);
0a1bf553
MB
551 break;
552 }
df1ef7a3 553
0a1bf553
MB
554 codec->bias_level = level;
555 return 0;
556}
557
1a55b3f6 558#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
0a1bf553
MB
559
560#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
561 SNDRV_PCM_FMTBIT_S24_LE)
562
563static struct snd_soc_dai_ops wm8974_ops = {
564 .hw_params = wm8974_pcm_hw_params,
565 .digital_mute = wm8974_mute,
566 .set_fmt = wm8974_set_dai_fmt,
567 .set_clkdiv = wm8974_set_dai_clkdiv,
568 .set_pll = wm8974_set_dai_pll,
569};
570
571struct snd_soc_dai wm8974_dai = {
572 .name = "WM8974 HiFi",
573 .playback = {
574 .stream_name = "Playback",
575 .channels_min = 1,
33d81af4 576 .channels_max = 2, /* Only 1 channel of data */
0a1bf553
MB
577 .rates = WM8974_RATES,
578 .formats = WM8974_FORMATS,},
579 .capture = {
580 .stream_name = "Capture",
581 .channels_min = 1,
33d81af4 582 .channels_max = 2, /* Only 1 channel of data */
0a1bf553
MB
583 .rates = WM8974_RATES,
584 .formats = WM8974_FORMATS,},
585 .ops = &wm8974_ops,
cb11d39e 586 .symmetric_rates = 1,
0a1bf553
MB
587};
588EXPORT_SYMBOL_GPL(wm8974_dai);
589
590static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
591{
592 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
593 struct snd_soc_codec *codec = socdev->card->codec;
594
595 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
596 return 0;
597}
598
599static int wm8974_resume(struct platform_device *pdev)
600{
601 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
602 struct snd_soc_codec *codec = socdev->card->codec;
603 int i;
604 u8 data[2];
605 u16 *cache = codec->reg_cache;
606
607 /* Sync reg_cache with the hardware */
608 for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
609 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
610 data[1] = cache[i] & 0x00ff;
611 codec->hw_write(codec->control_data, data, 2);
612 }
613 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
614 wm8974_set_bias_level(codec, codec->suspend_bias_level);
615 return 0;
616}
617
4fcbbb67 618static int wm8974_probe(struct platform_device *pdev)
0a1bf553 619{
4fcbbb67
MB
620 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
621 struct snd_soc_codec *codec;
0a1bf553
MB
622 int ret = 0;
623
4fcbbb67
MB
624 if (wm8974_codec == NULL) {
625 dev_err(&pdev->dev, "Codec device not registered\n");
626 return -ENODEV;
627 }
0a1bf553 628
4fcbbb67
MB
629 socdev->card->codec = wm8974_codec;
630 codec = wm8974_codec;
0a1bf553
MB
631
632 /* register pcms */
633 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
1a55b3f6 634 if (ret < 0) {
4fcbbb67 635 dev_err(codec->dev, "failed to create pcms: %d\n", ret);
0a1bf553
MB
636 goto pcm_err;
637 }
638
4fcbbb67
MB
639 snd_soc_add_controls(codec, wm8974_snd_controls,
640 ARRAY_SIZE(wm8974_snd_controls));
0a1bf553
MB
641 wm8974_add_widgets(codec);
642 ret = snd_soc_init_card(socdev);
643 if (ret < 0) {
4fcbbb67 644 dev_err(codec->dev, "failed to register card: %d\n", ret);
0a1bf553
MB
645 goto card_err;
646 }
4fcbbb67 647
0a1bf553
MB
648 return ret;
649
650card_err:
651 snd_soc_free_pcms(socdev);
652 snd_soc_dapm_free(socdev);
653pcm_err:
0a1bf553
MB
654 return ret;
655}
656
4fcbbb67
MB
657/* power down chip */
658static int wm8974_remove(struct platform_device *pdev)
659{
660 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
0a1bf553 661
4fcbbb67
MB
662 snd_soc_free_pcms(socdev);
663 snd_soc_dapm_free(socdev);
0a1bf553 664
4fcbbb67
MB
665 return 0;
666}
0a1bf553 667
4fcbbb67
MB
668struct snd_soc_codec_device soc_codec_dev_wm8974 = {
669 .probe = wm8974_probe,
670 .remove = wm8974_remove,
671 .suspend = wm8974_suspend,
672 .resume = wm8974_resume,
673};
674EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
0a1bf553 675
4fcbbb67 676static __devinit int wm8974_register(struct wm8974_priv *wm8974)
0a1bf553 677{
0a1bf553 678 int ret;
4fcbbb67 679 struct snd_soc_codec *codec = &wm8974->codec;
0a1bf553 680
4fcbbb67
MB
681 if (wm8974_codec) {
682 dev_err(codec->dev, "Another WM8974 is registered\n");
683 return -EINVAL;
684 }
0a1bf553 685
4fcbbb67
MB
686 mutex_init(&codec->mutex);
687 INIT_LIST_HEAD(&codec->dapm_widgets);
688 INIT_LIST_HEAD(&codec->dapm_paths);
0a1bf553 689
4fcbbb67
MB
690 codec->private_data = wm8974;
691 codec->name = "WM8974";
692 codec->owner = THIS_MODULE;
4fcbbb67
MB
693 codec->bias_level = SND_SOC_BIAS_OFF;
694 codec->set_bias_level = wm8974_set_bias_level;
695 codec->dai = &wm8974_dai;
696 codec->num_dai = 1;
697 codec->reg_cache_size = WM8974_CACHEREGNUM;
698 codec->reg_cache = &wm8974->reg_cache;
0a1bf553 699
1e97f50b
MB
700 ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
701 if (ret < 0) {
702 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
703 goto err;
704 }
705
4fcbbb67
MB
706 memcpy(codec->reg_cache, wm8974_reg, sizeof(wm8974_reg));
707
708 ret = wm8974_reset(codec);
0a1bf553 709 if (ret < 0) {
4fcbbb67 710 dev_err(codec->dev, "Failed to issue reset\n");
1e97f50b 711 goto err;
0a1bf553
MB
712 }
713
4fcbbb67
MB
714 wm8974_dai.dev = codec->dev;
715
716 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
717
718 wm8974_codec = codec;
719
720 ret = snd_soc_register_codec(codec);
721 if (ret != 0) {
722 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
1e97f50b 723 goto err;
0a1bf553 724 }
0a1bf553 725
4fcbbb67
MB
726 ret = snd_soc_register_dai(&wm8974_dai);
727 if (ret != 0) {
728 dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
1e97f50b 729 goto err_codec;
4fcbbb67 730 }
0a1bf553 731
0a1bf553 732 return 0;
1e97f50b
MB
733
734err_codec:
735 snd_soc_unregister_codec(codec);
736err:
737 kfree(wm8974);
738 return ret;
0a1bf553
MB
739}
740
4fcbbb67 741static __devexit void wm8974_unregister(struct wm8974_priv *wm8974)
0a1bf553 742{
4fcbbb67
MB
743 wm8974_set_bias_level(&wm8974->codec, SND_SOC_BIAS_OFF);
744 snd_soc_unregister_dai(&wm8974_dai);
745 snd_soc_unregister_codec(&wm8974->codec);
746 kfree(wm8974);
747 wm8974_codec = NULL;
0a1bf553
MB
748}
749
4fcbbb67
MB
750static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
751 const struct i2c_device_id *id)
0a1bf553 752{
4fcbbb67 753 struct wm8974_priv *wm8974;
0a1bf553 754 struct snd_soc_codec *codec;
0a1bf553 755
4fcbbb67
MB
756 wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
757 if (wm8974 == NULL)
0a1bf553
MB
758 return -ENOMEM;
759
4fcbbb67
MB
760 codec = &wm8974->codec;
761 codec->hw_write = (hw_write_t)i2c_master_send;
0a1bf553 762
4fcbbb67
MB
763 i2c_set_clientdata(i2c, wm8974);
764 codec->control_data = i2c;
0a1bf553 765
4fcbbb67 766 codec->dev = &i2c->dev;
0a1bf553 767
4fcbbb67
MB
768 return wm8974_register(wm8974);
769}
0a1bf553 770
4fcbbb67
MB
771static __devexit int wm8974_i2c_remove(struct i2c_client *client)
772{
773 struct wm8974_priv *wm8974 = i2c_get_clientdata(client);
774 wm8974_unregister(wm8974);
0a1bf553
MB
775 return 0;
776}
777
4fcbbb67
MB
778static const struct i2c_device_id wm8974_i2c_id[] = {
779 { "wm8974", 0 },
780 { }
781};
782MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
783
784static struct i2c_driver wm8974_i2c_driver = {
785 .driver = {
8b83a193 786 .name = "WM8974",
4fcbbb67
MB
787 .owner = THIS_MODULE,
788 },
789 .probe = wm8974_i2c_probe,
790 .remove = __devexit_p(wm8974_i2c_remove),
791 .id_table = wm8974_i2c_id,
0a1bf553 792};
0a1bf553
MB
793
794static int __init wm8974_modinit(void)
795{
4fcbbb67 796 return i2c_add_driver(&wm8974_i2c_driver);
0a1bf553
MB
797}
798module_init(wm8974_modinit);
799
800static void __exit wm8974_exit(void)
801{
4fcbbb67 802 i2c_del_driver(&wm8974_i2c_driver);
0a1bf553
MB
803}
804module_exit(wm8974_exit);
805
806MODULE_DESCRIPTION("ASoC WM8974 driver");
807MODULE_AUTHOR("Liam Girdwood");
808MODULE_LICENSE("GPL");