blob: 6f0455c5318b620903d9640ab0ef47acc7ab3dcc [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>
15#include <linux/version.h>
16#include <linux/kernel.h>
17#include <linux/init.h>
18#include <linux/delay.h>
19#include <linux/pm.h>
20#include <linux/i2c.h>
21#include <linux/platform_device.h>
22#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
51#define WM8974_POWER1_BUFIOEN 0x10
52
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 Brown0a1bf552009-05-23 11:18:41 +010060/*
61 * read wm8974 register cache
62 */
Mark Brown1a55b3f2009-05-23 11:31:40 +010063static inline unsigned int wm8974_read_reg_cache(struct snd_soc_codec *codec,
Mark Brown0a1bf552009-05-23 11:18:41 +010064 unsigned int reg)
65{
66 u16 *cache = codec->reg_cache;
67 if (reg == WM8974_RESET)
68 return 0;
69 if (reg >= WM8974_CACHEREGNUM)
70 return -1;
71 return cache[reg];
72}
73
74/*
75 * write wm8974 register cache
76 */
77static inline void wm8974_write_reg_cache(struct snd_soc_codec *codec,
78 u16 reg, unsigned int value)
79{
80 u16 *cache = codec->reg_cache;
81 if (reg >= WM8974_CACHEREGNUM)
82 return;
83 cache[reg] = value;
84}
85
86/*
87 * write to the WM8974 register space
88 */
89static int wm8974_write(struct snd_soc_codec *codec, unsigned int reg,
90 unsigned int value)
91{
92 u8 data[2];
93
94 /* data is
95 * D15..D9 WM8974 register offset
96 * D8...D0 register data
97 */
98 data[0] = (reg << 1) | ((value >> 8) & 0x0001);
99 data[1] = value & 0x00ff;
100
Mark Brown1a55b3f2009-05-23 11:31:40 +0100101 wm8974_write_reg_cache(codec, reg, value);
Mark Brown0a1bf552009-05-23 11:18:41 +0100102 if (codec->hw_write(codec->control_data, data, 2) == 2)
103 return 0;
104 else
105 return -EIO;
106}
107
108#define wm8974_reset(c) wm8974_write(c, WM8974_RESET, 0)
109
110static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
111static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
112static const char *wm8974_eqmode[] = {"Capture", "Playback" };
113static const char *wm8974_bw[] = {"Narrow", "Wide" };
114static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
115static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
116static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
117static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
118static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
119static const char *wm8974_alc[] = {"ALC", "Limiter" };
120
121static const struct soc_enum wm8974_enum[] = {
122 SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
123 SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
124 SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp),
125 SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode),
126
127 SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1),
128 SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw),
129 SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2),
130 SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw),
131
132 SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3),
133 SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw),
134 SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4),
135 SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw),
136
137 SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5),
138 SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc),
139};
140
Mark Browna5f8d2f2009-06-30 19:30:33 +0100141static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
142static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
143static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
144static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
145
Mark Brown0a1bf552009-05-23 11:18:41 +0100146static const struct snd_kcontrol_new wm8974_snd_controls[] = {
147
148SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
149
150SOC_ENUM("DAC Companding", wm8974_enum[1]),
151SOC_ENUM("ADC Companding", wm8974_enum[0]),
152
153SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
154SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
155
Mark Browna5f8d2f2009-06-30 19:30:33 +0100156SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100157
158SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
159SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
160SOC_SINGLE("ADC Inversion Switch", WM8974_COMP, 0, 1, 0),
161
Mark Browna5f8d2f2009-06-30 19:30:33 +0100162SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL, 0, 255, 0, digital_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100163
164SOC_ENUM("Equaliser Function", wm8974_enum[3]),
165SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100166SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100167
168SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
169SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100170SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100171
172SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
173SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100174SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100175
176SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
177SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100178SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100179
180SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
181SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100182SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5, 0, 24, 1, eq_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100183
184SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0),
185SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0),
186SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0),
187
188SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0),
189SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0),
190
191SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0),
192SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0),
193SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0),
194
195SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0),
196SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0),
197SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0),
198
199SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
200SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0),
201SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0),
202
203SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0),
204SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0),
205
206SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100207SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0, inpga_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100208
209SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0),
210SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1),
Mark Browna5f8d2f2009-06-30 19:30:33 +0100211SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 1, spk_tlv),
Mark Brown0a1bf552009-05-23 11:18:41 +0100212
213SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
214SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 0),
215};
216
Mark Brown0a1bf552009-05-23 11:18:41 +0100217/* Speaker Output Mixer */
218static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
219SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
220SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
221SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1),
222};
223
224/* Mono Output Mixer */
225static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
226SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
227SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
228SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 1),
229};
230
231/* AUX Input boost vol */
232static const struct snd_kcontrol_new wm8974_aux_boost_controls =
233SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
234
235/* Mic Input boost vol */
236static const struct snd_kcontrol_new wm8974_mic_boost_controls =
237SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
238
239/* Capture boost switch */
240static const struct snd_kcontrol_new wm8974_capture_boost_controls =
241SOC_DAPM_SINGLE("Capture Boost Switch", WM8974_INPPGA, 6, 1, 0);
242
243/* Aux In to PGA */
244static const struct snd_kcontrol_new wm8974_aux_capture_boost_controls =
245SOC_DAPM_SINGLE("Aux Capture Boost Switch", WM8974_INPPGA, 2, 1, 0);
246
247/* Mic P In to PGA */
248static const struct snd_kcontrol_new wm8974_micp_capture_boost_controls =
249SOC_DAPM_SINGLE("Mic P Capture Boost Switch", WM8974_INPPGA, 0, 1, 0);
250
251/* Mic N In to PGA */
252static const struct snd_kcontrol_new wm8974_micn_capture_boost_controls =
253SOC_DAPM_SINGLE("Mic N Capture Boost Switch", WM8974_INPPGA, 1, 1, 0);
254
255static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
256SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
257 &wm8974_speaker_mixer_controls[0],
258 ARRAY_SIZE(wm8974_speaker_mixer_controls)),
259SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
260 &wm8974_mono_mixer_controls[0],
261 ARRAY_SIZE(wm8974_mono_mixer_controls)),
262SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
263SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER3, 0, 0),
264SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
265SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
266SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
267SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
268SND_SOC_DAPM_PGA("Mic PGA", WM8974_POWER2, 2, 0, NULL, 0),
269
270SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
271 &wm8974_aux_boost_controls, 1),
272SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
273 &wm8974_mic_boost_controls, 1),
274SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
275 &wm8974_capture_boost_controls),
276
277SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0, NULL, 0),
278
279SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
280
281SND_SOC_DAPM_INPUT("MICN"),
282SND_SOC_DAPM_INPUT("MICP"),
283SND_SOC_DAPM_INPUT("AUX"),
284SND_SOC_DAPM_OUTPUT("MONOOUT"),
285SND_SOC_DAPM_OUTPUT("SPKOUTP"),
286SND_SOC_DAPM_OUTPUT("SPKOUTN"),
287};
288
289static const struct snd_soc_dapm_route audio_map[] = {
290 /* Mono output mixer */
291 {"Mono Mixer", "PCM Playback Switch", "DAC"},
292 {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
293 {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
294
295 /* Speaker output mixer */
296 {"Speaker Mixer", "PCM Playback Switch", "DAC"},
297 {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
298 {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
299
300 /* Outputs */
301 {"Mono Out", NULL, "Mono Mixer"},
302 {"MONOOUT", NULL, "Mono Out"},
303 {"SpkN Out", NULL, "Speaker Mixer"},
304 {"SpkP Out", NULL, "Speaker Mixer"},
305 {"SPKOUTN", NULL, "SpkN Out"},
306 {"SPKOUTP", NULL, "SpkP Out"},
307
308 /* Boost Mixer */
309 {"Boost Mixer", NULL, "ADC"},
310 {"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
311 {"Aux Boost", "Aux Volume", "Boost Mixer"},
312 {"Capture Boost", "Capture Switch", "Boost Mixer"},
313 {"Mic Boost", "Mic Volume", "Boost Mixer"},
314
315 /* Inputs */
316 {"MICP", NULL, "Mic Boost"},
317 {"MICN", NULL, "Mic PGA"},
318 {"Mic PGA", NULL, "Capture Boost"},
319 {"AUX", NULL, "Aux Input"},
320};
321
322static int wm8974_add_widgets(struct snd_soc_codec *codec)
323{
324 snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets,
325 ARRAY_SIZE(wm8974_dapm_widgets));
326
327 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
328
329 snd_soc_dapm_new_widgets(codec);
330 return 0;
331}
332
333struct pll_ {
Mark Brown91d0c3e2009-06-30 19:02:32 +0100334 unsigned int pre_div:4; /* prescale - 1 */
Mark Brown0a1bf552009-05-23 11:18:41 +0100335 unsigned int n:4;
336 unsigned int k;
337};
338
Mark Brown91d0c3e2009-06-30 19:02:32 +0100339static struct pll_ pll_div;
340
341/* The size in bits of the pll divide multiplied by 10
342 * to allow rounding later */
343#define FIXED_PLL_SIZE ((1 << 24) * 10)
344
345static void pll_factors(unsigned int target, unsigned int source)
346{
347 unsigned long long Kpart;
348 unsigned int K, Ndiv, Nmod;
349
350 Ndiv = target / source;
351 if (Ndiv < 6) {
352 source >>= 1;
353 pll_div.pre_div = 1;
354 Ndiv = target / source;
355 } else
356 pll_div.pre_div = 0;
357
358 if ((Ndiv < 6) || (Ndiv > 12))
359 printk(KERN_WARNING
Mark Brown8b83a192009-06-30 19:37:02 +0100360 "WM8974 N value %u outwith recommended range!\n",
Mark Brown91d0c3e2009-06-30 19:02:32 +0100361 Ndiv);
362
363 pll_div.n = Ndiv;
364 Nmod = target % source;
365 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
366
367 do_div(Kpart, source);
368
369 K = Kpart & 0xFFFFFFFF;
370
371 /* Check if we need to round */
372 if ((K % 10) >= 5)
373 K += 5;
374
375 /* Move down to proper range now rounding is done */
376 K /= 10;
377
378 pll_div.k = K;
379}
Mark Brown0a1bf552009-05-23 11:18:41 +0100380
381static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
382 int pll_id, unsigned int freq_in, unsigned int freq_out)
383{
384 struct snd_soc_codec *codec = codec_dai->codec;
Mark Brown0a1bf552009-05-23 11:18:41 +0100385 u16 reg;
386
Mark Brown1a55b3f2009-05-23 11:31:40 +0100387 if (freq_in == 0 || freq_out == 0) {
Mark Brown91d0c3e2009-06-30 19:02:32 +0100388 /* Clock CODEC directly from MCLK */
389 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
390 wm8974_write(codec, WM8974_CLOCK, reg & 0x0ff);
391
392 /* Turn off PLL */
Mark Brown0a1bf552009-05-23 11:18:41 +0100393 reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
394 wm8974_write(codec, WM8974_POWER1, reg & 0x1df);
395 return 0;
396 }
397
Mark Brown91d0c3e2009-06-30 19:02:32 +0100398 pll_factors(freq_out*4, freq_in);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100399
Mark Brown91d0c3e2009-06-30 19:02:32 +0100400 wm8974_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
401 wm8974_write(codec, WM8974_PLLK1, pll_div.k >> 18);
402 wm8974_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
403 wm8974_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
404 reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
405 wm8974_write(codec, WM8974_POWER1, reg | 0x020);
406
407 /* Run CODEC from PLL instead of MCLK */
408 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
409 wm8974_write(codec, WM8974_CLOCK, reg | 0x100);
410
411 return 0;
Mark Brown0a1bf552009-05-23 11:18:41 +0100412}
413
414/*
415 * Configure WM8974 clock dividers.
416 */
417static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
418 int div_id, int div)
419{
420 struct snd_soc_codec *codec = codec_dai->codec;
421 u16 reg;
422
423 switch (div_id) {
424 case WM8974_OPCLKDIV:
Mark Brown1a55b3f2009-05-23 11:31:40 +0100425 reg = wm8974_read_reg_cache(codec, WM8974_GPIO) & 0x1cf;
Mark Brown0a1bf552009-05-23 11:18:41 +0100426 wm8974_write(codec, WM8974_GPIO, reg | div);
427 break;
428 case WM8974_MCLKDIV:
429 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x11f;
430 wm8974_write(codec, WM8974_CLOCK, reg | div);
431 break;
432 case WM8974_ADCCLK:
433 reg = wm8974_read_reg_cache(codec, WM8974_ADC) & 0x1f7;
434 wm8974_write(codec, WM8974_ADC, reg | div);
435 break;
436 case WM8974_DACCLK:
437 reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0x1f7;
438 wm8974_write(codec, WM8974_DAC, reg | div);
439 break;
440 case WM8974_BCLKDIV:
441 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1e3;
442 wm8974_write(codec, WM8974_CLOCK, reg | div);
443 break;
444 default:
445 return -EINVAL;
446 }
447
448 return 0;
449}
450
451static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
452 unsigned int fmt)
453{
454 struct snd_soc_codec *codec = codec_dai->codec;
455 u16 iface = 0;
456 u16 clk = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1fe;
457
458 /* set master/slave audio interface */
459 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
460 case SND_SOC_DAIFMT_CBM_CFM:
461 clk |= 0x0001;
462 break;
463 case SND_SOC_DAIFMT_CBS_CFS:
464 break;
465 default:
466 return -EINVAL;
467 }
468
469 /* interface format */
470 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
471 case SND_SOC_DAIFMT_I2S:
472 iface |= 0x0010;
473 break;
474 case SND_SOC_DAIFMT_RIGHT_J:
475 break;
476 case SND_SOC_DAIFMT_LEFT_J:
477 iface |= 0x0008;
478 break;
479 case SND_SOC_DAIFMT_DSP_A:
480 iface |= 0x00018;
481 break;
482 default:
483 return -EINVAL;
484 }
485
486 /* clock inversion */
487 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
488 case SND_SOC_DAIFMT_NB_NF:
489 break;
490 case SND_SOC_DAIFMT_IB_IF:
491 iface |= 0x0180;
492 break;
493 case SND_SOC_DAIFMT_IB_NF:
494 iface |= 0x0100;
495 break;
496 case SND_SOC_DAIFMT_NB_IF:
497 iface |= 0x0080;
498 break;
499 default:
500 return -EINVAL;
501 }
502
503 wm8974_write(codec, WM8974_IFACE, iface);
504 wm8974_write(codec, WM8974_CLOCK, clk);
505 return 0;
506}
507
508static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
509 struct snd_pcm_hw_params *params,
510 struct snd_soc_dai *dai)
511{
512 struct snd_soc_codec *codec = dai->codec;
513 u16 iface = wm8974_read_reg_cache(codec, WM8974_IFACE) & 0x19f;
514 u16 adn = wm8974_read_reg_cache(codec, WM8974_ADD) & 0x1f1;
515
516 /* bit size */
517 switch (params_format(params)) {
518 case SNDRV_PCM_FORMAT_S16_LE:
519 break;
520 case SNDRV_PCM_FORMAT_S20_3LE:
521 iface |= 0x0020;
522 break;
523 case SNDRV_PCM_FORMAT_S24_LE:
524 iface |= 0x0040;
525 break;
526 case SNDRV_PCM_FORMAT_S32_LE:
527 iface |= 0x0060;
528 break;
529 }
530
531 /* filter coefficient */
532 switch (params_rate(params)) {
533 case SNDRV_PCM_RATE_8000:
534 adn |= 0x5 << 1;
535 break;
536 case SNDRV_PCM_RATE_11025:
537 adn |= 0x4 << 1;
538 break;
539 case SNDRV_PCM_RATE_16000:
540 adn |= 0x3 << 1;
541 break;
542 case SNDRV_PCM_RATE_22050:
543 adn |= 0x2 << 1;
544 break;
545 case SNDRV_PCM_RATE_32000:
546 adn |= 0x1 << 1;
547 break;
548 case SNDRV_PCM_RATE_44100:
Mark Brown8b83a192009-06-30 19:37:02 +0100549 case SNDRV_PCM_RATE_48000:
Mark Brown0a1bf552009-05-23 11:18:41 +0100550 break;
551 }
552
553 wm8974_write(codec, WM8974_IFACE, iface);
554 wm8974_write(codec, WM8974_ADD, adn);
555 return 0;
556}
557
558static int wm8974_mute(struct snd_soc_dai *dai, int mute)
559{
560 struct snd_soc_codec *codec = dai->codec;
561 u16 mute_reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0xffbf;
562
Mark Brown1a55b3f2009-05-23 11:31:40 +0100563 if (mute)
Mark Brown0a1bf552009-05-23 11:18:41 +0100564 wm8974_write(codec, WM8974_DAC, mute_reg | 0x40);
565 else
566 wm8974_write(codec, WM8974_DAC, mute_reg);
567 return 0;
568}
569
570/* liam need to make this lower power with dapm */
571static int wm8974_set_bias_level(struct snd_soc_codec *codec,
572 enum snd_soc_bias_level level)
573{
Mark Browndf1ef7a2009-06-30 19:01:09 +0100574 u16 power1 = wm8974_read_reg_cache(codec, WM8974_POWER1) & ~0x3;
575
Mark Brown0a1bf552009-05-23 11:18:41 +0100576 switch (level) {
577 case SND_SOC_BIAS_ON:
Mark Brown0a1bf552009-05-23 11:18:41 +0100578 case SND_SOC_BIAS_PREPARE:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100579 power1 |= 0x1; /* VMID 50k */
580 wm8974_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100581 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100582
Mark Brown0a1bf552009-05-23 11:18:41 +0100583 case SND_SOC_BIAS_STANDBY:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100584 power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
585
586 if (codec->bias_level == SND_SOC_BIAS_OFF) {
587 /* Initial cap charge at VMID 5k */
588 wm8974_write(codec, WM8974_POWER1, power1 | 0x3);
589 mdelay(100);
590 }
591
592 power1 |= 0x2; /* VMID 500k */
593 wm8974_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100594 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100595
Mark Brown0a1bf552009-05-23 11:18:41 +0100596 case SND_SOC_BIAS_OFF:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100597 wm8974_write(codec, WM8974_POWER1, 0);
598 wm8974_write(codec, WM8974_POWER2, 0);
599 wm8974_write(codec, WM8974_POWER3, 0);
Mark Brown0a1bf552009-05-23 11:18:41 +0100600 break;
601 }
Mark Browndf1ef7a2009-06-30 19:01:09 +0100602
Mark Brown0a1bf552009-05-23 11:18:41 +0100603 codec->bias_level = level;
604 return 0;
605}
606
Mark Brown1a55b3f2009-05-23 11:31:40 +0100607#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
Mark Brown0a1bf552009-05-23 11:18:41 +0100608
609#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
610 SNDRV_PCM_FMTBIT_S24_LE)
611
612static struct snd_soc_dai_ops wm8974_ops = {
613 .hw_params = wm8974_pcm_hw_params,
614 .digital_mute = wm8974_mute,
615 .set_fmt = wm8974_set_dai_fmt,
616 .set_clkdiv = wm8974_set_dai_clkdiv,
617 .set_pll = wm8974_set_dai_pll,
618};
619
620struct snd_soc_dai wm8974_dai = {
621 .name = "WM8974 HiFi",
622 .playback = {
623 .stream_name = "Playback",
624 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100625 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100626 .rates = WM8974_RATES,
627 .formats = WM8974_FORMATS,},
628 .capture = {
629 .stream_name = "Capture",
630 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100631 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100632 .rates = WM8974_RATES,
633 .formats = WM8974_FORMATS,},
634 .ops = &wm8974_ops,
Mark Browncb11d392009-06-30 19:36:39 +0100635 .symmetric_rates = 1,
Mark Brown0a1bf552009-05-23 11:18:41 +0100636};
637EXPORT_SYMBOL_GPL(wm8974_dai);
638
639static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
640{
641 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
642 struct snd_soc_codec *codec = socdev->card->codec;
643
644 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
645 return 0;
646}
647
648static int wm8974_resume(struct platform_device *pdev)
649{
650 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
651 struct snd_soc_codec *codec = socdev->card->codec;
652 int i;
653 u8 data[2];
654 u16 *cache = codec->reg_cache;
655
656 /* Sync reg_cache with the hardware */
657 for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
658 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
659 data[1] = cache[i] & 0x00ff;
660 codec->hw_write(codec->control_data, data, 2);
661 }
662 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
663 wm8974_set_bias_level(codec, codec->suspend_bias_level);
664 return 0;
665}
666
Mark Brown4fcbbb62009-05-23 12:27:03 +0100667static int wm8974_probe(struct platform_device *pdev)
Mark Brown0a1bf552009-05-23 11:18:41 +0100668{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100669 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
670 struct snd_soc_codec *codec;
Mark Brown0a1bf552009-05-23 11:18:41 +0100671 int ret = 0;
672
Mark Brown4fcbbb62009-05-23 12:27:03 +0100673 if (wm8974_codec == NULL) {
674 dev_err(&pdev->dev, "Codec device not registered\n");
675 return -ENODEV;
676 }
Mark Brown0a1bf552009-05-23 11:18:41 +0100677
Mark Brown4fcbbb62009-05-23 12:27:03 +0100678 socdev->card->codec = wm8974_codec;
679 codec = wm8974_codec;
Mark Brown0a1bf552009-05-23 11:18:41 +0100680
681 /* register pcms */
682 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100683 if (ret < 0) {
Mark Brown4fcbbb62009-05-23 12:27:03 +0100684 dev_err(codec->dev, "failed to create pcms: %d\n", ret);
Mark Brown0a1bf552009-05-23 11:18:41 +0100685 goto pcm_err;
686 }
687
Mark Brown4fcbbb62009-05-23 12:27:03 +0100688 snd_soc_add_controls(codec, wm8974_snd_controls,
689 ARRAY_SIZE(wm8974_snd_controls));
Mark Brown0a1bf552009-05-23 11:18:41 +0100690 wm8974_add_widgets(codec);
691 ret = snd_soc_init_card(socdev);
692 if (ret < 0) {
Mark Brown4fcbbb62009-05-23 12:27:03 +0100693 dev_err(codec->dev, "failed to register card: %d\n", ret);
Mark Brown0a1bf552009-05-23 11:18:41 +0100694 goto card_err;
695 }
Mark Brown4fcbbb62009-05-23 12:27:03 +0100696
Mark Brown0a1bf552009-05-23 11:18:41 +0100697 return ret;
698
699card_err:
700 snd_soc_free_pcms(socdev);
701 snd_soc_dapm_free(socdev);
702pcm_err:
Mark Brown0a1bf552009-05-23 11:18:41 +0100703 return ret;
704}
705
706/* power down chip */
707static int wm8974_remove(struct platform_device *pdev)
708{
709 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
Mark Brown0a1bf552009-05-23 11:18:41 +0100710
711 snd_soc_free_pcms(socdev);
712 snd_soc_dapm_free(socdev);
Mark Brown0a1bf552009-05-23 11:18:41 +0100713
714 return 0;
715}
716
717struct snd_soc_codec_device soc_codec_dev_wm8974 = {
718 .probe = wm8974_probe,
719 .remove = wm8974_remove,
720 .suspend = wm8974_suspend,
721 .resume = wm8974_resume,
722};
723EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
724
Mark Brown4fcbbb62009-05-23 12:27:03 +0100725static __devinit int wm8974_register(struct wm8974_priv *wm8974)
726{
727 int ret;
728 struct snd_soc_codec *codec = &wm8974->codec;
729
730 if (wm8974_codec) {
731 dev_err(codec->dev, "Another WM8974 is registered\n");
732 return -EINVAL;
733 }
734
735 mutex_init(&codec->mutex);
736 INIT_LIST_HEAD(&codec->dapm_widgets);
737 INIT_LIST_HEAD(&codec->dapm_paths);
738
739 codec->private_data = wm8974;
740 codec->name = "WM8974";
741 codec->owner = THIS_MODULE;
742 codec->read = wm8974_read_reg_cache;
743 codec->write = wm8974_write;
744 codec->bias_level = SND_SOC_BIAS_OFF;
745 codec->set_bias_level = wm8974_set_bias_level;
746 codec->dai = &wm8974_dai;
747 codec->num_dai = 1;
748 codec->reg_cache_size = WM8974_CACHEREGNUM;
749 codec->reg_cache = &wm8974->reg_cache;
750
751 memcpy(codec->reg_cache, wm8974_reg, sizeof(wm8974_reg));
752
753 ret = wm8974_reset(codec);
754 if (ret < 0) {
755 dev_err(codec->dev, "Failed to issue reset\n");
756 return ret;
757 }
758
759 wm8974_dai.dev = codec->dev;
760
761 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
762
763 wm8974_codec = codec;
764
765 ret = snd_soc_register_codec(codec);
766 if (ret != 0) {
767 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
768 return ret;
769 }
770
771 ret = snd_soc_register_dai(&wm8974_dai);
772 if (ret != 0) {
773 dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
774 snd_soc_unregister_codec(codec);
775 return ret;
776 }
777
778 return 0;
779}
780
781static __devexit void wm8974_unregister(struct wm8974_priv *wm8974)
782{
783 wm8974_set_bias_level(&wm8974->codec, SND_SOC_BIAS_OFF);
784 snd_soc_unregister_dai(&wm8974_dai);
785 snd_soc_unregister_codec(&wm8974->codec);
786 kfree(wm8974);
787 wm8974_codec = NULL;
788}
789
790static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
791 const struct i2c_device_id *id)
792{
793 struct wm8974_priv *wm8974;
794 struct snd_soc_codec *codec;
795
796 wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
797 if (wm8974 == NULL)
798 return -ENOMEM;
799
800 codec = &wm8974->codec;
801 codec->hw_write = (hw_write_t)i2c_master_send;
802
803 i2c_set_clientdata(i2c, wm8974);
804 codec->control_data = i2c;
805
806 codec->dev = &i2c->dev;
807
808 return wm8974_register(wm8974);
809}
810
811static __devexit int wm8974_i2c_remove(struct i2c_client *client)
812{
813 struct wm8974_priv *wm8974 = i2c_get_clientdata(client);
814 wm8974_unregister(wm8974);
815 return 0;
816}
817
818static const struct i2c_device_id wm8974_i2c_id[] = {
819 { "wm8974", 0 },
820 { }
821};
822MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
823
824static struct i2c_driver wm8974_i2c_driver = {
825 .driver = {
Mark Brown8b83a192009-06-30 19:37:02 +0100826 .name = "WM8974",
Mark Brown4fcbbb62009-05-23 12:27:03 +0100827 .owner = THIS_MODULE,
828 },
829 .probe = wm8974_i2c_probe,
830 .remove = __devexit_p(wm8974_i2c_remove),
831 .id_table = wm8974_i2c_id,
832};
833
Mark Brown0a1bf552009-05-23 11:18:41 +0100834static int __init wm8974_modinit(void)
835{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100836 return i2c_add_driver(&wm8974_i2c_driver);
Mark Brown0a1bf552009-05-23 11:18:41 +0100837}
838module_init(wm8974_modinit);
839
840static void __exit wm8974_exit(void)
841{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100842 i2c_del_driver(&wm8974_i2c_driver);
Mark Brown0a1bf552009-05-23 11:18:41 +0100843}
844module_exit(wm8974_exit);
845
846MODULE_DESCRIPTION("ASoC WM8974 driver");
847MODULE_AUTHOR("Liam Girdwood");
848MODULE_LICENSE("GPL");