blob: 1e7a87a561685acb6a9f5665b7769a1fa01d39aa [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
Mark Browna2bd6912011-12-29 11:10:27 +0000238static const struct snd_soc_dapm_route wm8974_dapm_routes[] = {
Mark Brown0a1bf552009-05-23 11:18:41 +0100239 /* 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
Mark Brown0a1bf552009-05-23 11:18:41 +0100272struct pll_ {
Mark Brownc36b2fc2009-09-30 14:31:38 +0100273 unsigned int pre_div:1;
Mark Brown0a1bf552009-05-23 11:18:41 +0100274 unsigned int n:4;
275 unsigned int k;
276};
277
Mark Brown91d0c3e2009-06-30 19:02:32 +0100278/* The size in bits of the pll divide multiplied by 10
279 * to allow rounding later */
280#define FIXED_PLL_SIZE ((1 << 24) * 10)
281
Mark Brownc36b2fc2009-09-30 14:31:38 +0100282static void pll_factors(struct pll_ *pll_div,
283 unsigned int target, unsigned int source)
Mark Brown91d0c3e2009-06-30 19:02:32 +0100284{
285 unsigned long long Kpart;
286 unsigned int K, Ndiv, Nmod;
287
Mark Brownc36b2fc2009-09-30 14:31:38 +0100288 /* There is a fixed divide by 4 in the output path */
289 target *= 4;
290
Mark Brown91d0c3e2009-06-30 19:02:32 +0100291 Ndiv = target / source;
292 if (Ndiv < 6) {
Mark Brownc36b2fc2009-09-30 14:31:38 +0100293 source /= 2;
294 pll_div->pre_div = 1;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100295 Ndiv = target / source;
296 } else
Mark Brownc36b2fc2009-09-30 14:31:38 +0100297 pll_div->pre_div = 0;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100298
299 if ((Ndiv < 6) || (Ndiv > 12))
300 printk(KERN_WARNING
Mark Brown8b83a192009-06-30 19:37:02 +0100301 "WM8974 N value %u outwith recommended range!\n",
Mark Brown91d0c3e2009-06-30 19:02:32 +0100302 Ndiv);
303
Mark Brownc36b2fc2009-09-30 14:31:38 +0100304 pll_div->n = Ndiv;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100305 Nmod = target % source;
306 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
307
308 do_div(Kpart, source);
309
310 K = Kpart & 0xFFFFFFFF;
311
312 /* Check if we need to round */
313 if ((K % 10) >= 5)
314 K += 5;
315
316 /* Move down to proper range now rounding is done */
317 K /= 10;
318
Mark Brownc36b2fc2009-09-30 14:31:38 +0100319 pll_div->k = K;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100320}
Mark Brown0a1bf552009-05-23 11:18:41 +0100321
Mark Brown85488032009-09-05 18:52:16 +0100322static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
323 int source, unsigned int freq_in, unsigned int freq_out)
Mark Brown0a1bf552009-05-23 11:18:41 +0100324{
325 struct snd_soc_codec *codec = codec_dai->codec;
Mark Brownc36b2fc2009-09-30 14:31:38 +0100326 struct pll_ pll_div;
Mark Brown0a1bf552009-05-23 11:18:41 +0100327 u16 reg;
328
Mark Brown1a55b3f2009-05-23 11:31:40 +0100329 if (freq_in == 0 || freq_out == 0) {
Mark Brown91d0c3e2009-06-30 19:02:32 +0100330 /* Clock CODEC directly from MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100331 reg = snd_soc_read(codec, WM8974_CLOCK);
332 snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100333
334 /* Turn off PLL */
Mark Brown1e97f502009-08-15 12:15:10 +0100335 reg = snd_soc_read(codec, WM8974_POWER1);
336 snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
Mark Brown0a1bf552009-05-23 11:18:41 +0100337 return 0;
338 }
339
Mark Brownc36b2fc2009-09-30 14:31:38 +0100340 pll_factors(&pll_div, freq_out, freq_in);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100341
Mark Brown1e97f502009-08-15 12:15:10 +0100342 snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
343 snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
344 snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
345 snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
346 reg = snd_soc_read(codec, WM8974_POWER1);
347 snd_soc_write(codec, WM8974_POWER1, reg | 0x020);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100348
349 /* Run CODEC from PLL instead of MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100350 reg = snd_soc_read(codec, WM8974_CLOCK);
351 snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100352
353 return 0;
Mark Brown0a1bf552009-05-23 11:18:41 +0100354}
355
356/*
357 * Configure WM8974 clock dividers.
358 */
359static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
360 int div_id, int div)
361{
362 struct snd_soc_codec *codec = codec_dai->codec;
363 u16 reg;
364
365 switch (div_id) {
366 case WM8974_OPCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100367 reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
368 snd_soc_write(codec, WM8974_GPIO, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100369 break;
370 case WM8974_MCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100371 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
372 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100373 break;
Mark Brown0a1bf552009-05-23 11:18:41 +0100374 case WM8974_BCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100375 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
376 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100377 break;
378 default:
379 return -EINVAL;
380 }
381
382 return 0;
383}
384
385static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
386 unsigned int fmt)
387{
388 struct snd_soc_codec *codec = codec_dai->codec;
389 u16 iface = 0;
Mark Brown1e97f502009-08-15 12:15:10 +0100390 u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
Mark Brown0a1bf552009-05-23 11:18:41 +0100391
392 /* set master/slave audio interface */
393 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
394 case SND_SOC_DAIFMT_CBM_CFM:
395 clk |= 0x0001;
396 break;
397 case SND_SOC_DAIFMT_CBS_CFS:
398 break;
399 default:
400 return -EINVAL;
401 }
402
403 /* interface format */
404 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
405 case SND_SOC_DAIFMT_I2S:
406 iface |= 0x0010;
407 break;
408 case SND_SOC_DAIFMT_RIGHT_J:
409 break;
410 case SND_SOC_DAIFMT_LEFT_J:
411 iface |= 0x0008;
412 break;
413 case SND_SOC_DAIFMT_DSP_A:
414 iface |= 0x00018;
415 break;
416 default:
417 return -EINVAL;
418 }
419
420 /* clock inversion */
421 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
422 case SND_SOC_DAIFMT_NB_NF:
423 break;
424 case SND_SOC_DAIFMT_IB_IF:
425 iface |= 0x0180;
426 break;
427 case SND_SOC_DAIFMT_IB_NF:
428 iface |= 0x0100;
429 break;
430 case SND_SOC_DAIFMT_NB_IF:
431 iface |= 0x0080;
432 break;
433 default:
434 return -EINVAL;
435 }
436
Mark Brown1e97f502009-08-15 12:15:10 +0100437 snd_soc_write(codec, WM8974_IFACE, iface);
438 snd_soc_write(codec, WM8974_CLOCK, clk);
Mark Brown0a1bf552009-05-23 11:18:41 +0100439 return 0;
440}
441
442static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
443 struct snd_pcm_hw_params *params,
444 struct snd_soc_dai *dai)
445{
446 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100447 u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
448 u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
Mark Brown0a1bf552009-05-23 11:18:41 +0100449
450 /* bit size */
451 switch (params_format(params)) {
452 case SNDRV_PCM_FORMAT_S16_LE:
453 break;
454 case SNDRV_PCM_FORMAT_S20_3LE:
455 iface |= 0x0020;
456 break;
457 case SNDRV_PCM_FORMAT_S24_LE:
458 iface |= 0x0040;
459 break;
460 case SNDRV_PCM_FORMAT_S32_LE:
461 iface |= 0x0060;
462 break;
463 }
464
465 /* filter coefficient */
466 switch (params_rate(params)) {
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100467 case 8000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100468 adn |= 0x5 << 1;
469 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100470 case 11025:
Mark Brown0a1bf552009-05-23 11:18:41 +0100471 adn |= 0x4 << 1;
472 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100473 case 16000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100474 adn |= 0x3 << 1;
475 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100476 case 22050:
Mark Brown0a1bf552009-05-23 11:18:41 +0100477 adn |= 0x2 << 1;
478 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100479 case 32000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100480 adn |= 0x1 << 1;
481 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100482 case 44100:
483 case 48000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100484 break;
485 }
486
Mark Brown1e97f502009-08-15 12:15:10 +0100487 snd_soc_write(codec, WM8974_IFACE, iface);
488 snd_soc_write(codec, WM8974_ADD, adn);
Mark Brown0a1bf552009-05-23 11:18:41 +0100489 return 0;
490}
491
492static int wm8974_mute(struct snd_soc_dai *dai, int mute)
493{
494 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100495 u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
Mark Brown0a1bf552009-05-23 11:18:41 +0100496
Mark Brown1a55b3f2009-05-23 11:31:40 +0100497 if (mute)
Mark Brown1e97f502009-08-15 12:15:10 +0100498 snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
Mark Brown0a1bf552009-05-23 11:18:41 +0100499 else
Mark Brown1e97f502009-08-15 12:15:10 +0100500 snd_soc_write(codec, WM8974_DAC, mute_reg);
Mark Brown0a1bf552009-05-23 11:18:41 +0100501 return 0;
502}
503
504/* liam need to make this lower power with dapm */
505static int wm8974_set_bias_level(struct snd_soc_codec *codec,
506 enum snd_soc_bias_level level)
507{
Mark Brown1e97f502009-08-15 12:15:10 +0100508 u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100509
Mark Brown0a1bf552009-05-23 11:18:41 +0100510 switch (level) {
511 case SND_SOC_BIAS_ON:
Mark Brown0a1bf552009-05-23 11:18:41 +0100512 case SND_SOC_BIAS_PREPARE:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100513 power1 |= 0x1; /* VMID 50k */
Mark Brown1e97f502009-08-15 12:15:10 +0100514 snd_soc_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100515 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100516
Mark Brown0a1bf552009-05-23 11:18:41 +0100517 case SND_SOC_BIAS_STANDBY:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100518 power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
519
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200520 if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
Axel Lin0bad3d82011-10-07 21:52:42 +0800521 snd_soc_cache_sync(codec);
522
Mark Browndf1ef7a2009-06-30 19:01:09 +0100523 /* Initial cap charge at VMID 5k */
Mark Brown1e97f502009-08-15 12:15:10 +0100524 snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
Mark Browndf1ef7a2009-06-30 19:01:09 +0100525 mdelay(100);
526 }
527
528 power1 |= 0x2; /* VMID 500k */
Mark Brown1e97f502009-08-15 12:15:10 +0100529 snd_soc_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100530 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100531
Mark Brown0a1bf552009-05-23 11:18:41 +0100532 case SND_SOC_BIAS_OFF:
Mark Brown1e97f502009-08-15 12:15:10 +0100533 snd_soc_write(codec, WM8974_POWER1, 0);
534 snd_soc_write(codec, WM8974_POWER2, 0);
535 snd_soc_write(codec, WM8974_POWER3, 0);
Mark Brown0a1bf552009-05-23 11:18:41 +0100536 break;
537 }
Mark Browndf1ef7a2009-06-30 19:01:09 +0100538
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200539 codec->dapm.bias_level = level;
Mark Brown0a1bf552009-05-23 11:18:41 +0100540 return 0;
541}
542
Mark Brown1a55b3f2009-05-23 11:31:40 +0100543#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
Mark Brown0a1bf552009-05-23 11:18:41 +0100544
545#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
546 SNDRV_PCM_FMTBIT_S24_LE)
547
Lars-Peter Clausen85e76522011-11-23 11:40:40 +0100548static const struct snd_soc_dai_ops wm8974_ops = {
Mark Brown0a1bf552009-05-23 11:18:41 +0100549 .hw_params = wm8974_pcm_hw_params,
550 .digital_mute = wm8974_mute,
551 .set_fmt = wm8974_set_dai_fmt,
552 .set_clkdiv = wm8974_set_dai_clkdiv,
553 .set_pll = wm8974_set_dai_pll,
554};
555
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000556static struct snd_soc_dai_driver wm8974_dai = {
557 .name = "wm8974-hifi",
Mark Brown0a1bf552009-05-23 11:18:41 +0100558 .playback = {
559 .stream_name = "Playback",
560 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100561 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100562 .rates = WM8974_RATES,
563 .formats = WM8974_FORMATS,},
564 .capture = {
565 .stream_name = "Capture",
566 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100567 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100568 .rates = WM8974_RATES,
569 .formats = WM8974_FORMATS,},
570 .ops = &wm8974_ops,
Mark Browncb11d392009-06-30 19:36:39 +0100571 .symmetric_rates = 1,
Mark Brown0a1bf552009-05-23 11:18:41 +0100572};
Mark Brown0a1bf552009-05-23 11:18:41 +0100573
Lars-Peter Clausen84b315e2011-12-02 10:18:28 +0100574static int wm8974_suspend(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100575{
Mark Brown0a1bf552009-05-23 11:18:41 +0100576 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
577 return 0;
578}
579
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000580static int wm8974_resume(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100581{
Mark Brown0a1bf552009-05-23 11:18:41 +0100582 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
Mark Brown0a1bf552009-05-23 11:18:41 +0100583 return 0;
584}
585
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000586static int wm8974_probe(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100587{
Mark Brown0a1bf552009-05-23 11:18:41 +0100588 int ret = 0;
589
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000590 ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100591 if (ret < 0) {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000592 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
593 return ret;
Mark Brown0a1bf552009-05-23 11:18:41 +0100594 }
595
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000596 ret = wm8974_reset(codec);
597 if (ret < 0) {
598 dev_err(codec->dev, "Failed to issue reset\n");
599 return ret;
600 }
601
602 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100603
Mark Brown0a1bf552009-05-23 11:18:41 +0100604 return ret;
Mark Brown0a1bf552009-05-23 11:18:41 +0100605}
606
607/* power down chip */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000608static int wm8974_remove(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100609{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000610 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
Mark Brown0a1bf552009-05-23 11:18:41 +0100611 return 0;
612}
613
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000614static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
Mark Brown0a1bf552009-05-23 11:18:41 +0100615 .probe = wm8974_probe,
616 .remove = wm8974_remove,
617 .suspend = wm8974_suspend,
618 .resume = wm8974_resume,
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000619 .set_bias_level = wm8974_set_bias_level,
620 .reg_cache_size = ARRAY_SIZE(wm8974_reg),
621 .reg_word_size = sizeof(u16),
622 .reg_cache_default = wm8974_reg,
Mark Browna2bd6912011-12-29 11:10:27 +0000623
624 .controls = wm8974_snd_controls,
625 .num_controls = ARRAY_SIZE(wm8974_snd_controls),
626 .dapm_widgets = wm8974_dapm_widgets,
627 .num_dapm_widgets = ARRAY_SIZE(wm8974_dapm_widgets),
628 .dapm_routes = wm8974_dapm_routes,
629 .num_dapm_routes = ARRAY_SIZE(wm8974_dapm_routes),
Mark Brown0a1bf552009-05-23 11:18:41 +0100630};
Mark Brown0a1bf552009-05-23 11:18:41 +0100631
Mark Brown4fcbbb62009-05-23 12:27:03 +0100632static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
633 const struct i2c_device_id *id)
634{
635 struct wm8974_priv *wm8974;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000636 int ret;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100637
638 wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
639 if (wm8974 == NULL)
640 return -ENOMEM;
641
Mark Brown4fcbbb62009-05-23 12:27:03 +0100642 i2c_set_clientdata(i2c, wm8974);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100643
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000644 ret = snd_soc_register_codec(&i2c->dev,
645 &soc_codec_dev_wm8974, &wm8974_dai, 1);
646 if (ret < 0)
647 kfree(wm8974);
648 return ret;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100649}
650
651static __devexit int wm8974_i2c_remove(struct i2c_client *client)
652{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000653 snd_soc_unregister_codec(&client->dev);
654 kfree(i2c_get_clientdata(client));
Mark Brown4fcbbb62009-05-23 12:27:03 +0100655 return 0;
656}
657
658static const struct i2c_device_id wm8974_i2c_id[] = {
659 { "wm8974", 0 },
660 { }
661};
662MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
663
664static struct i2c_driver wm8974_i2c_driver = {
665 .driver = {
Mark Brown091edcc2011-12-02 22:08:49 +0000666 .name = "wm8974",
Mark Brown4fcbbb62009-05-23 12:27:03 +0100667 .owner = THIS_MODULE,
668 },
669 .probe = wm8974_i2c_probe,
670 .remove = __devexit_p(wm8974_i2c_remove),
671 .id_table = wm8974_i2c_id,
672};
673
Mark Brown0a1bf552009-05-23 11:18:41 +0100674static int __init wm8974_modinit(void)
675{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000676 int ret = 0;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000677 ret = i2c_add_driver(&wm8974_i2c_driver);
678 if (ret != 0) {
679 printk(KERN_ERR "Failed to register wm8974 I2C driver: %d\n",
680 ret);
681 }
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000682 return ret;
Mark Brown0a1bf552009-05-23 11:18:41 +0100683}
684module_init(wm8974_modinit);
685
686static void __exit wm8974_exit(void)
687{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100688 i2c_del_driver(&wm8974_i2c_driver);
Mark Brown0a1bf552009-05-23 11:18:41 +0100689}
690module_exit(wm8974_exit);
691
692MODULE_DESCRIPTION("ASoC WM8974 driver");
693MODULE_AUTHOR("Liam Girdwood");
694MODULE_LICENSE("GPL");