blob: 69708c4cc004c9de2abb1f6bdd781c0634307d93 [file] [log] [blame]
Mark Brown0a1bf552009-05-23 11:18:41 +01001/*
2 * wm8974.c -- WM8974 ALSA Soc Audio driver
3 *
Mark Brown8b83a192009-06-30 19:37:02 +01004 * Copyright 2006-2009 Wolfson Microelectronics PLC.
Mark Brown0a1bf552009-05-23 11:18:41 +01005 *
Mark Brown4fcbbb62009-05-23 12:27:03 +01006 * Author: Liam Girdwood <linux@wolfsonmicro.com>
Mark Brown0a1bf552009-05-23 11:18:41 +01007 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/module.h>
14#include <linux/moduleparam.h>
Mark Brown0a1bf552009-05-23 11:18:41 +010015#include <linux/kernel.h>
16#include <linux/init.h>
17#include <linux/delay.h>
18#include <linux/pm.h>
19#include <linux/i2c.h>
20#include <linux/platform_device.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090021#include <linux/slab.h>
Mark Brown0a1bf552009-05-23 11:18:41 +010022#include <sound/core.h>
23#include <sound/pcm.h>
24#include <sound/pcm_params.h>
25#include <sound/soc.h>
26#include <sound/soc-dapm.h>
27#include <sound/initval.h>
Mark Browna5f8d2f2009-06-30 19:30:33 +010028#include <sound/tlv.h>
Mark Brown0a1bf552009-05-23 11:18:41 +010029
30#include "wm8974.h"
31
Mark Brown0a1bf552009-05-23 11:18:41 +010032static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
Mark Brown1a55b3f2009-05-23 11:31:40 +010033 0x0000, 0x0000, 0x0000, 0x0000,
34 0x0050, 0x0000, 0x0140, 0x0000,
35 0x0000, 0x0000, 0x0000, 0x00ff,
36 0x0000, 0x0000, 0x0100, 0x00ff,
37 0x0000, 0x0000, 0x012c, 0x002c,
38 0x002c, 0x002c, 0x002c, 0x0000,
39 0x0032, 0x0000, 0x0000, 0x0000,
40 0x0000, 0x0000, 0x0000, 0x0000,
41 0x0038, 0x000b, 0x0032, 0x0000,
42 0x0008, 0x000c, 0x0093, 0x00e9,
43 0x0000, 0x0000, 0x0000, 0x0000,
44 0x0003, 0x0010, 0x0000, 0x0000,
45 0x0000, 0x0002, 0x0000, 0x0000,
46 0x0000, 0x0000, 0x0039, 0x0000,
47 0x0000,
Mark Brown0a1bf552009-05-23 11:18:41 +010048};
49
Mark Browndf1ef7a2009-06-30 19:01:09 +010050#define WM8974_POWER1_BIASEN 0x08
Guennadi Liakhovetski48c03ce2009-12-17 14:51:35 +010051#define WM8974_POWER1_BUFIOEN 0x04
Mark Browndf1ef7a2009-06-30 19:01:09 +010052
Mark Brown4fcbbb62009-05-23 12:27:03 +010053struct wm8974_priv {
54 struct snd_soc_codec codec;
55 u16 reg_cache[WM8974_CACHEREGNUM];
56};
57
58static struct snd_soc_codec *wm8974_codec;
59
Mark Brown1e97f502009-08-15 12:15:10 +010060#define wm8974_reset(c) snd_soc_write(c, WM8974_RESET, 0)
Mark Brown0a1bf552009-05-23 11:18:41 +010061
62static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
63static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
64static const char *wm8974_eqmode[] = {"Capture", "Playback" };
65static const char *wm8974_bw[] = {"Narrow", "Wide" };
66static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
67static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
68static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
69static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
70static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
71static const char *wm8974_alc[] = {"ALC", "Limiter" };
72
73static const struct soc_enum wm8974_enum[] = {
74 SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
75 SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
76 SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp),
77 SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode),
78
79 SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1),
80 SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw),
81 SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2),
82 SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw),
83
84 SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3),
85 SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw),
86 SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4),
87 SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw),
88
89 SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5),
90 SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc),
91};
92
Mark Brown8a123ee2009-06-30 21:10:34 +010093static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" };
94
95static const struct soc_enum wm8974_auxmode =
96 SOC_ENUM_SINGLE(WM8974_INPUT, 3, 2, wm8974_auxmode_text);
97
Mark Browna5f8d2f2009-06-30 19:30:33 +010098static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
99static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
100static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
101static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
102
Mark Brown0a1bf552009-05-23 11:18:41 +0100103static const struct snd_kcontrol_new wm8974_snd_controls[] = {
104
105SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
106
107SOC_ENUM("DAC Companding", wm8974_enum[1]),
108SOC_ENUM("ADC Companding", wm8974_enum[0]),
109
110SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
111SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
112
Mark Browna5f8d2f2009-06-30 19:30:33 +0100113SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100114
115SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
116SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
javier Martin25cbf462009-07-21 11:15:06 +0200117SOC_SINGLE("ADC Inversion Switch", WM8974_ADC, 0, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100118
Mark Browna5f8d2f2009-06-30 19:30:33 +0100119SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL, 0, 255, 0, digital_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100120
121SOC_ENUM("Equaliser Function", wm8974_enum[3]),
122SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100123SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100124
125SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
126SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100127SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100128
129SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
130SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100131SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100132
133SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
134SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100135SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100136
137SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
138SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100139SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100140
141SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0),
142SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0),
143SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0),
144
145SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0),
146SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0),
147
148SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0),
149SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0),
150SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0),
151
152SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0),
153SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0),
154SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0),
155
156SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
157SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0),
158SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0),
159
160SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0),
161SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0),
162
163SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100164SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0, inpga_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100165
166SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0),
167SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1),
Mark Brown8a123ee2009-06-30 21:10:34 +0100168SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0, spk_tlv),
169
170SOC_ENUM("Aux Mode", wm8974_auxmode),
Mark Brown0a1bf552009-05-23 11:18:41 +0100171
172SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100173SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1),
Guennadi Liakhovetskib2c3e922010-01-29 15:31:06 +0100174
175/* DAC / ADC oversampling */
176SOC_SINGLE("DAC 128x Oversampling Switch", WM8974_DAC, 8, 1, 0),
177SOC_SINGLE("ADC 128x Oversampling Switch", WM8974_ADC, 8, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100178};
179
Mark Brown0a1bf552009-05-23 11:18:41 +0100180/* Speaker Output Mixer */
181static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
182SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
183SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
184SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1),
185};
186
187/* Mono Output Mixer */
188static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
189SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
190SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100191SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0),
192};
193
194/* Boost mixer */
195static const struct snd_kcontrol_new wm8974_boost_mixer[] = {
196SOC_DAPM_SINGLE("Aux Switch", WM8974_INPPGA, 6, 1, 0),
197};
198
199/* Input PGA */
200static const struct snd_kcontrol_new wm8974_inpga[] = {
201SOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0),
202SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0),
203SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100204};
205
206/* AUX Input boost vol */
207static const struct snd_kcontrol_new wm8974_aux_boost_controls =
208SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
209
210/* Mic Input boost vol */
211static const struct snd_kcontrol_new wm8974_mic_boost_controls =
212SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
213
Mark Brown0a1bf552009-05-23 11:18:41 +0100214static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
215SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
216 &wm8974_speaker_mixer_controls[0],
217 ARRAY_SIZE(wm8974_speaker_mixer_controls)),
218SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
219 &wm8974_mono_mixer_controls[0],
220 ARRAY_SIZE(wm8974_mono_mixer_controls)),
221SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100222SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100223SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
224SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
225SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
226SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100227
Mark Brown8a123ee2009-06-30 21:10:34 +0100228SND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga,
229 ARRAY_SIZE(wm8974_inpga)),
230SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0,
231 wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)),
Mark Brown0a1bf552009-05-23 11:18:41 +0100232
233SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
234
235SND_SOC_DAPM_INPUT("MICN"),
236SND_SOC_DAPM_INPUT("MICP"),
237SND_SOC_DAPM_INPUT("AUX"),
238SND_SOC_DAPM_OUTPUT("MONOOUT"),
239SND_SOC_DAPM_OUTPUT("SPKOUTP"),
240SND_SOC_DAPM_OUTPUT("SPKOUTN"),
241};
242
243static const struct snd_soc_dapm_route audio_map[] = {
244 /* Mono output mixer */
245 {"Mono Mixer", "PCM Playback Switch", "DAC"},
246 {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
247 {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
248
249 /* Speaker output mixer */
250 {"Speaker Mixer", "PCM Playback Switch", "DAC"},
251 {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
252 {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
253
254 /* Outputs */
255 {"Mono Out", NULL, "Mono Mixer"},
256 {"MONOOUT", NULL, "Mono Out"},
257 {"SpkN Out", NULL, "Speaker Mixer"},
258 {"SpkP Out", NULL, "Speaker Mixer"},
259 {"SPKOUTN", NULL, "SpkN Out"},
260 {"SPKOUTP", NULL, "SpkP Out"},
261
262 /* Boost Mixer */
Mark Brown8a123ee2009-06-30 21:10:34 +0100263 {"ADC", NULL, "Boost Mixer"},
264 {"Boost Mixer", "Aux Switch", "Aux Input"},
265 {"Boost Mixer", NULL, "Input PGA"},
266 {"Boost Mixer", NULL, "MICP"},
267
268 /* Input PGA */
269 {"Input PGA", "Aux Switch", "Aux Input"},
270 {"Input PGA", "MicN Switch", "MICN"},
271 {"Input PGA", "MicP Switch", "MICP"},
Mark Brown0a1bf552009-05-23 11:18:41 +0100272
273 /* Inputs */
Mark Brown8a123ee2009-06-30 21:10:34 +0100274 {"Aux Input", NULL, "AUX"},
Mark Brown0a1bf552009-05-23 11:18:41 +0100275};
276
277static int wm8974_add_widgets(struct snd_soc_codec *codec)
278{
279 snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets,
280 ARRAY_SIZE(wm8974_dapm_widgets));
281
282 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
283
Mark Brown0a1bf552009-05-23 11:18:41 +0100284 return 0;
285}
286
287struct pll_ {
Mark Brownc36b2fc2009-09-30 14:31:38 +0100288 unsigned int pre_div:1;
Mark Brown0a1bf552009-05-23 11:18:41 +0100289 unsigned int n:4;
290 unsigned int k;
291};
292
Mark Brown91d0c3e2009-06-30 19:02:32 +0100293/* The size in bits of the pll divide multiplied by 10
294 * to allow rounding later */
295#define FIXED_PLL_SIZE ((1 << 24) * 10)
296
Mark Brownc36b2fc2009-09-30 14:31:38 +0100297static void pll_factors(struct pll_ *pll_div,
298 unsigned int target, unsigned int source)
Mark Brown91d0c3e2009-06-30 19:02:32 +0100299{
300 unsigned long long Kpart;
301 unsigned int K, Ndiv, Nmod;
302
Mark Brownc36b2fc2009-09-30 14:31:38 +0100303 /* There is a fixed divide by 4 in the output path */
304 target *= 4;
305
Mark Brown91d0c3e2009-06-30 19:02:32 +0100306 Ndiv = target / source;
307 if (Ndiv < 6) {
Mark Brownc36b2fc2009-09-30 14:31:38 +0100308 source /= 2;
309 pll_div->pre_div = 1;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100310 Ndiv = target / source;
311 } else
Mark Brownc36b2fc2009-09-30 14:31:38 +0100312 pll_div->pre_div = 0;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100313
314 if ((Ndiv < 6) || (Ndiv > 12))
315 printk(KERN_WARNING
Mark Brown8b83a192009-06-30 19:37:02 +0100316 "WM8974 N value %u outwith recommended range!\n",
Mark Brown91d0c3e2009-06-30 19:02:32 +0100317 Ndiv);
318
Mark Brownc36b2fc2009-09-30 14:31:38 +0100319 pll_div->n = Ndiv;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100320 Nmod = target % source;
321 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
322
323 do_div(Kpart, source);
324
325 K = Kpart & 0xFFFFFFFF;
326
327 /* Check if we need to round */
328 if ((K % 10) >= 5)
329 K += 5;
330
331 /* Move down to proper range now rounding is done */
332 K /= 10;
333
Mark Brownc36b2fc2009-09-30 14:31:38 +0100334 pll_div->k = K;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100335}
Mark Brown0a1bf552009-05-23 11:18:41 +0100336
Mark Brown85488032009-09-05 18:52:16 +0100337static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
338 int source, unsigned int freq_in, unsigned int freq_out)
Mark Brown0a1bf552009-05-23 11:18:41 +0100339{
340 struct snd_soc_codec *codec = codec_dai->codec;
Mark Brownc36b2fc2009-09-30 14:31:38 +0100341 struct pll_ pll_div;
Mark Brown0a1bf552009-05-23 11:18:41 +0100342 u16 reg;
343
Mark Brown1a55b3f2009-05-23 11:31:40 +0100344 if (freq_in == 0 || freq_out == 0) {
Mark Brown91d0c3e2009-06-30 19:02:32 +0100345 /* Clock CODEC directly from MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100346 reg = snd_soc_read(codec, WM8974_CLOCK);
347 snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100348
349 /* Turn off PLL */
Mark Brown1e97f502009-08-15 12:15:10 +0100350 reg = snd_soc_read(codec, WM8974_POWER1);
351 snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
Mark Brown0a1bf552009-05-23 11:18:41 +0100352 return 0;
353 }
354
Mark Brownc36b2fc2009-09-30 14:31:38 +0100355 pll_factors(&pll_div, freq_out, freq_in);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100356
Mark Brown1e97f502009-08-15 12:15:10 +0100357 snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
358 snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
359 snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
360 snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
361 reg = snd_soc_read(codec, WM8974_POWER1);
362 snd_soc_write(codec, WM8974_POWER1, reg | 0x020);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100363
364 /* Run CODEC from PLL instead of MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100365 reg = snd_soc_read(codec, WM8974_CLOCK);
366 snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100367
368 return 0;
Mark Brown0a1bf552009-05-23 11:18:41 +0100369}
370
371/*
372 * Configure WM8974 clock dividers.
373 */
374static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
375 int div_id, int div)
376{
377 struct snd_soc_codec *codec = codec_dai->codec;
378 u16 reg;
379
380 switch (div_id) {
381 case WM8974_OPCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100382 reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
383 snd_soc_write(codec, WM8974_GPIO, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100384 break;
385 case WM8974_MCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100386 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
387 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100388 break;
Mark Brown0a1bf552009-05-23 11:18:41 +0100389 case WM8974_BCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100390 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
391 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100392 break;
393 default:
394 return -EINVAL;
395 }
396
397 return 0;
398}
399
400static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
401 unsigned int fmt)
402{
403 struct snd_soc_codec *codec = codec_dai->codec;
404 u16 iface = 0;
Mark Brown1e97f502009-08-15 12:15:10 +0100405 u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
Mark Brown0a1bf552009-05-23 11:18:41 +0100406
407 /* set master/slave audio interface */
408 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
409 case SND_SOC_DAIFMT_CBM_CFM:
410 clk |= 0x0001;
411 break;
412 case SND_SOC_DAIFMT_CBS_CFS:
413 break;
414 default:
415 return -EINVAL;
416 }
417
418 /* interface format */
419 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
420 case SND_SOC_DAIFMT_I2S:
421 iface |= 0x0010;
422 break;
423 case SND_SOC_DAIFMT_RIGHT_J:
424 break;
425 case SND_SOC_DAIFMT_LEFT_J:
426 iface |= 0x0008;
427 break;
428 case SND_SOC_DAIFMT_DSP_A:
429 iface |= 0x00018;
430 break;
431 default:
432 return -EINVAL;
433 }
434
435 /* clock inversion */
436 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
437 case SND_SOC_DAIFMT_NB_NF:
438 break;
439 case SND_SOC_DAIFMT_IB_IF:
440 iface |= 0x0180;
441 break;
442 case SND_SOC_DAIFMT_IB_NF:
443 iface |= 0x0100;
444 break;
445 case SND_SOC_DAIFMT_NB_IF:
446 iface |= 0x0080;
447 break;
448 default:
449 return -EINVAL;
450 }
451
Mark Brown1e97f502009-08-15 12:15:10 +0100452 snd_soc_write(codec, WM8974_IFACE, iface);
453 snd_soc_write(codec, WM8974_CLOCK, clk);
Mark Brown0a1bf552009-05-23 11:18:41 +0100454 return 0;
455}
456
457static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
458 struct snd_pcm_hw_params *params,
459 struct snd_soc_dai *dai)
460{
461 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100462 u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
463 u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
Mark Brown0a1bf552009-05-23 11:18:41 +0100464
465 /* bit size */
466 switch (params_format(params)) {
467 case SNDRV_PCM_FORMAT_S16_LE:
468 break;
469 case SNDRV_PCM_FORMAT_S20_3LE:
470 iface |= 0x0020;
471 break;
472 case SNDRV_PCM_FORMAT_S24_LE:
473 iface |= 0x0040;
474 break;
475 case SNDRV_PCM_FORMAT_S32_LE:
476 iface |= 0x0060;
477 break;
478 }
479
480 /* filter coefficient */
481 switch (params_rate(params)) {
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100482 case 8000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100483 adn |= 0x5 << 1;
484 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100485 case 11025:
Mark Brown0a1bf552009-05-23 11:18:41 +0100486 adn |= 0x4 << 1;
487 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100488 case 16000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100489 adn |= 0x3 << 1;
490 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100491 case 22050:
Mark Brown0a1bf552009-05-23 11:18:41 +0100492 adn |= 0x2 << 1;
493 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100494 case 32000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100495 adn |= 0x1 << 1;
496 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100497 case 44100:
498 case 48000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100499 break;
500 }
501
Mark Brown1e97f502009-08-15 12:15:10 +0100502 snd_soc_write(codec, WM8974_IFACE, iface);
503 snd_soc_write(codec, WM8974_ADD, adn);
Mark Brown0a1bf552009-05-23 11:18:41 +0100504 return 0;
505}
506
507static int wm8974_mute(struct snd_soc_dai *dai, int mute)
508{
509 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100510 u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
Mark Brown0a1bf552009-05-23 11:18:41 +0100511
Mark Brown1a55b3f2009-05-23 11:31:40 +0100512 if (mute)
Mark Brown1e97f502009-08-15 12:15:10 +0100513 snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
Mark Brown0a1bf552009-05-23 11:18:41 +0100514 else
Mark Brown1e97f502009-08-15 12:15:10 +0100515 snd_soc_write(codec, WM8974_DAC, mute_reg);
Mark Brown0a1bf552009-05-23 11:18:41 +0100516 return 0;
517}
518
519/* liam need to make this lower power with dapm */
520static int wm8974_set_bias_level(struct snd_soc_codec *codec,
521 enum snd_soc_bias_level level)
522{
Mark Brown1e97f502009-08-15 12:15:10 +0100523 u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100524
Mark Brown0a1bf552009-05-23 11:18:41 +0100525 switch (level) {
526 case SND_SOC_BIAS_ON:
Mark Brown0a1bf552009-05-23 11:18:41 +0100527 case SND_SOC_BIAS_PREPARE:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100528 power1 |= 0x1; /* VMID 50k */
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_STANDBY:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100533 power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
534
535 if (codec->bias_level == SND_SOC_BIAS_OFF) {
536 /* Initial cap charge at VMID 5k */
Mark Brown1e97f502009-08-15 12:15:10 +0100537 snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
Mark Browndf1ef7a2009-06-30 19:01:09 +0100538 mdelay(100);
539 }
540
541 power1 |= 0x2; /* VMID 500k */
Mark Brown1e97f502009-08-15 12:15:10 +0100542 snd_soc_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100543 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100544
Mark Brown0a1bf552009-05-23 11:18:41 +0100545 case SND_SOC_BIAS_OFF:
Mark Brown1e97f502009-08-15 12:15:10 +0100546 snd_soc_write(codec, WM8974_POWER1, 0);
547 snd_soc_write(codec, WM8974_POWER2, 0);
548 snd_soc_write(codec, WM8974_POWER3, 0);
Mark Brown0a1bf552009-05-23 11:18:41 +0100549 break;
550 }
Mark Browndf1ef7a2009-06-30 19:01:09 +0100551
Mark Brown0a1bf552009-05-23 11:18:41 +0100552 codec->bias_level = level;
553 return 0;
554}
555
Mark Brown1a55b3f2009-05-23 11:31:40 +0100556#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
Mark Brown0a1bf552009-05-23 11:18:41 +0100557
558#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
559 SNDRV_PCM_FMTBIT_S24_LE)
560
561static struct snd_soc_dai_ops wm8974_ops = {
562 .hw_params = wm8974_pcm_hw_params,
563 .digital_mute = wm8974_mute,
564 .set_fmt = wm8974_set_dai_fmt,
565 .set_clkdiv = wm8974_set_dai_clkdiv,
566 .set_pll = wm8974_set_dai_pll,
567};
568
569struct snd_soc_dai wm8974_dai = {
570 .name = "WM8974 HiFi",
571 .playback = {
572 .stream_name = "Playback",
573 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100574 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100575 .rates = WM8974_RATES,
576 .formats = WM8974_FORMATS,},
577 .capture = {
578 .stream_name = "Capture",
579 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100580 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100581 .rates = WM8974_RATES,
582 .formats = WM8974_FORMATS,},
583 .ops = &wm8974_ops,
Mark Browncb11d392009-06-30 19:36:39 +0100584 .symmetric_rates = 1,
Mark Brown0a1bf552009-05-23 11:18:41 +0100585};
586EXPORT_SYMBOL_GPL(wm8974_dai);
587
588static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
589{
590 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
591 struct snd_soc_codec *codec = socdev->card->codec;
592
593 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
594 return 0;
595}
596
597static int wm8974_resume(struct platform_device *pdev)
598{
599 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
600 struct snd_soc_codec *codec = socdev->card->codec;
601 int i;
602 u8 data[2];
603 u16 *cache = codec->reg_cache;
604
605 /* Sync reg_cache with the hardware */
606 for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
607 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
608 data[1] = cache[i] & 0x00ff;
609 codec->hw_write(codec->control_data, data, 2);
610 }
611 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
612 wm8974_set_bias_level(codec, codec->suspend_bias_level);
613 return 0;
614}
615
Mark Brown4fcbbb62009-05-23 12:27:03 +0100616static int wm8974_probe(struct platform_device *pdev)
Mark Brown0a1bf552009-05-23 11:18:41 +0100617{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100618 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
619 struct snd_soc_codec *codec;
Mark Brown0a1bf552009-05-23 11:18:41 +0100620 int ret = 0;
621
Mark Brown4fcbbb62009-05-23 12:27:03 +0100622 if (wm8974_codec == NULL) {
623 dev_err(&pdev->dev, "Codec device not registered\n");
624 return -ENODEV;
625 }
Mark Brown0a1bf552009-05-23 11:18:41 +0100626
Mark Brown4fcbbb62009-05-23 12:27:03 +0100627 socdev->card->codec = wm8974_codec;
628 codec = wm8974_codec;
Mark Brown0a1bf552009-05-23 11:18:41 +0100629
630 /* register pcms */
631 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100632 if (ret < 0) {
Mark Brown4fcbbb62009-05-23 12:27:03 +0100633 dev_err(codec->dev, "failed to create pcms: %d\n", ret);
Mark Brown0a1bf552009-05-23 11:18:41 +0100634 goto pcm_err;
635 }
636
Mark Brown4fcbbb62009-05-23 12:27:03 +0100637 snd_soc_add_controls(codec, wm8974_snd_controls,
638 ARRAY_SIZE(wm8974_snd_controls));
Mark Brown0a1bf552009-05-23 11:18:41 +0100639 wm8974_add_widgets(codec);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100640
Mark Brown0a1bf552009-05-23 11:18:41 +0100641 return ret;
642
Mark Brown0a1bf552009-05-23 11:18:41 +0100643pcm_err:
Mark Brown0a1bf552009-05-23 11:18:41 +0100644 return ret;
645}
646
647/* power down chip */
648static int wm8974_remove(struct platform_device *pdev)
649{
650 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
Mark Brown0a1bf552009-05-23 11:18:41 +0100651
652 snd_soc_free_pcms(socdev);
653 snd_soc_dapm_free(socdev);
Mark Brown0a1bf552009-05-23 11:18:41 +0100654
655 return 0;
656}
657
658struct snd_soc_codec_device soc_codec_dev_wm8974 = {
659 .probe = wm8974_probe,
660 .remove = wm8974_remove,
661 .suspend = wm8974_suspend,
662 .resume = wm8974_resume,
663};
664EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
665
Mark Brown4fcbbb62009-05-23 12:27:03 +0100666static __devinit int wm8974_register(struct wm8974_priv *wm8974)
667{
668 int ret;
669 struct snd_soc_codec *codec = &wm8974->codec;
670
671 if (wm8974_codec) {
672 dev_err(codec->dev, "Another WM8974 is registered\n");
673 return -EINVAL;
674 }
675
676 mutex_init(&codec->mutex);
677 INIT_LIST_HEAD(&codec->dapm_widgets);
678 INIT_LIST_HEAD(&codec->dapm_paths);
679
680 codec->private_data = wm8974;
681 codec->name = "WM8974";
682 codec->owner = THIS_MODULE;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100683 codec->bias_level = SND_SOC_BIAS_OFF;
684 codec->set_bias_level = wm8974_set_bias_level;
685 codec->dai = &wm8974_dai;
686 codec->num_dai = 1;
687 codec->reg_cache_size = WM8974_CACHEREGNUM;
688 codec->reg_cache = &wm8974->reg_cache;
689
Mark Brown1e97f502009-08-15 12:15:10 +0100690 ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
691 if (ret < 0) {
692 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
693 goto err;
694 }
695
Mark Brown4fcbbb62009-05-23 12:27:03 +0100696 memcpy(codec->reg_cache, wm8974_reg, sizeof(wm8974_reg));
697
698 ret = wm8974_reset(codec);
699 if (ret < 0) {
700 dev_err(codec->dev, "Failed to issue reset\n");
Mark Brown1e97f502009-08-15 12:15:10 +0100701 goto err;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100702 }
703
704 wm8974_dai.dev = codec->dev;
705
706 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
707
708 wm8974_codec = codec;
709
710 ret = snd_soc_register_codec(codec);
711 if (ret != 0) {
712 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
Mark Brown1e97f502009-08-15 12:15:10 +0100713 goto err;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100714 }
715
716 ret = snd_soc_register_dai(&wm8974_dai);
717 if (ret != 0) {
718 dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
Mark Brown1e97f502009-08-15 12:15:10 +0100719 goto err_codec;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100720 }
721
722 return 0;
Mark Brown1e97f502009-08-15 12:15:10 +0100723
724err_codec:
725 snd_soc_unregister_codec(codec);
726err:
727 kfree(wm8974);
728 return ret;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100729}
730
731static __devexit void wm8974_unregister(struct wm8974_priv *wm8974)
732{
733 wm8974_set_bias_level(&wm8974->codec, SND_SOC_BIAS_OFF);
734 snd_soc_unregister_dai(&wm8974_dai);
735 snd_soc_unregister_codec(&wm8974->codec);
736 kfree(wm8974);
737 wm8974_codec = NULL;
738}
739
740static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
741 const struct i2c_device_id *id)
742{
743 struct wm8974_priv *wm8974;
744 struct snd_soc_codec *codec;
745
746 wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
747 if (wm8974 == NULL)
748 return -ENOMEM;
749
750 codec = &wm8974->codec;
751 codec->hw_write = (hw_write_t)i2c_master_send;
752
753 i2c_set_clientdata(i2c, wm8974);
754 codec->control_data = i2c;
755
756 codec->dev = &i2c->dev;
757
758 return wm8974_register(wm8974);
759}
760
761static __devexit int wm8974_i2c_remove(struct i2c_client *client)
762{
763 struct wm8974_priv *wm8974 = i2c_get_clientdata(client);
764 wm8974_unregister(wm8974);
765 return 0;
766}
767
768static const struct i2c_device_id wm8974_i2c_id[] = {
769 { "wm8974", 0 },
770 { }
771};
772MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
773
774static struct i2c_driver wm8974_i2c_driver = {
775 .driver = {
Mark Brown8b83a192009-06-30 19:37:02 +0100776 .name = "WM8974",
Mark Brown4fcbbb62009-05-23 12:27:03 +0100777 .owner = THIS_MODULE,
778 },
779 .probe = wm8974_i2c_probe,
780 .remove = __devexit_p(wm8974_i2c_remove),
781 .id_table = wm8974_i2c_id,
782};
783
Mark Brown0a1bf552009-05-23 11:18:41 +0100784static int __init wm8974_modinit(void)
785{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100786 return i2c_add_driver(&wm8974_i2c_driver);
Mark Brown0a1bf552009-05-23 11:18:41 +0100787}
788module_init(wm8974_modinit);
789
790static void __exit wm8974_exit(void)
791{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100792 i2c_del_driver(&wm8974_i2c_driver);
Mark Brown0a1bf552009-05-23 11:18:41 +0100793}
794module_exit(wm8974_exit);
795
796MODULE_DESCRIPTION("ASoC WM8974 driver");
797MODULE_AUTHOR("Liam Girdwood");
798MODULE_LICENSE("GPL");