blob: 4a6a7b5a61ba442583cce6d1d6e6338836130f70 [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 Brown9a185b92011-10-06 11:10:01 +01006 * Author: Liam Girdwood <Liam.Girdwood@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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090020#include <linux/slab.h>
Mark Brown0a1bf552009-05-23 11:18:41 +010021#include <sound/core.h>
22#include <sound/pcm.h>
23#include <sound/pcm_params.h>
24#include <sound/soc.h>
Mark Brown0a1bf552009-05-23 11:18:41 +010025#include <sound/initval.h>
Mark Browna5f8d2f2009-06-30 19:30:33 +010026#include <sound/tlv.h>
Mark Brown0a1bf552009-05-23 11:18:41 +010027
28#include "wm8974.h"
29
Mark Brown0a1bf552009-05-23 11:18:41 +010030static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
Mark Brown1a55b3f2009-05-23 11:31:40 +010031 0x0000, 0x0000, 0x0000, 0x0000,
32 0x0050, 0x0000, 0x0140, 0x0000,
33 0x0000, 0x0000, 0x0000, 0x00ff,
34 0x0000, 0x0000, 0x0100, 0x00ff,
35 0x0000, 0x0000, 0x012c, 0x002c,
36 0x002c, 0x002c, 0x002c, 0x0000,
37 0x0032, 0x0000, 0x0000, 0x0000,
38 0x0000, 0x0000, 0x0000, 0x0000,
39 0x0038, 0x000b, 0x0032, 0x0000,
40 0x0008, 0x000c, 0x0093, 0x00e9,
41 0x0000, 0x0000, 0x0000, 0x0000,
42 0x0003, 0x0010, 0x0000, 0x0000,
43 0x0000, 0x0002, 0x0000, 0x0000,
44 0x0000, 0x0000, 0x0039, 0x0000,
45 0x0000,
Mark Brown0a1bf552009-05-23 11:18:41 +010046};
47
Mark Browndf1ef7a2009-06-30 19:01:09 +010048#define WM8974_POWER1_BIASEN 0x08
Guennadi Liakhovetski48c03ce2009-12-17 14:51:35 +010049#define WM8974_POWER1_BUFIOEN 0x04
Mark Browndf1ef7a2009-06-30 19:01:09 +010050
Mark Brown4fcbbb62009-05-23 12:27:03 +010051struct wm8974_priv {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +000052 enum snd_soc_control_type control_type;
Mark Brown4fcbbb62009-05-23 12:27:03 +010053};
54
Mark Brown1e97f502009-08-15 12:15:10 +010055#define wm8974_reset(c) snd_soc_write(c, WM8974_RESET, 0)
Mark Brown0a1bf552009-05-23 11:18:41 +010056
57static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
58static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
59static const char *wm8974_eqmode[] = {"Capture", "Playback" };
60static const char *wm8974_bw[] = {"Narrow", "Wide" };
61static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
62static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
63static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
64static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
65static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
66static const char *wm8974_alc[] = {"ALC", "Limiter" };
67
68static const struct soc_enum wm8974_enum[] = {
69 SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
70 SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
71 SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp),
72 SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode),
73
74 SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1),
75 SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw),
76 SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2),
77 SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw),
78
79 SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3),
80 SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw),
81 SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4),
82 SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw),
83
84 SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5),
85 SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc),
86};
87
Mark Brown8a123ee2009-06-30 21:10:34 +010088static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" };
89
90static const struct soc_enum wm8974_auxmode =
91 SOC_ENUM_SINGLE(WM8974_INPUT, 3, 2, wm8974_auxmode_text);
92
Mark Browna5f8d2f2009-06-30 19:30:33 +010093static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
94static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
95static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
96static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
97
Mark Brown0a1bf552009-05-23 11:18:41 +010098static const struct snd_kcontrol_new wm8974_snd_controls[] = {
99
100SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
101
102SOC_ENUM("DAC Companding", wm8974_enum[1]),
103SOC_ENUM("ADC Companding", wm8974_enum[0]),
104
105SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
106SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
107
Mark Browna5f8d2f2009-06-30 19:30:33 +0100108SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100109
110SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
111SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
javier Martin25cbf462009-07-21 11:15:06 +0200112SOC_SINGLE("ADC Inversion Switch", WM8974_ADC, 0, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100113
Mark Browna5f8d2f2009-06-30 19:30:33 +0100114SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL, 0, 255, 0, digital_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100115
116SOC_ENUM("Equaliser Function", wm8974_enum[3]),
117SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100118SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100119
120SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
121SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100122SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100123
124SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
125SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100126SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100127
128SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
129SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100130SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100131
132SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
133SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100134SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100135
136SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0),
137SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0),
138SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0),
139
140SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0),
141SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0),
142
143SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0),
144SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0),
145SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0),
146
147SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0),
148SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0),
149SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0),
150
151SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
152SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0),
153SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0),
154
155SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0),
156SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0),
157
158SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100159SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0, inpga_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100160
161SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0),
162SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1),
Mark Brown8a123ee2009-06-30 21:10:34 +0100163SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0, spk_tlv),
164
165SOC_ENUM("Aux Mode", wm8974_auxmode),
Mark Brown0a1bf552009-05-23 11:18:41 +0100166
167SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100168SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1),
Guennadi Liakhovetskib2c3e922010-01-29 15:31:06 +0100169
170/* DAC / ADC oversampling */
171SOC_SINGLE("DAC 128x Oversampling Switch", WM8974_DAC, 8, 1, 0),
172SOC_SINGLE("ADC 128x Oversampling Switch", WM8974_ADC, 8, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100173};
174
Mark Brown0a1bf552009-05-23 11:18:41 +0100175/* Speaker Output Mixer */
176static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
177SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
178SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
Mark Brown759512f2010-04-23 17:39:23 +0100179SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100180};
181
182/* Mono Output Mixer */
183static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
184SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
185SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100186SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0),
187};
188
189/* Boost mixer */
190static const struct snd_kcontrol_new wm8974_boost_mixer[] = {
191SOC_DAPM_SINGLE("Aux Switch", WM8974_INPPGA, 6, 1, 0),
192};
193
194/* Input PGA */
195static const struct snd_kcontrol_new wm8974_inpga[] = {
196SOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0),
197SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0),
198SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100199};
200
201/* AUX Input boost vol */
202static const struct snd_kcontrol_new wm8974_aux_boost_controls =
203SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
204
205/* Mic Input boost vol */
206static const struct snd_kcontrol_new wm8974_mic_boost_controls =
207SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
208
Mark Brown0a1bf552009-05-23 11:18:41 +0100209static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
210SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
211 &wm8974_speaker_mixer_controls[0],
212 ARRAY_SIZE(wm8974_speaker_mixer_controls)),
213SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
214 &wm8974_mono_mixer_controls[0],
215 ARRAY_SIZE(wm8974_mono_mixer_controls)),
216SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100217SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100218SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
219SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
220SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
221SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100222
Mark Brown8a123ee2009-06-30 21:10:34 +0100223SND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga,
224 ARRAY_SIZE(wm8974_inpga)),
225SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0,
226 wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)),
Mark Brown0a1bf552009-05-23 11:18:41 +0100227
Mark Brown48dd2312011-10-27 09:47:09 +0200228SND_SOC_DAPM_SUPPLY("Mic Bias", WM8974_POWER1, 4, 0, NULL, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100229
230SND_SOC_DAPM_INPUT("MICN"),
231SND_SOC_DAPM_INPUT("MICP"),
232SND_SOC_DAPM_INPUT("AUX"),
233SND_SOC_DAPM_OUTPUT("MONOOUT"),
234SND_SOC_DAPM_OUTPUT("SPKOUTP"),
235SND_SOC_DAPM_OUTPUT("SPKOUTN"),
236};
237
238static const struct snd_soc_dapm_route audio_map[] = {
239 /* Mono output mixer */
240 {"Mono Mixer", "PCM Playback Switch", "DAC"},
241 {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
242 {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
243
244 /* Speaker output mixer */
245 {"Speaker Mixer", "PCM Playback Switch", "DAC"},
246 {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
247 {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
248
249 /* Outputs */
250 {"Mono Out", NULL, "Mono Mixer"},
251 {"MONOOUT", NULL, "Mono Out"},
252 {"SpkN Out", NULL, "Speaker Mixer"},
253 {"SpkP Out", NULL, "Speaker Mixer"},
254 {"SPKOUTN", NULL, "SpkN Out"},
255 {"SPKOUTP", NULL, "SpkP Out"},
256
257 /* Boost Mixer */
Mark Brown8a123ee2009-06-30 21:10:34 +0100258 {"ADC", NULL, "Boost Mixer"},
259 {"Boost Mixer", "Aux Switch", "Aux Input"},
260 {"Boost Mixer", NULL, "Input PGA"},
261 {"Boost Mixer", NULL, "MICP"},
262
263 /* Input PGA */
264 {"Input PGA", "Aux Switch", "Aux Input"},
265 {"Input PGA", "MicN Switch", "MICN"},
266 {"Input PGA", "MicP Switch", "MICP"},
Mark Brown0a1bf552009-05-23 11:18:41 +0100267
268 /* Inputs */
Mark Brown8a123ee2009-06-30 21:10:34 +0100269 {"Aux Input", NULL, "AUX"},
Mark Brown0a1bf552009-05-23 11:18:41 +0100270};
271
272static int wm8974_add_widgets(struct snd_soc_codec *codec)
273{
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200274 struct snd_soc_dapm_context *dapm = &codec->dapm;
Mark Brown0a1bf552009-05-23 11:18:41 +0100275
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200276 snd_soc_dapm_new_controls(dapm, wm8974_dapm_widgets,
277 ARRAY_SIZE(wm8974_dapm_widgets));
278 snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
Mark Brown0a1bf552009-05-23 11:18:41 +0100279
Mark Brown0a1bf552009-05-23 11:18:41 +0100280 return 0;
281}
282
283struct pll_ {
Mark Brownc36b2fc2009-09-30 14:31:38 +0100284 unsigned int pre_div:1;
Mark Brown0a1bf552009-05-23 11:18:41 +0100285 unsigned int n:4;
286 unsigned int k;
287};
288
Mark Brown91d0c3e2009-06-30 19:02:32 +0100289/* The size in bits of the pll divide multiplied by 10
290 * to allow rounding later */
291#define FIXED_PLL_SIZE ((1 << 24) * 10)
292
Mark Brownc36b2fc2009-09-30 14:31:38 +0100293static void pll_factors(struct pll_ *pll_div,
294 unsigned int target, unsigned int source)
Mark Brown91d0c3e2009-06-30 19:02:32 +0100295{
296 unsigned long long Kpart;
297 unsigned int K, Ndiv, Nmod;
298
Mark Brownc36b2fc2009-09-30 14:31:38 +0100299 /* There is a fixed divide by 4 in the output path */
300 target *= 4;
301
Mark Brown91d0c3e2009-06-30 19:02:32 +0100302 Ndiv = target / source;
303 if (Ndiv < 6) {
Mark Brownc36b2fc2009-09-30 14:31:38 +0100304 source /= 2;
305 pll_div->pre_div = 1;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100306 Ndiv = target / source;
307 } else
Mark Brownc36b2fc2009-09-30 14:31:38 +0100308 pll_div->pre_div = 0;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100309
310 if ((Ndiv < 6) || (Ndiv > 12))
311 printk(KERN_WARNING
Mark Brown8b83a192009-06-30 19:37:02 +0100312 "WM8974 N value %u outwith recommended range!\n",
Mark Brown91d0c3e2009-06-30 19:02:32 +0100313 Ndiv);
314
Mark Brownc36b2fc2009-09-30 14:31:38 +0100315 pll_div->n = Ndiv;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100316 Nmod = target % source;
317 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
318
319 do_div(Kpart, source);
320
321 K = Kpart & 0xFFFFFFFF;
322
323 /* Check if we need to round */
324 if ((K % 10) >= 5)
325 K += 5;
326
327 /* Move down to proper range now rounding is done */
328 K /= 10;
329
Mark Brownc36b2fc2009-09-30 14:31:38 +0100330 pll_div->k = K;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100331}
Mark Brown0a1bf552009-05-23 11:18:41 +0100332
Mark Brown85488032009-09-05 18:52:16 +0100333static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
334 int source, unsigned int freq_in, unsigned int freq_out)
Mark Brown0a1bf552009-05-23 11:18:41 +0100335{
336 struct snd_soc_codec *codec = codec_dai->codec;
Mark Brownc36b2fc2009-09-30 14:31:38 +0100337 struct pll_ pll_div;
Mark Brown0a1bf552009-05-23 11:18:41 +0100338 u16 reg;
339
Mark Brown1a55b3f2009-05-23 11:31:40 +0100340 if (freq_in == 0 || freq_out == 0) {
Mark Brown91d0c3e2009-06-30 19:02:32 +0100341 /* Clock CODEC directly from MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100342 reg = snd_soc_read(codec, WM8974_CLOCK);
343 snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100344
345 /* Turn off PLL */
Mark Brown1e97f502009-08-15 12:15:10 +0100346 reg = snd_soc_read(codec, WM8974_POWER1);
347 snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
Mark Brown0a1bf552009-05-23 11:18:41 +0100348 return 0;
349 }
350
Mark Brownc36b2fc2009-09-30 14:31:38 +0100351 pll_factors(&pll_div, freq_out, freq_in);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100352
Mark Brown1e97f502009-08-15 12:15:10 +0100353 snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
354 snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
355 snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
356 snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
357 reg = snd_soc_read(codec, WM8974_POWER1);
358 snd_soc_write(codec, WM8974_POWER1, reg | 0x020);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100359
360 /* Run CODEC from PLL instead of MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100361 reg = snd_soc_read(codec, WM8974_CLOCK);
362 snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100363
364 return 0;
Mark Brown0a1bf552009-05-23 11:18:41 +0100365}
366
367/*
368 * Configure WM8974 clock dividers.
369 */
370static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
371 int div_id, int div)
372{
373 struct snd_soc_codec *codec = codec_dai->codec;
374 u16 reg;
375
376 switch (div_id) {
377 case WM8974_OPCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100378 reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
379 snd_soc_write(codec, WM8974_GPIO, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100380 break;
381 case WM8974_MCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100382 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
383 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100384 break;
Mark Brown0a1bf552009-05-23 11:18:41 +0100385 case WM8974_BCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100386 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
387 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100388 break;
389 default:
390 return -EINVAL;
391 }
392
393 return 0;
394}
395
396static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
397 unsigned int fmt)
398{
399 struct snd_soc_codec *codec = codec_dai->codec;
400 u16 iface = 0;
Mark Brown1e97f502009-08-15 12:15:10 +0100401 u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
Mark Brown0a1bf552009-05-23 11:18:41 +0100402
403 /* set master/slave audio interface */
404 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
405 case SND_SOC_DAIFMT_CBM_CFM:
406 clk |= 0x0001;
407 break;
408 case SND_SOC_DAIFMT_CBS_CFS:
409 break;
410 default:
411 return -EINVAL;
412 }
413
414 /* interface format */
415 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
416 case SND_SOC_DAIFMT_I2S:
417 iface |= 0x0010;
418 break;
419 case SND_SOC_DAIFMT_RIGHT_J:
420 break;
421 case SND_SOC_DAIFMT_LEFT_J:
422 iface |= 0x0008;
423 break;
424 case SND_SOC_DAIFMT_DSP_A:
425 iface |= 0x00018;
426 break;
427 default:
428 return -EINVAL;
429 }
430
431 /* clock inversion */
432 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
433 case SND_SOC_DAIFMT_NB_NF:
434 break;
435 case SND_SOC_DAIFMT_IB_IF:
436 iface |= 0x0180;
437 break;
438 case SND_SOC_DAIFMT_IB_NF:
439 iface |= 0x0100;
440 break;
441 case SND_SOC_DAIFMT_NB_IF:
442 iface |= 0x0080;
443 break;
444 default:
445 return -EINVAL;
446 }
447
Mark Brown1e97f502009-08-15 12:15:10 +0100448 snd_soc_write(codec, WM8974_IFACE, iface);
449 snd_soc_write(codec, WM8974_CLOCK, clk);
Mark Brown0a1bf552009-05-23 11:18:41 +0100450 return 0;
451}
452
453static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
454 struct snd_pcm_hw_params *params,
455 struct snd_soc_dai *dai)
456{
457 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100458 u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
459 u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
Mark Brown0a1bf552009-05-23 11:18:41 +0100460
461 /* bit size */
462 switch (params_format(params)) {
463 case SNDRV_PCM_FORMAT_S16_LE:
464 break;
465 case SNDRV_PCM_FORMAT_S20_3LE:
466 iface |= 0x0020;
467 break;
468 case SNDRV_PCM_FORMAT_S24_LE:
469 iface |= 0x0040;
470 break;
471 case SNDRV_PCM_FORMAT_S32_LE:
472 iface |= 0x0060;
473 break;
474 }
475
476 /* filter coefficient */
477 switch (params_rate(params)) {
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100478 case 8000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100479 adn |= 0x5 << 1;
480 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100481 case 11025:
Mark Brown0a1bf552009-05-23 11:18:41 +0100482 adn |= 0x4 << 1;
483 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100484 case 16000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100485 adn |= 0x3 << 1;
486 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100487 case 22050:
Mark Brown0a1bf552009-05-23 11:18:41 +0100488 adn |= 0x2 << 1;
489 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100490 case 32000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100491 adn |= 0x1 << 1;
492 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100493 case 44100:
494 case 48000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100495 break;
496 }
497
Mark Brown1e97f502009-08-15 12:15:10 +0100498 snd_soc_write(codec, WM8974_IFACE, iface);
499 snd_soc_write(codec, WM8974_ADD, adn);
Mark Brown0a1bf552009-05-23 11:18:41 +0100500 return 0;
501}
502
503static int wm8974_mute(struct snd_soc_dai *dai, int mute)
504{
505 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100506 u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
Mark Brown0a1bf552009-05-23 11:18:41 +0100507
Mark Brown1a55b3f2009-05-23 11:31:40 +0100508 if (mute)
Mark Brown1e97f502009-08-15 12:15:10 +0100509 snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
Mark Brown0a1bf552009-05-23 11:18:41 +0100510 else
Mark Brown1e97f502009-08-15 12:15:10 +0100511 snd_soc_write(codec, WM8974_DAC, mute_reg);
Mark Brown0a1bf552009-05-23 11:18:41 +0100512 return 0;
513}
514
515/* liam need to make this lower power with dapm */
516static int wm8974_set_bias_level(struct snd_soc_codec *codec,
517 enum snd_soc_bias_level level)
518{
Mark Brown1e97f502009-08-15 12:15:10 +0100519 u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100520
Mark Brown0a1bf552009-05-23 11:18:41 +0100521 switch (level) {
522 case SND_SOC_BIAS_ON:
Mark Brown0a1bf552009-05-23 11:18:41 +0100523 case SND_SOC_BIAS_PREPARE:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100524 power1 |= 0x1; /* VMID 50k */
Mark Brown1e97f502009-08-15 12:15:10 +0100525 snd_soc_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100526 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100527
Mark Brown0a1bf552009-05-23 11:18:41 +0100528 case SND_SOC_BIAS_STANDBY:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100529 power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
530
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200531 if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
Axel Lin0bad3d82011-10-07 21:52:42 +0800532 snd_soc_cache_sync(codec);
533
Mark Browndf1ef7a2009-06-30 19:01:09 +0100534 /* 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
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200550 codec->dapm.bias_level = level;
Mark Brown0a1bf552009-05-23 11:18:41 +0100551 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
Lars-Peter Clausen85e76522011-11-23 11:40:40 +0100559static const struct snd_soc_dai_ops wm8974_ops = {
Mark Brown0a1bf552009-05-23 11:18:41 +0100560 .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
Lars-Peter Clausen84b315e2011-12-02 10:18:28 +0100585static int wm8974_suspend(struct snd_soc_codec *codec)
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 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
Mark Brown0a1bf552009-05-23 11:18:41 +0100594 return 0;
595}
596
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000597static int wm8974_probe(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100598{
Mark Brown0a1bf552009-05-23 11:18:41 +0100599 int ret = 0;
600
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000601 ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100602 if (ret < 0) {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000603 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
604 return ret;
Mark Brown0a1bf552009-05-23 11:18:41 +0100605 }
606
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000607 ret = wm8974_reset(codec);
608 if (ret < 0) {
609 dev_err(codec->dev, "Failed to issue reset\n");
610 return ret;
611 }
612
613 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100614 snd_soc_add_controls(codec, wm8974_snd_controls,
615 ARRAY_SIZE(wm8974_snd_controls));
Mark Brown0a1bf552009-05-23 11:18:41 +0100616 wm8974_add_widgets(codec);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100617
Mark Brown0a1bf552009-05-23 11:18:41 +0100618 return ret;
Mark Brown0a1bf552009-05-23 11:18:41 +0100619}
620
621/* power down chip */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000622static int wm8974_remove(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100623{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000624 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
Mark Brown0a1bf552009-05-23 11:18:41 +0100625 return 0;
626}
627
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000628static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
Mark Brown0a1bf552009-05-23 11:18:41 +0100629 .probe = wm8974_probe,
630 .remove = wm8974_remove,
631 .suspend = wm8974_suspend,
632 .resume = wm8974_resume,
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000633 .set_bias_level = wm8974_set_bias_level,
634 .reg_cache_size = ARRAY_SIZE(wm8974_reg),
635 .reg_word_size = sizeof(u16),
636 .reg_cache_default = wm8974_reg,
Mark Brown0a1bf552009-05-23 11:18:41 +0100637};
Mark Brown0a1bf552009-05-23 11:18:41 +0100638
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000639#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
Mark Brown4fcbbb62009-05-23 12:27:03 +0100640static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
641 const struct i2c_device_id *id)
642{
643 struct wm8974_priv *wm8974;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000644 int ret;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100645
646 wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
647 if (wm8974 == NULL)
648 return -ENOMEM;
649
Mark Brown4fcbbb62009-05-23 12:27:03 +0100650 i2c_set_clientdata(i2c, wm8974);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100651
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000652 ret = snd_soc_register_codec(&i2c->dev,
653 &soc_codec_dev_wm8974, &wm8974_dai, 1);
654 if (ret < 0)
655 kfree(wm8974);
656 return ret;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100657}
658
659static __devexit int wm8974_i2c_remove(struct i2c_client *client)
660{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000661 snd_soc_unregister_codec(&client->dev);
662 kfree(i2c_get_clientdata(client));
Mark Brown4fcbbb62009-05-23 12:27:03 +0100663 return 0;
664}
665
666static const struct i2c_device_id wm8974_i2c_id[] = {
667 { "wm8974", 0 },
668 { }
669};
670MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
671
672static struct i2c_driver wm8974_i2c_driver = {
673 .driver = {
Mark Brown091edcc2011-12-02 22:08:49 +0000674 .name = "wm8974",
Mark Brown4fcbbb62009-05-23 12:27:03 +0100675 .owner = THIS_MODULE,
676 },
677 .probe = wm8974_i2c_probe,
678 .remove = __devexit_p(wm8974_i2c_remove),
679 .id_table = wm8974_i2c_id,
680};
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000681#endif
Mark Brown4fcbbb62009-05-23 12:27:03 +0100682
Mark Brown0a1bf552009-05-23 11:18:41 +0100683static int __init wm8974_modinit(void)
684{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000685 int ret = 0;
686#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
687 ret = i2c_add_driver(&wm8974_i2c_driver);
688 if (ret != 0) {
689 printk(KERN_ERR "Failed to register wm8974 I2C driver: %d\n",
690 ret);
691 }
692#endif
693 return ret;
Mark Brown0a1bf552009-05-23 11:18:41 +0100694}
695module_init(wm8974_modinit);
696
697static void __exit wm8974_exit(void)
698{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000699#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
Mark Brown4fcbbb62009-05-23 12:27:03 +0100700 i2c_del_driver(&wm8974_i2c_driver);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000701#endif
Mark Brown0a1bf552009-05-23 11:18:41 +0100702}
703module_exit(wm8974_exit);
704
705MODULE_DESCRIPTION("ASoC WM8974 driver");
706MODULE_AUTHOR("Liam Girdwood");
707MODULE_LICENSE("GPL");