10a1bf553SMark Brown /* 20a1bf553SMark Brown * wm8974.c -- WM8974 ALSA Soc Audio driver 30a1bf553SMark Brown * 48b83a193SMark Brown * Copyright 2006-2009 Wolfson Microelectronics PLC. 50a1bf553SMark Brown * 69a185b9aSMark Brown * Author: Liam Girdwood <Liam.Girdwood@wolfsonmicro.com> 70a1bf553SMark Brown * 80a1bf553SMark Brown * This program is free software; you can redistribute it and/or modify 90a1bf553SMark Brown * it under the terms of the GNU General Public License version 2 as 100a1bf553SMark Brown * published by the Free Software Foundation. 110a1bf553SMark Brown */ 120a1bf553SMark Brown 130a1bf553SMark Brown #include <linux/module.h> 140a1bf553SMark Brown #include <linux/moduleparam.h> 150a1bf553SMark Brown #include <linux/kernel.h> 160a1bf553SMark Brown #include <linux/init.h> 170a1bf553SMark Brown #include <linux/delay.h> 180a1bf553SMark Brown #include <linux/pm.h> 190a1bf553SMark Brown #include <linux/i2c.h> 200a1bf553SMark Brown #include <linux/platform_device.h> 215a0e3ad6STejun Heo #include <linux/slab.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/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 5048c03ce7SGuennadi Liakhovetski #define WM8974_POWER1_BUFIOEN 0x04 51df1ef7a3SMark Brown 524fcbbb67SMark Brown struct wm8974_priv { 53f0fba2adSLiam Girdwood enum snd_soc_control_type control_type; 544fcbbb67SMark Brown }; 554fcbbb67SMark Brown 561e97f50bSMark Brown #define wm8974_reset(c) snd_soc_write(c, WM8974_RESET, 0) 570a1bf553SMark Brown 580a1bf553SMark Brown static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" }; 590a1bf553SMark Brown static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" }; 600a1bf553SMark Brown static const char *wm8974_eqmode[] = {"Capture", "Playback" }; 610a1bf553SMark Brown static const char *wm8974_bw[] = {"Narrow", "Wide" }; 620a1bf553SMark Brown static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" }; 630a1bf553SMark Brown static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" }; 640a1bf553SMark Brown static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" }; 650a1bf553SMark Brown static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" }; 660a1bf553SMark Brown static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" }; 670a1bf553SMark Brown static const char *wm8974_alc[] = {"ALC", "Limiter" }; 680a1bf553SMark Brown 690a1bf553SMark Brown static const struct soc_enum wm8974_enum[] = { 700a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */ 710a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */ 720a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp), 730a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode), 740a1bf553SMark Brown 750a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1), 760a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw), 770a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2), 780a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw), 790a1bf553SMark Brown 800a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3), 810a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw), 820a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4), 830a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw), 840a1bf553SMark Brown 850a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5), 860a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc), 870a1bf553SMark Brown }; 880a1bf553SMark Brown 898a123ee2SMark Brown static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" }; 908a123ee2SMark Brown 918a123ee2SMark Brown static const struct soc_enum wm8974_auxmode = 928a123ee2SMark Brown SOC_ENUM_SINGLE(WM8974_INPUT, 3, 2, wm8974_auxmode_text); 938a123ee2SMark Brown 94a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1); 95a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); 96a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0); 97a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0); 98a5f8d2f1SMark Brown 990a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_snd_controls[] = { 1000a1bf553SMark Brown 1010a1bf553SMark Brown SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0), 1020a1bf553SMark Brown 1030a1bf553SMark Brown SOC_ENUM("DAC Companding", wm8974_enum[1]), 1040a1bf553SMark Brown SOC_ENUM("ADC Companding", wm8974_enum[0]), 1050a1bf553SMark Brown 1060a1bf553SMark Brown SOC_ENUM("Playback De-emphasis", wm8974_enum[2]), 1070a1bf553SMark Brown SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0), 1080a1bf553SMark Brown 109a5f8d2f1SMark Brown SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv), 1100a1bf553SMark Brown 1110a1bf553SMark Brown SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0), 1120a1bf553SMark Brown SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0), 11325cbf465Sjavier Martin SOC_SINGLE("ADC Inversion Switch", WM8974_ADC, 0, 1, 0), 1140a1bf553SMark Brown 115a5f8d2f1SMark Brown SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL, 0, 255, 0, digital_tlv), 1160a1bf553SMark Brown 1170a1bf553SMark Brown SOC_ENUM("Equaliser Function", wm8974_enum[3]), 1180a1bf553SMark Brown SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]), 119a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1, 0, 24, 1, eq_tlv), 1200a1bf553SMark Brown 1210a1bf553SMark Brown SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]), 1220a1bf553SMark Brown SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]), 123a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2, 0, 24, 1, eq_tlv), 1240a1bf553SMark Brown 1250a1bf553SMark Brown SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]), 1260a1bf553SMark Brown SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]), 127a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3, 0, 24, 1, eq_tlv), 1280a1bf553SMark Brown 1290a1bf553SMark Brown SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]), 1300a1bf553SMark Brown SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]), 131a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4, 0, 24, 1, eq_tlv), 1320a1bf553SMark Brown 1330a1bf553SMark Brown SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]), 1340a1bf553SMark Brown SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]), 135a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5, 0, 24, 1, eq_tlv), 1360a1bf553SMark Brown 1370a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0), 1380a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0), 1390a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0), 1400a1bf553SMark Brown 1410a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0), 1420a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0), 1430a1bf553SMark Brown 1440a1bf553SMark Brown SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0), 1450a1bf553SMark Brown SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0), 1460a1bf553SMark Brown SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0), 1470a1bf553SMark Brown 1480a1bf553SMark Brown SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0), 1490a1bf553SMark Brown SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0), 1500a1bf553SMark Brown SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0), 1510a1bf553SMark Brown 1520a1bf553SMark Brown SOC_ENUM("ALC Capture Mode", wm8974_enum[13]), 1530a1bf553SMark Brown SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0), 1540a1bf553SMark Brown SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0), 1550a1bf553SMark Brown 1560a1bf553SMark Brown SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0), 1570a1bf553SMark Brown SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0), 1580a1bf553SMark Brown 1590a1bf553SMark Brown SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0), 160a5f8d2f1SMark Brown SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0, inpga_tlv), 1610a1bf553SMark Brown 1620a1bf553SMark Brown SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0), 1630a1bf553SMark Brown SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1), 1648a123ee2SMark Brown SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0, spk_tlv), 1658a123ee2SMark Brown 1668a123ee2SMark Brown SOC_ENUM("Aux Mode", wm8974_auxmode), 1670a1bf553SMark Brown 1680a1bf553SMark Brown SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0), 1698a123ee2SMark Brown SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1), 170b2c3e923SGuennadi Liakhovetski 171b2c3e923SGuennadi Liakhovetski /* DAC / ADC oversampling */ 172b2c3e923SGuennadi Liakhovetski SOC_SINGLE("DAC 128x Oversampling Switch", WM8974_DAC, 8, 1, 0), 173b2c3e923SGuennadi Liakhovetski SOC_SINGLE("ADC 128x Oversampling Switch", WM8974_ADC, 8, 1, 0), 1740a1bf553SMark Brown }; 1750a1bf553SMark Brown 1760a1bf553SMark Brown /* Speaker Output Mixer */ 1770a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = { 1780a1bf553SMark Brown SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0), 1790a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0), 180759512fbSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 0), 1810a1bf553SMark Brown }; 1820a1bf553SMark Brown 1830a1bf553SMark Brown /* Mono Output Mixer */ 1840a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = { 1850a1bf553SMark Brown SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0), 1860a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0), 1878a123ee2SMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0), 1888a123ee2SMark Brown }; 1898a123ee2SMark Brown 1908a123ee2SMark Brown /* Boost mixer */ 1918a123ee2SMark Brown static const struct snd_kcontrol_new wm8974_boost_mixer[] = { 1928a123ee2SMark Brown SOC_DAPM_SINGLE("Aux Switch", WM8974_INPPGA, 6, 1, 0), 1938a123ee2SMark Brown }; 1948a123ee2SMark Brown 1958a123ee2SMark Brown /* Input PGA */ 1968a123ee2SMark Brown static const struct snd_kcontrol_new wm8974_inpga[] = { 1978a123ee2SMark Brown SOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0), 1988a123ee2SMark Brown SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0), 1998a123ee2SMark Brown SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0), 2000a1bf553SMark Brown }; 2010a1bf553SMark Brown 2020a1bf553SMark Brown /* AUX Input boost vol */ 2030a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_aux_boost_controls = 2040a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0); 2050a1bf553SMark Brown 2060a1bf553SMark Brown /* Mic Input boost vol */ 2070a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_mic_boost_controls = 2080a1bf553SMark Brown SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0); 2090a1bf553SMark Brown 2100a1bf553SMark Brown static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = { 2110a1bf553SMark Brown SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0, 2120a1bf553SMark Brown &wm8974_speaker_mixer_controls[0], 2130a1bf553SMark Brown ARRAY_SIZE(wm8974_speaker_mixer_controls)), 2140a1bf553SMark Brown SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0, 2150a1bf553SMark Brown &wm8974_mono_mixer_controls[0], 2160a1bf553SMark Brown ARRAY_SIZE(wm8974_mono_mixer_controls)), 2170a1bf553SMark Brown SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0), 2188a123ee2SMark Brown SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0), 2190a1bf553SMark Brown SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0), 2200a1bf553SMark Brown SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0), 2210a1bf553SMark Brown SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0), 2220a1bf553SMark Brown SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0), 2230a1bf553SMark Brown 2248a123ee2SMark Brown SND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga, 2258a123ee2SMark Brown ARRAY_SIZE(wm8974_inpga)), 2268a123ee2SMark Brown SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0, 2278a123ee2SMark Brown wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)), 2280a1bf553SMark Brown 22948dd231bSMark Brown SND_SOC_DAPM_SUPPLY("Mic Bias", WM8974_POWER1, 4, 0, NULL, 0), 2300a1bf553SMark Brown 2310a1bf553SMark Brown SND_SOC_DAPM_INPUT("MICN"), 2320a1bf553SMark Brown SND_SOC_DAPM_INPUT("MICP"), 2330a1bf553SMark Brown SND_SOC_DAPM_INPUT("AUX"), 2340a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("MONOOUT"), 2350a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("SPKOUTP"), 2360a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("SPKOUTN"), 2370a1bf553SMark Brown }; 2380a1bf553SMark Brown 2390a1bf553SMark Brown static const struct snd_soc_dapm_route audio_map[] = { 2400a1bf553SMark Brown /* Mono output mixer */ 2410a1bf553SMark Brown {"Mono Mixer", "PCM Playback Switch", "DAC"}, 2420a1bf553SMark Brown {"Mono Mixer", "Aux Playback Switch", "Aux Input"}, 2430a1bf553SMark Brown {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"}, 2440a1bf553SMark Brown 2450a1bf553SMark Brown /* Speaker output mixer */ 2460a1bf553SMark Brown {"Speaker Mixer", "PCM Playback Switch", "DAC"}, 2470a1bf553SMark Brown {"Speaker Mixer", "Aux Playback Switch", "Aux Input"}, 2480a1bf553SMark Brown {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"}, 2490a1bf553SMark Brown 2500a1bf553SMark Brown /* Outputs */ 2510a1bf553SMark Brown {"Mono Out", NULL, "Mono Mixer"}, 2520a1bf553SMark Brown {"MONOOUT", NULL, "Mono Out"}, 2530a1bf553SMark Brown {"SpkN Out", NULL, "Speaker Mixer"}, 2540a1bf553SMark Brown {"SpkP Out", NULL, "Speaker Mixer"}, 2550a1bf553SMark Brown {"SPKOUTN", NULL, "SpkN Out"}, 2560a1bf553SMark Brown {"SPKOUTP", NULL, "SpkP Out"}, 2570a1bf553SMark Brown 2580a1bf553SMark Brown /* Boost Mixer */ 2598a123ee2SMark Brown {"ADC", NULL, "Boost Mixer"}, 2608a123ee2SMark Brown {"Boost Mixer", "Aux Switch", "Aux Input"}, 2618a123ee2SMark Brown {"Boost Mixer", NULL, "Input PGA"}, 2628a123ee2SMark Brown {"Boost Mixer", NULL, "MICP"}, 2638a123ee2SMark Brown 2648a123ee2SMark Brown /* Input PGA */ 2658a123ee2SMark Brown {"Input PGA", "Aux Switch", "Aux Input"}, 2668a123ee2SMark Brown {"Input PGA", "MicN Switch", "MICN"}, 2678a123ee2SMark Brown {"Input PGA", "MicP Switch", "MICP"}, 2680a1bf553SMark Brown 2690a1bf553SMark Brown /* Inputs */ 2708a123ee2SMark Brown {"Aux Input", NULL, "AUX"}, 2710a1bf553SMark Brown }; 2720a1bf553SMark Brown 2730a1bf553SMark Brown static int wm8974_add_widgets(struct snd_soc_codec *codec) 2740a1bf553SMark Brown { 275ce6120ccSLiam Girdwood struct snd_soc_dapm_context *dapm = &codec->dapm; 2760a1bf553SMark Brown 277ce6120ccSLiam Girdwood snd_soc_dapm_new_controls(dapm, wm8974_dapm_widgets, 278ce6120ccSLiam Girdwood ARRAY_SIZE(wm8974_dapm_widgets)); 279ce6120ccSLiam Girdwood snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); 2800a1bf553SMark Brown 2810a1bf553SMark Brown return 0; 2820a1bf553SMark Brown } 2830a1bf553SMark Brown 2840a1bf553SMark Brown struct pll_ { 285c36b2fc7SMark Brown unsigned int pre_div:1; 2860a1bf553SMark Brown unsigned int n:4; 2870a1bf553SMark Brown unsigned int k; 2880a1bf553SMark Brown }; 2890a1bf553SMark Brown 29091d0c3ecSMark Brown /* The size in bits of the pll divide multiplied by 10 29191d0c3ecSMark Brown * to allow rounding later */ 29291d0c3ecSMark Brown #define FIXED_PLL_SIZE ((1 << 24) * 10) 29391d0c3ecSMark Brown 294c36b2fc7SMark Brown static void pll_factors(struct pll_ *pll_div, 295c36b2fc7SMark Brown unsigned int target, unsigned int source) 29691d0c3ecSMark Brown { 29791d0c3ecSMark Brown unsigned long long Kpart; 29891d0c3ecSMark Brown unsigned int K, Ndiv, Nmod; 29991d0c3ecSMark Brown 300c36b2fc7SMark Brown /* There is a fixed divide by 4 in the output path */ 301c36b2fc7SMark Brown target *= 4; 302c36b2fc7SMark Brown 30391d0c3ecSMark Brown Ndiv = target / source; 30491d0c3ecSMark Brown if (Ndiv < 6) { 305c36b2fc7SMark Brown source /= 2; 306c36b2fc7SMark Brown pll_div->pre_div = 1; 30791d0c3ecSMark Brown Ndiv = target / source; 30891d0c3ecSMark Brown } else 309c36b2fc7SMark Brown pll_div->pre_div = 0; 31091d0c3ecSMark Brown 31191d0c3ecSMark Brown if ((Ndiv < 6) || (Ndiv > 12)) 31291d0c3ecSMark Brown printk(KERN_WARNING 3138b83a193SMark Brown "WM8974 N value %u outwith recommended range!\n", 31491d0c3ecSMark Brown Ndiv); 31591d0c3ecSMark Brown 316c36b2fc7SMark Brown pll_div->n = Ndiv; 31791d0c3ecSMark Brown Nmod = target % source; 31891d0c3ecSMark Brown Kpart = FIXED_PLL_SIZE * (long long)Nmod; 31991d0c3ecSMark Brown 32091d0c3ecSMark Brown do_div(Kpart, source); 32191d0c3ecSMark Brown 32291d0c3ecSMark Brown K = Kpart & 0xFFFFFFFF; 32391d0c3ecSMark Brown 32491d0c3ecSMark Brown /* Check if we need to round */ 32591d0c3ecSMark Brown if ((K % 10) >= 5) 32691d0c3ecSMark Brown K += 5; 32791d0c3ecSMark Brown 32891d0c3ecSMark Brown /* Move down to proper range now rounding is done */ 32991d0c3ecSMark Brown K /= 10; 33091d0c3ecSMark Brown 331c36b2fc7SMark Brown pll_div->k = K; 33291d0c3ecSMark Brown } 3330a1bf553SMark Brown 33485488037SMark Brown static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, 33585488037SMark Brown int source, unsigned int freq_in, unsigned int freq_out) 3360a1bf553SMark Brown { 3370a1bf553SMark Brown struct snd_soc_codec *codec = codec_dai->codec; 338c36b2fc7SMark Brown struct pll_ pll_div; 3390a1bf553SMark Brown u16 reg; 3400a1bf553SMark Brown 3410a1bf553SMark Brown if (freq_in == 0 || freq_out == 0) { 34291d0c3ecSMark Brown /* Clock CODEC directly from MCLK */ 3431e97f50bSMark Brown reg = snd_soc_read(codec, WM8974_CLOCK); 3441e97f50bSMark Brown snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff); 34591d0c3ecSMark Brown 34691d0c3ecSMark Brown /* Turn off PLL */ 3471e97f50bSMark Brown reg = snd_soc_read(codec, WM8974_POWER1); 3481e97f50bSMark Brown snd_soc_write(codec, WM8974_POWER1, reg & 0x1df); 3490a1bf553SMark Brown return 0; 3500a1bf553SMark Brown } 3510a1bf553SMark Brown 352c36b2fc7SMark Brown pll_factors(&pll_div, freq_out, freq_in); 35391d0c3ecSMark Brown 3541e97f50bSMark Brown snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n); 3551e97f50bSMark Brown snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18); 3561e97f50bSMark Brown snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff); 3571e97f50bSMark Brown snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff); 3581e97f50bSMark Brown reg = snd_soc_read(codec, WM8974_POWER1); 3591e97f50bSMark Brown snd_soc_write(codec, WM8974_POWER1, reg | 0x020); 3601a55b3f6SMark Brown 36191d0c3ecSMark Brown /* Run CODEC from PLL instead of MCLK */ 3621e97f50bSMark Brown reg = snd_soc_read(codec, WM8974_CLOCK); 3631e97f50bSMark Brown snd_soc_write(codec, WM8974_CLOCK, reg | 0x100); 36491d0c3ecSMark Brown 36591d0c3ecSMark Brown return 0; 3660a1bf553SMark Brown } 3670a1bf553SMark Brown 3680a1bf553SMark Brown /* 3690a1bf553SMark Brown * Configure WM8974 clock dividers. 3700a1bf553SMark Brown */ 3710a1bf553SMark Brown static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai, 3720a1bf553SMark Brown int div_id, int div) 3730a1bf553SMark Brown { 3740a1bf553SMark Brown struct snd_soc_codec *codec = codec_dai->codec; 3750a1bf553SMark Brown u16 reg; 3760a1bf553SMark Brown 3770a1bf553SMark Brown switch (div_id) { 3780a1bf553SMark Brown case WM8974_OPCLKDIV: 3791e97f50bSMark Brown reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf; 3801e97f50bSMark Brown snd_soc_write(codec, WM8974_GPIO, reg | div); 3810a1bf553SMark Brown break; 3820a1bf553SMark Brown case WM8974_MCLKDIV: 3831e97f50bSMark Brown reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f; 3841e97f50bSMark Brown snd_soc_write(codec, WM8974_CLOCK, reg | div); 3850a1bf553SMark Brown break; 3860a1bf553SMark Brown case WM8974_BCLKDIV: 3871e97f50bSMark Brown reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3; 3881e97f50bSMark Brown snd_soc_write(codec, WM8974_CLOCK, reg | div); 3890a1bf553SMark Brown break; 3900a1bf553SMark Brown default: 3910a1bf553SMark Brown return -EINVAL; 3920a1bf553SMark Brown } 3930a1bf553SMark Brown 3940a1bf553SMark Brown return 0; 3950a1bf553SMark Brown } 3960a1bf553SMark Brown 3970a1bf553SMark Brown static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai, 3980a1bf553SMark Brown unsigned int fmt) 3990a1bf553SMark Brown { 4000a1bf553SMark Brown struct snd_soc_codec *codec = codec_dai->codec; 4010a1bf553SMark Brown u16 iface = 0; 4021e97f50bSMark Brown u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe; 4030a1bf553SMark Brown 4040a1bf553SMark Brown /* set master/slave audio interface */ 4050a1bf553SMark Brown switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 4060a1bf553SMark Brown case SND_SOC_DAIFMT_CBM_CFM: 4070a1bf553SMark Brown clk |= 0x0001; 4080a1bf553SMark Brown break; 4090a1bf553SMark Brown case SND_SOC_DAIFMT_CBS_CFS: 4100a1bf553SMark Brown break; 4110a1bf553SMark Brown default: 4120a1bf553SMark Brown return -EINVAL; 4130a1bf553SMark Brown } 4140a1bf553SMark Brown 4150a1bf553SMark Brown /* interface format */ 4160a1bf553SMark Brown switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 4170a1bf553SMark Brown case SND_SOC_DAIFMT_I2S: 4180a1bf553SMark Brown iface |= 0x0010; 4190a1bf553SMark Brown break; 4200a1bf553SMark Brown case SND_SOC_DAIFMT_RIGHT_J: 4210a1bf553SMark Brown break; 4220a1bf553SMark Brown case SND_SOC_DAIFMT_LEFT_J: 4230a1bf553SMark Brown iface |= 0x0008; 4240a1bf553SMark Brown break; 4250a1bf553SMark Brown case SND_SOC_DAIFMT_DSP_A: 4260a1bf553SMark Brown iface |= 0x00018; 4270a1bf553SMark Brown break; 4280a1bf553SMark Brown default: 4290a1bf553SMark Brown return -EINVAL; 4300a1bf553SMark Brown } 4310a1bf553SMark Brown 4320a1bf553SMark Brown /* clock inversion */ 4330a1bf553SMark Brown switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 4340a1bf553SMark Brown case SND_SOC_DAIFMT_NB_NF: 4350a1bf553SMark Brown break; 4360a1bf553SMark Brown case SND_SOC_DAIFMT_IB_IF: 4370a1bf553SMark Brown iface |= 0x0180; 4380a1bf553SMark Brown break; 4390a1bf553SMark Brown case SND_SOC_DAIFMT_IB_NF: 4400a1bf553SMark Brown iface |= 0x0100; 4410a1bf553SMark Brown break; 4420a1bf553SMark Brown case SND_SOC_DAIFMT_NB_IF: 4430a1bf553SMark Brown iface |= 0x0080; 4440a1bf553SMark Brown break; 4450a1bf553SMark Brown default: 4460a1bf553SMark Brown return -EINVAL; 4470a1bf553SMark Brown } 4480a1bf553SMark Brown 4491e97f50bSMark Brown snd_soc_write(codec, WM8974_IFACE, iface); 4501e97f50bSMark Brown snd_soc_write(codec, WM8974_CLOCK, clk); 4510a1bf553SMark Brown return 0; 4520a1bf553SMark Brown } 4530a1bf553SMark Brown 4540a1bf553SMark Brown static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream, 4550a1bf553SMark Brown struct snd_pcm_hw_params *params, 4560a1bf553SMark Brown struct snd_soc_dai *dai) 4570a1bf553SMark Brown { 4580a1bf553SMark Brown struct snd_soc_codec *codec = dai->codec; 4591e97f50bSMark Brown u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f; 4601e97f50bSMark Brown u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1; 4610a1bf553SMark Brown 4620a1bf553SMark Brown /* bit size */ 4630a1bf553SMark Brown switch (params_format(params)) { 4640a1bf553SMark Brown case SNDRV_PCM_FORMAT_S16_LE: 4650a1bf553SMark Brown break; 4660a1bf553SMark Brown case SNDRV_PCM_FORMAT_S20_3LE: 4670a1bf553SMark Brown iface |= 0x0020; 4680a1bf553SMark Brown break; 4690a1bf553SMark Brown case SNDRV_PCM_FORMAT_S24_LE: 4700a1bf553SMark Brown iface |= 0x0040; 4710a1bf553SMark Brown break; 4720a1bf553SMark Brown case SNDRV_PCM_FORMAT_S32_LE: 4730a1bf553SMark Brown iface |= 0x0060; 4740a1bf553SMark Brown break; 4750a1bf553SMark Brown } 4760a1bf553SMark Brown 4770a1bf553SMark Brown /* filter coefficient */ 4780a1bf553SMark Brown switch (params_rate(params)) { 479b3172f22SGuennadi Liakhovetski case 8000: 4800a1bf553SMark Brown adn |= 0x5 << 1; 4810a1bf553SMark Brown break; 482b3172f22SGuennadi Liakhovetski case 11025: 4830a1bf553SMark Brown adn |= 0x4 << 1; 4840a1bf553SMark Brown break; 485b3172f22SGuennadi Liakhovetski case 16000: 4860a1bf553SMark Brown adn |= 0x3 << 1; 4870a1bf553SMark Brown break; 488b3172f22SGuennadi Liakhovetski case 22050: 4890a1bf553SMark Brown adn |= 0x2 << 1; 4900a1bf553SMark Brown break; 491b3172f22SGuennadi Liakhovetski case 32000: 4920a1bf553SMark Brown adn |= 0x1 << 1; 4930a1bf553SMark Brown break; 494b3172f22SGuennadi Liakhovetski case 44100: 495b3172f22SGuennadi Liakhovetski case 48000: 4960a1bf553SMark Brown break; 4970a1bf553SMark Brown } 4980a1bf553SMark Brown 4991e97f50bSMark Brown snd_soc_write(codec, WM8974_IFACE, iface); 5001e97f50bSMark Brown snd_soc_write(codec, WM8974_ADD, adn); 5010a1bf553SMark Brown return 0; 5020a1bf553SMark Brown } 5030a1bf553SMark Brown 5040a1bf553SMark Brown static int wm8974_mute(struct snd_soc_dai *dai, int mute) 5050a1bf553SMark Brown { 5060a1bf553SMark Brown struct snd_soc_codec *codec = dai->codec; 5071e97f50bSMark Brown u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf; 5080a1bf553SMark Brown 5090a1bf553SMark Brown if (mute) 5101e97f50bSMark Brown snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40); 5110a1bf553SMark Brown else 5121e97f50bSMark Brown snd_soc_write(codec, WM8974_DAC, mute_reg); 5130a1bf553SMark Brown return 0; 5140a1bf553SMark Brown } 5150a1bf553SMark Brown 5160a1bf553SMark Brown /* liam need to make this lower power with dapm */ 5170a1bf553SMark Brown static int wm8974_set_bias_level(struct snd_soc_codec *codec, 5180a1bf553SMark Brown enum snd_soc_bias_level level) 5190a1bf553SMark Brown { 5201e97f50bSMark Brown u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3; 521df1ef7a3SMark Brown 5220a1bf553SMark Brown switch (level) { 5230a1bf553SMark Brown case SND_SOC_BIAS_ON: 5240a1bf553SMark Brown case SND_SOC_BIAS_PREPARE: 525df1ef7a3SMark Brown power1 |= 0x1; /* VMID 50k */ 5261e97f50bSMark Brown snd_soc_write(codec, WM8974_POWER1, power1); 5270a1bf553SMark Brown break; 528df1ef7a3SMark Brown 5290a1bf553SMark Brown case SND_SOC_BIAS_STANDBY: 530df1ef7a3SMark Brown power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN; 531df1ef7a3SMark Brown 532ce6120ccSLiam Girdwood if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { 5330bad3d84SAxel Lin snd_soc_cache_sync(codec); 5340bad3d84SAxel Lin 535df1ef7a3SMark Brown /* Initial cap charge at VMID 5k */ 5361e97f50bSMark Brown snd_soc_write(codec, WM8974_POWER1, power1 | 0x3); 537df1ef7a3SMark Brown mdelay(100); 538df1ef7a3SMark Brown } 539df1ef7a3SMark Brown 540df1ef7a3SMark Brown power1 |= 0x2; /* VMID 500k */ 5411e97f50bSMark Brown snd_soc_write(codec, WM8974_POWER1, power1); 5420a1bf553SMark Brown break; 543df1ef7a3SMark Brown 5440a1bf553SMark Brown case SND_SOC_BIAS_OFF: 5451e97f50bSMark Brown snd_soc_write(codec, WM8974_POWER1, 0); 5461e97f50bSMark Brown snd_soc_write(codec, WM8974_POWER2, 0); 5471e97f50bSMark Brown snd_soc_write(codec, WM8974_POWER3, 0); 5480a1bf553SMark Brown break; 5490a1bf553SMark Brown } 550df1ef7a3SMark Brown 551ce6120ccSLiam Girdwood codec->dapm.bias_level = level; 5520a1bf553SMark Brown return 0; 5530a1bf553SMark Brown } 5540a1bf553SMark Brown 5551a55b3f6SMark Brown #define WM8974_RATES (SNDRV_PCM_RATE_8000_48000) 5560a1bf553SMark Brown 5570a1bf553SMark Brown #define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 5580a1bf553SMark Brown SNDRV_PCM_FMTBIT_S24_LE) 5590a1bf553SMark Brown 560*85e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm8974_ops = { 5610a1bf553SMark Brown .hw_params = wm8974_pcm_hw_params, 5620a1bf553SMark Brown .digital_mute = wm8974_mute, 5630a1bf553SMark Brown .set_fmt = wm8974_set_dai_fmt, 5640a1bf553SMark Brown .set_clkdiv = wm8974_set_dai_clkdiv, 5650a1bf553SMark Brown .set_pll = wm8974_set_dai_pll, 5660a1bf553SMark Brown }; 5670a1bf553SMark Brown 568f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8974_dai = { 569f0fba2adSLiam Girdwood .name = "wm8974-hifi", 5700a1bf553SMark Brown .playback = { 5710a1bf553SMark Brown .stream_name = "Playback", 5720a1bf553SMark Brown .channels_min = 1, 57333d81af4SMark Brown .channels_max = 2, /* Only 1 channel of data */ 5740a1bf553SMark Brown .rates = WM8974_RATES, 5750a1bf553SMark Brown .formats = WM8974_FORMATS,}, 5760a1bf553SMark Brown .capture = { 5770a1bf553SMark Brown .stream_name = "Capture", 5780a1bf553SMark Brown .channels_min = 1, 57933d81af4SMark Brown .channels_max = 2, /* Only 1 channel of data */ 5800a1bf553SMark Brown .rates = WM8974_RATES, 5810a1bf553SMark Brown .formats = WM8974_FORMATS,}, 5820a1bf553SMark Brown .ops = &wm8974_ops, 583cb11d39eSMark Brown .symmetric_rates = 1, 5840a1bf553SMark Brown }; 5850a1bf553SMark Brown 586f0fba2adSLiam Girdwood static int wm8974_suspend(struct snd_soc_codec *codec, pm_message_t state) 5870a1bf553SMark Brown { 5880a1bf553SMark Brown wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF); 5890a1bf553SMark Brown return 0; 5900a1bf553SMark Brown } 5910a1bf553SMark Brown 592f0fba2adSLiam Girdwood static int wm8974_resume(struct snd_soc_codec *codec) 5930a1bf553SMark Brown { 5940a1bf553SMark Brown wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 5950a1bf553SMark Brown return 0; 5960a1bf553SMark Brown } 5970a1bf553SMark Brown 598f0fba2adSLiam Girdwood static int wm8974_probe(struct snd_soc_codec *codec) 5990a1bf553SMark Brown { 6000a1bf553SMark Brown int ret = 0; 6010a1bf553SMark Brown 602f0fba2adSLiam Girdwood ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C); 6030a1bf553SMark Brown if (ret < 0) { 604f0fba2adSLiam Girdwood dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); 605f0fba2adSLiam Girdwood return ret; 6060a1bf553SMark Brown } 6070a1bf553SMark Brown 608f0fba2adSLiam Girdwood ret = wm8974_reset(codec); 609f0fba2adSLiam Girdwood if (ret < 0) { 610f0fba2adSLiam Girdwood dev_err(codec->dev, "Failed to issue reset\n"); 611f0fba2adSLiam Girdwood return ret; 612f0fba2adSLiam Girdwood } 613f0fba2adSLiam Girdwood 614f0fba2adSLiam Girdwood wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 6154fcbbb67SMark Brown snd_soc_add_controls(codec, wm8974_snd_controls, 6164fcbbb67SMark Brown ARRAY_SIZE(wm8974_snd_controls)); 6170a1bf553SMark Brown wm8974_add_widgets(codec); 6184fcbbb67SMark Brown 6190a1bf553SMark Brown return ret; 6200a1bf553SMark Brown } 6210a1bf553SMark Brown 6220a1bf553SMark Brown /* power down chip */ 623f0fba2adSLiam Girdwood static int wm8974_remove(struct snd_soc_codec *codec) 6240a1bf553SMark Brown { 625f0fba2adSLiam Girdwood wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF); 6260a1bf553SMark Brown return 0; 6270a1bf553SMark Brown } 6280a1bf553SMark Brown 629f0fba2adSLiam Girdwood static struct snd_soc_codec_driver soc_codec_dev_wm8974 = { 6300a1bf553SMark Brown .probe = wm8974_probe, 6310a1bf553SMark Brown .remove = wm8974_remove, 6320a1bf553SMark Brown .suspend = wm8974_suspend, 6330a1bf553SMark Brown .resume = wm8974_resume, 634f0fba2adSLiam Girdwood .set_bias_level = wm8974_set_bias_level, 635f0fba2adSLiam Girdwood .reg_cache_size = ARRAY_SIZE(wm8974_reg), 636f0fba2adSLiam Girdwood .reg_word_size = sizeof(u16), 637f0fba2adSLiam Girdwood .reg_cache_default = wm8974_reg, 6380a1bf553SMark Brown }; 6390a1bf553SMark Brown 640f0fba2adSLiam Girdwood #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 6414fcbbb67SMark Brown static __devinit int wm8974_i2c_probe(struct i2c_client *i2c, 6424fcbbb67SMark Brown const struct i2c_device_id *id) 6434fcbbb67SMark Brown { 6444fcbbb67SMark Brown struct wm8974_priv *wm8974; 645f0fba2adSLiam Girdwood int ret; 6464fcbbb67SMark Brown 6474fcbbb67SMark Brown wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL); 6484fcbbb67SMark Brown if (wm8974 == NULL) 6494fcbbb67SMark Brown return -ENOMEM; 6504fcbbb67SMark Brown 6514fcbbb67SMark Brown i2c_set_clientdata(i2c, wm8974); 6524fcbbb67SMark Brown 653f0fba2adSLiam Girdwood ret = snd_soc_register_codec(&i2c->dev, 654f0fba2adSLiam Girdwood &soc_codec_dev_wm8974, &wm8974_dai, 1); 655f0fba2adSLiam Girdwood if (ret < 0) 656f0fba2adSLiam Girdwood kfree(wm8974); 657f0fba2adSLiam Girdwood return ret; 6584fcbbb67SMark Brown } 6594fcbbb67SMark Brown 6604fcbbb67SMark Brown static __devexit int wm8974_i2c_remove(struct i2c_client *client) 6614fcbbb67SMark Brown { 662f0fba2adSLiam Girdwood snd_soc_unregister_codec(&client->dev); 663f0fba2adSLiam Girdwood kfree(i2c_get_clientdata(client)); 6644fcbbb67SMark Brown return 0; 6654fcbbb67SMark Brown } 6664fcbbb67SMark Brown 6674fcbbb67SMark Brown static const struct i2c_device_id wm8974_i2c_id[] = { 6684fcbbb67SMark Brown { "wm8974", 0 }, 6694fcbbb67SMark Brown { } 6704fcbbb67SMark Brown }; 6714fcbbb67SMark Brown MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id); 6724fcbbb67SMark Brown 6734fcbbb67SMark Brown static struct i2c_driver wm8974_i2c_driver = { 6744fcbbb67SMark Brown .driver = { 675f0fba2adSLiam Girdwood .name = "wm8974-codec", 6764fcbbb67SMark Brown .owner = THIS_MODULE, 6774fcbbb67SMark Brown }, 6784fcbbb67SMark Brown .probe = wm8974_i2c_probe, 6794fcbbb67SMark Brown .remove = __devexit_p(wm8974_i2c_remove), 6804fcbbb67SMark Brown .id_table = wm8974_i2c_id, 6814fcbbb67SMark Brown }; 682f0fba2adSLiam Girdwood #endif 6834fcbbb67SMark Brown 6840a1bf553SMark Brown static int __init wm8974_modinit(void) 6850a1bf553SMark Brown { 686f0fba2adSLiam Girdwood int ret = 0; 687f0fba2adSLiam Girdwood #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 688f0fba2adSLiam Girdwood ret = i2c_add_driver(&wm8974_i2c_driver); 689f0fba2adSLiam Girdwood if (ret != 0) { 690f0fba2adSLiam Girdwood printk(KERN_ERR "Failed to register wm8974 I2C driver: %d\n", 691f0fba2adSLiam Girdwood ret); 692f0fba2adSLiam Girdwood } 693f0fba2adSLiam Girdwood #endif 694f0fba2adSLiam Girdwood return ret; 6950a1bf553SMark Brown } 6960a1bf553SMark Brown module_init(wm8974_modinit); 6970a1bf553SMark Brown 6980a1bf553SMark Brown static void __exit wm8974_exit(void) 6990a1bf553SMark Brown { 700f0fba2adSLiam Girdwood #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 7014fcbbb67SMark Brown i2c_del_driver(&wm8974_i2c_driver); 702f0fba2adSLiam Girdwood #endif 7030a1bf553SMark Brown } 7040a1bf553SMark Brown module_exit(wm8974_exit); 7050a1bf553SMark Brown 7060a1bf553SMark Brown MODULE_DESCRIPTION("ASoC WM8974 driver"); 7070a1bf553SMark Brown MODULE_AUTHOR("Liam Girdwood"); 7080a1bf553SMark Brown MODULE_LICENSE("GPL"); 709