blob: a2d01d10a5dd8866747b335a027ed3049c9c71d3 [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 Brown1e97f502009-08-15 12:15:10 +010051#define wm8974_reset(c) snd_soc_write(c, WM8974_RESET, 0)
Mark Brown0a1bf552009-05-23 11:18:41 +010052
53static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
54static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
55static const char *wm8974_eqmode[] = {"Capture", "Playback" };
56static const char *wm8974_bw[] = {"Narrow", "Wide" };
57static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
58static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
59static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
60static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
61static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
62static const char *wm8974_alc[] = {"ALC", "Limiter" };
63
64static const struct soc_enum wm8974_enum[] = {
65 SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
66 SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
67 SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp),
68 SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode),
69
70 SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1),
71 SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw),
72 SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2),
73 SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw),
74
75 SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3),
76 SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw),
77 SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4),
78 SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw),
79
80 SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5),
81 SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc),
82};
83
Mark Brown8a123ee2009-06-30 21:10:34 +010084static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" };
85
86static const struct soc_enum wm8974_auxmode =
87 SOC_ENUM_SINGLE(WM8974_INPUT, 3, 2, wm8974_auxmode_text);
88
Mark Browna5f8d2f2009-06-30 19:30:33 +010089static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
90static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
91static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
92static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
93
Mark Brown0a1bf552009-05-23 11:18:41 +010094static const struct snd_kcontrol_new wm8974_snd_controls[] = {
95
96SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
97
98SOC_ENUM("DAC Companding", wm8974_enum[1]),
99SOC_ENUM("ADC Companding", wm8974_enum[0]),
100
101SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
102SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
103
Mark Browna5f8d2f2009-06-30 19:30:33 +0100104SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100105
106SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
107SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
javier Martin25cbf462009-07-21 11:15:06 +0200108SOC_SINGLE("ADC Inversion Switch", WM8974_ADC, 0, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100109
Mark Browna5f8d2f2009-06-30 19:30:33 +0100110SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL, 0, 255, 0, digital_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100111
112SOC_ENUM("Equaliser Function", wm8974_enum[3]),
113SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100114SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100115
Masanari Iidac46d5c02012-11-02 23:25:30 +0900116SOC_ENUM("Equaliser EQ2 Bandwidth", wm8974_enum[5]),
Mark Brown0a1bf552009-05-23 11:18:41 +0100117SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100118SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100119
Masanari Iidac46d5c02012-11-02 23:25:30 +0900120SOC_ENUM("Equaliser EQ3 Bandwidth", wm8974_enum[7]),
Mark Brown0a1bf552009-05-23 11:18:41 +0100121SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100122SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100123
Masanari Iidac46d5c02012-11-02 23:25:30 +0900124SOC_ENUM("Equaliser EQ4 Bandwidth", wm8974_enum[9]),
Mark Brown0a1bf552009-05-23 11:18:41 +0100125SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100126SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100127
Masanari Iidaa895d572013-04-09 02:06:50 +0900128SOC_ENUM("Equaliser EQ5 Bandwidth", wm8974_enum[11]),
Mark Brown0a1bf552009-05-23 11:18:41 +0100129SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100130SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100131
132SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0),
133SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0),
134SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0),
135
136SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0),
137SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0),
138
139SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0),
140SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0),
141SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0),
142
143SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0),
144SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0),
145SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0),
146
147SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
148SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0),
149SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0),
150
151SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0),
152SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0),
153
154SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100155SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0, inpga_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100156
157SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0),
158SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1),
Mark Brown8a123ee2009-06-30 21:10:34 +0100159SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0, spk_tlv),
160
161SOC_ENUM("Aux Mode", wm8974_auxmode),
Mark Brown0a1bf552009-05-23 11:18:41 +0100162
163SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100164SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1),
Guennadi Liakhovetskib2c3e922010-01-29 15:31:06 +0100165
166/* DAC / ADC oversampling */
167SOC_SINGLE("DAC 128x Oversampling Switch", WM8974_DAC, 8, 1, 0),
168SOC_SINGLE("ADC 128x Oversampling Switch", WM8974_ADC, 8, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100169};
170
Mark Brown0a1bf552009-05-23 11:18:41 +0100171/* Speaker Output Mixer */
172static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
173SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
174SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
Mark Brown759512f2010-04-23 17:39:23 +0100175SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100176};
177
178/* Mono Output Mixer */
179static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
180SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
181SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100182SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0),
183};
184
185/* Boost mixer */
186static const struct snd_kcontrol_new wm8974_boost_mixer[] = {
187SOC_DAPM_SINGLE("Aux Switch", WM8974_INPPGA, 6, 1, 0),
188};
189
190/* Input PGA */
191static const struct snd_kcontrol_new wm8974_inpga[] = {
192SOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0),
193SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0),
194SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100195};
196
197/* AUX Input boost vol */
198static const struct snd_kcontrol_new wm8974_aux_boost_controls =
199SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
200
201/* Mic Input boost vol */
202static const struct snd_kcontrol_new wm8974_mic_boost_controls =
203SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
204
Mark Brown0a1bf552009-05-23 11:18:41 +0100205static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
206SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
207 &wm8974_speaker_mixer_controls[0],
208 ARRAY_SIZE(wm8974_speaker_mixer_controls)),
209SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
210 &wm8974_mono_mixer_controls[0],
211 ARRAY_SIZE(wm8974_mono_mixer_controls)),
212SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
Mark Brown8a123ee2009-06-30 21:10:34 +0100213SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100214SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
215SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
216SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
217SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100218
Mark Brown8a123ee2009-06-30 21:10:34 +0100219SND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga,
220 ARRAY_SIZE(wm8974_inpga)),
221SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0,
222 wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)),
Mark Brown0a1bf552009-05-23 11:18:41 +0100223
Mark Brown48dd2312011-10-27 09:47:09 +0200224SND_SOC_DAPM_SUPPLY("Mic Bias", WM8974_POWER1, 4, 0, NULL, 0),
Mark Brown0a1bf552009-05-23 11:18:41 +0100225
226SND_SOC_DAPM_INPUT("MICN"),
227SND_SOC_DAPM_INPUT("MICP"),
228SND_SOC_DAPM_INPUT("AUX"),
229SND_SOC_DAPM_OUTPUT("MONOOUT"),
230SND_SOC_DAPM_OUTPUT("SPKOUTP"),
231SND_SOC_DAPM_OUTPUT("SPKOUTN"),
232};
233
Mark Browna2bd6912011-12-29 11:10:27 +0000234static const struct snd_soc_dapm_route wm8974_dapm_routes[] = {
Mark Brown0a1bf552009-05-23 11:18:41 +0100235 /* Mono output mixer */
236 {"Mono Mixer", "PCM Playback Switch", "DAC"},
237 {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
238 {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
239
240 /* Speaker output mixer */
241 {"Speaker Mixer", "PCM Playback Switch", "DAC"},
242 {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
243 {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
244
245 /* Outputs */
246 {"Mono Out", NULL, "Mono Mixer"},
247 {"MONOOUT", NULL, "Mono Out"},
248 {"SpkN Out", NULL, "Speaker Mixer"},
249 {"SpkP Out", NULL, "Speaker Mixer"},
250 {"SPKOUTN", NULL, "SpkN Out"},
251 {"SPKOUTP", NULL, "SpkP Out"},
252
253 /* Boost Mixer */
Mark Brown8a123ee2009-06-30 21:10:34 +0100254 {"ADC", NULL, "Boost Mixer"},
255 {"Boost Mixer", "Aux Switch", "Aux Input"},
256 {"Boost Mixer", NULL, "Input PGA"},
257 {"Boost Mixer", NULL, "MICP"},
258
259 /* Input PGA */
260 {"Input PGA", "Aux Switch", "Aux Input"},
261 {"Input PGA", "MicN Switch", "MICN"},
262 {"Input PGA", "MicP Switch", "MICP"},
Mark Brown0a1bf552009-05-23 11:18:41 +0100263
264 /* Inputs */
Mark Brown8a123ee2009-06-30 21:10:34 +0100265 {"Aux Input", NULL, "AUX"},
Mark Brown0a1bf552009-05-23 11:18:41 +0100266};
267
Mark Brown0a1bf552009-05-23 11:18:41 +0100268struct pll_ {
Mark Brownc36b2fc2009-09-30 14:31:38 +0100269 unsigned int pre_div:1;
Mark Brown0a1bf552009-05-23 11:18:41 +0100270 unsigned int n:4;
271 unsigned int k;
272};
273
Mark Brown91d0c3e2009-06-30 19:02:32 +0100274/* The size in bits of the pll divide multiplied by 10
275 * to allow rounding later */
276#define FIXED_PLL_SIZE ((1 << 24) * 10)
277
Mark Brownc36b2fc2009-09-30 14:31:38 +0100278static void pll_factors(struct pll_ *pll_div,
279 unsigned int target, unsigned int source)
Mark Brown91d0c3e2009-06-30 19:02:32 +0100280{
281 unsigned long long Kpart;
282 unsigned int K, Ndiv, Nmod;
283
Mark Brownc36b2fc2009-09-30 14:31:38 +0100284 /* There is a fixed divide by 4 in the output path */
285 target *= 4;
286
Mark Brown91d0c3e2009-06-30 19:02:32 +0100287 Ndiv = target / source;
288 if (Ndiv < 6) {
Mark Brownc36b2fc2009-09-30 14:31:38 +0100289 source /= 2;
290 pll_div->pre_div = 1;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100291 Ndiv = target / source;
292 } else
Mark Brownc36b2fc2009-09-30 14:31:38 +0100293 pll_div->pre_div = 0;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100294
295 if ((Ndiv < 6) || (Ndiv > 12))
296 printk(KERN_WARNING
Mark Brown8b83a192009-06-30 19:37:02 +0100297 "WM8974 N value %u outwith recommended range!\n",
Mark Brown91d0c3e2009-06-30 19:02:32 +0100298 Ndiv);
299
Mark Brownc36b2fc2009-09-30 14:31:38 +0100300 pll_div->n = Ndiv;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100301 Nmod = target % source;
302 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
303
304 do_div(Kpart, source);
305
306 K = Kpart & 0xFFFFFFFF;
307
308 /* Check if we need to round */
309 if ((K % 10) >= 5)
310 K += 5;
311
312 /* Move down to proper range now rounding is done */
313 K /= 10;
314
Mark Brownc36b2fc2009-09-30 14:31:38 +0100315 pll_div->k = K;
Mark Brown91d0c3e2009-06-30 19:02:32 +0100316}
Mark Brown0a1bf552009-05-23 11:18:41 +0100317
Mark Brown85488032009-09-05 18:52:16 +0100318static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
319 int source, unsigned int freq_in, unsigned int freq_out)
Mark Brown0a1bf552009-05-23 11:18:41 +0100320{
321 struct snd_soc_codec *codec = codec_dai->codec;
Mark Brownc36b2fc2009-09-30 14:31:38 +0100322 struct pll_ pll_div;
Mark Brown0a1bf552009-05-23 11:18:41 +0100323 u16 reg;
324
Mark Brown1a55b3f2009-05-23 11:31:40 +0100325 if (freq_in == 0 || freq_out == 0) {
Mark Brown91d0c3e2009-06-30 19:02:32 +0100326 /* Clock CODEC directly from MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100327 reg = snd_soc_read(codec, WM8974_CLOCK);
328 snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100329
330 /* Turn off PLL */
Mark Brown1e97f502009-08-15 12:15:10 +0100331 reg = snd_soc_read(codec, WM8974_POWER1);
332 snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
Mark Brown0a1bf552009-05-23 11:18:41 +0100333 return 0;
334 }
335
Mark Brownc36b2fc2009-09-30 14:31:38 +0100336 pll_factors(&pll_div, freq_out, freq_in);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100337
Mark Brown1e97f502009-08-15 12:15:10 +0100338 snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
339 snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
340 snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
341 snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
342 reg = snd_soc_read(codec, WM8974_POWER1);
343 snd_soc_write(codec, WM8974_POWER1, reg | 0x020);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100344
345 /* Run CODEC from PLL instead of MCLK */
Mark Brown1e97f502009-08-15 12:15:10 +0100346 reg = snd_soc_read(codec, WM8974_CLOCK);
347 snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
Mark Brown91d0c3e2009-06-30 19:02:32 +0100348
349 return 0;
Mark Brown0a1bf552009-05-23 11:18:41 +0100350}
351
352/*
353 * Configure WM8974 clock dividers.
354 */
355static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
356 int div_id, int div)
357{
358 struct snd_soc_codec *codec = codec_dai->codec;
359 u16 reg;
360
361 switch (div_id) {
362 case WM8974_OPCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100363 reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
364 snd_soc_write(codec, WM8974_GPIO, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100365 break;
366 case WM8974_MCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100367 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
368 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100369 break;
Mark Brown0a1bf552009-05-23 11:18:41 +0100370 case WM8974_BCLKDIV:
Mark Brown1e97f502009-08-15 12:15:10 +0100371 reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
372 snd_soc_write(codec, WM8974_CLOCK, reg | div);
Mark Brown0a1bf552009-05-23 11:18:41 +0100373 break;
374 default:
375 return -EINVAL;
376 }
377
378 return 0;
379}
380
381static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
382 unsigned int fmt)
383{
384 struct snd_soc_codec *codec = codec_dai->codec;
385 u16 iface = 0;
Mark Brown1e97f502009-08-15 12:15:10 +0100386 u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
Mark Brown0a1bf552009-05-23 11:18:41 +0100387
388 /* set master/slave audio interface */
389 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
390 case SND_SOC_DAIFMT_CBM_CFM:
391 clk |= 0x0001;
392 break;
393 case SND_SOC_DAIFMT_CBS_CFS:
394 break;
395 default:
396 return -EINVAL;
397 }
398
399 /* interface format */
400 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
401 case SND_SOC_DAIFMT_I2S:
402 iface |= 0x0010;
403 break;
404 case SND_SOC_DAIFMT_RIGHT_J:
405 break;
406 case SND_SOC_DAIFMT_LEFT_J:
407 iface |= 0x0008;
408 break;
409 case SND_SOC_DAIFMT_DSP_A:
410 iface |= 0x00018;
411 break;
412 default:
413 return -EINVAL;
414 }
415
416 /* clock inversion */
417 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
418 case SND_SOC_DAIFMT_NB_NF:
419 break;
420 case SND_SOC_DAIFMT_IB_IF:
421 iface |= 0x0180;
422 break;
423 case SND_SOC_DAIFMT_IB_NF:
424 iface |= 0x0100;
425 break;
426 case SND_SOC_DAIFMT_NB_IF:
427 iface |= 0x0080;
428 break;
429 default:
430 return -EINVAL;
431 }
432
Mark Brown1e97f502009-08-15 12:15:10 +0100433 snd_soc_write(codec, WM8974_IFACE, iface);
434 snd_soc_write(codec, WM8974_CLOCK, clk);
Mark Brown0a1bf552009-05-23 11:18:41 +0100435 return 0;
436}
437
438static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
439 struct snd_pcm_hw_params *params,
440 struct snd_soc_dai *dai)
441{
442 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100443 u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
444 u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
Mark Brown0a1bf552009-05-23 11:18:41 +0100445
446 /* bit size */
447 switch (params_format(params)) {
448 case SNDRV_PCM_FORMAT_S16_LE:
449 break;
450 case SNDRV_PCM_FORMAT_S20_3LE:
451 iface |= 0x0020;
452 break;
453 case SNDRV_PCM_FORMAT_S24_LE:
454 iface |= 0x0040;
455 break;
456 case SNDRV_PCM_FORMAT_S32_LE:
457 iface |= 0x0060;
458 break;
459 }
460
461 /* filter coefficient */
462 switch (params_rate(params)) {
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100463 case 8000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100464 adn |= 0x5 << 1;
465 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100466 case 11025:
Mark Brown0a1bf552009-05-23 11:18:41 +0100467 adn |= 0x4 << 1;
468 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100469 case 16000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100470 adn |= 0x3 << 1;
471 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100472 case 22050:
Mark Brown0a1bf552009-05-23 11:18:41 +0100473 adn |= 0x2 << 1;
474 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100475 case 32000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100476 adn |= 0x1 << 1;
477 break;
Guennadi Liakhovetskib3172f22009-12-24 01:13:51 +0100478 case 44100:
479 case 48000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100480 break;
481 }
482
Mark Brown1e97f502009-08-15 12:15:10 +0100483 snd_soc_write(codec, WM8974_IFACE, iface);
484 snd_soc_write(codec, WM8974_ADD, adn);
Mark Brown0a1bf552009-05-23 11:18:41 +0100485 return 0;
486}
487
488static int wm8974_mute(struct snd_soc_dai *dai, int mute)
489{
490 struct snd_soc_codec *codec = dai->codec;
Mark Brown1e97f502009-08-15 12:15:10 +0100491 u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
Mark Brown0a1bf552009-05-23 11:18:41 +0100492
Mark Brown1a55b3f2009-05-23 11:31:40 +0100493 if (mute)
Mark Brown1e97f502009-08-15 12:15:10 +0100494 snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
Mark Brown0a1bf552009-05-23 11:18:41 +0100495 else
Mark Brown1e97f502009-08-15 12:15:10 +0100496 snd_soc_write(codec, WM8974_DAC, mute_reg);
Mark Brown0a1bf552009-05-23 11:18:41 +0100497 return 0;
498}
499
500/* liam need to make this lower power with dapm */
501static int wm8974_set_bias_level(struct snd_soc_codec *codec,
502 enum snd_soc_bias_level level)
503{
Mark Brown1e97f502009-08-15 12:15:10 +0100504 u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100505
Mark Brown0a1bf552009-05-23 11:18:41 +0100506 switch (level) {
507 case SND_SOC_BIAS_ON:
Mark Brown0a1bf552009-05-23 11:18:41 +0100508 case SND_SOC_BIAS_PREPARE:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100509 power1 |= 0x1; /* VMID 50k */
Mark Brown1e97f502009-08-15 12:15:10 +0100510 snd_soc_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100511 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100512
Mark Brown0a1bf552009-05-23 11:18:41 +0100513 case SND_SOC_BIAS_STANDBY:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100514 power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
515
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200516 if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
Axel Lin0bad3d82011-10-07 21:52:42 +0800517 snd_soc_cache_sync(codec);
518
Mark Browndf1ef7a2009-06-30 19:01:09 +0100519 /* Initial cap charge at VMID 5k */
Mark Brown1e97f502009-08-15 12:15:10 +0100520 snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
Mark Browndf1ef7a2009-06-30 19:01:09 +0100521 mdelay(100);
522 }
523
524 power1 |= 0x2; /* VMID 500k */
Mark Brown1e97f502009-08-15 12:15:10 +0100525 snd_soc_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100526 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100527
Mark Brown0a1bf552009-05-23 11:18:41 +0100528 case SND_SOC_BIAS_OFF:
Mark Brown1e97f502009-08-15 12:15:10 +0100529 snd_soc_write(codec, WM8974_POWER1, 0);
530 snd_soc_write(codec, WM8974_POWER2, 0);
531 snd_soc_write(codec, WM8974_POWER3, 0);
Mark Brown0a1bf552009-05-23 11:18:41 +0100532 break;
533 }
Mark Browndf1ef7a2009-06-30 19:01:09 +0100534
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200535 codec->dapm.bias_level = level;
Mark Brown0a1bf552009-05-23 11:18:41 +0100536 return 0;
537}
538
Mark Brown1a55b3f2009-05-23 11:31:40 +0100539#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
Mark Brown0a1bf552009-05-23 11:18:41 +0100540
541#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
542 SNDRV_PCM_FMTBIT_S24_LE)
543
Lars-Peter Clausen85e76522011-11-23 11:40:40 +0100544static const struct snd_soc_dai_ops wm8974_ops = {
Mark Brown0a1bf552009-05-23 11:18:41 +0100545 .hw_params = wm8974_pcm_hw_params,
546 .digital_mute = wm8974_mute,
547 .set_fmt = wm8974_set_dai_fmt,
548 .set_clkdiv = wm8974_set_dai_clkdiv,
549 .set_pll = wm8974_set_dai_pll,
550};
551
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000552static struct snd_soc_dai_driver wm8974_dai = {
553 .name = "wm8974-hifi",
Mark Brown0a1bf552009-05-23 11:18:41 +0100554 .playback = {
555 .stream_name = "Playback",
556 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100557 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100558 .rates = WM8974_RATES,
559 .formats = WM8974_FORMATS,},
560 .capture = {
561 .stream_name = "Capture",
562 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100563 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100564 .rates = WM8974_RATES,
565 .formats = WM8974_FORMATS,},
566 .ops = &wm8974_ops,
Mark Browncb11d392009-06-30 19:36:39 +0100567 .symmetric_rates = 1,
Mark Brown0a1bf552009-05-23 11:18:41 +0100568};
Mark Brown0a1bf552009-05-23 11:18:41 +0100569
Lars-Peter Clausen84b315e2011-12-02 10:18:28 +0100570static int wm8974_suspend(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100571{
Mark Brown0a1bf552009-05-23 11:18:41 +0100572 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
573 return 0;
574}
575
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000576static int wm8974_resume(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100577{
Mark Brown0a1bf552009-05-23 11:18:41 +0100578 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
Mark Brown0a1bf552009-05-23 11:18:41 +0100579 return 0;
580}
581
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000582static int wm8974_probe(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100583{
Mark Brown0a1bf552009-05-23 11:18:41 +0100584 int ret = 0;
585
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000586 ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100587 if (ret < 0) {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000588 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
589 return ret;
Mark Brown0a1bf552009-05-23 11:18:41 +0100590 }
591
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000592 ret = wm8974_reset(codec);
593 if (ret < 0) {
594 dev_err(codec->dev, "Failed to issue reset\n");
595 return ret;
596 }
597
598 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
Mark Brown4fcbbb62009-05-23 12:27:03 +0100599
Mark Brown0a1bf552009-05-23 11:18:41 +0100600 return ret;
Mark Brown0a1bf552009-05-23 11:18:41 +0100601}
602
603/* power down chip */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000604static int wm8974_remove(struct snd_soc_codec *codec)
Mark Brown0a1bf552009-05-23 11:18:41 +0100605{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000606 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
Mark Brown0a1bf552009-05-23 11:18:41 +0100607 return 0;
608}
609
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000610static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
Mark Brown0a1bf552009-05-23 11:18:41 +0100611 .probe = wm8974_probe,
612 .remove = wm8974_remove,
613 .suspend = wm8974_suspend,
614 .resume = wm8974_resume,
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000615 .set_bias_level = wm8974_set_bias_level,
616 .reg_cache_size = ARRAY_SIZE(wm8974_reg),
617 .reg_word_size = sizeof(u16),
618 .reg_cache_default = wm8974_reg,
Mark Browna2bd6912011-12-29 11:10:27 +0000619
620 .controls = wm8974_snd_controls,
621 .num_controls = ARRAY_SIZE(wm8974_snd_controls),
622 .dapm_widgets = wm8974_dapm_widgets,
623 .num_dapm_widgets = ARRAY_SIZE(wm8974_dapm_widgets),
624 .dapm_routes = wm8974_dapm_routes,
625 .num_dapm_routes = ARRAY_SIZE(wm8974_dapm_routes),
Mark Brown0a1bf552009-05-23 11:18:41 +0100626};
Mark Brown0a1bf552009-05-23 11:18:41 +0100627
Bill Pemberton7a79e942012-12-07 09:26:37 -0500628static int wm8974_i2c_probe(struct i2c_client *i2c,
629 const struct i2c_device_id *id)
Mark Brown4fcbbb62009-05-23 12:27:03 +0100630{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000631 int ret;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100632
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000633 ret = snd_soc_register_codec(&i2c->dev,
634 &soc_codec_dev_wm8974, &wm8974_dai, 1);
Mark Brownc2562a82011-12-29 11:11:25 +0000635
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000636 return ret;
Mark Brown4fcbbb62009-05-23 12:27:03 +0100637}
638
Bill Pemberton7a79e942012-12-07 09:26:37 -0500639static int wm8974_i2c_remove(struct i2c_client *client)
Mark Brown4fcbbb62009-05-23 12:27:03 +0100640{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000641 snd_soc_unregister_codec(&client->dev);
Mark Brownc2562a82011-12-29 11:11:25 +0000642
Mark Brown4fcbbb62009-05-23 12:27:03 +0100643 return 0;
644}
645
646static const struct i2c_device_id wm8974_i2c_id[] = {
647 { "wm8974", 0 },
648 { }
649};
650MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
651
652static struct i2c_driver wm8974_i2c_driver = {
653 .driver = {
Mark Brown091edcc2011-12-02 22:08:49 +0000654 .name = "wm8974",
Mark Brown4fcbbb62009-05-23 12:27:03 +0100655 .owner = THIS_MODULE,
656 },
657 .probe = wm8974_i2c_probe,
Bill Pemberton7a79e942012-12-07 09:26:37 -0500658 .remove = wm8974_i2c_remove,
Mark Brown4fcbbb62009-05-23 12:27:03 +0100659 .id_table = wm8974_i2c_id,
660};
661
Sachin Kamat2be59412012-08-06 17:25:59 +0530662module_i2c_driver(wm8974_i2c_driver);
Mark Brown0a1bf552009-05-23 11:18:41 +0100663
664MODULE_DESCRIPTION("ASoC WM8974 driver");
665MODULE_AUTHOR("Liam Girdwood");
666MODULE_LICENSE("GPL");