blob: 98d663afc97d290e9d954eb08d37e007b16fbfae [file] [log] [blame]
Mark Brown0a1bf552009-05-23 11:18:41 +01001/*
2 * wm8974.c -- WM8974 ALSA Soc Audio driver
3 *
Mark Brown8b83a192009-06-30 19:37:02 +01004 * Copyright 2006-2009 Wolfson Microelectronics PLC.
Mark Brown0a1bf552009-05-23 11:18:41 +01005 *
Mark Brown4fcbbb62009-05-23 12:27:03 +01006 * Author: Liam Girdwood <linux@wolfsonmicro.com>
Mark Brown0a1bf552009-05-23 11:18:41 +01007 *
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>
Mark Brown0a1bf552009-05-23 11:18:41 +010015#include <linux/kernel.h>
16#include <linux/init.h>
17#include <linux/delay.h>
18#include <linux/pm.h>
19#include <linux/i2c.h>
20#include <linux/platform_device.h>
21#include <sound/core.h>
22#include <sound/pcm.h>
23#include <sound/pcm_params.h>
24#include <sound/soc.h>
25#include <sound/soc-dapm.h>
26#include <sound/initval.h>
Mark Browna5f8d2f2009-06-30 19:30:33 +010027#include <sound/tlv.h>
Mark Brown0a1bf552009-05-23 11:18:41 +010028
29#include "wm8974.h"
30
Mark Brown0a1bf552009-05-23 11:18:41 +010031static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
Mark Brown1a55b3f2009-05-23 11:31:40 +010032 0x0000, 0x0000, 0x0000, 0x0000,
33 0x0050, 0x0000, 0x0140, 0x0000,
34 0x0000, 0x0000, 0x0000, 0x00ff,
35 0x0000, 0x0000, 0x0100, 0x00ff,
36 0x0000, 0x0000, 0x012c, 0x002c,
37 0x002c, 0x002c, 0x002c, 0x0000,
38 0x0032, 0x0000, 0x0000, 0x0000,
39 0x0000, 0x0000, 0x0000, 0x0000,
40 0x0038, 0x000b, 0x0032, 0x0000,
41 0x0008, 0x000c, 0x0093, 0x00e9,
42 0x0000, 0x0000, 0x0000, 0x0000,
43 0x0003, 0x0010, 0x0000, 0x0000,
44 0x0000, 0x0002, 0x0000, 0x0000,
45 0x0000, 0x0000, 0x0039, 0x0000,
46 0x0000,
Mark Brown0a1bf552009-05-23 11:18:41 +010047};
48
Mark Browndf1ef7a2009-06-30 19:01:09 +010049#define WM8974_POWER1_BIASEN 0x08
50#define WM8974_POWER1_BUFIOEN 0x10
51
Mark Brown4fcbbb62009-05-23 12:27:03 +010052struct wm8974_priv {
53 struct snd_soc_codec codec;
54 u16 reg_cache[WM8974_CACHEREGNUM];
55};
56
57static struct snd_soc_codec *wm8974_codec;
58
Mark Brown1e97f502009-08-15 12:15:10 +010059#define wm8974_reset(c) snd_soc_write(c, WM8974_RESET, 0)
Mark Brown0a1bf552009-05-23 11:18:41 +010060
61static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
62static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
63static const char *wm8974_eqmode[] = {"Capture", "Playback" };
64static const char *wm8974_bw[] = {"Narrow", "Wide" };
65static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
66static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
67static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
68static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
69static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
70static const char *wm8974_alc[] = {"ALC", "Limiter" };
71
72static const struct soc_enum wm8974_enum[] = {
73 SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
74 SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
75 SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp),
76 SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode),
77
78 SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1),
79 SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw),
80 SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2),
81 SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw),
82
83 SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3),
84 SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw),
85 SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4),
86 SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw),
87
88 SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5),
89 SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc),
90};
91
Mark Brown8a123ee2009-06-30 21:10:34 +010092static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" };
93
94static const struct soc_enum wm8974_auxmode =
95 SOC_ENUM_SINGLE(WM8974_INPUT, 3, 2, wm8974_auxmode_text);
96
Mark Browna5f8d2f2009-06-30 19:30:33 +010097static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
98static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
99static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
100static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
101
Mark Brown0a1bf552009-05-23 11:18:41 +0100102static const struct snd_kcontrol_new wm8974_snd_controls[] = {
103
104SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
105
106SOC_ENUM("DAC Companding", wm8974_enum[1]),
107SOC_ENUM("ADC Companding", wm8974_enum[0]),
108
109SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
110SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
111
Mark Browna5f8d2f2009-06-30 19:30:33 +0100112SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100113
114SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
115SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
javier Martin25cbf462009-07-21 11:15:06 +0200116SOC_SINGLE("ADC Inversion Switch", WM8974_ADC, 0, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100117
Mark Browna5f8d2f2009-06-30 19:30:33 +0100118SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL, 0, 255, 0, digital_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100119
120SOC_ENUM("Equaliser Function", wm8974_enum[3]),
121SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100122SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100123
124SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
125SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100126SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100127
128SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
129SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100130SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100131
132SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
133SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100134SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100135
136SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
137SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100138SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100139
140SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0),
141SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0),
142SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0),
143
144SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0),
145SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0),
146
147SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0),
148SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0),
149SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0),
150
151SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0),
152SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0),
153SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0),
154
155SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
156SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0),
157SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0),
158
159SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0),
160SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0),
161
162SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100163SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0, inpga_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100164
165SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0),
166SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1),
Mark Brown8a123ee2009-06-30 21:10:34 +0100167SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0, spk_tlv),
168
169SOC_ENUM("Aux Mode", wm8974_auxmode),
Mark Brown0a1bf552009-05-23 11:18:41 +0100170
171SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100172SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1),
Mark Brown0a1bf552009-05-23 11:18:41 +0100173};
174
Mark Brown0a1bf552009-05-23 11:18:41 +0100175/* Speaker Output Mixer */
176static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
177SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
178SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
179SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1),
180};
181
182/* Mono Output Mixer */
183static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
184SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
185SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100186SOC_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),
Mark Brown0a1bf552009-05-23 11:18:41 +0100199};
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
Mark Brown0a1bf552009-05-23 11:18:41 +0100209static 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),
Mark Brown8a123ee2009-06-30 21:10:34 +0100217SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100218SND_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),
Mark Brown0a1bf552009-05-23 11:18:41 +0100222
Mark Brown8a123ee2009-06-30 21:10:34 +0100223SND_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)),
Mark Brown0a1bf552009-05-23 11:18:41 +0100227
228SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
229
230SND_SOC_DAPM_INPUT("MICN"),
231SND_SOC_DAPM_INPUT("MICP"),
232SND_SOC_DAPM_INPUT("AUX"),
233SND_SOC_DAPM_OUTPUT("MONOOUT"),
234SND_SOC_DAPM_OUTPUT("SPKOUTP"),
235SND_SOC_DAPM_OUTPUT("SPKOUTN"),
236};
237
238static const struct snd_soc_dapm_route audio_map[] = {
239 /* Mono output mixer */
240 {"Mono Mixer", "PCM Playback Switch", "DAC"},
241 {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
242 {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
243
244 /* Speaker output mixer */
245 {"Speaker Mixer", "PCM Playback Switch", "DAC"},
246 {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
247 {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
248
249 /* Outputs */
250 {"Mono Out", NULL, "Mono Mixer"},
251 {"MONOOUT", NULL, "Mono Out"},
252 {"SpkN Out", NULL, "Speaker Mixer"},
253 {"SpkP Out", NULL, "Speaker Mixer"},
254 {"SPKOUTN", NULL, "SpkN Out"},
255 {"SPKOUTP", NULL, "SpkP Out"},
256
257 /* Boost Mixer */
Mark Brown8a123ee2009-06-30 21:10:34 +0100258 {"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"},
Mark Brown0a1bf552009-05-23 11:18:41 +0100267
268 /* Inputs */
Mark Brown8a123ee2009-06-30 21:10:34 +0100269 {"Aux Input", NULL, "AUX"},
Mark Brown0a1bf552009-05-23 11:18:41 +0100270};
271
272static int wm8974_add_widgets(struct snd_soc_codec *codec)
273{
274 snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets,
275 ARRAY_SIZE(wm8974_dapm_widgets));
276
277 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
278
279 snd_soc_dapm_new_widgets(codec);
280 return 0;
281}
282
283struct pll_ {
Mark Brown91d0c3e2009-06-30 19:02:32 +0100284 unsigned int pre_div:4; /* prescale - 1 */
Mark Brown0a1bf552009-05-23 11:18:41 +0100285 unsigned int n:4;
286 unsigned int k;
287};
288
Mark Brown91d0c3e2009-06-30 19:02:32 +0100289static struct pll_ pll_div;
290
291/* The size in bits of the pll divide multiplied by 10
292 * to allow rounding later */
293#define FIXED_PLL_SIZE ((1 << 24) * 10)
294
295static void pll_factors(unsigned int target, unsigned int source)
296{
297 unsigned long long Kpart;
298 unsigned int K, Ndiv, Nmod;
299
300 Ndiv = target / source;
301 if (Ndiv < 6) {
302 source >>= 1;
303 pll_div.pre_div = 1;
304 Ndiv = target / source;
305 } else
306 pll_div.pre_div = 0;
307
308 if ((Ndiv < 6) || (Ndiv > 12))
309 printk(KERN_WARNING
Mark Brown8b83a192009-06-30 19:37:02 +0100310 "WM8974 N value %u outwith recommended range!\n",
Mark Brown91d0c3e2009-06-30 19:02:32 +0100311 Ndiv);
312
313 pll_div.n = Ndiv;
314 Nmod = target % source;
315 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
316
317 do_div(Kpart, source);
318
319 K = Kpart & 0xFFFFFFFF;
320
321 /* Check if we need to round */
322 if ((K % 10) >= 5)
323 K += 5;
324
325 /* Move down to proper range now rounding is done */
326 K /= 10;
327
328 pll_div.k = K;
329}
Mark Brown0a1bf552009-05-23 11:18:41 +0100330
331static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
332 int pll_id, unsigned int freq_in, unsigned int freq_out)
333{
334 struct snd_soc_codec *codec = codec_dai->codec;
Mark Brown0a1bf552009-05-23 11:18:41 +0100335 u16 reg;
336
Mark Brown1a55b3f2009-05-23 11:31:40 +0100337 if (freq_in == 0 || freq_out == 0) {
Mark Brown91d0c3e2009-06-30 19:02:32 +0100338 /* Clock CODEC directly from MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100339 reg = snd_soc_read(codec, WM8974_CLOCK);
340 snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100341
342 /* Turn off PLL */
Mark Brown1e97f502009-08-15 12:15:10 +0100343 reg = snd_soc_read(codec, WM8974_POWER1);
344 snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
Mark Brown0a1bf552009-05-23 11:18:41 +0100345 return 0;
346 }
347
Mark Brown91d0c3e2009-06-30 19:02:32 +0100348 pll_factors(freq_out*4, freq_in);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100349
Mark Brown1e97f502009-08-15 12:15:10 +0100350 snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
351 snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
352 snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
353 snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
354 reg = snd_soc_read(codec, WM8974_POWER1);
355 snd_soc_write(codec, WM8974_POWER1, reg | 0x020);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100356
357 /* Run CODEC from PLL instead of MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100358 reg = snd_soc_read(codec, WM8974_CLOCK);
359 snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100360
361 return 0;
Mark Brown0a1bf552009-05-23 11:18:41 +0100362}
363
364/*
365 * Configure WM8974 clock dividers.
366 */
367static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
368 int div_id, int div)
369{
370 struct snd_soc_codec *codec = codec_dai->codec;
371 u16 reg;
372
373 switch (div_id) {
374 case WM8974_OPCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100375 reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
376 snd_soc_write(codec, WM8974_GPIO, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100377 break;
378 case WM8974_MCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100379 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
380 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100381 break;
382 case WM8974_ADCCLK:
Mark Brown1e97f502009-08-15 12:15:10 +0100383 reg = snd_soc_read(codec, WM8974_ADC) & 0x1f7;
384 snd_soc_write(codec, WM8974_ADC, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100385 break;
386 case WM8974_DACCLK:
Mark Brown1e97f502009-08-15 12:15:10 +0100387 reg = snd_soc_read(codec, WM8974_DAC) & 0x1f7;
388 snd_soc_write(codec, WM8974_DAC, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100389 break;
390 case WM8974_BCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100391 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
392 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100393 break;
394 default:
395 return -EINVAL;
396 }
397
398 return 0;
399}
400
401static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
402 unsigned int fmt)
403{
404 struct snd_soc_codec *codec = codec_dai->codec;
405 u16 iface = 0;
Mark Brown1e97f502009-08-15 12:15:10 +0100406 u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
Mark Brown0a1bf552009-05-23 11:18:41 +0100407
408 /* set master/slave audio interface */
409 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
410 case SND_SOC_DAIFMT_CBM_CFM:
411 clk |= 0x0001;
412 break;
413 case SND_SOC_DAIFMT_CBS_CFS:
414 break;
415 default:
416 return -EINVAL;
417 }
418
419 /* interface format */
420 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
421 case SND_SOC_DAIFMT_I2S:
422 iface |= 0x0010;
423 break;
424 case SND_SOC_DAIFMT_RIGHT_J:
425 break;
426 case SND_SOC_DAIFMT_LEFT_J:
427 iface |= 0x0008;
428 break;
429 case SND_SOC_DAIFMT_DSP_A:
430 iface |= 0x00018;
431 break;
432 default:
433 return -EINVAL;
434 }
435
436 /* clock inversion */
437 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
438 case SND_SOC_DAIFMT_NB_NF:
439 break;
440 case SND_SOC_DAIFMT_IB_IF:
441 iface |= 0x0180;
442 break;
443 case SND_SOC_DAIFMT_IB_NF:
444 iface |= 0x0100;
445 break;
446 case SND_SOC_DAIFMT_NB_IF:
447 iface |= 0x0080;
448 break;
449 default:
450 return -EINVAL;
451 }
452
Mark Brown1e97f502009-08-15 12:15:10 +0100453 snd_soc_write(codec, WM8974_IFACE, iface);
454 snd_soc_write(codec, WM8974_CLOCK, clk);
Mark Brown0a1bf552009-05-23 11:18:41 +0100455 return 0;
456}
457
458static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
459 struct snd_pcm_hw_params *params,
460 struct snd_soc_dai *dai)
461{
462 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100463 u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
464 u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
Mark Brown0a1bf552009-05-23 11:18:41 +0100465
466 /* bit size */
467 switch (params_format(params)) {
468 case SNDRV_PCM_FORMAT_S16_LE:
469 break;
470 case SNDRV_PCM_FORMAT_S20_3LE:
471 iface |= 0x0020;
472 break;
473 case SNDRV_PCM_FORMAT_S24_LE:
474 iface |= 0x0040;
475 break;
476 case SNDRV_PCM_FORMAT_S32_LE:
477 iface |= 0x0060;
478 break;
479 }
480
481 /* filter coefficient */
482 switch (params_rate(params)) {
483 case SNDRV_PCM_RATE_8000:
484 adn |= 0x5 << 1;
485 break;
486 case SNDRV_PCM_RATE_11025:
487 adn |= 0x4 << 1;
488 break;
489 case SNDRV_PCM_RATE_16000:
490 adn |= 0x3 << 1;
491 break;
492 case SNDRV_PCM_RATE_22050:
493 adn |= 0x2 << 1;
494 break;
495 case SNDRV_PCM_RATE_32000:
496 adn |= 0x1 << 1;
497 break;
498 case SNDRV_PCM_RATE_44100:
Mark Brown8b83a192009-06-30 19:37:02 +0100499 case SNDRV_PCM_RATE_48000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100500 break;
501 }
502
Mark Brown1e97f502009-08-15 12:15:10 +0100503 snd_soc_write(codec, WM8974_IFACE, iface);
504 snd_soc_write(codec, WM8974_ADD, adn);
Mark Brown0a1bf552009-05-23 11:18:41 +0100505 return 0;
506}
507
508static int wm8974_mute(struct snd_soc_dai *dai, int mute)
509{
510 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100511 u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
Mark Brown0a1bf552009-05-23 11:18:41 +0100512
Mark Brown1a55b3f2009-05-23 11:31:40 +0100513 if (mute)
Mark Brown1e97f502009-08-15 12:15:10 +0100514 snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
Mark Brown0a1bf552009-05-23 11:18:41 +0100515 else
Mark Brown1e97f502009-08-15 12:15:10 +0100516 snd_soc_write(codec, WM8974_DAC, mute_reg);
Mark Brown0a1bf552009-05-23 11:18:41 +0100517 return 0;
518}
519
520/* liam need to make this lower power with dapm */
521static int wm8974_set_bias_level(struct snd_soc_codec *codec,
522 enum snd_soc_bias_level level)
523{
Mark Brown1e97f502009-08-15 12:15:10 +0100524 u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100525
Mark Brown0a1bf552009-05-23 11:18:41 +0100526 switch (level) {
527 case SND_SOC_BIAS_ON:
Mark Brown0a1bf552009-05-23 11:18:41 +0100528 case SND_SOC_BIAS_PREPARE:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100529 power1 |= 0x1; /* VMID 50k */
Mark Brown1e97f502009-08-15 12:15:10 +0100530 snd_soc_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100531 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100532
Mark Brown0a1bf552009-05-23 11:18:41 +0100533 case SND_SOC_BIAS_STANDBY:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100534 power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
535
536 if (codec->bias_level == SND_SOC_BIAS_OFF) {
537 /* Initial cap charge at VMID 5k */
Mark Brown1e97f502009-08-15 12:15:10 +0100538 snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
Mark Browndf1ef7a2009-06-30 19:01:09 +0100539 mdelay(100);
540 }
541
542 power1 |= 0x2; /* VMID 500k */
Mark Brown1e97f502009-08-15 12:15:10 +0100543 snd_soc_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100544 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100545
Mark Brown0a1bf552009-05-23 11:18:41 +0100546 case SND_SOC_BIAS_OFF:
Mark Brown1e97f502009-08-15 12:15:10 +0100547 snd_soc_write(codec, WM8974_POWER1, 0);
548 snd_soc_write(codec, WM8974_POWER2, 0);
549 snd_soc_write(codec, WM8974_POWER3, 0);
Mark Brown0a1bf552009-05-23 11:18:41 +0100550 break;
551 }
Mark Browndf1ef7a2009-06-30 19:01:09 +0100552
Mark Brown0a1bf552009-05-23 11:18:41 +0100553 codec->bias_level = level;
554 return 0;
555}
556
Mark Brown1a55b3f2009-05-23 11:31:40 +0100557#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
Mark Brown0a1bf552009-05-23 11:18:41 +0100558
559#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
560 SNDRV_PCM_FMTBIT_S24_LE)
561
562static struct snd_soc_dai_ops wm8974_ops = {
563 .hw_params = wm8974_pcm_hw_params,
564 .digital_mute = wm8974_mute,
565 .set_fmt = wm8974_set_dai_fmt,
566 .set_clkdiv = wm8974_set_dai_clkdiv,
567 .set_pll = wm8974_set_dai_pll,
568};
569
570struct snd_soc_dai wm8974_dai = {
571 .name = "WM8974 HiFi",
572 .playback = {
573 .stream_name = "Playback",
574 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100575 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100576 .rates = WM8974_RATES,
577 .formats = WM8974_FORMATS,},
578 .capture = {
579 .stream_name = "Capture",
580 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100581 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100582 .rates = WM8974_RATES,
583 .formats = WM8974_FORMATS,},
584 .ops = &wm8974_ops,
Mark Browncb11d392009-06-30 19:36:39 +0100585 .symmetric_rates = 1,
Mark Brown0a1bf552009-05-23 11:18:41 +0100586};
587EXPORT_SYMBOL_GPL(wm8974_dai);
588
589static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
590{
591 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
592 struct snd_soc_codec *codec = socdev->card->codec;
593
594 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
595 return 0;
596}
597
598static int wm8974_resume(struct platform_device *pdev)
599{
600 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
601 struct snd_soc_codec *codec = socdev->card->codec;
602 int i;
603 u8 data[2];
604 u16 *cache = codec->reg_cache;
605
606 /* Sync reg_cache with the hardware */
607 for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
608 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
609 data[1] = cache[i] & 0x00ff;
610 codec->hw_write(codec->control_data, data, 2);
611 }
612 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
613 wm8974_set_bias_level(codec, codec->suspend_bias_level);
614 return 0;
615}
616
Mark Brown4fcbbb62009-05-23 12:27:03 +0100617static int wm8974_probe(struct platform_device *pdev)
Mark Brown0a1bf552009-05-23 11:18:41 +0100618{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100619 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
620 struct snd_soc_codec *codec;
Mark Brown0a1bf552009-05-23 11:18:41 +0100621 int ret = 0;
622
Mark Brown4fcbbb62009-05-23 12:27:03 +0100623 if (wm8974_codec == NULL) {
624 dev_err(&pdev->dev, "Codec device not registered\n");
625 return -ENODEV;
626 }
Mark Brown0a1bf552009-05-23 11:18:41 +0100627
Mark Brown4fcbbb62009-05-23 12:27:03 +0100628 socdev->card->codec = wm8974_codec;
629 codec = wm8974_codec;
Mark Brown0a1bf552009-05-23 11:18:41 +0100630
631 /* register pcms */
632 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100633 if (ret < 0) {
Mark Brown4fcbbb62009-05-23 12:27:03 +0100634 dev_err(codec->dev, "failed to create pcms: %d\n", ret);
Mark Brown0a1bf552009-05-23 11:18:41 +0100635 goto pcm_err;
636 }
637
Mark Brown4fcbbb62009-05-23 12:27:03 +0100638 snd_soc_add_controls(codec, wm8974_snd_controls,
639 ARRAY_SIZE(wm8974_snd_controls));
Mark Brown0a1bf552009-05-23 11:18:41 +0100640 wm8974_add_widgets(codec);
641 ret = snd_soc_init_card(socdev);
642 if (ret < 0) {
Mark Brown4fcbbb62009-05-23 12:27:03 +0100643 dev_err(codec->dev, "failed to register card: %d\n", ret);
Mark Brown0a1bf552009-05-23 11:18:41 +0100644 goto card_err;
645 }
Mark Brown4fcbbb62009-05-23 12:27:03 +0100646
Mark Brown0a1bf552009-05-23 11:18:41 +0100647 return ret;
648
649card_err:
650 snd_soc_free_pcms(socdev);
651 snd_soc_dapm_free(socdev);
652pcm_err:
Mark Brown0a1bf552009-05-23 11:18:41 +0100653 return ret;
654}
655
656/* power down chip */
657static int wm8974_remove(struct platform_device *pdev)
658{
659 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
Mark Brown0a1bf552009-05-23 11:18:41 +0100660
661 snd_soc_free_pcms(socdev);
662 snd_soc_dapm_free(socdev);
Mark Brown0a1bf552009-05-23 11:18:41 +0100663
664 return 0;
665}
666
667struct snd_soc_codec_device soc_codec_dev_wm8974 = {
668 .probe = wm8974_probe,
669 .remove = wm8974_remove,
670 .suspend = wm8974_suspend,
671 .resume = wm8974_resume,
672};
673EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
674
Mark Brown4fcbbb62009-05-23 12:27:03 +0100675static __devinit int wm8974_register(struct wm8974_priv *wm8974)
676{
677 int ret;
678 struct snd_soc_codec *codec = &wm8974->codec;
679
680 if (wm8974_codec) {
681 dev_err(codec->dev, "Another WM8974 is registered\n");
682 return -EINVAL;
683 }
684
685 mutex_init(&codec->mutex);
686 INIT_LIST_HEAD(&codec->dapm_widgets);
687 INIT_LIST_HEAD(&codec->dapm_paths);
688
689 codec->private_data = wm8974;
690 codec->name = "WM8974";
691 codec->owner = THIS_MODULE;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100692 codec->bias_level = SND_SOC_BIAS_OFF;
693 codec->set_bias_level = wm8974_set_bias_level;
694 codec->dai = &wm8974_dai;
695 codec->num_dai = 1;
696 codec->reg_cache_size = WM8974_CACHEREGNUM;
697 codec->reg_cache = &wm8974->reg_cache;
698
Mark Brown1e97f502009-08-15 12:15:10 +0100699 ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
700 if (ret < 0) {
701 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
702 goto err;
703 }
704
Mark Brown4fcbbb62009-05-23 12:27:03 +0100705 memcpy(codec->reg_cache, wm8974_reg, sizeof(wm8974_reg));
706
707 ret = wm8974_reset(codec);
708 if (ret < 0) {
709 dev_err(codec->dev, "Failed to issue reset\n");
Mark Brown1e97f502009-08-15 12:15:10 +0100710 goto err;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100711 }
712
713 wm8974_dai.dev = codec->dev;
714
715 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
716
717 wm8974_codec = codec;
718
719 ret = snd_soc_register_codec(codec);
720 if (ret != 0) {
721 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
Mark Brown1e97f502009-08-15 12:15:10 +0100722 goto err;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100723 }
724
725 ret = snd_soc_register_dai(&wm8974_dai);
726 if (ret != 0) {
727 dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
Mark Brown1e97f502009-08-15 12:15:10 +0100728 goto err_codec;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100729 }
730
731 return 0;
Mark Brown1e97f502009-08-15 12:15:10 +0100732
733err_codec:
734 snd_soc_unregister_codec(codec);
735err:
736 kfree(wm8974);
737 return ret;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100738}
739
740static __devexit void wm8974_unregister(struct wm8974_priv *wm8974)
741{
742 wm8974_set_bias_level(&wm8974->codec, SND_SOC_BIAS_OFF);
743 snd_soc_unregister_dai(&wm8974_dai);
744 snd_soc_unregister_codec(&wm8974->codec);
745 kfree(wm8974);
746 wm8974_codec = NULL;
747}
748
749static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
750 const struct i2c_device_id *id)
751{
752 struct wm8974_priv *wm8974;
753 struct snd_soc_codec *codec;
754
755 wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
756 if (wm8974 == NULL)
757 return -ENOMEM;
758
759 codec = &wm8974->codec;
760 codec->hw_write = (hw_write_t)i2c_master_send;
761
762 i2c_set_clientdata(i2c, wm8974);
763 codec->control_data = i2c;
764
765 codec->dev = &i2c->dev;
766
767 return wm8974_register(wm8974);
768}
769
770static __devexit int wm8974_i2c_remove(struct i2c_client *client)
771{
772 struct wm8974_priv *wm8974 = i2c_get_clientdata(client);
773 wm8974_unregister(wm8974);
774 return 0;
775}
776
777static const struct i2c_device_id wm8974_i2c_id[] = {
778 { "wm8974", 0 },
779 { }
780};
781MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
782
783static struct i2c_driver wm8974_i2c_driver = {
784 .driver = {
Mark Brown8b83a192009-06-30 19:37:02 +0100785 .name = "WM8974",
Mark Brown4fcbbb62009-05-23 12:27:03 +0100786 .owner = THIS_MODULE,
787 },
788 .probe = wm8974_i2c_probe,
789 .remove = __devexit_p(wm8974_i2c_remove),
790 .id_table = wm8974_i2c_id,
791};
792
Mark Brown0a1bf552009-05-23 11:18:41 +0100793static int __init wm8974_modinit(void)
794{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100795 return i2c_add_driver(&wm8974_i2c_driver);
Mark Brown0a1bf552009-05-23 11:18:41 +0100796}
797module_init(wm8974_modinit);
798
799static void __exit wm8974_exit(void)
800{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100801 i2c_del_driver(&wm8974_i2c_driver);
Mark Brown0a1bf552009-05-23 11:18:41 +0100802}
803module_exit(wm8974_exit);
804
805MODULE_DESCRIPTION("ASoC WM8974 driver");
806MODULE_AUTHOR("Liam Girdwood");
807MODULE_LICENSE("GPL");