xref: /linux/sound/soc/codecs/wm8974.c (revision 48c03ce72f2665f79a3fe54fc6d71b8cc3d30803)
10a1bf553SMark Brown /*
20a1bf553SMark Brown  * wm8974.c  --  WM8974 ALSA Soc Audio driver
30a1bf553SMark Brown  *
48b83a193SMark Brown  * Copyright 2006-2009 Wolfson Microelectronics PLC.
50a1bf553SMark Brown  *
64fcbbb67SMark Brown  * Author: Liam Girdwood <linux@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>
200a1bf553SMark Brown #include <linux/platform_device.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/soc-dapm.h>
260a1bf553SMark Brown #include <sound/initval.h>
27a5f8d2f1SMark Brown #include <sound/tlv.h>
280a1bf553SMark Brown 
290a1bf553SMark Brown #include "wm8974.h"
300a1bf553SMark Brown 
310a1bf553SMark Brown static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
320a1bf553SMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
330a1bf553SMark Brown 	0x0050, 0x0000, 0x0140, 0x0000,
340a1bf553SMark Brown 	0x0000, 0x0000, 0x0000, 0x00ff,
350a1bf553SMark Brown 	0x0000, 0x0000, 0x0100, 0x00ff,
360a1bf553SMark Brown 	0x0000, 0x0000, 0x012c, 0x002c,
370a1bf553SMark Brown 	0x002c, 0x002c, 0x002c, 0x0000,
380a1bf553SMark Brown 	0x0032, 0x0000, 0x0000, 0x0000,
390a1bf553SMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
400a1bf553SMark Brown 	0x0038, 0x000b, 0x0032, 0x0000,
410a1bf553SMark Brown 	0x0008, 0x000c, 0x0093, 0x00e9,
420a1bf553SMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
430a1bf553SMark Brown 	0x0003, 0x0010, 0x0000, 0x0000,
440a1bf553SMark Brown 	0x0000, 0x0002, 0x0000, 0x0000,
450a1bf553SMark Brown 	0x0000, 0x0000, 0x0039, 0x0000,
460a1bf553SMark Brown 	0x0000,
470a1bf553SMark Brown };
480a1bf553SMark Brown 
49df1ef7a3SMark Brown #define WM8974_POWER1_BIASEN  0x08
50*48c03ce7SGuennadi Liakhovetski #define WM8974_POWER1_BUFIOEN 0x04
51df1ef7a3SMark Brown 
524fcbbb67SMark Brown struct wm8974_priv {
534fcbbb67SMark Brown 	struct snd_soc_codec codec;
544fcbbb67SMark Brown 	u16 reg_cache[WM8974_CACHEREGNUM];
554fcbbb67SMark Brown };
564fcbbb67SMark Brown 
574fcbbb67SMark Brown static struct snd_soc_codec *wm8974_codec;
584fcbbb67SMark Brown 
591e97f50bSMark Brown #define wm8974_reset(c)	snd_soc_write(c, WM8974_RESET, 0)
600a1bf553SMark Brown 
610a1bf553SMark Brown static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
620a1bf553SMark Brown static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
630a1bf553SMark Brown static const char *wm8974_eqmode[] = {"Capture", "Playback" };
640a1bf553SMark Brown static const char *wm8974_bw[] = {"Narrow", "Wide" };
650a1bf553SMark Brown static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
660a1bf553SMark Brown static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
670a1bf553SMark Brown static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
680a1bf553SMark Brown static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
690a1bf553SMark Brown static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
700a1bf553SMark Brown static const char *wm8974_alc[] = {"ALC", "Limiter" };
710a1bf553SMark Brown 
720a1bf553SMark Brown static const struct soc_enum wm8974_enum[] = {
730a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
740a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
750a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_DAC,  4, 4, wm8974_deemp),
760a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ1,  8, 2, wm8974_eqmode),
770a1bf553SMark Brown 
780a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ1,  5, 4, wm8974_eq1),
790a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ2,  8, 2, wm8974_bw),
800a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ2,  5, 4, wm8974_eq2),
810a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ3,  8, 2, wm8974_bw),
820a1bf553SMark Brown 
830a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ3,  5, 4, wm8974_eq3),
840a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ4,  8, 2, wm8974_bw),
850a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ4,  5, 4, wm8974_eq4),
860a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ5,  8, 2, wm8974_bw),
870a1bf553SMark Brown 
880a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ5,  5, 4, wm8974_eq5),
890a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_ALC3,  8, 2, wm8974_alc),
900a1bf553SMark Brown };
910a1bf553SMark Brown 
928a123ee2SMark Brown static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" };
938a123ee2SMark Brown 
948a123ee2SMark Brown static const struct soc_enum wm8974_auxmode =
958a123ee2SMark Brown 	SOC_ENUM_SINGLE(WM8974_INPUT,  3, 2, wm8974_auxmode_text);
968a123ee2SMark Brown 
97a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
98a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
99a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
100a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
101a5f8d2f1SMark Brown 
1020a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_snd_controls[] = {
1030a1bf553SMark Brown 
1040a1bf553SMark Brown SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
1050a1bf553SMark Brown 
1060a1bf553SMark Brown SOC_ENUM("DAC Companding", wm8974_enum[1]),
1070a1bf553SMark Brown SOC_ENUM("ADC Companding", wm8974_enum[0]),
1080a1bf553SMark Brown 
1090a1bf553SMark Brown SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
1100a1bf553SMark Brown SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
1110a1bf553SMark Brown 
112a5f8d2f1SMark Brown SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv),
1130a1bf553SMark Brown 
1140a1bf553SMark Brown SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
1150a1bf553SMark Brown SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
11625cbf465Sjavier Martin SOC_SINGLE("ADC Inversion Switch", WM8974_ADC, 0, 1, 0),
1170a1bf553SMark Brown 
118a5f8d2f1SMark Brown SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL,  0, 255, 0, digital_tlv),
1190a1bf553SMark Brown 
1200a1bf553SMark Brown SOC_ENUM("Equaliser Function", wm8974_enum[3]),
1210a1bf553SMark Brown SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
122a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1,  0, 24, 1, eq_tlv),
1230a1bf553SMark Brown 
1240a1bf553SMark Brown SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
1250a1bf553SMark Brown SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
126a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2,  0, 24, 1, eq_tlv),
1270a1bf553SMark Brown 
1280a1bf553SMark Brown SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
1290a1bf553SMark Brown SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
130a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3,  0, 24, 1, eq_tlv),
1310a1bf553SMark Brown 
1320a1bf553SMark Brown SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
1330a1bf553SMark Brown SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
134a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4,  0, 24, 1, eq_tlv),
1350a1bf553SMark Brown 
1360a1bf553SMark Brown SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
1370a1bf553SMark Brown SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
138a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5,  0, 24, 1, eq_tlv),
1390a1bf553SMark Brown 
1400a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1,  8, 1, 0),
1410a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1,  4, 15, 0),
1420a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1,  0, 15, 0),
1430a1bf553SMark Brown 
1440a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2,  4, 7, 0),
1450a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2,  0, 15, 0),
1460a1bf553SMark Brown 
1470a1bf553SMark Brown SOC_SINGLE("ALC Enable Switch", WM8974_ALC1,  8, 1, 0),
1480a1bf553SMark Brown SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1,  3, 7, 0),
1490a1bf553SMark Brown SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1,  0, 7, 0),
1500a1bf553SMark Brown 
1510a1bf553SMark Brown SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2,  8, 1, 0),
1520a1bf553SMark Brown SOC_SINGLE("ALC Capture Hold", WM8974_ALC2,  4, 7, 0),
1530a1bf553SMark Brown SOC_SINGLE("ALC Capture Target", WM8974_ALC2,  0, 15, 0),
1540a1bf553SMark Brown 
1550a1bf553SMark Brown SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
1560a1bf553SMark Brown SOC_SINGLE("ALC Capture Decay", WM8974_ALC3,  4, 15, 0),
1570a1bf553SMark Brown SOC_SINGLE("ALC Capture Attack", WM8974_ALC3,  0, 15, 0),
1580a1bf553SMark Brown 
1590a1bf553SMark Brown SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE,  3, 1, 0),
1600a1bf553SMark Brown SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE,  0, 7, 0),
1610a1bf553SMark Brown 
1620a1bf553SMark Brown SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA,  7, 1, 0),
163a5f8d2f1SMark Brown SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA,  0, 63, 0, inpga_tlv),
1640a1bf553SMark Brown 
1650a1bf553SMark Brown SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL,  7, 1, 0),
1660a1bf553SMark Brown SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL,  6, 1, 1),
1678a123ee2SMark Brown SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL,  0, 63, 0, spk_tlv),
1688a123ee2SMark Brown 
1698a123ee2SMark Brown SOC_ENUM("Aux Mode", wm8974_auxmode),
1700a1bf553SMark Brown 
1710a1bf553SMark Brown SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST,  8, 1, 0),
1728a123ee2SMark Brown SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1),
1730a1bf553SMark Brown };
1740a1bf553SMark Brown 
1750a1bf553SMark Brown /* Speaker Output Mixer */
1760a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
1770a1bf553SMark Brown SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
1780a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
1790a1bf553SMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1),
1800a1bf553SMark Brown };
1810a1bf553SMark Brown 
1820a1bf553SMark Brown /* Mono Output Mixer */
1830a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
1840a1bf553SMark Brown SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
1850a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
1868a123ee2SMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0),
1878a123ee2SMark Brown };
1888a123ee2SMark Brown 
1898a123ee2SMark Brown /* Boost mixer */
1908a123ee2SMark Brown static const struct snd_kcontrol_new wm8974_boost_mixer[] = {
1918a123ee2SMark Brown SOC_DAPM_SINGLE("Aux Switch", WM8974_INPPGA, 6, 1, 0),
1928a123ee2SMark Brown };
1938a123ee2SMark Brown 
1948a123ee2SMark Brown /* Input PGA */
1958a123ee2SMark Brown static const struct snd_kcontrol_new wm8974_inpga[] = {
1968a123ee2SMark Brown SOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0),
1978a123ee2SMark Brown SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0),
1988a123ee2SMark Brown SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0),
1990a1bf553SMark Brown };
2000a1bf553SMark Brown 
2010a1bf553SMark Brown /* AUX Input boost vol */
2020a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_aux_boost_controls =
2030a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
2040a1bf553SMark Brown 
2050a1bf553SMark Brown /* Mic Input boost vol */
2060a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_mic_boost_controls =
2070a1bf553SMark Brown SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
2080a1bf553SMark Brown 
2090a1bf553SMark Brown static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
2100a1bf553SMark Brown SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
2110a1bf553SMark Brown 	&wm8974_speaker_mixer_controls[0],
2120a1bf553SMark Brown 	ARRAY_SIZE(wm8974_speaker_mixer_controls)),
2130a1bf553SMark Brown SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
2140a1bf553SMark Brown 	&wm8974_mono_mixer_controls[0],
2150a1bf553SMark Brown 	ARRAY_SIZE(wm8974_mono_mixer_controls)),
2160a1bf553SMark Brown SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
2178a123ee2SMark Brown SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0),
2180a1bf553SMark Brown SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
2190a1bf553SMark Brown SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
2200a1bf553SMark Brown SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
2210a1bf553SMark Brown SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
2220a1bf553SMark Brown 
2238a123ee2SMark Brown SND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga,
2248a123ee2SMark Brown 		   ARRAY_SIZE(wm8974_inpga)),
2258a123ee2SMark Brown SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0,
2268a123ee2SMark Brown 		   wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)),
2270a1bf553SMark Brown 
2280a1bf553SMark Brown SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
2290a1bf553SMark Brown 
2300a1bf553SMark Brown SND_SOC_DAPM_INPUT("MICN"),
2310a1bf553SMark Brown SND_SOC_DAPM_INPUT("MICP"),
2320a1bf553SMark Brown SND_SOC_DAPM_INPUT("AUX"),
2330a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("MONOOUT"),
2340a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("SPKOUTP"),
2350a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("SPKOUTN"),
2360a1bf553SMark Brown };
2370a1bf553SMark Brown 
2380a1bf553SMark Brown static const struct snd_soc_dapm_route audio_map[] = {
2390a1bf553SMark Brown 	/* Mono output mixer */
2400a1bf553SMark Brown 	{"Mono Mixer", "PCM Playback Switch", "DAC"},
2410a1bf553SMark Brown 	{"Mono Mixer", "Aux Playback Switch", "Aux Input"},
2420a1bf553SMark Brown 	{"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
2430a1bf553SMark Brown 
2440a1bf553SMark Brown 	/* Speaker output mixer */
2450a1bf553SMark Brown 	{"Speaker Mixer", "PCM Playback Switch", "DAC"},
2460a1bf553SMark Brown 	{"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
2470a1bf553SMark Brown 	{"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
2480a1bf553SMark Brown 
2490a1bf553SMark Brown 	/* Outputs */
2500a1bf553SMark Brown 	{"Mono Out", NULL, "Mono Mixer"},
2510a1bf553SMark Brown 	{"MONOOUT", NULL, "Mono Out"},
2520a1bf553SMark Brown 	{"SpkN Out", NULL, "Speaker Mixer"},
2530a1bf553SMark Brown 	{"SpkP Out", NULL, "Speaker Mixer"},
2540a1bf553SMark Brown 	{"SPKOUTN", NULL, "SpkN Out"},
2550a1bf553SMark Brown 	{"SPKOUTP", NULL, "SpkP Out"},
2560a1bf553SMark Brown 
2570a1bf553SMark Brown 	/* Boost Mixer */
2588a123ee2SMark Brown 	{"ADC", NULL, "Boost Mixer"},
2598a123ee2SMark Brown 	{"Boost Mixer", "Aux Switch", "Aux Input"},
2608a123ee2SMark Brown 	{"Boost Mixer", NULL, "Input PGA"},
2618a123ee2SMark Brown 	{"Boost Mixer", NULL, "MICP"},
2628a123ee2SMark Brown 
2638a123ee2SMark Brown 	/* Input PGA */
2648a123ee2SMark Brown 	{"Input PGA", "Aux Switch", "Aux Input"},
2658a123ee2SMark Brown 	{"Input PGA", "MicN Switch", "MICN"},
2668a123ee2SMark Brown 	{"Input PGA", "MicP Switch", "MICP"},
2670a1bf553SMark Brown 
2680a1bf553SMark Brown 	/* Inputs */
2698a123ee2SMark Brown 	{"Aux Input", NULL, "AUX"},
2700a1bf553SMark Brown };
2710a1bf553SMark Brown 
2720a1bf553SMark Brown static int wm8974_add_widgets(struct snd_soc_codec *codec)
2730a1bf553SMark Brown {
2740a1bf553SMark Brown 	snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets,
2750a1bf553SMark Brown 				  ARRAY_SIZE(wm8974_dapm_widgets));
2760a1bf553SMark Brown 
2770a1bf553SMark Brown 	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
2780a1bf553SMark Brown 
2790a1bf553SMark Brown 	return 0;
2800a1bf553SMark Brown }
2810a1bf553SMark Brown 
2820a1bf553SMark Brown struct pll_ {
283c36b2fc7SMark Brown 	unsigned int pre_div:1;
2840a1bf553SMark Brown 	unsigned int n:4;
2850a1bf553SMark Brown 	unsigned int k;
2860a1bf553SMark Brown };
2870a1bf553SMark Brown 
28891d0c3ecSMark Brown /* The size in bits of the pll divide multiplied by 10
28991d0c3ecSMark Brown  * to allow rounding later */
29091d0c3ecSMark Brown #define FIXED_PLL_SIZE ((1 << 24) * 10)
29191d0c3ecSMark Brown 
292c36b2fc7SMark Brown static void pll_factors(struct pll_ *pll_div,
293c36b2fc7SMark Brown 			unsigned int target, unsigned int source)
29491d0c3ecSMark Brown {
29591d0c3ecSMark Brown 	unsigned long long Kpart;
29691d0c3ecSMark Brown 	unsigned int K, Ndiv, Nmod;
29791d0c3ecSMark Brown 
298c36b2fc7SMark Brown 	/* There is a fixed divide by 4 in the output path */
299c36b2fc7SMark Brown 	target *= 4;
300c36b2fc7SMark Brown 
30191d0c3ecSMark Brown 	Ndiv = target / source;
30291d0c3ecSMark Brown 	if (Ndiv < 6) {
303c36b2fc7SMark Brown 		source /= 2;
304c36b2fc7SMark Brown 		pll_div->pre_div = 1;
30591d0c3ecSMark Brown 		Ndiv = target / source;
30691d0c3ecSMark Brown 	} else
307c36b2fc7SMark Brown 		pll_div->pre_div = 0;
30891d0c3ecSMark Brown 
30991d0c3ecSMark Brown 	if ((Ndiv < 6) || (Ndiv > 12))
31091d0c3ecSMark Brown 		printk(KERN_WARNING
3118b83a193SMark Brown 			"WM8974 N value %u outwith recommended range!\n",
31291d0c3ecSMark Brown 			Ndiv);
31391d0c3ecSMark Brown 
314c36b2fc7SMark Brown 	pll_div->n = Ndiv;
31591d0c3ecSMark Brown 	Nmod = target % source;
31691d0c3ecSMark Brown 	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
31791d0c3ecSMark Brown 
31891d0c3ecSMark Brown 	do_div(Kpart, source);
31991d0c3ecSMark Brown 
32091d0c3ecSMark Brown 	K = Kpart & 0xFFFFFFFF;
32191d0c3ecSMark Brown 
32291d0c3ecSMark Brown 	/* Check if we need to round */
32391d0c3ecSMark Brown 	if ((K % 10) >= 5)
32491d0c3ecSMark Brown 		K += 5;
32591d0c3ecSMark Brown 
32691d0c3ecSMark Brown 	/* Move down to proper range now rounding is done */
32791d0c3ecSMark Brown 	K /= 10;
32891d0c3ecSMark Brown 
329c36b2fc7SMark Brown 	pll_div->k = K;
33091d0c3ecSMark Brown }
3310a1bf553SMark Brown 
33285488037SMark Brown static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
33385488037SMark Brown 		int source, unsigned int freq_in, unsigned int freq_out)
3340a1bf553SMark Brown {
3350a1bf553SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
336c36b2fc7SMark Brown 	struct pll_ pll_div;
3370a1bf553SMark Brown 	u16 reg;
3380a1bf553SMark Brown 
3390a1bf553SMark Brown 	if (freq_in == 0 || freq_out == 0) {
34091d0c3ecSMark Brown 		/* Clock CODEC directly from MCLK */
3411e97f50bSMark Brown 		reg = snd_soc_read(codec, WM8974_CLOCK);
3421e97f50bSMark Brown 		snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
34391d0c3ecSMark Brown 
34491d0c3ecSMark Brown 		/* Turn off PLL */
3451e97f50bSMark Brown 		reg = snd_soc_read(codec, WM8974_POWER1);
3461e97f50bSMark Brown 		snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
3470a1bf553SMark Brown 		return 0;
3480a1bf553SMark Brown 	}
3490a1bf553SMark Brown 
350c36b2fc7SMark Brown 	pll_factors(&pll_div, freq_out, freq_in);
35191d0c3ecSMark Brown 
3521e97f50bSMark Brown 	snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
3531e97f50bSMark Brown 	snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
3541e97f50bSMark Brown 	snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
3551e97f50bSMark Brown 	snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
3561e97f50bSMark Brown 	reg = snd_soc_read(codec, WM8974_POWER1);
3571e97f50bSMark Brown 	snd_soc_write(codec, WM8974_POWER1, reg | 0x020);
3581a55b3f6SMark Brown 
35991d0c3ecSMark Brown 	/* Run CODEC from PLL instead of MCLK */
3601e97f50bSMark Brown 	reg = snd_soc_read(codec, WM8974_CLOCK);
3611e97f50bSMark Brown 	snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
36291d0c3ecSMark Brown 
36391d0c3ecSMark Brown 	return 0;
3640a1bf553SMark Brown }
3650a1bf553SMark Brown 
3660a1bf553SMark Brown /*
3670a1bf553SMark Brown  * Configure WM8974 clock dividers.
3680a1bf553SMark Brown  */
3690a1bf553SMark Brown static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
3700a1bf553SMark Brown 		int div_id, int div)
3710a1bf553SMark Brown {
3720a1bf553SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
3730a1bf553SMark Brown 	u16 reg;
3740a1bf553SMark Brown 
3750a1bf553SMark Brown 	switch (div_id) {
3760a1bf553SMark Brown 	case WM8974_OPCLKDIV:
3771e97f50bSMark Brown 		reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
3781e97f50bSMark Brown 		snd_soc_write(codec, WM8974_GPIO, reg | div);
3790a1bf553SMark Brown 		break;
3800a1bf553SMark Brown 	case WM8974_MCLKDIV:
3811e97f50bSMark Brown 		reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
3821e97f50bSMark Brown 		snd_soc_write(codec, WM8974_CLOCK, reg | div);
3830a1bf553SMark Brown 		break;
3840a1bf553SMark Brown 	case WM8974_ADCCLK:
3851e97f50bSMark Brown 		reg = snd_soc_read(codec, WM8974_ADC) & 0x1f7;
3861e97f50bSMark Brown 		snd_soc_write(codec, WM8974_ADC, reg | div);
3870a1bf553SMark Brown 		break;
3880a1bf553SMark Brown 	case WM8974_DACCLK:
3891e97f50bSMark Brown 		reg = snd_soc_read(codec, WM8974_DAC) & 0x1f7;
3901e97f50bSMark Brown 		snd_soc_write(codec, WM8974_DAC, reg | div);
3910a1bf553SMark Brown 		break;
3920a1bf553SMark Brown 	case WM8974_BCLKDIV:
3931e97f50bSMark Brown 		reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
3941e97f50bSMark Brown 		snd_soc_write(codec, WM8974_CLOCK, reg | div);
3950a1bf553SMark Brown 		break;
3960a1bf553SMark Brown 	default:
3970a1bf553SMark Brown 		return -EINVAL;
3980a1bf553SMark Brown 	}
3990a1bf553SMark Brown 
4000a1bf553SMark Brown 	return 0;
4010a1bf553SMark Brown }
4020a1bf553SMark Brown 
4030a1bf553SMark Brown static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
4040a1bf553SMark Brown 		unsigned int fmt)
4050a1bf553SMark Brown {
4060a1bf553SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
4070a1bf553SMark Brown 	u16 iface = 0;
4081e97f50bSMark Brown 	u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
4090a1bf553SMark Brown 
4100a1bf553SMark Brown 	/* set master/slave audio interface */
4110a1bf553SMark Brown 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
4120a1bf553SMark Brown 	case SND_SOC_DAIFMT_CBM_CFM:
4130a1bf553SMark Brown 		clk |= 0x0001;
4140a1bf553SMark Brown 		break;
4150a1bf553SMark Brown 	case SND_SOC_DAIFMT_CBS_CFS:
4160a1bf553SMark Brown 		break;
4170a1bf553SMark Brown 	default:
4180a1bf553SMark Brown 		return -EINVAL;
4190a1bf553SMark Brown 	}
4200a1bf553SMark Brown 
4210a1bf553SMark Brown 	/* interface format */
4220a1bf553SMark Brown 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
4230a1bf553SMark Brown 	case SND_SOC_DAIFMT_I2S:
4240a1bf553SMark Brown 		iface |= 0x0010;
4250a1bf553SMark Brown 		break;
4260a1bf553SMark Brown 	case SND_SOC_DAIFMT_RIGHT_J:
4270a1bf553SMark Brown 		break;
4280a1bf553SMark Brown 	case SND_SOC_DAIFMT_LEFT_J:
4290a1bf553SMark Brown 		iface |= 0x0008;
4300a1bf553SMark Brown 		break;
4310a1bf553SMark Brown 	case SND_SOC_DAIFMT_DSP_A:
4320a1bf553SMark Brown 		iface |= 0x00018;
4330a1bf553SMark Brown 		break;
4340a1bf553SMark Brown 	default:
4350a1bf553SMark Brown 		return -EINVAL;
4360a1bf553SMark Brown 	}
4370a1bf553SMark Brown 
4380a1bf553SMark Brown 	/* clock inversion */
4390a1bf553SMark Brown 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
4400a1bf553SMark Brown 	case SND_SOC_DAIFMT_NB_NF:
4410a1bf553SMark Brown 		break;
4420a1bf553SMark Brown 	case SND_SOC_DAIFMT_IB_IF:
4430a1bf553SMark Brown 		iface |= 0x0180;
4440a1bf553SMark Brown 		break;
4450a1bf553SMark Brown 	case SND_SOC_DAIFMT_IB_NF:
4460a1bf553SMark Brown 		iface |= 0x0100;
4470a1bf553SMark Brown 		break;
4480a1bf553SMark Brown 	case SND_SOC_DAIFMT_NB_IF:
4490a1bf553SMark Brown 		iface |= 0x0080;
4500a1bf553SMark Brown 		break;
4510a1bf553SMark Brown 	default:
4520a1bf553SMark Brown 		return -EINVAL;
4530a1bf553SMark Brown 	}
4540a1bf553SMark Brown 
4551e97f50bSMark Brown 	snd_soc_write(codec, WM8974_IFACE, iface);
4561e97f50bSMark Brown 	snd_soc_write(codec, WM8974_CLOCK, clk);
4570a1bf553SMark Brown 	return 0;
4580a1bf553SMark Brown }
4590a1bf553SMark Brown 
4600a1bf553SMark Brown static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
4610a1bf553SMark Brown 				struct snd_pcm_hw_params *params,
4620a1bf553SMark Brown 				struct snd_soc_dai *dai)
4630a1bf553SMark Brown {
4640a1bf553SMark Brown 	struct snd_soc_codec *codec = dai->codec;
4651e97f50bSMark Brown 	u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
4661e97f50bSMark Brown 	u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
4670a1bf553SMark Brown 
4680a1bf553SMark Brown 	/* bit size */
4690a1bf553SMark Brown 	switch (params_format(params)) {
4700a1bf553SMark Brown 	case SNDRV_PCM_FORMAT_S16_LE:
4710a1bf553SMark Brown 		break;
4720a1bf553SMark Brown 	case SNDRV_PCM_FORMAT_S20_3LE:
4730a1bf553SMark Brown 		iface |= 0x0020;
4740a1bf553SMark Brown 		break;
4750a1bf553SMark Brown 	case SNDRV_PCM_FORMAT_S24_LE:
4760a1bf553SMark Brown 		iface |= 0x0040;
4770a1bf553SMark Brown 		break;
4780a1bf553SMark Brown 	case SNDRV_PCM_FORMAT_S32_LE:
4790a1bf553SMark Brown 		iface |= 0x0060;
4800a1bf553SMark Brown 		break;
4810a1bf553SMark Brown 	}
4820a1bf553SMark Brown 
4830a1bf553SMark Brown 	/* filter coefficient */
4840a1bf553SMark Brown 	switch (params_rate(params)) {
4850a1bf553SMark Brown 	case SNDRV_PCM_RATE_8000:
4860a1bf553SMark Brown 		adn |= 0x5 << 1;
4870a1bf553SMark Brown 		break;
4880a1bf553SMark Brown 	case SNDRV_PCM_RATE_11025:
4890a1bf553SMark Brown 		adn |= 0x4 << 1;
4900a1bf553SMark Brown 		break;
4910a1bf553SMark Brown 	case SNDRV_PCM_RATE_16000:
4920a1bf553SMark Brown 		adn |= 0x3 << 1;
4930a1bf553SMark Brown 		break;
4940a1bf553SMark Brown 	case SNDRV_PCM_RATE_22050:
4950a1bf553SMark Brown 		adn |= 0x2 << 1;
4960a1bf553SMark Brown 		break;
4970a1bf553SMark Brown 	case SNDRV_PCM_RATE_32000:
4980a1bf553SMark Brown 		adn |= 0x1 << 1;
4990a1bf553SMark Brown 		break;
5000a1bf553SMark Brown 	case SNDRV_PCM_RATE_44100:
5018b83a193SMark Brown 	case SNDRV_PCM_RATE_48000:
5020a1bf553SMark Brown 		break;
5030a1bf553SMark Brown 	}
5040a1bf553SMark Brown 
5051e97f50bSMark Brown 	snd_soc_write(codec, WM8974_IFACE, iface);
5061e97f50bSMark Brown 	snd_soc_write(codec, WM8974_ADD, adn);
5070a1bf553SMark Brown 	return 0;
5080a1bf553SMark Brown }
5090a1bf553SMark Brown 
5100a1bf553SMark Brown static int wm8974_mute(struct snd_soc_dai *dai, int mute)
5110a1bf553SMark Brown {
5120a1bf553SMark Brown 	struct snd_soc_codec *codec = dai->codec;
5131e97f50bSMark Brown 	u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
5140a1bf553SMark Brown 
5150a1bf553SMark Brown 	if (mute)
5161e97f50bSMark Brown 		snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
5170a1bf553SMark Brown 	else
5181e97f50bSMark Brown 		snd_soc_write(codec, WM8974_DAC, mute_reg);
5190a1bf553SMark Brown 	return 0;
5200a1bf553SMark Brown }
5210a1bf553SMark Brown 
5220a1bf553SMark Brown /* liam need to make this lower power with dapm */
5230a1bf553SMark Brown static int wm8974_set_bias_level(struct snd_soc_codec *codec,
5240a1bf553SMark Brown 	enum snd_soc_bias_level level)
5250a1bf553SMark Brown {
5261e97f50bSMark Brown 	u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
527df1ef7a3SMark Brown 
5280a1bf553SMark Brown 	switch (level) {
5290a1bf553SMark Brown 	case SND_SOC_BIAS_ON:
5300a1bf553SMark Brown 	case SND_SOC_BIAS_PREPARE:
531df1ef7a3SMark Brown 		power1 |= 0x1;  /* VMID 50k */
5321e97f50bSMark Brown 		snd_soc_write(codec, WM8974_POWER1, power1);
5330a1bf553SMark Brown 		break;
534df1ef7a3SMark Brown 
5350a1bf553SMark Brown 	case SND_SOC_BIAS_STANDBY:
536df1ef7a3SMark Brown 		power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
537df1ef7a3SMark Brown 
538df1ef7a3SMark Brown 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
539df1ef7a3SMark Brown 			/* Initial cap charge at VMID 5k */
5401e97f50bSMark Brown 			snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
541df1ef7a3SMark Brown 			mdelay(100);
542df1ef7a3SMark Brown 		}
543df1ef7a3SMark Brown 
544df1ef7a3SMark Brown 		power1 |= 0x2;  /* VMID 500k */
5451e97f50bSMark Brown 		snd_soc_write(codec, WM8974_POWER1, power1);
5460a1bf553SMark Brown 		break;
547df1ef7a3SMark Brown 
5480a1bf553SMark Brown 	case SND_SOC_BIAS_OFF:
5491e97f50bSMark Brown 		snd_soc_write(codec, WM8974_POWER1, 0);
5501e97f50bSMark Brown 		snd_soc_write(codec, WM8974_POWER2, 0);
5511e97f50bSMark Brown 		snd_soc_write(codec, WM8974_POWER3, 0);
5520a1bf553SMark Brown 		break;
5530a1bf553SMark Brown 	}
554df1ef7a3SMark Brown 
5550a1bf553SMark Brown 	codec->bias_level = level;
5560a1bf553SMark Brown 	return 0;
5570a1bf553SMark Brown }
5580a1bf553SMark Brown 
5591a55b3f6SMark Brown #define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
5600a1bf553SMark Brown 
5610a1bf553SMark Brown #define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
5620a1bf553SMark Brown 	SNDRV_PCM_FMTBIT_S24_LE)
5630a1bf553SMark Brown 
5640a1bf553SMark Brown static struct snd_soc_dai_ops wm8974_ops = {
5650a1bf553SMark Brown 	.hw_params = wm8974_pcm_hw_params,
5660a1bf553SMark Brown 	.digital_mute = wm8974_mute,
5670a1bf553SMark Brown 	.set_fmt = wm8974_set_dai_fmt,
5680a1bf553SMark Brown 	.set_clkdiv = wm8974_set_dai_clkdiv,
5690a1bf553SMark Brown 	.set_pll = wm8974_set_dai_pll,
5700a1bf553SMark Brown };
5710a1bf553SMark Brown 
5720a1bf553SMark Brown struct snd_soc_dai wm8974_dai = {
5730a1bf553SMark Brown 	.name = "WM8974 HiFi",
5740a1bf553SMark Brown 	.playback = {
5750a1bf553SMark Brown 		.stream_name = "Playback",
5760a1bf553SMark Brown 		.channels_min = 1,
57733d81af4SMark Brown 		.channels_max = 2,   /* Only 1 channel of data */
5780a1bf553SMark Brown 		.rates = WM8974_RATES,
5790a1bf553SMark Brown 		.formats = WM8974_FORMATS,},
5800a1bf553SMark Brown 	.capture = {
5810a1bf553SMark Brown 		.stream_name = "Capture",
5820a1bf553SMark Brown 		.channels_min = 1,
58333d81af4SMark Brown 		.channels_max = 2,   /* Only 1 channel of data */
5840a1bf553SMark Brown 		.rates = WM8974_RATES,
5850a1bf553SMark Brown 		.formats = WM8974_FORMATS,},
5860a1bf553SMark Brown 	.ops = &wm8974_ops,
587cb11d39eSMark Brown 	.symmetric_rates = 1,
5880a1bf553SMark Brown };
5890a1bf553SMark Brown EXPORT_SYMBOL_GPL(wm8974_dai);
5900a1bf553SMark Brown 
5910a1bf553SMark Brown static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
5920a1bf553SMark Brown {
5930a1bf553SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
5940a1bf553SMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
5950a1bf553SMark Brown 
5960a1bf553SMark Brown 	wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
5970a1bf553SMark Brown 	return 0;
5980a1bf553SMark Brown }
5990a1bf553SMark Brown 
6000a1bf553SMark Brown static int wm8974_resume(struct platform_device *pdev)
6010a1bf553SMark Brown {
6020a1bf553SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
6030a1bf553SMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
6040a1bf553SMark Brown 	int i;
6050a1bf553SMark Brown 	u8 data[2];
6060a1bf553SMark Brown 	u16 *cache = codec->reg_cache;
6070a1bf553SMark Brown 
6080a1bf553SMark Brown 	/* Sync reg_cache with the hardware */
6090a1bf553SMark Brown 	for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
6100a1bf553SMark Brown 		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
6110a1bf553SMark Brown 		data[1] = cache[i] & 0x00ff;
6120a1bf553SMark Brown 		codec->hw_write(codec->control_data, data, 2);
6130a1bf553SMark Brown 	}
6140a1bf553SMark Brown 	wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
6150a1bf553SMark Brown 	wm8974_set_bias_level(codec, codec->suspend_bias_level);
6160a1bf553SMark Brown 	return 0;
6170a1bf553SMark Brown }
6180a1bf553SMark Brown 
6194fcbbb67SMark Brown static int wm8974_probe(struct platform_device *pdev)
6200a1bf553SMark Brown {
6214fcbbb67SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
6224fcbbb67SMark Brown 	struct snd_soc_codec *codec;
6230a1bf553SMark Brown 	int ret = 0;
6240a1bf553SMark Brown 
6254fcbbb67SMark Brown 	if (wm8974_codec == NULL) {
6264fcbbb67SMark Brown 		dev_err(&pdev->dev, "Codec device not registered\n");
6274fcbbb67SMark Brown 		return -ENODEV;
6284fcbbb67SMark Brown 	}
6290a1bf553SMark Brown 
6304fcbbb67SMark Brown 	socdev->card->codec = wm8974_codec;
6314fcbbb67SMark Brown 	codec = wm8974_codec;
6320a1bf553SMark Brown 
6330a1bf553SMark Brown 	/* register pcms */
6340a1bf553SMark Brown 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
6350a1bf553SMark Brown 	if (ret < 0) {
6364fcbbb67SMark Brown 		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
6370a1bf553SMark Brown 		goto pcm_err;
6380a1bf553SMark Brown 	}
6390a1bf553SMark Brown 
6404fcbbb67SMark Brown 	snd_soc_add_controls(codec, wm8974_snd_controls,
6414fcbbb67SMark Brown 			     ARRAY_SIZE(wm8974_snd_controls));
6420a1bf553SMark Brown 	wm8974_add_widgets(codec);
6434fcbbb67SMark Brown 
6440a1bf553SMark Brown 	return ret;
6450a1bf553SMark Brown 
6460a1bf553SMark Brown pcm_err:
6470a1bf553SMark Brown 	return ret;
6480a1bf553SMark Brown }
6490a1bf553SMark Brown 
6500a1bf553SMark Brown /* power down chip */
6510a1bf553SMark Brown static int wm8974_remove(struct platform_device *pdev)
6520a1bf553SMark Brown {
6530a1bf553SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
6540a1bf553SMark Brown 
6550a1bf553SMark Brown 	snd_soc_free_pcms(socdev);
6560a1bf553SMark Brown 	snd_soc_dapm_free(socdev);
6570a1bf553SMark Brown 
6580a1bf553SMark Brown 	return 0;
6590a1bf553SMark Brown }
6600a1bf553SMark Brown 
6610a1bf553SMark Brown struct snd_soc_codec_device soc_codec_dev_wm8974 = {
6620a1bf553SMark Brown 	.probe = 	wm8974_probe,
6630a1bf553SMark Brown 	.remove = 	wm8974_remove,
6640a1bf553SMark Brown 	.suspend = 	wm8974_suspend,
6650a1bf553SMark Brown 	.resume =	wm8974_resume,
6660a1bf553SMark Brown };
6670a1bf553SMark Brown EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
6680a1bf553SMark Brown 
6694fcbbb67SMark Brown static __devinit int wm8974_register(struct wm8974_priv *wm8974)
6704fcbbb67SMark Brown {
6714fcbbb67SMark Brown 	int ret;
6724fcbbb67SMark Brown 	struct snd_soc_codec *codec = &wm8974->codec;
6734fcbbb67SMark Brown 
6744fcbbb67SMark Brown 	if (wm8974_codec) {
6754fcbbb67SMark Brown 		dev_err(codec->dev, "Another WM8974 is registered\n");
6764fcbbb67SMark Brown 		return -EINVAL;
6774fcbbb67SMark Brown 	}
6784fcbbb67SMark Brown 
6794fcbbb67SMark Brown 	mutex_init(&codec->mutex);
6804fcbbb67SMark Brown 	INIT_LIST_HEAD(&codec->dapm_widgets);
6814fcbbb67SMark Brown 	INIT_LIST_HEAD(&codec->dapm_paths);
6824fcbbb67SMark Brown 
6834fcbbb67SMark Brown 	codec->private_data = wm8974;
6844fcbbb67SMark Brown 	codec->name = "WM8974";
6854fcbbb67SMark Brown 	codec->owner = THIS_MODULE;
6864fcbbb67SMark Brown 	codec->bias_level = SND_SOC_BIAS_OFF;
6874fcbbb67SMark Brown 	codec->set_bias_level = wm8974_set_bias_level;
6884fcbbb67SMark Brown 	codec->dai = &wm8974_dai;
6894fcbbb67SMark Brown 	codec->num_dai = 1;
6904fcbbb67SMark Brown 	codec->reg_cache_size = WM8974_CACHEREGNUM;
6914fcbbb67SMark Brown 	codec->reg_cache = &wm8974->reg_cache;
6924fcbbb67SMark Brown 
6931e97f50bSMark Brown 	ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
6941e97f50bSMark Brown 	if (ret < 0) {
6951e97f50bSMark Brown 		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
6961e97f50bSMark Brown 		goto err;
6971e97f50bSMark Brown 	}
6981e97f50bSMark Brown 
6994fcbbb67SMark Brown 	memcpy(codec->reg_cache, wm8974_reg, sizeof(wm8974_reg));
7004fcbbb67SMark Brown 
7014fcbbb67SMark Brown 	ret = wm8974_reset(codec);
7024fcbbb67SMark Brown 	if (ret < 0) {
7034fcbbb67SMark Brown 		dev_err(codec->dev, "Failed to issue reset\n");
7041e97f50bSMark Brown 		goto err;
7054fcbbb67SMark Brown 	}
7064fcbbb67SMark Brown 
7074fcbbb67SMark Brown 	wm8974_dai.dev = codec->dev;
7084fcbbb67SMark Brown 
7094fcbbb67SMark Brown 	wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
7104fcbbb67SMark Brown 
7114fcbbb67SMark Brown 	wm8974_codec = codec;
7124fcbbb67SMark Brown 
7134fcbbb67SMark Brown 	ret = snd_soc_register_codec(codec);
7144fcbbb67SMark Brown 	if (ret != 0) {
7154fcbbb67SMark Brown 		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
7161e97f50bSMark Brown 		goto err;
7174fcbbb67SMark Brown 	}
7184fcbbb67SMark Brown 
7194fcbbb67SMark Brown 	ret = snd_soc_register_dai(&wm8974_dai);
7204fcbbb67SMark Brown 	if (ret != 0) {
7214fcbbb67SMark Brown 		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
7221e97f50bSMark Brown 		goto err_codec;
7234fcbbb67SMark Brown 	}
7244fcbbb67SMark Brown 
7254fcbbb67SMark Brown 	return 0;
7261e97f50bSMark Brown 
7271e97f50bSMark Brown err_codec:
7281e97f50bSMark Brown 	snd_soc_unregister_codec(codec);
7291e97f50bSMark Brown err:
7301e97f50bSMark Brown 	kfree(wm8974);
7311e97f50bSMark Brown 	return ret;
7324fcbbb67SMark Brown }
7334fcbbb67SMark Brown 
7344fcbbb67SMark Brown static __devexit void wm8974_unregister(struct wm8974_priv *wm8974)
7354fcbbb67SMark Brown {
7364fcbbb67SMark Brown 	wm8974_set_bias_level(&wm8974->codec, SND_SOC_BIAS_OFF);
7374fcbbb67SMark Brown 	snd_soc_unregister_dai(&wm8974_dai);
7384fcbbb67SMark Brown 	snd_soc_unregister_codec(&wm8974->codec);
7394fcbbb67SMark Brown 	kfree(wm8974);
7404fcbbb67SMark Brown 	wm8974_codec = NULL;
7414fcbbb67SMark Brown }
7424fcbbb67SMark Brown 
7434fcbbb67SMark Brown static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
7444fcbbb67SMark Brown 				      const struct i2c_device_id *id)
7454fcbbb67SMark Brown {
7464fcbbb67SMark Brown 	struct wm8974_priv *wm8974;
7474fcbbb67SMark Brown 	struct snd_soc_codec *codec;
7484fcbbb67SMark Brown 
7494fcbbb67SMark Brown 	wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
7504fcbbb67SMark Brown 	if (wm8974 == NULL)
7514fcbbb67SMark Brown 		return -ENOMEM;
7524fcbbb67SMark Brown 
7534fcbbb67SMark Brown 	codec = &wm8974->codec;
7544fcbbb67SMark Brown 	codec->hw_write = (hw_write_t)i2c_master_send;
7554fcbbb67SMark Brown 
7564fcbbb67SMark Brown 	i2c_set_clientdata(i2c, wm8974);
7574fcbbb67SMark Brown 	codec->control_data = i2c;
7584fcbbb67SMark Brown 
7594fcbbb67SMark Brown 	codec->dev = &i2c->dev;
7604fcbbb67SMark Brown 
7614fcbbb67SMark Brown 	return wm8974_register(wm8974);
7624fcbbb67SMark Brown }
7634fcbbb67SMark Brown 
7644fcbbb67SMark Brown static __devexit int wm8974_i2c_remove(struct i2c_client *client)
7654fcbbb67SMark Brown {
7664fcbbb67SMark Brown 	struct wm8974_priv *wm8974 = i2c_get_clientdata(client);
7674fcbbb67SMark Brown 	wm8974_unregister(wm8974);
7684fcbbb67SMark Brown 	return 0;
7694fcbbb67SMark Brown }
7704fcbbb67SMark Brown 
7714fcbbb67SMark Brown static const struct i2c_device_id wm8974_i2c_id[] = {
7724fcbbb67SMark Brown 	{ "wm8974", 0 },
7734fcbbb67SMark Brown 	{ }
7744fcbbb67SMark Brown };
7754fcbbb67SMark Brown MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
7764fcbbb67SMark Brown 
7774fcbbb67SMark Brown static struct i2c_driver wm8974_i2c_driver = {
7784fcbbb67SMark Brown 	.driver = {
7798b83a193SMark Brown 		.name = "WM8974",
7804fcbbb67SMark Brown 		.owner = THIS_MODULE,
7814fcbbb67SMark Brown 	},
7824fcbbb67SMark Brown 	.probe =    wm8974_i2c_probe,
7834fcbbb67SMark Brown 	.remove =   __devexit_p(wm8974_i2c_remove),
7844fcbbb67SMark Brown 	.id_table = wm8974_i2c_id,
7854fcbbb67SMark Brown };
7864fcbbb67SMark Brown 
7870a1bf553SMark Brown static int __init wm8974_modinit(void)
7880a1bf553SMark Brown {
7894fcbbb67SMark Brown 	return i2c_add_driver(&wm8974_i2c_driver);
7900a1bf553SMark Brown }
7910a1bf553SMark Brown module_init(wm8974_modinit);
7920a1bf553SMark Brown 
7930a1bf553SMark Brown static void __exit wm8974_exit(void)
7940a1bf553SMark Brown {
7954fcbbb67SMark Brown 	i2c_del_driver(&wm8974_i2c_driver);
7960a1bf553SMark Brown }
7970a1bf553SMark Brown module_exit(wm8974_exit);
7980a1bf553SMark Brown 
7990a1bf553SMark Brown MODULE_DESCRIPTION("ASoC WM8974 driver");
8000a1bf553SMark Brown MODULE_AUTHOR("Liam Girdwood");
8010a1bf553SMark Brown MODULE_LICENSE("GPL");
802