ASoC: WM8974 DAPM cleanups
[deliverable/linux.git] / sound / soc / codecs / wm8974.c
CommitLineData
0a1bf553
MB
1/*
2 * wm8974.c -- WM8974 ALSA Soc Audio driver
3 *
8b83a193 4 * Copyright 2006-2009 Wolfson Microelectronics PLC.
0a1bf553 5 *
4fcbbb67 6 * Author: Liam Girdwood <linux@wolfsonmicro.com>
0a1bf553
MB
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>
a5f8d2f1 28#include <sound/tlv.h>
0a1bf553
MB
29
30#include "wm8974.h"
31
0a1bf553 32static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
1a55b3f6
MB
33 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,
0a1bf553
MB
48};
49
df1ef7a3
MB
50#define WM8974_POWER1_BIASEN 0x08
51#define WM8974_POWER1_BUFIOEN 0x10
52
4fcbbb67
MB
53struct wm8974_priv {
54 struct snd_soc_codec codec;
55 u16 reg_cache[WM8974_CACHEREGNUM];
56};
57
58static struct snd_soc_codec *wm8974_codec;
59
0a1bf553
MB
60/*
61 * read wm8974 register cache
62 */
1a55b3f6 63static inline unsigned int wm8974_read_reg_cache(struct snd_soc_codec *codec,
0a1bf553
MB
64 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
1a55b3f6 101 wm8974_write_reg_cache(codec, reg, value);
0a1bf553
MB
102 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
8a123ee2
MB
141static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" };
142
143static const struct soc_enum wm8974_auxmode =
144 SOC_ENUM_SINGLE(WM8974_INPUT, 3, 2, wm8974_auxmode_text);
145
a5f8d2f1
MB
146static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
147static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
148static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
149static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
150
0a1bf553
MB
151static const struct snd_kcontrol_new wm8974_snd_controls[] = {
152
153SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
154
155SOC_ENUM("DAC Companding", wm8974_enum[1]),
156SOC_ENUM("ADC Companding", wm8974_enum[0]),
157
158SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
159SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
160
a5f8d2f1 161SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv),
0a1bf553
MB
162
163SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
164SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
165SOC_SINGLE("ADC Inversion Switch", WM8974_COMP, 0, 1, 0),
166
a5f8d2f1 167SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL, 0, 255, 0, digital_tlv),
0a1bf553
MB
168
169SOC_ENUM("Equaliser Function", wm8974_enum[3]),
170SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
a5f8d2f1 171SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1, 0, 24, 1, eq_tlv),
0a1bf553
MB
172
173SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
174SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
a5f8d2f1 175SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2, 0, 24, 1, eq_tlv),
0a1bf553
MB
176
177SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
178SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
a5f8d2f1 179SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3, 0, 24, 1, eq_tlv),
0a1bf553
MB
180
181SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
182SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
a5f8d2f1 183SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4, 0, 24, 1, eq_tlv),
0a1bf553
MB
184
185SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
186SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
a5f8d2f1 187SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5, 0, 24, 1, eq_tlv),
0a1bf553
MB
188
189SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0),
190SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0),
191SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0),
192
193SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0),
194SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0),
195
196SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0),
197SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0),
198SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0),
199
200SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0),
201SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0),
202SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0),
203
204SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
205SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0),
206SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0),
207
208SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0),
209SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0),
210
211SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0),
a5f8d2f1 212SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0, inpga_tlv),
0a1bf553
MB
213
214SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0),
215SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1),
8a123ee2
MB
216SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0, spk_tlv),
217
218SOC_ENUM("Aux Mode", wm8974_auxmode),
0a1bf553
MB
219
220SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
8a123ee2 221SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1),
0a1bf553
MB
222};
223
0a1bf553
MB
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),
8a123ee2
MB
235SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0),
236};
237
238/* Boost mixer */
239static const struct snd_kcontrol_new wm8974_boost_mixer[] = {
240SOC_DAPM_SINGLE("Aux Switch", WM8974_INPPGA, 6, 1, 0),
241};
242
243/* Input PGA */
244static const struct snd_kcontrol_new wm8974_inpga[] = {
245SOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0),
246SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0),
247SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0),
0a1bf553
MB
248};
249
250/* AUX Input boost vol */
251static const struct snd_kcontrol_new wm8974_aux_boost_controls =
252SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
253
254/* Mic Input boost vol */
255static const struct snd_kcontrol_new wm8974_mic_boost_controls =
256SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
257
0a1bf553
MB
258static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
259SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
260 &wm8974_speaker_mixer_controls[0],
261 ARRAY_SIZE(wm8974_speaker_mixer_controls)),
262SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
263 &wm8974_mono_mixer_controls[0],
264 ARRAY_SIZE(wm8974_mono_mixer_controls)),
265SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
8a123ee2 266SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0),
0a1bf553
MB
267SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
268SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
269SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
270SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
0a1bf553 271
8a123ee2
MB
272SND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga,
273 ARRAY_SIZE(wm8974_inpga)),
274SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0,
275 wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)),
0a1bf553
MB
276
277SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
278
279SND_SOC_DAPM_INPUT("MICN"),
280SND_SOC_DAPM_INPUT("MICP"),
281SND_SOC_DAPM_INPUT("AUX"),
282SND_SOC_DAPM_OUTPUT("MONOOUT"),
283SND_SOC_DAPM_OUTPUT("SPKOUTP"),
284SND_SOC_DAPM_OUTPUT("SPKOUTN"),
285};
286
287static const struct snd_soc_dapm_route audio_map[] = {
288 /* Mono output mixer */
289 {"Mono Mixer", "PCM Playback Switch", "DAC"},
290 {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
291 {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
292
293 /* Speaker output mixer */
294 {"Speaker Mixer", "PCM Playback Switch", "DAC"},
295 {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
296 {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
297
298 /* Outputs */
299 {"Mono Out", NULL, "Mono Mixer"},
300 {"MONOOUT", NULL, "Mono Out"},
301 {"SpkN Out", NULL, "Speaker Mixer"},
302 {"SpkP Out", NULL, "Speaker Mixer"},
303 {"SPKOUTN", NULL, "SpkN Out"},
304 {"SPKOUTP", NULL, "SpkP Out"},
305
306 /* Boost Mixer */
8a123ee2
MB
307 {"ADC", NULL, "Boost Mixer"},
308 {"Boost Mixer", "Aux Switch", "Aux Input"},
309 {"Boost Mixer", NULL, "Input PGA"},
310 {"Boost Mixer", NULL, "MICP"},
311
312 /* Input PGA */
313 {"Input PGA", "Aux Switch", "Aux Input"},
314 {"Input PGA", "MicN Switch", "MICN"},
315 {"Input PGA", "MicP Switch", "MICP"},
0a1bf553
MB
316
317 /* Inputs */
8a123ee2 318 {"Aux Input", NULL, "AUX"},
0a1bf553
MB
319};
320
321static int wm8974_add_widgets(struct snd_soc_codec *codec)
322{
323 snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets,
324 ARRAY_SIZE(wm8974_dapm_widgets));
325
326 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
327
328 snd_soc_dapm_new_widgets(codec);
329 return 0;
330}
331
332struct pll_ {
91d0c3ec 333 unsigned int pre_div:4; /* prescale - 1 */
0a1bf553
MB
334 unsigned int n:4;
335 unsigned int k;
336};
337
91d0c3ec
MB
338static struct pll_ pll_div;
339
340/* The size in bits of the pll divide multiplied by 10
341 * to allow rounding later */
342#define FIXED_PLL_SIZE ((1 << 24) * 10)
343
344static void pll_factors(unsigned int target, unsigned int source)
345{
346 unsigned long long Kpart;
347 unsigned int K, Ndiv, Nmod;
348
349 Ndiv = target / source;
350 if (Ndiv < 6) {
351 source >>= 1;
352 pll_div.pre_div = 1;
353 Ndiv = target / source;
354 } else
355 pll_div.pre_div = 0;
356
357 if ((Ndiv < 6) || (Ndiv > 12))
358 printk(KERN_WARNING
8b83a193 359 "WM8974 N value %u outwith recommended range!\n",
91d0c3ec
MB
360 Ndiv);
361
362 pll_div.n = Ndiv;
363 Nmod = target % source;
364 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
365
366 do_div(Kpart, source);
367
368 K = Kpart & 0xFFFFFFFF;
369
370 /* Check if we need to round */
371 if ((K % 10) >= 5)
372 K += 5;
373
374 /* Move down to proper range now rounding is done */
375 K /= 10;
376
377 pll_div.k = K;
378}
0a1bf553
MB
379
380static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
381 int pll_id, unsigned int freq_in, unsigned int freq_out)
382{
383 struct snd_soc_codec *codec = codec_dai->codec;
0a1bf553
MB
384 u16 reg;
385
1a55b3f6 386 if (freq_in == 0 || freq_out == 0) {
91d0c3ec
MB
387 /* Clock CODEC directly from MCLK */
388 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
389 wm8974_write(codec, WM8974_CLOCK, reg & 0x0ff);
390
391 /* Turn off PLL */
0a1bf553
MB
392 reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
393 wm8974_write(codec, WM8974_POWER1, reg & 0x1df);
394 return 0;
395 }
396
91d0c3ec
MB
397 pll_factors(freq_out*4, freq_in);
398
399 wm8974_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
400 wm8974_write(codec, WM8974_PLLK1, pll_div.k >> 18);
401 wm8974_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
402 wm8974_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
403 reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
404 wm8974_write(codec, WM8974_POWER1, reg | 0x020);
1a55b3f6 405
91d0c3ec
MB
406 /* Run CODEC from PLL instead of MCLK */
407 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
408 wm8974_write(codec, WM8974_CLOCK, reg | 0x100);
409
410 return 0;
0a1bf553
MB
411}
412
413/*
414 * Configure WM8974 clock dividers.
415 */
416static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
417 int div_id, int div)
418{
419 struct snd_soc_codec *codec = codec_dai->codec;
420 u16 reg;
421
422 switch (div_id) {
423 case WM8974_OPCLKDIV:
1a55b3f6 424 reg = wm8974_read_reg_cache(codec, WM8974_GPIO) & 0x1cf;
0a1bf553
MB
425 wm8974_write(codec, WM8974_GPIO, reg | div);
426 break;
427 case WM8974_MCLKDIV:
428 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x11f;
429 wm8974_write(codec, WM8974_CLOCK, reg | div);
430 break;
431 case WM8974_ADCCLK:
432 reg = wm8974_read_reg_cache(codec, WM8974_ADC) & 0x1f7;
433 wm8974_write(codec, WM8974_ADC, reg | div);
434 break;
435 case WM8974_DACCLK:
436 reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0x1f7;
437 wm8974_write(codec, WM8974_DAC, reg | div);
438 break;
439 case WM8974_BCLKDIV:
440 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1e3;
441 wm8974_write(codec, WM8974_CLOCK, reg | div);
442 break;
443 default:
444 return -EINVAL;
445 }
446
447 return 0;
448}
449
450static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
451 unsigned int fmt)
452{
453 struct snd_soc_codec *codec = codec_dai->codec;
454 u16 iface = 0;
455 u16 clk = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1fe;
456
457 /* set master/slave audio interface */
458 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
459 case SND_SOC_DAIFMT_CBM_CFM:
460 clk |= 0x0001;
461 break;
462 case SND_SOC_DAIFMT_CBS_CFS:
463 break;
464 default:
465 return -EINVAL;
466 }
467
468 /* interface format */
469 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
470 case SND_SOC_DAIFMT_I2S:
471 iface |= 0x0010;
472 break;
473 case SND_SOC_DAIFMT_RIGHT_J:
474 break;
475 case SND_SOC_DAIFMT_LEFT_J:
476 iface |= 0x0008;
477 break;
478 case SND_SOC_DAIFMT_DSP_A:
479 iface |= 0x00018;
480 break;
481 default:
482 return -EINVAL;
483 }
484
485 /* clock inversion */
486 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
487 case SND_SOC_DAIFMT_NB_NF:
488 break;
489 case SND_SOC_DAIFMT_IB_IF:
490 iface |= 0x0180;
491 break;
492 case SND_SOC_DAIFMT_IB_NF:
493 iface |= 0x0100;
494 break;
495 case SND_SOC_DAIFMT_NB_IF:
496 iface |= 0x0080;
497 break;
498 default:
499 return -EINVAL;
500 }
501
502 wm8974_write(codec, WM8974_IFACE, iface);
503 wm8974_write(codec, WM8974_CLOCK, clk);
504 return 0;
505}
506
507static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
508 struct snd_pcm_hw_params *params,
509 struct snd_soc_dai *dai)
510{
511 struct snd_soc_codec *codec = dai->codec;
512 u16 iface = wm8974_read_reg_cache(codec, WM8974_IFACE) & 0x19f;
513 u16 adn = wm8974_read_reg_cache(codec, WM8974_ADD) & 0x1f1;
514
515 /* bit size */
516 switch (params_format(params)) {
517 case SNDRV_PCM_FORMAT_S16_LE:
518 break;
519 case SNDRV_PCM_FORMAT_S20_3LE:
520 iface |= 0x0020;
521 break;
522 case SNDRV_PCM_FORMAT_S24_LE:
523 iface |= 0x0040;
524 break;
525 case SNDRV_PCM_FORMAT_S32_LE:
526 iface |= 0x0060;
527 break;
528 }
529
530 /* filter coefficient */
531 switch (params_rate(params)) {
532 case SNDRV_PCM_RATE_8000:
533 adn |= 0x5 << 1;
534 break;
535 case SNDRV_PCM_RATE_11025:
536 adn |= 0x4 << 1;
537 break;
538 case SNDRV_PCM_RATE_16000:
539 adn |= 0x3 << 1;
540 break;
541 case SNDRV_PCM_RATE_22050:
542 adn |= 0x2 << 1;
543 break;
544 case SNDRV_PCM_RATE_32000:
545 adn |= 0x1 << 1;
546 break;
547 case SNDRV_PCM_RATE_44100:
8b83a193 548 case SNDRV_PCM_RATE_48000:
0a1bf553
MB
549 break;
550 }
551
552 wm8974_write(codec, WM8974_IFACE, iface);
553 wm8974_write(codec, WM8974_ADD, adn);
554 return 0;
555}
556
557static int wm8974_mute(struct snd_soc_dai *dai, int mute)
558{
559 struct snd_soc_codec *codec = dai->codec;
560 u16 mute_reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0xffbf;
561
1a55b3f6 562 if (mute)
0a1bf553
MB
563 wm8974_write(codec, WM8974_DAC, mute_reg | 0x40);
564 else
565 wm8974_write(codec, WM8974_DAC, mute_reg);
566 return 0;
567}
568
569/* liam need to make this lower power with dapm */
570static int wm8974_set_bias_level(struct snd_soc_codec *codec,
571 enum snd_soc_bias_level level)
572{
df1ef7a3
MB
573 u16 power1 = wm8974_read_reg_cache(codec, WM8974_POWER1) & ~0x3;
574
0a1bf553
MB
575 switch (level) {
576 case SND_SOC_BIAS_ON:
0a1bf553 577 case SND_SOC_BIAS_PREPARE:
df1ef7a3
MB
578 power1 |= 0x1; /* VMID 50k */
579 wm8974_write(codec, WM8974_POWER1, power1);
0a1bf553 580 break;
df1ef7a3 581
0a1bf553 582 case SND_SOC_BIAS_STANDBY:
df1ef7a3
MB
583 power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
584
585 if (codec->bias_level == SND_SOC_BIAS_OFF) {
586 /* Initial cap charge at VMID 5k */
587 wm8974_write(codec, WM8974_POWER1, power1 | 0x3);
588 mdelay(100);
589 }
590
591 power1 |= 0x2; /* VMID 500k */
592 wm8974_write(codec, WM8974_POWER1, power1);
0a1bf553 593 break;
df1ef7a3 594
0a1bf553 595 case SND_SOC_BIAS_OFF:
df1ef7a3
MB
596 wm8974_write(codec, WM8974_POWER1, 0);
597 wm8974_write(codec, WM8974_POWER2, 0);
598 wm8974_write(codec, WM8974_POWER3, 0);
0a1bf553
MB
599 break;
600 }
df1ef7a3 601
0a1bf553
MB
602 codec->bias_level = level;
603 return 0;
604}
605
1a55b3f6 606#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
0a1bf553
MB
607
608#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
609 SNDRV_PCM_FMTBIT_S24_LE)
610
611static struct snd_soc_dai_ops wm8974_ops = {
612 .hw_params = wm8974_pcm_hw_params,
613 .digital_mute = wm8974_mute,
614 .set_fmt = wm8974_set_dai_fmt,
615 .set_clkdiv = wm8974_set_dai_clkdiv,
616 .set_pll = wm8974_set_dai_pll,
617};
618
619struct snd_soc_dai wm8974_dai = {
620 .name = "WM8974 HiFi",
621 .playback = {
622 .stream_name = "Playback",
623 .channels_min = 1,
33d81af4 624 .channels_max = 2, /* Only 1 channel of data */
0a1bf553
MB
625 .rates = WM8974_RATES,
626 .formats = WM8974_FORMATS,},
627 .capture = {
628 .stream_name = "Capture",
629 .channels_min = 1,
33d81af4 630 .channels_max = 2, /* Only 1 channel of data */
0a1bf553
MB
631 .rates = WM8974_RATES,
632 .formats = WM8974_FORMATS,},
633 .ops = &wm8974_ops,
cb11d39e 634 .symmetric_rates = 1,
0a1bf553
MB
635};
636EXPORT_SYMBOL_GPL(wm8974_dai);
637
638static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
639{
640 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
641 struct snd_soc_codec *codec = socdev->card->codec;
642
643 wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
644 return 0;
645}
646
647static int wm8974_resume(struct platform_device *pdev)
648{
649 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
650 struct snd_soc_codec *codec = socdev->card->codec;
651 int i;
652 u8 data[2];
653 u16 *cache = codec->reg_cache;
654
655 /* Sync reg_cache with the hardware */
656 for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
657 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
658 data[1] = cache[i] & 0x00ff;
659 codec->hw_write(codec->control_data, data, 2);
660 }
661 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
662 wm8974_set_bias_level(codec, codec->suspend_bias_level);
663 return 0;
664}
665
4fcbbb67 666static int wm8974_probe(struct platform_device *pdev)
0a1bf553 667{
4fcbbb67
MB
668 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
669 struct snd_soc_codec *codec;
0a1bf553
MB
670 int ret = 0;
671
4fcbbb67
MB
672 if (wm8974_codec == NULL) {
673 dev_err(&pdev->dev, "Codec device not registered\n");
674 return -ENODEV;
675 }
0a1bf553 676
4fcbbb67
MB
677 socdev->card->codec = wm8974_codec;
678 codec = wm8974_codec;
0a1bf553
MB
679
680 /* register pcms */
681 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
1a55b3f6 682 if (ret < 0) {
4fcbbb67 683 dev_err(codec->dev, "failed to create pcms: %d\n", ret);
0a1bf553
MB
684 goto pcm_err;
685 }
686
4fcbbb67
MB
687 snd_soc_add_controls(codec, wm8974_snd_controls,
688 ARRAY_SIZE(wm8974_snd_controls));
0a1bf553
MB
689 wm8974_add_widgets(codec);
690 ret = snd_soc_init_card(socdev);
691 if (ret < 0) {
4fcbbb67 692 dev_err(codec->dev, "failed to register card: %d\n", ret);
0a1bf553
MB
693 goto card_err;
694 }
4fcbbb67 695
0a1bf553
MB
696 return ret;
697
698card_err:
699 snd_soc_free_pcms(socdev);
700 snd_soc_dapm_free(socdev);
701pcm_err:
0a1bf553
MB
702 return ret;
703}
704
4fcbbb67
MB
705/* power down chip */
706static int wm8974_remove(struct platform_device *pdev)
707{
708 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
0a1bf553 709
4fcbbb67
MB
710 snd_soc_free_pcms(socdev);
711 snd_soc_dapm_free(socdev);
0a1bf553 712
4fcbbb67
MB
713 return 0;
714}
0a1bf553 715
4fcbbb67
MB
716struct snd_soc_codec_device soc_codec_dev_wm8974 = {
717 .probe = wm8974_probe,
718 .remove = wm8974_remove,
719 .suspend = wm8974_suspend,
720 .resume = wm8974_resume,
721};
722EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
0a1bf553 723
4fcbbb67 724static __devinit int wm8974_register(struct wm8974_priv *wm8974)
0a1bf553 725{
0a1bf553 726 int ret;
4fcbbb67 727 struct snd_soc_codec *codec = &wm8974->codec;
0a1bf553 728
4fcbbb67
MB
729 if (wm8974_codec) {
730 dev_err(codec->dev, "Another WM8974 is registered\n");
731 return -EINVAL;
732 }
0a1bf553 733
4fcbbb67
MB
734 mutex_init(&codec->mutex);
735 INIT_LIST_HEAD(&codec->dapm_widgets);
736 INIT_LIST_HEAD(&codec->dapm_paths);
0a1bf553 737
4fcbbb67
MB
738 codec->private_data = wm8974;
739 codec->name = "WM8974";
740 codec->owner = THIS_MODULE;
741 codec->read = wm8974_read_reg_cache;
742 codec->write = wm8974_write;
743 codec->bias_level = SND_SOC_BIAS_OFF;
744 codec->set_bias_level = wm8974_set_bias_level;
745 codec->dai = &wm8974_dai;
746 codec->num_dai = 1;
747 codec->reg_cache_size = WM8974_CACHEREGNUM;
748 codec->reg_cache = &wm8974->reg_cache;
0a1bf553 749
4fcbbb67
MB
750 memcpy(codec->reg_cache, wm8974_reg, sizeof(wm8974_reg));
751
752 ret = wm8974_reset(codec);
0a1bf553 753 if (ret < 0) {
4fcbbb67
MB
754 dev_err(codec->dev, "Failed to issue reset\n");
755 return ret;
0a1bf553
MB
756 }
757
4fcbbb67
MB
758 wm8974_dai.dev = codec->dev;
759
760 wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
761
762 wm8974_codec = codec;
763
764 ret = snd_soc_register_codec(codec);
765 if (ret != 0) {
766 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
767 return ret;
0a1bf553 768 }
0a1bf553 769
4fcbbb67
MB
770 ret = snd_soc_register_dai(&wm8974_dai);
771 if (ret != 0) {
772 dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
773 snd_soc_unregister_codec(codec);
774 return ret;
775 }
0a1bf553 776
0a1bf553
MB
777 return 0;
778}
779
4fcbbb67 780static __devexit void wm8974_unregister(struct wm8974_priv *wm8974)
0a1bf553 781{
4fcbbb67
MB
782 wm8974_set_bias_level(&wm8974->codec, SND_SOC_BIAS_OFF);
783 snd_soc_unregister_dai(&wm8974_dai);
784 snd_soc_unregister_codec(&wm8974->codec);
785 kfree(wm8974);
786 wm8974_codec = NULL;
0a1bf553
MB
787}
788
4fcbbb67
MB
789static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
790 const struct i2c_device_id *id)
0a1bf553 791{
4fcbbb67 792 struct wm8974_priv *wm8974;
0a1bf553 793 struct snd_soc_codec *codec;
0a1bf553 794
4fcbbb67
MB
795 wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
796 if (wm8974 == NULL)
0a1bf553
MB
797 return -ENOMEM;
798
4fcbbb67
MB
799 codec = &wm8974->codec;
800 codec->hw_write = (hw_write_t)i2c_master_send;
0a1bf553 801
4fcbbb67
MB
802 i2c_set_clientdata(i2c, wm8974);
803 codec->control_data = i2c;
0a1bf553 804
4fcbbb67 805 codec->dev = &i2c->dev;
0a1bf553 806
4fcbbb67
MB
807 return wm8974_register(wm8974);
808}
0a1bf553 809
4fcbbb67
MB
810static __devexit int wm8974_i2c_remove(struct i2c_client *client)
811{
812 struct wm8974_priv *wm8974 = i2c_get_clientdata(client);
813 wm8974_unregister(wm8974);
0a1bf553
MB
814 return 0;
815}
816
4fcbbb67
MB
817static const struct i2c_device_id wm8974_i2c_id[] = {
818 { "wm8974", 0 },
819 { }
820};
821MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
822
823static struct i2c_driver wm8974_i2c_driver = {
824 .driver = {
8b83a193 825 .name = "WM8974",
4fcbbb67
MB
826 .owner = THIS_MODULE,
827 },
828 .probe = wm8974_i2c_probe,
829 .remove = __devexit_p(wm8974_i2c_remove),
830 .id_table = wm8974_i2c_id,
0a1bf553 831};
0a1bf553
MB
832
833static int __init wm8974_modinit(void)
834{
4fcbbb67 835 return i2c_add_driver(&wm8974_i2c_driver);
0a1bf553
MB
836}
837module_init(wm8974_modinit);
838
839static void __exit wm8974_exit(void)
840{
4fcbbb67 841 i2c_del_driver(&wm8974_i2c_driver);
0a1bf553
MB
842}
843module_exit(wm8974_exit);
844
845MODULE_DESCRIPTION("ASoC WM8974 driver");
846MODULE_AUTHOR("Liam Girdwood");
847MODULE_LICENSE("GPL");
This page took 0.059729 seconds and 5 git commands to generate.