]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - sound/soc/codecs/wm8974.c
ASoC: TWL4030: Capture route runtime DAPM ordering fix
[mirror_ubuntu-bionic-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),
759512fb 184SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 0),
0a1bf553
MB
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);
29e189c2 612
0a1bf553
MB
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");
4eaac505
AL
673 ret = -EINVAL;
674 goto err;
4fcbbb67 675 }
0a1bf553 676
4fcbbb67
MB
677 mutex_init(&codec->mutex);
678 INIT_LIST_HEAD(&codec->dapm_widgets);
679 INIT_LIST_HEAD(&codec->dapm_paths);
0a1bf553 680
b2c812e2 681 snd_soc_codec_set_drvdata(codec, wm8974);
4fcbbb67
MB
682 codec->name = "WM8974";
683 codec->owner = THIS_MODULE;
4fcbbb67
MB
684 codec->bias_level = SND_SOC_BIAS_OFF;
685 codec->set_bias_level = wm8974_set_bias_level;
686 codec->dai = &wm8974_dai;
687 codec->num_dai = 1;
688 codec->reg_cache_size = WM8974_CACHEREGNUM;
689 codec->reg_cache = &wm8974->reg_cache;
0a1bf553 690
1e97f50b
MB
691 ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
692 if (ret < 0) {
693 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
694 goto err;
695 }
696
4fcbbb67
MB
697 memcpy(codec->reg_cache, wm8974_reg, sizeof(wm8974_reg));
698
699 ret = wm8974_reset(codec);
0a1bf553 700 if (ret < 0) {
4fcbbb67 701 dev_err(codec->dev, "Failed to issue reset\n");
1e97f50b 702 goto err;
0a1bf553
MB
703 }
704
4fcbbb67
MB
705 wm8974_dai.dev = codec->dev;
706
707 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
708
709 wm8974_codec = codec;
710
711 ret = snd_soc_register_codec(codec);
712 if (ret != 0) {
713 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
1e97f50b 714 goto err;
0a1bf553 715 }
0a1bf553 716
4fcbbb67
MB
717 ret = snd_soc_register_dai(&wm8974_dai);
718 if (ret != 0) {
719 dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
1e97f50b 720 goto err_codec;
4fcbbb67 721 }
0a1bf553 722
0a1bf553 723 return 0;
1e97f50b
MB
724
725err_codec:
726 snd_soc_unregister_codec(codec);
727err:
728 kfree(wm8974);
729 return ret;
0a1bf553
MB
730}
731
4fcbbb67 732static __devexit void wm8974_unregister(struct wm8974_priv *wm8974)
0a1bf553 733{
4fcbbb67
MB
734 wm8974_set_bias_level(&wm8974->codec, SND_SOC_BIAS_OFF);
735 snd_soc_unregister_dai(&wm8974_dai);
736 snd_soc_unregister_codec(&wm8974->codec);
737 kfree(wm8974);
738 wm8974_codec = NULL;
0a1bf553
MB
739}
740
4fcbbb67
MB
741static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
742 const struct i2c_device_id *id)
0a1bf553 743{
4fcbbb67 744 struct wm8974_priv *wm8974;
0a1bf553 745 struct snd_soc_codec *codec;
0a1bf553 746
4fcbbb67
MB
747 wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
748 if (wm8974 == NULL)
0a1bf553
MB
749 return -ENOMEM;
750
4fcbbb67
MB
751 codec = &wm8974->codec;
752 codec->hw_write = (hw_write_t)i2c_master_send;
0a1bf553 753
4fcbbb67
MB
754 i2c_set_clientdata(i2c, wm8974);
755 codec->control_data = i2c;
0a1bf553 756
4fcbbb67 757 codec->dev = &i2c->dev;
0a1bf553 758
4fcbbb67
MB
759 return wm8974_register(wm8974);
760}
0a1bf553 761
4fcbbb67
MB
762static __devexit int wm8974_i2c_remove(struct i2c_client *client)
763{
764 struct wm8974_priv *wm8974 = i2c_get_clientdata(client);
765 wm8974_unregister(wm8974);
0a1bf553
MB
766 return 0;
767}
768
4fcbbb67
MB
769static const struct i2c_device_id wm8974_i2c_id[] = {
770 { "wm8974", 0 },
771 { }
772};
773MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
774
775static struct i2c_driver wm8974_i2c_driver = {
776 .driver = {
8b83a193 777 .name = "WM8974",
4fcbbb67
MB
778 .owner = THIS_MODULE,
779 },
780 .probe = wm8974_i2c_probe,
781 .remove = __devexit_p(wm8974_i2c_remove),
782 .id_table = wm8974_i2c_id,
0a1bf553 783};
0a1bf553
MB
784
785static int __init wm8974_modinit(void)
786{
4fcbbb67 787 return i2c_add_driver(&wm8974_i2c_driver);
0a1bf553
MB
788}
789module_init(wm8974_modinit);
790
791static void __exit wm8974_exit(void)
792{
4fcbbb67 793 i2c_del_driver(&wm8974_i2c_driver);
0a1bf553
MB
794}
795module_exit(wm8974_exit);
796
797MODULE_DESCRIPTION("ASoC WM8974 driver");
798MODULE_AUTHOR("Liam Girdwood");
799MODULE_LICENSE("GPL");