blob: b5f1a707cd76646e066f4cdc7405b4a20e386e22 [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 *
6 * Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
7 *
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 +010031struct snd_soc_codec_device soc_codec_dev_wm8974;
32
33/*
34 * wm8974 register cache
35 * We can't read the WM8974 register space when we are
36 * using 2 wire for device control, so we cache them instead.
37 */
38static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
Mark Brown1a55b3f2009-05-23 11:31:40 +010039 0x0000, 0x0000, 0x0000, 0x0000,
40 0x0050, 0x0000, 0x0140, 0x0000,
41 0x0000, 0x0000, 0x0000, 0x00ff,
42 0x0000, 0x0000, 0x0100, 0x00ff,
43 0x0000, 0x0000, 0x012c, 0x002c,
44 0x002c, 0x002c, 0x002c, 0x0000,
45 0x0032, 0x0000, 0x0000, 0x0000,
46 0x0000, 0x0000, 0x0000, 0x0000,
47 0x0038, 0x000b, 0x0032, 0x0000,
48 0x0008, 0x000c, 0x0093, 0x00e9,
49 0x0000, 0x0000, 0x0000, 0x0000,
50 0x0003, 0x0010, 0x0000, 0x0000,
51 0x0000, 0x0002, 0x0000, 0x0000,
52 0x0000, 0x0000, 0x0039, 0x0000,
53 0x0000,
Mark Brown0a1bf552009-05-23 11:18:41 +010054};
55
56/*
57 * read wm8974 register cache
58 */
Mark Brown1a55b3f2009-05-23 11:31:40 +010059static inline unsigned int wm8974_read_reg_cache(struct snd_soc_codec *codec,
Mark Brown0a1bf552009-05-23 11:18:41 +010060 unsigned int reg)
61{
62 u16 *cache = codec->reg_cache;
63 if (reg == WM8974_RESET)
64 return 0;
65 if (reg >= WM8974_CACHEREGNUM)
66 return -1;
67 return cache[reg];
68}
69
70/*
71 * write wm8974 register cache
72 */
73static inline void wm8974_write_reg_cache(struct snd_soc_codec *codec,
74 u16 reg, unsigned int value)
75{
76 u16 *cache = codec->reg_cache;
77 if (reg >= WM8974_CACHEREGNUM)
78 return;
79 cache[reg] = value;
80}
81
82/*
83 * write to the WM8974 register space
84 */
85static int wm8974_write(struct snd_soc_codec *codec, unsigned int reg,
86 unsigned int value)
87{
88 u8 data[2];
89
90 /* data is
91 * D15..D9 WM8974 register offset
92 * D8...D0 register data
93 */
94 data[0] = (reg << 1) | ((value >> 8) & 0x0001);
95 data[1] = value & 0x00ff;
96
Mark Brown1a55b3f2009-05-23 11:31:40 +010097 wm8974_write_reg_cache(codec, reg, value);
Mark Brown0a1bf552009-05-23 11:18:41 +010098 if (codec->hw_write(codec->control_data, data, 2) == 2)
99 return 0;
100 else
101 return -EIO;
102}
103
104#define wm8974_reset(c) wm8974_write(c, WM8974_RESET, 0)
105
106static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
107static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
108static const char *wm8974_eqmode[] = {"Capture", "Playback" };
109static const char *wm8974_bw[] = {"Narrow", "Wide" };
110static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
111static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
112static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
113static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
114static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
115static const char *wm8974_alc[] = {"ALC", "Limiter" };
116
117static const struct soc_enum wm8974_enum[] = {
118 SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
119 SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
120 SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp),
121 SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode),
122
123 SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1),
124 SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw),
125 SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2),
126 SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw),
127
128 SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3),
129 SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw),
130 SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4),
131 SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw),
132
133 SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5),
134 SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc),
135};
136
137static const struct snd_kcontrol_new wm8974_snd_controls[] = {
138
139SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
140
141SOC_ENUM("DAC Companding", wm8974_enum[1]),
142SOC_ENUM("ADC Companding", wm8974_enum[0]),
143
144SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
145SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
146
147SOC_SINGLE("PCM Volume", WM8974_DACVOL, 0, 127, 0),
148
149SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
150SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
151SOC_SINGLE("ADC Inversion Switch", WM8974_COMP, 0, 1, 0),
152
153SOC_SINGLE("Capture Volume", WM8974_ADCVOL, 0, 127, 0),
154
155SOC_ENUM("Equaliser Function", wm8974_enum[3]),
156SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
157SOC_SINGLE("EQ1 Volume", WM8974_EQ1, 0, 31, 1),
158
159SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
160SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
161SOC_SINGLE("EQ2 Volume", WM8974_EQ2, 0, 31, 1),
162
163SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
164SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
165SOC_SINGLE("EQ3 Volume", WM8974_EQ3, 0, 31, 1),
166
167SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
168SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
169SOC_SINGLE("EQ4 Volume", WM8974_EQ4, 0, 31, 1),
170
171SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
172SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
173SOC_SINGLE("EQ5 Volume", WM8974_EQ5, 0, 31, 1),
174
175SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0),
176SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0),
177SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0),
178
179SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0),
180SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0),
181
182SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0),
183SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0),
184SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0),
185
186SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0),
187SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0),
188SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0),
189
190SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
191SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0),
192SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0),
193
194SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0),
195SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0),
196
197SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0),
198SOC_SINGLE("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0),
199
200SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0),
201SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1),
202SOC_SINGLE("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0),
203
204SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
205SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 0),
206};
207
208/* add non dapm controls */
209static int wm8974_add_controls(struct snd_soc_codec *codec)
210{
211 int err, i;
212
213 for (i = 0; i < ARRAY_SIZE(wm8974_snd_controls); i++) {
214 err = snd_ctl_add(codec->card,
Mark Brown1a55b3f2009-05-23 11:31:40 +0100215 snd_soc_cnew(&wm8974_snd_controls[i],
216 codec, NULL));
Mark Brown0a1bf552009-05-23 11:18:41 +0100217 if (err < 0)
218 return err;
219 }
220
221 return 0;
222}
223
224/* Speaker Output Mixer */
225static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
226SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
227SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
228SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1),
229};
230
231/* Mono Output Mixer */
232static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
233SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
234SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
235SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 1),
236};
237
238/* AUX Input boost vol */
239static const struct snd_kcontrol_new wm8974_aux_boost_controls =
240SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
241
242/* Mic Input boost vol */
243static const struct snd_kcontrol_new wm8974_mic_boost_controls =
244SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
245
246/* Capture boost switch */
247static const struct snd_kcontrol_new wm8974_capture_boost_controls =
248SOC_DAPM_SINGLE("Capture Boost Switch", WM8974_INPPGA, 6, 1, 0);
249
250/* Aux In to PGA */
251static const struct snd_kcontrol_new wm8974_aux_capture_boost_controls =
252SOC_DAPM_SINGLE("Aux Capture Boost Switch", WM8974_INPPGA, 2, 1, 0);
253
254/* Mic P In to PGA */
255static const struct snd_kcontrol_new wm8974_micp_capture_boost_controls =
256SOC_DAPM_SINGLE("Mic P Capture Boost Switch", WM8974_INPPGA, 0, 1, 0);
257
258/* Mic N In to PGA */
259static const struct snd_kcontrol_new wm8974_micn_capture_boost_controls =
260SOC_DAPM_SINGLE("Mic N Capture Boost Switch", WM8974_INPPGA, 1, 1, 0);
261
262static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
263SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
264 &wm8974_speaker_mixer_controls[0],
265 ARRAY_SIZE(wm8974_speaker_mixer_controls)),
266SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
267 &wm8974_mono_mixer_controls[0],
268 ARRAY_SIZE(wm8974_mono_mixer_controls)),
269SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
270SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER3, 0, 0),
271SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
272SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
273SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
274SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
275SND_SOC_DAPM_PGA("Mic PGA", WM8974_POWER2, 2, 0, NULL, 0),
276
277SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
278 &wm8974_aux_boost_controls, 1),
279SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
280 &wm8974_mic_boost_controls, 1),
281SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
282 &wm8974_capture_boost_controls),
283
284SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0, NULL, 0),
285
286SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
287
288SND_SOC_DAPM_INPUT("MICN"),
289SND_SOC_DAPM_INPUT("MICP"),
290SND_SOC_DAPM_INPUT("AUX"),
291SND_SOC_DAPM_OUTPUT("MONOOUT"),
292SND_SOC_DAPM_OUTPUT("SPKOUTP"),
293SND_SOC_DAPM_OUTPUT("SPKOUTN"),
294};
295
296static const struct snd_soc_dapm_route audio_map[] = {
297 /* Mono output mixer */
298 {"Mono Mixer", "PCM Playback Switch", "DAC"},
299 {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
300 {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
301
302 /* Speaker output mixer */
303 {"Speaker Mixer", "PCM Playback Switch", "DAC"},
304 {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
305 {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
306
307 /* Outputs */
308 {"Mono Out", NULL, "Mono Mixer"},
309 {"MONOOUT", NULL, "Mono Out"},
310 {"SpkN Out", NULL, "Speaker Mixer"},
311 {"SpkP Out", NULL, "Speaker Mixer"},
312 {"SPKOUTN", NULL, "SpkN Out"},
313 {"SPKOUTP", NULL, "SpkP Out"},
314
315 /* Boost Mixer */
316 {"Boost Mixer", NULL, "ADC"},
317 {"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
318 {"Aux Boost", "Aux Volume", "Boost Mixer"},
319 {"Capture Boost", "Capture Switch", "Boost Mixer"},
320 {"Mic Boost", "Mic Volume", "Boost Mixer"},
321
322 /* Inputs */
323 {"MICP", NULL, "Mic Boost"},
324 {"MICN", NULL, "Mic PGA"},
325 {"Mic PGA", NULL, "Capture Boost"},
326 {"AUX", NULL, "Aux Input"},
327};
328
329static int wm8974_add_widgets(struct snd_soc_codec *codec)
330{
331 snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets,
332 ARRAY_SIZE(wm8974_dapm_widgets));
333
334 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
335
336 snd_soc_dapm_new_widgets(codec);
337 return 0;
338}
339
340struct pll_ {
341 unsigned int in_hz, out_hz;
342 unsigned int pre:4; /* prescale - 1 */
343 unsigned int n:4;
344 unsigned int k;
345};
346
347static struct pll_ pll[] = {
Mark Brown1a55b3f2009-05-23 11:31:40 +0100348 { 12000000, 11289600, 0, 7, 0x86c220 },
349 { 12000000, 12288000, 0, 8, 0x3126e8 },
350 { 13000000, 11289600, 0, 6, 0xf28bd4 },
351 { 13000000, 12288000, 0, 7, 0x8fd525 },
352 { 12288000, 11289600, 0, 7, 0x59999a },
353 { 11289600, 12288000, 0, 8, 0x80dee9 },
354 { 25000000, 11289600, 1, 7, 0x39B024 },
355 { 25000000, 24576000, 1, 7, 0xdd4413 }
Mark Brown0a1bf552009-05-23 11:18:41 +0100356};
357
358static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
359 int pll_id, unsigned int freq_in, unsigned int freq_out)
360{
361 struct snd_soc_codec *codec = codec_dai->codec;
362 int i;
363 u16 reg;
364
Mark Brown1a55b3f2009-05-23 11:31:40 +0100365 if (freq_in == 0 || freq_out == 0) {
Mark Brown0a1bf552009-05-23 11:18:41 +0100366 reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
367 wm8974_write(codec, WM8974_POWER1, reg & 0x1df);
368 return 0;
369 }
370
Mark Brown1a55b3f2009-05-23 11:31:40 +0100371 for (i = 0; i < ARRAY_SIZE(pll); i++) {
Mark Brown0a1bf552009-05-23 11:18:41 +0100372 if (freq_in == pll[i].in_hz && freq_out == pll[i].out_hz) {
Mark Brown1a55b3f2009-05-23 11:31:40 +0100373 wm8974_write(codec, WM8974_PLLN,
374 (pll[i].pre << 4) | pll[i].n);
Mark Brown0a1bf552009-05-23 11:18:41 +0100375 wm8974_write(codec, WM8974_PLLK1, pll[i].k >> 18);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100376 wm8974_write(codec, WM8974_PLLK2,
377 (pll[i].k >> 9) & 0x1ff);
Mark Brown0a1bf552009-05-23 11:18:41 +0100378 wm8974_write(codec, WM8974_PLLK3, pll[i].k & 0x1ff);
379 reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
380 wm8974_write(codec, WM8974_POWER1, reg | 0x020);
381 return 0;
382 }
383 }
Mark Brown1a55b3f2009-05-23 11:31:40 +0100384
Mark Brown0a1bf552009-05-23 11:18:41 +0100385 return -EINVAL;
386}
387
388/*
389 * Configure WM8974 clock dividers.
390 */
391static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
392 int div_id, int div)
393{
394 struct snd_soc_codec *codec = codec_dai->codec;
395 u16 reg;
396
397 switch (div_id) {
398 case WM8974_OPCLKDIV:
Mark Brown1a55b3f2009-05-23 11:31:40 +0100399 reg = wm8974_read_reg_cache(codec, WM8974_GPIO) & 0x1cf;
Mark Brown0a1bf552009-05-23 11:18:41 +0100400 wm8974_write(codec, WM8974_GPIO, reg | div);
401 break;
402 case WM8974_MCLKDIV:
403 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x11f;
404 wm8974_write(codec, WM8974_CLOCK, reg | div);
405 break;
406 case WM8974_ADCCLK:
407 reg = wm8974_read_reg_cache(codec, WM8974_ADC) & 0x1f7;
408 wm8974_write(codec, WM8974_ADC, reg | div);
409 break;
410 case WM8974_DACCLK:
411 reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0x1f7;
412 wm8974_write(codec, WM8974_DAC, reg | div);
413 break;
414 case WM8974_BCLKDIV:
415 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1e3;
416 wm8974_write(codec, WM8974_CLOCK, reg | div);
417 break;
418 default:
419 return -EINVAL;
420 }
421
422 return 0;
423}
424
425static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
426 unsigned int fmt)
427{
428 struct snd_soc_codec *codec = codec_dai->codec;
429 u16 iface = 0;
430 u16 clk = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1fe;
431
432 /* set master/slave audio interface */
433 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
434 case SND_SOC_DAIFMT_CBM_CFM:
435 clk |= 0x0001;
436 break;
437 case SND_SOC_DAIFMT_CBS_CFS:
438 break;
439 default:
440 return -EINVAL;
441 }
442
443 /* interface format */
444 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
445 case SND_SOC_DAIFMT_I2S:
446 iface |= 0x0010;
447 break;
448 case SND_SOC_DAIFMT_RIGHT_J:
449 break;
450 case SND_SOC_DAIFMT_LEFT_J:
451 iface |= 0x0008;
452 break;
453 case SND_SOC_DAIFMT_DSP_A:
454 iface |= 0x00018;
455 break;
456 default:
457 return -EINVAL;
458 }
459
460 /* clock inversion */
461 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
462 case SND_SOC_DAIFMT_NB_NF:
463 break;
464 case SND_SOC_DAIFMT_IB_IF:
465 iface |= 0x0180;
466 break;
467 case SND_SOC_DAIFMT_IB_NF:
468 iface |= 0x0100;
469 break;
470 case SND_SOC_DAIFMT_NB_IF:
471 iface |= 0x0080;
472 break;
473 default:
474 return -EINVAL;
475 }
476
477 wm8974_write(codec, WM8974_IFACE, iface);
478 wm8974_write(codec, WM8974_CLOCK, clk);
479 return 0;
480}
481
482static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
483 struct snd_pcm_hw_params *params,
484 struct snd_soc_dai *dai)
485{
486 struct snd_soc_codec *codec = dai->codec;
487 u16 iface = wm8974_read_reg_cache(codec, WM8974_IFACE) & 0x19f;
488 u16 adn = wm8974_read_reg_cache(codec, WM8974_ADD) & 0x1f1;
489
490 /* bit size */
491 switch (params_format(params)) {
492 case SNDRV_PCM_FORMAT_S16_LE:
493 break;
494 case SNDRV_PCM_FORMAT_S20_3LE:
495 iface |= 0x0020;
496 break;
497 case SNDRV_PCM_FORMAT_S24_LE:
498 iface |= 0x0040;
499 break;
500 case SNDRV_PCM_FORMAT_S32_LE:
501 iface |= 0x0060;
502 break;
503 }
504
505 /* filter coefficient */
506 switch (params_rate(params)) {
507 case SNDRV_PCM_RATE_8000:
508 adn |= 0x5 << 1;
509 break;
510 case SNDRV_PCM_RATE_11025:
511 adn |= 0x4 << 1;
512 break;
513 case SNDRV_PCM_RATE_16000:
514 adn |= 0x3 << 1;
515 break;
516 case SNDRV_PCM_RATE_22050:
517 adn |= 0x2 << 1;
518 break;
519 case SNDRV_PCM_RATE_32000:
520 adn |= 0x1 << 1;
521 break;
522 case SNDRV_PCM_RATE_44100:
523 break;
524 }
525
526 wm8974_write(codec, WM8974_IFACE, iface);
527 wm8974_write(codec, WM8974_ADD, adn);
528 return 0;
529}
530
531static int wm8974_mute(struct snd_soc_dai *dai, int mute)
532{
533 struct snd_soc_codec *codec = dai->codec;
534 u16 mute_reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0xffbf;
535
Mark Brown1a55b3f2009-05-23 11:31:40 +0100536 if (mute)
Mark Brown0a1bf552009-05-23 11:18:41 +0100537 wm8974_write(codec, WM8974_DAC, mute_reg | 0x40);
538 else
539 wm8974_write(codec, WM8974_DAC, mute_reg);
540 return 0;
541}
542
543/* liam need to make this lower power with dapm */
544static int wm8974_set_bias_level(struct snd_soc_codec *codec,
545 enum snd_soc_bias_level level)
546{
547 switch (level) {
548 case SND_SOC_BIAS_ON:
549 wm8974_write(codec, WM8974_POWER1, 0x1ff);
550 wm8974_write(codec, WM8974_POWER2, 0x1ff);
551 wm8974_write(codec, WM8974_POWER3, 0x1ff);
552 break;
553 case SND_SOC_BIAS_PREPARE:
554 break;
555 case SND_SOC_BIAS_STANDBY:
556 break;
557 case SND_SOC_BIAS_OFF:
558 wm8974_write(codec, WM8974_POWER1, 0x0);
559 wm8974_write(codec, WM8974_POWER2, 0x0);
560 wm8974_write(codec, WM8974_POWER3, 0x0);
561 break;
562 }
563 codec->bias_level = level;
564 return 0;
565}
566
Mark Brown1a55b3f2009-05-23 11:31:40 +0100567#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
Mark Brown0a1bf552009-05-23 11:18:41 +0100568
569#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
570 SNDRV_PCM_FMTBIT_S24_LE)
571
572static struct snd_soc_dai_ops wm8974_ops = {
573 .hw_params = wm8974_pcm_hw_params,
574 .digital_mute = wm8974_mute,
575 .set_fmt = wm8974_set_dai_fmt,
576 .set_clkdiv = wm8974_set_dai_clkdiv,
577 .set_pll = wm8974_set_dai_pll,
578};
579
580struct snd_soc_dai wm8974_dai = {
581 .name = "WM8974 HiFi",
582 .playback = {
583 .stream_name = "Playback",
584 .channels_min = 1,
585 .channels_max = 1,
586 .rates = WM8974_RATES,
587 .formats = WM8974_FORMATS,},
588 .capture = {
589 .stream_name = "Capture",
590 .channels_min = 1,
591 .channels_max = 1,
592 .rates = WM8974_RATES,
593 .formats = WM8974_FORMATS,},
594 .ops = &wm8974_ops,
595};
596EXPORT_SYMBOL_GPL(wm8974_dai);
597
598static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
599{
600 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
601 struct snd_soc_codec *codec = socdev->card->codec;
602
603 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
604 return 0;
605}
606
607static int wm8974_resume(struct platform_device *pdev)
608{
609 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
610 struct snd_soc_codec *codec = socdev->card->codec;
611 int i;
612 u8 data[2];
613 u16 *cache = codec->reg_cache;
614
615 /* Sync reg_cache with the hardware */
616 for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
617 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
618 data[1] = cache[i] & 0x00ff;
619 codec->hw_write(codec->control_data, data, 2);
620 }
621 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
622 wm8974_set_bias_level(codec, codec->suspend_bias_level);
623 return 0;
624}
625
626/*
627 * initialise the WM8974 driver
628 * register the mixer and dsp interfaces with the kernel
629 */
630static int wm8974_init(struct snd_soc_device *socdev)
631{
632 struct snd_soc_codec *codec = socdev->card->codec;
633 int ret = 0;
634
635 codec->name = "WM8974";
636 codec->owner = THIS_MODULE;
637 codec->read = wm8974_read_reg_cache;
638 codec->write = wm8974_write;
639 codec->set_bias_level = wm8974_set_bias_level;
640 codec->dai = &wm8974_dai;
641 codec->num_dai = 1;
642 codec->reg_cache_size = ARRAY_SIZE(wm8974_reg);
643 codec->reg_cache = kmemdup(wm8974_reg, sizeof(wm8974_reg), GFP_KERNEL);
644
645 if (codec->reg_cache == NULL)
646 return -ENOMEM;
647
648 wm8974_reset(codec);
649
650 /* register pcms */
651 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
Mark Brown1a55b3f2009-05-23 11:31:40 +0100652 if (ret < 0) {
Mark Brown0a1bf552009-05-23 11:18:41 +0100653 printk(KERN_ERR "wm8974: failed to create pcms\n");
654 goto pcm_err;
655 }
656
657 /* power on device */
658 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
659 wm8974_add_controls(codec);
660 wm8974_add_widgets(codec);
661 ret = snd_soc_init_card(socdev);
662 if (ret < 0) {
663 printk(KERN_ERR "wm8974: failed to register card\n");
664 goto card_err;
665 }
666 return ret;
667
668card_err:
669 snd_soc_free_pcms(socdev);
670 snd_soc_dapm_free(socdev);
671pcm_err:
672 kfree(codec->reg_cache);
673 return ret;
674}
675
676static struct snd_soc_device *wm8974_socdev;
677
678#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
679
680/*
681 * WM8974 2 wire address is 0x1a
682 */
683#define I2C_DRIVERID_WM8974 0xfefe /* liam - need a proper id */
684
685static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
686
687/* Magic definition of all other variables and things */
688I2C_CLIENT_INSMOD;
689
690static struct i2c_driver wm8974_i2c_driver;
691static struct i2c_client client_template;
692
693/* If the i2c layer weren't so broken, we could pass this kind of data
694 around */
695
696static int wm8974_codec_probe(struct i2c_adapter *adap, int addr, int kind)
697{
698 struct snd_soc_device *socdev = wm8974_socdev;
699 struct wm8974_setup_data *setup = socdev->codec_data;
700 struct snd_soc_codec *codec = socdev->card->codec;
701 struct i2c_client *i2c;
702 int ret;
703
704 if (addr != setup->i2c_address)
705 return -ENODEV;
706
707 client_template.adapter = adap;
708 client_template.addr = addr;
709
710 i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
711 if (i2c == NULL) {
712 kfree(codec);
713 return -ENOMEM;
714 }
715 i2c_set_clientdata(i2c, codec);
716 codec->control_data = i2c;
717
718 ret = i2c_attach_client(i2c);
719 if (ret < 0) {
720 pr_err("failed to attach codec at addr %x\n", addr);
721 goto err;
722 }
723
724 ret = wm8974_init(socdev);
725 if (ret < 0) {
726 pr_err("failed to initialise WM8974\n");
727 goto err;
728 }
729 return ret;
730
731err:
732 kfree(codec);
733 kfree(i2c);
734 return ret;
735}
736
737static int wm8974_i2c_detach(struct i2c_client *client)
738{
739 struct snd_soc_codec *codec = i2c_get_clientdata(client);
740 i2c_detach_client(client);
741 kfree(codec->reg_cache);
742 kfree(client);
743 return 0;
744}
745
746static int wm8974_i2c_attach(struct i2c_adapter *adap)
747{
748 return i2c_probe(adap, &addr_data, wm8974_codec_probe);
749}
750
751/* corgi i2c codec control layer */
752static struct i2c_driver wm8974_i2c_driver = {
753 .driver = {
754 .name = "WM8974 I2C Codec",
755 .owner = THIS_MODULE,
756 },
757 .id = I2C_DRIVERID_WM8974,
758 .attach_adapter = wm8974_i2c_attach,
759 .detach_client = wm8974_i2c_detach,
760 .command = NULL,
761};
762
763static struct i2c_client client_template = {
764 .name = "WM8974",
765 .driver = &wm8974_i2c_driver,
766};
767#endif
768
769static int wm8974_probe(struct platform_device *pdev)
770{
771 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
772 struct wm8974_setup_data *setup;
773 struct snd_soc_codec *codec;
774 int ret = 0;
775
Mark Brown0a1bf552009-05-23 11:18:41 +0100776 setup = socdev->codec_data;
777 codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
778 if (codec == NULL)
779 return -ENOMEM;
780
781 socdev->card->codec = codec;
782 mutex_init(&codec->mutex);
783 INIT_LIST_HEAD(&codec->dapm_widgets);
784 INIT_LIST_HEAD(&codec->dapm_paths);
785
786 wm8974_socdev = socdev;
787#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
788 if (setup->i2c_address) {
789 normal_i2c[0] = setup->i2c_address;
790 codec->hw_write = (hw_write_t)i2c_master_send;
791 ret = i2c_add_driver(&wm8974_i2c_driver);
792 if (ret != 0)
793 printk(KERN_ERR "can't add i2c driver");
794 }
795#else
796 /* Add other interfaces here */
797#endif
798 return ret;
799}
800
801/* power down chip */
802static int wm8974_remove(struct platform_device *pdev)
803{
804 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
805 struct snd_soc_codec *codec = socdev->card->codec;
806
807 if (codec->control_data)
808 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
809
810 snd_soc_free_pcms(socdev);
811 snd_soc_dapm_free(socdev);
812#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
813 i2c_del_driver(&wm8974_i2c_driver);
814#endif
815 kfree(codec);
816
817 return 0;
818}
819
820struct snd_soc_codec_device soc_codec_dev_wm8974 = {
821 .probe = wm8974_probe,
822 .remove = wm8974_remove,
823 .suspend = wm8974_suspend,
824 .resume = wm8974_resume,
825};
826EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
827
828static int __init wm8974_modinit(void)
829{
830 return snd_soc_register_dai(&wm8974_dai);
831}
832module_init(wm8974_modinit);
833
834static void __exit wm8974_exit(void)
835{
836 snd_soc_unregister_dai(&wm8974_dai);
837}
838module_exit(wm8974_exit);
839
840MODULE_DESCRIPTION("ASoC WM8974 driver");
841MODULE_AUTHOR("Liam Girdwood");
842MODULE_LICENSE("GPL");