blob: ee637af4737a039c252c82c287f5c37e22b4ee37 [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
Guennadi Liakhovetski48c03ce2009-12-17 14:51:35 +010050#define WM8974_POWER1_BUFIOEN 0x04
Mark Browndf1ef7a2009-06-30 19:01:09 +010051
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),
Guennadi Liakhovetskib2c3e922010-01-29 15:31:06 +0100173
174/* DAC / ADC oversampling */
175SOC_SINGLE("DAC 128x Oversampling Switch", WM8974_DAC, 8, 1, 0),
176SOC_SINGLE("ADC 128x Oversampling Switch", WM8974_ADC, 8, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100177};
178
Mark Brown0a1bf552009-05-23 11:18:41 +0100179/* Speaker Output Mixer */
180static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
181SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
182SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
183SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1),
184};
185
186/* Mono Output Mixer */
187static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
188SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
189SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100190SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0),
191};
192
193/* Boost mixer */
194static const struct snd_kcontrol_new wm8974_boost_mixer[] = {
195SOC_DAPM_SINGLE("Aux Switch", WM8974_INPPGA, 6, 1, 0),
196};
197
198/* Input PGA */
199static const struct snd_kcontrol_new wm8974_inpga[] = {
200SOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0),
201SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0),
202SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100203};
204
205/* AUX Input boost vol */
206static const struct snd_kcontrol_new wm8974_aux_boost_controls =
207SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
208
209/* Mic Input boost vol */
210static const struct snd_kcontrol_new wm8974_mic_boost_controls =
211SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
212
Mark Brown0a1bf552009-05-23 11:18:41 +0100213static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
214SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
215 &wm8974_speaker_mixer_controls[0],
216 ARRAY_SIZE(wm8974_speaker_mixer_controls)),
217SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
218 &wm8974_mono_mixer_controls[0],
219 ARRAY_SIZE(wm8974_mono_mixer_controls)),
220SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100221SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100222SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
223SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
224SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
225SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100226
Mark Brown8a123ee2009-06-30 21:10:34 +0100227SND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga,
228 ARRAY_SIZE(wm8974_inpga)),
229SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0,
230 wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)),
Mark Brown0a1bf552009-05-23 11:18:41 +0100231
232SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
233
234SND_SOC_DAPM_INPUT("MICN"),
235SND_SOC_DAPM_INPUT("MICP"),
236SND_SOC_DAPM_INPUT("AUX"),
237SND_SOC_DAPM_OUTPUT("MONOOUT"),
238SND_SOC_DAPM_OUTPUT("SPKOUTP"),
239SND_SOC_DAPM_OUTPUT("SPKOUTN"),
240};
241
242static const struct snd_soc_dapm_route audio_map[] = {
243 /* Mono output mixer */
244 {"Mono Mixer", "PCM Playback Switch", "DAC"},
245 {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
246 {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
247
248 /* Speaker output mixer */
249 {"Speaker Mixer", "PCM Playback Switch", "DAC"},
250 {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
251 {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
252
253 /* Outputs */
254 {"Mono Out", NULL, "Mono Mixer"},
255 {"MONOOUT", NULL, "Mono Out"},
256 {"SpkN Out", NULL, "Speaker Mixer"},
257 {"SpkP Out", NULL, "Speaker Mixer"},
258 {"SPKOUTN", NULL, "SpkN Out"},
259 {"SPKOUTP", NULL, "SpkP Out"},
260
261 /* Boost Mixer */
Mark Brown8a123ee2009-06-30 21:10:34 +0100262 {"ADC", NULL, "Boost Mixer"},
263 {"Boost Mixer", "Aux Switch", "Aux Input"},
264 {"Boost Mixer", NULL, "Input PGA"},
265 {"Boost Mixer", NULL, "MICP"},
266
267 /* Input PGA */
268 {"Input PGA", "Aux Switch", "Aux Input"},
269 {"Input PGA", "MicN Switch", "MICN"},
270 {"Input PGA", "MicP Switch", "MICP"},
Mark Brown0a1bf552009-05-23 11:18:41 +0100271
272 /* Inputs */
Mark Brown8a123ee2009-06-30 21:10:34 +0100273 {"Aux Input", NULL, "AUX"},
Mark Brown0a1bf552009-05-23 11:18:41 +0100274};
275
276static int wm8974_add_widgets(struct snd_soc_codec *codec)
277{
278 snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets,
279 ARRAY_SIZE(wm8974_dapm_widgets));
280
281 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
282
Mark Brown0a1bf552009-05-23 11:18:41 +0100283 return 0;
284}
285
286struct pll_ {
Mark Brownc36b2fc2009-09-30 14:31:38 +0100287 unsigned int pre_div:1;
Mark Brown0a1bf552009-05-23 11:18:41 +0100288 unsigned int n:4;
289 unsigned int k;
290};
291
Mark Brown91d0c3e2009-06-30 19:02:32 +0100292/* 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
Mark Brownc36b2fc2009-09-30 14:31:38 +0100296static void pll_factors(struct pll_ *pll_div,
297 unsigned int target, unsigned int source)
Mark Brown91d0c3e2009-06-30 19:02:32 +0100298{
299 unsigned long long Kpart;
300 unsigned int K, Ndiv, Nmod;
301
Mark Brownc36b2fc2009-09-30 14:31:38 +0100302 /* There is a fixed divide by 4 in the output path */
303 target *= 4;
304
Mark Brown91d0c3e2009-06-30 19:02:32 +0100305 Ndiv = target / source;
306 if (Ndiv < 6) {
Mark Brownc36b2fc2009-09-30 14:31:38 +0100307 source /= 2;
308 pll_div->pre_div = 1;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100309 Ndiv = target / source;
310 } else
Mark Brownc36b2fc2009-09-30 14:31:38 +0100311 pll_div->pre_div = 0;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100312
313 if ((Ndiv < 6) || (Ndiv > 12))
314 printk(KERN_WARNING
Mark Brown8b83a192009-06-30 19:37:02 +0100315 "WM8974 N value %u outwith recommended range!\n",
Mark Brown91d0c3e2009-06-30 19:02:32 +0100316 Ndiv);
317
Mark Brownc36b2fc2009-09-30 14:31:38 +0100318 pll_div->n = Ndiv;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100319 Nmod = target % source;
320 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
321
322 do_div(Kpart, source);
323
324 K = Kpart & 0xFFFFFFFF;
325
326 /* Check if we need to round */
327 if ((K % 10) >= 5)
328 K += 5;
329
330 /* Move down to proper range now rounding is done */
331 K /= 10;
332
Mark Brownc36b2fc2009-09-30 14:31:38 +0100333 pll_div->k = K;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100334}
Mark Brown0a1bf552009-05-23 11:18:41 +0100335
Mark Brown85488032009-09-05 18:52:16 +0100336static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
337 int source, unsigned int freq_in, unsigned int freq_out)
Mark Brown0a1bf552009-05-23 11:18:41 +0100338{
339 struct snd_soc_codec *codec = codec_dai->codec;
Mark Brownc36b2fc2009-09-30 14:31:38 +0100340 struct pll_ pll_div;
Mark Brown0a1bf552009-05-23 11:18:41 +0100341 u16 reg;
342
Mark Brown1a55b3f2009-05-23 11:31:40 +0100343 if (freq_in == 0 || freq_out == 0) {
Mark Brown91d0c3e2009-06-30 19:02:32 +0100344 /* Clock CODEC directly from MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100345 reg = snd_soc_read(codec, WM8974_CLOCK);
346 snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100347
348 /* Turn off PLL */
Mark Brown1e97f502009-08-15 12:15:10 +0100349 reg = snd_soc_read(codec, WM8974_POWER1);
350 snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
Mark Brown0a1bf552009-05-23 11:18:41 +0100351 return 0;
352 }
353
Mark Brownc36b2fc2009-09-30 14:31:38 +0100354 pll_factors(&pll_div, freq_out, freq_in);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100355
Mark Brown1e97f502009-08-15 12:15:10 +0100356 snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
357 snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
358 snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
359 snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
360 reg = snd_soc_read(codec, WM8974_POWER1);
361 snd_soc_write(codec, WM8974_POWER1, reg | 0x020);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100362
363 /* Run CODEC from PLL instead of MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100364 reg = snd_soc_read(codec, WM8974_CLOCK);
365 snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100366
367 return 0;
Mark Brown0a1bf552009-05-23 11:18:41 +0100368}
369
370/*
371 * Configure WM8974 clock dividers.
372 */
373static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
374 int div_id, int div)
375{
376 struct snd_soc_codec *codec = codec_dai->codec;
377 u16 reg;
378
379 switch (div_id) {
380 case WM8974_OPCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100381 reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
382 snd_soc_write(codec, WM8974_GPIO, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100383 break;
384 case WM8974_MCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100385 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
386 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100387 break;
Mark Brown0a1bf552009-05-23 11:18:41 +0100388 case WM8974_BCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100389 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
390 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100391 break;
392 default:
393 return -EINVAL;
394 }
395
396 return 0;
397}
398
399static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
400 unsigned int fmt)
401{
402 struct snd_soc_codec *codec = codec_dai->codec;
403 u16 iface = 0;
Mark Brown1e97f502009-08-15 12:15:10 +0100404 u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
Mark Brown0a1bf552009-05-23 11:18:41 +0100405
406 /* set master/slave audio interface */
407 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
408 case SND_SOC_DAIFMT_CBM_CFM:
409 clk |= 0x0001;
410 break;
411 case SND_SOC_DAIFMT_CBS_CFS:
412 break;
413 default:
414 return -EINVAL;
415 }
416
417 /* interface format */
418 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
419 case SND_SOC_DAIFMT_I2S:
420 iface |= 0x0010;
421 break;
422 case SND_SOC_DAIFMT_RIGHT_J:
423 break;
424 case SND_SOC_DAIFMT_LEFT_J:
425 iface |= 0x0008;
426 break;
427 case SND_SOC_DAIFMT_DSP_A:
428 iface |= 0x00018;
429 break;
430 default:
431 return -EINVAL;
432 }
433
434 /* clock inversion */
435 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
436 case SND_SOC_DAIFMT_NB_NF:
437 break;
438 case SND_SOC_DAIFMT_IB_IF:
439 iface |= 0x0180;
440 break;
441 case SND_SOC_DAIFMT_IB_NF:
442 iface |= 0x0100;
443 break;
444 case SND_SOC_DAIFMT_NB_IF:
445 iface |= 0x0080;
446 break;
447 default:
448 return -EINVAL;
449 }
450
Mark Brown1e97f502009-08-15 12:15:10 +0100451 snd_soc_write(codec, WM8974_IFACE, iface);
452 snd_soc_write(codec, WM8974_CLOCK, clk);
Mark Brown0a1bf552009-05-23 11:18:41 +0100453 return 0;
454}
455
456static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
457 struct snd_pcm_hw_params *params,
458 struct snd_soc_dai *dai)
459{
460 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100461 u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
462 u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
Mark Brown0a1bf552009-05-23 11:18:41 +0100463
464 /* bit size */
465 switch (params_format(params)) {
466 case SNDRV_PCM_FORMAT_S16_LE:
467 break;
468 case SNDRV_PCM_FORMAT_S20_3LE:
469 iface |= 0x0020;
470 break;
471 case SNDRV_PCM_FORMAT_S24_LE:
472 iface |= 0x0040;
473 break;
474 case SNDRV_PCM_FORMAT_S32_LE:
475 iface |= 0x0060;
476 break;
477 }
478
479 /* filter coefficient */
480 switch (params_rate(params)) {
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100481 case 8000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100482 adn |= 0x5 << 1;
483 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100484 case 11025:
Mark Brown0a1bf552009-05-23 11:18:41 +0100485 adn |= 0x4 << 1;
486 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100487 case 16000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100488 adn |= 0x3 << 1;
489 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100490 case 22050:
Mark Brown0a1bf552009-05-23 11:18:41 +0100491 adn |= 0x2 << 1;
492 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100493 case 32000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100494 adn |= 0x1 << 1;
495 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100496 case 44100:
497 case 48000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100498 break;
499 }
500
Mark Brown1e97f502009-08-15 12:15:10 +0100501 snd_soc_write(codec, WM8974_IFACE, iface);
502 snd_soc_write(codec, WM8974_ADD, adn);
Mark Brown0a1bf552009-05-23 11:18:41 +0100503 return 0;
504}
505
506static int wm8974_mute(struct snd_soc_dai *dai, int mute)
507{
508 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100509 u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
Mark Brown0a1bf552009-05-23 11:18:41 +0100510
Mark Brown1a55b3f2009-05-23 11:31:40 +0100511 if (mute)
Mark Brown1e97f502009-08-15 12:15:10 +0100512 snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
Mark Brown0a1bf552009-05-23 11:18:41 +0100513 else
Mark Brown1e97f502009-08-15 12:15:10 +0100514 snd_soc_write(codec, WM8974_DAC, mute_reg);
Mark Brown0a1bf552009-05-23 11:18:41 +0100515 return 0;
516}
517
518/* liam need to make this lower power with dapm */
519static int wm8974_set_bias_level(struct snd_soc_codec *codec,
520 enum snd_soc_bias_level level)
521{
Mark Brown1e97f502009-08-15 12:15:10 +0100522 u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100523
Mark Brown0a1bf552009-05-23 11:18:41 +0100524 switch (level) {
525 case SND_SOC_BIAS_ON:
Mark Brown0a1bf552009-05-23 11:18:41 +0100526 case SND_SOC_BIAS_PREPARE:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100527 power1 |= 0x1; /* VMID 50k */
Mark Brown1e97f502009-08-15 12:15:10 +0100528 snd_soc_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100529 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100530
Mark Brown0a1bf552009-05-23 11:18:41 +0100531 case SND_SOC_BIAS_STANDBY:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100532 power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
533
534 if (codec->bias_level == SND_SOC_BIAS_OFF) {
535 /* Initial cap charge at VMID 5k */
Mark Brown1e97f502009-08-15 12:15:10 +0100536 snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
Mark Browndf1ef7a2009-06-30 19:01:09 +0100537 mdelay(100);
538 }
539
540 power1 |= 0x2; /* VMID 500k */
Mark Brown1e97f502009-08-15 12:15:10 +0100541 snd_soc_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100542 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100543
Mark Brown0a1bf552009-05-23 11:18:41 +0100544 case SND_SOC_BIAS_OFF:
Mark Brown1e97f502009-08-15 12:15:10 +0100545 snd_soc_write(codec, WM8974_POWER1, 0);
546 snd_soc_write(codec, WM8974_POWER2, 0);
547 snd_soc_write(codec, WM8974_POWER3, 0);
Mark Brown0a1bf552009-05-23 11:18:41 +0100548 break;
549 }
Mark Browndf1ef7a2009-06-30 19:01:09 +0100550
Mark Brown0a1bf552009-05-23 11:18:41 +0100551 codec->bias_level = level;
552 return 0;
553}
554
Mark Brown1a55b3f2009-05-23 11:31:40 +0100555#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
Mark Brown0a1bf552009-05-23 11:18:41 +0100556
557#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
558 SNDRV_PCM_FMTBIT_S24_LE)
559
560static struct snd_soc_dai_ops wm8974_ops = {
561 .hw_params = wm8974_pcm_hw_params,
562 .digital_mute = wm8974_mute,
563 .set_fmt = wm8974_set_dai_fmt,
564 .set_clkdiv = wm8974_set_dai_clkdiv,
565 .set_pll = wm8974_set_dai_pll,
566};
567
568struct snd_soc_dai wm8974_dai = {
569 .name = "WM8974 HiFi",
570 .playback = {
571 .stream_name = "Playback",
572 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100573 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100574 .rates = WM8974_RATES,
575 .formats = WM8974_FORMATS,},
576 .capture = {
577 .stream_name = "Capture",
578 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100579 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100580 .rates = WM8974_RATES,
581 .formats = WM8974_FORMATS,},
582 .ops = &wm8974_ops,
Mark Browncb11d392009-06-30 19:36:39 +0100583 .symmetric_rates = 1,
Mark Brown0a1bf552009-05-23 11:18:41 +0100584};
585EXPORT_SYMBOL_GPL(wm8974_dai);
586
587static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
588{
589 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
590 struct snd_soc_codec *codec = socdev->card->codec;
591
592 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
593 return 0;
594}
595
596static int wm8974_resume(struct platform_device *pdev)
597{
598 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
599 struct snd_soc_codec *codec = socdev->card->codec;
600 int i;
601 u8 data[2];
602 u16 *cache = codec->reg_cache;
603
604 /* Sync reg_cache with the hardware */
605 for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
606 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
607 data[1] = cache[i] & 0x00ff;
608 codec->hw_write(codec->control_data, data, 2);
609 }
610 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
611 wm8974_set_bias_level(codec, codec->suspend_bias_level);
612 return 0;
613}
614
Mark Brown4fcbbb62009-05-23 12:27:03 +0100615static int wm8974_probe(struct platform_device *pdev)
Mark Brown0a1bf552009-05-23 11:18:41 +0100616{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100617 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
618 struct snd_soc_codec *codec;
Mark Brown0a1bf552009-05-23 11:18:41 +0100619 int ret = 0;
620
Mark Brown4fcbbb62009-05-23 12:27:03 +0100621 if (wm8974_codec == NULL) {
622 dev_err(&pdev->dev, "Codec device not registered\n");
623 return -ENODEV;
624 }
Mark Brown0a1bf552009-05-23 11:18:41 +0100625
Mark Brown4fcbbb62009-05-23 12:27:03 +0100626 socdev->card->codec = wm8974_codec;
627 codec = wm8974_codec;
Mark Brown0a1bf552009-05-23 11:18:41 +0100628
629 /* register pcms */
630 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100631 if (ret < 0) {
Mark Brown4fcbbb62009-05-23 12:27:03 +0100632 dev_err(codec->dev, "failed to create pcms: %d\n", ret);
Mark Brown0a1bf552009-05-23 11:18:41 +0100633 goto pcm_err;
634 }
635
Mark Brown4fcbbb62009-05-23 12:27:03 +0100636 snd_soc_add_controls(codec, wm8974_snd_controls,
637 ARRAY_SIZE(wm8974_snd_controls));
Mark Brown0a1bf552009-05-23 11:18:41 +0100638 wm8974_add_widgets(codec);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100639
Mark Brown0a1bf552009-05-23 11:18:41 +0100640 return ret;
641
Mark Brown0a1bf552009-05-23 11:18:41 +0100642pcm_err:
Mark Brown0a1bf552009-05-23 11:18:41 +0100643 return ret;
644}
645
646/* power down chip */
647static int wm8974_remove(struct platform_device *pdev)
648{
649 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
Mark Brown0a1bf552009-05-23 11:18:41 +0100650
651 snd_soc_free_pcms(socdev);
652 snd_soc_dapm_free(socdev);
Mark Brown0a1bf552009-05-23 11:18:41 +0100653
654 return 0;
655}
656
657struct snd_soc_codec_device soc_codec_dev_wm8974 = {
658 .probe = wm8974_probe,
659 .remove = wm8974_remove,
660 .suspend = wm8974_suspend,
661 .resume = wm8974_resume,
662};
663EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
664
Mark Brown4fcbbb62009-05-23 12:27:03 +0100665static __devinit int wm8974_register(struct wm8974_priv *wm8974)
666{
667 int ret;
668 struct snd_soc_codec *codec = &wm8974->codec;
669
670 if (wm8974_codec) {
671 dev_err(codec->dev, "Another WM8974 is registered\n");
672 return -EINVAL;
673 }
674
675 mutex_init(&codec->mutex);
676 INIT_LIST_HEAD(&codec->dapm_widgets);
677 INIT_LIST_HEAD(&codec->dapm_paths);
678
679 codec->private_data = wm8974;
680 codec->name = "WM8974";
681 codec->owner = THIS_MODULE;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100682 codec->bias_level = SND_SOC_BIAS_OFF;
683 codec->set_bias_level = wm8974_set_bias_level;
684 codec->dai = &wm8974_dai;
685 codec->num_dai = 1;
686 codec->reg_cache_size = WM8974_CACHEREGNUM;
687 codec->reg_cache = &wm8974->reg_cache;
688
Mark Brown1e97f502009-08-15 12:15:10 +0100689 ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
690 if (ret < 0) {
691 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
692 goto err;
693 }
694
Mark Brown4fcbbb62009-05-23 12:27:03 +0100695 memcpy(codec->reg_cache, wm8974_reg, sizeof(wm8974_reg));
696
697 ret = wm8974_reset(codec);
698 if (ret < 0) {
699 dev_err(codec->dev, "Failed to issue reset\n");
Mark Brown1e97f502009-08-15 12:15:10 +0100700 goto err;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100701 }
702
703 wm8974_dai.dev = codec->dev;
704
705 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
706
707 wm8974_codec = codec;
708
709 ret = snd_soc_register_codec(codec);
710 if (ret != 0) {
711 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
Mark Brown1e97f502009-08-15 12:15:10 +0100712 goto err;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100713 }
714
715 ret = snd_soc_register_dai(&wm8974_dai);
716 if (ret != 0) {
717 dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
Mark Brown1e97f502009-08-15 12:15:10 +0100718 goto err_codec;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100719 }
720
721 return 0;
Mark Brown1e97f502009-08-15 12:15:10 +0100722
723err_codec:
724 snd_soc_unregister_codec(codec);
725err:
726 kfree(wm8974);
727 return ret;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100728}
729
730static __devexit void wm8974_unregister(struct wm8974_priv *wm8974)
731{
732 wm8974_set_bias_level(&wm8974->codec, SND_SOC_BIAS_OFF);
733 snd_soc_unregister_dai(&wm8974_dai);
734 snd_soc_unregister_codec(&wm8974->codec);
735 kfree(wm8974);
736 wm8974_codec = NULL;
737}
738
739static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
740 const struct i2c_device_id *id)
741{
742 struct wm8974_priv *wm8974;
743 struct snd_soc_codec *codec;
744
745 wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
746 if (wm8974 == NULL)
747 return -ENOMEM;
748
749 codec = &wm8974->codec;
750 codec->hw_write = (hw_write_t)i2c_master_send;
751
752 i2c_set_clientdata(i2c, wm8974);
753 codec->control_data = i2c;
754
755 codec->dev = &i2c->dev;
756
757 return wm8974_register(wm8974);
758}
759
760static __devexit int wm8974_i2c_remove(struct i2c_client *client)
761{
762 struct wm8974_priv *wm8974 = i2c_get_clientdata(client);
763 wm8974_unregister(wm8974);
764 return 0;
765}
766
767static const struct i2c_device_id wm8974_i2c_id[] = {
768 { "wm8974", 0 },
769 { }
770};
771MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
772
773static struct i2c_driver wm8974_i2c_driver = {
774 .driver = {
Mark Brown8b83a192009-06-30 19:37:02 +0100775 .name = "WM8974",
Mark Brown4fcbbb62009-05-23 12:27:03 +0100776 .owner = THIS_MODULE,
777 },
778 .probe = wm8974_i2c_probe,
779 .remove = __devexit_p(wm8974_i2c_remove),
780 .id_table = wm8974_i2c_id,
781};
782
Mark Brown0a1bf552009-05-23 11:18:41 +0100783static int __init wm8974_modinit(void)
784{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100785 return i2c_add_driver(&wm8974_i2c_driver);
Mark Brown0a1bf552009-05-23 11:18:41 +0100786}
787module_init(wm8974_modinit);
788
789static void __exit wm8974_exit(void)
790{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100791 i2c_del_driver(&wm8974_i2c_driver);
Mark Brown0a1bf552009-05-23 11:18:41 +0100792}
793module_exit(wm8974_exit);
794
795MODULE_DESCRIPTION("ASoC WM8974 driver");
796MODULE_AUTHOR("Liam Girdwood");
797MODULE_LICENSE("GPL");