1*0a1bf553SMark Brown /* 2*0a1bf553SMark Brown * wm8974.c -- WM8974 ALSA Soc Audio driver 3*0a1bf553SMark Brown * 4*0a1bf553SMark Brown * Copyright 2006 Wolfson Microelectronics PLC. 5*0a1bf553SMark Brown * 6*0a1bf553SMark Brown * Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com> 7*0a1bf553SMark Brown * 8*0a1bf553SMark Brown * This program is free software; you can redistribute it and/or modify 9*0a1bf553SMark Brown * it under the terms of the GNU General Public License version 2 as 10*0a1bf553SMark Brown * published by the Free Software Foundation. 11*0a1bf553SMark Brown */ 12*0a1bf553SMark Brown 13*0a1bf553SMark Brown #include <linux/module.h> 14*0a1bf553SMark Brown #include <linux/moduleparam.h> 15*0a1bf553SMark Brown #include <linux/version.h> 16*0a1bf553SMark Brown #include <linux/kernel.h> 17*0a1bf553SMark Brown #include <linux/init.h> 18*0a1bf553SMark Brown #include <linux/delay.h> 19*0a1bf553SMark Brown #include <linux/pm.h> 20*0a1bf553SMark Brown #include <linux/i2c.h> 21*0a1bf553SMark Brown #include <linux/platform_device.h> 22*0a1bf553SMark Brown #include <sound/core.h> 23*0a1bf553SMark Brown #include <sound/pcm.h> 24*0a1bf553SMark Brown #include <sound/pcm_params.h> 25*0a1bf553SMark Brown #include <sound/soc.h> 26*0a1bf553SMark Brown #include <sound/soc-dapm.h> 27*0a1bf553SMark Brown #include <sound/initval.h> 28*0a1bf553SMark Brown 29*0a1bf553SMark Brown #include "wm8974.h" 30*0a1bf553SMark Brown 31*0a1bf553SMark Brown #define AUDIO_NAME "wm8974" 32*0a1bf553SMark Brown #define WM8974_VERSION "0.6" 33*0a1bf553SMark Brown 34*0a1bf553SMark Brown struct snd_soc_codec_device soc_codec_dev_wm8974; 35*0a1bf553SMark Brown 36*0a1bf553SMark Brown /* 37*0a1bf553SMark Brown * wm8974 register cache 38*0a1bf553SMark Brown * We can't read the WM8974 register space when we are 39*0a1bf553SMark Brown * using 2 wire for device control, so we cache them instead. 40*0a1bf553SMark Brown */ 41*0a1bf553SMark Brown static const u16 wm8974_reg[WM8974_CACHEREGNUM] = { 42*0a1bf553SMark Brown 0x0000, 0x0000, 0x0000, 0x0000, 43*0a1bf553SMark Brown 0x0050, 0x0000, 0x0140, 0x0000, 44*0a1bf553SMark Brown 0x0000, 0x0000, 0x0000, 0x00ff, 45*0a1bf553SMark Brown 0x0000, 0x0000, 0x0100, 0x00ff, 46*0a1bf553SMark Brown 0x0000, 0x0000, 0x012c, 0x002c, 47*0a1bf553SMark Brown 0x002c, 0x002c, 0x002c, 0x0000, 48*0a1bf553SMark Brown 0x0032, 0x0000, 0x0000, 0x0000, 49*0a1bf553SMark Brown 0x0000, 0x0000, 0x0000, 0x0000, 50*0a1bf553SMark Brown 0x0038, 0x000b, 0x0032, 0x0000, 51*0a1bf553SMark Brown 0x0008, 0x000c, 0x0093, 0x00e9, 52*0a1bf553SMark Brown 0x0000, 0x0000, 0x0000, 0x0000, 53*0a1bf553SMark Brown 0x0003, 0x0010, 0x0000, 0x0000, 54*0a1bf553SMark Brown 0x0000, 0x0002, 0x0000, 0x0000, 55*0a1bf553SMark Brown 0x0000, 0x0000, 0x0039, 0x0000, 56*0a1bf553SMark Brown 0x0000, 57*0a1bf553SMark Brown }; 58*0a1bf553SMark Brown 59*0a1bf553SMark Brown /* 60*0a1bf553SMark Brown * read wm8974 register cache 61*0a1bf553SMark Brown */ 62*0a1bf553SMark Brown static inline unsigned int wm8974_read_reg_cache(struct snd_soc_codec * codec, 63*0a1bf553SMark Brown unsigned int reg) 64*0a1bf553SMark Brown { 65*0a1bf553SMark Brown u16 *cache = codec->reg_cache; 66*0a1bf553SMark Brown if (reg == WM8974_RESET) 67*0a1bf553SMark Brown return 0; 68*0a1bf553SMark Brown if (reg >= WM8974_CACHEREGNUM) 69*0a1bf553SMark Brown return -1; 70*0a1bf553SMark Brown return cache[reg]; 71*0a1bf553SMark Brown } 72*0a1bf553SMark Brown 73*0a1bf553SMark Brown /* 74*0a1bf553SMark Brown * write wm8974 register cache 75*0a1bf553SMark Brown */ 76*0a1bf553SMark Brown static inline void wm8974_write_reg_cache(struct snd_soc_codec *codec, 77*0a1bf553SMark Brown u16 reg, unsigned int value) 78*0a1bf553SMark Brown { 79*0a1bf553SMark Brown u16 *cache = codec->reg_cache; 80*0a1bf553SMark Brown if (reg >= WM8974_CACHEREGNUM) 81*0a1bf553SMark Brown return; 82*0a1bf553SMark Brown cache[reg] = value; 83*0a1bf553SMark Brown } 84*0a1bf553SMark Brown 85*0a1bf553SMark Brown /* 86*0a1bf553SMark Brown * write to the WM8974 register space 87*0a1bf553SMark Brown */ 88*0a1bf553SMark Brown static int wm8974_write(struct snd_soc_codec *codec, unsigned int reg, 89*0a1bf553SMark Brown unsigned int value) 90*0a1bf553SMark Brown { 91*0a1bf553SMark Brown u8 data[2]; 92*0a1bf553SMark Brown 93*0a1bf553SMark Brown /* data is 94*0a1bf553SMark Brown * D15..D9 WM8974 register offset 95*0a1bf553SMark Brown * D8...D0 register data 96*0a1bf553SMark Brown */ 97*0a1bf553SMark Brown data[0] = (reg << 1) | ((value >> 8) & 0x0001); 98*0a1bf553SMark Brown data[1] = value & 0x00ff; 99*0a1bf553SMark Brown 100*0a1bf553SMark Brown wm8974_write_reg_cache (codec, reg, value); 101*0a1bf553SMark Brown if (codec->hw_write(codec->control_data, data, 2) == 2) 102*0a1bf553SMark Brown return 0; 103*0a1bf553SMark Brown else 104*0a1bf553SMark Brown return -EIO; 105*0a1bf553SMark Brown } 106*0a1bf553SMark Brown 107*0a1bf553SMark Brown #define wm8974_reset(c) wm8974_write(c, WM8974_RESET, 0) 108*0a1bf553SMark Brown 109*0a1bf553SMark Brown static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" }; 110*0a1bf553SMark Brown static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" }; 111*0a1bf553SMark Brown static const char *wm8974_eqmode[] = {"Capture", "Playback" }; 112*0a1bf553SMark Brown static const char *wm8974_bw[] = {"Narrow", "Wide" }; 113*0a1bf553SMark Brown static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" }; 114*0a1bf553SMark Brown static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" }; 115*0a1bf553SMark Brown static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" }; 116*0a1bf553SMark Brown static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" }; 117*0a1bf553SMark Brown static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" }; 118*0a1bf553SMark Brown static const char *wm8974_alc[] = {"ALC", "Limiter" }; 119*0a1bf553SMark Brown 120*0a1bf553SMark Brown static const struct soc_enum wm8974_enum[] = { 121*0a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */ 122*0a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */ 123*0a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp), 124*0a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode), 125*0a1bf553SMark Brown 126*0a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1), 127*0a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw), 128*0a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2), 129*0a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw), 130*0a1bf553SMark Brown 131*0a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3), 132*0a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw), 133*0a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4), 134*0a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw), 135*0a1bf553SMark Brown 136*0a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5), 137*0a1bf553SMark Brown SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc), 138*0a1bf553SMark Brown }; 139*0a1bf553SMark Brown 140*0a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_snd_controls[] = { 141*0a1bf553SMark Brown 142*0a1bf553SMark Brown SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0), 143*0a1bf553SMark Brown 144*0a1bf553SMark Brown SOC_ENUM("DAC Companding", wm8974_enum[1]), 145*0a1bf553SMark Brown SOC_ENUM("ADC Companding", wm8974_enum[0]), 146*0a1bf553SMark Brown 147*0a1bf553SMark Brown SOC_ENUM("Playback De-emphasis", wm8974_enum[2]), 148*0a1bf553SMark Brown SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0), 149*0a1bf553SMark Brown 150*0a1bf553SMark Brown SOC_SINGLE("PCM Volume", WM8974_DACVOL, 0, 127, 0), 151*0a1bf553SMark Brown 152*0a1bf553SMark Brown SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0), 153*0a1bf553SMark Brown SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0), 154*0a1bf553SMark Brown SOC_SINGLE("ADC Inversion Switch", WM8974_COMP, 0, 1, 0), 155*0a1bf553SMark Brown 156*0a1bf553SMark Brown SOC_SINGLE("Capture Volume", WM8974_ADCVOL, 0, 127, 0), 157*0a1bf553SMark Brown 158*0a1bf553SMark Brown SOC_ENUM("Equaliser Function", wm8974_enum[3]), 159*0a1bf553SMark Brown SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]), 160*0a1bf553SMark Brown SOC_SINGLE("EQ1 Volume", WM8974_EQ1, 0, 31, 1), 161*0a1bf553SMark Brown 162*0a1bf553SMark Brown SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]), 163*0a1bf553SMark Brown SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]), 164*0a1bf553SMark Brown SOC_SINGLE("EQ2 Volume", WM8974_EQ2, 0, 31, 1), 165*0a1bf553SMark Brown 166*0a1bf553SMark Brown SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]), 167*0a1bf553SMark Brown SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]), 168*0a1bf553SMark Brown SOC_SINGLE("EQ3 Volume", WM8974_EQ3, 0, 31, 1), 169*0a1bf553SMark Brown 170*0a1bf553SMark Brown SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]), 171*0a1bf553SMark Brown SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]), 172*0a1bf553SMark Brown SOC_SINGLE("EQ4 Volume", WM8974_EQ4, 0, 31, 1), 173*0a1bf553SMark Brown 174*0a1bf553SMark Brown SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]), 175*0a1bf553SMark Brown SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]), 176*0a1bf553SMark Brown SOC_SINGLE("EQ5 Volume", WM8974_EQ5, 0, 31, 1), 177*0a1bf553SMark Brown 178*0a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0), 179*0a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0), 180*0a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0), 181*0a1bf553SMark Brown 182*0a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0), 183*0a1bf553SMark Brown SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0), 184*0a1bf553SMark Brown 185*0a1bf553SMark Brown SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0), 186*0a1bf553SMark Brown SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0), 187*0a1bf553SMark Brown SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0), 188*0a1bf553SMark Brown 189*0a1bf553SMark Brown SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0), 190*0a1bf553SMark Brown SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0), 191*0a1bf553SMark Brown SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0), 192*0a1bf553SMark Brown 193*0a1bf553SMark Brown SOC_ENUM("ALC Capture Mode", wm8974_enum[13]), 194*0a1bf553SMark Brown SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0), 195*0a1bf553SMark Brown SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0), 196*0a1bf553SMark Brown 197*0a1bf553SMark Brown SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0), 198*0a1bf553SMark Brown SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0), 199*0a1bf553SMark Brown 200*0a1bf553SMark Brown SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0), 201*0a1bf553SMark Brown SOC_SINGLE("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0), 202*0a1bf553SMark Brown 203*0a1bf553SMark Brown SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0), 204*0a1bf553SMark Brown SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1), 205*0a1bf553SMark Brown SOC_SINGLE("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0), 206*0a1bf553SMark Brown 207*0a1bf553SMark Brown SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0), 208*0a1bf553SMark Brown SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 0), 209*0a1bf553SMark Brown }; 210*0a1bf553SMark Brown 211*0a1bf553SMark Brown /* add non dapm controls */ 212*0a1bf553SMark Brown static int wm8974_add_controls(struct snd_soc_codec *codec) 213*0a1bf553SMark Brown { 214*0a1bf553SMark Brown int err, i; 215*0a1bf553SMark Brown 216*0a1bf553SMark Brown for (i = 0; i < ARRAY_SIZE(wm8974_snd_controls); i++) { 217*0a1bf553SMark Brown err = snd_ctl_add(codec->card, 218*0a1bf553SMark Brown snd_soc_cnew(&wm8974_snd_controls[i],codec, NULL)); 219*0a1bf553SMark Brown if (err < 0) 220*0a1bf553SMark Brown return err; 221*0a1bf553SMark Brown } 222*0a1bf553SMark Brown 223*0a1bf553SMark Brown return 0; 224*0a1bf553SMark Brown } 225*0a1bf553SMark Brown 226*0a1bf553SMark Brown /* Speaker Output Mixer */ 227*0a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = { 228*0a1bf553SMark Brown SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0), 229*0a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0), 230*0a1bf553SMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1), 231*0a1bf553SMark Brown }; 232*0a1bf553SMark Brown 233*0a1bf553SMark Brown /* Mono Output Mixer */ 234*0a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = { 235*0a1bf553SMark Brown SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0), 236*0a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0), 237*0a1bf553SMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 1), 238*0a1bf553SMark Brown }; 239*0a1bf553SMark Brown 240*0a1bf553SMark Brown /* AUX Input boost vol */ 241*0a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_aux_boost_controls = 242*0a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0); 243*0a1bf553SMark Brown 244*0a1bf553SMark Brown /* Mic Input boost vol */ 245*0a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_mic_boost_controls = 246*0a1bf553SMark Brown SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0); 247*0a1bf553SMark Brown 248*0a1bf553SMark Brown /* Capture boost switch */ 249*0a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_capture_boost_controls = 250*0a1bf553SMark Brown SOC_DAPM_SINGLE("Capture Boost Switch", WM8974_INPPGA, 6, 1, 0); 251*0a1bf553SMark Brown 252*0a1bf553SMark Brown /* Aux In to PGA */ 253*0a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_aux_capture_boost_controls = 254*0a1bf553SMark Brown SOC_DAPM_SINGLE("Aux Capture Boost Switch", WM8974_INPPGA, 2, 1, 0); 255*0a1bf553SMark Brown 256*0a1bf553SMark Brown /* Mic P In to PGA */ 257*0a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_micp_capture_boost_controls = 258*0a1bf553SMark Brown SOC_DAPM_SINGLE("Mic P Capture Boost Switch", WM8974_INPPGA, 0, 1, 0); 259*0a1bf553SMark Brown 260*0a1bf553SMark Brown /* Mic N In to PGA */ 261*0a1bf553SMark Brown static const struct snd_kcontrol_new wm8974_micn_capture_boost_controls = 262*0a1bf553SMark Brown SOC_DAPM_SINGLE("Mic N Capture Boost Switch", WM8974_INPPGA, 1, 1, 0); 263*0a1bf553SMark Brown 264*0a1bf553SMark Brown static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = { 265*0a1bf553SMark Brown SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0, 266*0a1bf553SMark Brown &wm8974_speaker_mixer_controls[0], 267*0a1bf553SMark Brown ARRAY_SIZE(wm8974_speaker_mixer_controls)), 268*0a1bf553SMark Brown SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0, 269*0a1bf553SMark Brown &wm8974_mono_mixer_controls[0], 270*0a1bf553SMark Brown ARRAY_SIZE(wm8974_mono_mixer_controls)), 271*0a1bf553SMark Brown SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0), 272*0a1bf553SMark Brown SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER3, 0, 0), 273*0a1bf553SMark Brown SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0), 274*0a1bf553SMark Brown SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0), 275*0a1bf553SMark Brown SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0), 276*0a1bf553SMark Brown SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0), 277*0a1bf553SMark Brown SND_SOC_DAPM_PGA("Mic PGA", WM8974_POWER2, 2, 0, NULL, 0), 278*0a1bf553SMark Brown 279*0a1bf553SMark Brown SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0, 280*0a1bf553SMark Brown &wm8974_aux_boost_controls, 1), 281*0a1bf553SMark Brown SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0, 282*0a1bf553SMark Brown &wm8974_mic_boost_controls, 1), 283*0a1bf553SMark Brown SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0, 284*0a1bf553SMark Brown &wm8974_capture_boost_controls), 285*0a1bf553SMark Brown 286*0a1bf553SMark Brown SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0, NULL, 0), 287*0a1bf553SMark Brown 288*0a1bf553SMark Brown SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0), 289*0a1bf553SMark Brown 290*0a1bf553SMark Brown SND_SOC_DAPM_INPUT("MICN"), 291*0a1bf553SMark Brown SND_SOC_DAPM_INPUT("MICP"), 292*0a1bf553SMark Brown SND_SOC_DAPM_INPUT("AUX"), 293*0a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("MONOOUT"), 294*0a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("SPKOUTP"), 295*0a1bf553SMark Brown SND_SOC_DAPM_OUTPUT("SPKOUTN"), 296*0a1bf553SMark Brown }; 297*0a1bf553SMark Brown 298*0a1bf553SMark Brown static const struct snd_soc_dapm_route audio_map[] = { 299*0a1bf553SMark Brown /* Mono output mixer */ 300*0a1bf553SMark Brown {"Mono Mixer", "PCM Playback Switch", "DAC"}, 301*0a1bf553SMark Brown {"Mono Mixer", "Aux Playback Switch", "Aux Input"}, 302*0a1bf553SMark Brown {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"}, 303*0a1bf553SMark Brown 304*0a1bf553SMark Brown /* Speaker output mixer */ 305*0a1bf553SMark Brown {"Speaker Mixer", "PCM Playback Switch", "DAC"}, 306*0a1bf553SMark Brown {"Speaker Mixer", "Aux Playback Switch", "Aux Input"}, 307*0a1bf553SMark Brown {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"}, 308*0a1bf553SMark Brown 309*0a1bf553SMark Brown /* Outputs */ 310*0a1bf553SMark Brown {"Mono Out", NULL, "Mono Mixer"}, 311*0a1bf553SMark Brown {"MONOOUT", NULL, "Mono Out"}, 312*0a1bf553SMark Brown {"SpkN Out", NULL, "Speaker Mixer"}, 313*0a1bf553SMark Brown {"SpkP Out", NULL, "Speaker Mixer"}, 314*0a1bf553SMark Brown {"SPKOUTN", NULL, "SpkN Out"}, 315*0a1bf553SMark Brown {"SPKOUTP", NULL, "SpkP Out"}, 316*0a1bf553SMark Brown 317*0a1bf553SMark Brown /* Boost Mixer */ 318*0a1bf553SMark Brown {"Boost Mixer", NULL, "ADC"}, 319*0a1bf553SMark Brown {"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"}, 320*0a1bf553SMark Brown {"Aux Boost", "Aux Volume", "Boost Mixer"}, 321*0a1bf553SMark Brown {"Capture Boost", "Capture Switch", "Boost Mixer"}, 322*0a1bf553SMark Brown {"Mic Boost", "Mic Volume", "Boost Mixer"}, 323*0a1bf553SMark Brown 324*0a1bf553SMark Brown /* Inputs */ 325*0a1bf553SMark Brown {"MICP", NULL, "Mic Boost"}, 326*0a1bf553SMark Brown {"MICN", NULL, "Mic PGA"}, 327*0a1bf553SMark Brown {"Mic PGA", NULL, "Capture Boost"}, 328*0a1bf553SMark Brown {"AUX", NULL, "Aux Input"}, 329*0a1bf553SMark Brown }; 330*0a1bf553SMark Brown 331*0a1bf553SMark Brown static int wm8974_add_widgets(struct snd_soc_codec *codec) 332*0a1bf553SMark Brown { 333*0a1bf553SMark Brown snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets, 334*0a1bf553SMark Brown ARRAY_SIZE(wm8974_dapm_widgets)); 335*0a1bf553SMark Brown 336*0a1bf553SMark Brown snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); 337*0a1bf553SMark Brown 338*0a1bf553SMark Brown snd_soc_dapm_new_widgets(codec); 339*0a1bf553SMark Brown return 0; 340*0a1bf553SMark Brown } 341*0a1bf553SMark Brown 342*0a1bf553SMark Brown struct pll_ { 343*0a1bf553SMark Brown unsigned int in_hz, out_hz; 344*0a1bf553SMark Brown unsigned int pre:4; /* prescale - 1 */ 345*0a1bf553SMark Brown unsigned int n:4; 346*0a1bf553SMark Brown unsigned int k; 347*0a1bf553SMark Brown }; 348*0a1bf553SMark Brown 349*0a1bf553SMark Brown static struct pll_ pll[] = { 350*0a1bf553SMark Brown {12000000, 11289600, 0, 7, 0x86c220}, 351*0a1bf553SMark Brown {12000000, 12288000, 0, 8, 0x3126e8}, 352*0a1bf553SMark Brown {13000000, 11289600, 0, 6, 0xf28bd4}, 353*0a1bf553SMark Brown {13000000, 12288000, 0, 7, 0x8fd525}, 354*0a1bf553SMark Brown {12288000, 11289600, 0, 7, 0x59999a}, 355*0a1bf553SMark Brown {11289600, 12288000, 0, 8, 0x80dee9}, 356*0a1bf553SMark Brown /* liam - add more entries */ 357*0a1bf553SMark Brown }; 358*0a1bf553SMark Brown 359*0a1bf553SMark Brown static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, 360*0a1bf553SMark Brown int pll_id, unsigned int freq_in, unsigned int freq_out) 361*0a1bf553SMark Brown { 362*0a1bf553SMark Brown struct snd_soc_codec *codec = codec_dai->codec; 363*0a1bf553SMark Brown int i; 364*0a1bf553SMark Brown u16 reg; 365*0a1bf553SMark Brown 366*0a1bf553SMark Brown if(freq_in == 0 || freq_out == 0) { 367*0a1bf553SMark Brown reg = wm8974_read_reg_cache(codec, WM8974_POWER1); 368*0a1bf553SMark Brown wm8974_write(codec, WM8974_POWER1, reg & 0x1df); 369*0a1bf553SMark Brown return 0; 370*0a1bf553SMark Brown } 371*0a1bf553SMark Brown 372*0a1bf553SMark Brown for(i = 0; i < ARRAY_SIZE(pll); i++) { 373*0a1bf553SMark Brown if (freq_in == pll[i].in_hz && freq_out == pll[i].out_hz) { 374*0a1bf553SMark Brown wm8974_write(codec, WM8974_PLLN, (pll[i].pre << 4) | pll[i].n); 375*0a1bf553SMark Brown wm8974_write(codec, WM8974_PLLK1, pll[i].k >> 18); 376*0a1bf553SMark Brown wm8974_write(codec, WM8974_PLLK2, (pll[i].k >> 9) & 0x1ff); 377*0a1bf553SMark Brown wm8974_write(codec, WM8974_PLLK3, pll[i].k & 0x1ff); 378*0a1bf553SMark Brown reg = wm8974_read_reg_cache(codec, WM8974_POWER1); 379*0a1bf553SMark Brown wm8974_write(codec, WM8974_POWER1, reg | 0x020); 380*0a1bf553SMark Brown return 0; 381*0a1bf553SMark Brown } 382*0a1bf553SMark Brown } 383*0a1bf553SMark Brown return -EINVAL; 384*0a1bf553SMark Brown } 385*0a1bf553SMark Brown 386*0a1bf553SMark Brown /* 387*0a1bf553SMark Brown * Configure WM8974 clock dividers. 388*0a1bf553SMark Brown */ 389*0a1bf553SMark Brown static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai, 390*0a1bf553SMark Brown int div_id, int div) 391*0a1bf553SMark Brown { 392*0a1bf553SMark Brown struct snd_soc_codec *codec = codec_dai->codec; 393*0a1bf553SMark Brown u16 reg; 394*0a1bf553SMark Brown 395*0a1bf553SMark Brown switch (div_id) { 396*0a1bf553SMark Brown case WM8974_OPCLKDIV: 397*0a1bf553SMark Brown reg = wm8974_read_reg_cache(codec, WM8974_GPIO) & 0x1cf; 398*0a1bf553SMark Brown wm8974_write(codec, WM8974_GPIO, reg | div); 399*0a1bf553SMark Brown break; 400*0a1bf553SMark Brown case WM8974_MCLKDIV: 401*0a1bf553SMark Brown reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x11f; 402*0a1bf553SMark Brown wm8974_write(codec, WM8974_CLOCK, reg | div); 403*0a1bf553SMark Brown break; 404*0a1bf553SMark Brown case WM8974_ADCCLK: 405*0a1bf553SMark Brown reg = wm8974_read_reg_cache(codec, WM8974_ADC) & 0x1f7; 406*0a1bf553SMark Brown wm8974_write(codec, WM8974_ADC, reg | div); 407*0a1bf553SMark Brown break; 408*0a1bf553SMark Brown case WM8974_DACCLK: 409*0a1bf553SMark Brown reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0x1f7; 410*0a1bf553SMark Brown wm8974_write(codec, WM8974_DAC, reg | div); 411*0a1bf553SMark Brown break; 412*0a1bf553SMark Brown case WM8974_BCLKDIV: 413*0a1bf553SMark Brown reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1e3; 414*0a1bf553SMark Brown wm8974_write(codec, WM8974_CLOCK, reg | div); 415*0a1bf553SMark Brown break; 416*0a1bf553SMark Brown default: 417*0a1bf553SMark Brown return -EINVAL; 418*0a1bf553SMark Brown } 419*0a1bf553SMark Brown 420*0a1bf553SMark Brown return 0; 421*0a1bf553SMark Brown } 422*0a1bf553SMark Brown 423*0a1bf553SMark Brown static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai, 424*0a1bf553SMark Brown unsigned int fmt) 425*0a1bf553SMark Brown { 426*0a1bf553SMark Brown struct snd_soc_codec *codec = codec_dai->codec; 427*0a1bf553SMark Brown u16 iface = 0; 428*0a1bf553SMark Brown u16 clk = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1fe; 429*0a1bf553SMark Brown 430*0a1bf553SMark Brown /* set master/slave audio interface */ 431*0a1bf553SMark Brown switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 432*0a1bf553SMark Brown case SND_SOC_DAIFMT_CBM_CFM: 433*0a1bf553SMark Brown clk |= 0x0001; 434*0a1bf553SMark Brown break; 435*0a1bf553SMark Brown case SND_SOC_DAIFMT_CBS_CFS: 436*0a1bf553SMark Brown break; 437*0a1bf553SMark Brown default: 438*0a1bf553SMark Brown return -EINVAL; 439*0a1bf553SMark Brown } 440*0a1bf553SMark Brown 441*0a1bf553SMark Brown /* interface format */ 442*0a1bf553SMark Brown switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 443*0a1bf553SMark Brown case SND_SOC_DAIFMT_I2S: 444*0a1bf553SMark Brown iface |= 0x0010; 445*0a1bf553SMark Brown break; 446*0a1bf553SMark Brown case SND_SOC_DAIFMT_RIGHT_J: 447*0a1bf553SMark Brown break; 448*0a1bf553SMark Brown case SND_SOC_DAIFMT_LEFT_J: 449*0a1bf553SMark Brown iface |= 0x0008; 450*0a1bf553SMark Brown break; 451*0a1bf553SMark Brown case SND_SOC_DAIFMT_DSP_A: 452*0a1bf553SMark Brown iface |= 0x00018; 453*0a1bf553SMark Brown break; 454*0a1bf553SMark Brown default: 455*0a1bf553SMark Brown return -EINVAL; 456*0a1bf553SMark Brown } 457*0a1bf553SMark Brown 458*0a1bf553SMark Brown /* clock inversion */ 459*0a1bf553SMark Brown switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 460*0a1bf553SMark Brown case SND_SOC_DAIFMT_NB_NF: 461*0a1bf553SMark Brown break; 462*0a1bf553SMark Brown case SND_SOC_DAIFMT_IB_IF: 463*0a1bf553SMark Brown iface |= 0x0180; 464*0a1bf553SMark Brown break; 465*0a1bf553SMark Brown case SND_SOC_DAIFMT_IB_NF: 466*0a1bf553SMark Brown iface |= 0x0100; 467*0a1bf553SMark Brown break; 468*0a1bf553SMark Brown case SND_SOC_DAIFMT_NB_IF: 469*0a1bf553SMark Brown iface |= 0x0080; 470*0a1bf553SMark Brown break; 471*0a1bf553SMark Brown default: 472*0a1bf553SMark Brown return -EINVAL; 473*0a1bf553SMark Brown } 474*0a1bf553SMark Brown 475*0a1bf553SMark Brown wm8974_write(codec, WM8974_IFACE, iface); 476*0a1bf553SMark Brown wm8974_write(codec, WM8974_CLOCK, clk); 477*0a1bf553SMark Brown return 0; 478*0a1bf553SMark Brown } 479*0a1bf553SMark Brown 480*0a1bf553SMark Brown static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream, 481*0a1bf553SMark Brown struct snd_pcm_hw_params *params, 482*0a1bf553SMark Brown struct snd_soc_dai *dai) 483*0a1bf553SMark Brown { 484*0a1bf553SMark Brown struct snd_soc_codec *codec = dai->codec; 485*0a1bf553SMark Brown u16 iface = wm8974_read_reg_cache(codec, WM8974_IFACE) & 0x19f; 486*0a1bf553SMark Brown u16 adn = wm8974_read_reg_cache(codec, WM8974_ADD) & 0x1f1; 487*0a1bf553SMark Brown 488*0a1bf553SMark Brown /* bit size */ 489*0a1bf553SMark Brown switch (params_format(params)) { 490*0a1bf553SMark Brown case SNDRV_PCM_FORMAT_S16_LE: 491*0a1bf553SMark Brown break; 492*0a1bf553SMark Brown case SNDRV_PCM_FORMAT_S20_3LE: 493*0a1bf553SMark Brown iface |= 0x0020; 494*0a1bf553SMark Brown break; 495*0a1bf553SMark Brown case SNDRV_PCM_FORMAT_S24_LE: 496*0a1bf553SMark Brown iface |= 0x0040; 497*0a1bf553SMark Brown break; 498*0a1bf553SMark Brown case SNDRV_PCM_FORMAT_S32_LE: 499*0a1bf553SMark Brown iface |= 0x0060; 500*0a1bf553SMark Brown break; 501*0a1bf553SMark Brown } 502*0a1bf553SMark Brown 503*0a1bf553SMark Brown /* filter coefficient */ 504*0a1bf553SMark Brown switch (params_rate(params)) { 505*0a1bf553SMark Brown case SNDRV_PCM_RATE_8000: 506*0a1bf553SMark Brown adn |= 0x5 << 1; 507*0a1bf553SMark Brown break; 508*0a1bf553SMark Brown case SNDRV_PCM_RATE_11025: 509*0a1bf553SMark Brown adn |= 0x4 << 1; 510*0a1bf553SMark Brown break; 511*0a1bf553SMark Brown case SNDRV_PCM_RATE_16000: 512*0a1bf553SMark Brown adn |= 0x3 << 1; 513*0a1bf553SMark Brown break; 514*0a1bf553SMark Brown case SNDRV_PCM_RATE_22050: 515*0a1bf553SMark Brown adn |= 0x2 << 1; 516*0a1bf553SMark Brown break; 517*0a1bf553SMark Brown case SNDRV_PCM_RATE_32000: 518*0a1bf553SMark Brown adn |= 0x1 << 1; 519*0a1bf553SMark Brown break; 520*0a1bf553SMark Brown case SNDRV_PCM_RATE_44100: 521*0a1bf553SMark Brown break; 522*0a1bf553SMark Brown } 523*0a1bf553SMark Brown 524*0a1bf553SMark Brown wm8974_write(codec, WM8974_IFACE, iface); 525*0a1bf553SMark Brown wm8974_write(codec, WM8974_ADD, adn); 526*0a1bf553SMark Brown return 0; 527*0a1bf553SMark Brown } 528*0a1bf553SMark Brown 529*0a1bf553SMark Brown static int wm8974_mute(struct snd_soc_dai *dai, int mute) 530*0a1bf553SMark Brown { 531*0a1bf553SMark Brown struct snd_soc_codec *codec = dai->codec; 532*0a1bf553SMark Brown u16 mute_reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0xffbf; 533*0a1bf553SMark Brown 534*0a1bf553SMark Brown if(mute) 535*0a1bf553SMark Brown wm8974_write(codec, WM8974_DAC, mute_reg | 0x40); 536*0a1bf553SMark Brown else 537*0a1bf553SMark Brown wm8974_write(codec, WM8974_DAC, mute_reg); 538*0a1bf553SMark Brown return 0; 539*0a1bf553SMark Brown } 540*0a1bf553SMark Brown 541*0a1bf553SMark Brown /* liam need to make this lower power with dapm */ 542*0a1bf553SMark Brown static int wm8974_set_bias_level(struct snd_soc_codec *codec, 543*0a1bf553SMark Brown enum snd_soc_bias_level level) 544*0a1bf553SMark Brown { 545*0a1bf553SMark Brown switch (level) { 546*0a1bf553SMark Brown case SND_SOC_BIAS_ON: 547*0a1bf553SMark Brown wm8974_write(codec, WM8974_POWER1, 0x1ff); 548*0a1bf553SMark Brown wm8974_write(codec, WM8974_POWER2, 0x1ff); 549*0a1bf553SMark Brown wm8974_write(codec, WM8974_POWER3, 0x1ff); 550*0a1bf553SMark Brown break; 551*0a1bf553SMark Brown case SND_SOC_BIAS_PREPARE: 552*0a1bf553SMark Brown break; 553*0a1bf553SMark Brown case SND_SOC_BIAS_STANDBY: 554*0a1bf553SMark Brown break; 555*0a1bf553SMark Brown case SND_SOC_BIAS_OFF: 556*0a1bf553SMark Brown wm8974_write(codec, WM8974_POWER1, 0x0); 557*0a1bf553SMark Brown wm8974_write(codec, WM8974_POWER2, 0x0); 558*0a1bf553SMark Brown wm8974_write(codec, WM8974_POWER3, 0x0); 559*0a1bf553SMark Brown break; 560*0a1bf553SMark Brown } 561*0a1bf553SMark Brown codec->bias_level = level; 562*0a1bf553SMark Brown return 0; 563*0a1bf553SMark Brown } 564*0a1bf553SMark Brown 565*0a1bf553SMark Brown #define WM8974_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ 566*0a1bf553SMark Brown SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ 567*0a1bf553SMark Brown SNDRV_PCM_RATE_48000) 568*0a1bf553SMark Brown 569*0a1bf553SMark Brown #define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 570*0a1bf553SMark Brown SNDRV_PCM_FMTBIT_S24_LE) 571*0a1bf553SMark Brown 572*0a1bf553SMark Brown static struct snd_soc_dai_ops wm8974_ops = { 573*0a1bf553SMark Brown .hw_params = wm8974_pcm_hw_params, 574*0a1bf553SMark Brown .digital_mute = wm8974_mute, 575*0a1bf553SMark Brown .set_fmt = wm8974_set_dai_fmt, 576*0a1bf553SMark Brown .set_clkdiv = wm8974_set_dai_clkdiv, 577*0a1bf553SMark Brown .set_pll = wm8974_set_dai_pll, 578*0a1bf553SMark Brown }; 579*0a1bf553SMark Brown 580*0a1bf553SMark Brown struct snd_soc_dai wm8974_dai = { 581*0a1bf553SMark Brown .name = "WM8974 HiFi", 582*0a1bf553SMark Brown .playback = { 583*0a1bf553SMark Brown .stream_name = "Playback", 584*0a1bf553SMark Brown .channels_min = 1, 585*0a1bf553SMark Brown .channels_max = 1, 586*0a1bf553SMark Brown .rates = WM8974_RATES, 587*0a1bf553SMark Brown .formats = WM8974_FORMATS,}, 588*0a1bf553SMark Brown .capture = { 589*0a1bf553SMark Brown .stream_name = "Capture", 590*0a1bf553SMark Brown .channels_min = 1, 591*0a1bf553SMark Brown .channels_max = 1, 592*0a1bf553SMark Brown .rates = WM8974_RATES, 593*0a1bf553SMark Brown .formats = WM8974_FORMATS,}, 594*0a1bf553SMark Brown .ops = &wm8974_ops, 595*0a1bf553SMark Brown }; 596*0a1bf553SMark Brown EXPORT_SYMBOL_GPL(wm8974_dai); 597*0a1bf553SMark Brown 598*0a1bf553SMark Brown static int wm8974_suspend(struct platform_device *pdev, pm_message_t state) 599*0a1bf553SMark Brown { 600*0a1bf553SMark Brown struct snd_soc_device *socdev = platform_get_drvdata(pdev); 601*0a1bf553SMark Brown struct snd_soc_codec *codec = socdev->card->codec; 602*0a1bf553SMark Brown 603*0a1bf553SMark Brown wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF); 604*0a1bf553SMark Brown return 0; 605*0a1bf553SMark Brown } 606*0a1bf553SMark Brown 607*0a1bf553SMark Brown static int wm8974_resume(struct platform_device *pdev) 608*0a1bf553SMark Brown { 609*0a1bf553SMark Brown struct snd_soc_device *socdev = platform_get_drvdata(pdev); 610*0a1bf553SMark Brown struct snd_soc_codec *codec = socdev->card->codec; 611*0a1bf553SMark Brown int i; 612*0a1bf553SMark Brown u8 data[2]; 613*0a1bf553SMark Brown u16 *cache = codec->reg_cache; 614*0a1bf553SMark Brown 615*0a1bf553SMark Brown /* Sync reg_cache with the hardware */ 616*0a1bf553SMark Brown for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) { 617*0a1bf553SMark Brown data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); 618*0a1bf553SMark Brown data[1] = cache[i] & 0x00ff; 619*0a1bf553SMark Brown codec->hw_write(codec->control_data, data, 2); 620*0a1bf553SMark Brown } 621*0a1bf553SMark Brown wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 622*0a1bf553SMark Brown wm8974_set_bias_level(codec, codec->suspend_bias_level); 623*0a1bf553SMark Brown return 0; 624*0a1bf553SMark Brown } 625*0a1bf553SMark Brown 626*0a1bf553SMark Brown /* 627*0a1bf553SMark Brown * initialise the WM8974 driver 628*0a1bf553SMark Brown * register the mixer and dsp interfaces with the kernel 629*0a1bf553SMark Brown */ 630*0a1bf553SMark Brown static int wm8974_init(struct snd_soc_device *socdev) 631*0a1bf553SMark Brown { 632*0a1bf553SMark Brown struct snd_soc_codec *codec = socdev->card->codec; 633*0a1bf553SMark Brown int ret = 0; 634*0a1bf553SMark Brown 635*0a1bf553SMark Brown codec->name = "WM8974"; 636*0a1bf553SMark Brown codec->owner = THIS_MODULE; 637*0a1bf553SMark Brown codec->read = wm8974_read_reg_cache; 638*0a1bf553SMark Brown codec->write = wm8974_write; 639*0a1bf553SMark Brown codec->set_bias_level = wm8974_set_bias_level; 640*0a1bf553SMark Brown codec->dai = &wm8974_dai; 641*0a1bf553SMark Brown codec->num_dai = 1; 642*0a1bf553SMark Brown codec->reg_cache_size = ARRAY_SIZE(wm8974_reg); 643*0a1bf553SMark Brown codec->reg_cache = kmemdup(wm8974_reg, sizeof(wm8974_reg), GFP_KERNEL); 644*0a1bf553SMark Brown 645*0a1bf553SMark Brown if (codec->reg_cache == NULL) 646*0a1bf553SMark Brown return -ENOMEM; 647*0a1bf553SMark Brown 648*0a1bf553SMark Brown wm8974_reset(codec); 649*0a1bf553SMark Brown 650*0a1bf553SMark Brown /* register pcms */ 651*0a1bf553SMark Brown ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); 652*0a1bf553SMark Brown if(ret < 0) { 653*0a1bf553SMark Brown printk(KERN_ERR "wm8974: failed to create pcms\n"); 654*0a1bf553SMark Brown goto pcm_err; 655*0a1bf553SMark Brown } 656*0a1bf553SMark Brown 657*0a1bf553SMark Brown /* power on device */ 658*0a1bf553SMark Brown wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 659*0a1bf553SMark Brown wm8974_add_controls(codec); 660*0a1bf553SMark Brown wm8974_add_widgets(codec); 661*0a1bf553SMark Brown ret = snd_soc_init_card(socdev); 662*0a1bf553SMark Brown if (ret < 0) { 663*0a1bf553SMark Brown printk(KERN_ERR "wm8974: failed to register card\n"); 664*0a1bf553SMark Brown goto card_err; 665*0a1bf553SMark Brown } 666*0a1bf553SMark Brown return ret; 667*0a1bf553SMark Brown 668*0a1bf553SMark Brown card_err: 669*0a1bf553SMark Brown snd_soc_free_pcms(socdev); 670*0a1bf553SMark Brown snd_soc_dapm_free(socdev); 671*0a1bf553SMark Brown pcm_err: 672*0a1bf553SMark Brown kfree(codec->reg_cache); 673*0a1bf553SMark Brown return ret; 674*0a1bf553SMark Brown } 675*0a1bf553SMark Brown 676*0a1bf553SMark Brown static struct snd_soc_device *wm8974_socdev; 677*0a1bf553SMark Brown 678*0a1bf553SMark Brown #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) 679*0a1bf553SMark Brown 680*0a1bf553SMark Brown /* 681*0a1bf553SMark Brown * WM8974 2 wire address is 0x1a 682*0a1bf553SMark Brown */ 683*0a1bf553SMark Brown #define I2C_DRIVERID_WM8974 0xfefe /* liam - need a proper id */ 684*0a1bf553SMark Brown 685*0a1bf553SMark Brown static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; 686*0a1bf553SMark Brown 687*0a1bf553SMark Brown /* Magic definition of all other variables and things */ 688*0a1bf553SMark Brown I2C_CLIENT_INSMOD; 689*0a1bf553SMark Brown 690*0a1bf553SMark Brown static struct i2c_driver wm8974_i2c_driver; 691*0a1bf553SMark Brown static struct i2c_client client_template; 692*0a1bf553SMark Brown 693*0a1bf553SMark Brown /* If the i2c layer weren't so broken, we could pass this kind of data 694*0a1bf553SMark Brown around */ 695*0a1bf553SMark Brown 696*0a1bf553SMark Brown static int wm8974_codec_probe(struct i2c_adapter *adap, int addr, int kind) 697*0a1bf553SMark Brown { 698*0a1bf553SMark Brown struct snd_soc_device *socdev = wm8974_socdev; 699*0a1bf553SMark Brown struct wm8974_setup_data *setup = socdev->codec_data; 700*0a1bf553SMark Brown struct snd_soc_codec *codec = socdev->card->codec; 701*0a1bf553SMark Brown struct i2c_client *i2c; 702*0a1bf553SMark Brown int ret; 703*0a1bf553SMark Brown 704*0a1bf553SMark Brown if (addr != setup->i2c_address) 705*0a1bf553SMark Brown return -ENODEV; 706*0a1bf553SMark Brown 707*0a1bf553SMark Brown client_template.adapter = adap; 708*0a1bf553SMark Brown client_template.addr = addr; 709*0a1bf553SMark Brown 710*0a1bf553SMark Brown i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); 711*0a1bf553SMark Brown if (i2c == NULL) { 712*0a1bf553SMark Brown kfree(codec); 713*0a1bf553SMark Brown return -ENOMEM; 714*0a1bf553SMark Brown } 715*0a1bf553SMark Brown i2c_set_clientdata(i2c, codec); 716*0a1bf553SMark Brown codec->control_data = i2c; 717*0a1bf553SMark Brown 718*0a1bf553SMark Brown ret = i2c_attach_client(i2c); 719*0a1bf553SMark Brown if (ret < 0) { 720*0a1bf553SMark Brown pr_err("failed to attach codec at addr %x\n", addr); 721*0a1bf553SMark Brown goto err; 722*0a1bf553SMark Brown } 723*0a1bf553SMark Brown 724*0a1bf553SMark Brown ret = wm8974_init(socdev); 725*0a1bf553SMark Brown if (ret < 0) { 726*0a1bf553SMark Brown pr_err("failed to initialise WM8974\n"); 727*0a1bf553SMark Brown goto err; 728*0a1bf553SMark Brown } 729*0a1bf553SMark Brown return ret; 730*0a1bf553SMark Brown 731*0a1bf553SMark Brown err: 732*0a1bf553SMark Brown kfree(codec); 733*0a1bf553SMark Brown kfree(i2c); 734*0a1bf553SMark Brown return ret; 735*0a1bf553SMark Brown } 736*0a1bf553SMark Brown 737*0a1bf553SMark Brown static int wm8974_i2c_detach(struct i2c_client *client) 738*0a1bf553SMark Brown { 739*0a1bf553SMark Brown struct snd_soc_codec *codec = i2c_get_clientdata(client); 740*0a1bf553SMark Brown i2c_detach_client(client); 741*0a1bf553SMark Brown kfree(codec->reg_cache); 742*0a1bf553SMark Brown kfree(client); 743*0a1bf553SMark Brown return 0; 744*0a1bf553SMark Brown } 745*0a1bf553SMark Brown 746*0a1bf553SMark Brown static int wm8974_i2c_attach(struct i2c_adapter *adap) 747*0a1bf553SMark Brown { 748*0a1bf553SMark Brown return i2c_probe(adap, &addr_data, wm8974_codec_probe); 749*0a1bf553SMark Brown } 750*0a1bf553SMark Brown 751*0a1bf553SMark Brown /* corgi i2c codec control layer */ 752*0a1bf553SMark Brown static struct i2c_driver wm8974_i2c_driver = { 753*0a1bf553SMark Brown .driver = { 754*0a1bf553SMark Brown .name = "WM8974 I2C Codec", 755*0a1bf553SMark Brown .owner = THIS_MODULE, 756*0a1bf553SMark Brown }, 757*0a1bf553SMark Brown .id = I2C_DRIVERID_WM8974, 758*0a1bf553SMark Brown .attach_adapter = wm8974_i2c_attach, 759*0a1bf553SMark Brown .detach_client = wm8974_i2c_detach, 760*0a1bf553SMark Brown .command = NULL, 761*0a1bf553SMark Brown }; 762*0a1bf553SMark Brown 763*0a1bf553SMark Brown static struct i2c_client client_template = { 764*0a1bf553SMark Brown .name = "WM8974", 765*0a1bf553SMark Brown .driver = &wm8974_i2c_driver, 766*0a1bf553SMark Brown }; 767*0a1bf553SMark Brown #endif 768*0a1bf553SMark Brown 769*0a1bf553SMark Brown static int wm8974_probe(struct platform_device *pdev) 770*0a1bf553SMark Brown { 771*0a1bf553SMark Brown struct snd_soc_device *socdev = platform_get_drvdata(pdev); 772*0a1bf553SMark Brown struct wm8974_setup_data *setup; 773*0a1bf553SMark Brown struct snd_soc_codec *codec; 774*0a1bf553SMark Brown int ret = 0; 775*0a1bf553SMark Brown 776*0a1bf553SMark Brown pr_info("WM8974 Audio Codec %s", WM8974_VERSION); 777*0a1bf553SMark Brown 778*0a1bf553SMark Brown setup = socdev->codec_data; 779*0a1bf553SMark Brown codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); 780*0a1bf553SMark Brown if (codec == NULL) 781*0a1bf553SMark Brown return -ENOMEM; 782*0a1bf553SMark Brown 783*0a1bf553SMark Brown socdev->card->codec = codec; 784*0a1bf553SMark Brown mutex_init(&codec->mutex); 785*0a1bf553SMark Brown INIT_LIST_HEAD(&codec->dapm_widgets); 786*0a1bf553SMark Brown INIT_LIST_HEAD(&codec->dapm_paths); 787*0a1bf553SMark Brown 788*0a1bf553SMark Brown wm8974_socdev = socdev; 789*0a1bf553SMark Brown #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) 790*0a1bf553SMark Brown if (setup->i2c_address) { 791*0a1bf553SMark Brown normal_i2c[0] = setup->i2c_address; 792*0a1bf553SMark Brown codec->hw_write = (hw_write_t)i2c_master_send; 793*0a1bf553SMark Brown ret = i2c_add_driver(&wm8974_i2c_driver); 794*0a1bf553SMark Brown if (ret != 0) 795*0a1bf553SMark Brown printk(KERN_ERR "can't add i2c driver"); 796*0a1bf553SMark Brown } 797*0a1bf553SMark Brown #else 798*0a1bf553SMark Brown /* Add other interfaces here */ 799*0a1bf553SMark Brown #endif 800*0a1bf553SMark Brown return ret; 801*0a1bf553SMark Brown } 802*0a1bf553SMark Brown 803*0a1bf553SMark Brown /* power down chip */ 804*0a1bf553SMark Brown static int wm8974_remove(struct platform_device *pdev) 805*0a1bf553SMark Brown { 806*0a1bf553SMark Brown struct snd_soc_device *socdev = platform_get_drvdata(pdev); 807*0a1bf553SMark Brown struct snd_soc_codec *codec = socdev->card->codec; 808*0a1bf553SMark Brown 809*0a1bf553SMark Brown if (codec->control_data) 810*0a1bf553SMark Brown wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF); 811*0a1bf553SMark Brown 812*0a1bf553SMark Brown snd_soc_free_pcms(socdev); 813*0a1bf553SMark Brown snd_soc_dapm_free(socdev); 814*0a1bf553SMark Brown #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) 815*0a1bf553SMark Brown i2c_del_driver(&wm8974_i2c_driver); 816*0a1bf553SMark Brown #endif 817*0a1bf553SMark Brown kfree(codec); 818*0a1bf553SMark Brown 819*0a1bf553SMark Brown return 0; 820*0a1bf553SMark Brown } 821*0a1bf553SMark Brown 822*0a1bf553SMark Brown struct snd_soc_codec_device soc_codec_dev_wm8974 = { 823*0a1bf553SMark Brown .probe = wm8974_probe, 824*0a1bf553SMark Brown .remove = wm8974_remove, 825*0a1bf553SMark Brown .suspend = wm8974_suspend, 826*0a1bf553SMark Brown .resume = wm8974_resume, 827*0a1bf553SMark Brown }; 828*0a1bf553SMark Brown EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974); 829*0a1bf553SMark Brown 830*0a1bf553SMark Brown static int __init wm8974_modinit(void) 831*0a1bf553SMark Brown { 832*0a1bf553SMark Brown return snd_soc_register_dai(&wm8974_dai); 833*0a1bf553SMark Brown } 834*0a1bf553SMark Brown module_init(wm8974_modinit); 835*0a1bf553SMark Brown 836*0a1bf553SMark Brown static void __exit wm8974_exit(void) 837*0a1bf553SMark Brown { 838*0a1bf553SMark Brown snd_soc_unregister_dai(&wm8974_dai); 839*0a1bf553SMark Brown } 840*0a1bf553SMark Brown module_exit(wm8974_exit); 841*0a1bf553SMark Brown 842*0a1bf553SMark Brown MODULE_DESCRIPTION("ASoC WM8974 driver"); 843*0a1bf553SMark Brown MODULE_AUTHOR("Liam Girdwood"); 844*0a1bf553SMark Brown MODULE_LICENSE("GPL"); 845