xref: /linux/sound/soc/codecs/wm8974.c (revision 1a55b3f6ed1d917dd26271dae19dda088d820540)
10a1bf553SMark Brown /*
20a1bf553SMark Brown  * wm8974.c  --  WM8974 ALSA Soc Audio driver
30a1bf553SMark Brown  *
40a1bf553SMark Brown  * Copyright 2006 Wolfson Microelectronics PLC.
50a1bf553SMark Brown  *
60a1bf553SMark 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/version.h>
160a1bf553SMark Brown #include <linux/kernel.h>
170a1bf553SMark Brown #include <linux/init.h>
180a1bf553SMark Brown #include <linux/delay.h>
190a1bf553SMark Brown #include <linux/pm.h>
200a1bf553SMark Brown #include <linux/i2c.h>
210a1bf553SMark Brown #include <linux/platform_device.h>
220a1bf553SMark Brown #include <sound/core.h>
230a1bf553SMark Brown #include <sound/pcm.h>
240a1bf553SMark Brown #include <sound/pcm_params.h>
250a1bf553SMark Brown #include <sound/soc.h>
260a1bf553SMark Brown #include <sound/soc-dapm.h>
270a1bf553SMark Brown #include <sound/initval.h>
280a1bf553SMark Brown 
290a1bf553SMark Brown #include "wm8974.h"
300a1bf553SMark Brown 
310a1bf553SMark Brown struct snd_soc_codec_device soc_codec_dev_wm8974;
320a1bf553SMark Brown 
330a1bf553SMark Brown /*
340a1bf553SMark Brown  * wm8974 register cache
350a1bf553SMark Brown  * We can't read the WM8974 register space when we are
360a1bf553SMark Brown  * using 2 wire for device control, so we cache them instead.
370a1bf553SMark Brown  */
380a1bf553SMark Brown static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
390a1bf553SMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
400a1bf553SMark Brown 	0x0050, 0x0000, 0x0140, 0x0000,
410a1bf553SMark Brown 	0x0000, 0x0000, 0x0000, 0x00ff,
420a1bf553SMark Brown 	0x0000, 0x0000, 0x0100, 0x00ff,
430a1bf553SMark Brown 	0x0000, 0x0000, 0x012c, 0x002c,
440a1bf553SMark Brown 	0x002c, 0x002c, 0x002c, 0x0000,
450a1bf553SMark Brown 	0x0032, 0x0000, 0x0000, 0x0000,
460a1bf553SMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
470a1bf553SMark Brown 	0x0038, 0x000b, 0x0032, 0x0000,
480a1bf553SMark Brown 	0x0008, 0x000c, 0x0093, 0x00e9,
490a1bf553SMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
500a1bf553SMark Brown 	0x0003, 0x0010, 0x0000, 0x0000,
510a1bf553SMark Brown 	0x0000, 0x0002, 0x0000, 0x0000,
520a1bf553SMark Brown 	0x0000, 0x0000, 0x0039, 0x0000,
530a1bf553SMark Brown 	0x0000,
540a1bf553SMark Brown };
550a1bf553SMark Brown 
560a1bf553SMark Brown /*
570a1bf553SMark Brown  * read wm8974 register cache
580a1bf553SMark Brown  */
590a1bf553SMark Brown static inline unsigned int wm8974_read_reg_cache(struct snd_soc_codec *codec,
600a1bf553SMark Brown 	unsigned int reg)
610a1bf553SMark Brown {
620a1bf553SMark Brown 	u16 *cache = codec->reg_cache;
630a1bf553SMark Brown 	if (reg == WM8974_RESET)
640a1bf553SMark Brown 		return 0;
650a1bf553SMark Brown 	if (reg >= WM8974_CACHEREGNUM)
660a1bf553SMark Brown 		return -1;
670a1bf553SMark Brown 	return cache[reg];
680a1bf553SMark Brown }
690a1bf553SMark Brown 
700a1bf553SMark Brown /*
710a1bf553SMark Brown  * write wm8974 register cache
720a1bf553SMark Brown  */
730a1bf553SMark Brown static inline void wm8974_write_reg_cache(struct snd_soc_codec *codec,
740a1bf553SMark Brown 	u16 reg, unsigned int value)
750a1bf553SMark Brown {
760a1bf553SMark Brown 	u16 *cache = codec->reg_cache;
770a1bf553SMark Brown 	if (reg >= WM8974_CACHEREGNUM)
780a1bf553SMark Brown 		return;
790a1bf553SMark Brown 	cache[reg] = value;
800a1bf553SMark Brown }
810a1bf553SMark Brown 
820a1bf553SMark Brown /*
830a1bf553SMark Brown  * write to the WM8974 register space
840a1bf553SMark Brown  */
850a1bf553SMark Brown static int wm8974_write(struct snd_soc_codec *codec, unsigned int reg,
860a1bf553SMark Brown 	unsigned int value)
870a1bf553SMark Brown {
880a1bf553SMark Brown 	u8 data[2];
890a1bf553SMark Brown 
900a1bf553SMark Brown 	/* data is
910a1bf553SMark Brown 	 *   D15..D9 WM8974 register offset
920a1bf553SMark Brown 	 *   D8...D0 register data
930a1bf553SMark Brown 	 */
940a1bf553SMark Brown 	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
950a1bf553SMark Brown 	data[1] = value & 0x00ff;
960a1bf553SMark Brown 
970a1bf553SMark Brown 	wm8974_write_reg_cache(codec, reg, value);
980a1bf553SMark Brown 	if (codec->hw_write(codec->control_data, data, 2) == 2)
990a1bf553SMark Brown 		return 0;
1000a1bf553SMark Brown 	else
1010a1bf553SMark Brown 		return -EIO;
1020a1bf553SMark Brown }
1030a1bf553SMark Brown 
1040a1bf553SMark Brown #define wm8974_reset(c)	wm8974_write(c, WM8974_RESET, 0)
1050a1bf553SMark Brown 
1060a1bf553SMark Brown static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
1070a1bf553SMark Brown static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
1080a1bf553SMark Brown static const char *wm8974_eqmode[] = {"Capture", "Playback" };
1090a1bf553SMark Brown static const char *wm8974_bw[] = {"Narrow", "Wide" };
1100a1bf553SMark Brown static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
1110a1bf553SMark Brown static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
1120a1bf553SMark Brown static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
1130a1bf553SMark Brown static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
1140a1bf553SMark Brown static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
1150a1bf553SMark Brown static const char *wm8974_alc[] = {"ALC", "Limiter" };
1160a1bf553SMark Brown 
1170a1bf553SMark Brown static const struct soc_enum wm8974_enum[] = {
1180a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
1190a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
1200a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_DAC,  4, 4, wm8974_deemp),
1210a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ1,  8, 2, wm8974_eqmode),
1220a1bf553SMark Brown 
1230a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ1,  5, 4, wm8974_eq1),
1240a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ2,  8, 2, wm8974_bw),
1250a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ2,  5, 4, wm8974_eq2),
1260a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ3,  8, 2, wm8974_bw),
1270a1bf553SMark Brown 
1280a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ3,  5, 4, wm8974_eq3),
1290a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ4,  8, 2, wm8974_bw),
1300a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ4,  5, 4, wm8974_eq4),
1310a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ5,  8, 2, wm8974_bw),
1320a1bf553SMark Brown 
1330a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ5,  5, 4, wm8974_eq5),
1340a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_ALC3,  8, 2, wm8974_alc),
1350a1bf553SMark Brown };
1360a1bf553SMark Brown 
1370a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_snd_controls[] = {
1380a1bf553SMark Brown 
1390a1bf553SMark Brown SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
1400a1bf553SMark Brown 
1410a1bf553SMark Brown SOC_ENUM("DAC Companding", wm8974_enum[1]),
1420a1bf553SMark Brown SOC_ENUM("ADC Companding", wm8974_enum[0]),
1430a1bf553SMark Brown 
1440a1bf553SMark Brown SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
1450a1bf553SMark Brown SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
1460a1bf553SMark Brown 
1470a1bf553SMark Brown SOC_SINGLE("PCM Volume", WM8974_DACVOL, 0, 127, 0),
1480a1bf553SMark Brown 
1490a1bf553SMark Brown SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
1500a1bf553SMark Brown SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
1510a1bf553SMark Brown SOC_SINGLE("ADC Inversion Switch", WM8974_COMP, 0, 1, 0),
1520a1bf553SMark Brown 
1530a1bf553SMark Brown SOC_SINGLE("Capture Volume", WM8974_ADCVOL,  0, 127, 0),
1540a1bf553SMark Brown 
1550a1bf553SMark Brown SOC_ENUM("Equaliser Function", wm8974_enum[3]),
1560a1bf553SMark Brown SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
1570a1bf553SMark Brown SOC_SINGLE("EQ1 Volume", WM8974_EQ1,  0, 31, 1),
1580a1bf553SMark Brown 
1590a1bf553SMark Brown SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
1600a1bf553SMark Brown SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
1610a1bf553SMark Brown SOC_SINGLE("EQ2 Volume", WM8974_EQ2,  0, 31, 1),
1620a1bf553SMark Brown 
1630a1bf553SMark Brown SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
1640a1bf553SMark Brown SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
1650a1bf553SMark Brown SOC_SINGLE("EQ3 Volume", WM8974_EQ3,  0, 31, 1),
1660a1bf553SMark Brown 
1670a1bf553SMark Brown SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
1680a1bf553SMark Brown SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
1690a1bf553SMark Brown SOC_SINGLE("EQ4 Volume", WM8974_EQ4,  0, 31, 1),
1700a1bf553SMark Brown 
1710a1bf553SMark Brown SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
1720a1bf553SMark Brown SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
1730a1bf553SMark Brown SOC_SINGLE("EQ5 Volume", WM8974_EQ5,  0, 31, 1),
1740a1bf553SMark Brown 
1750a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1,  8, 1, 0),
1760a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1,  4, 15, 0),
1770a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1,  0, 15, 0),
1780a1bf553SMark Brown 
1790a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2,  4, 7, 0),
1800a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2,  0, 15, 0),
1810a1bf553SMark Brown 
1820a1bf553SMark Brown SOC_SINGLE("ALC Enable Switch", WM8974_ALC1,  8, 1, 0),
1830a1bf553SMark Brown SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1,  3, 7, 0),
1840a1bf553SMark Brown SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1,  0, 7, 0),
1850a1bf553SMark Brown 
1860a1bf553SMark Brown SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2,  8, 1, 0),
1870a1bf553SMark Brown SOC_SINGLE("ALC Capture Hold", WM8974_ALC2,  4, 7, 0),
1880a1bf553SMark Brown SOC_SINGLE("ALC Capture Target", WM8974_ALC2,  0, 15, 0),
1890a1bf553SMark Brown 
1900a1bf553SMark Brown SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
1910a1bf553SMark Brown SOC_SINGLE("ALC Capture Decay", WM8974_ALC3,  4, 15, 0),
1920a1bf553SMark Brown SOC_SINGLE("ALC Capture Attack", WM8974_ALC3,  0, 15, 0),
1930a1bf553SMark Brown 
1940a1bf553SMark Brown SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE,  3, 1, 0),
1950a1bf553SMark Brown SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE,  0, 7, 0),
1960a1bf553SMark Brown 
1970a1bf553SMark Brown SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA,  7, 1, 0),
1980a1bf553SMark Brown SOC_SINGLE("Capture PGA Volume", WM8974_INPPGA,  0, 63, 0),
1990a1bf553SMark Brown 
2000a1bf553SMark Brown SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL,  7, 1, 0),
2010a1bf553SMark Brown SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL,  6, 1, 1),
2020a1bf553SMark Brown SOC_SINGLE("Speaker Playback Volume", WM8974_SPKVOL,  0, 63, 0),
2030a1bf553SMark Brown 
2040a1bf553SMark Brown SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST,  8, 1, 0),
2050a1bf553SMark Brown SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 0),
2060a1bf553SMark Brown };
2070a1bf553SMark Brown 
2080a1bf553SMark Brown /* add non dapm controls */
2090a1bf553SMark Brown static int wm8974_add_controls(struct snd_soc_codec *codec)
2100a1bf553SMark Brown {
2110a1bf553SMark Brown 	int err, i;
2120a1bf553SMark Brown 
2130a1bf553SMark Brown 	for (i = 0; i < ARRAY_SIZE(wm8974_snd_controls); i++) {
2140a1bf553SMark Brown 		err = snd_ctl_add(codec->card,
215*1a55b3f6SMark Brown 				  snd_soc_cnew(&wm8974_snd_controls[i],
216*1a55b3f6SMark Brown 					       codec, NULL));
2170a1bf553SMark Brown 		if (err < 0)
2180a1bf553SMark Brown 			return err;
2190a1bf553SMark Brown 	}
2200a1bf553SMark Brown 
2210a1bf553SMark Brown 	return 0;
2220a1bf553SMark Brown }
2230a1bf553SMark Brown 
2240a1bf553SMark Brown /* Speaker Output Mixer */
2250a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
2260a1bf553SMark Brown SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
2270a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
2280a1bf553SMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1),
2290a1bf553SMark Brown };
2300a1bf553SMark Brown 
2310a1bf553SMark Brown /* Mono Output Mixer */
2320a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
2330a1bf553SMark Brown SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
2340a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
2350a1bf553SMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 1),
2360a1bf553SMark Brown };
2370a1bf553SMark Brown 
2380a1bf553SMark Brown /* AUX Input boost vol */
2390a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_aux_boost_controls =
2400a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
2410a1bf553SMark Brown 
2420a1bf553SMark Brown /* Mic Input boost vol */
2430a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_mic_boost_controls =
2440a1bf553SMark Brown SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
2450a1bf553SMark Brown 
2460a1bf553SMark Brown /* Capture boost switch */
2470a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_capture_boost_controls =
2480a1bf553SMark Brown SOC_DAPM_SINGLE("Capture Boost Switch", WM8974_INPPGA,  6, 1, 0);
2490a1bf553SMark Brown 
2500a1bf553SMark Brown /* Aux In to PGA */
2510a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_aux_capture_boost_controls =
2520a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Capture Boost Switch", WM8974_INPPGA,  2, 1, 0);
2530a1bf553SMark Brown 
2540a1bf553SMark Brown /* Mic P In to PGA */
2550a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_micp_capture_boost_controls =
2560a1bf553SMark Brown SOC_DAPM_SINGLE("Mic P Capture Boost Switch", WM8974_INPPGA,  0, 1, 0);
2570a1bf553SMark Brown 
2580a1bf553SMark Brown /* Mic N In to PGA */
2590a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_micn_capture_boost_controls =
2600a1bf553SMark Brown SOC_DAPM_SINGLE("Mic N Capture Boost Switch", WM8974_INPPGA,  1, 1, 0);
2610a1bf553SMark Brown 
2620a1bf553SMark Brown static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
2630a1bf553SMark Brown SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
2640a1bf553SMark Brown 	&wm8974_speaker_mixer_controls[0],
2650a1bf553SMark Brown 	ARRAY_SIZE(wm8974_speaker_mixer_controls)),
2660a1bf553SMark Brown SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
2670a1bf553SMark Brown 	&wm8974_mono_mixer_controls[0],
2680a1bf553SMark Brown 	ARRAY_SIZE(wm8974_mono_mixer_controls)),
2690a1bf553SMark Brown SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
2700a1bf553SMark Brown SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER3, 0, 0),
2710a1bf553SMark Brown SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
2720a1bf553SMark Brown SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
2730a1bf553SMark Brown SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
2740a1bf553SMark Brown SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
2750a1bf553SMark Brown SND_SOC_DAPM_PGA("Mic PGA", WM8974_POWER2, 2, 0, NULL, 0),
2760a1bf553SMark Brown 
2770a1bf553SMark Brown SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
2780a1bf553SMark Brown 	&wm8974_aux_boost_controls, 1),
2790a1bf553SMark Brown SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
2800a1bf553SMark Brown 	&wm8974_mic_boost_controls, 1),
2810a1bf553SMark Brown SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
2820a1bf553SMark Brown 	&wm8974_capture_boost_controls),
2830a1bf553SMark Brown 
2840a1bf553SMark Brown SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0, NULL, 0),
2850a1bf553SMark Brown 
2860a1bf553SMark Brown SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
2870a1bf553SMark Brown 
2880a1bf553SMark Brown SND_SOC_DAPM_INPUT("MICN"),
2890a1bf553SMark Brown SND_SOC_DAPM_INPUT("MICP"),
2900a1bf553SMark Brown SND_SOC_DAPM_INPUT("AUX"),
2910a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("MONOOUT"),
2920a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("SPKOUTP"),
2930a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("SPKOUTN"),
2940a1bf553SMark Brown };
2950a1bf553SMark Brown 
2960a1bf553SMark Brown static const struct snd_soc_dapm_route audio_map[] = {
2970a1bf553SMark Brown 	/* Mono output mixer */
2980a1bf553SMark Brown 	{"Mono Mixer", "PCM Playback Switch", "DAC"},
2990a1bf553SMark Brown 	{"Mono Mixer", "Aux Playback Switch", "Aux Input"},
3000a1bf553SMark Brown 	{"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
3010a1bf553SMark Brown 
3020a1bf553SMark Brown 	/* Speaker output mixer */
3030a1bf553SMark Brown 	{"Speaker Mixer", "PCM Playback Switch", "DAC"},
3040a1bf553SMark Brown 	{"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
3050a1bf553SMark Brown 	{"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
3060a1bf553SMark Brown 
3070a1bf553SMark Brown 	/* Outputs */
3080a1bf553SMark Brown 	{"Mono Out", NULL, "Mono Mixer"},
3090a1bf553SMark Brown 	{"MONOOUT", NULL, "Mono Out"},
3100a1bf553SMark Brown 	{"SpkN Out", NULL, "Speaker Mixer"},
3110a1bf553SMark Brown 	{"SpkP Out", NULL, "Speaker Mixer"},
3120a1bf553SMark Brown 	{"SPKOUTN", NULL, "SpkN Out"},
3130a1bf553SMark Brown 	{"SPKOUTP", NULL, "SpkP Out"},
3140a1bf553SMark Brown 
3150a1bf553SMark Brown 	/* Boost Mixer */
3160a1bf553SMark Brown 	{"Boost Mixer", NULL, "ADC"},
3170a1bf553SMark Brown 	{"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
3180a1bf553SMark Brown 	{"Aux Boost", "Aux Volume", "Boost Mixer"},
3190a1bf553SMark Brown 	{"Capture Boost", "Capture Switch", "Boost Mixer"},
3200a1bf553SMark Brown 	{"Mic Boost", "Mic Volume", "Boost Mixer"},
3210a1bf553SMark Brown 
3220a1bf553SMark Brown 	/* Inputs */
3230a1bf553SMark Brown 	{"MICP", NULL, "Mic Boost"},
3240a1bf553SMark Brown 	{"MICN", NULL, "Mic PGA"},
3250a1bf553SMark Brown 	{"Mic PGA", NULL, "Capture Boost"},
3260a1bf553SMark Brown 	{"AUX", NULL, "Aux Input"},
3270a1bf553SMark Brown };
3280a1bf553SMark Brown 
3290a1bf553SMark Brown static int wm8974_add_widgets(struct snd_soc_codec *codec)
3300a1bf553SMark Brown {
3310a1bf553SMark Brown 	snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets,
3320a1bf553SMark Brown 				  ARRAY_SIZE(wm8974_dapm_widgets));
3330a1bf553SMark Brown 
3340a1bf553SMark Brown 	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
3350a1bf553SMark Brown 
3360a1bf553SMark Brown 	snd_soc_dapm_new_widgets(codec);
3370a1bf553SMark Brown 	return 0;
3380a1bf553SMark Brown }
3390a1bf553SMark Brown 
3400a1bf553SMark Brown struct pll_ {
3410a1bf553SMark Brown 	unsigned int in_hz, out_hz;
3420a1bf553SMark Brown 	unsigned int pre:4; /* prescale - 1 */
3430a1bf553SMark Brown 	unsigned int n:4;
3440a1bf553SMark Brown 	unsigned int k;
3450a1bf553SMark Brown };
3460a1bf553SMark Brown 
3470a1bf553SMark Brown static struct pll_ pll[] = {
3480a1bf553SMark Brown 	{ 12000000, 11289600, 0, 7, 0x86c220 },
3490a1bf553SMark Brown 	{ 12000000, 12288000, 0, 8, 0x3126e8 },
3500a1bf553SMark Brown 	{ 13000000, 11289600, 0, 6, 0xf28bd4 },
3510a1bf553SMark Brown 	{ 13000000, 12288000, 0, 7, 0x8fd525 },
3520a1bf553SMark Brown 	{ 12288000, 11289600, 0, 7, 0x59999a },
3530a1bf553SMark Brown 	{ 11289600, 12288000, 0, 8, 0x80dee9 },
354*1a55b3f6SMark Brown 	{ 25000000, 11289600, 1, 7, 0x39B024 },
355*1a55b3f6SMark Brown 	{ 25000000, 24576000, 1, 7, 0xdd4413 }
3560a1bf553SMark Brown };
3570a1bf553SMark Brown 
3580a1bf553SMark Brown static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
3590a1bf553SMark Brown 		int pll_id, unsigned int freq_in, unsigned int freq_out)
3600a1bf553SMark Brown {
3610a1bf553SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
3620a1bf553SMark Brown 	int i;
3630a1bf553SMark Brown 	u16 reg;
3640a1bf553SMark Brown 
3650a1bf553SMark Brown 	if (freq_in == 0 || freq_out == 0) {
3660a1bf553SMark Brown 		reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
3670a1bf553SMark Brown 		wm8974_write(codec, WM8974_POWER1, reg & 0x1df);
3680a1bf553SMark Brown 		return 0;
3690a1bf553SMark Brown 	}
3700a1bf553SMark Brown 
3710a1bf553SMark Brown 	for (i = 0; i < ARRAY_SIZE(pll); i++) {
3720a1bf553SMark Brown 		if (freq_in == pll[i].in_hz && freq_out == pll[i].out_hz) {
373*1a55b3f6SMark Brown 			wm8974_write(codec, WM8974_PLLN,
374*1a55b3f6SMark Brown 				     (pll[i].pre << 4) | pll[i].n);
3750a1bf553SMark Brown 			wm8974_write(codec, WM8974_PLLK1, pll[i].k >> 18);
376*1a55b3f6SMark Brown 			wm8974_write(codec, WM8974_PLLK2,
377*1a55b3f6SMark Brown 				     (pll[i].k >> 9) & 0x1ff);
3780a1bf553SMark Brown 			wm8974_write(codec, WM8974_PLLK3, pll[i].k & 0x1ff);
3790a1bf553SMark Brown 			reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
3800a1bf553SMark Brown 			wm8974_write(codec, WM8974_POWER1, reg | 0x020);
3810a1bf553SMark Brown 			return 0;
3820a1bf553SMark Brown 		}
3830a1bf553SMark Brown 	}
384*1a55b3f6SMark Brown 
3850a1bf553SMark Brown 	return -EINVAL;
3860a1bf553SMark Brown }
3870a1bf553SMark Brown 
3880a1bf553SMark Brown /*
3890a1bf553SMark Brown  * Configure WM8974 clock dividers.
3900a1bf553SMark Brown  */
3910a1bf553SMark Brown static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
3920a1bf553SMark Brown 		int div_id, int div)
3930a1bf553SMark Brown {
3940a1bf553SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
3950a1bf553SMark Brown 	u16 reg;
3960a1bf553SMark Brown 
3970a1bf553SMark Brown 	switch (div_id) {
3980a1bf553SMark Brown 	case WM8974_OPCLKDIV:
3990a1bf553SMark Brown 		reg = wm8974_read_reg_cache(codec, WM8974_GPIO) & 0x1cf;
4000a1bf553SMark Brown 		wm8974_write(codec, WM8974_GPIO, reg | div);
4010a1bf553SMark Brown 		break;
4020a1bf553SMark Brown 	case WM8974_MCLKDIV:
4030a1bf553SMark Brown 		reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x11f;
4040a1bf553SMark Brown 		wm8974_write(codec, WM8974_CLOCK, reg | div);
4050a1bf553SMark Brown 		break;
4060a1bf553SMark Brown 	case WM8974_ADCCLK:
4070a1bf553SMark Brown 		reg = wm8974_read_reg_cache(codec, WM8974_ADC) & 0x1f7;
4080a1bf553SMark Brown 		wm8974_write(codec, WM8974_ADC, reg | div);
4090a1bf553SMark Brown 		break;
4100a1bf553SMark Brown 	case WM8974_DACCLK:
4110a1bf553SMark Brown 		reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0x1f7;
4120a1bf553SMark Brown 		wm8974_write(codec, WM8974_DAC, reg | div);
4130a1bf553SMark Brown 		break;
4140a1bf553SMark Brown 	case WM8974_BCLKDIV:
4150a1bf553SMark Brown 		reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1e3;
4160a1bf553SMark Brown 		wm8974_write(codec, WM8974_CLOCK, reg | div);
4170a1bf553SMark Brown 		break;
4180a1bf553SMark Brown 	default:
4190a1bf553SMark Brown 		return -EINVAL;
4200a1bf553SMark Brown 	}
4210a1bf553SMark Brown 
4220a1bf553SMark Brown 	return 0;
4230a1bf553SMark Brown }
4240a1bf553SMark Brown 
4250a1bf553SMark Brown static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
4260a1bf553SMark Brown 		unsigned int fmt)
4270a1bf553SMark Brown {
4280a1bf553SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
4290a1bf553SMark Brown 	u16 iface = 0;
4300a1bf553SMark Brown 	u16 clk = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1fe;
4310a1bf553SMark Brown 
4320a1bf553SMark Brown 	/* set master/slave audio interface */
4330a1bf553SMark Brown 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
4340a1bf553SMark Brown 	case SND_SOC_DAIFMT_CBM_CFM:
4350a1bf553SMark Brown 		clk |= 0x0001;
4360a1bf553SMark Brown 		break;
4370a1bf553SMark Brown 	case SND_SOC_DAIFMT_CBS_CFS:
4380a1bf553SMark Brown 		break;
4390a1bf553SMark Brown 	default:
4400a1bf553SMark Brown 		return -EINVAL;
4410a1bf553SMark Brown 	}
4420a1bf553SMark Brown 
4430a1bf553SMark Brown 	/* interface format */
4440a1bf553SMark Brown 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
4450a1bf553SMark Brown 	case SND_SOC_DAIFMT_I2S:
4460a1bf553SMark Brown 		iface |= 0x0010;
4470a1bf553SMark Brown 		break;
4480a1bf553SMark Brown 	case SND_SOC_DAIFMT_RIGHT_J:
4490a1bf553SMark Brown 		break;
4500a1bf553SMark Brown 	case SND_SOC_DAIFMT_LEFT_J:
4510a1bf553SMark Brown 		iface |= 0x0008;
4520a1bf553SMark Brown 		break;
4530a1bf553SMark Brown 	case SND_SOC_DAIFMT_DSP_A:
4540a1bf553SMark Brown 		iface |= 0x00018;
4550a1bf553SMark Brown 		break;
4560a1bf553SMark Brown 	default:
4570a1bf553SMark Brown 		return -EINVAL;
4580a1bf553SMark Brown 	}
4590a1bf553SMark Brown 
4600a1bf553SMark Brown 	/* clock inversion */
4610a1bf553SMark Brown 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
4620a1bf553SMark Brown 	case SND_SOC_DAIFMT_NB_NF:
4630a1bf553SMark Brown 		break;
4640a1bf553SMark Brown 	case SND_SOC_DAIFMT_IB_IF:
4650a1bf553SMark Brown 		iface |= 0x0180;
4660a1bf553SMark Brown 		break;
4670a1bf553SMark Brown 	case SND_SOC_DAIFMT_IB_NF:
4680a1bf553SMark Brown 		iface |= 0x0100;
4690a1bf553SMark Brown 		break;
4700a1bf553SMark Brown 	case SND_SOC_DAIFMT_NB_IF:
4710a1bf553SMark Brown 		iface |= 0x0080;
4720a1bf553SMark Brown 		break;
4730a1bf553SMark Brown 	default:
4740a1bf553SMark Brown 		return -EINVAL;
4750a1bf553SMark Brown 	}
4760a1bf553SMark Brown 
4770a1bf553SMark Brown 	wm8974_write(codec, WM8974_IFACE, iface);
4780a1bf553SMark Brown 	wm8974_write(codec, WM8974_CLOCK, clk);
4790a1bf553SMark Brown 	return 0;
4800a1bf553SMark Brown }
4810a1bf553SMark Brown 
4820a1bf553SMark Brown static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
4830a1bf553SMark Brown 				struct snd_pcm_hw_params *params,
4840a1bf553SMark Brown 				struct snd_soc_dai *dai)
4850a1bf553SMark Brown {
4860a1bf553SMark Brown 	struct snd_soc_codec *codec = dai->codec;
4870a1bf553SMark Brown 	u16 iface = wm8974_read_reg_cache(codec, WM8974_IFACE) & 0x19f;
4880a1bf553SMark Brown 	u16 adn = wm8974_read_reg_cache(codec, WM8974_ADD) & 0x1f1;
4890a1bf553SMark Brown 
4900a1bf553SMark Brown 	/* bit size */
4910a1bf553SMark Brown 	switch (params_format(params)) {
4920a1bf553SMark Brown 	case SNDRV_PCM_FORMAT_S16_LE:
4930a1bf553SMark Brown 		break;
4940a1bf553SMark Brown 	case SNDRV_PCM_FORMAT_S20_3LE:
4950a1bf553SMark Brown 		iface |= 0x0020;
4960a1bf553SMark Brown 		break;
4970a1bf553SMark Brown 	case SNDRV_PCM_FORMAT_S24_LE:
4980a1bf553SMark Brown 		iface |= 0x0040;
4990a1bf553SMark Brown 		break;
5000a1bf553SMark Brown 	case SNDRV_PCM_FORMAT_S32_LE:
5010a1bf553SMark Brown 		iface |= 0x0060;
5020a1bf553SMark Brown 		break;
5030a1bf553SMark Brown 	}
5040a1bf553SMark Brown 
5050a1bf553SMark Brown 	/* filter coefficient */
5060a1bf553SMark Brown 	switch (params_rate(params)) {
5070a1bf553SMark Brown 	case SNDRV_PCM_RATE_8000:
5080a1bf553SMark Brown 		adn |= 0x5 << 1;
5090a1bf553SMark Brown 		break;
5100a1bf553SMark Brown 	case SNDRV_PCM_RATE_11025:
5110a1bf553SMark Brown 		adn |= 0x4 << 1;
5120a1bf553SMark Brown 		break;
5130a1bf553SMark Brown 	case SNDRV_PCM_RATE_16000:
5140a1bf553SMark Brown 		adn |= 0x3 << 1;
5150a1bf553SMark Brown 		break;
5160a1bf553SMark Brown 	case SNDRV_PCM_RATE_22050:
5170a1bf553SMark Brown 		adn |= 0x2 << 1;
5180a1bf553SMark Brown 		break;
5190a1bf553SMark Brown 	case SNDRV_PCM_RATE_32000:
5200a1bf553SMark Brown 		adn |= 0x1 << 1;
5210a1bf553SMark Brown 		break;
5220a1bf553SMark Brown 	case SNDRV_PCM_RATE_44100:
5230a1bf553SMark Brown 		break;
5240a1bf553SMark Brown 	}
5250a1bf553SMark Brown 
5260a1bf553SMark Brown 	wm8974_write(codec, WM8974_IFACE, iface);
5270a1bf553SMark Brown 	wm8974_write(codec, WM8974_ADD, adn);
5280a1bf553SMark Brown 	return 0;
5290a1bf553SMark Brown }
5300a1bf553SMark Brown 
5310a1bf553SMark Brown static int wm8974_mute(struct snd_soc_dai *dai, int mute)
5320a1bf553SMark Brown {
5330a1bf553SMark Brown 	struct snd_soc_codec *codec = dai->codec;
5340a1bf553SMark Brown 	u16 mute_reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0xffbf;
5350a1bf553SMark Brown 
5360a1bf553SMark Brown 	if (mute)
5370a1bf553SMark Brown 		wm8974_write(codec, WM8974_DAC, mute_reg | 0x40);
5380a1bf553SMark Brown 	else
5390a1bf553SMark Brown 		wm8974_write(codec, WM8974_DAC, mute_reg);
5400a1bf553SMark Brown 	return 0;
5410a1bf553SMark Brown }
5420a1bf553SMark Brown 
5430a1bf553SMark Brown /* liam need to make this lower power with dapm */
5440a1bf553SMark Brown static int wm8974_set_bias_level(struct snd_soc_codec *codec,
5450a1bf553SMark Brown 	enum snd_soc_bias_level level)
5460a1bf553SMark Brown {
5470a1bf553SMark Brown 	switch (level) {
5480a1bf553SMark Brown 	case SND_SOC_BIAS_ON:
5490a1bf553SMark Brown 		wm8974_write(codec, WM8974_POWER1, 0x1ff);
5500a1bf553SMark Brown 		wm8974_write(codec, WM8974_POWER2, 0x1ff);
5510a1bf553SMark Brown 		wm8974_write(codec, WM8974_POWER3, 0x1ff);
5520a1bf553SMark Brown 		break;
5530a1bf553SMark Brown 	case SND_SOC_BIAS_PREPARE:
5540a1bf553SMark Brown 		break;
5550a1bf553SMark Brown 	case SND_SOC_BIAS_STANDBY:
5560a1bf553SMark Brown 		break;
5570a1bf553SMark Brown 	case SND_SOC_BIAS_OFF:
5580a1bf553SMark Brown 		wm8974_write(codec, WM8974_POWER1, 0x0);
5590a1bf553SMark Brown 		wm8974_write(codec, WM8974_POWER2, 0x0);
5600a1bf553SMark Brown 		wm8974_write(codec, WM8974_POWER3, 0x0);
5610a1bf553SMark Brown 		break;
5620a1bf553SMark Brown 	}
5630a1bf553SMark Brown 	codec->bias_level = level;
5640a1bf553SMark Brown 	return 0;
5650a1bf553SMark Brown }
5660a1bf553SMark Brown 
567*1a55b3f6SMark Brown #define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
5680a1bf553SMark Brown 
5690a1bf553SMark Brown #define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
5700a1bf553SMark Brown 	SNDRV_PCM_FMTBIT_S24_LE)
5710a1bf553SMark Brown 
5720a1bf553SMark Brown static struct snd_soc_dai_ops wm8974_ops = {
5730a1bf553SMark Brown 	.hw_params = wm8974_pcm_hw_params,
5740a1bf553SMark Brown 	.digital_mute = wm8974_mute,
5750a1bf553SMark Brown 	.set_fmt = wm8974_set_dai_fmt,
5760a1bf553SMark Brown 	.set_clkdiv = wm8974_set_dai_clkdiv,
5770a1bf553SMark Brown 	.set_pll = wm8974_set_dai_pll,
5780a1bf553SMark Brown };
5790a1bf553SMark Brown 
5800a1bf553SMark Brown struct snd_soc_dai wm8974_dai = {
5810a1bf553SMark Brown 	.name = "WM8974 HiFi",
5820a1bf553SMark Brown 	.playback = {
5830a1bf553SMark Brown 		.stream_name = "Playback",
5840a1bf553SMark Brown 		.channels_min = 1,
5850a1bf553SMark Brown 		.channels_max = 1,
5860a1bf553SMark Brown 		.rates = WM8974_RATES,
5870a1bf553SMark Brown 		.formats = WM8974_FORMATS,},
5880a1bf553SMark Brown 	.capture = {
5890a1bf553SMark Brown 		.stream_name = "Capture",
5900a1bf553SMark Brown 		.channels_min = 1,
5910a1bf553SMark Brown 		.channels_max = 1,
5920a1bf553SMark Brown 		.rates = WM8974_RATES,
5930a1bf553SMark Brown 		.formats = WM8974_FORMATS,},
5940a1bf553SMark Brown 	.ops = &wm8974_ops,
5950a1bf553SMark Brown };
5960a1bf553SMark Brown EXPORT_SYMBOL_GPL(wm8974_dai);
5970a1bf553SMark Brown 
5980a1bf553SMark Brown static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
5990a1bf553SMark Brown {
6000a1bf553SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
6010a1bf553SMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
6020a1bf553SMark Brown 
6030a1bf553SMark Brown 	wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
6040a1bf553SMark Brown 	return 0;
6050a1bf553SMark Brown }
6060a1bf553SMark Brown 
6070a1bf553SMark Brown static int wm8974_resume(struct platform_device *pdev)
6080a1bf553SMark Brown {
6090a1bf553SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
6100a1bf553SMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
6110a1bf553SMark Brown 	int i;
6120a1bf553SMark Brown 	u8 data[2];
6130a1bf553SMark Brown 	u16 *cache = codec->reg_cache;
6140a1bf553SMark Brown 
6150a1bf553SMark Brown 	/* Sync reg_cache with the hardware */
6160a1bf553SMark Brown 	for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
6170a1bf553SMark Brown 		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
6180a1bf553SMark Brown 		data[1] = cache[i] & 0x00ff;
6190a1bf553SMark Brown 		codec->hw_write(codec->control_data, data, 2);
6200a1bf553SMark Brown 	}
6210a1bf553SMark Brown 	wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
6220a1bf553SMark Brown 	wm8974_set_bias_level(codec, codec->suspend_bias_level);
6230a1bf553SMark Brown 	return 0;
6240a1bf553SMark Brown }
6250a1bf553SMark Brown 
6260a1bf553SMark Brown /*
6270a1bf553SMark Brown  * initialise the WM8974 driver
6280a1bf553SMark Brown  * register the mixer and dsp interfaces with the kernel
6290a1bf553SMark Brown  */
6300a1bf553SMark Brown static int wm8974_init(struct snd_soc_device *socdev)
6310a1bf553SMark Brown {
6320a1bf553SMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
6330a1bf553SMark Brown 	int ret = 0;
6340a1bf553SMark Brown 
6350a1bf553SMark Brown 	codec->name = "WM8974";
6360a1bf553SMark Brown 	codec->owner = THIS_MODULE;
6370a1bf553SMark Brown 	codec->read = wm8974_read_reg_cache;
6380a1bf553SMark Brown 	codec->write = wm8974_write;
6390a1bf553SMark Brown 	codec->set_bias_level = wm8974_set_bias_level;
6400a1bf553SMark Brown 	codec->dai = &wm8974_dai;
6410a1bf553SMark Brown 	codec->num_dai = 1;
6420a1bf553SMark Brown 	codec->reg_cache_size = ARRAY_SIZE(wm8974_reg);
6430a1bf553SMark Brown 	codec->reg_cache = kmemdup(wm8974_reg, sizeof(wm8974_reg), GFP_KERNEL);
6440a1bf553SMark Brown 
6450a1bf553SMark Brown 	if (codec->reg_cache == NULL)
6460a1bf553SMark Brown 		return -ENOMEM;
6470a1bf553SMark Brown 
6480a1bf553SMark Brown 	wm8974_reset(codec);
6490a1bf553SMark Brown 
6500a1bf553SMark Brown 	/* register pcms */
6510a1bf553SMark Brown 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
6520a1bf553SMark Brown 	if (ret < 0) {
6530a1bf553SMark Brown 		printk(KERN_ERR "wm8974: failed to create pcms\n");
6540a1bf553SMark Brown 		goto pcm_err;
6550a1bf553SMark Brown 	}
6560a1bf553SMark Brown 
6570a1bf553SMark Brown 	/* power on device */
6580a1bf553SMark Brown 	wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
6590a1bf553SMark Brown 	wm8974_add_controls(codec);
6600a1bf553SMark Brown 	wm8974_add_widgets(codec);
6610a1bf553SMark Brown 	ret = snd_soc_init_card(socdev);
6620a1bf553SMark Brown 	if (ret < 0) {
6630a1bf553SMark Brown 		printk(KERN_ERR "wm8974: failed to register card\n");
6640a1bf553SMark Brown 		goto card_err;
6650a1bf553SMark Brown 	}
6660a1bf553SMark Brown 	return ret;
6670a1bf553SMark Brown 
6680a1bf553SMark Brown card_err:
6690a1bf553SMark Brown 	snd_soc_free_pcms(socdev);
6700a1bf553SMark Brown 	snd_soc_dapm_free(socdev);
6710a1bf553SMark Brown pcm_err:
6720a1bf553SMark Brown 	kfree(codec->reg_cache);
6730a1bf553SMark Brown 	return ret;
6740a1bf553SMark Brown }
6750a1bf553SMark Brown 
6760a1bf553SMark Brown static struct snd_soc_device *wm8974_socdev;
6770a1bf553SMark Brown 
6780a1bf553SMark Brown #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
6790a1bf553SMark Brown 
6800a1bf553SMark Brown /*
6810a1bf553SMark Brown  * WM8974 2 wire address is 0x1a
6820a1bf553SMark Brown  */
6830a1bf553SMark Brown #define I2C_DRIVERID_WM8974 0xfefe /* liam -  need a proper id */
6840a1bf553SMark Brown 
6850a1bf553SMark Brown static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
6860a1bf553SMark Brown 
6870a1bf553SMark Brown /* Magic definition of all other variables and things */
6880a1bf553SMark Brown I2C_CLIENT_INSMOD;
6890a1bf553SMark Brown 
6900a1bf553SMark Brown static struct i2c_driver wm8974_i2c_driver;
6910a1bf553SMark Brown static struct i2c_client client_template;
6920a1bf553SMark Brown 
6930a1bf553SMark Brown /* If the i2c layer weren't so broken, we could pass this kind of data
6940a1bf553SMark Brown    around */
6950a1bf553SMark Brown 
6960a1bf553SMark Brown static int wm8974_codec_probe(struct i2c_adapter *adap, int addr, int kind)
6970a1bf553SMark Brown {
6980a1bf553SMark Brown 	struct snd_soc_device *socdev = wm8974_socdev;
6990a1bf553SMark Brown 	struct wm8974_setup_data *setup = socdev->codec_data;
7000a1bf553SMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
7010a1bf553SMark Brown 	struct i2c_client *i2c;
7020a1bf553SMark Brown 	int ret;
7030a1bf553SMark Brown 
7040a1bf553SMark Brown 	if (addr != setup->i2c_address)
7050a1bf553SMark Brown 		return -ENODEV;
7060a1bf553SMark Brown 
7070a1bf553SMark Brown 	client_template.adapter = adap;
7080a1bf553SMark Brown 	client_template.addr = addr;
7090a1bf553SMark Brown 
7100a1bf553SMark Brown 	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
7110a1bf553SMark Brown 	if (i2c == NULL) {
7120a1bf553SMark Brown 		kfree(codec);
7130a1bf553SMark Brown 		return -ENOMEM;
7140a1bf553SMark Brown 	}
7150a1bf553SMark Brown 	i2c_set_clientdata(i2c, codec);
7160a1bf553SMark Brown 	codec->control_data = i2c;
7170a1bf553SMark Brown 
7180a1bf553SMark Brown 	ret = i2c_attach_client(i2c);
7190a1bf553SMark Brown 	if (ret < 0) {
7200a1bf553SMark Brown 		pr_err("failed to attach codec at addr %x\n", addr);
7210a1bf553SMark Brown 		goto err;
7220a1bf553SMark Brown 	}
7230a1bf553SMark Brown 
7240a1bf553SMark Brown 	ret = wm8974_init(socdev);
7250a1bf553SMark Brown 	if (ret < 0) {
7260a1bf553SMark Brown 		pr_err("failed to initialise WM8974\n");
7270a1bf553SMark Brown 		goto err;
7280a1bf553SMark Brown 	}
7290a1bf553SMark Brown 	return ret;
7300a1bf553SMark Brown 
7310a1bf553SMark Brown err:
7320a1bf553SMark Brown 	kfree(codec);
7330a1bf553SMark Brown 	kfree(i2c);
7340a1bf553SMark Brown 	return ret;
7350a1bf553SMark Brown }
7360a1bf553SMark Brown 
7370a1bf553SMark Brown static int wm8974_i2c_detach(struct i2c_client *client)
7380a1bf553SMark Brown {
7390a1bf553SMark Brown 	struct snd_soc_codec *codec = i2c_get_clientdata(client);
7400a1bf553SMark Brown 	i2c_detach_client(client);
7410a1bf553SMark Brown 	kfree(codec->reg_cache);
7420a1bf553SMark Brown 	kfree(client);
7430a1bf553SMark Brown 	return 0;
7440a1bf553SMark Brown }
7450a1bf553SMark Brown 
7460a1bf553SMark Brown static int wm8974_i2c_attach(struct i2c_adapter *adap)
7470a1bf553SMark Brown {
7480a1bf553SMark Brown 	return i2c_probe(adap, &addr_data, wm8974_codec_probe);
7490a1bf553SMark Brown }
7500a1bf553SMark Brown 
7510a1bf553SMark Brown /* corgi i2c codec control layer */
7520a1bf553SMark Brown static struct i2c_driver wm8974_i2c_driver = {
7530a1bf553SMark Brown 	.driver = {
7540a1bf553SMark Brown 		.name = "WM8974 I2C Codec",
7550a1bf553SMark Brown 		.owner = THIS_MODULE,
7560a1bf553SMark Brown 	},
7570a1bf553SMark Brown 	.id =             I2C_DRIVERID_WM8974,
7580a1bf553SMark Brown 	.attach_adapter = wm8974_i2c_attach,
7590a1bf553SMark Brown 	.detach_client =  wm8974_i2c_detach,
7600a1bf553SMark Brown 	.command =        NULL,
7610a1bf553SMark Brown };
7620a1bf553SMark Brown 
7630a1bf553SMark Brown static struct i2c_client client_template = {
7640a1bf553SMark Brown 	.name =   "WM8974",
7650a1bf553SMark Brown 	.driver = &wm8974_i2c_driver,
7660a1bf553SMark Brown };
7670a1bf553SMark Brown #endif
7680a1bf553SMark Brown 
7690a1bf553SMark Brown static int wm8974_probe(struct platform_device *pdev)
7700a1bf553SMark Brown {
7710a1bf553SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
7720a1bf553SMark Brown 	struct wm8974_setup_data *setup;
7730a1bf553SMark Brown 	struct snd_soc_codec *codec;
7740a1bf553SMark Brown 	int ret = 0;
7750a1bf553SMark Brown 
7760a1bf553SMark Brown 	setup = socdev->codec_data;
7770a1bf553SMark Brown 	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
7780a1bf553SMark Brown 	if (codec == NULL)
7790a1bf553SMark Brown 		return -ENOMEM;
7800a1bf553SMark Brown 
7810a1bf553SMark Brown 	socdev->card->codec = codec;
7820a1bf553SMark Brown 	mutex_init(&codec->mutex);
7830a1bf553SMark Brown 	INIT_LIST_HEAD(&codec->dapm_widgets);
7840a1bf553SMark Brown 	INIT_LIST_HEAD(&codec->dapm_paths);
7850a1bf553SMark Brown 
7860a1bf553SMark Brown 	wm8974_socdev = socdev;
7870a1bf553SMark Brown #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
7880a1bf553SMark Brown 	if (setup->i2c_address) {
7890a1bf553SMark Brown 		normal_i2c[0] = setup->i2c_address;
7900a1bf553SMark Brown 		codec->hw_write = (hw_write_t)i2c_master_send;
7910a1bf553SMark Brown 		ret = i2c_add_driver(&wm8974_i2c_driver);
7920a1bf553SMark Brown 		if (ret != 0)
7930a1bf553SMark Brown 			printk(KERN_ERR "can't add i2c driver");
7940a1bf553SMark Brown 	}
7950a1bf553SMark Brown #else
7960a1bf553SMark Brown 	/* Add other interfaces here */
7970a1bf553SMark Brown #endif
7980a1bf553SMark Brown 	return ret;
7990a1bf553SMark Brown }
8000a1bf553SMark Brown 
8010a1bf553SMark Brown /* power down chip */
8020a1bf553SMark Brown static int wm8974_remove(struct platform_device *pdev)
8030a1bf553SMark Brown {
8040a1bf553SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
8050a1bf553SMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
8060a1bf553SMark Brown 
8070a1bf553SMark Brown 	if (codec->control_data)
8080a1bf553SMark Brown 		wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
8090a1bf553SMark Brown 
8100a1bf553SMark Brown 	snd_soc_free_pcms(socdev);
8110a1bf553SMark Brown 	snd_soc_dapm_free(socdev);
8120a1bf553SMark Brown #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
8130a1bf553SMark Brown 	i2c_del_driver(&wm8974_i2c_driver);
8140a1bf553SMark Brown #endif
8150a1bf553SMark Brown 	kfree(codec);
8160a1bf553SMark Brown 
8170a1bf553SMark Brown 	return 0;
8180a1bf553SMark Brown }
8190a1bf553SMark Brown 
8200a1bf553SMark Brown struct snd_soc_codec_device soc_codec_dev_wm8974 = {
8210a1bf553SMark Brown 	.probe = 	wm8974_probe,
8220a1bf553SMark Brown 	.remove = 	wm8974_remove,
8230a1bf553SMark Brown 	.suspend = 	wm8974_suspend,
8240a1bf553SMark Brown 	.resume =	wm8974_resume,
8250a1bf553SMark Brown };
8260a1bf553SMark Brown EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
8270a1bf553SMark Brown 
8280a1bf553SMark Brown static int __init wm8974_modinit(void)
8290a1bf553SMark Brown {
8300a1bf553SMark Brown 	return snd_soc_register_dai(&wm8974_dai);
8310a1bf553SMark Brown }
8320a1bf553SMark Brown module_init(wm8974_modinit);
8330a1bf553SMark Brown 
8340a1bf553SMark Brown static void __exit wm8974_exit(void)
8350a1bf553SMark Brown {
8360a1bf553SMark Brown 	snd_soc_unregister_dai(&wm8974_dai);
8370a1bf553SMark Brown }
8380a1bf553SMark Brown module_exit(wm8974_exit);
8390a1bf553SMark Brown 
8400a1bf553SMark Brown MODULE_DESCRIPTION("ASoC WM8974 driver");
8410a1bf553SMark Brown MODULE_AUTHOR("Liam Girdwood");
8420a1bf553SMark Brown MODULE_LICENSE("GPL");
843