blob: 81c57b5c591c5d35676eae9d3dfa5f1858326149 [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
Mark Brown0a1bf552009-05-23 11:18:41 +0100279 return 0;
280}
281
282struct pll_ {
Mark Brownc36b2fc2009-09-30 14:31:38 +0100283 unsigned int pre_div:1;
Mark Brown0a1bf552009-05-23 11:18:41 +0100284 unsigned int n:4;
285 unsigned int k;
286};
287
Mark Brown91d0c3e2009-06-30 19:02:32 +0100288/* The size in bits of the pll divide multiplied by 10
289 * to allow rounding later */
290#define FIXED_PLL_SIZE ((1 << 24) * 10)
291
Mark Brownc36b2fc2009-09-30 14:31:38 +0100292static void pll_factors(struct pll_ *pll_div,
293 unsigned int target, unsigned int source)
Mark Brown91d0c3e2009-06-30 19:02:32 +0100294{
295 unsigned long long Kpart;
296 unsigned int K, Ndiv, Nmod;
297
Mark Brownc36b2fc2009-09-30 14:31:38 +0100298 /* There is a fixed divide by 4 in the output path */
299 target *= 4;
300
Mark Brown91d0c3e2009-06-30 19:02:32 +0100301 Ndiv = target / source;
302 if (Ndiv < 6) {
Mark Brownc36b2fc2009-09-30 14:31:38 +0100303 source /= 2;
304 pll_div->pre_div = 1;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100305 Ndiv = target / source;
306 } else
Mark Brownc36b2fc2009-09-30 14:31:38 +0100307 pll_div->pre_div = 0;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100308
309 if ((Ndiv < 6) || (Ndiv > 12))
310 printk(KERN_WARNING
Mark Brown8b83a192009-06-30 19:37:02 +0100311 "WM8974 N value %u outwith recommended range!\n",
Mark Brown91d0c3e2009-06-30 19:02:32 +0100312 Ndiv);
313
Mark Brownc36b2fc2009-09-30 14:31:38 +0100314 pll_div->n = Ndiv;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100315 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
Mark Brownc36b2fc2009-09-30 14:31:38 +0100329 pll_div->k = K;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100330}
Mark Brown0a1bf552009-05-23 11:18:41 +0100331
Mark Brown85488032009-09-05 18:52:16 +0100332static 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)
Mark Brown0a1bf552009-05-23 11:18:41 +0100334{
335 struct snd_soc_codec *codec = codec_dai->codec;
Mark Brownc36b2fc2009-09-30 14:31:38 +0100336 struct pll_ pll_div;
Mark Brown0a1bf552009-05-23 11:18:41 +0100337 u16 reg;
338
Mark Brown1a55b3f2009-05-23 11:31:40 +0100339 if (freq_in == 0 || freq_out == 0) {
Mark Brown91d0c3e2009-06-30 19:02:32 +0100340 /* Clock CODEC directly from MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100341 reg = snd_soc_read(codec, WM8974_CLOCK);
342 snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100343
344 /* Turn off PLL */
Mark Brown1e97f502009-08-15 12:15:10 +0100345 reg = snd_soc_read(codec, WM8974_POWER1);
346 snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
Mark Brown0a1bf552009-05-23 11:18:41 +0100347 return 0;
348 }
349
Mark Brownc36b2fc2009-09-30 14:31:38 +0100350 pll_factors(&pll_div, freq_out, freq_in);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100351
Mark Brown1e97f502009-08-15 12:15:10 +0100352 snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
353 snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
354 snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
355 snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
356 reg = snd_soc_read(codec, WM8974_POWER1);
357 snd_soc_write(codec, WM8974_POWER1, reg | 0x020);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100358
359 /* Run CODEC from PLL instead of MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100360 reg = snd_soc_read(codec, WM8974_CLOCK);
361 snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100362
363 return 0;
Mark Brown0a1bf552009-05-23 11:18:41 +0100364}
365
366/*
367 * Configure WM8974 clock dividers.
368 */
369static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
370 int div_id, int div)
371{
372 struct snd_soc_codec *codec = codec_dai->codec;
373 u16 reg;
374
375 switch (div_id) {
376 case WM8974_OPCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100377 reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
378 snd_soc_write(codec, WM8974_GPIO, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100379 break;
380 case WM8974_MCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100381 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
382 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100383 break;
384 case WM8974_ADCCLK:
Mark Brown1e97f502009-08-15 12:15:10 +0100385 reg = snd_soc_read(codec, WM8974_ADC) & 0x1f7;
386 snd_soc_write(codec, WM8974_ADC, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100387 break;
388 case WM8974_DACCLK:
Mark Brown1e97f502009-08-15 12:15:10 +0100389 reg = snd_soc_read(codec, WM8974_DAC) & 0x1f7;
390 snd_soc_write(codec, WM8974_DAC, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100391 break;
392 case WM8974_BCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100393 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
394 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100395 break;
396 default:
397 return -EINVAL;
398 }
399
400 return 0;
401}
402
403static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
404 unsigned int fmt)
405{
406 struct snd_soc_codec *codec = codec_dai->codec;
407 u16 iface = 0;
Mark Brown1e97f502009-08-15 12:15:10 +0100408 u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
Mark Brown0a1bf552009-05-23 11:18:41 +0100409
410 /* set master/slave audio interface */
411 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
412 case SND_SOC_DAIFMT_CBM_CFM:
413 clk |= 0x0001;
414 break;
415 case SND_SOC_DAIFMT_CBS_CFS:
416 break;
417 default:
418 return -EINVAL;
419 }
420
421 /* interface format */
422 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
423 case SND_SOC_DAIFMT_I2S:
424 iface |= 0x0010;
425 break;
426 case SND_SOC_DAIFMT_RIGHT_J:
427 break;
428 case SND_SOC_DAIFMT_LEFT_J:
429 iface |= 0x0008;
430 break;
431 case SND_SOC_DAIFMT_DSP_A:
432 iface |= 0x00018;
433 break;
434 default:
435 return -EINVAL;
436 }
437
438 /* clock inversion */
439 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
440 case SND_SOC_DAIFMT_NB_NF:
441 break;
442 case SND_SOC_DAIFMT_IB_IF:
443 iface |= 0x0180;
444 break;
445 case SND_SOC_DAIFMT_IB_NF:
446 iface |= 0x0100;
447 break;
448 case SND_SOC_DAIFMT_NB_IF:
449 iface |= 0x0080;
450 break;
451 default:
452 return -EINVAL;
453 }
454
Mark Brown1e97f502009-08-15 12:15:10 +0100455 snd_soc_write(codec, WM8974_IFACE, iface);
456 snd_soc_write(codec, WM8974_CLOCK, clk);
Mark Brown0a1bf552009-05-23 11:18:41 +0100457 return 0;
458}
459
460static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
461 struct snd_pcm_hw_params *params,
462 struct snd_soc_dai *dai)
463{
464 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100465 u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
466 u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
Mark Brown0a1bf552009-05-23 11:18:41 +0100467
468 /* bit size */
469 switch (params_format(params)) {
470 case SNDRV_PCM_FORMAT_S16_LE:
471 break;
472 case SNDRV_PCM_FORMAT_S20_3LE:
473 iface |= 0x0020;
474 break;
475 case SNDRV_PCM_FORMAT_S24_LE:
476 iface |= 0x0040;
477 break;
478 case SNDRV_PCM_FORMAT_S32_LE:
479 iface |= 0x0060;
480 break;
481 }
482
483 /* filter coefficient */
484 switch (params_rate(params)) {
485 case SNDRV_PCM_RATE_8000:
486 adn |= 0x5 << 1;
487 break;
488 case SNDRV_PCM_RATE_11025:
489 adn |= 0x4 << 1;
490 break;
491 case SNDRV_PCM_RATE_16000:
492 adn |= 0x3 << 1;
493 break;
494 case SNDRV_PCM_RATE_22050:
495 adn |= 0x2 << 1;
496 break;
497 case SNDRV_PCM_RATE_32000:
498 adn |= 0x1 << 1;
499 break;
500 case SNDRV_PCM_RATE_44100:
Mark Brown8b83a192009-06-30 19:37:02 +0100501 case SNDRV_PCM_RATE_48000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100502 break;
503 }
504
Mark Brown1e97f502009-08-15 12:15:10 +0100505 snd_soc_write(codec, WM8974_IFACE, iface);
506 snd_soc_write(codec, WM8974_ADD, adn);
Mark Brown0a1bf552009-05-23 11:18:41 +0100507 return 0;
508}
509
510static int wm8974_mute(struct snd_soc_dai *dai, int mute)
511{
512 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100513 u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
Mark Brown0a1bf552009-05-23 11:18:41 +0100514
Mark Brown1a55b3f2009-05-23 11:31:40 +0100515 if (mute)
Mark Brown1e97f502009-08-15 12:15:10 +0100516 snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
Mark Brown0a1bf552009-05-23 11:18:41 +0100517 else
Mark Brown1e97f502009-08-15 12:15:10 +0100518 snd_soc_write(codec, WM8974_DAC, mute_reg);
Mark Brown0a1bf552009-05-23 11:18:41 +0100519 return 0;
520}
521
522/* liam need to make this lower power with dapm */
523static int wm8974_set_bias_level(struct snd_soc_codec *codec,
524 enum snd_soc_bias_level level)
525{
Mark Brown1e97f502009-08-15 12:15:10 +0100526 u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100527
Mark Brown0a1bf552009-05-23 11:18:41 +0100528 switch (level) {
529 case SND_SOC_BIAS_ON:
Mark Brown0a1bf552009-05-23 11:18:41 +0100530 case SND_SOC_BIAS_PREPARE:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100531 power1 |= 0x1; /* VMID 50k */
Mark Brown1e97f502009-08-15 12:15:10 +0100532 snd_soc_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100533 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100534
Mark Brown0a1bf552009-05-23 11:18:41 +0100535 case SND_SOC_BIAS_STANDBY:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100536 power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
537
538 if (codec->bias_level == SND_SOC_BIAS_OFF) {
539 /* Initial cap charge at VMID 5k */
Mark Brown1e97f502009-08-15 12:15:10 +0100540 snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
Mark Browndf1ef7a2009-06-30 19:01:09 +0100541 mdelay(100);
542 }
543
544 power1 |= 0x2; /* VMID 500k */
Mark Brown1e97f502009-08-15 12:15:10 +0100545 snd_soc_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100546 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100547
Mark Brown0a1bf552009-05-23 11:18:41 +0100548 case SND_SOC_BIAS_OFF:
Mark Brown1e97f502009-08-15 12:15:10 +0100549 snd_soc_write(codec, WM8974_POWER1, 0);
550 snd_soc_write(codec, WM8974_POWER2, 0);
551 snd_soc_write(codec, WM8974_POWER3, 0);
Mark Brown0a1bf552009-05-23 11:18:41 +0100552 break;
553 }
Mark Browndf1ef7a2009-06-30 19:01:09 +0100554
Mark Brown0a1bf552009-05-23 11:18:41 +0100555 codec->bias_level = level;
556 return 0;
557}
558
Mark Brown1a55b3f2009-05-23 11:31:40 +0100559#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
Mark Brown0a1bf552009-05-23 11:18:41 +0100560
561#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
562 SNDRV_PCM_FMTBIT_S24_LE)
563
564static struct snd_soc_dai_ops wm8974_ops = {
565 .hw_params = wm8974_pcm_hw_params,
566 .digital_mute = wm8974_mute,
567 .set_fmt = wm8974_set_dai_fmt,
568 .set_clkdiv = wm8974_set_dai_clkdiv,
569 .set_pll = wm8974_set_dai_pll,
570};
571
572struct snd_soc_dai wm8974_dai = {
573 .name = "WM8974 HiFi",
574 .playback = {
575 .stream_name = "Playback",
576 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100577 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100578 .rates = WM8974_RATES,
579 .formats = WM8974_FORMATS,},
580 .capture = {
581 .stream_name = "Capture",
582 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100583 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100584 .rates = WM8974_RATES,
585 .formats = WM8974_FORMATS,},
586 .ops = &wm8974_ops,
Mark Browncb11d392009-06-30 19:36:39 +0100587 .symmetric_rates = 1,
Mark Brown0a1bf552009-05-23 11:18:41 +0100588};
589EXPORT_SYMBOL_GPL(wm8974_dai);
590
591static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
592{
593 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
594 struct snd_soc_codec *codec = socdev->card->codec;
595
596 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
597 return 0;
598}
599
600static int wm8974_resume(struct platform_device *pdev)
601{
602 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
603 struct snd_soc_codec *codec = socdev->card->codec;
604 int i;
605 u8 data[2];
606 u16 *cache = codec->reg_cache;
607
608 /* Sync reg_cache with the hardware */
609 for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
610 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
611 data[1] = cache[i] & 0x00ff;
612 codec->hw_write(codec->control_data, data, 2);
613 }
614 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
615 wm8974_set_bias_level(codec, codec->suspend_bias_level);
616 return 0;
617}
618
Mark Brown4fcbbb62009-05-23 12:27:03 +0100619static int wm8974_probe(struct platform_device *pdev)
Mark Brown0a1bf552009-05-23 11:18:41 +0100620{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100621 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
622 struct snd_soc_codec *codec;
Mark Brown0a1bf552009-05-23 11:18:41 +0100623 int ret = 0;
624
Mark Brown4fcbbb62009-05-23 12:27:03 +0100625 if (wm8974_codec == NULL) {
626 dev_err(&pdev->dev, "Codec device not registered\n");
627 return -ENODEV;
628 }
Mark Brown0a1bf552009-05-23 11:18:41 +0100629
Mark Brown4fcbbb62009-05-23 12:27:03 +0100630 socdev->card->codec = wm8974_codec;
631 codec = wm8974_codec;
Mark Brown0a1bf552009-05-23 11:18:41 +0100632
633 /* register pcms */
634 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100635 if (ret < 0) {
Mark Brown4fcbbb62009-05-23 12:27:03 +0100636 dev_err(codec->dev, "failed to create pcms: %d\n", ret);
Mark Brown0a1bf552009-05-23 11:18:41 +0100637 goto pcm_err;
638 }
639
Mark Brown4fcbbb62009-05-23 12:27:03 +0100640 snd_soc_add_controls(codec, wm8974_snd_controls,
641 ARRAY_SIZE(wm8974_snd_controls));
Mark Brown0a1bf552009-05-23 11:18:41 +0100642 wm8974_add_widgets(codec);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100643
Mark Brown0a1bf552009-05-23 11:18:41 +0100644 return ret;
645
Mark Brown0a1bf552009-05-23 11:18:41 +0100646pcm_err:
Mark Brown0a1bf552009-05-23 11:18:41 +0100647 return ret;
648}
649
650/* power down chip */
651static int wm8974_remove(struct platform_device *pdev)
652{
653 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
Mark Brown0a1bf552009-05-23 11:18:41 +0100654
655 snd_soc_free_pcms(socdev);
656 snd_soc_dapm_free(socdev);
Mark Brown0a1bf552009-05-23 11:18:41 +0100657
658 return 0;
659}
660
661struct snd_soc_codec_device soc_codec_dev_wm8974 = {
662 .probe = wm8974_probe,
663 .remove = wm8974_remove,
664 .suspend = wm8974_suspend,
665 .resume = wm8974_resume,
666};
667EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
668
Mark Brown4fcbbb62009-05-23 12:27:03 +0100669static __devinit int wm8974_register(struct wm8974_priv *wm8974)
670{
671 int ret;
672 struct snd_soc_codec *codec = &wm8974->codec;
673
674 if (wm8974_codec) {
675 dev_err(codec->dev, "Another WM8974 is registered\n");
676 return -EINVAL;
677 }
678
679 mutex_init(&codec->mutex);
680 INIT_LIST_HEAD(&codec->dapm_widgets);
681 INIT_LIST_HEAD(&codec->dapm_paths);
682
683 codec->private_data = wm8974;
684 codec->name = "WM8974";
685 codec->owner = THIS_MODULE;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100686 codec->bias_level = SND_SOC_BIAS_OFF;
687 codec->set_bias_level = wm8974_set_bias_level;
688 codec->dai = &wm8974_dai;
689 codec->num_dai = 1;
690 codec->reg_cache_size = WM8974_CACHEREGNUM;
691 codec->reg_cache = &wm8974->reg_cache;
692
Mark Brown1e97f502009-08-15 12:15:10 +0100693 ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
694 if (ret < 0) {
695 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
696 goto err;
697 }
698
Mark Brown4fcbbb62009-05-23 12:27:03 +0100699 memcpy(codec->reg_cache, wm8974_reg, sizeof(wm8974_reg));
700
701 ret = wm8974_reset(codec);
702 if (ret < 0) {
703 dev_err(codec->dev, "Failed to issue reset\n");
Mark Brown1e97f502009-08-15 12:15:10 +0100704 goto err;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100705 }
706
707 wm8974_dai.dev = codec->dev;
708
709 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
710
711 wm8974_codec = codec;
712
713 ret = snd_soc_register_codec(codec);
714 if (ret != 0) {
715 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
Mark Brown1e97f502009-08-15 12:15:10 +0100716 goto err;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100717 }
718
719 ret = snd_soc_register_dai(&wm8974_dai);
720 if (ret != 0) {
721 dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
Mark Brown1e97f502009-08-15 12:15:10 +0100722 goto err_codec;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100723 }
724
725 return 0;
Mark Brown1e97f502009-08-15 12:15:10 +0100726
727err_codec:
728 snd_soc_unregister_codec(codec);
729err:
730 kfree(wm8974);
731 return ret;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100732}
733
734static __devexit void wm8974_unregister(struct wm8974_priv *wm8974)
735{
736 wm8974_set_bias_level(&wm8974->codec, SND_SOC_BIAS_OFF);
737 snd_soc_unregister_dai(&wm8974_dai);
738 snd_soc_unregister_codec(&wm8974->codec);
739 kfree(wm8974);
740 wm8974_codec = NULL;
741}
742
743static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
744 const struct i2c_device_id *id)
745{
746 struct wm8974_priv *wm8974;
747 struct snd_soc_codec *codec;
748
749 wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
750 if (wm8974 == NULL)
751 return -ENOMEM;
752
753 codec = &wm8974->codec;
754 codec->hw_write = (hw_write_t)i2c_master_send;
755
756 i2c_set_clientdata(i2c, wm8974);
757 codec->control_data = i2c;
758
759 codec->dev = &i2c->dev;
760
761 return wm8974_register(wm8974);
762}
763
764static __devexit int wm8974_i2c_remove(struct i2c_client *client)
765{
766 struct wm8974_priv *wm8974 = i2c_get_clientdata(client);
767 wm8974_unregister(wm8974);
768 return 0;
769}
770
771static const struct i2c_device_id wm8974_i2c_id[] = {
772 { "wm8974", 0 },
773 { }
774};
775MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
776
777static struct i2c_driver wm8974_i2c_driver = {
778 .driver = {
Mark Brown8b83a192009-06-30 19:37:02 +0100779 .name = "WM8974",
Mark Brown4fcbbb62009-05-23 12:27:03 +0100780 .owner = THIS_MODULE,
781 },
782 .probe = wm8974_i2c_probe,
783 .remove = __devexit_p(wm8974_i2c_remove),
784 .id_table = wm8974_i2c_id,
785};
786
Mark Brown0a1bf552009-05-23 11:18:41 +0100787static int __init wm8974_modinit(void)
788{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100789 return i2c_add_driver(&wm8974_i2c_driver);
Mark Brown0a1bf552009-05-23 11:18:41 +0100790}
791module_init(wm8974_modinit);
792
793static void __exit wm8974_exit(void)
794{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100795 i2c_del_driver(&wm8974_i2c_driver);
Mark Brown0a1bf552009-05-23 11:18:41 +0100796}
797module_exit(wm8974_exit);
798
799MODULE_DESCRIPTION("ASoC WM8974 driver");
800MODULE_AUTHOR("Liam Girdwood");
801MODULE_LICENSE("GPL");