xref: /linux/sound/soc/codecs/wm8974.c (revision 2be59418f76dac590b98027586ac1714be17fcae)
10a1bf553SMark Brown /*
20a1bf553SMark Brown  * wm8974.c  --  WM8974 ALSA Soc Audio driver
30a1bf553SMark Brown  *
48b83a193SMark Brown  * Copyright 2006-2009 Wolfson Microelectronics PLC.
50a1bf553SMark Brown  *
69a185b9aSMark Brown  * Author: Liam Girdwood <Liam.Girdwood@wolfsonmicro.com>
70a1bf553SMark Brown  *
80a1bf553SMark Brown  * This program is free software; you can redistribute it and/or modify
90a1bf553SMark Brown  * it under the terms of the GNU General Public License version 2 as
100a1bf553SMark Brown  * published by the Free Software Foundation.
110a1bf553SMark Brown  */
120a1bf553SMark Brown 
130a1bf553SMark Brown #include <linux/module.h>
140a1bf553SMark Brown #include <linux/moduleparam.h>
150a1bf553SMark Brown #include <linux/kernel.h>
160a1bf553SMark Brown #include <linux/init.h>
170a1bf553SMark Brown #include <linux/delay.h>
180a1bf553SMark Brown #include <linux/pm.h>
190a1bf553SMark Brown #include <linux/i2c.h>
205a0e3ad6STejun Heo #include <linux/slab.h>
210a1bf553SMark Brown #include <sound/core.h>
220a1bf553SMark Brown #include <sound/pcm.h>
230a1bf553SMark Brown #include <sound/pcm_params.h>
240a1bf553SMark Brown #include <sound/soc.h>
250a1bf553SMark Brown #include <sound/initval.h>
26a5f8d2f1SMark Brown #include <sound/tlv.h>
270a1bf553SMark Brown 
280a1bf553SMark Brown #include "wm8974.h"
290a1bf553SMark Brown 
300a1bf553SMark Brown static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
310a1bf553SMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
320a1bf553SMark Brown 	0x0050, 0x0000, 0x0140, 0x0000,
330a1bf553SMark Brown 	0x0000, 0x0000, 0x0000, 0x00ff,
340a1bf553SMark Brown 	0x0000, 0x0000, 0x0100, 0x00ff,
350a1bf553SMark Brown 	0x0000, 0x0000, 0x012c, 0x002c,
360a1bf553SMark Brown 	0x002c, 0x002c, 0x002c, 0x0000,
370a1bf553SMark Brown 	0x0032, 0x0000, 0x0000, 0x0000,
380a1bf553SMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
390a1bf553SMark Brown 	0x0038, 0x000b, 0x0032, 0x0000,
400a1bf553SMark Brown 	0x0008, 0x000c, 0x0093, 0x00e9,
410a1bf553SMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
420a1bf553SMark Brown 	0x0003, 0x0010, 0x0000, 0x0000,
430a1bf553SMark Brown 	0x0000, 0x0002, 0x0000, 0x0000,
440a1bf553SMark Brown 	0x0000, 0x0000, 0x0039, 0x0000,
450a1bf553SMark Brown 	0x0000,
460a1bf553SMark Brown };
470a1bf553SMark Brown 
48df1ef7a3SMark Brown #define WM8974_POWER1_BIASEN  0x08
4948c03ce7SGuennadi Liakhovetski #define WM8974_POWER1_BUFIOEN 0x04
50df1ef7a3SMark Brown 
511e97f50bSMark Brown #define wm8974_reset(c)	snd_soc_write(c, WM8974_RESET, 0)
520a1bf553SMark Brown 
530a1bf553SMark Brown static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
540a1bf553SMark Brown static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
550a1bf553SMark Brown static const char *wm8974_eqmode[] = {"Capture", "Playback" };
560a1bf553SMark Brown static const char *wm8974_bw[] = {"Narrow", "Wide" };
570a1bf553SMark Brown static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
580a1bf553SMark Brown static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
590a1bf553SMark Brown static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
600a1bf553SMark Brown static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
610a1bf553SMark Brown static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
620a1bf553SMark Brown static const char *wm8974_alc[] = {"ALC", "Limiter" };
630a1bf553SMark Brown 
640a1bf553SMark Brown static const struct soc_enum wm8974_enum[] = {
650a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
660a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
670a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_DAC,  4, 4, wm8974_deemp),
680a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ1,  8, 2, wm8974_eqmode),
690a1bf553SMark Brown 
700a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ1,  5, 4, wm8974_eq1),
710a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ2,  8, 2, wm8974_bw),
720a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ2,  5, 4, wm8974_eq2),
730a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ3,  8, 2, wm8974_bw),
740a1bf553SMark Brown 
750a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ3,  5, 4, wm8974_eq3),
760a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ4,  8, 2, wm8974_bw),
770a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ4,  5, 4, wm8974_eq4),
780a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ5,  8, 2, wm8974_bw),
790a1bf553SMark Brown 
800a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ5,  5, 4, wm8974_eq5),
810a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_ALC3,  8, 2, wm8974_alc),
820a1bf553SMark Brown };
830a1bf553SMark Brown 
848a123ee2SMark Brown static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" };
858a123ee2SMark Brown 
868a123ee2SMark Brown static const struct soc_enum wm8974_auxmode =
878a123ee2SMark Brown 	SOC_ENUM_SINGLE(WM8974_INPUT,  3, 2, wm8974_auxmode_text);
888a123ee2SMark Brown 
89a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
90a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
91a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
92a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
93a5f8d2f1SMark Brown 
940a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_snd_controls[] = {
950a1bf553SMark Brown 
960a1bf553SMark Brown SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
970a1bf553SMark Brown 
980a1bf553SMark Brown SOC_ENUM("DAC Companding", wm8974_enum[1]),
990a1bf553SMark Brown SOC_ENUM("ADC Companding", wm8974_enum[0]),
1000a1bf553SMark Brown 
1010a1bf553SMark Brown SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
1020a1bf553SMark Brown SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
1030a1bf553SMark Brown 
104a5f8d2f1SMark Brown SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv),
1050a1bf553SMark Brown 
1060a1bf553SMark Brown SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
1070a1bf553SMark Brown SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
10825cbf465Sjavier Martin SOC_SINGLE("ADC Inversion Switch", WM8974_ADC, 0, 1, 0),
1090a1bf553SMark Brown 
110a5f8d2f1SMark Brown SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL,  0, 255, 0, digital_tlv),
1110a1bf553SMark Brown 
1120a1bf553SMark Brown SOC_ENUM("Equaliser Function", wm8974_enum[3]),
1130a1bf553SMark Brown SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
114a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1,  0, 24, 1, eq_tlv),
1150a1bf553SMark Brown 
1160a1bf553SMark Brown SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
1170a1bf553SMark Brown SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
118a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2,  0, 24, 1, eq_tlv),
1190a1bf553SMark Brown 
1200a1bf553SMark Brown SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
1210a1bf553SMark Brown SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
122a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3,  0, 24, 1, eq_tlv),
1230a1bf553SMark Brown 
1240a1bf553SMark Brown SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
1250a1bf553SMark Brown SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
126a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4,  0, 24, 1, eq_tlv),
1270a1bf553SMark Brown 
1280a1bf553SMark Brown SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
1290a1bf553SMark Brown SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
130a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5,  0, 24, 1, eq_tlv),
1310a1bf553SMark Brown 
1320a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1,  8, 1, 0),
1330a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1,  4, 15, 0),
1340a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1,  0, 15, 0),
1350a1bf553SMark Brown 
1360a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2,  4, 7, 0),
1370a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2,  0, 15, 0),
1380a1bf553SMark Brown 
1390a1bf553SMark Brown SOC_SINGLE("ALC Enable Switch", WM8974_ALC1,  8, 1, 0),
1400a1bf553SMark Brown SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1,  3, 7, 0),
1410a1bf553SMark Brown SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1,  0, 7, 0),
1420a1bf553SMark Brown 
1430a1bf553SMark Brown SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2,  8, 1, 0),
1440a1bf553SMark Brown SOC_SINGLE("ALC Capture Hold", WM8974_ALC2,  4, 7, 0),
1450a1bf553SMark Brown SOC_SINGLE("ALC Capture Target", WM8974_ALC2,  0, 15, 0),
1460a1bf553SMark Brown 
1470a1bf553SMark Brown SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
1480a1bf553SMark Brown SOC_SINGLE("ALC Capture Decay", WM8974_ALC3,  4, 15, 0),
1490a1bf553SMark Brown SOC_SINGLE("ALC Capture Attack", WM8974_ALC3,  0, 15, 0),
1500a1bf553SMark Brown 
1510a1bf553SMark Brown SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE,  3, 1, 0),
1520a1bf553SMark Brown SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE,  0, 7, 0),
1530a1bf553SMark Brown 
1540a1bf553SMark Brown SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA,  7, 1, 0),
155a5f8d2f1SMark Brown SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA,  0, 63, 0, inpga_tlv),
1560a1bf553SMark Brown 
1570a1bf553SMark Brown SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL,  7, 1, 0),
1580a1bf553SMark Brown SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL,  6, 1, 1),
1598a123ee2SMark Brown SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL,  0, 63, 0, spk_tlv),
1608a123ee2SMark Brown 
1618a123ee2SMark Brown SOC_ENUM("Aux Mode", wm8974_auxmode),
1620a1bf553SMark Brown 
1630a1bf553SMark Brown SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST,  8, 1, 0),
1648a123ee2SMark Brown SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1),
165b2c3e923SGuennadi Liakhovetski 
166b2c3e923SGuennadi Liakhovetski /* DAC / ADC oversampling */
167b2c3e923SGuennadi Liakhovetski SOC_SINGLE("DAC 128x Oversampling Switch", WM8974_DAC, 8, 1, 0),
168b2c3e923SGuennadi Liakhovetski SOC_SINGLE("ADC 128x Oversampling Switch", WM8974_ADC, 8, 1, 0),
1690a1bf553SMark Brown };
1700a1bf553SMark Brown 
1710a1bf553SMark Brown /* Speaker Output Mixer */
1720a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
1730a1bf553SMark Brown SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
1740a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
175759512fbSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 0),
1760a1bf553SMark Brown };
1770a1bf553SMark Brown 
1780a1bf553SMark Brown /* Mono Output Mixer */
1790a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
1800a1bf553SMark Brown SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
1810a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
1828a123ee2SMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0),
1838a123ee2SMark Brown };
1848a123ee2SMark Brown 
1858a123ee2SMark Brown /* Boost mixer */
1868a123ee2SMark Brown static const struct snd_kcontrol_new wm8974_boost_mixer[] = {
1878a123ee2SMark Brown SOC_DAPM_SINGLE("Aux Switch", WM8974_INPPGA, 6, 1, 0),
1888a123ee2SMark Brown };
1898a123ee2SMark Brown 
1908a123ee2SMark Brown /* Input PGA */
1918a123ee2SMark Brown static const struct snd_kcontrol_new wm8974_inpga[] = {
1928a123ee2SMark Brown SOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0),
1938a123ee2SMark Brown SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0),
1948a123ee2SMark Brown SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0),
1950a1bf553SMark Brown };
1960a1bf553SMark Brown 
1970a1bf553SMark Brown /* AUX Input boost vol */
1980a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_aux_boost_controls =
1990a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
2000a1bf553SMark Brown 
2010a1bf553SMark Brown /* Mic Input boost vol */
2020a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_mic_boost_controls =
2030a1bf553SMark Brown SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
2040a1bf553SMark Brown 
2050a1bf553SMark Brown static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
2060a1bf553SMark Brown SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
2070a1bf553SMark Brown 	&wm8974_speaker_mixer_controls[0],
2080a1bf553SMark Brown 	ARRAY_SIZE(wm8974_speaker_mixer_controls)),
2090a1bf553SMark Brown SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
2100a1bf553SMark Brown 	&wm8974_mono_mixer_controls[0],
2110a1bf553SMark Brown 	ARRAY_SIZE(wm8974_mono_mixer_controls)),
2120a1bf553SMark Brown SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
2138a123ee2SMark Brown SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0),
2140a1bf553SMark Brown SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
2150a1bf553SMark Brown SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
2160a1bf553SMark Brown SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
2170a1bf553SMark Brown SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
2180a1bf553SMark Brown 
2198a123ee2SMark Brown SND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga,
2208a123ee2SMark Brown 		   ARRAY_SIZE(wm8974_inpga)),
2218a123ee2SMark Brown SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0,
2228a123ee2SMark Brown 		   wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)),
2230a1bf553SMark Brown 
22448dd231bSMark Brown SND_SOC_DAPM_SUPPLY("Mic Bias", WM8974_POWER1, 4, 0, NULL, 0),
2250a1bf553SMark Brown 
2260a1bf553SMark Brown SND_SOC_DAPM_INPUT("MICN"),
2270a1bf553SMark Brown SND_SOC_DAPM_INPUT("MICP"),
2280a1bf553SMark Brown SND_SOC_DAPM_INPUT("AUX"),
2290a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("MONOOUT"),
2300a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("SPKOUTP"),
2310a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("SPKOUTN"),
2320a1bf553SMark Brown };
2330a1bf553SMark Brown 
234a2bd691cSMark Brown static const struct snd_soc_dapm_route wm8974_dapm_routes[] = {
2350a1bf553SMark Brown 	/* Mono output mixer */
2360a1bf553SMark Brown 	{"Mono Mixer", "PCM Playback Switch", "DAC"},
2370a1bf553SMark Brown 	{"Mono Mixer", "Aux Playback Switch", "Aux Input"},
2380a1bf553SMark Brown 	{"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
2390a1bf553SMark Brown 
2400a1bf553SMark Brown 	/* Speaker output mixer */
2410a1bf553SMark Brown 	{"Speaker Mixer", "PCM Playback Switch", "DAC"},
2420a1bf553SMark Brown 	{"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
2430a1bf553SMark Brown 	{"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
2440a1bf553SMark Brown 
2450a1bf553SMark Brown 	/* Outputs */
2460a1bf553SMark Brown 	{"Mono Out", NULL, "Mono Mixer"},
2470a1bf553SMark Brown 	{"MONOOUT", NULL, "Mono Out"},
2480a1bf553SMark Brown 	{"SpkN Out", NULL, "Speaker Mixer"},
2490a1bf553SMark Brown 	{"SpkP Out", NULL, "Speaker Mixer"},
2500a1bf553SMark Brown 	{"SPKOUTN", NULL, "SpkN Out"},
2510a1bf553SMark Brown 	{"SPKOUTP", NULL, "SpkP Out"},
2520a1bf553SMark Brown 
2530a1bf553SMark Brown 	/* Boost Mixer */
2548a123ee2SMark Brown 	{"ADC", NULL, "Boost Mixer"},
2558a123ee2SMark Brown 	{"Boost Mixer", "Aux Switch", "Aux Input"},
2568a123ee2SMark Brown 	{"Boost Mixer", NULL, "Input PGA"},
2578a123ee2SMark Brown 	{"Boost Mixer", NULL, "MICP"},
2588a123ee2SMark Brown 
2598a123ee2SMark Brown 	/* Input PGA */
2608a123ee2SMark Brown 	{"Input PGA", "Aux Switch", "Aux Input"},
2618a123ee2SMark Brown 	{"Input PGA", "MicN Switch", "MICN"},
2628a123ee2SMark Brown 	{"Input PGA", "MicP Switch", "MICP"},
2630a1bf553SMark Brown 
2640a1bf553SMark Brown 	/* Inputs */
2658a123ee2SMark Brown 	{"Aux Input", NULL, "AUX"},
2660a1bf553SMark Brown };
2670a1bf553SMark Brown 
2680a1bf553SMark Brown struct pll_ {
269c36b2fc7SMark Brown 	unsigned int pre_div:1;
2700a1bf553SMark Brown 	unsigned int n:4;
2710a1bf553SMark Brown 	unsigned int k;
2720a1bf553SMark Brown };
2730a1bf553SMark Brown 
27491d0c3ecSMark Brown /* The size in bits of the pll divide multiplied by 10
27591d0c3ecSMark Brown  * to allow rounding later */
27691d0c3ecSMark Brown #define FIXED_PLL_SIZE ((1 << 24) * 10)
27791d0c3ecSMark Brown 
278c36b2fc7SMark Brown static void pll_factors(struct pll_ *pll_div,
279c36b2fc7SMark Brown 			unsigned int target, unsigned int source)
28091d0c3ecSMark Brown {
28191d0c3ecSMark Brown 	unsigned long long Kpart;
28291d0c3ecSMark Brown 	unsigned int K, Ndiv, Nmod;
28391d0c3ecSMark Brown 
284c36b2fc7SMark Brown 	/* There is a fixed divide by 4 in the output path */
285c36b2fc7SMark Brown 	target *= 4;
286c36b2fc7SMark Brown 
28791d0c3ecSMark Brown 	Ndiv = target / source;
28891d0c3ecSMark Brown 	if (Ndiv < 6) {
289c36b2fc7SMark Brown 		source /= 2;
290c36b2fc7SMark Brown 		pll_div->pre_div = 1;
29191d0c3ecSMark Brown 		Ndiv = target / source;
29291d0c3ecSMark Brown 	} else
293c36b2fc7SMark Brown 		pll_div->pre_div = 0;
29491d0c3ecSMark Brown 
29591d0c3ecSMark Brown 	if ((Ndiv < 6) || (Ndiv > 12))
29691d0c3ecSMark Brown 		printk(KERN_WARNING
2978b83a193SMark Brown 			"WM8974 N value %u outwith recommended range!\n",
29891d0c3ecSMark Brown 			Ndiv);
29991d0c3ecSMark Brown 
300c36b2fc7SMark Brown 	pll_div->n = Ndiv;
30191d0c3ecSMark Brown 	Nmod = target % source;
30291d0c3ecSMark Brown 	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
30391d0c3ecSMark Brown 
30491d0c3ecSMark Brown 	do_div(Kpart, source);
30591d0c3ecSMark Brown 
30691d0c3ecSMark Brown 	K = Kpart & 0xFFFFFFFF;
30791d0c3ecSMark Brown 
30891d0c3ecSMark Brown 	/* Check if we need to round */
30991d0c3ecSMark Brown 	if ((K % 10) >= 5)
31091d0c3ecSMark Brown 		K += 5;
31191d0c3ecSMark Brown 
31291d0c3ecSMark Brown 	/* Move down to proper range now rounding is done */
31391d0c3ecSMark Brown 	K /= 10;
31491d0c3ecSMark Brown 
315c36b2fc7SMark Brown 	pll_div->k = K;
31691d0c3ecSMark Brown }
3170a1bf553SMark Brown 
31885488037SMark Brown static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
31985488037SMark Brown 		int source, unsigned int freq_in, unsigned int freq_out)
3200a1bf553SMark Brown {
3210a1bf553SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
322c36b2fc7SMark Brown 	struct pll_ pll_div;
3230a1bf553SMark Brown 	u16 reg;
3240a1bf553SMark Brown 
3250a1bf553SMark Brown 	if (freq_in == 0 || freq_out == 0) {
32691d0c3ecSMark Brown 		/* Clock CODEC directly from MCLK */
3271e97f50bSMark Brown 		reg = snd_soc_read(codec, WM8974_CLOCK);
3281e97f50bSMark Brown 		snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
32991d0c3ecSMark Brown 
33091d0c3ecSMark Brown 		/* Turn off PLL */
3311e97f50bSMark Brown 		reg = snd_soc_read(codec, WM8974_POWER1);
3321e97f50bSMark Brown 		snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
3330a1bf553SMark Brown 		return 0;
3340a1bf553SMark Brown 	}
3350a1bf553SMark Brown 
336c36b2fc7SMark Brown 	pll_factors(&pll_div, freq_out, freq_in);
33791d0c3ecSMark Brown 
3381e97f50bSMark Brown 	snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
3391e97f50bSMark Brown 	snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
3401e97f50bSMark Brown 	snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
3411e97f50bSMark Brown 	snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
3421e97f50bSMark Brown 	reg = snd_soc_read(codec, WM8974_POWER1);
3431e97f50bSMark Brown 	snd_soc_write(codec, WM8974_POWER1, reg | 0x020);
3441a55b3f6SMark Brown 
34591d0c3ecSMark Brown 	/* Run CODEC from PLL instead of MCLK */
3461e97f50bSMark Brown 	reg = snd_soc_read(codec, WM8974_CLOCK);
3471e97f50bSMark Brown 	snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
34891d0c3ecSMark Brown 
34991d0c3ecSMark Brown 	return 0;
3500a1bf553SMark Brown }
3510a1bf553SMark Brown 
3520a1bf553SMark Brown /*
3530a1bf553SMark Brown  * Configure WM8974 clock dividers.
3540a1bf553SMark Brown  */
3550a1bf553SMark Brown static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
3560a1bf553SMark Brown 		int div_id, int div)
3570a1bf553SMark Brown {
3580a1bf553SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
3590a1bf553SMark Brown 	u16 reg;
3600a1bf553SMark Brown 
3610a1bf553SMark Brown 	switch (div_id) {
3620a1bf553SMark Brown 	case WM8974_OPCLKDIV:
3631e97f50bSMark Brown 		reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
3641e97f50bSMark Brown 		snd_soc_write(codec, WM8974_GPIO, reg | div);
3650a1bf553SMark Brown 		break;
3660a1bf553SMark Brown 	case WM8974_MCLKDIV:
3671e97f50bSMark Brown 		reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
3681e97f50bSMark Brown 		snd_soc_write(codec, WM8974_CLOCK, reg | div);
3690a1bf553SMark Brown 		break;
3700a1bf553SMark Brown 	case WM8974_BCLKDIV:
3711e97f50bSMark Brown 		reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
3721e97f50bSMark Brown 		snd_soc_write(codec, WM8974_CLOCK, reg | div);
3730a1bf553SMark Brown 		break;
3740a1bf553SMark Brown 	default:
3750a1bf553SMark Brown 		return -EINVAL;
3760a1bf553SMark Brown 	}
3770a1bf553SMark Brown 
3780a1bf553SMark Brown 	return 0;
3790a1bf553SMark Brown }
3800a1bf553SMark Brown 
3810a1bf553SMark Brown static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
3820a1bf553SMark Brown 		unsigned int fmt)
3830a1bf553SMark Brown {
3840a1bf553SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
3850a1bf553SMark Brown 	u16 iface = 0;
3861e97f50bSMark Brown 	u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
3870a1bf553SMark Brown 
3880a1bf553SMark Brown 	/* set master/slave audio interface */
3890a1bf553SMark Brown 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
3900a1bf553SMark Brown 	case SND_SOC_DAIFMT_CBM_CFM:
3910a1bf553SMark Brown 		clk |= 0x0001;
3920a1bf553SMark Brown 		break;
3930a1bf553SMark Brown 	case SND_SOC_DAIFMT_CBS_CFS:
3940a1bf553SMark Brown 		break;
3950a1bf553SMark Brown 	default:
3960a1bf553SMark Brown 		return -EINVAL;
3970a1bf553SMark Brown 	}
3980a1bf553SMark Brown 
3990a1bf553SMark Brown 	/* interface format */
4000a1bf553SMark Brown 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
4010a1bf553SMark Brown 	case SND_SOC_DAIFMT_I2S:
4020a1bf553SMark Brown 		iface |= 0x0010;
4030a1bf553SMark Brown 		break;
4040a1bf553SMark Brown 	case SND_SOC_DAIFMT_RIGHT_J:
4050a1bf553SMark Brown 		break;
4060a1bf553SMark Brown 	case SND_SOC_DAIFMT_LEFT_J:
4070a1bf553SMark Brown 		iface |= 0x0008;
4080a1bf553SMark Brown 		break;
4090a1bf553SMark Brown 	case SND_SOC_DAIFMT_DSP_A:
4100a1bf553SMark Brown 		iface |= 0x00018;
4110a1bf553SMark Brown 		break;
4120a1bf553SMark Brown 	default:
4130a1bf553SMark Brown 		return -EINVAL;
4140a1bf553SMark Brown 	}
4150a1bf553SMark Brown 
4160a1bf553SMark Brown 	/* clock inversion */
4170a1bf553SMark Brown 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
4180a1bf553SMark Brown 	case SND_SOC_DAIFMT_NB_NF:
4190a1bf553SMark Brown 		break;
4200a1bf553SMark Brown 	case SND_SOC_DAIFMT_IB_IF:
4210a1bf553SMark Brown 		iface |= 0x0180;
4220a1bf553SMark Brown 		break;
4230a1bf553SMark Brown 	case SND_SOC_DAIFMT_IB_NF:
4240a1bf553SMark Brown 		iface |= 0x0100;
4250a1bf553SMark Brown 		break;
4260a1bf553SMark Brown 	case SND_SOC_DAIFMT_NB_IF:
4270a1bf553SMark Brown 		iface |= 0x0080;
4280a1bf553SMark Brown 		break;
4290a1bf553SMark Brown 	default:
4300a1bf553SMark Brown 		return -EINVAL;
4310a1bf553SMark Brown 	}
4320a1bf553SMark Brown 
4331e97f50bSMark Brown 	snd_soc_write(codec, WM8974_IFACE, iface);
4341e97f50bSMark Brown 	snd_soc_write(codec, WM8974_CLOCK, clk);
4350a1bf553SMark Brown 	return 0;
4360a1bf553SMark Brown }
4370a1bf553SMark Brown 
4380a1bf553SMark Brown static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
4390a1bf553SMark Brown 				struct snd_pcm_hw_params *params,
4400a1bf553SMark Brown 				struct snd_soc_dai *dai)
4410a1bf553SMark Brown {
4420a1bf553SMark Brown 	struct snd_soc_codec *codec = dai->codec;
4431e97f50bSMark Brown 	u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
4441e97f50bSMark Brown 	u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
4450a1bf553SMark Brown 
4460a1bf553SMark Brown 	/* bit size */
4470a1bf553SMark Brown 	switch (params_format(params)) {
4480a1bf553SMark Brown 	case SNDRV_PCM_FORMAT_S16_LE:
4490a1bf553SMark Brown 		break;
4500a1bf553SMark Brown 	case SNDRV_PCM_FORMAT_S20_3LE:
4510a1bf553SMark Brown 		iface |= 0x0020;
4520a1bf553SMark Brown 		break;
4530a1bf553SMark Brown 	case SNDRV_PCM_FORMAT_S24_LE:
4540a1bf553SMark Brown 		iface |= 0x0040;
4550a1bf553SMark Brown 		break;
4560a1bf553SMark Brown 	case SNDRV_PCM_FORMAT_S32_LE:
4570a1bf553SMark Brown 		iface |= 0x0060;
4580a1bf553SMark Brown 		break;
4590a1bf553SMark Brown 	}
4600a1bf553SMark Brown 
4610a1bf553SMark Brown 	/* filter coefficient */
4620a1bf553SMark Brown 	switch (params_rate(params)) {
463b3172f22SGuennadi Liakhovetski 	case 8000:
4640a1bf553SMark Brown 		adn |= 0x5 << 1;
4650a1bf553SMark Brown 		break;
466b3172f22SGuennadi Liakhovetski 	case 11025:
4670a1bf553SMark Brown 		adn |= 0x4 << 1;
4680a1bf553SMark Brown 		break;
469b3172f22SGuennadi Liakhovetski 	case 16000:
4700a1bf553SMark Brown 		adn |= 0x3 << 1;
4710a1bf553SMark Brown 		break;
472b3172f22SGuennadi Liakhovetski 	case 22050:
4730a1bf553SMark Brown 		adn |= 0x2 << 1;
4740a1bf553SMark Brown 		break;
475b3172f22SGuennadi Liakhovetski 	case 32000:
4760a1bf553SMark Brown 		adn |= 0x1 << 1;
4770a1bf553SMark Brown 		break;
478b3172f22SGuennadi Liakhovetski 	case 44100:
479b3172f22SGuennadi Liakhovetski 	case 48000:
4800a1bf553SMark Brown 		break;
4810a1bf553SMark Brown 	}
4820a1bf553SMark Brown 
4831e97f50bSMark Brown 	snd_soc_write(codec, WM8974_IFACE, iface);
4841e97f50bSMark Brown 	snd_soc_write(codec, WM8974_ADD, adn);
4850a1bf553SMark Brown 	return 0;
4860a1bf553SMark Brown }
4870a1bf553SMark Brown 
4880a1bf553SMark Brown static int wm8974_mute(struct snd_soc_dai *dai, int mute)
4890a1bf553SMark Brown {
4900a1bf553SMark Brown 	struct snd_soc_codec *codec = dai->codec;
4911e97f50bSMark Brown 	u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
4920a1bf553SMark Brown 
4930a1bf553SMark Brown 	if (mute)
4941e97f50bSMark Brown 		snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
4950a1bf553SMark Brown 	else
4961e97f50bSMark Brown 		snd_soc_write(codec, WM8974_DAC, mute_reg);
4970a1bf553SMark Brown 	return 0;
4980a1bf553SMark Brown }
4990a1bf553SMark Brown 
5000a1bf553SMark Brown /* liam need to make this lower power with dapm */
5010a1bf553SMark Brown static int wm8974_set_bias_level(struct snd_soc_codec *codec,
5020a1bf553SMark Brown 	enum snd_soc_bias_level level)
5030a1bf553SMark Brown {
5041e97f50bSMark Brown 	u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
505df1ef7a3SMark Brown 
5060a1bf553SMark Brown 	switch (level) {
5070a1bf553SMark Brown 	case SND_SOC_BIAS_ON:
5080a1bf553SMark Brown 	case SND_SOC_BIAS_PREPARE:
509df1ef7a3SMark Brown 		power1 |= 0x1;  /* VMID 50k */
5101e97f50bSMark Brown 		snd_soc_write(codec, WM8974_POWER1, power1);
5110a1bf553SMark Brown 		break;
512df1ef7a3SMark Brown 
5130a1bf553SMark Brown 	case SND_SOC_BIAS_STANDBY:
514df1ef7a3SMark Brown 		power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
515df1ef7a3SMark Brown 
516ce6120ccSLiam Girdwood 		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
5170bad3d84SAxel Lin 			snd_soc_cache_sync(codec);
5180bad3d84SAxel Lin 
519df1ef7a3SMark Brown 			/* Initial cap charge at VMID 5k */
5201e97f50bSMark Brown 			snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
521df1ef7a3SMark Brown 			mdelay(100);
522df1ef7a3SMark Brown 		}
523df1ef7a3SMark Brown 
524df1ef7a3SMark Brown 		power1 |= 0x2;  /* VMID 500k */
5251e97f50bSMark Brown 		snd_soc_write(codec, WM8974_POWER1, power1);
5260a1bf553SMark Brown 		break;
527df1ef7a3SMark Brown 
5280a1bf553SMark Brown 	case SND_SOC_BIAS_OFF:
5291e97f50bSMark Brown 		snd_soc_write(codec, WM8974_POWER1, 0);
5301e97f50bSMark Brown 		snd_soc_write(codec, WM8974_POWER2, 0);
5311e97f50bSMark Brown 		snd_soc_write(codec, WM8974_POWER3, 0);
5320a1bf553SMark Brown 		break;
5330a1bf553SMark Brown 	}
534df1ef7a3SMark Brown 
535ce6120ccSLiam Girdwood 	codec->dapm.bias_level = level;
5360a1bf553SMark Brown 	return 0;
5370a1bf553SMark Brown }
5380a1bf553SMark Brown 
5391a55b3f6SMark Brown #define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
5400a1bf553SMark Brown 
5410a1bf553SMark Brown #define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
5420a1bf553SMark Brown 	SNDRV_PCM_FMTBIT_S24_LE)
5430a1bf553SMark Brown 
54485e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm8974_ops = {
5450a1bf553SMark Brown 	.hw_params = wm8974_pcm_hw_params,
5460a1bf553SMark Brown 	.digital_mute = wm8974_mute,
5470a1bf553SMark Brown 	.set_fmt = wm8974_set_dai_fmt,
5480a1bf553SMark Brown 	.set_clkdiv = wm8974_set_dai_clkdiv,
5490a1bf553SMark Brown 	.set_pll = wm8974_set_dai_pll,
5500a1bf553SMark Brown };
5510a1bf553SMark Brown 
552f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8974_dai = {
553f0fba2adSLiam Girdwood 	.name = "wm8974-hifi",
5540a1bf553SMark Brown 	.playback = {
5550a1bf553SMark Brown 		.stream_name = "Playback",
5560a1bf553SMark Brown 		.channels_min = 1,
55733d81af4SMark Brown 		.channels_max = 2,   /* Only 1 channel of data */
5580a1bf553SMark Brown 		.rates = WM8974_RATES,
5590a1bf553SMark Brown 		.formats = WM8974_FORMATS,},
5600a1bf553SMark Brown 	.capture = {
5610a1bf553SMark Brown 		.stream_name = "Capture",
5620a1bf553SMark Brown 		.channels_min = 1,
56333d81af4SMark Brown 		.channels_max = 2,   /* Only 1 channel of data */
5640a1bf553SMark Brown 		.rates = WM8974_RATES,
5650a1bf553SMark Brown 		.formats = WM8974_FORMATS,},
5660a1bf553SMark Brown 	.ops = &wm8974_ops,
567cb11d39eSMark Brown 	.symmetric_rates = 1,
5680a1bf553SMark Brown };
5690a1bf553SMark Brown 
57084b315eeSLars-Peter Clausen static int wm8974_suspend(struct snd_soc_codec *codec)
5710a1bf553SMark Brown {
5720a1bf553SMark Brown 	wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
5730a1bf553SMark Brown 	return 0;
5740a1bf553SMark Brown }
5750a1bf553SMark Brown 
576f0fba2adSLiam Girdwood static int wm8974_resume(struct snd_soc_codec *codec)
5770a1bf553SMark Brown {
5780a1bf553SMark Brown 	wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
5790a1bf553SMark Brown 	return 0;
5800a1bf553SMark Brown }
5810a1bf553SMark Brown 
582f0fba2adSLiam Girdwood static int wm8974_probe(struct snd_soc_codec *codec)
5830a1bf553SMark Brown {
5840a1bf553SMark Brown 	int ret = 0;
5850a1bf553SMark Brown 
586f0fba2adSLiam Girdwood 	ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
5870a1bf553SMark Brown 	if (ret < 0) {
588f0fba2adSLiam Girdwood 		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
589f0fba2adSLiam Girdwood 		return ret;
5900a1bf553SMark Brown 	}
5910a1bf553SMark Brown 
592f0fba2adSLiam Girdwood 	ret = wm8974_reset(codec);
593f0fba2adSLiam Girdwood 	if (ret < 0) {
594f0fba2adSLiam Girdwood 		dev_err(codec->dev, "Failed to issue reset\n");
595f0fba2adSLiam Girdwood 		return ret;
596f0fba2adSLiam Girdwood 	}
597f0fba2adSLiam Girdwood 
598f0fba2adSLiam Girdwood 	wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
5994fcbbb67SMark Brown 
6000a1bf553SMark Brown 	return ret;
6010a1bf553SMark Brown }
6020a1bf553SMark Brown 
6030a1bf553SMark Brown /* power down chip */
604f0fba2adSLiam Girdwood static int wm8974_remove(struct snd_soc_codec *codec)
6050a1bf553SMark Brown {
606f0fba2adSLiam Girdwood 	wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
6070a1bf553SMark Brown 	return 0;
6080a1bf553SMark Brown }
6090a1bf553SMark Brown 
610f0fba2adSLiam Girdwood static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
6110a1bf553SMark Brown 	.probe = 	wm8974_probe,
6120a1bf553SMark Brown 	.remove = 	wm8974_remove,
6130a1bf553SMark Brown 	.suspend = 	wm8974_suspend,
6140a1bf553SMark Brown 	.resume =	wm8974_resume,
615f0fba2adSLiam Girdwood 	.set_bias_level = wm8974_set_bias_level,
616f0fba2adSLiam Girdwood 	.reg_cache_size = ARRAY_SIZE(wm8974_reg),
617f0fba2adSLiam Girdwood 	.reg_word_size = sizeof(u16),
618f0fba2adSLiam Girdwood 	.reg_cache_default = wm8974_reg,
619a2bd691cSMark Brown 
620a2bd691cSMark Brown 	.controls = wm8974_snd_controls,
621a2bd691cSMark Brown 	.num_controls = ARRAY_SIZE(wm8974_snd_controls),
622a2bd691cSMark Brown 	.dapm_widgets = wm8974_dapm_widgets,
623a2bd691cSMark Brown 	.num_dapm_widgets = ARRAY_SIZE(wm8974_dapm_widgets),
624a2bd691cSMark Brown 	.dapm_routes = wm8974_dapm_routes,
625a2bd691cSMark Brown 	.num_dapm_routes = ARRAY_SIZE(wm8974_dapm_routes),
6260a1bf553SMark Brown };
6270a1bf553SMark Brown 
6284fcbbb67SMark Brown static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
6294fcbbb67SMark Brown 				      const struct i2c_device_id *id)
6304fcbbb67SMark Brown {
631f0fba2adSLiam Girdwood 	int ret;
6324fcbbb67SMark Brown 
633f0fba2adSLiam Girdwood 	ret = snd_soc_register_codec(&i2c->dev,
634f0fba2adSLiam Girdwood 			&soc_codec_dev_wm8974, &wm8974_dai, 1);
635c2562a8eSMark Brown 
636f0fba2adSLiam Girdwood 	return ret;
6374fcbbb67SMark Brown }
6384fcbbb67SMark Brown 
6394fcbbb67SMark Brown static __devexit int wm8974_i2c_remove(struct i2c_client *client)
6404fcbbb67SMark Brown {
641f0fba2adSLiam Girdwood 	snd_soc_unregister_codec(&client->dev);
642c2562a8eSMark Brown 
6434fcbbb67SMark Brown 	return 0;
6444fcbbb67SMark Brown }
6454fcbbb67SMark Brown 
6464fcbbb67SMark Brown static const struct i2c_device_id wm8974_i2c_id[] = {
6474fcbbb67SMark Brown 	{ "wm8974", 0 },
6484fcbbb67SMark Brown 	{ }
6494fcbbb67SMark Brown };
6504fcbbb67SMark Brown MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
6514fcbbb67SMark Brown 
6524fcbbb67SMark Brown static struct i2c_driver wm8974_i2c_driver = {
6534fcbbb67SMark Brown 	.driver = {
654091edccfSMark Brown 		.name = "wm8974",
6554fcbbb67SMark Brown 		.owner = THIS_MODULE,
6564fcbbb67SMark Brown 	},
6574fcbbb67SMark Brown 	.probe =    wm8974_i2c_probe,
6584fcbbb67SMark Brown 	.remove =   __devexit_p(wm8974_i2c_remove),
6594fcbbb67SMark Brown 	.id_table = wm8974_i2c_id,
6604fcbbb67SMark Brown };
6614fcbbb67SMark Brown 
662*2be59418SSachin Kamat module_i2c_driver(wm8974_i2c_driver);
6630a1bf553SMark Brown 
6640a1bf553SMark Brown MODULE_DESCRIPTION("ASoC WM8974 driver");
6650a1bf553SMark Brown MODULE_AUTHOR("Liam Girdwood");
6660a1bf553SMark Brown MODULE_LICENSE("GPL");
667