blob: b4363f6d19b3b2b8437dcce636aa9545d023544e [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{
277 snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets,
278 ARRAY_SIZE(wm8974_dapm_widgets));
279
280 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
281
Mark Brown0a1bf552009-05-23 11:18:41 +0100282 return 0;
283}
284
285struct pll_ {
Mark Brownc36b2fc2009-09-30 14:31:38 +0100286 unsigned int pre_div:1;
Mark Brown0a1bf552009-05-23 11:18:41 +0100287 unsigned int n:4;
288 unsigned int k;
289};
290
Mark Brown91d0c3e2009-06-30 19:02:32 +0100291/* The size in bits of the pll divide multiplied by 10
292 * to allow rounding later */
293#define FIXED_PLL_SIZE ((1 << 24) * 10)
294
Mark Brownc36b2fc2009-09-30 14:31:38 +0100295static void pll_factors(struct pll_ *pll_div,
296 unsigned int target, unsigned int source)
Mark Brown91d0c3e2009-06-30 19:02:32 +0100297{
298 unsigned long long Kpart;
299 unsigned int K, Ndiv, Nmod;
300
Mark Brownc36b2fc2009-09-30 14:31:38 +0100301 /* There is a fixed divide by 4 in the output path */
302 target *= 4;
303
Mark Brown91d0c3e2009-06-30 19:02:32 +0100304 Ndiv = target / source;
305 if (Ndiv < 6) {
Mark Brownc36b2fc2009-09-30 14:31:38 +0100306 source /= 2;
307 pll_div->pre_div = 1;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100308 Ndiv = target / source;
309 } else
Mark Brownc36b2fc2009-09-30 14:31:38 +0100310 pll_div->pre_div = 0;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100311
312 if ((Ndiv < 6) || (Ndiv > 12))
313 printk(KERN_WARNING
Mark Brown8b83a192009-06-30 19:37:02 +0100314 "WM8974 N value %u outwith recommended range!\n",
Mark Brown91d0c3e2009-06-30 19:02:32 +0100315 Ndiv);
316
Mark Brownc36b2fc2009-09-30 14:31:38 +0100317 pll_div->n = Ndiv;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100318 Nmod = target % source;
319 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
320
321 do_div(Kpart, source);
322
323 K = Kpart & 0xFFFFFFFF;
324
325 /* Check if we need to round */
326 if ((K % 10) >= 5)
327 K += 5;
328
329 /* Move down to proper range now rounding is done */
330 K /= 10;
331
Mark Brownc36b2fc2009-09-30 14:31:38 +0100332 pll_div->k = K;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100333}
Mark Brown0a1bf552009-05-23 11:18:41 +0100334
Mark Brown85488032009-09-05 18:52:16 +0100335static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
336 int source, unsigned int freq_in, unsigned int freq_out)
Mark Brown0a1bf552009-05-23 11:18:41 +0100337{
338 struct snd_soc_codec *codec = codec_dai->codec;
Mark Brownc36b2fc2009-09-30 14:31:38 +0100339 struct pll_ pll_div;
Mark Brown0a1bf552009-05-23 11:18:41 +0100340 u16 reg;
341
Mark Brown1a55b3f2009-05-23 11:31:40 +0100342 if (freq_in == 0 || freq_out == 0) {
Mark Brown91d0c3e2009-06-30 19:02:32 +0100343 /* Clock CODEC directly from MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100344 reg = snd_soc_read(codec, WM8974_CLOCK);
345 snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100346
347 /* Turn off PLL */
Mark Brown1e97f502009-08-15 12:15:10 +0100348 reg = snd_soc_read(codec, WM8974_POWER1);
349 snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
Mark Brown0a1bf552009-05-23 11:18:41 +0100350 return 0;
351 }
352
Mark Brownc36b2fc2009-09-30 14:31:38 +0100353 pll_factors(&pll_div, freq_out, freq_in);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100354
Mark Brown1e97f502009-08-15 12:15:10 +0100355 snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
356 snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
357 snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
358 snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
359 reg = snd_soc_read(codec, WM8974_POWER1);
360 snd_soc_write(codec, WM8974_POWER1, reg | 0x020);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100361
362 /* Run CODEC from PLL instead of MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100363 reg = snd_soc_read(codec, WM8974_CLOCK);
364 snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100365
366 return 0;
Mark Brown0a1bf552009-05-23 11:18:41 +0100367}
368
369/*
370 * Configure WM8974 clock dividers.
371 */
372static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
373 int div_id, int div)
374{
375 struct snd_soc_codec *codec = codec_dai->codec;
376 u16 reg;
377
378 switch (div_id) {
379 case WM8974_OPCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100380 reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
381 snd_soc_write(codec, WM8974_GPIO, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100382 break;
383 case WM8974_MCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100384 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
385 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100386 break;
Mark Brown0a1bf552009-05-23 11:18:41 +0100387 case WM8974_BCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100388 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
389 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100390 break;
391 default:
392 return -EINVAL;
393 }
394
395 return 0;
396}
397
398static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
399 unsigned int fmt)
400{
401 struct snd_soc_codec *codec = codec_dai->codec;
402 u16 iface = 0;
Mark Brown1e97f502009-08-15 12:15:10 +0100403 u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
Mark Brown0a1bf552009-05-23 11:18:41 +0100404
405 /* set master/slave audio interface */
406 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
407 case SND_SOC_DAIFMT_CBM_CFM:
408 clk |= 0x0001;
409 break;
410 case SND_SOC_DAIFMT_CBS_CFS:
411 break;
412 default:
413 return -EINVAL;
414 }
415
416 /* interface format */
417 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
418 case SND_SOC_DAIFMT_I2S:
419 iface |= 0x0010;
420 break;
421 case SND_SOC_DAIFMT_RIGHT_J:
422 break;
423 case SND_SOC_DAIFMT_LEFT_J:
424 iface |= 0x0008;
425 break;
426 case SND_SOC_DAIFMT_DSP_A:
427 iface |= 0x00018;
428 break;
429 default:
430 return -EINVAL;
431 }
432
433 /* clock inversion */
434 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
435 case SND_SOC_DAIFMT_NB_NF:
436 break;
437 case SND_SOC_DAIFMT_IB_IF:
438 iface |= 0x0180;
439 break;
440 case SND_SOC_DAIFMT_IB_NF:
441 iface |= 0x0100;
442 break;
443 case SND_SOC_DAIFMT_NB_IF:
444 iface |= 0x0080;
445 break;
446 default:
447 return -EINVAL;
448 }
449
Mark Brown1e97f502009-08-15 12:15:10 +0100450 snd_soc_write(codec, WM8974_IFACE, iface);
451 snd_soc_write(codec, WM8974_CLOCK, clk);
Mark Brown0a1bf552009-05-23 11:18:41 +0100452 return 0;
453}
454
455static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
456 struct snd_pcm_hw_params *params,
457 struct snd_soc_dai *dai)
458{
459 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100460 u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
461 u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
Mark Brown0a1bf552009-05-23 11:18:41 +0100462
463 /* bit size */
464 switch (params_format(params)) {
465 case SNDRV_PCM_FORMAT_S16_LE:
466 break;
467 case SNDRV_PCM_FORMAT_S20_3LE:
468 iface |= 0x0020;
469 break;
470 case SNDRV_PCM_FORMAT_S24_LE:
471 iface |= 0x0040;
472 break;
473 case SNDRV_PCM_FORMAT_S32_LE:
474 iface |= 0x0060;
475 break;
476 }
477
478 /* filter coefficient */
479 switch (params_rate(params)) {
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100480 case 8000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100481 adn |= 0x5 << 1;
482 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100483 case 11025:
Mark Brown0a1bf552009-05-23 11:18:41 +0100484 adn |= 0x4 << 1;
485 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100486 case 16000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100487 adn |= 0x3 << 1;
488 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100489 case 22050:
Mark Brown0a1bf552009-05-23 11:18:41 +0100490 adn |= 0x2 << 1;
491 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100492 case 32000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100493 adn |= 0x1 << 1;
494 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100495 case 44100:
496 case 48000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100497 break;
498 }
499
Mark Brown1e97f502009-08-15 12:15:10 +0100500 snd_soc_write(codec, WM8974_IFACE, iface);
501 snd_soc_write(codec, WM8974_ADD, adn);
Mark Brown0a1bf552009-05-23 11:18:41 +0100502 return 0;
503}
504
505static int wm8974_mute(struct snd_soc_dai *dai, int mute)
506{
507 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100508 u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
Mark Brown0a1bf552009-05-23 11:18:41 +0100509
Mark Brown1a55b3f2009-05-23 11:31:40 +0100510 if (mute)
Mark Brown1e97f502009-08-15 12:15:10 +0100511 snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
Mark Brown0a1bf552009-05-23 11:18:41 +0100512 else
Mark Brown1e97f502009-08-15 12:15:10 +0100513 snd_soc_write(codec, WM8974_DAC, mute_reg);
Mark Brown0a1bf552009-05-23 11:18:41 +0100514 return 0;
515}
516
517/* liam need to make this lower power with dapm */
518static int wm8974_set_bias_level(struct snd_soc_codec *codec,
519 enum snd_soc_bias_level level)
520{
Mark Brown1e97f502009-08-15 12:15:10 +0100521 u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100522
Mark Brown0a1bf552009-05-23 11:18:41 +0100523 switch (level) {
524 case SND_SOC_BIAS_ON:
Mark Brown0a1bf552009-05-23 11:18:41 +0100525 case SND_SOC_BIAS_PREPARE:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100526 power1 |= 0x1; /* VMID 50k */
Mark Brown1e97f502009-08-15 12:15:10 +0100527 snd_soc_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100528 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100529
Mark Brown0a1bf552009-05-23 11:18:41 +0100530 case SND_SOC_BIAS_STANDBY:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100531 power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
532
533 if (codec->bias_level == SND_SOC_BIAS_OFF) {
534 /* Initial cap charge at VMID 5k */
Mark Brown1e97f502009-08-15 12:15:10 +0100535 snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
Mark Browndf1ef7a2009-06-30 19:01:09 +0100536 mdelay(100);
537 }
538
539 power1 |= 0x2; /* VMID 500k */
Mark Brown1e97f502009-08-15 12:15:10 +0100540 snd_soc_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100541 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100542
Mark Brown0a1bf552009-05-23 11:18:41 +0100543 case SND_SOC_BIAS_OFF:
Mark Brown1e97f502009-08-15 12:15:10 +0100544 snd_soc_write(codec, WM8974_POWER1, 0);
545 snd_soc_write(codec, WM8974_POWER2, 0);
546 snd_soc_write(codec, WM8974_POWER3, 0);
Mark Brown0a1bf552009-05-23 11:18:41 +0100547 break;
548 }
Mark Browndf1ef7a2009-06-30 19:01:09 +0100549
Mark Brown0a1bf552009-05-23 11:18:41 +0100550 codec->bias_level = level;
551 return 0;
552}
553
Mark Brown1a55b3f2009-05-23 11:31:40 +0100554#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
Mark Brown0a1bf552009-05-23 11:18:41 +0100555
556#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
557 SNDRV_PCM_FMTBIT_S24_LE)
558
559static struct snd_soc_dai_ops wm8974_ops = {
560 .hw_params = wm8974_pcm_hw_params,
561 .digital_mute = wm8974_mute,
562 .set_fmt = wm8974_set_dai_fmt,
563 .set_clkdiv = wm8974_set_dai_clkdiv,
564 .set_pll = wm8974_set_dai_pll,
565};
566
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000567static struct snd_soc_dai_driver wm8974_dai = {
568 .name = "wm8974-hifi",
Mark Brown0a1bf552009-05-23 11:18:41 +0100569 .playback = {
570 .stream_name = "Playback",
571 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100572 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100573 .rates = WM8974_RATES,
574 .formats = WM8974_FORMATS,},
575 .capture = {
576 .stream_name = "Capture",
577 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100578 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100579 .rates = WM8974_RATES,
580 .formats = WM8974_FORMATS,},
581 .ops = &wm8974_ops,
Mark Browncb11d392009-06-30 19:36:39 +0100582 .symmetric_rates = 1,
Mark Brown0a1bf552009-05-23 11:18:41 +0100583};
Mark Brown0a1bf552009-05-23 11:18:41 +0100584
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000585static int wm8974_suspend(struct snd_soc_codec *codec, pm_message_t state)
Mark Brown0a1bf552009-05-23 11:18:41 +0100586{
Mark Brown0a1bf552009-05-23 11:18:41 +0100587 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
588 return 0;
589}
590
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000591static int wm8974_resume(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100592{
Mark Brown0a1bf552009-05-23 11:18:41 +0100593 int i;
594 u8 data[2];
595 u16 *cache = codec->reg_cache;
596
597 /* Sync reg_cache with the hardware */
598 for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
599 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
600 data[1] = cache[i] & 0x00ff;
601 codec->hw_write(codec->control_data, data, 2);
602 }
603 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
Mark Brown29e189c2010-05-07 20:30:00 +0100604
Mark Brown0a1bf552009-05-23 11:18:41 +0100605 return 0;
606}
607
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000608static int wm8974_probe(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100609{
Mark Brown0a1bf552009-05-23 11:18:41 +0100610 int ret = 0;
611
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000612 ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100613 if (ret < 0) {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000614 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
615 return ret;
Mark Brown0a1bf552009-05-23 11:18:41 +0100616 }
617
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000618 ret = wm8974_reset(codec);
619 if (ret < 0) {
620 dev_err(codec->dev, "Failed to issue reset\n");
621 return ret;
622 }
623
624 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100625 snd_soc_add_controls(codec, wm8974_snd_controls,
626 ARRAY_SIZE(wm8974_snd_controls));
Mark Brown0a1bf552009-05-23 11:18:41 +0100627 wm8974_add_widgets(codec);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100628
Mark Brown0a1bf552009-05-23 11:18:41 +0100629 return ret;
Mark Brown0a1bf552009-05-23 11:18:41 +0100630}
631
632/* power down chip */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000633static int wm8974_remove(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100634{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000635 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
Mark Brown0a1bf552009-05-23 11:18:41 +0100636 return 0;
637}
638
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000639static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
Mark Brown0a1bf552009-05-23 11:18:41 +0100640 .probe = wm8974_probe,
641 .remove = wm8974_remove,
642 .suspend = wm8974_suspend,
643 .resume = wm8974_resume,
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000644 .set_bias_level = wm8974_set_bias_level,
645 .reg_cache_size = ARRAY_SIZE(wm8974_reg),
646 .reg_word_size = sizeof(u16),
647 .reg_cache_default = wm8974_reg,
Mark Brown0a1bf552009-05-23 11:18:41 +0100648};
Mark Brown0a1bf552009-05-23 11:18:41 +0100649
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000650#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
Mark Brown4fcbbb62009-05-23 12:27:03 +0100651static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
652 const struct i2c_device_id *id)
653{
654 struct wm8974_priv *wm8974;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000655 int ret;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100656
657 wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
658 if (wm8974 == NULL)
659 return -ENOMEM;
660
Mark Brown4fcbbb62009-05-23 12:27:03 +0100661 i2c_set_clientdata(i2c, wm8974);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100662
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000663 ret = snd_soc_register_codec(&i2c->dev,
664 &soc_codec_dev_wm8974, &wm8974_dai, 1);
665 if (ret < 0)
666 kfree(wm8974);
667 return ret;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100668}
669
670static __devexit int wm8974_i2c_remove(struct i2c_client *client)
671{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000672 snd_soc_unregister_codec(&client->dev);
673 kfree(i2c_get_clientdata(client));
Mark Brown4fcbbb62009-05-23 12:27:03 +0100674 return 0;
675}
676
677static const struct i2c_device_id wm8974_i2c_id[] = {
678 { "wm8974", 0 },
679 { }
680};
681MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
682
683static struct i2c_driver wm8974_i2c_driver = {
684 .driver = {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000685 .name = "wm8974-codec",
Mark Brown4fcbbb62009-05-23 12:27:03 +0100686 .owner = THIS_MODULE,
687 },
688 .probe = wm8974_i2c_probe,
689 .remove = __devexit_p(wm8974_i2c_remove),
690 .id_table = wm8974_i2c_id,
691};
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000692#endif
Mark Brown4fcbbb62009-05-23 12:27:03 +0100693
Mark Brown0a1bf552009-05-23 11:18:41 +0100694static int __init wm8974_modinit(void)
695{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000696 int ret = 0;
697#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
698 ret = i2c_add_driver(&wm8974_i2c_driver);
699 if (ret != 0) {
700 printk(KERN_ERR "Failed to register wm8974 I2C driver: %d\n",
701 ret);
702 }
703#endif
704 return ret;
Mark Brown0a1bf552009-05-23 11:18:41 +0100705}
706module_init(wm8974_modinit);
707
708static void __exit wm8974_exit(void)
709{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000710#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
Mark Brown4fcbbb62009-05-23 12:27:03 +0100711 i2c_del_driver(&wm8974_i2c_driver);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000712#endif
Mark Brown0a1bf552009-05-23 11:18:41 +0100713}
714module_exit(wm8974_exit);
715
716MODULE_DESCRIPTION("ASoC WM8974 driver");
717MODULE_AUTHOR("Liam Girdwood");
718MODULE_LICENSE("GPL");