blob: d19bb14842d465c336ed1aaa22b0ba4bc4e2ff14 [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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090021#include <linux/slab.h>
Mark Brown0a1bf552009-05-23 11:18:41 +010022#include <sound/core.h>
23#include <sound/pcm.h>
24#include <sound/pcm_params.h>
25#include <sound/soc.h>
26#include <sound/soc-dapm.h>
27#include <sound/initval.h>
Mark Browna5f8d2f2009-06-30 19:30:33 +010028#include <sound/tlv.h>
Mark Brown0a1bf552009-05-23 11:18:41 +010029
30#include "wm8974.h"
31
Mark Brown0a1bf552009-05-23 11:18:41 +010032static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
Mark Brown1a55b3f2009-05-23 11:31:40 +010033 0x0000, 0x0000, 0x0000, 0x0000,
34 0x0050, 0x0000, 0x0140, 0x0000,
35 0x0000, 0x0000, 0x0000, 0x00ff,
36 0x0000, 0x0000, 0x0100, 0x00ff,
37 0x0000, 0x0000, 0x012c, 0x002c,
38 0x002c, 0x002c, 0x002c, 0x0000,
39 0x0032, 0x0000, 0x0000, 0x0000,
40 0x0000, 0x0000, 0x0000, 0x0000,
41 0x0038, 0x000b, 0x0032, 0x0000,
42 0x0008, 0x000c, 0x0093, 0x00e9,
43 0x0000, 0x0000, 0x0000, 0x0000,
44 0x0003, 0x0010, 0x0000, 0x0000,
45 0x0000, 0x0002, 0x0000, 0x0000,
46 0x0000, 0x0000, 0x0039, 0x0000,
47 0x0000,
Mark Brown0a1bf552009-05-23 11:18:41 +010048};
49
Mark Browndf1ef7a2009-06-30 19:01:09 +010050#define WM8974_POWER1_BIASEN 0x08
Guennadi Liakhovetski48c03ce2009-12-17 14:51:35 +010051#define WM8974_POWER1_BUFIOEN 0x04
Mark Browndf1ef7a2009-06-30 19:01:09 +010052
Mark Brown4fcbbb62009-05-23 12:27:03 +010053struct wm8974_priv {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +000054 enum snd_soc_control_type control_type;
Mark Brown4fcbbb62009-05-23 12:27:03 +010055 u16 reg_cache[WM8974_CACHEREGNUM];
56};
57
Mark Brown1e97f502009-08-15 12:15:10 +010058#define wm8974_reset(c) snd_soc_write(c, WM8974_RESET, 0)
Mark Brown0a1bf552009-05-23 11:18:41 +010059
60static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
61static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
62static const char *wm8974_eqmode[] = {"Capture", "Playback" };
63static const char *wm8974_bw[] = {"Narrow", "Wide" };
64static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
65static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
66static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
67static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
68static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
69static const char *wm8974_alc[] = {"ALC", "Limiter" };
70
71static const struct soc_enum wm8974_enum[] = {
72 SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
73 SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
74 SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp),
75 SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode),
76
77 SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1),
78 SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw),
79 SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2),
80 SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw),
81
82 SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3),
83 SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw),
84 SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4),
85 SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw),
86
87 SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5),
88 SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc),
89};
90
Mark Brown8a123ee2009-06-30 21:10:34 +010091static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" };
92
93static const struct soc_enum wm8974_auxmode =
94 SOC_ENUM_SINGLE(WM8974_INPUT, 3, 2, wm8974_auxmode_text);
95
Mark Browna5f8d2f2009-06-30 19:30:33 +010096static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
97static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
98static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
99static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
100
Mark Brown0a1bf552009-05-23 11:18:41 +0100101static const struct snd_kcontrol_new wm8974_snd_controls[] = {
102
103SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
104
105SOC_ENUM("DAC Companding", wm8974_enum[1]),
106SOC_ENUM("ADC Companding", wm8974_enum[0]),
107
108SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
109SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
110
Mark Browna5f8d2f2009-06-30 19:30:33 +0100111SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100112
113SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
114SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
javier Martin25cbf462009-07-21 11:15:06 +0200115SOC_SINGLE("ADC Inversion Switch", WM8974_ADC, 0, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100116
Mark Browna5f8d2f2009-06-30 19:30:33 +0100117SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL, 0, 255, 0, digital_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100118
119SOC_ENUM("Equaliser Function", wm8974_enum[3]),
120SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100121SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100122
123SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
124SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100125SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100126
127SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
128SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100129SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100130
131SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
132SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100133SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100134
135SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
136SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100137SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100138
139SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0),
140SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0),
141SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0),
142
143SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0),
144SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0),
145
146SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0),
147SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0),
148SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0),
149
150SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0),
151SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0),
152SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0),
153
154SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
155SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0),
156SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0),
157
158SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0),
159SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0),
160
161SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100162SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0, inpga_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100163
164SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0),
165SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1),
Mark Brown8a123ee2009-06-30 21:10:34 +0100166SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0, spk_tlv),
167
168SOC_ENUM("Aux Mode", wm8974_auxmode),
Mark Brown0a1bf552009-05-23 11:18:41 +0100169
170SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100171SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1),
Guennadi Liakhovetskib2c3e922010-01-29 15:31:06 +0100172
173/* DAC / ADC oversampling */
174SOC_SINGLE("DAC 128x Oversampling Switch", WM8974_DAC, 8, 1, 0),
175SOC_SINGLE("ADC 128x Oversampling Switch", WM8974_ADC, 8, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100176};
177
Mark Brown0a1bf552009-05-23 11:18:41 +0100178/* Speaker Output Mixer */
179static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
180SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
181SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
Mark Brown759512f2010-04-23 17:39:23 +0100182SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100183};
184
185/* Mono Output Mixer */
186static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
187SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
188SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100189SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0),
190};
191
192/* Boost mixer */
193static const struct snd_kcontrol_new wm8974_boost_mixer[] = {
194SOC_DAPM_SINGLE("Aux Switch", WM8974_INPPGA, 6, 1, 0),
195};
196
197/* Input PGA */
198static const struct snd_kcontrol_new wm8974_inpga[] = {
199SOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0),
200SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0),
201SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100202};
203
204/* AUX Input boost vol */
205static const struct snd_kcontrol_new wm8974_aux_boost_controls =
206SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
207
208/* Mic Input boost vol */
209static const struct snd_kcontrol_new wm8974_mic_boost_controls =
210SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
211
Mark Brown0a1bf552009-05-23 11:18:41 +0100212static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
213SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
214 &wm8974_speaker_mixer_controls[0],
215 ARRAY_SIZE(wm8974_speaker_mixer_controls)),
216SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
217 &wm8974_mono_mixer_controls[0],
218 ARRAY_SIZE(wm8974_mono_mixer_controls)),
219SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100220SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100221SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
222SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
223SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
224SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100225
Mark Brown8a123ee2009-06-30 21:10:34 +0100226SND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga,
227 ARRAY_SIZE(wm8974_inpga)),
228SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0,
229 wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)),
Mark Brown0a1bf552009-05-23 11:18:41 +0100230
231SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
232
233SND_SOC_DAPM_INPUT("MICN"),
234SND_SOC_DAPM_INPUT("MICP"),
235SND_SOC_DAPM_INPUT("AUX"),
236SND_SOC_DAPM_OUTPUT("MONOOUT"),
237SND_SOC_DAPM_OUTPUT("SPKOUTP"),
238SND_SOC_DAPM_OUTPUT("SPKOUTN"),
239};
240
241static const struct snd_soc_dapm_route audio_map[] = {
242 /* Mono output mixer */
243 {"Mono Mixer", "PCM Playback Switch", "DAC"},
244 {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
245 {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
246
247 /* Speaker output mixer */
248 {"Speaker Mixer", "PCM Playback Switch", "DAC"},
249 {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
250 {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
251
252 /* Outputs */
253 {"Mono Out", NULL, "Mono Mixer"},
254 {"MONOOUT", NULL, "Mono Out"},
255 {"SpkN Out", NULL, "Speaker Mixer"},
256 {"SpkP Out", NULL, "Speaker Mixer"},
257 {"SPKOUTN", NULL, "SpkN Out"},
258 {"SPKOUTP", NULL, "SpkP Out"},
259
260 /* Boost Mixer */
Mark Brown8a123ee2009-06-30 21:10:34 +0100261 {"ADC", NULL, "Boost Mixer"},
262 {"Boost Mixer", "Aux Switch", "Aux Input"},
263 {"Boost Mixer", NULL, "Input PGA"},
264 {"Boost Mixer", NULL, "MICP"},
265
266 /* Input PGA */
267 {"Input PGA", "Aux Switch", "Aux Input"},
268 {"Input PGA", "MicN Switch", "MICN"},
269 {"Input PGA", "MicP Switch", "MICP"},
Mark Brown0a1bf552009-05-23 11:18:41 +0100270
271 /* Inputs */
Mark Brown8a123ee2009-06-30 21:10:34 +0100272 {"Aux Input", NULL, "AUX"},
Mark Brown0a1bf552009-05-23 11:18:41 +0100273};
274
275static int wm8974_add_widgets(struct snd_soc_codec *codec)
276{
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200277 struct snd_soc_dapm_context *dapm = &codec->dapm;
Mark Brown0a1bf552009-05-23 11:18:41 +0100278
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200279 snd_soc_dapm_new_controls(dapm, wm8974_dapm_widgets,
280 ARRAY_SIZE(wm8974_dapm_widgets));
281 snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
Mark Brown0a1bf552009-05-23 11:18:41 +0100282
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
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200534 if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
Mark Browndf1ef7a2009-06-30 19:01:09 +0100535 /* 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
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200551 codec->dapm.bias_level = level;
Mark Brown0a1bf552009-05-23 11:18:41 +0100552 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
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000568static struct snd_soc_dai_driver wm8974_dai = {
569 .name = "wm8974-hifi",
Mark Brown0a1bf552009-05-23 11:18:41 +0100570 .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};
Mark Brown0a1bf552009-05-23 11:18:41 +0100585
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000586static int wm8974_suspend(struct snd_soc_codec *codec, pm_message_t state)
Mark Brown0a1bf552009-05-23 11:18:41 +0100587{
Mark Brown0a1bf552009-05-23 11:18:41 +0100588 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
589 return 0;
590}
591
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000592static int wm8974_resume(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100593{
Mark Brown0a1bf552009-05-23 11:18:41 +0100594 int i;
595 u8 data[2];
596 u16 *cache = codec->reg_cache;
597
598 /* Sync reg_cache with the hardware */
599 for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
600 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
601 data[1] = cache[i] & 0x00ff;
602 codec->hw_write(codec->control_data, data, 2);
603 }
604 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
Mark Brown29e189c2010-05-07 20:30:00 +0100605
Mark Brown0a1bf552009-05-23 11:18:41 +0100606 return 0;
607}
608
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000609static int wm8974_probe(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100610{
Mark Brown0a1bf552009-05-23 11:18:41 +0100611 int ret = 0;
612
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000613 ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100614 if (ret < 0) {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000615 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
616 return ret;
Mark Brown0a1bf552009-05-23 11:18:41 +0100617 }
618
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000619 ret = wm8974_reset(codec);
620 if (ret < 0) {
621 dev_err(codec->dev, "Failed to issue reset\n");
622 return ret;
623 }
624
625 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100626 snd_soc_add_controls(codec, wm8974_snd_controls,
627 ARRAY_SIZE(wm8974_snd_controls));
Mark Brown0a1bf552009-05-23 11:18:41 +0100628 wm8974_add_widgets(codec);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100629
Mark Brown0a1bf552009-05-23 11:18:41 +0100630 return ret;
Mark Brown0a1bf552009-05-23 11:18:41 +0100631}
632
633/* power down chip */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000634static int wm8974_remove(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100635{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000636 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
Mark Brown0a1bf552009-05-23 11:18:41 +0100637 return 0;
638}
639
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000640static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
Mark Brown0a1bf552009-05-23 11:18:41 +0100641 .probe = wm8974_probe,
642 .remove = wm8974_remove,
643 .suspend = wm8974_suspend,
644 .resume = wm8974_resume,
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000645 .set_bias_level = wm8974_set_bias_level,
646 .reg_cache_size = ARRAY_SIZE(wm8974_reg),
647 .reg_word_size = sizeof(u16),
648 .reg_cache_default = wm8974_reg,
Mark Brown0a1bf552009-05-23 11:18:41 +0100649};
Mark Brown0a1bf552009-05-23 11:18:41 +0100650
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000651#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
Mark Brown4fcbbb62009-05-23 12:27:03 +0100652static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
653 const struct i2c_device_id *id)
654{
655 struct wm8974_priv *wm8974;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000656 int ret;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100657
658 wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
659 if (wm8974 == NULL)
660 return -ENOMEM;
661
Mark Brown4fcbbb62009-05-23 12:27:03 +0100662 i2c_set_clientdata(i2c, wm8974);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100663
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000664 ret = snd_soc_register_codec(&i2c->dev,
665 &soc_codec_dev_wm8974, &wm8974_dai, 1);
666 if (ret < 0)
667 kfree(wm8974);
668 return ret;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100669}
670
671static __devexit int wm8974_i2c_remove(struct i2c_client *client)
672{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000673 snd_soc_unregister_codec(&client->dev);
674 kfree(i2c_get_clientdata(client));
Mark Brown4fcbbb62009-05-23 12:27:03 +0100675 return 0;
676}
677
678static const struct i2c_device_id wm8974_i2c_id[] = {
679 { "wm8974", 0 },
680 { }
681};
682MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
683
684static struct i2c_driver wm8974_i2c_driver = {
685 .driver = {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000686 .name = "wm8974-codec",
Mark Brown4fcbbb62009-05-23 12:27:03 +0100687 .owner = THIS_MODULE,
688 },
689 .probe = wm8974_i2c_probe,
690 .remove = __devexit_p(wm8974_i2c_remove),
691 .id_table = wm8974_i2c_id,
692};
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000693#endif
Mark Brown4fcbbb62009-05-23 12:27:03 +0100694
Mark Brown0a1bf552009-05-23 11:18:41 +0100695static int __init wm8974_modinit(void)
696{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000697 int ret = 0;
698#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
699 ret = i2c_add_driver(&wm8974_i2c_driver);
700 if (ret != 0) {
701 printk(KERN_ERR "Failed to register wm8974 I2C driver: %d\n",
702 ret);
703 }
704#endif
705 return ret;
Mark Brown0a1bf552009-05-23 11:18:41 +0100706}
707module_init(wm8974_modinit);
708
709static void __exit wm8974_exit(void)
710{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000711#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
Mark Brown4fcbbb62009-05-23 12:27:03 +0100712 i2c_del_driver(&wm8974_i2c_driver);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000713#endif
Mark Brown0a1bf552009-05-23 11:18:41 +0100714}
715module_exit(wm8974_exit);
716
717MODULE_DESCRIPTION("ASoC WM8974 driver");
718MODULE_AUTHOR("Liam Girdwood");
719MODULE_LICENSE("GPL");