xref: /linux/sound/soc/codecs/wm8974.c (revision 8b83a19367dc3bdfef07634646bbad90f6cba898)
10a1bf553SMark Brown /*
20a1bf553SMark Brown  * wm8974.c  --  WM8974 ALSA Soc Audio driver
30a1bf553SMark Brown  *
4*8b83a193SMark 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/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>
28a5f8d2f1SMark Brown #include <sound/tlv.h>
290a1bf553SMark Brown 
300a1bf553SMark Brown #include "wm8974.h"
310a1bf553SMark Brown 
320a1bf553SMark Brown static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
330a1bf553SMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
340a1bf553SMark Brown 	0x0050, 0x0000, 0x0140, 0x0000,
350a1bf553SMark Brown 	0x0000, 0x0000, 0x0000, 0x00ff,
360a1bf553SMark Brown 	0x0000, 0x0000, 0x0100, 0x00ff,
370a1bf553SMark Brown 	0x0000, 0x0000, 0x012c, 0x002c,
380a1bf553SMark Brown 	0x002c, 0x002c, 0x002c, 0x0000,
390a1bf553SMark Brown 	0x0032, 0x0000, 0x0000, 0x0000,
400a1bf553SMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
410a1bf553SMark Brown 	0x0038, 0x000b, 0x0032, 0x0000,
420a1bf553SMark Brown 	0x0008, 0x000c, 0x0093, 0x00e9,
430a1bf553SMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
440a1bf553SMark Brown 	0x0003, 0x0010, 0x0000, 0x0000,
450a1bf553SMark Brown 	0x0000, 0x0002, 0x0000, 0x0000,
460a1bf553SMark Brown 	0x0000, 0x0000, 0x0039, 0x0000,
470a1bf553SMark Brown 	0x0000,
480a1bf553SMark Brown };
490a1bf553SMark Brown 
50df1ef7a3SMark Brown #define WM8974_POWER1_BIASEN  0x08
51df1ef7a3SMark Brown #define WM8974_POWER1_BUFIOEN 0x10
52df1ef7a3SMark Brown 
534fcbbb67SMark Brown struct wm8974_priv {
544fcbbb67SMark Brown 	struct snd_soc_codec codec;
554fcbbb67SMark Brown 	u16 reg_cache[WM8974_CACHEREGNUM];
564fcbbb67SMark Brown };
574fcbbb67SMark Brown 
584fcbbb67SMark Brown static struct snd_soc_codec *wm8974_codec;
594fcbbb67SMark Brown 
600a1bf553SMark Brown /*
610a1bf553SMark Brown  * read wm8974 register cache
620a1bf553SMark Brown  */
630a1bf553SMark Brown static inline unsigned int wm8974_read_reg_cache(struct snd_soc_codec *codec,
640a1bf553SMark Brown 	unsigned int reg)
650a1bf553SMark Brown {
660a1bf553SMark Brown 	u16 *cache = codec->reg_cache;
670a1bf553SMark Brown 	if (reg == WM8974_RESET)
680a1bf553SMark Brown 		return 0;
690a1bf553SMark Brown 	if (reg >= WM8974_CACHEREGNUM)
700a1bf553SMark Brown 		return -1;
710a1bf553SMark Brown 	return cache[reg];
720a1bf553SMark Brown }
730a1bf553SMark Brown 
740a1bf553SMark Brown /*
750a1bf553SMark Brown  * write wm8974 register cache
760a1bf553SMark Brown  */
770a1bf553SMark Brown static inline void wm8974_write_reg_cache(struct snd_soc_codec *codec,
780a1bf553SMark Brown 	u16 reg, unsigned int value)
790a1bf553SMark Brown {
800a1bf553SMark Brown 	u16 *cache = codec->reg_cache;
810a1bf553SMark Brown 	if (reg >= WM8974_CACHEREGNUM)
820a1bf553SMark Brown 		return;
830a1bf553SMark Brown 	cache[reg] = value;
840a1bf553SMark Brown }
850a1bf553SMark Brown 
860a1bf553SMark Brown /*
870a1bf553SMark Brown  * write to the WM8974 register space
880a1bf553SMark Brown  */
890a1bf553SMark Brown static int wm8974_write(struct snd_soc_codec *codec, unsigned int reg,
900a1bf553SMark Brown 	unsigned int value)
910a1bf553SMark Brown {
920a1bf553SMark Brown 	u8 data[2];
930a1bf553SMark Brown 
940a1bf553SMark Brown 	/* data is
950a1bf553SMark Brown 	 *   D15..D9 WM8974 register offset
960a1bf553SMark Brown 	 *   D8...D0 register data
970a1bf553SMark Brown 	 */
980a1bf553SMark Brown 	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
990a1bf553SMark Brown 	data[1] = value & 0x00ff;
1000a1bf553SMark Brown 
1010a1bf553SMark Brown 	wm8974_write_reg_cache(codec, reg, value);
1020a1bf553SMark Brown 	if (codec->hw_write(codec->control_data, data, 2) == 2)
1030a1bf553SMark Brown 		return 0;
1040a1bf553SMark Brown 	else
1050a1bf553SMark Brown 		return -EIO;
1060a1bf553SMark Brown }
1070a1bf553SMark Brown 
1080a1bf553SMark Brown #define wm8974_reset(c)	wm8974_write(c, WM8974_RESET, 0)
1090a1bf553SMark Brown 
1100a1bf553SMark Brown static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
1110a1bf553SMark Brown static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
1120a1bf553SMark Brown static const char *wm8974_eqmode[] = {"Capture", "Playback" };
1130a1bf553SMark Brown static const char *wm8974_bw[] = {"Narrow", "Wide" };
1140a1bf553SMark Brown static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
1150a1bf553SMark Brown static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
1160a1bf553SMark Brown static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
1170a1bf553SMark Brown static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
1180a1bf553SMark Brown static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
1190a1bf553SMark Brown static const char *wm8974_alc[] = {"ALC", "Limiter" };
1200a1bf553SMark Brown 
1210a1bf553SMark Brown static const struct soc_enum wm8974_enum[] = {
1220a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
1230a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
1240a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_DAC,  4, 4, wm8974_deemp),
1250a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ1,  8, 2, wm8974_eqmode),
1260a1bf553SMark Brown 
1270a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ1,  5, 4, wm8974_eq1),
1280a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ2,  8, 2, wm8974_bw),
1290a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ2,  5, 4, wm8974_eq2),
1300a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ3,  8, 2, wm8974_bw),
1310a1bf553SMark Brown 
1320a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ3,  5, 4, wm8974_eq3),
1330a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ4,  8, 2, wm8974_bw),
1340a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ4,  5, 4, wm8974_eq4),
1350a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ5,  8, 2, wm8974_bw),
1360a1bf553SMark Brown 
1370a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_EQ5,  5, 4, wm8974_eq5),
1380a1bf553SMark Brown 	SOC_ENUM_SINGLE(WM8974_ALC3,  8, 2, wm8974_alc),
1390a1bf553SMark Brown };
1400a1bf553SMark Brown 
141a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
142a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
143a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
144a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
145a5f8d2f1SMark Brown 
1460a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_snd_controls[] = {
1470a1bf553SMark Brown 
1480a1bf553SMark Brown SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
1490a1bf553SMark Brown 
1500a1bf553SMark Brown SOC_ENUM("DAC Companding", wm8974_enum[1]),
1510a1bf553SMark Brown SOC_ENUM("ADC Companding", wm8974_enum[0]),
1520a1bf553SMark Brown 
1530a1bf553SMark Brown SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
1540a1bf553SMark Brown SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
1550a1bf553SMark Brown 
156a5f8d2f1SMark Brown SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv),
1570a1bf553SMark Brown 
1580a1bf553SMark Brown SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
1590a1bf553SMark Brown SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
1600a1bf553SMark Brown SOC_SINGLE("ADC Inversion Switch", WM8974_COMP, 0, 1, 0),
1610a1bf553SMark Brown 
162a5f8d2f1SMark Brown SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL,  0, 255, 0, digital_tlv),
1630a1bf553SMark Brown 
1640a1bf553SMark Brown SOC_ENUM("Equaliser Function", wm8974_enum[3]),
1650a1bf553SMark Brown SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
166a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1,  0, 24, 1, eq_tlv),
1670a1bf553SMark Brown 
1680a1bf553SMark Brown SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
1690a1bf553SMark Brown SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
170a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2,  0, 24, 1, eq_tlv),
1710a1bf553SMark Brown 
1720a1bf553SMark Brown SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
1730a1bf553SMark Brown SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
174a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3,  0, 24, 1, eq_tlv),
1750a1bf553SMark Brown 
1760a1bf553SMark Brown SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
1770a1bf553SMark Brown SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
178a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4,  0, 24, 1, eq_tlv),
1790a1bf553SMark Brown 
1800a1bf553SMark Brown SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
1810a1bf553SMark Brown SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
182a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5,  0, 24, 1, eq_tlv),
1830a1bf553SMark Brown 
1840a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1,  8, 1, 0),
1850a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1,  4, 15, 0),
1860a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1,  0, 15, 0),
1870a1bf553SMark Brown 
1880a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2,  4, 7, 0),
1890a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2,  0, 15, 0),
1900a1bf553SMark Brown 
1910a1bf553SMark Brown SOC_SINGLE("ALC Enable Switch", WM8974_ALC1,  8, 1, 0),
1920a1bf553SMark Brown SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1,  3, 7, 0),
1930a1bf553SMark Brown SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1,  0, 7, 0),
1940a1bf553SMark Brown 
1950a1bf553SMark Brown SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2,  8, 1, 0),
1960a1bf553SMark Brown SOC_SINGLE("ALC Capture Hold", WM8974_ALC2,  4, 7, 0),
1970a1bf553SMark Brown SOC_SINGLE("ALC Capture Target", WM8974_ALC2,  0, 15, 0),
1980a1bf553SMark Brown 
1990a1bf553SMark Brown SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
2000a1bf553SMark Brown SOC_SINGLE("ALC Capture Decay", WM8974_ALC3,  4, 15, 0),
2010a1bf553SMark Brown SOC_SINGLE("ALC Capture Attack", WM8974_ALC3,  0, 15, 0),
2020a1bf553SMark Brown 
2030a1bf553SMark Brown SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE,  3, 1, 0),
2040a1bf553SMark Brown SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE,  0, 7, 0),
2050a1bf553SMark Brown 
2060a1bf553SMark Brown SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA,  7, 1, 0),
207a5f8d2f1SMark Brown SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA,  0, 63, 0, inpga_tlv),
2080a1bf553SMark Brown 
2090a1bf553SMark Brown SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL,  7, 1, 0),
2100a1bf553SMark Brown SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL,  6, 1, 1),
211a5f8d2f1SMark Brown SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL,  0, 63, 1, spk_tlv),
2120a1bf553SMark Brown 
2130a1bf553SMark Brown SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST,  8, 1, 0),
2140a1bf553SMark Brown SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 0),
2150a1bf553SMark Brown };
2160a1bf553SMark Brown 
2170a1bf553SMark Brown /* Speaker Output Mixer */
2180a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
2190a1bf553SMark Brown SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
2200a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
2210a1bf553SMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1),
2220a1bf553SMark Brown };
2230a1bf553SMark Brown 
2240a1bf553SMark Brown /* Mono Output Mixer */
2250a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
2260a1bf553SMark Brown SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
2270a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
2280a1bf553SMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 1),
2290a1bf553SMark Brown };
2300a1bf553SMark Brown 
2310a1bf553SMark Brown /* AUX Input boost vol */
2320a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_aux_boost_controls =
2330a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
2340a1bf553SMark Brown 
2350a1bf553SMark Brown /* Mic Input boost vol */
2360a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_mic_boost_controls =
2370a1bf553SMark Brown SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
2380a1bf553SMark Brown 
2390a1bf553SMark Brown /* Capture boost switch */
2400a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_capture_boost_controls =
2410a1bf553SMark Brown SOC_DAPM_SINGLE("Capture Boost Switch", WM8974_INPPGA,  6, 1, 0);
2420a1bf553SMark Brown 
2430a1bf553SMark Brown /* Aux In to PGA */
2440a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_aux_capture_boost_controls =
2450a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Capture Boost Switch", WM8974_INPPGA,  2, 1, 0);
2460a1bf553SMark Brown 
2470a1bf553SMark Brown /* Mic P In to PGA */
2480a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_micp_capture_boost_controls =
2490a1bf553SMark Brown SOC_DAPM_SINGLE("Mic P Capture Boost Switch", WM8974_INPPGA,  0, 1, 0);
2500a1bf553SMark Brown 
2510a1bf553SMark Brown /* Mic N In to PGA */
2520a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_micn_capture_boost_controls =
2530a1bf553SMark Brown SOC_DAPM_SINGLE("Mic N Capture Boost Switch", WM8974_INPPGA,  1, 1, 0);
2540a1bf553SMark Brown 
2550a1bf553SMark Brown static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
2560a1bf553SMark Brown SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
2570a1bf553SMark Brown 	&wm8974_speaker_mixer_controls[0],
2580a1bf553SMark Brown 	ARRAY_SIZE(wm8974_speaker_mixer_controls)),
2590a1bf553SMark Brown SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
2600a1bf553SMark Brown 	&wm8974_mono_mixer_controls[0],
2610a1bf553SMark Brown 	ARRAY_SIZE(wm8974_mono_mixer_controls)),
2620a1bf553SMark Brown SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
2630a1bf553SMark Brown SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER3, 0, 0),
2640a1bf553SMark Brown SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
2650a1bf553SMark Brown SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
2660a1bf553SMark Brown SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
2670a1bf553SMark Brown SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
2680a1bf553SMark Brown SND_SOC_DAPM_PGA("Mic PGA", WM8974_POWER2, 2, 0, NULL, 0),
2690a1bf553SMark Brown 
2700a1bf553SMark Brown SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
2710a1bf553SMark Brown 	&wm8974_aux_boost_controls, 1),
2720a1bf553SMark Brown SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
2730a1bf553SMark Brown 	&wm8974_mic_boost_controls, 1),
2740a1bf553SMark Brown SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
2750a1bf553SMark Brown 	&wm8974_capture_boost_controls),
2760a1bf553SMark Brown 
2770a1bf553SMark Brown SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0, NULL, 0),
2780a1bf553SMark Brown 
2790a1bf553SMark Brown SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
2800a1bf553SMark Brown 
2810a1bf553SMark Brown SND_SOC_DAPM_INPUT("MICN"),
2820a1bf553SMark Brown SND_SOC_DAPM_INPUT("MICP"),
2830a1bf553SMark Brown SND_SOC_DAPM_INPUT("AUX"),
2840a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("MONOOUT"),
2850a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("SPKOUTP"),
2860a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("SPKOUTN"),
2870a1bf553SMark Brown };
2880a1bf553SMark Brown 
2890a1bf553SMark Brown static const struct snd_soc_dapm_route audio_map[] = {
2900a1bf553SMark Brown 	/* Mono output mixer */
2910a1bf553SMark Brown 	{"Mono Mixer", "PCM Playback Switch", "DAC"},
2920a1bf553SMark Brown 	{"Mono Mixer", "Aux Playback Switch", "Aux Input"},
2930a1bf553SMark Brown 	{"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
2940a1bf553SMark Brown 
2950a1bf553SMark Brown 	/* Speaker output mixer */
2960a1bf553SMark Brown 	{"Speaker Mixer", "PCM Playback Switch", "DAC"},
2970a1bf553SMark Brown 	{"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
2980a1bf553SMark Brown 	{"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
2990a1bf553SMark Brown 
3000a1bf553SMark Brown 	/* Outputs */
3010a1bf553SMark Brown 	{"Mono Out", NULL, "Mono Mixer"},
3020a1bf553SMark Brown 	{"MONOOUT", NULL, "Mono Out"},
3030a1bf553SMark Brown 	{"SpkN Out", NULL, "Speaker Mixer"},
3040a1bf553SMark Brown 	{"SpkP Out", NULL, "Speaker Mixer"},
3050a1bf553SMark Brown 	{"SPKOUTN", NULL, "SpkN Out"},
3060a1bf553SMark Brown 	{"SPKOUTP", NULL, "SpkP Out"},
3070a1bf553SMark Brown 
3080a1bf553SMark Brown 	/* Boost Mixer */
3090a1bf553SMark Brown 	{"Boost Mixer", NULL, "ADC"},
3100a1bf553SMark Brown 	{"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
3110a1bf553SMark Brown 	{"Aux Boost", "Aux Volume", "Boost Mixer"},
3120a1bf553SMark Brown 	{"Capture Boost", "Capture Switch", "Boost Mixer"},
3130a1bf553SMark Brown 	{"Mic Boost", "Mic Volume", "Boost Mixer"},
3140a1bf553SMark Brown 
3150a1bf553SMark Brown 	/* Inputs */
3160a1bf553SMark Brown 	{"MICP", NULL, "Mic Boost"},
3170a1bf553SMark Brown 	{"MICN", NULL, "Mic PGA"},
3180a1bf553SMark Brown 	{"Mic PGA", NULL, "Capture Boost"},
3190a1bf553SMark Brown 	{"AUX", NULL, "Aux Input"},
3200a1bf553SMark Brown };
3210a1bf553SMark Brown 
3220a1bf553SMark Brown static int wm8974_add_widgets(struct snd_soc_codec *codec)
3230a1bf553SMark Brown {
3240a1bf553SMark Brown 	snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets,
3250a1bf553SMark Brown 				  ARRAY_SIZE(wm8974_dapm_widgets));
3260a1bf553SMark Brown 
3270a1bf553SMark Brown 	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
3280a1bf553SMark Brown 
3290a1bf553SMark Brown 	snd_soc_dapm_new_widgets(codec);
3300a1bf553SMark Brown 	return 0;
3310a1bf553SMark Brown }
3320a1bf553SMark Brown 
3330a1bf553SMark Brown struct pll_ {
33491d0c3ecSMark Brown 	unsigned int pre_div:4; /* prescale - 1 */
3350a1bf553SMark Brown 	unsigned int n:4;
3360a1bf553SMark Brown 	unsigned int k;
3370a1bf553SMark Brown };
3380a1bf553SMark Brown 
33991d0c3ecSMark Brown static struct pll_ pll_div;
34091d0c3ecSMark Brown 
34191d0c3ecSMark Brown /* The size in bits of the pll divide multiplied by 10
34291d0c3ecSMark Brown  * to allow rounding later */
34391d0c3ecSMark Brown #define FIXED_PLL_SIZE ((1 << 24) * 10)
34491d0c3ecSMark Brown 
34591d0c3ecSMark Brown static void pll_factors(unsigned int target, unsigned int source)
34691d0c3ecSMark Brown {
34791d0c3ecSMark Brown 	unsigned long long Kpart;
34891d0c3ecSMark Brown 	unsigned int K, Ndiv, Nmod;
34991d0c3ecSMark Brown 
35091d0c3ecSMark Brown 	Ndiv = target / source;
35191d0c3ecSMark Brown 	if (Ndiv < 6) {
35291d0c3ecSMark Brown 		source >>= 1;
35391d0c3ecSMark Brown 		pll_div.pre_div = 1;
35491d0c3ecSMark Brown 		Ndiv = target / source;
35591d0c3ecSMark Brown 	} else
35691d0c3ecSMark Brown 		pll_div.pre_div = 0;
35791d0c3ecSMark Brown 
35891d0c3ecSMark Brown 	if ((Ndiv < 6) || (Ndiv > 12))
35991d0c3ecSMark Brown 		printk(KERN_WARNING
360*8b83a193SMark Brown 			"WM8974 N value %u outwith recommended range!\n",
36191d0c3ecSMark Brown 			Ndiv);
36291d0c3ecSMark Brown 
36391d0c3ecSMark Brown 	pll_div.n = Ndiv;
36491d0c3ecSMark Brown 	Nmod = target % source;
36591d0c3ecSMark Brown 	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
36691d0c3ecSMark Brown 
36791d0c3ecSMark Brown 	do_div(Kpart, source);
36891d0c3ecSMark Brown 
36991d0c3ecSMark Brown 	K = Kpart & 0xFFFFFFFF;
37091d0c3ecSMark Brown 
37191d0c3ecSMark Brown 	/* Check if we need to round */
37291d0c3ecSMark Brown 	if ((K % 10) >= 5)
37391d0c3ecSMark Brown 		K += 5;
37491d0c3ecSMark Brown 
37591d0c3ecSMark Brown 	/* Move down to proper range now rounding is done */
37691d0c3ecSMark Brown 	K /= 10;
37791d0c3ecSMark Brown 
37891d0c3ecSMark Brown 	pll_div.k = K;
37991d0c3ecSMark Brown }
3800a1bf553SMark Brown 
3810a1bf553SMark Brown static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
3820a1bf553SMark Brown 		int pll_id, unsigned int freq_in, unsigned int freq_out)
3830a1bf553SMark Brown {
3840a1bf553SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
3850a1bf553SMark Brown 	u16 reg;
3860a1bf553SMark Brown 
3870a1bf553SMark Brown 	if (freq_in == 0 || freq_out == 0) {
38891d0c3ecSMark Brown 		/* Clock CODEC directly from MCLK */
38991d0c3ecSMark Brown 		reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
39091d0c3ecSMark Brown 		wm8974_write(codec, WM8974_CLOCK, reg & 0x0ff);
39191d0c3ecSMark Brown 
39291d0c3ecSMark Brown 		/* Turn off PLL */
3930a1bf553SMark Brown 		reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
3940a1bf553SMark Brown 		wm8974_write(codec, WM8974_POWER1, reg & 0x1df);
3950a1bf553SMark Brown 		return 0;
3960a1bf553SMark Brown 	}
3970a1bf553SMark Brown 
39891d0c3ecSMark Brown 	pll_factors(freq_out*4, freq_in);
39991d0c3ecSMark Brown 
40091d0c3ecSMark Brown 	wm8974_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
40191d0c3ecSMark Brown 	wm8974_write(codec, WM8974_PLLK1, pll_div.k >> 18);
40291d0c3ecSMark Brown 	wm8974_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
40391d0c3ecSMark Brown 	wm8974_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
4040a1bf553SMark Brown 	reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
4050a1bf553SMark Brown 	wm8974_write(codec, WM8974_POWER1, reg | 0x020);
4061a55b3f6SMark Brown 
40791d0c3ecSMark Brown 	/* Run CODEC from PLL instead of MCLK */
40891d0c3ecSMark Brown 	reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
40991d0c3ecSMark Brown 	wm8974_write(codec, WM8974_CLOCK, reg | 0x100);
41091d0c3ecSMark Brown 
41191d0c3ecSMark Brown 	return 0;
4120a1bf553SMark Brown }
4130a1bf553SMark Brown 
4140a1bf553SMark Brown /*
4150a1bf553SMark Brown  * Configure WM8974 clock dividers.
4160a1bf553SMark Brown  */
4170a1bf553SMark Brown static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
4180a1bf553SMark Brown 		int div_id, int div)
4190a1bf553SMark Brown {
4200a1bf553SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
4210a1bf553SMark Brown 	u16 reg;
4220a1bf553SMark Brown 
4230a1bf553SMark Brown 	switch (div_id) {
4240a1bf553SMark Brown 	case WM8974_OPCLKDIV:
4250a1bf553SMark Brown 		reg = wm8974_read_reg_cache(codec, WM8974_GPIO) & 0x1cf;
4260a1bf553SMark Brown 		wm8974_write(codec, WM8974_GPIO, reg | div);
4270a1bf553SMark Brown 		break;
4280a1bf553SMark Brown 	case WM8974_MCLKDIV:
4290a1bf553SMark Brown 		reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x11f;
4300a1bf553SMark Brown 		wm8974_write(codec, WM8974_CLOCK, reg | div);
4310a1bf553SMark Brown 		break;
4320a1bf553SMark Brown 	case WM8974_ADCCLK:
4330a1bf553SMark Brown 		reg = wm8974_read_reg_cache(codec, WM8974_ADC) & 0x1f7;
4340a1bf553SMark Brown 		wm8974_write(codec, WM8974_ADC, reg | div);
4350a1bf553SMark Brown 		break;
4360a1bf553SMark Brown 	case WM8974_DACCLK:
4370a1bf553SMark Brown 		reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0x1f7;
4380a1bf553SMark Brown 		wm8974_write(codec, WM8974_DAC, reg | div);
4390a1bf553SMark Brown 		break;
4400a1bf553SMark Brown 	case WM8974_BCLKDIV:
4410a1bf553SMark Brown 		reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1e3;
4420a1bf553SMark Brown 		wm8974_write(codec, WM8974_CLOCK, reg | div);
4430a1bf553SMark Brown 		break;
4440a1bf553SMark Brown 	default:
4450a1bf553SMark Brown 		return -EINVAL;
4460a1bf553SMark Brown 	}
4470a1bf553SMark Brown 
4480a1bf553SMark Brown 	return 0;
4490a1bf553SMark Brown }
4500a1bf553SMark Brown 
4510a1bf553SMark Brown static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
4520a1bf553SMark Brown 		unsigned int fmt)
4530a1bf553SMark Brown {
4540a1bf553SMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
4550a1bf553SMark Brown 	u16 iface = 0;
4560a1bf553SMark Brown 	u16 clk = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1fe;
4570a1bf553SMark Brown 
4580a1bf553SMark Brown 	/* set master/slave audio interface */
4590a1bf553SMark Brown 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
4600a1bf553SMark Brown 	case SND_SOC_DAIFMT_CBM_CFM:
4610a1bf553SMark Brown 		clk |= 0x0001;
4620a1bf553SMark Brown 		break;
4630a1bf553SMark Brown 	case SND_SOC_DAIFMT_CBS_CFS:
4640a1bf553SMark Brown 		break;
4650a1bf553SMark Brown 	default:
4660a1bf553SMark Brown 		return -EINVAL;
4670a1bf553SMark Brown 	}
4680a1bf553SMark Brown 
4690a1bf553SMark Brown 	/* interface format */
4700a1bf553SMark Brown 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
4710a1bf553SMark Brown 	case SND_SOC_DAIFMT_I2S:
4720a1bf553SMark Brown 		iface |= 0x0010;
4730a1bf553SMark Brown 		break;
4740a1bf553SMark Brown 	case SND_SOC_DAIFMT_RIGHT_J:
4750a1bf553SMark Brown 		break;
4760a1bf553SMark Brown 	case SND_SOC_DAIFMT_LEFT_J:
4770a1bf553SMark Brown 		iface |= 0x0008;
4780a1bf553SMark Brown 		break;
4790a1bf553SMark Brown 	case SND_SOC_DAIFMT_DSP_A:
4800a1bf553SMark Brown 		iface |= 0x00018;
4810a1bf553SMark Brown 		break;
4820a1bf553SMark Brown 	default:
4830a1bf553SMark Brown 		return -EINVAL;
4840a1bf553SMark Brown 	}
4850a1bf553SMark Brown 
4860a1bf553SMark Brown 	/* clock inversion */
4870a1bf553SMark Brown 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
4880a1bf553SMark Brown 	case SND_SOC_DAIFMT_NB_NF:
4890a1bf553SMark Brown 		break;
4900a1bf553SMark Brown 	case SND_SOC_DAIFMT_IB_IF:
4910a1bf553SMark Brown 		iface |= 0x0180;
4920a1bf553SMark Brown 		break;
4930a1bf553SMark Brown 	case SND_SOC_DAIFMT_IB_NF:
4940a1bf553SMark Brown 		iface |= 0x0100;
4950a1bf553SMark Brown 		break;
4960a1bf553SMark Brown 	case SND_SOC_DAIFMT_NB_IF:
4970a1bf553SMark Brown 		iface |= 0x0080;
4980a1bf553SMark Brown 		break;
4990a1bf553SMark Brown 	default:
5000a1bf553SMark Brown 		return -EINVAL;
5010a1bf553SMark Brown 	}
5020a1bf553SMark Brown 
5030a1bf553SMark Brown 	wm8974_write(codec, WM8974_IFACE, iface);
5040a1bf553SMark Brown 	wm8974_write(codec, WM8974_CLOCK, clk);
5050a1bf553SMark Brown 	return 0;
5060a1bf553SMark Brown }
5070a1bf553SMark Brown 
5080a1bf553SMark Brown static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
5090a1bf553SMark Brown 				struct snd_pcm_hw_params *params,
5100a1bf553SMark Brown 				struct snd_soc_dai *dai)
5110a1bf553SMark Brown {
5120a1bf553SMark Brown 	struct snd_soc_codec *codec = dai->codec;
5130a1bf553SMark Brown 	u16 iface = wm8974_read_reg_cache(codec, WM8974_IFACE) & 0x19f;
5140a1bf553SMark Brown 	u16 adn = wm8974_read_reg_cache(codec, WM8974_ADD) & 0x1f1;
5150a1bf553SMark Brown 
5160a1bf553SMark Brown 	/* bit size */
5170a1bf553SMark Brown 	switch (params_format(params)) {
5180a1bf553SMark Brown 	case SNDRV_PCM_FORMAT_S16_LE:
5190a1bf553SMark Brown 		break;
5200a1bf553SMark Brown 	case SNDRV_PCM_FORMAT_S20_3LE:
5210a1bf553SMark Brown 		iface |= 0x0020;
5220a1bf553SMark Brown 		break;
5230a1bf553SMark Brown 	case SNDRV_PCM_FORMAT_S24_LE:
5240a1bf553SMark Brown 		iface |= 0x0040;
5250a1bf553SMark Brown 		break;
5260a1bf553SMark Brown 	case SNDRV_PCM_FORMAT_S32_LE:
5270a1bf553SMark Brown 		iface |= 0x0060;
5280a1bf553SMark Brown 		break;
5290a1bf553SMark Brown 	}
5300a1bf553SMark Brown 
5310a1bf553SMark Brown 	/* filter coefficient */
5320a1bf553SMark Brown 	switch (params_rate(params)) {
5330a1bf553SMark Brown 	case SNDRV_PCM_RATE_8000:
5340a1bf553SMark Brown 		adn |= 0x5 << 1;
5350a1bf553SMark Brown 		break;
5360a1bf553SMark Brown 	case SNDRV_PCM_RATE_11025:
5370a1bf553SMark Brown 		adn |= 0x4 << 1;
5380a1bf553SMark Brown 		break;
5390a1bf553SMark Brown 	case SNDRV_PCM_RATE_16000:
5400a1bf553SMark Brown 		adn |= 0x3 << 1;
5410a1bf553SMark Brown 		break;
5420a1bf553SMark Brown 	case SNDRV_PCM_RATE_22050:
5430a1bf553SMark Brown 		adn |= 0x2 << 1;
5440a1bf553SMark Brown 		break;
5450a1bf553SMark Brown 	case SNDRV_PCM_RATE_32000:
5460a1bf553SMark Brown 		adn |= 0x1 << 1;
5470a1bf553SMark Brown 		break;
5480a1bf553SMark Brown 	case SNDRV_PCM_RATE_44100:
549*8b83a193SMark Brown 	case SNDRV_PCM_RATE_48000:
5500a1bf553SMark Brown 		break;
5510a1bf553SMark Brown 	}
5520a1bf553SMark Brown 
5530a1bf553SMark Brown 	wm8974_write(codec, WM8974_IFACE, iface);
5540a1bf553SMark Brown 	wm8974_write(codec, WM8974_ADD, adn);
5550a1bf553SMark Brown 	return 0;
5560a1bf553SMark Brown }
5570a1bf553SMark Brown 
5580a1bf553SMark Brown static int wm8974_mute(struct snd_soc_dai *dai, int mute)
5590a1bf553SMark Brown {
5600a1bf553SMark Brown 	struct snd_soc_codec *codec = dai->codec;
5610a1bf553SMark Brown 	u16 mute_reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0xffbf;
5620a1bf553SMark Brown 
5630a1bf553SMark Brown 	if (mute)
5640a1bf553SMark Brown 		wm8974_write(codec, WM8974_DAC, mute_reg | 0x40);
5650a1bf553SMark Brown 	else
5660a1bf553SMark Brown 		wm8974_write(codec, WM8974_DAC, mute_reg);
5670a1bf553SMark Brown 	return 0;
5680a1bf553SMark Brown }
5690a1bf553SMark Brown 
5700a1bf553SMark Brown /* liam need to make this lower power with dapm */
5710a1bf553SMark Brown static int wm8974_set_bias_level(struct snd_soc_codec *codec,
5720a1bf553SMark Brown 	enum snd_soc_bias_level level)
5730a1bf553SMark Brown {
574df1ef7a3SMark Brown 	u16 power1 = wm8974_read_reg_cache(codec, WM8974_POWER1) & ~0x3;
575df1ef7a3SMark Brown 
5760a1bf553SMark Brown 	switch (level) {
5770a1bf553SMark Brown 	case SND_SOC_BIAS_ON:
5780a1bf553SMark Brown 	case SND_SOC_BIAS_PREPARE:
579df1ef7a3SMark Brown 		power1 |= 0x1;  /* VMID 50k */
580df1ef7a3SMark Brown 		wm8974_write(codec, WM8974_POWER1, power1);
5810a1bf553SMark Brown 		break;
582df1ef7a3SMark Brown 
5830a1bf553SMark Brown 	case SND_SOC_BIAS_STANDBY:
584df1ef7a3SMark Brown 		power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
585df1ef7a3SMark Brown 
586df1ef7a3SMark Brown 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
587df1ef7a3SMark Brown 			/* Initial cap charge at VMID 5k */
588df1ef7a3SMark Brown 			wm8974_write(codec, WM8974_POWER1, power1 | 0x3);
589df1ef7a3SMark Brown 			mdelay(100);
590df1ef7a3SMark Brown 		}
591df1ef7a3SMark Brown 
592df1ef7a3SMark Brown 		power1 |= 0x2;  /* VMID 500k */
593df1ef7a3SMark Brown 		wm8974_write(codec, WM8974_POWER1, power1);
5940a1bf553SMark Brown 		break;
595df1ef7a3SMark Brown 
5960a1bf553SMark Brown 	case SND_SOC_BIAS_OFF:
597df1ef7a3SMark Brown 		wm8974_write(codec, WM8974_POWER1, 0);
598df1ef7a3SMark Brown 		wm8974_write(codec, WM8974_POWER2, 0);
599df1ef7a3SMark Brown 		wm8974_write(codec, WM8974_POWER3, 0);
6000a1bf553SMark Brown 		break;
6010a1bf553SMark Brown 	}
602df1ef7a3SMark Brown 
6030a1bf553SMark Brown 	codec->bias_level = level;
6040a1bf553SMark Brown 	return 0;
6050a1bf553SMark Brown }
6060a1bf553SMark Brown 
6071a55b3f6SMark Brown #define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
6080a1bf553SMark Brown 
6090a1bf553SMark Brown #define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
6100a1bf553SMark Brown 	SNDRV_PCM_FMTBIT_S24_LE)
6110a1bf553SMark Brown 
6120a1bf553SMark Brown static struct snd_soc_dai_ops wm8974_ops = {
6130a1bf553SMark Brown 	.hw_params = wm8974_pcm_hw_params,
6140a1bf553SMark Brown 	.digital_mute = wm8974_mute,
6150a1bf553SMark Brown 	.set_fmt = wm8974_set_dai_fmt,
6160a1bf553SMark Brown 	.set_clkdiv = wm8974_set_dai_clkdiv,
6170a1bf553SMark Brown 	.set_pll = wm8974_set_dai_pll,
6180a1bf553SMark Brown };
6190a1bf553SMark Brown 
6200a1bf553SMark Brown struct snd_soc_dai wm8974_dai = {
6210a1bf553SMark Brown 	.name = "WM8974 HiFi",
6220a1bf553SMark Brown 	.playback = {
6230a1bf553SMark Brown 		.stream_name = "Playback",
6240a1bf553SMark Brown 		.channels_min = 1,
62533d81af4SMark Brown 		.channels_max = 2,   /* Only 1 channel of data */
6260a1bf553SMark Brown 		.rates = WM8974_RATES,
6270a1bf553SMark Brown 		.formats = WM8974_FORMATS,},
6280a1bf553SMark Brown 	.capture = {
6290a1bf553SMark Brown 		.stream_name = "Capture",
6300a1bf553SMark Brown 		.channels_min = 1,
63133d81af4SMark Brown 		.channels_max = 2,   /* Only 1 channel of data */
6320a1bf553SMark Brown 		.rates = WM8974_RATES,
6330a1bf553SMark Brown 		.formats = WM8974_FORMATS,},
6340a1bf553SMark Brown 	.ops = &wm8974_ops,
635cb11d39eSMark Brown 	.symmetric_rates = 1,
6360a1bf553SMark Brown };
6370a1bf553SMark Brown EXPORT_SYMBOL_GPL(wm8974_dai);
6380a1bf553SMark Brown 
6390a1bf553SMark Brown static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
6400a1bf553SMark Brown {
6410a1bf553SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
6420a1bf553SMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
6430a1bf553SMark Brown 
6440a1bf553SMark Brown 	wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
6450a1bf553SMark Brown 	return 0;
6460a1bf553SMark Brown }
6470a1bf553SMark Brown 
6480a1bf553SMark Brown static int wm8974_resume(struct platform_device *pdev)
6490a1bf553SMark Brown {
6500a1bf553SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
6510a1bf553SMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
6520a1bf553SMark Brown 	int i;
6530a1bf553SMark Brown 	u8 data[2];
6540a1bf553SMark Brown 	u16 *cache = codec->reg_cache;
6550a1bf553SMark Brown 
6560a1bf553SMark Brown 	/* Sync reg_cache with the hardware */
6570a1bf553SMark Brown 	for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
6580a1bf553SMark Brown 		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
6590a1bf553SMark Brown 		data[1] = cache[i] & 0x00ff;
6600a1bf553SMark Brown 		codec->hw_write(codec->control_data, data, 2);
6610a1bf553SMark Brown 	}
6620a1bf553SMark Brown 	wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
6630a1bf553SMark Brown 	wm8974_set_bias_level(codec, codec->suspend_bias_level);
6640a1bf553SMark Brown 	return 0;
6650a1bf553SMark Brown }
6660a1bf553SMark Brown 
6674fcbbb67SMark Brown static int wm8974_probe(struct platform_device *pdev)
6680a1bf553SMark Brown {
6694fcbbb67SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
6704fcbbb67SMark Brown 	struct snd_soc_codec *codec;
6710a1bf553SMark Brown 	int ret = 0;
6720a1bf553SMark Brown 
6734fcbbb67SMark Brown 	if (wm8974_codec == NULL) {
6744fcbbb67SMark Brown 		dev_err(&pdev->dev, "Codec device not registered\n");
6754fcbbb67SMark Brown 		return -ENODEV;
6764fcbbb67SMark Brown 	}
6770a1bf553SMark Brown 
6784fcbbb67SMark Brown 	socdev->card->codec = wm8974_codec;
6794fcbbb67SMark Brown 	codec = wm8974_codec;
6800a1bf553SMark Brown 
6810a1bf553SMark Brown 	/* register pcms */
6820a1bf553SMark Brown 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
6830a1bf553SMark Brown 	if (ret < 0) {
6844fcbbb67SMark Brown 		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
6850a1bf553SMark Brown 		goto pcm_err;
6860a1bf553SMark Brown 	}
6870a1bf553SMark Brown 
6884fcbbb67SMark Brown 	snd_soc_add_controls(codec, wm8974_snd_controls,
6894fcbbb67SMark Brown 			     ARRAY_SIZE(wm8974_snd_controls));
6900a1bf553SMark Brown 	wm8974_add_widgets(codec);
6910a1bf553SMark Brown 	ret = snd_soc_init_card(socdev);
6920a1bf553SMark Brown 	if (ret < 0) {
6934fcbbb67SMark Brown 		dev_err(codec->dev, "failed to register card: %d\n", ret);
6940a1bf553SMark Brown 		goto card_err;
6950a1bf553SMark Brown 	}
6964fcbbb67SMark Brown 
6970a1bf553SMark Brown 	return ret;
6980a1bf553SMark Brown 
6990a1bf553SMark Brown card_err:
7000a1bf553SMark Brown 	snd_soc_free_pcms(socdev);
7010a1bf553SMark Brown 	snd_soc_dapm_free(socdev);
7020a1bf553SMark Brown pcm_err:
7030a1bf553SMark Brown 	return ret;
7040a1bf553SMark Brown }
7050a1bf553SMark Brown 
7060a1bf553SMark Brown /* power down chip */
7070a1bf553SMark Brown static int wm8974_remove(struct platform_device *pdev)
7080a1bf553SMark Brown {
7090a1bf553SMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
7100a1bf553SMark Brown 
7110a1bf553SMark Brown 	snd_soc_free_pcms(socdev);
7120a1bf553SMark Brown 	snd_soc_dapm_free(socdev);
7130a1bf553SMark Brown 
7140a1bf553SMark Brown 	return 0;
7150a1bf553SMark Brown }
7160a1bf553SMark Brown 
7170a1bf553SMark Brown struct snd_soc_codec_device soc_codec_dev_wm8974 = {
7180a1bf553SMark Brown 	.probe = 	wm8974_probe,
7190a1bf553SMark Brown 	.remove = 	wm8974_remove,
7200a1bf553SMark Brown 	.suspend = 	wm8974_suspend,
7210a1bf553SMark Brown 	.resume =	wm8974_resume,
7220a1bf553SMark Brown };
7230a1bf553SMark Brown EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
7240a1bf553SMark Brown 
7254fcbbb67SMark Brown static __devinit int wm8974_register(struct wm8974_priv *wm8974)
7264fcbbb67SMark Brown {
7274fcbbb67SMark Brown 	int ret;
7284fcbbb67SMark Brown 	struct snd_soc_codec *codec = &wm8974->codec;
7294fcbbb67SMark Brown 
7304fcbbb67SMark Brown 	if (wm8974_codec) {
7314fcbbb67SMark Brown 		dev_err(codec->dev, "Another WM8974 is registered\n");
7324fcbbb67SMark Brown 		return -EINVAL;
7334fcbbb67SMark Brown 	}
7344fcbbb67SMark Brown 
7354fcbbb67SMark Brown 	mutex_init(&codec->mutex);
7364fcbbb67SMark Brown 	INIT_LIST_HEAD(&codec->dapm_widgets);
7374fcbbb67SMark Brown 	INIT_LIST_HEAD(&codec->dapm_paths);
7384fcbbb67SMark Brown 
7394fcbbb67SMark Brown 	codec->private_data = wm8974;
7404fcbbb67SMark Brown 	codec->name = "WM8974";
7414fcbbb67SMark Brown 	codec->owner = THIS_MODULE;
7424fcbbb67SMark Brown 	codec->read = wm8974_read_reg_cache;
7434fcbbb67SMark Brown 	codec->write = wm8974_write;
7444fcbbb67SMark Brown 	codec->bias_level = SND_SOC_BIAS_OFF;
7454fcbbb67SMark Brown 	codec->set_bias_level = wm8974_set_bias_level;
7464fcbbb67SMark Brown 	codec->dai = &wm8974_dai;
7474fcbbb67SMark Brown 	codec->num_dai = 1;
7484fcbbb67SMark Brown 	codec->reg_cache_size = WM8974_CACHEREGNUM;
7494fcbbb67SMark Brown 	codec->reg_cache = &wm8974->reg_cache;
7504fcbbb67SMark Brown 
7514fcbbb67SMark Brown 	memcpy(codec->reg_cache, wm8974_reg, sizeof(wm8974_reg));
7524fcbbb67SMark Brown 
7534fcbbb67SMark Brown 	ret = wm8974_reset(codec);
7544fcbbb67SMark Brown 	if (ret < 0) {
7554fcbbb67SMark Brown 		dev_err(codec->dev, "Failed to issue reset\n");
7564fcbbb67SMark Brown 		return ret;
7574fcbbb67SMark Brown 	}
7584fcbbb67SMark Brown 
7594fcbbb67SMark Brown 	wm8974_dai.dev = codec->dev;
7604fcbbb67SMark Brown 
7614fcbbb67SMark Brown 	wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
7624fcbbb67SMark Brown 
7634fcbbb67SMark Brown 	wm8974_codec = codec;
7644fcbbb67SMark Brown 
7654fcbbb67SMark Brown 	ret = snd_soc_register_codec(codec);
7664fcbbb67SMark Brown 	if (ret != 0) {
7674fcbbb67SMark Brown 		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
7684fcbbb67SMark Brown 		return ret;
7694fcbbb67SMark Brown 	}
7704fcbbb67SMark Brown 
7714fcbbb67SMark Brown 	ret = snd_soc_register_dai(&wm8974_dai);
7724fcbbb67SMark Brown 	if (ret != 0) {
7734fcbbb67SMark Brown 		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
7744fcbbb67SMark Brown 		snd_soc_unregister_codec(codec);
7754fcbbb67SMark Brown 		return ret;
7764fcbbb67SMark Brown 	}
7774fcbbb67SMark Brown 
7784fcbbb67SMark Brown 	return 0;
7794fcbbb67SMark Brown }
7804fcbbb67SMark Brown 
7814fcbbb67SMark Brown static __devexit void wm8974_unregister(struct wm8974_priv *wm8974)
7824fcbbb67SMark Brown {
7834fcbbb67SMark Brown 	wm8974_set_bias_level(&wm8974->codec, SND_SOC_BIAS_OFF);
7844fcbbb67SMark Brown 	snd_soc_unregister_dai(&wm8974_dai);
7854fcbbb67SMark Brown 	snd_soc_unregister_codec(&wm8974->codec);
7864fcbbb67SMark Brown 	kfree(wm8974);
7874fcbbb67SMark Brown 	wm8974_codec = NULL;
7884fcbbb67SMark Brown }
7894fcbbb67SMark Brown 
7904fcbbb67SMark Brown static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
7914fcbbb67SMark Brown 				      const struct i2c_device_id *id)
7924fcbbb67SMark Brown {
7934fcbbb67SMark Brown 	struct wm8974_priv *wm8974;
7944fcbbb67SMark Brown 	struct snd_soc_codec *codec;
7954fcbbb67SMark Brown 
7964fcbbb67SMark Brown 	wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
7974fcbbb67SMark Brown 	if (wm8974 == NULL)
7984fcbbb67SMark Brown 		return -ENOMEM;
7994fcbbb67SMark Brown 
8004fcbbb67SMark Brown 	codec = &wm8974->codec;
8014fcbbb67SMark Brown 	codec->hw_write = (hw_write_t)i2c_master_send;
8024fcbbb67SMark Brown 
8034fcbbb67SMark Brown 	i2c_set_clientdata(i2c, wm8974);
8044fcbbb67SMark Brown 	codec->control_data = i2c;
8054fcbbb67SMark Brown 
8064fcbbb67SMark Brown 	codec->dev = &i2c->dev;
8074fcbbb67SMark Brown 
8084fcbbb67SMark Brown 	return wm8974_register(wm8974);
8094fcbbb67SMark Brown }
8104fcbbb67SMark Brown 
8114fcbbb67SMark Brown static __devexit int wm8974_i2c_remove(struct i2c_client *client)
8124fcbbb67SMark Brown {
8134fcbbb67SMark Brown 	struct wm8974_priv *wm8974 = i2c_get_clientdata(client);
8144fcbbb67SMark Brown 	wm8974_unregister(wm8974);
8154fcbbb67SMark Brown 	return 0;
8164fcbbb67SMark Brown }
8174fcbbb67SMark Brown 
8184fcbbb67SMark Brown static const struct i2c_device_id wm8974_i2c_id[] = {
8194fcbbb67SMark Brown 	{ "wm8974", 0 },
8204fcbbb67SMark Brown 	{ }
8214fcbbb67SMark Brown };
8224fcbbb67SMark Brown MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
8234fcbbb67SMark Brown 
8244fcbbb67SMark Brown static struct i2c_driver wm8974_i2c_driver = {
8254fcbbb67SMark Brown 	.driver = {
826*8b83a193SMark Brown 		.name = "WM8974",
8274fcbbb67SMark Brown 		.owner = THIS_MODULE,
8284fcbbb67SMark Brown 	},
8294fcbbb67SMark Brown 	.probe =    wm8974_i2c_probe,
8304fcbbb67SMark Brown 	.remove =   __devexit_p(wm8974_i2c_remove),
8314fcbbb67SMark Brown 	.id_table = wm8974_i2c_id,
8324fcbbb67SMark Brown };
8334fcbbb67SMark Brown 
8340a1bf553SMark Brown static int __init wm8974_modinit(void)
8350a1bf553SMark Brown {
8364fcbbb67SMark Brown 	return i2c_add_driver(&wm8974_i2c_driver);
8370a1bf553SMark Brown }
8380a1bf553SMark Brown module_init(wm8974_modinit);
8390a1bf553SMark Brown 
8400a1bf553SMark Brown static void __exit wm8974_exit(void)
8410a1bf553SMark Brown {
8424fcbbb67SMark Brown 	i2c_del_driver(&wm8974_i2c_driver);
8430a1bf553SMark Brown }
8440a1bf553SMark Brown module_exit(wm8974_exit);
8450a1bf553SMark Brown 
8460a1bf553SMark Brown MODULE_DESCRIPTION("ASoC WM8974 driver");
8470a1bf553SMark Brown MODULE_AUTHOR("Liam Girdwood");
8480a1bf553SMark Brown MODULE_LICENSE("GPL");
849