10a1bf553SMark Brown /* 20a1bf553SMark Brown * wm8974.c -- WM8974 ALSA Soc Audio driver 30a1bf553SMark Brown * 48b83a193SMark Brown * Copyright 2006-2009 Wolfson Microelectronics PLC. 50a1bf553SMark Brown * 64fcbbb67SMark Brown * Author: Liam Girdwood <linux@wolfsonmicro.com> 70a1bf553SMark Brown * 80a1bf553SMark Brown * This program is free software; you can redistribute it and/or modify 90a1bf553SMark Brown * it under the terms of the GNU General Public License version 2 as 100a1bf553SMark Brown * published by the Free Software Foundation. 110a1bf553SMark Brown */ 120a1bf553SMark Brown 130a1bf553SMark Brown #include <linux/module.h> 140a1bf553SMark Brown #include <linux/moduleparam.h> 150a1bf553SMark Brown #include <linux/kernel.h> 160a1bf553SMark Brown #include <linux/init.h> 170a1bf553SMark Brown #include <linux/delay.h> 180a1bf553SMark Brown #include <linux/pm.h> 190a1bf553SMark Brown #include <linux/i2c.h> 200a1bf553SMark Brown #include <linux/platform_device.h> 21*5a0e3ad6STejun 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/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 5148c03ce7SGuennadi Liakhovetski #define WM8974_POWER1_BUFIOEN 0x04 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 601e97f50bSMark Brown #define wm8974_reset(c) snd_soc_write(c, WM8974_RESET, 0) 610a1bf553SMark Brown 620a1bf553SMark Brown static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" }; 630a1bf553SMark Brown static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" }; 640a1bf553SMark Brown static const char *wm8974_eqmode[] = {"Capture", "Playback" }; 650a1bf553SMark Brown static const char *wm8974_bw[] = {"Narrow", "Wide" }; 660a1bf553SMark Brown static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" }; 670a1bf553SMark Brown static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" }; 680a1bf553SMark Brown static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" }; 690a1bf553SMark Brown static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" }; 700a1bf553SMark Brown static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" }; 710a1bf553SMark Brown static const char *wm8974_alc[] = {"ALC", "Limiter" }; 720a1bf553SMark Brown 730a1bf553SMark Brown static const struct soc_enum wm8974_enum[] = { 740a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */ 750a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */ 760a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp), 770a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode), 780a1bf553SMark Brown 790a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1), 800a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw), 810a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2), 820a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw), 830a1bf553SMark Brown 840a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3), 850a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw), 860a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4), 870a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw), 880a1bf553SMark Brown 890a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5), 900a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc), 910a1bf553SMark Brown }; 920a1bf553SMark Brown 938a123ee2SMark Brown static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" }; 948a123ee2SMark Brown 958a123ee2SMark Brown static const struct soc_enum wm8974_auxmode = 968a123ee2SMark Brown SOC_ENUM_SINGLE(WM8974_INPUT, 3, 2, wm8974_auxmode_text); 978a123ee2SMark Brown 98a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1); 99a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); 100a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0); 101a5f8d2f1SMark Brown static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0); 102a5f8d2f1SMark Brown 1030a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_snd_controls[] = { 1040a1bf553SMark Brown 1050a1bf553SMark Brown SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0), 1060a1bf553SMark Brown 1070a1bf553SMark Brown SOC_ENUM("DAC Companding", wm8974_enum[1]), 1080a1bf553SMark Brown SOC_ENUM("ADC Companding", wm8974_enum[0]), 1090a1bf553SMark Brown 1100a1bf553SMark Brown SOC_ENUM("Playback De-emphasis", wm8974_enum[2]), 1110a1bf553SMark Brown SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0), 1120a1bf553SMark Brown 113a5f8d2f1SMark Brown SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv), 1140a1bf553SMark Brown 1150a1bf553SMark Brown SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0), 1160a1bf553SMark Brown SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0), 11725cbf465Sjavier Martin SOC_SINGLE("ADC Inversion Switch", WM8974_ADC, 0, 1, 0), 1180a1bf553SMark Brown 119a5f8d2f1SMark Brown SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL, 0, 255, 0, digital_tlv), 1200a1bf553SMark Brown 1210a1bf553SMark Brown SOC_ENUM("Equaliser Function", wm8974_enum[3]), 1220a1bf553SMark Brown SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]), 123a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1, 0, 24, 1, eq_tlv), 1240a1bf553SMark Brown 1250a1bf553SMark Brown SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]), 1260a1bf553SMark Brown SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]), 127a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2, 0, 24, 1, eq_tlv), 1280a1bf553SMark Brown 1290a1bf553SMark Brown SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]), 1300a1bf553SMark Brown SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]), 131a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3, 0, 24, 1, eq_tlv), 1320a1bf553SMark Brown 1330a1bf553SMark Brown SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]), 1340a1bf553SMark Brown SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]), 135a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4, 0, 24, 1, eq_tlv), 1360a1bf553SMark Brown 1370a1bf553SMark Brown SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]), 1380a1bf553SMark Brown SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]), 139a5f8d2f1SMark Brown SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5, 0, 24, 1, eq_tlv), 1400a1bf553SMark Brown 1410a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0), 1420a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0), 1430a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0), 1440a1bf553SMark Brown 1450a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0), 1460a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0), 1470a1bf553SMark Brown 1480a1bf553SMark Brown SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0), 1490a1bf553SMark Brown SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0), 1500a1bf553SMark Brown SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0), 1510a1bf553SMark Brown 1520a1bf553SMark Brown SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0), 1530a1bf553SMark Brown SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0), 1540a1bf553SMark Brown SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0), 1550a1bf553SMark Brown 1560a1bf553SMark Brown SOC_ENUM("ALC Capture Mode", wm8974_enum[13]), 1570a1bf553SMark Brown SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0), 1580a1bf553SMark Brown SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0), 1590a1bf553SMark Brown 1600a1bf553SMark Brown SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0), 1610a1bf553SMark Brown SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0), 1620a1bf553SMark Brown 1630a1bf553SMark Brown SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0), 164a5f8d2f1SMark Brown SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0, inpga_tlv), 1650a1bf553SMark Brown 1660a1bf553SMark Brown SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0), 1670a1bf553SMark Brown SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1), 1688a123ee2SMark Brown SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0, spk_tlv), 1698a123ee2SMark Brown 1708a123ee2SMark Brown SOC_ENUM("Aux Mode", wm8974_auxmode), 1710a1bf553SMark Brown 1720a1bf553SMark Brown SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0), 1738a123ee2SMark Brown SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1), 174b2c3e923SGuennadi Liakhovetski 175b2c3e923SGuennadi Liakhovetski /* DAC / ADC oversampling */ 176b2c3e923SGuennadi Liakhovetski SOC_SINGLE("DAC 128x Oversampling Switch", WM8974_DAC, 8, 1, 0), 177b2c3e923SGuennadi Liakhovetski SOC_SINGLE("ADC 128x Oversampling Switch", WM8974_ADC, 8, 1, 0), 1780a1bf553SMark Brown }; 1790a1bf553SMark Brown 1800a1bf553SMark Brown /* Speaker Output Mixer */ 1810a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = { 1820a1bf553SMark Brown SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0), 1830a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0), 1840a1bf553SMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1), 1850a1bf553SMark Brown }; 1860a1bf553SMark Brown 1870a1bf553SMark Brown /* Mono Output Mixer */ 1880a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = { 1890a1bf553SMark Brown SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0), 1900a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0), 1918a123ee2SMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0), 1928a123ee2SMark Brown }; 1938a123ee2SMark Brown 1948a123ee2SMark Brown /* Boost mixer */ 1958a123ee2SMark Brown static const struct snd_kcontrol_new wm8974_boost_mixer[] = { 1968a123ee2SMark Brown SOC_DAPM_SINGLE("Aux Switch", WM8974_INPPGA, 6, 1, 0), 1978a123ee2SMark Brown }; 1988a123ee2SMark Brown 1998a123ee2SMark Brown /* Input PGA */ 2008a123ee2SMark Brown static const struct snd_kcontrol_new wm8974_inpga[] = { 2018a123ee2SMark Brown SOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0), 2028a123ee2SMark Brown SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0), 2038a123ee2SMark Brown SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0), 2040a1bf553SMark Brown }; 2050a1bf553SMark Brown 2060a1bf553SMark Brown /* AUX Input boost vol */ 2070a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_aux_boost_controls = 2080a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0); 2090a1bf553SMark Brown 2100a1bf553SMark Brown /* Mic Input boost vol */ 2110a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_mic_boost_controls = 2120a1bf553SMark Brown SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0); 2130a1bf553SMark Brown 2140a1bf553SMark Brown static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = { 2150a1bf553SMark Brown SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0, 2160a1bf553SMark Brown &wm8974_speaker_mixer_controls[0], 2170a1bf553SMark Brown ARRAY_SIZE(wm8974_speaker_mixer_controls)), 2180a1bf553SMark Brown SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0, 2190a1bf553SMark Brown &wm8974_mono_mixer_controls[0], 2200a1bf553SMark Brown ARRAY_SIZE(wm8974_mono_mixer_controls)), 2210a1bf553SMark Brown SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0), 2228a123ee2SMark Brown SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0), 2230a1bf553SMark Brown SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0), 2240a1bf553SMark Brown SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0), 2250a1bf553SMark Brown SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0), 2260a1bf553SMark Brown SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0), 2270a1bf553SMark Brown 2288a123ee2SMark Brown SND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga, 2298a123ee2SMark Brown ARRAY_SIZE(wm8974_inpga)), 2308a123ee2SMark Brown SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0, 2318a123ee2SMark Brown wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)), 2320a1bf553SMark Brown 2330a1bf553SMark Brown SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0), 2340a1bf553SMark Brown 2350a1bf553SMark Brown SND_SOC_DAPM_INPUT("MICN"), 2360a1bf553SMark Brown SND_SOC_DAPM_INPUT("MICP"), 2370a1bf553SMark Brown SND_SOC_DAPM_INPUT("AUX"), 2380a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("MONOOUT"), 2390a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("SPKOUTP"), 2400a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("SPKOUTN"), 2410a1bf553SMark Brown }; 2420a1bf553SMark Brown 2430a1bf553SMark Brown static const struct snd_soc_dapm_route audio_map[] = { 2440a1bf553SMark Brown /* Mono output mixer */ 2450a1bf553SMark Brown {"Mono Mixer", "PCM Playback Switch", "DAC"}, 2460a1bf553SMark Brown {"Mono Mixer", "Aux Playback Switch", "Aux Input"}, 2470a1bf553SMark Brown {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"}, 2480a1bf553SMark Brown 2490a1bf553SMark Brown /* Speaker output mixer */ 2500a1bf553SMark Brown {"Speaker Mixer", "PCM Playback Switch", "DAC"}, 2510a1bf553SMark Brown {"Speaker Mixer", "Aux Playback Switch", "Aux Input"}, 2520a1bf553SMark Brown {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"}, 2530a1bf553SMark Brown 2540a1bf553SMark Brown /* Outputs */ 2550a1bf553SMark Brown {"Mono Out", NULL, "Mono Mixer"}, 2560a1bf553SMark Brown {"MONOOUT", NULL, "Mono Out"}, 2570a1bf553SMark Brown {"SpkN Out", NULL, "Speaker Mixer"}, 2580a1bf553SMark Brown {"SpkP Out", NULL, "Speaker Mixer"}, 2590a1bf553SMark Brown {"SPKOUTN", NULL, "SpkN Out"}, 2600a1bf553SMark Brown {"SPKOUTP", NULL, "SpkP Out"}, 2610a1bf553SMark Brown 2620a1bf553SMark Brown /* Boost Mixer */ 2638a123ee2SMark Brown {"ADC", NULL, "Boost Mixer"}, 2648a123ee2SMark Brown {"Boost Mixer", "Aux Switch", "Aux Input"}, 2658a123ee2SMark Brown {"Boost Mixer", NULL, "Input PGA"}, 2668a123ee2SMark Brown {"Boost Mixer", NULL, "MICP"}, 2678a123ee2SMark Brown 2688a123ee2SMark Brown /* Input PGA */ 2698a123ee2SMark Brown {"Input PGA", "Aux Switch", "Aux Input"}, 2708a123ee2SMark Brown {"Input PGA", "MicN Switch", "MICN"}, 2718a123ee2SMark Brown {"Input PGA", "MicP Switch", "MICP"}, 2720a1bf553SMark Brown 2730a1bf553SMark Brown /* Inputs */ 2748a123ee2SMark Brown {"Aux Input", NULL, "AUX"}, 2750a1bf553SMark Brown }; 2760a1bf553SMark Brown 2770a1bf553SMark Brown static int wm8974_add_widgets(struct snd_soc_codec *codec) 2780a1bf553SMark Brown { 2790a1bf553SMark Brown snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets, 2800a1bf553SMark Brown ARRAY_SIZE(wm8974_dapm_widgets)); 2810a1bf553SMark Brown 2820a1bf553SMark Brown snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); 2830a1bf553SMark Brown 2840a1bf553SMark Brown return 0; 2850a1bf553SMark Brown } 2860a1bf553SMark Brown 2870a1bf553SMark Brown struct pll_ { 288c36b2fc7SMark Brown unsigned int pre_div:1; 2890a1bf553SMark Brown unsigned int n:4; 2900a1bf553SMark Brown unsigned int k; 2910a1bf553SMark Brown }; 2920a1bf553SMark Brown 29391d0c3ecSMark Brown /* The size in bits of the pll divide multiplied by 10 29491d0c3ecSMark Brown * to allow rounding later */ 29591d0c3ecSMark Brown #define FIXED_PLL_SIZE ((1 << 24) * 10) 29691d0c3ecSMark Brown 297c36b2fc7SMark Brown static void pll_factors(struct pll_ *pll_div, 298c36b2fc7SMark Brown unsigned int target, unsigned int source) 29991d0c3ecSMark Brown { 30091d0c3ecSMark Brown unsigned long long Kpart; 30191d0c3ecSMark Brown unsigned int K, Ndiv, Nmod; 30291d0c3ecSMark Brown 303c36b2fc7SMark Brown /* There is a fixed divide by 4 in the output path */ 304c36b2fc7SMark Brown target *= 4; 305c36b2fc7SMark Brown 30691d0c3ecSMark Brown Ndiv = target / source; 30791d0c3ecSMark Brown if (Ndiv < 6) { 308c36b2fc7SMark Brown source /= 2; 309c36b2fc7SMark Brown pll_div->pre_div = 1; 31091d0c3ecSMark Brown Ndiv = target / source; 31191d0c3ecSMark Brown } else 312c36b2fc7SMark Brown pll_div->pre_div = 0; 31391d0c3ecSMark Brown 31491d0c3ecSMark Brown if ((Ndiv < 6) || (Ndiv > 12)) 31591d0c3ecSMark Brown printk(KERN_WARNING 3168b83a193SMark Brown "WM8974 N value %u outwith recommended range!\n", 31791d0c3ecSMark Brown Ndiv); 31891d0c3ecSMark Brown 319c36b2fc7SMark Brown pll_div->n = Ndiv; 32091d0c3ecSMark Brown Nmod = target % source; 32191d0c3ecSMark Brown Kpart = FIXED_PLL_SIZE * (long long)Nmod; 32291d0c3ecSMark Brown 32391d0c3ecSMark Brown do_div(Kpart, source); 32491d0c3ecSMark Brown 32591d0c3ecSMark Brown K = Kpart & 0xFFFFFFFF; 32691d0c3ecSMark Brown 32791d0c3ecSMark Brown /* Check if we need to round */ 32891d0c3ecSMark Brown if ((K % 10) >= 5) 32991d0c3ecSMark Brown K += 5; 33091d0c3ecSMark Brown 33191d0c3ecSMark Brown /* Move down to proper range now rounding is done */ 33291d0c3ecSMark Brown K /= 10; 33391d0c3ecSMark Brown 334c36b2fc7SMark Brown pll_div->k = K; 33591d0c3ecSMark Brown } 3360a1bf553SMark Brown 33785488037SMark Brown static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, 33885488037SMark Brown int source, unsigned int freq_in, unsigned int freq_out) 3390a1bf553SMark Brown { 3400a1bf553SMark Brown struct snd_soc_codec *codec = codec_dai->codec; 341c36b2fc7SMark Brown struct pll_ pll_div; 3420a1bf553SMark Brown u16 reg; 3430a1bf553SMark Brown 3440a1bf553SMark Brown if (freq_in == 0 || freq_out == 0) { 34591d0c3ecSMark Brown /* Clock CODEC directly from MCLK */ 3461e97f50bSMark Brown reg = snd_soc_read(codec, WM8974_CLOCK); 3471e97f50bSMark Brown snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff); 34891d0c3ecSMark Brown 34991d0c3ecSMark Brown /* Turn off PLL */ 3501e97f50bSMark Brown reg = snd_soc_read(codec, WM8974_POWER1); 3511e97f50bSMark Brown snd_soc_write(codec, WM8974_POWER1, reg & 0x1df); 3520a1bf553SMark Brown return 0; 3530a1bf553SMark Brown } 3540a1bf553SMark Brown 355c36b2fc7SMark Brown pll_factors(&pll_div, freq_out, freq_in); 35691d0c3ecSMark Brown 3571e97f50bSMark Brown snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n); 3581e97f50bSMark Brown snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18); 3591e97f50bSMark Brown snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff); 3601e97f50bSMark Brown snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff); 3611e97f50bSMark Brown reg = snd_soc_read(codec, WM8974_POWER1); 3621e97f50bSMark Brown snd_soc_write(codec, WM8974_POWER1, reg | 0x020); 3631a55b3f6SMark Brown 36491d0c3ecSMark Brown /* Run CODEC from PLL instead of MCLK */ 3651e97f50bSMark Brown reg = snd_soc_read(codec, WM8974_CLOCK); 3661e97f50bSMark Brown snd_soc_write(codec, WM8974_CLOCK, reg | 0x100); 36791d0c3ecSMark Brown 36891d0c3ecSMark Brown return 0; 3690a1bf553SMark Brown } 3700a1bf553SMark Brown 3710a1bf553SMark Brown /* 3720a1bf553SMark Brown * Configure WM8974 clock dividers. 3730a1bf553SMark Brown */ 3740a1bf553SMark Brown static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai, 3750a1bf553SMark Brown int div_id, int div) 3760a1bf553SMark Brown { 3770a1bf553SMark Brown struct snd_soc_codec *codec = codec_dai->codec; 3780a1bf553SMark Brown u16 reg; 3790a1bf553SMark Brown 3800a1bf553SMark Brown switch (div_id) { 3810a1bf553SMark Brown case WM8974_OPCLKDIV: 3821e97f50bSMark Brown reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf; 3831e97f50bSMark Brown snd_soc_write(codec, WM8974_GPIO, reg | div); 3840a1bf553SMark Brown break; 3850a1bf553SMark Brown case WM8974_MCLKDIV: 3861e97f50bSMark Brown reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f; 3871e97f50bSMark Brown snd_soc_write(codec, WM8974_CLOCK, reg | div); 3880a1bf553SMark Brown break; 3890a1bf553SMark Brown case WM8974_BCLKDIV: 3901e97f50bSMark Brown reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3; 3911e97f50bSMark Brown snd_soc_write(codec, WM8974_CLOCK, reg | div); 3920a1bf553SMark Brown break; 3930a1bf553SMark Brown default: 3940a1bf553SMark Brown return -EINVAL; 3950a1bf553SMark Brown } 3960a1bf553SMark Brown 3970a1bf553SMark Brown return 0; 3980a1bf553SMark Brown } 3990a1bf553SMark Brown 4000a1bf553SMark Brown static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai, 4010a1bf553SMark Brown unsigned int fmt) 4020a1bf553SMark Brown { 4030a1bf553SMark Brown struct snd_soc_codec *codec = codec_dai->codec; 4040a1bf553SMark Brown u16 iface = 0; 4051e97f50bSMark Brown u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe; 4060a1bf553SMark Brown 4070a1bf553SMark Brown /* set master/slave audio interface */ 4080a1bf553SMark Brown switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 4090a1bf553SMark Brown case SND_SOC_DAIFMT_CBM_CFM: 4100a1bf553SMark Brown clk |= 0x0001; 4110a1bf553SMark Brown break; 4120a1bf553SMark Brown case SND_SOC_DAIFMT_CBS_CFS: 4130a1bf553SMark Brown break; 4140a1bf553SMark Brown default: 4150a1bf553SMark Brown return -EINVAL; 4160a1bf553SMark Brown } 4170a1bf553SMark Brown 4180a1bf553SMark Brown /* interface format */ 4190a1bf553SMark Brown switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 4200a1bf553SMark Brown case SND_SOC_DAIFMT_I2S: 4210a1bf553SMark Brown iface |= 0x0010; 4220a1bf553SMark Brown break; 4230a1bf553SMark Brown case SND_SOC_DAIFMT_RIGHT_J: 4240a1bf553SMark Brown break; 4250a1bf553SMark Brown case SND_SOC_DAIFMT_LEFT_J: 4260a1bf553SMark Brown iface |= 0x0008; 4270a1bf553SMark Brown break; 4280a1bf553SMark Brown case SND_SOC_DAIFMT_DSP_A: 4290a1bf553SMark Brown iface |= 0x00018; 4300a1bf553SMark Brown break; 4310a1bf553SMark Brown default: 4320a1bf553SMark Brown return -EINVAL; 4330a1bf553SMark Brown } 4340a1bf553SMark Brown 4350a1bf553SMark Brown /* clock inversion */ 4360a1bf553SMark Brown switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 4370a1bf553SMark Brown case SND_SOC_DAIFMT_NB_NF: 4380a1bf553SMark Brown break; 4390a1bf553SMark Brown case SND_SOC_DAIFMT_IB_IF: 4400a1bf553SMark Brown iface |= 0x0180; 4410a1bf553SMark Brown break; 4420a1bf553SMark Brown case SND_SOC_DAIFMT_IB_NF: 4430a1bf553SMark Brown iface |= 0x0100; 4440a1bf553SMark Brown break; 4450a1bf553SMark Brown case SND_SOC_DAIFMT_NB_IF: 4460a1bf553SMark Brown iface |= 0x0080; 4470a1bf553SMark Brown break; 4480a1bf553SMark Brown default: 4490a1bf553SMark Brown return -EINVAL; 4500a1bf553SMark Brown } 4510a1bf553SMark Brown 4521e97f50bSMark Brown snd_soc_write(codec, WM8974_IFACE, iface); 4531e97f50bSMark Brown snd_soc_write(codec, WM8974_CLOCK, clk); 4540a1bf553SMark Brown return 0; 4550a1bf553SMark Brown } 4560a1bf553SMark Brown 4570a1bf553SMark Brown static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream, 4580a1bf553SMark Brown struct snd_pcm_hw_params *params, 4590a1bf553SMark Brown struct snd_soc_dai *dai) 4600a1bf553SMark Brown { 4610a1bf553SMark Brown struct snd_soc_codec *codec = dai->codec; 4621e97f50bSMark Brown u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f; 4631e97f50bSMark Brown u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1; 4640a1bf553SMark Brown 4650a1bf553SMark Brown /* bit size */ 4660a1bf553SMark Brown switch (params_format(params)) { 4670a1bf553SMark Brown case SNDRV_PCM_FORMAT_S16_LE: 4680a1bf553SMark Brown break; 4690a1bf553SMark Brown case SNDRV_PCM_FORMAT_S20_3LE: 4700a1bf553SMark Brown iface |= 0x0020; 4710a1bf553SMark Brown break; 4720a1bf553SMark Brown case SNDRV_PCM_FORMAT_S24_LE: 4730a1bf553SMark Brown iface |= 0x0040; 4740a1bf553SMark Brown break; 4750a1bf553SMark Brown case SNDRV_PCM_FORMAT_S32_LE: 4760a1bf553SMark Brown iface |= 0x0060; 4770a1bf553SMark Brown break; 4780a1bf553SMark Brown } 4790a1bf553SMark Brown 4800a1bf553SMark Brown /* filter coefficient */ 4810a1bf553SMark Brown switch (params_rate(params)) { 482b3172f22SGuennadi Liakhovetski case 8000: 4830a1bf553SMark Brown adn |= 0x5 << 1; 4840a1bf553SMark Brown break; 485b3172f22SGuennadi Liakhovetski case 11025: 4860a1bf553SMark Brown adn |= 0x4 << 1; 4870a1bf553SMark Brown break; 488b3172f22SGuennadi Liakhovetski case 16000: 4890a1bf553SMark Brown adn |= 0x3 << 1; 4900a1bf553SMark Brown break; 491b3172f22SGuennadi Liakhovetski case 22050: 4920a1bf553SMark Brown adn |= 0x2 << 1; 4930a1bf553SMark Brown break; 494b3172f22SGuennadi Liakhovetski case 32000: 4950a1bf553SMark Brown adn |= 0x1 << 1; 4960a1bf553SMark Brown break; 497b3172f22SGuennadi Liakhovetski case 44100: 498b3172f22SGuennadi Liakhovetski case 48000: 4990a1bf553SMark Brown break; 5000a1bf553SMark Brown } 5010a1bf553SMark Brown 5021e97f50bSMark Brown snd_soc_write(codec, WM8974_IFACE, iface); 5031e97f50bSMark Brown snd_soc_write(codec, WM8974_ADD, adn); 5040a1bf553SMark Brown return 0; 5050a1bf553SMark Brown } 5060a1bf553SMark Brown 5070a1bf553SMark Brown static int wm8974_mute(struct snd_soc_dai *dai, int mute) 5080a1bf553SMark Brown { 5090a1bf553SMark Brown struct snd_soc_codec *codec = dai->codec; 5101e97f50bSMark Brown u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf; 5110a1bf553SMark Brown 5120a1bf553SMark Brown if (mute) 5131e97f50bSMark Brown snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40); 5140a1bf553SMark Brown else 5151e97f50bSMark Brown snd_soc_write(codec, WM8974_DAC, mute_reg); 5160a1bf553SMark Brown return 0; 5170a1bf553SMark Brown } 5180a1bf553SMark Brown 5190a1bf553SMark Brown /* liam need to make this lower power with dapm */ 5200a1bf553SMark Brown static int wm8974_set_bias_level(struct snd_soc_codec *codec, 5210a1bf553SMark Brown enum snd_soc_bias_level level) 5220a1bf553SMark Brown { 5231e97f50bSMark Brown u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3; 524df1ef7a3SMark Brown 5250a1bf553SMark Brown switch (level) { 5260a1bf553SMark Brown case SND_SOC_BIAS_ON: 5270a1bf553SMark Brown case SND_SOC_BIAS_PREPARE: 528df1ef7a3SMark Brown power1 |= 0x1; /* VMID 50k */ 5291e97f50bSMark Brown snd_soc_write(codec, WM8974_POWER1, power1); 5300a1bf553SMark Brown break; 531df1ef7a3SMark Brown 5320a1bf553SMark Brown case SND_SOC_BIAS_STANDBY: 533df1ef7a3SMark Brown power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN; 534df1ef7a3SMark Brown 535df1ef7a3SMark Brown if (codec->bias_level == SND_SOC_BIAS_OFF) { 536df1ef7a3SMark Brown /* Initial cap charge at VMID 5k */ 5371e97f50bSMark Brown snd_soc_write(codec, WM8974_POWER1, power1 | 0x3); 538df1ef7a3SMark Brown mdelay(100); 539df1ef7a3SMark Brown } 540df1ef7a3SMark Brown 541df1ef7a3SMark Brown power1 |= 0x2; /* VMID 500k */ 5421e97f50bSMark Brown snd_soc_write(codec, WM8974_POWER1, power1); 5430a1bf553SMark Brown break; 544df1ef7a3SMark Brown 5450a1bf553SMark Brown case SND_SOC_BIAS_OFF: 5461e97f50bSMark Brown snd_soc_write(codec, WM8974_POWER1, 0); 5471e97f50bSMark Brown snd_soc_write(codec, WM8974_POWER2, 0); 5481e97f50bSMark Brown snd_soc_write(codec, WM8974_POWER3, 0); 5490a1bf553SMark Brown break; 5500a1bf553SMark Brown } 551df1ef7a3SMark Brown 5520a1bf553SMark Brown codec->bias_level = level; 5530a1bf553SMark Brown return 0; 5540a1bf553SMark Brown } 5550a1bf553SMark Brown 5561a55b3f6SMark Brown #define WM8974_RATES (SNDRV_PCM_RATE_8000_48000) 5570a1bf553SMark Brown 5580a1bf553SMark Brown #define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 5590a1bf553SMark Brown SNDRV_PCM_FMTBIT_S24_LE) 5600a1bf553SMark Brown 5610a1bf553SMark Brown static struct snd_soc_dai_ops wm8974_ops = { 5620a1bf553SMark Brown .hw_params = wm8974_pcm_hw_params, 5630a1bf553SMark Brown .digital_mute = wm8974_mute, 5640a1bf553SMark Brown .set_fmt = wm8974_set_dai_fmt, 5650a1bf553SMark Brown .set_clkdiv = wm8974_set_dai_clkdiv, 5660a1bf553SMark Brown .set_pll = wm8974_set_dai_pll, 5670a1bf553SMark Brown }; 5680a1bf553SMark Brown 5690a1bf553SMark Brown struct snd_soc_dai wm8974_dai = { 5700a1bf553SMark Brown .name = "WM8974 HiFi", 5710a1bf553SMark Brown .playback = { 5720a1bf553SMark Brown .stream_name = "Playback", 5730a1bf553SMark Brown .channels_min = 1, 57433d81af4SMark Brown .channels_max = 2, /* Only 1 channel of data */ 5750a1bf553SMark Brown .rates = WM8974_RATES, 5760a1bf553SMark Brown .formats = WM8974_FORMATS,}, 5770a1bf553SMark Brown .capture = { 5780a1bf553SMark Brown .stream_name = "Capture", 5790a1bf553SMark Brown .channels_min = 1, 58033d81af4SMark Brown .channels_max = 2, /* Only 1 channel of data */ 5810a1bf553SMark Brown .rates = WM8974_RATES, 5820a1bf553SMark Brown .formats = WM8974_FORMATS,}, 5830a1bf553SMark Brown .ops = &wm8974_ops, 584cb11d39eSMark Brown .symmetric_rates = 1, 5850a1bf553SMark Brown }; 5860a1bf553SMark Brown EXPORT_SYMBOL_GPL(wm8974_dai); 5870a1bf553SMark Brown 5880a1bf553SMark Brown static int wm8974_suspend(struct platform_device *pdev, pm_message_t state) 5890a1bf553SMark Brown { 5900a1bf553SMark Brown struct snd_soc_device *socdev = platform_get_drvdata(pdev); 5910a1bf553SMark Brown struct snd_soc_codec *codec = socdev->card->codec; 5920a1bf553SMark Brown 5930a1bf553SMark Brown wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF); 5940a1bf553SMark Brown return 0; 5950a1bf553SMark Brown } 5960a1bf553SMark Brown 5970a1bf553SMark Brown static int wm8974_resume(struct platform_device *pdev) 5980a1bf553SMark Brown { 5990a1bf553SMark Brown struct snd_soc_device *socdev = platform_get_drvdata(pdev); 6000a1bf553SMark Brown struct snd_soc_codec *codec = socdev->card->codec; 6010a1bf553SMark Brown int i; 6020a1bf553SMark Brown u8 data[2]; 6030a1bf553SMark Brown u16 *cache = codec->reg_cache; 6040a1bf553SMark Brown 6050a1bf553SMark Brown /* Sync reg_cache with the hardware */ 6060a1bf553SMark Brown for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) { 6070a1bf553SMark Brown data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); 6080a1bf553SMark Brown data[1] = cache[i] & 0x00ff; 6090a1bf553SMark Brown codec->hw_write(codec->control_data, data, 2); 6100a1bf553SMark Brown } 6110a1bf553SMark Brown wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 6120a1bf553SMark Brown wm8974_set_bias_level(codec, codec->suspend_bias_level); 6130a1bf553SMark Brown return 0; 6140a1bf553SMark Brown } 6150a1bf553SMark Brown 6164fcbbb67SMark Brown static int wm8974_probe(struct platform_device *pdev) 6170a1bf553SMark Brown { 6184fcbbb67SMark Brown struct snd_soc_device *socdev = platform_get_drvdata(pdev); 6194fcbbb67SMark Brown struct snd_soc_codec *codec; 6200a1bf553SMark Brown int ret = 0; 6210a1bf553SMark Brown 6224fcbbb67SMark Brown if (wm8974_codec == NULL) { 6234fcbbb67SMark Brown dev_err(&pdev->dev, "Codec device not registered\n"); 6244fcbbb67SMark Brown return -ENODEV; 6254fcbbb67SMark Brown } 6260a1bf553SMark Brown 6274fcbbb67SMark Brown socdev->card->codec = wm8974_codec; 6284fcbbb67SMark Brown codec = wm8974_codec; 6290a1bf553SMark Brown 6300a1bf553SMark Brown /* register pcms */ 6310a1bf553SMark Brown ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); 6320a1bf553SMark Brown if (ret < 0) { 6334fcbbb67SMark Brown dev_err(codec->dev, "failed to create pcms: %d\n", ret); 6340a1bf553SMark Brown goto pcm_err; 6350a1bf553SMark Brown } 6360a1bf553SMark Brown 6374fcbbb67SMark Brown snd_soc_add_controls(codec, wm8974_snd_controls, 6384fcbbb67SMark Brown ARRAY_SIZE(wm8974_snd_controls)); 6390a1bf553SMark Brown wm8974_add_widgets(codec); 6404fcbbb67SMark Brown 6410a1bf553SMark Brown return ret; 6420a1bf553SMark Brown 6430a1bf553SMark Brown pcm_err: 6440a1bf553SMark Brown return ret; 6450a1bf553SMark Brown } 6460a1bf553SMark Brown 6470a1bf553SMark Brown /* power down chip */ 6480a1bf553SMark Brown static int wm8974_remove(struct platform_device *pdev) 6490a1bf553SMark Brown { 6500a1bf553SMark Brown struct snd_soc_device *socdev = platform_get_drvdata(pdev); 6510a1bf553SMark Brown 6520a1bf553SMark Brown snd_soc_free_pcms(socdev); 6530a1bf553SMark Brown snd_soc_dapm_free(socdev); 6540a1bf553SMark Brown 6550a1bf553SMark Brown return 0; 6560a1bf553SMark Brown } 6570a1bf553SMark Brown 6580a1bf553SMark Brown struct snd_soc_codec_device soc_codec_dev_wm8974 = { 6590a1bf553SMark Brown .probe = wm8974_probe, 6600a1bf553SMark Brown .remove = wm8974_remove, 6610a1bf553SMark Brown .suspend = wm8974_suspend, 6620a1bf553SMark Brown .resume = wm8974_resume, 6630a1bf553SMark Brown }; 6640a1bf553SMark Brown EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974); 6650a1bf553SMark Brown 6664fcbbb67SMark Brown static __devinit int wm8974_register(struct wm8974_priv *wm8974) 6674fcbbb67SMark Brown { 6684fcbbb67SMark Brown int ret; 6694fcbbb67SMark Brown struct snd_soc_codec *codec = &wm8974->codec; 6704fcbbb67SMark Brown 6714fcbbb67SMark Brown if (wm8974_codec) { 6724fcbbb67SMark Brown dev_err(codec->dev, "Another WM8974 is registered\n"); 6734fcbbb67SMark Brown return -EINVAL; 6744fcbbb67SMark Brown } 6754fcbbb67SMark Brown 6764fcbbb67SMark Brown mutex_init(&codec->mutex); 6774fcbbb67SMark Brown INIT_LIST_HEAD(&codec->dapm_widgets); 6784fcbbb67SMark Brown INIT_LIST_HEAD(&codec->dapm_paths); 6794fcbbb67SMark Brown 6804fcbbb67SMark Brown codec->private_data = wm8974; 6814fcbbb67SMark Brown codec->name = "WM8974"; 6824fcbbb67SMark Brown codec->owner = THIS_MODULE; 6834fcbbb67SMark Brown codec->bias_level = SND_SOC_BIAS_OFF; 6844fcbbb67SMark Brown codec->set_bias_level = wm8974_set_bias_level; 6854fcbbb67SMark Brown codec->dai = &wm8974_dai; 6864fcbbb67SMark Brown codec->num_dai = 1; 6874fcbbb67SMark Brown codec->reg_cache_size = WM8974_CACHEREGNUM; 6884fcbbb67SMark Brown codec->reg_cache = &wm8974->reg_cache; 6894fcbbb67SMark Brown 6901e97f50bSMark Brown ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C); 6911e97f50bSMark Brown if (ret < 0) { 6921e97f50bSMark Brown dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); 6931e97f50bSMark Brown goto err; 6941e97f50bSMark Brown } 6951e97f50bSMark Brown 6964fcbbb67SMark Brown memcpy(codec->reg_cache, wm8974_reg, sizeof(wm8974_reg)); 6974fcbbb67SMark Brown 6984fcbbb67SMark Brown ret = wm8974_reset(codec); 6994fcbbb67SMark Brown if (ret < 0) { 7004fcbbb67SMark Brown dev_err(codec->dev, "Failed to issue reset\n"); 7011e97f50bSMark Brown goto err; 7024fcbbb67SMark Brown } 7034fcbbb67SMark Brown 7044fcbbb67SMark Brown wm8974_dai.dev = codec->dev; 7054fcbbb67SMark Brown 7064fcbbb67SMark Brown wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 7074fcbbb67SMark Brown 7084fcbbb67SMark Brown wm8974_codec = codec; 7094fcbbb67SMark Brown 7104fcbbb67SMark Brown ret = snd_soc_register_codec(codec); 7114fcbbb67SMark Brown if (ret != 0) { 7124fcbbb67SMark Brown dev_err(codec->dev, "Failed to register codec: %d\n", ret); 7131e97f50bSMark Brown goto err; 7144fcbbb67SMark Brown } 7154fcbbb67SMark Brown 7164fcbbb67SMark Brown ret = snd_soc_register_dai(&wm8974_dai); 7174fcbbb67SMark Brown if (ret != 0) { 7184fcbbb67SMark Brown dev_err(codec->dev, "Failed to register DAI: %d\n", ret); 7191e97f50bSMark Brown goto err_codec; 7204fcbbb67SMark Brown } 7214fcbbb67SMark Brown 7224fcbbb67SMark Brown return 0; 7231e97f50bSMark Brown 7241e97f50bSMark Brown err_codec: 7251e97f50bSMark Brown snd_soc_unregister_codec(codec); 7261e97f50bSMark Brown err: 7271e97f50bSMark Brown kfree(wm8974); 7281e97f50bSMark Brown return ret; 7294fcbbb67SMark Brown } 7304fcbbb67SMark Brown 7314fcbbb67SMark Brown static __devexit void wm8974_unregister(struct wm8974_priv *wm8974) 7324fcbbb67SMark Brown { 7334fcbbb67SMark Brown wm8974_set_bias_level(&wm8974->codec, SND_SOC_BIAS_OFF); 7344fcbbb67SMark Brown snd_soc_unregister_dai(&wm8974_dai); 7354fcbbb67SMark Brown snd_soc_unregister_codec(&wm8974->codec); 7364fcbbb67SMark Brown kfree(wm8974); 7374fcbbb67SMark Brown wm8974_codec = NULL; 7384fcbbb67SMark Brown } 7394fcbbb67SMark Brown 7404fcbbb67SMark Brown static __devinit int wm8974_i2c_probe(struct i2c_client *i2c, 7414fcbbb67SMark Brown const struct i2c_device_id *id) 7424fcbbb67SMark Brown { 7434fcbbb67SMark Brown struct wm8974_priv *wm8974; 7444fcbbb67SMark Brown struct snd_soc_codec *codec; 7454fcbbb67SMark Brown 7464fcbbb67SMark Brown wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL); 7474fcbbb67SMark Brown if (wm8974 == NULL) 7484fcbbb67SMark Brown return -ENOMEM; 7494fcbbb67SMark Brown 7504fcbbb67SMark Brown codec = &wm8974->codec; 7514fcbbb67SMark Brown codec->hw_write = (hw_write_t)i2c_master_send; 7524fcbbb67SMark Brown 7534fcbbb67SMark Brown i2c_set_clientdata(i2c, wm8974); 7544fcbbb67SMark Brown codec->control_data = i2c; 7554fcbbb67SMark Brown 7564fcbbb67SMark Brown codec->dev = &i2c->dev; 7574fcbbb67SMark Brown 7584fcbbb67SMark Brown return wm8974_register(wm8974); 7594fcbbb67SMark Brown } 7604fcbbb67SMark Brown 7614fcbbb67SMark Brown static __devexit int wm8974_i2c_remove(struct i2c_client *client) 7624fcbbb67SMark Brown { 7634fcbbb67SMark Brown struct wm8974_priv *wm8974 = i2c_get_clientdata(client); 7644fcbbb67SMark Brown wm8974_unregister(wm8974); 7654fcbbb67SMark Brown return 0; 7664fcbbb67SMark Brown } 7674fcbbb67SMark Brown 7684fcbbb67SMark Brown static const struct i2c_device_id wm8974_i2c_id[] = { 7694fcbbb67SMark Brown { "wm8974", 0 }, 7704fcbbb67SMark Brown { } 7714fcbbb67SMark Brown }; 7724fcbbb67SMark Brown MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id); 7734fcbbb67SMark Brown 7744fcbbb67SMark Brown static struct i2c_driver wm8974_i2c_driver = { 7754fcbbb67SMark Brown .driver = { 7768b83a193SMark Brown .name = "WM8974", 7774fcbbb67SMark Brown .owner = THIS_MODULE, 7784fcbbb67SMark Brown }, 7794fcbbb67SMark Brown .probe = wm8974_i2c_probe, 7804fcbbb67SMark Brown .remove = __devexit_p(wm8974_i2c_remove), 7814fcbbb67SMark Brown .id_table = wm8974_i2c_id, 7824fcbbb67SMark Brown }; 7834fcbbb67SMark Brown 7840a1bf553SMark Brown static int __init wm8974_modinit(void) 7850a1bf553SMark Brown { 7864fcbbb67SMark Brown return i2c_add_driver(&wm8974_i2c_driver); 7870a1bf553SMark Brown } 7880a1bf553SMark Brown module_init(wm8974_modinit); 7890a1bf553SMark Brown 7900a1bf553SMark Brown static void __exit wm8974_exit(void) 7910a1bf553SMark Brown { 7924fcbbb67SMark Brown i2c_del_driver(&wm8974_i2c_driver); 7930a1bf553SMark Brown } 7940a1bf553SMark Brown module_exit(wm8974_exit); 7950a1bf553SMark Brown 7960a1bf553SMark Brown MODULE_DESCRIPTION("ASoC WM8974 driver"); 7970a1bf553SMark Brown MODULE_AUTHOR("Liam Girdwood"); 7980a1bf553SMark Brown MODULE_LICENSE("GPL"); 799