blob: d07bd0ddd439a48d1ab784d42e5b9fb37f45068b [file] [log] [blame]
Mark Brown0a1bf552009-05-23 11:18:41 +01001/*
2 * wm8974.c -- WM8974 ALSA Soc Audio driver
3 *
4 * Copyright 2006 Wolfson Microelectronics PLC.
5 *
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>
28
29#include "wm8974.h"
30
Mark Brown0a1bf552009-05-23 11:18:41 +010031static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
Mark Brown1a55b3f2009-05-23 11:31:40 +010032 0x0000, 0x0000, 0x0000, 0x0000,
33 0x0050, 0x0000, 0x0140, 0x0000,
34 0x0000, 0x0000, 0x0000, 0x00ff,
35 0x0000, 0x0000, 0x0100, 0x00ff,
36 0x0000, 0x0000, 0x012c, 0x002c,
37 0x002c, 0x002c, 0x002c, 0x0000,
38 0x0032, 0x0000, 0x0000, 0x0000,
39 0x0000, 0x0000, 0x0000, 0x0000,
40 0x0038, 0x000b, 0x0032, 0x0000,
41 0x0008, 0x000c, 0x0093, 0x00e9,
42 0x0000, 0x0000, 0x0000, 0x0000,
43 0x0003, 0x0010, 0x0000, 0x0000,
44 0x0000, 0x0002, 0x0000, 0x0000,
45 0x0000, 0x0000, 0x0039, 0x0000,
46 0x0000,
Mark Brown0a1bf552009-05-23 11:18:41 +010047};
48
Mark Browndf1ef7a2009-06-30 19:01:09 +010049#define WM8974_POWER1_BIASEN 0x08
50#define WM8974_POWER1_BUFIOEN 0x10
51
Mark Brown4fcbbb62009-05-23 12:27:03 +010052struct wm8974_priv {
53 struct snd_soc_codec codec;
54 u16 reg_cache[WM8974_CACHEREGNUM];
55};
56
57static struct snd_soc_codec *wm8974_codec;
58
Mark Brown0a1bf552009-05-23 11:18:41 +010059/*
60 * read wm8974 register cache
61 */
Mark Brown1a55b3f2009-05-23 11:31:40 +010062static inline unsigned int wm8974_read_reg_cache(struct snd_soc_codec *codec,
Mark Brown0a1bf552009-05-23 11:18:41 +010063 unsigned int reg)
64{
65 u16 *cache = codec->reg_cache;
66 if (reg == WM8974_RESET)
67 return 0;
68 if (reg >= WM8974_CACHEREGNUM)
69 return -1;
70 return cache[reg];
71}
72
73/*
74 * write wm8974 register cache
75 */
76static inline void wm8974_write_reg_cache(struct snd_soc_codec *codec,
77 u16 reg, unsigned int value)
78{
79 u16 *cache = codec->reg_cache;
80 if (reg >= WM8974_CACHEREGNUM)
81 return;
82 cache[reg] = value;
83}
84
85/*
86 * write to the WM8974 register space
87 */
88static int wm8974_write(struct snd_soc_codec *codec, unsigned int reg,
89 unsigned int value)
90{
91 u8 data[2];
92
93 /* data is
94 * D15..D9 WM8974 register offset
95 * D8...D0 register data
96 */
97 data[0] = (reg << 1) | ((value >> 8) & 0x0001);
98 data[1] = value & 0x00ff;
99
Mark Brown1a55b3f2009-05-23 11:31:40 +0100100 wm8974_write_reg_cache(codec, reg, value);
Mark Brown0a1bf552009-05-23 11:18:41 +0100101 if (codec->hw_write(codec->control_data, data, 2) == 2)
102 return 0;
103 else
104 return -EIO;
105}
106
107#define wm8974_reset(c) wm8974_write(c, WM8974_RESET, 0)
108
109static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
110static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
111static const char *wm8974_eqmode[] = {"Capture", "Playback" };
112static const char *wm8974_bw[] = {"Narrow", "Wide" };
113static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
114static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
115static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
116static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
117static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
118static const char *wm8974_alc[] = {"ALC", "Limiter" };
119
120static const struct soc_enum wm8974_enum[] = {
121 SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
122 SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
123 SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp),
124 SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode),
125
126 SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1),
127 SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw),
128 SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2),
129 SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw),
130
131 SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3),
132 SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw),
133 SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4),
134 SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw),
135
136 SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5),
137 SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc),
138};
139
140static const struct snd_kcontrol_new wm8974_snd_controls[] = {
141
142SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
143
144SOC_ENUM("DAC Companding", wm8974_enum[1]),
145SOC_ENUM("ADC Companding", wm8974_enum[0]),
146
147SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
148SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
149
150SOC_SINGLE("PCM Volume", WM8974_DACVOL, 0, 127, 0),
151
152SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
153SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
154SOC_SINGLE("ADC Inversion Switch", WM8974_COMP, 0, 1, 0),
155
156SOC_SINGLE("Capture Volume", WM8974_ADCVOL, 0, 127, 0),
157
158SOC_ENUM("Equaliser Function", wm8974_enum[3]),
159SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
160SOC_SINGLE("EQ1 Volume", WM8974_EQ1, 0, 31, 1),
161
162SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
163SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
164SOC_SINGLE("EQ2 Volume", WM8974_EQ2, 0, 31, 1),
165
166SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
167SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
168SOC_SINGLE("EQ3 Volume", WM8974_EQ3, 0, 31, 1),
169
170SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
171SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
172SOC_SINGLE("EQ4 Volume", WM8974_EQ4, 0, 31, 1),
173
174SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
175SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
176SOC_SINGLE("EQ5 Volume", WM8974_EQ5, 0, 31, 1),
177
178SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0),
179SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0),
180SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0),
181
182SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0),
183SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0),
184
185SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0),
186SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0),
187SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0),
188
189SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0),
190SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0),
191SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0),
192
193SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
194SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0),
195SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0),
196
197SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0),
198SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0),
199
200SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0),
201SOC_SINGLE("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0),
202
203SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0),
204SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1),
205SOC_SINGLE("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0),
206
207SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
208SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 0),
209};
210
Mark Brown0a1bf552009-05-23 11:18:41 +0100211/* Speaker Output Mixer */
212static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
213SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
214SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
215SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1),
216};
217
218/* Mono Output Mixer */
219static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
220SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
221SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
222SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 1),
223};
224
225/* AUX Input boost vol */
226static const struct snd_kcontrol_new wm8974_aux_boost_controls =
227SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
228
229/* Mic Input boost vol */
230static const struct snd_kcontrol_new wm8974_mic_boost_controls =
231SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
232
233/* Capture boost switch */
234static const struct snd_kcontrol_new wm8974_capture_boost_controls =
235SOC_DAPM_SINGLE("Capture Boost Switch", WM8974_INPPGA, 6, 1, 0);
236
237/* Aux In to PGA */
238static const struct snd_kcontrol_new wm8974_aux_capture_boost_controls =
239SOC_DAPM_SINGLE("Aux Capture Boost Switch", WM8974_INPPGA, 2, 1, 0);
240
241/* Mic P In to PGA */
242static const struct snd_kcontrol_new wm8974_micp_capture_boost_controls =
243SOC_DAPM_SINGLE("Mic P Capture Boost Switch", WM8974_INPPGA, 0, 1, 0);
244
245/* Mic N In to PGA */
246static const struct snd_kcontrol_new wm8974_micn_capture_boost_controls =
247SOC_DAPM_SINGLE("Mic N Capture Boost Switch", WM8974_INPPGA, 1, 1, 0);
248
249static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
250SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
251 &wm8974_speaker_mixer_controls[0],
252 ARRAY_SIZE(wm8974_speaker_mixer_controls)),
253SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
254 &wm8974_mono_mixer_controls[0],
255 ARRAY_SIZE(wm8974_mono_mixer_controls)),
256SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
257SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER3, 0, 0),
258SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
259SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
260SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
261SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
262SND_SOC_DAPM_PGA("Mic PGA", WM8974_POWER2, 2, 0, NULL, 0),
263
264SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
265 &wm8974_aux_boost_controls, 1),
266SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
267 &wm8974_mic_boost_controls, 1),
268SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
269 &wm8974_capture_boost_controls),
270
271SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0, NULL, 0),
272
273SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
274
275SND_SOC_DAPM_INPUT("MICN"),
276SND_SOC_DAPM_INPUT("MICP"),
277SND_SOC_DAPM_INPUT("AUX"),
278SND_SOC_DAPM_OUTPUT("MONOOUT"),
279SND_SOC_DAPM_OUTPUT("SPKOUTP"),
280SND_SOC_DAPM_OUTPUT("SPKOUTN"),
281};
282
283static const struct snd_soc_dapm_route audio_map[] = {
284 /* Mono output mixer */
285 {"Mono Mixer", "PCM Playback Switch", "DAC"},
286 {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
287 {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
288
289 /* Speaker output mixer */
290 {"Speaker Mixer", "PCM Playback Switch", "DAC"},
291 {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
292 {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
293
294 /* Outputs */
295 {"Mono Out", NULL, "Mono Mixer"},
296 {"MONOOUT", NULL, "Mono Out"},
297 {"SpkN Out", NULL, "Speaker Mixer"},
298 {"SpkP Out", NULL, "Speaker Mixer"},
299 {"SPKOUTN", NULL, "SpkN Out"},
300 {"SPKOUTP", NULL, "SpkP Out"},
301
302 /* Boost Mixer */
303 {"Boost Mixer", NULL, "ADC"},
304 {"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
305 {"Aux Boost", "Aux Volume", "Boost Mixer"},
306 {"Capture Boost", "Capture Switch", "Boost Mixer"},
307 {"Mic Boost", "Mic Volume", "Boost Mixer"},
308
309 /* Inputs */
310 {"MICP", NULL, "Mic Boost"},
311 {"MICN", NULL, "Mic PGA"},
312 {"Mic PGA", NULL, "Capture Boost"},
313 {"AUX", NULL, "Aux Input"},
314};
315
316static int wm8974_add_widgets(struct snd_soc_codec *codec)
317{
318 snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets,
319 ARRAY_SIZE(wm8974_dapm_widgets));
320
321 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
322
323 snd_soc_dapm_new_widgets(codec);
324 return 0;
325}
326
327struct pll_ {
Mark Brown91d0c3e2009-06-30 19:02:32 +0100328 unsigned int pre_div:4; /* prescale - 1 */
Mark Brown0a1bf552009-05-23 11:18:41 +0100329 unsigned int n:4;
330 unsigned int k;
331};
332
Mark Brown91d0c3e2009-06-30 19:02:32 +0100333static struct pll_ pll_div;
334
335/* The size in bits of the pll divide multiplied by 10
336 * to allow rounding later */
337#define FIXED_PLL_SIZE ((1 << 24) * 10)
338
339static void pll_factors(unsigned int target, unsigned int source)
340{
341 unsigned long long Kpart;
342 unsigned int K, Ndiv, Nmod;
343
344 Ndiv = target / source;
345 if (Ndiv < 6) {
346 source >>= 1;
347 pll_div.pre_div = 1;
348 Ndiv = target / source;
349 } else
350 pll_div.pre_div = 0;
351
352 if ((Ndiv < 6) || (Ndiv > 12))
353 printk(KERN_WARNING
354 "WM8974 N value %u outwith recommended range!d\n",
355 Ndiv);
356
357 pll_div.n = Ndiv;
358 Nmod = target % source;
359 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
360
361 do_div(Kpart, source);
362
363 K = Kpart & 0xFFFFFFFF;
364
365 /* Check if we need to round */
366 if ((K % 10) >= 5)
367 K += 5;
368
369 /* Move down to proper range now rounding is done */
370 K /= 10;
371
372 pll_div.k = K;
373}
Mark Brown0a1bf552009-05-23 11:18:41 +0100374
375static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
376 int pll_id, unsigned int freq_in, unsigned int freq_out)
377{
378 struct snd_soc_codec *codec = codec_dai->codec;
Mark Brown0a1bf552009-05-23 11:18:41 +0100379 u16 reg;
380
Mark Brown1a55b3f2009-05-23 11:31:40 +0100381 if (freq_in == 0 || freq_out == 0) {
Mark Brown91d0c3e2009-06-30 19:02:32 +0100382 /* Clock CODEC directly from MCLK */
383 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
384 wm8974_write(codec, WM8974_CLOCK, reg & 0x0ff);
385
386 /* Turn off PLL */
Mark Brown0a1bf552009-05-23 11:18:41 +0100387 reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
388 wm8974_write(codec, WM8974_POWER1, reg & 0x1df);
389 return 0;
390 }
391
Mark Brown91d0c3e2009-06-30 19:02:32 +0100392 pll_factors(freq_out*4, freq_in);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100393
Mark Brown91d0c3e2009-06-30 19:02:32 +0100394 wm8974_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
395 wm8974_write(codec, WM8974_PLLK1, pll_div.k >> 18);
396 wm8974_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
397 wm8974_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
398 reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
399 wm8974_write(codec, WM8974_POWER1, reg | 0x020);
400
401 /* Run CODEC from PLL instead of MCLK */
402 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
403 wm8974_write(codec, WM8974_CLOCK, reg | 0x100);
404
405 return 0;
Mark Brown0a1bf552009-05-23 11:18:41 +0100406}
407
408/*
409 * Configure WM8974 clock dividers.
410 */
411static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
412 int div_id, int div)
413{
414 struct snd_soc_codec *codec = codec_dai->codec;
415 u16 reg;
416
417 switch (div_id) {
418 case WM8974_OPCLKDIV:
Mark Brown1a55b3f2009-05-23 11:31:40 +0100419 reg = wm8974_read_reg_cache(codec, WM8974_GPIO) & 0x1cf;
Mark Brown0a1bf552009-05-23 11:18:41 +0100420 wm8974_write(codec, WM8974_GPIO, reg | div);
421 break;
422 case WM8974_MCLKDIV:
423 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x11f;
424 wm8974_write(codec, WM8974_CLOCK, reg | div);
425 break;
426 case WM8974_ADCCLK:
427 reg = wm8974_read_reg_cache(codec, WM8974_ADC) & 0x1f7;
428 wm8974_write(codec, WM8974_ADC, reg | div);
429 break;
430 case WM8974_DACCLK:
431 reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0x1f7;
432 wm8974_write(codec, WM8974_DAC, reg | div);
433 break;
434 case WM8974_BCLKDIV:
435 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1e3;
436 wm8974_write(codec, WM8974_CLOCK, reg | div);
437 break;
438 default:
439 return -EINVAL;
440 }
441
442 return 0;
443}
444
445static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
446 unsigned int fmt)
447{
448 struct snd_soc_codec *codec = codec_dai->codec;
449 u16 iface = 0;
450 u16 clk = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1fe;
451
452 /* set master/slave audio interface */
453 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
454 case SND_SOC_DAIFMT_CBM_CFM:
455 clk |= 0x0001;
456 break;
457 case SND_SOC_DAIFMT_CBS_CFS:
458 break;
459 default:
460 return -EINVAL;
461 }
462
463 /* interface format */
464 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
465 case SND_SOC_DAIFMT_I2S:
466 iface |= 0x0010;
467 break;
468 case SND_SOC_DAIFMT_RIGHT_J:
469 break;
470 case SND_SOC_DAIFMT_LEFT_J:
471 iface |= 0x0008;
472 break;
473 case SND_SOC_DAIFMT_DSP_A:
474 iface |= 0x00018;
475 break;
476 default:
477 return -EINVAL;
478 }
479
480 /* clock inversion */
481 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
482 case SND_SOC_DAIFMT_NB_NF:
483 break;
484 case SND_SOC_DAIFMT_IB_IF:
485 iface |= 0x0180;
486 break;
487 case SND_SOC_DAIFMT_IB_NF:
488 iface |= 0x0100;
489 break;
490 case SND_SOC_DAIFMT_NB_IF:
491 iface |= 0x0080;
492 break;
493 default:
494 return -EINVAL;
495 }
496
497 wm8974_write(codec, WM8974_IFACE, iface);
498 wm8974_write(codec, WM8974_CLOCK, clk);
499 return 0;
500}
501
502static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
503 struct snd_pcm_hw_params *params,
504 struct snd_soc_dai *dai)
505{
506 struct snd_soc_codec *codec = dai->codec;
507 u16 iface = wm8974_read_reg_cache(codec, WM8974_IFACE) & 0x19f;
508 u16 adn = wm8974_read_reg_cache(codec, WM8974_ADD) & 0x1f1;
509
510 /* bit size */
511 switch (params_format(params)) {
512 case SNDRV_PCM_FORMAT_S16_LE:
513 break;
514 case SNDRV_PCM_FORMAT_S20_3LE:
515 iface |= 0x0020;
516 break;
517 case SNDRV_PCM_FORMAT_S24_LE:
518 iface |= 0x0040;
519 break;
520 case SNDRV_PCM_FORMAT_S32_LE:
521 iface |= 0x0060;
522 break;
523 }
524
525 /* filter coefficient */
526 switch (params_rate(params)) {
527 case SNDRV_PCM_RATE_8000:
528 adn |= 0x5 << 1;
529 break;
530 case SNDRV_PCM_RATE_11025:
531 adn |= 0x4 << 1;
532 break;
533 case SNDRV_PCM_RATE_16000:
534 adn |= 0x3 << 1;
535 break;
536 case SNDRV_PCM_RATE_22050:
537 adn |= 0x2 << 1;
538 break;
539 case SNDRV_PCM_RATE_32000:
540 adn |= 0x1 << 1;
541 break;
542 case SNDRV_PCM_RATE_44100:
543 break;
544 }
545
546 wm8974_write(codec, WM8974_IFACE, iface);
547 wm8974_write(codec, WM8974_ADD, adn);
548 return 0;
549}
550
551static int wm8974_mute(struct snd_soc_dai *dai, int mute)
552{
553 struct snd_soc_codec *codec = dai->codec;
554 u16 mute_reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0xffbf;
555
Mark Brown1a55b3f2009-05-23 11:31:40 +0100556 if (mute)
Mark Brown0a1bf552009-05-23 11:18:41 +0100557 wm8974_write(codec, WM8974_DAC, mute_reg | 0x40);
558 else
559 wm8974_write(codec, WM8974_DAC, mute_reg);
560 return 0;
561}
562
563/* liam need to make this lower power with dapm */
564static int wm8974_set_bias_level(struct snd_soc_codec *codec,
565 enum snd_soc_bias_level level)
566{
Mark Browndf1ef7a2009-06-30 19:01:09 +0100567 u16 power1 = wm8974_read_reg_cache(codec, WM8974_POWER1) & ~0x3;
568
Mark Brown0a1bf552009-05-23 11:18:41 +0100569 switch (level) {
570 case SND_SOC_BIAS_ON:
Mark Brown0a1bf552009-05-23 11:18:41 +0100571 case SND_SOC_BIAS_PREPARE:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100572 power1 |= 0x1; /* VMID 50k */
573 wm8974_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100574 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100575
Mark Brown0a1bf552009-05-23 11:18:41 +0100576 case SND_SOC_BIAS_STANDBY:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100577 power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
578
579 if (codec->bias_level == SND_SOC_BIAS_OFF) {
580 /* Initial cap charge at VMID 5k */
581 wm8974_write(codec, WM8974_POWER1, power1 | 0x3);
582 mdelay(100);
583 }
584
585 power1 |= 0x2; /* VMID 500k */
586 wm8974_write(codec, WM8974_POWER1, power1);
Mark Brown0a1bf552009-05-23 11:18:41 +0100587 break;
Mark Browndf1ef7a2009-06-30 19:01:09 +0100588
Mark Brown0a1bf552009-05-23 11:18:41 +0100589 case SND_SOC_BIAS_OFF:
Mark Browndf1ef7a2009-06-30 19:01:09 +0100590 wm8974_write(codec, WM8974_POWER1, 0);
591 wm8974_write(codec, WM8974_POWER2, 0);
592 wm8974_write(codec, WM8974_POWER3, 0);
Mark Brown0a1bf552009-05-23 11:18:41 +0100593 break;
594 }
Mark Browndf1ef7a2009-06-30 19:01:09 +0100595
Mark Brown0a1bf552009-05-23 11:18:41 +0100596 codec->bias_level = level;
597 return 0;
598}
599
Mark Brown1a55b3f2009-05-23 11:31:40 +0100600#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
Mark Brown0a1bf552009-05-23 11:18:41 +0100601
602#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
603 SNDRV_PCM_FMTBIT_S24_LE)
604
605static struct snd_soc_dai_ops wm8974_ops = {
606 .hw_params = wm8974_pcm_hw_params,
607 .digital_mute = wm8974_mute,
608 .set_fmt = wm8974_set_dai_fmt,
609 .set_clkdiv = wm8974_set_dai_clkdiv,
610 .set_pll = wm8974_set_dai_pll,
611};
612
613struct snd_soc_dai wm8974_dai = {
614 .name = "WM8974 HiFi",
615 .playback = {
616 .stream_name = "Playback",
617 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100618 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100619 .rates = WM8974_RATES,
620 .formats = WM8974_FORMATS,},
621 .capture = {
622 .stream_name = "Capture",
623 .channels_min = 1,
Mark Brown33d81af2009-06-30 19:01:52 +0100624 .channels_max = 2, /* Only 1 channel of data */
Mark Brown0a1bf552009-05-23 11:18:41 +0100625 .rates = WM8974_RATES,
626 .formats = WM8974_FORMATS,},
627 .ops = &wm8974_ops,
628};
629EXPORT_SYMBOL_GPL(wm8974_dai);
630
631static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
632{
633 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
634 struct snd_soc_codec *codec = socdev->card->codec;
635
636 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
637 return 0;
638}
639
640static int wm8974_resume(struct platform_device *pdev)
641{
642 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
643 struct snd_soc_codec *codec = socdev->card->codec;
644 int i;
645 u8 data[2];
646 u16 *cache = codec->reg_cache;
647
648 /* Sync reg_cache with the hardware */
649 for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
650 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
651 data[1] = cache[i] & 0x00ff;
652 codec->hw_write(codec->control_data, data, 2);
653 }
654 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
655 wm8974_set_bias_level(codec, codec->suspend_bias_level);
656 return 0;
657}
658
Mark Brown4fcbbb62009-05-23 12:27:03 +0100659static int wm8974_probe(struct platform_device *pdev)
Mark Brown0a1bf552009-05-23 11:18:41 +0100660{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100661 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
662 struct snd_soc_codec *codec;
Mark Brown0a1bf552009-05-23 11:18:41 +0100663 int ret = 0;
664
Mark Brown4fcbbb62009-05-23 12:27:03 +0100665 if (wm8974_codec == NULL) {
666 dev_err(&pdev->dev, "Codec device not registered\n");
667 return -ENODEV;
668 }
Mark Brown0a1bf552009-05-23 11:18:41 +0100669
Mark Brown4fcbbb62009-05-23 12:27:03 +0100670 socdev->card->codec = wm8974_codec;
671 codec = wm8974_codec;
Mark Brown0a1bf552009-05-23 11:18:41 +0100672
673 /* register pcms */
674 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100675 if (ret < 0) {
Mark Brown4fcbbb62009-05-23 12:27:03 +0100676 dev_err(codec->dev, "failed to create pcms: %d\n", ret);
Mark Brown0a1bf552009-05-23 11:18:41 +0100677 goto pcm_err;
678 }
679
Mark Brown4fcbbb62009-05-23 12:27:03 +0100680 snd_soc_add_controls(codec, wm8974_snd_controls,
681 ARRAY_SIZE(wm8974_snd_controls));
Mark Brown0a1bf552009-05-23 11:18:41 +0100682 wm8974_add_widgets(codec);
683 ret = snd_soc_init_card(socdev);
684 if (ret < 0) {
Mark Brown4fcbbb62009-05-23 12:27:03 +0100685 dev_err(codec->dev, "failed to register card: %d\n", ret);
Mark Brown0a1bf552009-05-23 11:18:41 +0100686 goto card_err;
687 }
Mark Brown4fcbbb62009-05-23 12:27:03 +0100688
Mark Brown0a1bf552009-05-23 11:18:41 +0100689 return ret;
690
691card_err:
692 snd_soc_free_pcms(socdev);
693 snd_soc_dapm_free(socdev);
694pcm_err:
Mark Brown0a1bf552009-05-23 11:18:41 +0100695 return ret;
696}
697
698/* power down chip */
699static int wm8974_remove(struct platform_device *pdev)
700{
701 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
Mark Brown0a1bf552009-05-23 11:18:41 +0100702
703 snd_soc_free_pcms(socdev);
704 snd_soc_dapm_free(socdev);
Mark Brown0a1bf552009-05-23 11:18:41 +0100705
706 return 0;
707}
708
709struct snd_soc_codec_device soc_codec_dev_wm8974 = {
710 .probe = wm8974_probe,
711 .remove = wm8974_remove,
712 .suspend = wm8974_suspend,
713 .resume = wm8974_resume,
714};
715EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
716
Mark Brown4fcbbb62009-05-23 12:27:03 +0100717static __devinit int wm8974_register(struct wm8974_priv *wm8974)
718{
719 int ret;
720 struct snd_soc_codec *codec = &wm8974->codec;
721
722 if (wm8974_codec) {
723 dev_err(codec->dev, "Another WM8974 is registered\n");
724 return -EINVAL;
725 }
726
727 mutex_init(&codec->mutex);
728 INIT_LIST_HEAD(&codec->dapm_widgets);
729 INIT_LIST_HEAD(&codec->dapm_paths);
730
731 codec->private_data = wm8974;
732 codec->name = "WM8974";
733 codec->owner = THIS_MODULE;
734 codec->read = wm8974_read_reg_cache;
735 codec->write = wm8974_write;
736 codec->bias_level = SND_SOC_BIAS_OFF;
737 codec->set_bias_level = wm8974_set_bias_level;
738 codec->dai = &wm8974_dai;
739 codec->num_dai = 1;
740 codec->reg_cache_size = WM8974_CACHEREGNUM;
741 codec->reg_cache = &wm8974->reg_cache;
742
743 memcpy(codec->reg_cache, wm8974_reg, sizeof(wm8974_reg));
744
745 ret = wm8974_reset(codec);
746 if (ret < 0) {
747 dev_err(codec->dev, "Failed to issue reset\n");
748 return ret;
749 }
750
751 wm8974_dai.dev = codec->dev;
752
753 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
754
755 wm8974_codec = codec;
756
757 ret = snd_soc_register_codec(codec);
758 if (ret != 0) {
759 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
760 return ret;
761 }
762
763 ret = snd_soc_register_dai(&wm8974_dai);
764 if (ret != 0) {
765 dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
766 snd_soc_unregister_codec(codec);
767 return ret;
768 }
769
770 return 0;
771}
772
773static __devexit void wm8974_unregister(struct wm8974_priv *wm8974)
774{
775 wm8974_set_bias_level(&wm8974->codec, SND_SOC_BIAS_OFF);
776 snd_soc_unregister_dai(&wm8974_dai);
777 snd_soc_unregister_codec(&wm8974->codec);
778 kfree(wm8974);
779 wm8974_codec = NULL;
780}
781
782static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
783 const struct i2c_device_id *id)
784{
785 struct wm8974_priv *wm8974;
786 struct snd_soc_codec *codec;
787
788 wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
789 if (wm8974 == NULL)
790 return -ENOMEM;
791
792 codec = &wm8974->codec;
793 codec->hw_write = (hw_write_t)i2c_master_send;
794
795 i2c_set_clientdata(i2c, wm8974);
796 codec->control_data = i2c;
797
798 codec->dev = &i2c->dev;
799
800 return wm8974_register(wm8974);
801}
802
803static __devexit int wm8974_i2c_remove(struct i2c_client *client)
804{
805 struct wm8974_priv *wm8974 = i2c_get_clientdata(client);
806 wm8974_unregister(wm8974);
807 return 0;
808}
809
810static const struct i2c_device_id wm8974_i2c_id[] = {
811 { "wm8974", 0 },
812 { }
813};
814MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
815
816static struct i2c_driver wm8974_i2c_driver = {
817 .driver = {
818 .name = "WM8974 I2C Codec",
819 .owner = THIS_MODULE,
820 },
821 .probe = wm8974_i2c_probe,
822 .remove = __devexit_p(wm8974_i2c_remove),
823 .id_table = wm8974_i2c_id,
824};
825
Mark Brown0a1bf552009-05-23 11:18:41 +0100826static int __init wm8974_modinit(void)
827{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100828 return i2c_add_driver(&wm8974_i2c_driver);
Mark Brown0a1bf552009-05-23 11:18:41 +0100829}
830module_init(wm8974_modinit);
831
832static void __exit wm8974_exit(void)
833{
Mark Brown4fcbbb62009-05-23 12:27:03 +0100834 i2c_del_driver(&wm8974_i2c_driver);
Mark Brown0a1bf552009-05-23 11:18:41 +0100835}
836module_exit(wm8974_exit);
837
838MODULE_DESCRIPTION("ASoC WM8974 driver");
839MODULE_AUTHOR("Liam Girdwood");
840MODULE_LICENSE("GPL");