blob: 8e3bfc16def92f2947b63da2aa6505b145bc0ce0 [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>
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>
Mark Brown0a1bf552009-05-23 11:18:41 +010026#include <sound/initval.h>
Mark Browna5f8d2f2009-06-30 19:30:33 +010027#include <sound/tlv.h>
Mark Brown0a1bf552009-05-23 11:18:41 +010028
29#include "wm8974.h"
30
Mark Brown0a1bf552009-05-23 11:18:41 +010031static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
Mark Brown1a55b3f2009-05-23 11:31:40 +010032 0x0000, 0x0000, 0x0000, 0x0000,
33 0x0050, 0x0000, 0x0140, 0x0000,
34 0x0000, 0x0000, 0x0000, 0x00ff,
35 0x0000, 0x0000, 0x0100, 0x00ff,
36 0x0000, 0x0000, 0x012c, 0x002c,
37 0x002c, 0x002c, 0x002c, 0x0000,
38 0x0032, 0x0000, 0x0000, 0x0000,
39 0x0000, 0x0000, 0x0000, 0x0000,
40 0x0038, 0x000b, 0x0032, 0x0000,
41 0x0008, 0x000c, 0x0093, 0x00e9,
42 0x0000, 0x0000, 0x0000, 0x0000,
43 0x0003, 0x0010, 0x0000, 0x0000,
44 0x0000, 0x0002, 0x0000, 0x0000,
45 0x0000, 0x0000, 0x0039, 0x0000,
46 0x0000,
Mark Brown0a1bf552009-05-23 11:18:41 +010047};
48
Mark Browndf1ef7a2009-06-30 19:01:09 +010049#define WM8974_POWER1_BIASEN 0x08
Guennadi Liakhovetski48c03ce2009-12-17 14:51:35 +010050#define WM8974_POWER1_BUFIOEN 0x04
Mark Browndf1ef7a2009-06-30 19:01:09 +010051
Mark Brown4fcbbb62009-05-23 12:27:03 +010052struct wm8974_priv {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +000053 enum snd_soc_control_type control_type;
Mark Brown4fcbbb62009-05-23 12:27:03 +010054};
55
Mark Brown1e97f502009-08-15 12:15:10 +010056#define wm8974_reset(c) snd_soc_write(c, WM8974_RESET, 0)
Mark Brown0a1bf552009-05-23 11:18:41 +010057
58static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
59static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
60static const char *wm8974_eqmode[] = {"Capture", "Playback" };
61static const char *wm8974_bw[] = {"Narrow", "Wide" };
62static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
63static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
64static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
65static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
66static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
67static const char *wm8974_alc[] = {"ALC", "Limiter" };
68
69static const struct soc_enum wm8974_enum[] = {
70 SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
71 SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
72 SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp),
73 SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode),
74
75 SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1),
76 SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw),
77 SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2),
78 SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw),
79
80 SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3),
81 SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw),
82 SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4),
83 SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw),
84
85 SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5),
86 SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc),
87};
88
Mark Brown8a123ee2009-06-30 21:10:34 +010089static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" };
90
91static const struct soc_enum wm8974_auxmode =
92 SOC_ENUM_SINGLE(WM8974_INPUT, 3, 2, wm8974_auxmode_text);
93
Mark Browna5f8d2f2009-06-30 19:30:33 +010094static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
95static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
96static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
97static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
98
Mark Brown0a1bf552009-05-23 11:18:41 +010099static const struct snd_kcontrol_new wm8974_snd_controls[] = {
100
101SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
102
103SOC_ENUM("DAC Companding", wm8974_enum[1]),
104SOC_ENUM("ADC Companding", wm8974_enum[0]),
105
106SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
107SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
108
Mark Browna5f8d2f2009-06-30 19:30:33 +0100109SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100110
111SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
112SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
javier Martin25cbf462009-07-21 11:15:06 +0200113SOC_SINGLE("ADC Inversion Switch", WM8974_ADC, 0, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100114
Mark Browna5f8d2f2009-06-30 19:30:33 +0100115SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL, 0, 255, 0, digital_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100116
117SOC_ENUM("Equaliser Function", wm8974_enum[3]),
118SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100119SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100120
121SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
122SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100123SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100124
125SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
126SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100127SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100128
129SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
130SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100131SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100132
133SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
134SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100135SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100136
137SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0),
138SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0),
139SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0),
140
141SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0),
142SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0),
143
144SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0),
145SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0),
146SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0),
147
148SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0),
149SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0),
150SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0),
151
152SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
153SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0),
154SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0),
155
156SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0),
157SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0),
158
159SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100160SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0, inpga_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100161
162SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0),
163SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1),
Mark Brown8a123ee2009-06-30 21:10:34 +0100164SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0, spk_tlv),
165
166SOC_ENUM("Aux Mode", wm8974_auxmode),
Mark Brown0a1bf552009-05-23 11:18:41 +0100167
168SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100169SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1),
Guennadi Liakhovetskib2c3e922010-01-29 15:31:06 +0100170
171/* DAC / ADC oversampling */
172SOC_SINGLE("DAC 128x Oversampling Switch", WM8974_DAC, 8, 1, 0),
173SOC_SINGLE("ADC 128x Oversampling Switch", WM8974_ADC, 8, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100174};
175
Mark Brown0a1bf552009-05-23 11:18:41 +0100176/* Speaker Output Mixer */
177static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
178SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
179SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
Mark Brown759512f2010-04-23 17:39:23 +0100180SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100181};
182
183/* Mono Output Mixer */
184static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
185SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
186SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100187SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0),
188};
189
190/* Boost mixer */
191static const struct snd_kcontrol_new wm8974_boost_mixer[] = {
192SOC_DAPM_SINGLE("Aux Switch", WM8974_INPPGA, 6, 1, 0),
193};
194
195/* Input PGA */
196static const struct snd_kcontrol_new wm8974_inpga[] = {
197SOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0),
198SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0),
199SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100200};
201
202/* AUX Input boost vol */
203static const struct snd_kcontrol_new wm8974_aux_boost_controls =
204SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
205
206/* Mic Input boost vol */
207static const struct snd_kcontrol_new wm8974_mic_boost_controls =
208SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
209
Mark Brown0a1bf552009-05-23 11:18:41 +0100210static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
211SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
212 &wm8974_speaker_mixer_controls[0],
213 ARRAY_SIZE(wm8974_speaker_mixer_controls)),
214SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
215 &wm8974_mono_mixer_controls[0],
216 ARRAY_SIZE(wm8974_mono_mixer_controls)),
217SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100218SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100219SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
220SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
221SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
222SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100223
Mark Brown8a123ee2009-06-30 21:10:34 +0100224SND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga,
225 ARRAY_SIZE(wm8974_inpga)),
226SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0,
227 wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)),
Mark Brown0a1bf552009-05-23 11:18:41 +0100228
229SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
230
231SND_SOC_DAPM_INPUT("MICN"),
232SND_SOC_DAPM_INPUT("MICP"),
233SND_SOC_DAPM_INPUT("AUX"),
234SND_SOC_DAPM_OUTPUT("MONOOUT"),
235SND_SOC_DAPM_OUTPUT("SPKOUTP"),
236SND_SOC_DAPM_OUTPUT("SPKOUTN"),
237};
238
239static const struct snd_soc_dapm_route audio_map[] = {
240 /* Mono output mixer */
241 {"Mono Mixer", "PCM Playback Switch", "DAC"},
242 {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
243 {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
244
245 /* Speaker output mixer */
246 {"Speaker Mixer", "PCM Playback Switch", "DAC"},
247 {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
248 {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
249
250 /* Outputs */
251 {"Mono Out", NULL, "Mono Mixer"},
252 {"MONOOUT", NULL, "Mono Out"},
253 {"SpkN Out", NULL, "Speaker Mixer"},
254 {"SpkP Out", NULL, "Speaker Mixer"},
255 {"SPKOUTN", NULL, "SpkN Out"},
256 {"SPKOUTP", NULL, "SpkP Out"},
257
258 /* Boost Mixer */
Mark Brown8a123ee2009-06-30 21:10:34 +0100259 {"ADC", NULL, "Boost Mixer"},
260 {"Boost Mixer", "Aux Switch", "Aux Input"},
261 {"Boost Mixer", NULL, "Input PGA"},
262 {"Boost Mixer", NULL, "MICP"},
263
264 /* Input PGA */
265 {"Input PGA", "Aux Switch", "Aux Input"},
266 {"Input PGA", "MicN Switch", "MICN"},
267 {"Input PGA", "MicP Switch", "MICP"},
Mark Brown0a1bf552009-05-23 11:18:41 +0100268
269 /* Inputs */
Mark Brown8a123ee2009-06-30 21:10:34 +0100270 {"Aux Input", NULL, "AUX"},
Mark Brown0a1bf552009-05-23 11:18:41 +0100271};
272
273static int wm8974_add_widgets(struct snd_soc_codec *codec)
274{
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200275 struct snd_soc_dapm_context *dapm = &codec->dapm;
Mark Brown0a1bf552009-05-23 11:18:41 +0100276
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200277 snd_soc_dapm_new_controls(dapm, wm8974_dapm_widgets,
278 ARRAY_SIZE(wm8974_dapm_widgets));
279 snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
Mark Brown0a1bf552009-05-23 11:18:41 +0100280
Mark Brown0a1bf552009-05-23 11:18:41 +0100281 return 0;
282}
283
284struct pll_ {
Mark Brownc36b2fc2009-09-30 14:31:38 +0100285 unsigned int pre_div:1;
Mark Brown0a1bf552009-05-23 11:18:41 +0100286 unsigned int n:4;
287 unsigned int k;
288};
289
Mark Brown91d0c3e2009-06-30 19:02:32 +0100290/* The size in bits of the pll divide multiplied by 10
291 * to allow rounding later */
292#define FIXED_PLL_SIZE ((1 << 24) * 10)
293
Mark Brownc36b2fc2009-09-30 14:31:38 +0100294static void pll_factors(struct pll_ *pll_div,
295 unsigned int target, unsigned int source)
Mark Brown91d0c3e2009-06-30 19:02:32 +0100296{
297 unsigned long long Kpart;
298 unsigned int K, Ndiv, Nmod;
299
Mark Brownc36b2fc2009-09-30 14:31:38 +0100300 /* There is a fixed divide by 4 in the output path */
301 target *= 4;
302
Mark Brown91d0c3e2009-06-30 19:02:32 +0100303 Ndiv = target / source;
304 if (Ndiv < 6) {
Mark Brownc36b2fc2009-09-30 14:31:38 +0100305 source /= 2;
306 pll_div->pre_div = 1;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100307 Ndiv = target / source;
308 } else
Mark Brownc36b2fc2009-09-30 14:31:38 +0100309 pll_div->pre_div = 0;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100310
311 if ((Ndiv < 6) || (Ndiv > 12))
312 printk(KERN_WARNING
Mark Brown8b83a192009-06-30 19:37:02 +0100313 "WM8974 N value %u outwith recommended range!\n",
Mark Brown91d0c3e2009-06-30 19:02:32 +0100314 Ndiv);
315
Mark Brownc36b2fc2009-09-30 14:31:38 +0100316 pll_div->n = Ndiv;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100317 Nmod = target % source;
318 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
319
320 do_div(Kpart, source);
321
322 K = Kpart & 0xFFFFFFFF;
323
324 /* Check if we need to round */
325 if ((K % 10) >= 5)
326 K += 5;
327
328 /* Move down to proper range now rounding is done */
329 K /= 10;
330
Mark Brownc36b2fc2009-09-30 14:31:38 +0100331 pll_div->k = K;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100332}
Mark Brown0a1bf552009-05-23 11:18:41 +0100333
Mark Brown85488032009-09-05 18:52:16 +0100334static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
335 int source, unsigned int freq_in, unsigned int freq_out)
Mark Brown0a1bf552009-05-23 11:18:41 +0100336{
337 struct snd_soc_codec *codec = codec_dai->codec;
Mark Brownc36b2fc2009-09-30 14:31:38 +0100338 struct pll_ pll_div;
Mark Brown0a1bf552009-05-23 11:18:41 +0100339 u16 reg;
340
Mark Brown1a55b3f2009-05-23 11:31:40 +0100341 if (freq_in == 0 || freq_out == 0) {
Mark Brown91d0c3e2009-06-30 19:02:32 +0100342 /* Clock CODEC directly from MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100343 reg = snd_soc_read(codec, WM8974_CLOCK);
344 snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100345
346 /* Turn off PLL */
Mark Brown1e97f502009-08-15 12:15:10 +0100347 reg = snd_soc_read(codec, WM8974_POWER1);
348 snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
Mark Brown0a1bf552009-05-23 11:18:41 +0100349 return 0;
350 }
351
Mark Brownc36b2fc2009-09-30 14:31:38 +0100352 pll_factors(&pll_div, freq_out, freq_in);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100353
Mark Brown1e97f502009-08-15 12:15:10 +0100354 snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
355 snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
356 snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
357 snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
358 reg = snd_soc_read(codec, WM8974_POWER1);
359 snd_soc_write(codec, WM8974_POWER1, reg | 0x020);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100360
361 /* Run CODEC from PLL instead of MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100362 reg = snd_soc_read(codec, WM8974_CLOCK);
363 snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100364
365 return 0;
Mark Brown0a1bf552009-05-23 11:18:41 +0100366}
367
368/*
369 * Configure WM8974 clock dividers.
370 */
371static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
372 int div_id, int div)
373{
374 struct snd_soc_codec *codec = codec_dai->codec;
375 u16 reg;
376
377 switch (div_id) {
378 case WM8974_OPCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100379 reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
380 snd_soc_write(codec, WM8974_GPIO, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100381 break;
382 case WM8974_MCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100383 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
384 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100385 break;
Mark Brown0a1bf552009-05-23 11:18:41 +0100386 case WM8974_BCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100387 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
388 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100389 break;
390 default:
391 return -EINVAL;
392 }
393
394 return 0;
395}
396
397static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
398 unsigned int fmt)
399{
400 struct snd_soc_codec *codec = codec_dai->codec;
401 u16 iface = 0;
Mark Brown1e97f502009-08-15 12:15:10 +0100402 u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
Mark Brown0a1bf552009-05-23 11:18:41 +0100403
404 /* set master/slave audio interface */
405 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
406 case SND_SOC_DAIFMT_CBM_CFM:
407 clk |= 0x0001;
408 break;
409 case SND_SOC_DAIFMT_CBS_CFS:
410 break;
411 default:
412 return -EINVAL;
413 }
414
415 /* interface format */
416 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
417 case SND_SOC_DAIFMT_I2S:
418 iface |= 0x0010;
419 break;
420 case SND_SOC_DAIFMT_RIGHT_J:
421 break;
422 case SND_SOC_DAIFMT_LEFT_J:
423 iface |= 0x0008;
424 break;
425 case SND_SOC_DAIFMT_DSP_A:
426 iface |= 0x00018;
427 break;
428 default:
429 return -EINVAL;
430 }
431
432 /* clock inversion */
433 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
434 case SND_SOC_DAIFMT_NB_NF:
435 break;
436 case SND_SOC_DAIFMT_IB_IF:
437 iface |= 0x0180;
438 break;
439 case SND_SOC_DAIFMT_IB_NF:
440 iface |= 0x0100;
441 break;
442 case SND_SOC_DAIFMT_NB_IF:
443 iface |= 0x0080;
444 break;
445 default:
446 return -EINVAL;
447 }
448
Mark Brown1e97f502009-08-15 12:15:10 +0100449 snd_soc_write(codec, WM8974_IFACE, iface);
450 snd_soc_write(codec, WM8974_CLOCK, clk);
Mark Brown0a1bf552009-05-23 11:18:41 +0100451 return 0;
452}
453
454static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
455 struct snd_pcm_hw_params *params,
456 struct snd_soc_dai *dai)
457{
458 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100459 u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
460 u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
Mark Brown0a1bf552009-05-23 11:18:41 +0100461
462 /* bit size */
463 switch (params_format(params)) {
464 case SNDRV_PCM_FORMAT_S16_LE:
465 break;
466 case SNDRV_PCM_FORMAT_S20_3LE:
467 iface |= 0x0020;
468 break;
469 case SNDRV_PCM_FORMAT_S24_LE:
470 iface |= 0x0040;
471 break;
472 case SNDRV_PCM_FORMAT_S32_LE:
473 iface |= 0x0060;
474 break;
475 }
476
477 /* filter coefficient */
478 switch (params_rate(params)) {
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100479 case 8000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100480 adn |= 0x5 << 1;
481 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100482 case 11025:
Mark Brown0a1bf552009-05-23 11:18:41 +0100483 adn |= 0x4 << 1;
484 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100485 case 16000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100486 adn |= 0x3 << 1;
487 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100488 case 22050:
Mark Brown0a1bf552009-05-23 11:18:41 +0100489 adn |= 0x2 << 1;
490 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100491 case 32000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100492 adn |= 0x1 << 1;
493 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100494 case 44100:
495 case 48000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100496 break;
497 }
498
Mark Brown1e97f502009-08-15 12:15:10 +0100499 snd_soc_write(codec, WM8974_IFACE, iface);
500 snd_soc_write(codec, WM8974_ADD, adn);
Mark Brown0a1bf552009-05-23 11:18:41 +0100501 return 0;
502}
503
504static int wm8974_mute(struct snd_soc_dai *dai, int mute)
505{
506 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100507 u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
Mark Brown0a1bf552009-05-23 11:18:41 +0100508
Mark Brown1a55b3f2009-05-23 11:31:40 +0100509 if (mute)
Mark Brown1e97f502009-08-15 12:15:10 +0100510 snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
Mark Brown0a1bf552009-05-23 11:18:41 +0100511 else
Mark Brown1e97f502009-08-15 12:15:10 +0100512 snd_soc_write(codec, WM8974_DAC, mute_reg);
Mark Brown0a1bf552009-05-23 11:18:41 +0100513 return 0;
514}
515
516/* liam need to make this lower power with dapm */
517static int wm8974_set_bias_level(struct snd_soc_codec *codec,
518 enum snd_soc_bias_level level)
519{
Mark Brown1e97f502009-08-15 12:15:10 +0100520 u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100521
Mark Brown0a1bf552009-05-23 11:18:41 +0100522 switch (level) {
523 case SND_SOC_BIAS_ON:
Mark Brown0a1bf552009-05-23 11:18:41 +0100524 case SND_SOC_BIAS_PREPARE:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100525 power1 |= 0x1; /* VMID 50k */
Mark Brown1e97f502009-08-15 12:15:10 +0100526 snd_soc_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100527 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100528
Mark Brown0a1bf552009-05-23 11:18:41 +0100529 case SND_SOC_BIAS_STANDBY:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100530 power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
531
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200532 if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
Mark Browndf1ef7a2009-06-30 19:01:09 +0100533 /* Initial cap charge at VMID 5k */
Mark Brown1e97f502009-08-15 12:15:10 +0100534 snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
Mark Browndf1ef7a2009-06-30 19:01:09 +0100535 mdelay(100);
536 }
537
538 power1 |= 0x2; /* VMID 500k */
Mark Brown1e97f502009-08-15 12:15:10 +0100539 snd_soc_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100540 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100541
Mark Brown0a1bf552009-05-23 11:18:41 +0100542 case SND_SOC_BIAS_OFF:
Mark Brown1e97f502009-08-15 12:15:10 +0100543 snd_soc_write(codec, WM8974_POWER1, 0);
544 snd_soc_write(codec, WM8974_POWER2, 0);
545 snd_soc_write(codec, WM8974_POWER3, 0);
Mark Brown0a1bf552009-05-23 11:18:41 +0100546 break;
547 }
Mark Browndf1ef7a2009-06-30 19:01:09 +0100548
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200549 codec->dapm.bias_level = level;
Mark Brown0a1bf552009-05-23 11:18:41 +0100550 return 0;
551}
552
Mark Brown1a55b3f2009-05-23 11:31:40 +0100553#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
Mark Brown0a1bf552009-05-23 11:18:41 +0100554
555#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
556 SNDRV_PCM_FMTBIT_S24_LE)
557
558static struct snd_soc_dai_ops wm8974_ops = {
559 .hw_params = wm8974_pcm_hw_params,
560 .digital_mute = wm8974_mute,
561 .set_fmt = wm8974_set_dai_fmt,
562 .set_clkdiv = wm8974_set_dai_clkdiv,
563 .set_pll = wm8974_set_dai_pll,
564};
565
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000566static struct snd_soc_dai_driver wm8974_dai = {
567 .name = "wm8974-hifi",
Mark Brown0a1bf552009-05-23 11:18:41 +0100568 .playback = {
569 .stream_name = "Playback",
570 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100571 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100572 .rates = WM8974_RATES,
573 .formats = WM8974_FORMATS,},
574 .capture = {
575 .stream_name = "Capture",
576 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100577 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100578 .rates = WM8974_RATES,
579 .formats = WM8974_FORMATS,},
580 .ops = &wm8974_ops,
Mark Browncb11d392009-06-30 19:36:39 +0100581 .symmetric_rates = 1,
Mark Brown0a1bf552009-05-23 11:18:41 +0100582};
Mark Brown0a1bf552009-05-23 11:18:41 +0100583
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000584static int wm8974_suspend(struct snd_soc_codec *codec, pm_message_t state)
Mark Brown0a1bf552009-05-23 11:18:41 +0100585{
Mark Brown0a1bf552009-05-23 11:18:41 +0100586 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
587 return 0;
588}
589
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000590static int wm8974_resume(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100591{
Mark Brown0a1bf552009-05-23 11:18:41 +0100592 int i;
593 u8 data[2];
594 u16 *cache = codec->reg_cache;
595
596 /* Sync reg_cache with the hardware */
597 for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
598 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
599 data[1] = cache[i] & 0x00ff;
600 codec->hw_write(codec->control_data, data, 2);
601 }
602 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
Mark Brown29e189c2010-05-07 20:30:00 +0100603
Mark Brown0a1bf552009-05-23 11:18:41 +0100604 return 0;
605}
606
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000607static int wm8974_probe(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100608{
Mark Brown0a1bf552009-05-23 11:18:41 +0100609 int ret = 0;
610
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000611 ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100612 if (ret < 0) {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000613 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
614 return ret;
Mark Brown0a1bf552009-05-23 11:18:41 +0100615 }
616
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000617 ret = wm8974_reset(codec);
618 if (ret < 0) {
619 dev_err(codec->dev, "Failed to issue reset\n");
620 return ret;
621 }
622
623 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100624 snd_soc_add_controls(codec, wm8974_snd_controls,
625 ARRAY_SIZE(wm8974_snd_controls));
Mark Brown0a1bf552009-05-23 11:18:41 +0100626 wm8974_add_widgets(codec);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100627
Mark Brown0a1bf552009-05-23 11:18:41 +0100628 return ret;
Mark Brown0a1bf552009-05-23 11:18:41 +0100629}
630
631/* power down chip */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000632static int wm8974_remove(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100633{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000634 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
Mark Brown0a1bf552009-05-23 11:18:41 +0100635 return 0;
636}
637
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000638static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
Mark Brown0a1bf552009-05-23 11:18:41 +0100639 .probe = wm8974_probe,
640 .remove = wm8974_remove,
641 .suspend = wm8974_suspend,
642 .resume = wm8974_resume,
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000643 .set_bias_level = wm8974_set_bias_level,
644 .reg_cache_size = ARRAY_SIZE(wm8974_reg),
645 .reg_word_size = sizeof(u16),
646 .reg_cache_default = wm8974_reg,
Mark Brown0a1bf552009-05-23 11:18:41 +0100647};
Mark Brown0a1bf552009-05-23 11:18:41 +0100648
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000649#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
Mark Brown4fcbbb62009-05-23 12:27:03 +0100650static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
651 const struct i2c_device_id *id)
652{
653 struct wm8974_priv *wm8974;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000654 int ret;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100655
656 wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
657 if (wm8974 == NULL)
658 return -ENOMEM;
659
Mark Brown4fcbbb62009-05-23 12:27:03 +0100660 i2c_set_clientdata(i2c, wm8974);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100661
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000662 ret = snd_soc_register_codec(&i2c->dev,
663 &soc_codec_dev_wm8974, &wm8974_dai, 1);
664 if (ret < 0)
665 kfree(wm8974);
666 return ret;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100667}
668
669static __devexit int wm8974_i2c_remove(struct i2c_client *client)
670{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000671 snd_soc_unregister_codec(&client->dev);
672 kfree(i2c_get_clientdata(client));
Mark Brown4fcbbb62009-05-23 12:27:03 +0100673 return 0;
674}
675
676static const struct i2c_device_id wm8974_i2c_id[] = {
677 { "wm8974", 0 },
678 { }
679};
680MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
681
682static struct i2c_driver wm8974_i2c_driver = {
683 .driver = {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000684 .name = "wm8974-codec",
Mark Brown4fcbbb62009-05-23 12:27:03 +0100685 .owner = THIS_MODULE,
686 },
687 .probe = wm8974_i2c_probe,
688 .remove = __devexit_p(wm8974_i2c_remove),
689 .id_table = wm8974_i2c_id,
690};
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000691#endif
Mark Brown4fcbbb62009-05-23 12:27:03 +0100692
Mark Brown0a1bf552009-05-23 11:18:41 +0100693static int __init wm8974_modinit(void)
694{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000695 int ret = 0;
696#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
697 ret = i2c_add_driver(&wm8974_i2c_driver);
698 if (ret != 0) {
699 printk(KERN_ERR "Failed to register wm8974 I2C driver: %d\n",
700 ret);
701 }
702#endif
703 return ret;
Mark Brown0a1bf552009-05-23 11:18:41 +0100704}
705module_init(wm8974_modinit);
706
707static void __exit wm8974_exit(void)
708{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000709#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
Mark Brown4fcbbb62009-05-23 12:27:03 +0100710 i2c_del_driver(&wm8974_i2c_driver);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000711#endif
Mark Brown0a1bf552009-05-23 11:18:41 +0100712}
713module_exit(wm8974_exit);
714
715MODULE_DESCRIPTION("ASoC WM8974 driver");
716MODULE_AUTHOR("Liam Girdwood");
717MODULE_LICENSE("GPL");