1*5409fb4eSMark Brown /* 2*5409fb4eSMark Brown * wm8988.c -- WM8988 ALSA SoC audio driver 3*5409fb4eSMark Brown * 4*5409fb4eSMark Brown * Copyright 2009 Wolfson Microelectronics plc 5*5409fb4eSMark Brown * Copyright 2005 Openedhand Ltd. 6*5409fb4eSMark Brown * 7*5409fb4eSMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 8*5409fb4eSMark Brown * 9*5409fb4eSMark Brown * This program is free software; you can redistribute it and/or modify 10*5409fb4eSMark Brown * it under the terms of the GNU General Public License version 2 as 11*5409fb4eSMark Brown * published by the Free Software Foundation. 12*5409fb4eSMark Brown */ 13*5409fb4eSMark Brown 14*5409fb4eSMark Brown #include <linux/module.h> 15*5409fb4eSMark Brown #include <linux/moduleparam.h> 16*5409fb4eSMark Brown #include <linux/init.h> 17*5409fb4eSMark Brown #include <linux/delay.h> 18*5409fb4eSMark Brown #include <linux/pm.h> 19*5409fb4eSMark Brown #include <linux/i2c.h> 20*5409fb4eSMark Brown #include <linux/spi/spi.h> 21*5409fb4eSMark Brown #include <linux/platform_device.h> 22*5409fb4eSMark Brown #include <sound/core.h> 23*5409fb4eSMark Brown #include <sound/pcm.h> 24*5409fb4eSMark Brown #include <sound/pcm_params.h> 25*5409fb4eSMark Brown #include <sound/tlv.h> 26*5409fb4eSMark Brown #include <sound/soc.h> 27*5409fb4eSMark Brown #include <sound/soc-dapm.h> 28*5409fb4eSMark Brown #include <sound/initval.h> 29*5409fb4eSMark Brown 30*5409fb4eSMark Brown #include "wm8988.h" 31*5409fb4eSMark Brown 32*5409fb4eSMark Brown /* 33*5409fb4eSMark Brown * wm8988 register cache 34*5409fb4eSMark Brown * We can't read the WM8988 register space when we 35*5409fb4eSMark Brown * are using 2 wire for device control, so we cache them instead. 36*5409fb4eSMark Brown */ 37*5409fb4eSMark Brown static const u16 wm8988_reg[] = { 38*5409fb4eSMark Brown 0x0097, 0x0097, 0x0079, 0x0079, /* 0 */ 39*5409fb4eSMark Brown 0x0000, 0x0008, 0x0000, 0x000a, /* 4 */ 40*5409fb4eSMark Brown 0x0000, 0x0000, 0x00ff, 0x00ff, /* 8 */ 41*5409fb4eSMark Brown 0x000f, 0x000f, 0x0000, 0x0000, /* 12 */ 42*5409fb4eSMark Brown 0x0000, 0x007b, 0x0000, 0x0032, /* 16 */ 43*5409fb4eSMark Brown 0x0000, 0x00c3, 0x00c3, 0x00c0, /* 20 */ 44*5409fb4eSMark Brown 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */ 45*5409fb4eSMark Brown 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */ 46*5409fb4eSMark Brown 0x0000, 0x0000, 0x0050, 0x0050, /* 32 */ 47*5409fb4eSMark Brown 0x0050, 0x0050, 0x0050, 0x0050, /* 36 */ 48*5409fb4eSMark Brown 0x0079, 0x0079, 0x0079, /* 40 */ 49*5409fb4eSMark Brown }; 50*5409fb4eSMark Brown 51*5409fb4eSMark Brown /* codec private data */ 52*5409fb4eSMark Brown struct wm8988_priv { 53*5409fb4eSMark Brown unsigned int sysclk; 54*5409fb4eSMark Brown struct snd_soc_codec codec; 55*5409fb4eSMark Brown struct snd_pcm_hw_constraint_list *sysclk_constraints; 56*5409fb4eSMark Brown u16 reg_cache[WM8988_NUM_REG]; 57*5409fb4eSMark Brown }; 58*5409fb4eSMark Brown 59*5409fb4eSMark Brown 60*5409fb4eSMark Brown /* 61*5409fb4eSMark Brown * read wm8988 register cache 62*5409fb4eSMark Brown */ 63*5409fb4eSMark Brown static inline unsigned int wm8988_read_reg_cache(struct snd_soc_codec *codec, 64*5409fb4eSMark Brown unsigned int reg) 65*5409fb4eSMark Brown { 66*5409fb4eSMark Brown u16 *cache = codec->reg_cache; 67*5409fb4eSMark Brown if (reg > WM8988_NUM_REG) 68*5409fb4eSMark Brown return -1; 69*5409fb4eSMark Brown return cache[reg]; 70*5409fb4eSMark Brown } 71*5409fb4eSMark Brown 72*5409fb4eSMark Brown /* 73*5409fb4eSMark Brown * write wm8988 register cache 74*5409fb4eSMark Brown */ 75*5409fb4eSMark Brown static inline void wm8988_write_reg_cache(struct snd_soc_codec *codec, 76*5409fb4eSMark Brown unsigned int reg, unsigned int value) 77*5409fb4eSMark Brown { 78*5409fb4eSMark Brown u16 *cache = codec->reg_cache; 79*5409fb4eSMark Brown if (reg > WM8988_NUM_REG) 80*5409fb4eSMark Brown return; 81*5409fb4eSMark Brown cache[reg] = value; 82*5409fb4eSMark Brown } 83*5409fb4eSMark Brown 84*5409fb4eSMark Brown static int wm8988_write(struct snd_soc_codec *codec, unsigned int reg, 85*5409fb4eSMark Brown unsigned int value) 86*5409fb4eSMark Brown { 87*5409fb4eSMark Brown u8 data[2]; 88*5409fb4eSMark Brown 89*5409fb4eSMark Brown /* data is 90*5409fb4eSMark Brown * D15..D9 WM8753 register offset 91*5409fb4eSMark Brown * D8...D0 register data 92*5409fb4eSMark Brown */ 93*5409fb4eSMark Brown data[0] = (reg << 1) | ((value >> 8) & 0x0001); 94*5409fb4eSMark Brown data[1] = value & 0x00ff; 95*5409fb4eSMark Brown 96*5409fb4eSMark Brown wm8988_write_reg_cache(codec, reg, value); 97*5409fb4eSMark Brown if (codec->hw_write(codec->control_data, data, 2) == 2) 98*5409fb4eSMark Brown return 0; 99*5409fb4eSMark Brown else 100*5409fb4eSMark Brown return -EIO; 101*5409fb4eSMark Brown } 102*5409fb4eSMark Brown 103*5409fb4eSMark Brown #define wm8988_reset(c) wm8988_write(c, WM8988_RESET, 0) 104*5409fb4eSMark Brown 105*5409fb4eSMark Brown /* 106*5409fb4eSMark Brown * WM8988 Controls 107*5409fb4eSMark Brown */ 108*5409fb4eSMark Brown 109*5409fb4eSMark Brown static const char *bass_boost_txt[] = {"Linear Control", "Adaptive Boost"}; 110*5409fb4eSMark Brown static const struct soc_enum bass_boost = 111*5409fb4eSMark Brown SOC_ENUM_SINGLE(WM8988_BASS, 7, 2, bass_boost_txt); 112*5409fb4eSMark Brown 113*5409fb4eSMark Brown static const char *bass_filter_txt[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" }; 114*5409fb4eSMark Brown static const struct soc_enum bass_filter = 115*5409fb4eSMark Brown SOC_ENUM_SINGLE(WM8988_BASS, 6, 2, bass_filter_txt); 116*5409fb4eSMark Brown 117*5409fb4eSMark Brown static const char *treble_txt[] = {"8kHz", "4kHz"}; 118*5409fb4eSMark Brown static const struct soc_enum treble = 119*5409fb4eSMark Brown SOC_ENUM_SINGLE(WM8988_TREBLE, 6, 2, treble_txt); 120*5409fb4eSMark Brown 121*5409fb4eSMark Brown static const char *stereo_3d_lc_txt[] = {"200Hz", "500Hz"}; 122*5409fb4eSMark Brown static const struct soc_enum stereo_3d_lc = 123*5409fb4eSMark Brown SOC_ENUM_SINGLE(WM8988_3D, 5, 2, stereo_3d_lc_txt); 124*5409fb4eSMark Brown 125*5409fb4eSMark Brown static const char *stereo_3d_uc_txt[] = {"2.2kHz", "1.5kHz"}; 126*5409fb4eSMark Brown static const struct soc_enum stereo_3d_uc = 127*5409fb4eSMark Brown SOC_ENUM_SINGLE(WM8988_3D, 6, 2, stereo_3d_uc_txt); 128*5409fb4eSMark Brown 129*5409fb4eSMark Brown static const char *stereo_3d_func_txt[] = {"Capture", "Playback"}; 130*5409fb4eSMark Brown static const struct soc_enum stereo_3d_func = 131*5409fb4eSMark Brown SOC_ENUM_SINGLE(WM8988_3D, 7, 2, stereo_3d_func_txt); 132*5409fb4eSMark Brown 133*5409fb4eSMark Brown static const char *alc_func_txt[] = {"Off", "Right", "Left", "Stereo"}; 134*5409fb4eSMark Brown static const struct soc_enum alc_func = 135*5409fb4eSMark Brown SOC_ENUM_SINGLE(WM8988_ALC1, 7, 4, alc_func_txt); 136*5409fb4eSMark Brown 137*5409fb4eSMark Brown static const char *ng_type_txt[] = {"Constant PGA Gain", 138*5409fb4eSMark Brown "Mute ADC Output"}; 139*5409fb4eSMark Brown static const struct soc_enum ng_type = 140*5409fb4eSMark Brown SOC_ENUM_SINGLE(WM8988_NGATE, 1, 2, ng_type_txt); 141*5409fb4eSMark Brown 142*5409fb4eSMark Brown static const char *deemph_txt[] = {"None", "32Khz", "44.1Khz", "48Khz"}; 143*5409fb4eSMark Brown static const struct soc_enum deemph = 144*5409fb4eSMark Brown SOC_ENUM_SINGLE(WM8988_ADCDAC, 1, 4, deemph_txt); 145*5409fb4eSMark Brown 146*5409fb4eSMark Brown static const char *adcpol_txt[] = {"Normal", "L Invert", "R Invert", 147*5409fb4eSMark Brown "L + R Invert"}; 148*5409fb4eSMark Brown static const struct soc_enum adcpol = 149*5409fb4eSMark Brown SOC_ENUM_SINGLE(WM8988_ADCDAC, 5, 4, adcpol_txt); 150*5409fb4eSMark Brown 151*5409fb4eSMark Brown static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0); 152*5409fb4eSMark Brown static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1); 153*5409fb4eSMark Brown static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); 154*5409fb4eSMark Brown static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); 155*5409fb4eSMark Brown static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0); 156*5409fb4eSMark Brown 157*5409fb4eSMark Brown static const struct snd_kcontrol_new wm8988_snd_controls[] = { 158*5409fb4eSMark Brown 159*5409fb4eSMark Brown SOC_ENUM("Bass Boost", bass_boost), 160*5409fb4eSMark Brown SOC_ENUM("Bass Filter", bass_filter), 161*5409fb4eSMark Brown SOC_SINGLE("Bass Volume", WM8988_BASS, 0, 15, 1), 162*5409fb4eSMark Brown 163*5409fb4eSMark Brown SOC_SINGLE("Treble Volume", WM8988_TREBLE, 0, 15, 0), 164*5409fb4eSMark Brown SOC_ENUM("Treble Cut-off", treble), 165*5409fb4eSMark Brown 166*5409fb4eSMark Brown SOC_SINGLE("3D Switch", WM8988_3D, 0, 1, 0), 167*5409fb4eSMark Brown SOC_SINGLE("3D Volume", WM8988_3D, 1, 15, 0), 168*5409fb4eSMark Brown SOC_ENUM("3D Lower Cut-off", stereo_3d_lc), 169*5409fb4eSMark Brown SOC_ENUM("3D Upper Cut-off", stereo_3d_uc), 170*5409fb4eSMark Brown SOC_ENUM("3D Mode", stereo_3d_func), 171*5409fb4eSMark Brown 172*5409fb4eSMark Brown SOC_SINGLE("ALC Capture Target Volume", WM8988_ALC1, 0, 7, 0), 173*5409fb4eSMark Brown SOC_SINGLE("ALC Capture Max Volume", WM8988_ALC1, 4, 7, 0), 174*5409fb4eSMark Brown SOC_ENUM("ALC Capture Function", alc_func), 175*5409fb4eSMark Brown SOC_SINGLE("ALC Capture ZC Switch", WM8988_ALC2, 7, 1, 0), 176*5409fb4eSMark Brown SOC_SINGLE("ALC Capture Hold Time", WM8988_ALC2, 0, 15, 0), 177*5409fb4eSMark Brown SOC_SINGLE("ALC Capture Decay Time", WM8988_ALC3, 4, 15, 0), 178*5409fb4eSMark Brown SOC_SINGLE("ALC Capture Attack Time", WM8988_ALC3, 0, 15, 0), 179*5409fb4eSMark Brown SOC_SINGLE("ALC Capture NG Threshold", WM8988_NGATE, 3, 31, 0), 180*5409fb4eSMark Brown SOC_ENUM("ALC Capture NG Type", ng_type), 181*5409fb4eSMark Brown SOC_SINGLE("ALC Capture NG Switch", WM8988_NGATE, 0, 1, 0), 182*5409fb4eSMark Brown 183*5409fb4eSMark Brown SOC_SINGLE("ZC Timeout Switch", WM8988_ADCTL1, 0, 1, 0), 184*5409fb4eSMark Brown 185*5409fb4eSMark Brown SOC_DOUBLE_R_TLV("Capture Digital Volume", WM8988_LADC, WM8988_RADC, 186*5409fb4eSMark Brown 0, 255, 0, adc_tlv), 187*5409fb4eSMark Brown SOC_DOUBLE_R_TLV("Capture Volume", WM8988_LINVOL, WM8988_RINVOL, 188*5409fb4eSMark Brown 0, 63, 0, pga_tlv), 189*5409fb4eSMark Brown SOC_DOUBLE_R("Capture ZC Switch", WM8988_LINVOL, WM8988_RINVOL, 6, 1, 0), 190*5409fb4eSMark Brown SOC_DOUBLE_R("Capture Switch", WM8988_LINVOL, WM8988_RINVOL, 7, 1, 1), 191*5409fb4eSMark Brown 192*5409fb4eSMark Brown SOC_ENUM("Playback De-emphasis", deemph), 193*5409fb4eSMark Brown 194*5409fb4eSMark Brown SOC_ENUM("Capture Polarity", adcpol), 195*5409fb4eSMark Brown SOC_SINGLE("Playback 6dB Attenuate", WM8988_ADCDAC, 7, 1, 0), 196*5409fb4eSMark Brown SOC_SINGLE("Capture 6dB Attenuate", WM8988_ADCDAC, 8, 1, 0), 197*5409fb4eSMark Brown 198*5409fb4eSMark Brown SOC_DOUBLE_R_TLV("PCM Volume", WM8988_LDAC, WM8988_RDAC, 0, 255, 0, dac_tlv), 199*5409fb4eSMark Brown 200*5409fb4eSMark Brown SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", WM8988_LOUTM1, 4, 7, 1, 201*5409fb4eSMark Brown bypass_tlv), 202*5409fb4eSMark Brown SOC_SINGLE_TLV("Left Mixer Right Bypass Volume", WM8988_LOUTM2, 4, 7, 1, 203*5409fb4eSMark Brown bypass_tlv), 204*5409fb4eSMark Brown SOC_SINGLE_TLV("Right Mixer Left Bypass Volume", WM8988_ROUTM1, 4, 7, 1, 205*5409fb4eSMark Brown bypass_tlv), 206*5409fb4eSMark Brown SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", WM8988_ROUTM2, 4, 7, 1, 207*5409fb4eSMark Brown bypass_tlv), 208*5409fb4eSMark Brown 209*5409fb4eSMark Brown SOC_DOUBLE_R("Output 1 Playback ZC Switch", WM8988_LOUT1V, 210*5409fb4eSMark Brown WM8988_ROUT1V, 7, 1, 0), 211*5409fb4eSMark Brown SOC_DOUBLE_R_TLV("Output 1 Playback Volume", WM8988_LOUT1V, WM8988_ROUT1V, 212*5409fb4eSMark Brown 0, 127, 0, out_tlv), 213*5409fb4eSMark Brown 214*5409fb4eSMark Brown SOC_DOUBLE_R("Output 2 Playback ZC Switch", WM8988_LOUT2V, 215*5409fb4eSMark Brown WM8988_ROUT2V, 7, 1, 0), 216*5409fb4eSMark Brown SOC_DOUBLE_R_TLV("Output 2 Playback Volume", WM8988_LOUT2V, WM8988_ROUT2V, 217*5409fb4eSMark Brown 0, 127, 0, out_tlv), 218*5409fb4eSMark Brown 219*5409fb4eSMark Brown }; 220*5409fb4eSMark Brown 221*5409fb4eSMark Brown /* 222*5409fb4eSMark Brown * DAPM Controls 223*5409fb4eSMark Brown */ 224*5409fb4eSMark Brown 225*5409fb4eSMark Brown static int wm8988_lrc_control(struct snd_soc_dapm_widget *w, 226*5409fb4eSMark Brown struct snd_kcontrol *kcontrol, int event) 227*5409fb4eSMark Brown { 228*5409fb4eSMark Brown struct snd_soc_codec *codec = w->codec; 229*5409fb4eSMark Brown u16 adctl2 = wm8988_read_reg_cache(codec, WM8988_ADCTL2); 230*5409fb4eSMark Brown 231*5409fb4eSMark Brown /* Use the DAC to gate LRC if active, otherwise use ADC */ 232*5409fb4eSMark Brown if (wm8988_read_reg_cache(codec, WM8988_PWR2) & 0x180) 233*5409fb4eSMark Brown adctl2 &= ~0x4; 234*5409fb4eSMark Brown else 235*5409fb4eSMark Brown adctl2 |= 0x4; 236*5409fb4eSMark Brown 237*5409fb4eSMark Brown return wm8988_write(codec, WM8988_ADCTL2, adctl2); 238*5409fb4eSMark Brown } 239*5409fb4eSMark Brown 240*5409fb4eSMark Brown static const char *wm8988_line_texts[] = { 241*5409fb4eSMark Brown "Line 1", "Line 2", "PGA", "Differential"}; 242*5409fb4eSMark Brown 243*5409fb4eSMark Brown static const unsigned int wm8988_line_values[] = { 244*5409fb4eSMark Brown 0, 1, 3, 4}; 245*5409fb4eSMark Brown 246*5409fb4eSMark Brown static const struct soc_enum wm8988_lline_enum = 247*5409fb4eSMark Brown SOC_VALUE_ENUM_SINGLE(WM8988_LOUTM1, 0, 7, 248*5409fb4eSMark Brown ARRAY_SIZE(wm8988_line_texts), 249*5409fb4eSMark Brown wm8988_line_texts, 250*5409fb4eSMark Brown wm8988_line_values); 251*5409fb4eSMark Brown static const struct snd_kcontrol_new wm8988_left_line_controls = 252*5409fb4eSMark Brown SOC_DAPM_VALUE_ENUM("Route", wm8988_lline_enum); 253*5409fb4eSMark Brown 254*5409fb4eSMark Brown static const struct soc_enum wm8988_rline_enum = 255*5409fb4eSMark Brown SOC_VALUE_ENUM_SINGLE(WM8988_ROUTM1, 0, 7, 256*5409fb4eSMark Brown ARRAY_SIZE(wm8988_line_texts), 257*5409fb4eSMark Brown wm8988_line_texts, 258*5409fb4eSMark Brown wm8988_line_values); 259*5409fb4eSMark Brown static const struct snd_kcontrol_new wm8988_right_line_controls = 260*5409fb4eSMark Brown SOC_DAPM_VALUE_ENUM("Route", wm8988_lline_enum); 261*5409fb4eSMark Brown 262*5409fb4eSMark Brown /* Left Mixer */ 263*5409fb4eSMark Brown static const struct snd_kcontrol_new wm8988_left_mixer_controls[] = { 264*5409fb4eSMark Brown SOC_DAPM_SINGLE("Playback Switch", WM8988_LOUTM1, 8, 1, 0), 265*5409fb4eSMark Brown SOC_DAPM_SINGLE("Left Bypass Switch", WM8988_LOUTM1, 7, 1, 0), 266*5409fb4eSMark Brown SOC_DAPM_SINGLE("Right Playback Switch", WM8988_LOUTM2, 8, 1, 0), 267*5409fb4eSMark Brown SOC_DAPM_SINGLE("Right Bypass Switch", WM8988_LOUTM2, 7, 1, 0), 268*5409fb4eSMark Brown }; 269*5409fb4eSMark Brown 270*5409fb4eSMark Brown /* Right Mixer */ 271*5409fb4eSMark Brown static const struct snd_kcontrol_new wm8988_right_mixer_controls[] = { 272*5409fb4eSMark Brown SOC_DAPM_SINGLE("Left Playback Switch", WM8988_ROUTM1, 8, 1, 0), 273*5409fb4eSMark Brown SOC_DAPM_SINGLE("Left Bypass Switch", WM8988_ROUTM1, 7, 1, 0), 274*5409fb4eSMark Brown SOC_DAPM_SINGLE("Playback Switch", WM8988_ROUTM2, 8, 1, 0), 275*5409fb4eSMark Brown SOC_DAPM_SINGLE("Right Bypass Switch", WM8988_ROUTM2, 7, 1, 0), 276*5409fb4eSMark Brown }; 277*5409fb4eSMark Brown 278*5409fb4eSMark Brown static const char *wm8988_pga_sel[] = {"Line 1", "Line 2", "Differential"}; 279*5409fb4eSMark Brown static const unsigned int wm8988_pga_val[] = { 0, 1, 3 }; 280*5409fb4eSMark Brown 281*5409fb4eSMark Brown /* Left PGA Mux */ 282*5409fb4eSMark Brown static const struct soc_enum wm8988_lpga_enum = 283*5409fb4eSMark Brown SOC_VALUE_ENUM_SINGLE(WM8988_LADCIN, 6, 3, 284*5409fb4eSMark Brown ARRAY_SIZE(wm8988_pga_sel), 285*5409fb4eSMark Brown wm8988_pga_sel, 286*5409fb4eSMark Brown wm8988_pga_val); 287*5409fb4eSMark Brown static const struct snd_kcontrol_new wm8988_left_pga_controls = 288*5409fb4eSMark Brown SOC_DAPM_VALUE_ENUM("Route", wm8988_lpga_enum); 289*5409fb4eSMark Brown 290*5409fb4eSMark Brown /* Right PGA Mux */ 291*5409fb4eSMark Brown static const struct soc_enum wm8988_rpga_enum = 292*5409fb4eSMark Brown SOC_VALUE_ENUM_SINGLE(WM8988_RADCIN, 6, 3, 293*5409fb4eSMark Brown ARRAY_SIZE(wm8988_pga_sel), 294*5409fb4eSMark Brown wm8988_pga_sel, 295*5409fb4eSMark Brown wm8988_pga_val); 296*5409fb4eSMark Brown static const struct snd_kcontrol_new wm8988_right_pga_controls = 297*5409fb4eSMark Brown SOC_DAPM_VALUE_ENUM("Route", wm8988_rpga_enum); 298*5409fb4eSMark Brown 299*5409fb4eSMark Brown /* Differential Mux */ 300*5409fb4eSMark Brown static const char *wm8988_diff_sel[] = {"Line 1", "Line 2"}; 301*5409fb4eSMark Brown static const struct soc_enum diffmux = 302*5409fb4eSMark Brown SOC_ENUM_SINGLE(WM8988_ADCIN, 8, 2, wm8988_diff_sel); 303*5409fb4eSMark Brown static const struct snd_kcontrol_new wm8988_diffmux_controls = 304*5409fb4eSMark Brown SOC_DAPM_ENUM("Route", diffmux); 305*5409fb4eSMark Brown 306*5409fb4eSMark Brown /* Mono ADC Mux */ 307*5409fb4eSMark Brown static const char *wm8988_mono_mux[] = {"Stereo", "Mono (Left)", 308*5409fb4eSMark Brown "Mono (Right)", "Digital Mono"}; 309*5409fb4eSMark Brown static const struct soc_enum monomux = 310*5409fb4eSMark Brown SOC_ENUM_SINGLE(WM8988_ADCIN, 6, 4, wm8988_mono_mux); 311*5409fb4eSMark Brown static const struct snd_kcontrol_new wm8988_monomux_controls = 312*5409fb4eSMark Brown SOC_DAPM_ENUM("Route", monomux); 313*5409fb4eSMark Brown 314*5409fb4eSMark Brown static const struct snd_soc_dapm_widget wm8988_dapm_widgets[] = { 315*5409fb4eSMark Brown SND_SOC_DAPM_MICBIAS("Mic Bias", WM8988_PWR1, 1, 0), 316*5409fb4eSMark Brown 317*5409fb4eSMark Brown SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, 318*5409fb4eSMark Brown &wm8988_diffmux_controls), 319*5409fb4eSMark Brown SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, 320*5409fb4eSMark Brown &wm8988_monomux_controls), 321*5409fb4eSMark Brown SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, 322*5409fb4eSMark Brown &wm8988_monomux_controls), 323*5409fb4eSMark Brown 324*5409fb4eSMark Brown SND_SOC_DAPM_MUX("Left PGA Mux", WM8988_PWR1, 5, 0, 325*5409fb4eSMark Brown &wm8988_left_pga_controls), 326*5409fb4eSMark Brown SND_SOC_DAPM_MUX("Right PGA Mux", WM8988_PWR1, 4, 0, 327*5409fb4eSMark Brown &wm8988_right_pga_controls), 328*5409fb4eSMark Brown 329*5409fb4eSMark Brown SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, 330*5409fb4eSMark Brown &wm8988_left_line_controls), 331*5409fb4eSMark Brown SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, 332*5409fb4eSMark Brown &wm8988_right_line_controls), 333*5409fb4eSMark Brown 334*5409fb4eSMark Brown SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8988_PWR1, 2, 0), 335*5409fb4eSMark Brown SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8988_PWR1, 3, 0), 336*5409fb4eSMark Brown 337*5409fb4eSMark Brown SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8988_PWR2, 7, 0), 338*5409fb4eSMark Brown SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8988_PWR2, 8, 0), 339*5409fb4eSMark Brown 340*5409fb4eSMark Brown SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, 341*5409fb4eSMark Brown &wm8988_left_mixer_controls[0], 342*5409fb4eSMark Brown ARRAY_SIZE(wm8988_left_mixer_controls)), 343*5409fb4eSMark Brown SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, 344*5409fb4eSMark Brown &wm8988_right_mixer_controls[0], 345*5409fb4eSMark Brown ARRAY_SIZE(wm8988_right_mixer_controls)), 346*5409fb4eSMark Brown 347*5409fb4eSMark Brown SND_SOC_DAPM_PGA("Right Out 2", WM8988_PWR2, 3, 0, NULL, 0), 348*5409fb4eSMark Brown SND_SOC_DAPM_PGA("Left Out 2", WM8988_PWR2, 4, 0, NULL, 0), 349*5409fb4eSMark Brown SND_SOC_DAPM_PGA("Right Out 1", WM8988_PWR2, 5, 0, NULL, 0), 350*5409fb4eSMark Brown SND_SOC_DAPM_PGA("Left Out 1", WM8988_PWR2, 6, 0, NULL, 0), 351*5409fb4eSMark Brown 352*5409fb4eSMark Brown SND_SOC_DAPM_POST("LRC control", wm8988_lrc_control), 353*5409fb4eSMark Brown 354*5409fb4eSMark Brown SND_SOC_DAPM_OUTPUT("LOUT1"), 355*5409fb4eSMark Brown SND_SOC_DAPM_OUTPUT("ROUT1"), 356*5409fb4eSMark Brown SND_SOC_DAPM_OUTPUT("LOUT2"), 357*5409fb4eSMark Brown SND_SOC_DAPM_OUTPUT("ROUT2"), 358*5409fb4eSMark Brown SND_SOC_DAPM_OUTPUT("VREF"), 359*5409fb4eSMark Brown 360*5409fb4eSMark Brown SND_SOC_DAPM_INPUT("LINPUT1"), 361*5409fb4eSMark Brown SND_SOC_DAPM_INPUT("LINPUT2"), 362*5409fb4eSMark Brown SND_SOC_DAPM_INPUT("RINPUT1"), 363*5409fb4eSMark Brown SND_SOC_DAPM_INPUT("RINPUT2"), 364*5409fb4eSMark Brown }; 365*5409fb4eSMark Brown 366*5409fb4eSMark Brown static const struct snd_soc_dapm_route audio_map[] = { 367*5409fb4eSMark Brown 368*5409fb4eSMark Brown { "Left Line Mux", "Line 1", "LINPUT1" }, 369*5409fb4eSMark Brown { "Left Line Mux", "Line 2", "LINPUT2" }, 370*5409fb4eSMark Brown { "Left Line Mux", "PGA", "Left PGA Mux" }, 371*5409fb4eSMark Brown { "Left Line Mux", "Differential", "Differential Mux" }, 372*5409fb4eSMark Brown 373*5409fb4eSMark Brown { "Right Line Mux", "Line 1", "RINPUT1" }, 374*5409fb4eSMark Brown { "Right Line Mux", "Line 2", "RINPUT2" }, 375*5409fb4eSMark Brown { "Right Line Mux", "PGA", "Right PGA Mux" }, 376*5409fb4eSMark Brown { "Right Line Mux", "Differential", "Differential Mux" }, 377*5409fb4eSMark Brown 378*5409fb4eSMark Brown { "Left PGA Mux", "Line 1", "LINPUT1" }, 379*5409fb4eSMark Brown { "Left PGA Mux", "Line 2", "LINPUT2" }, 380*5409fb4eSMark Brown { "Left PGA Mux", "Differential", "Differential Mux" }, 381*5409fb4eSMark Brown 382*5409fb4eSMark Brown { "Right PGA Mux", "Line 1", "RINPUT1" }, 383*5409fb4eSMark Brown { "Right PGA Mux", "Line 2", "RINPUT2" }, 384*5409fb4eSMark Brown { "Right PGA Mux", "Differential", "Differential Mux" }, 385*5409fb4eSMark Brown 386*5409fb4eSMark Brown { "Differential Mux", "Line 1", "LINPUT1" }, 387*5409fb4eSMark Brown { "Differential Mux", "Line 1", "RINPUT1" }, 388*5409fb4eSMark Brown { "Differential Mux", "Line 2", "LINPUT2" }, 389*5409fb4eSMark Brown { "Differential Mux", "Line 2", "RINPUT2" }, 390*5409fb4eSMark Brown 391*5409fb4eSMark Brown { "Left ADC Mux", "Stereo", "Left PGA Mux" }, 392*5409fb4eSMark Brown { "Left ADC Mux", "Mono (Left)", "Left PGA Mux" }, 393*5409fb4eSMark Brown { "Left ADC Mux", "Digital Mono", "Left PGA Mux" }, 394*5409fb4eSMark Brown 395*5409fb4eSMark Brown { "Right ADC Mux", "Stereo", "Right PGA Mux" }, 396*5409fb4eSMark Brown { "Right ADC Mux", "Mono (Right)", "Right PGA Mux" }, 397*5409fb4eSMark Brown { "Right ADC Mux", "Digital Mono", "Right PGA Mux" }, 398*5409fb4eSMark Brown 399*5409fb4eSMark Brown { "Left ADC", NULL, "Left ADC Mux" }, 400*5409fb4eSMark Brown { "Right ADC", NULL, "Right ADC Mux" }, 401*5409fb4eSMark Brown 402*5409fb4eSMark Brown { "Left Line Mux", "Line 1", "LINPUT1" }, 403*5409fb4eSMark Brown { "Left Line Mux", "Line 2", "LINPUT2" }, 404*5409fb4eSMark Brown { "Left Line Mux", "PGA", "Left PGA Mux" }, 405*5409fb4eSMark Brown { "Left Line Mux", "Differential", "Differential Mux" }, 406*5409fb4eSMark Brown 407*5409fb4eSMark Brown { "Right Line Mux", "Line 1", "RINPUT1" }, 408*5409fb4eSMark Brown { "Right Line Mux", "Line 2", "RINPUT2" }, 409*5409fb4eSMark Brown { "Right Line Mux", "PGA", "Right PGA Mux" }, 410*5409fb4eSMark Brown { "Right Line Mux", "Differential", "Differential Mux" }, 411*5409fb4eSMark Brown 412*5409fb4eSMark Brown { "Left Mixer", "Playback Switch", "Left DAC" }, 413*5409fb4eSMark Brown { "Left Mixer", "Left Bypass Switch", "Left Line Mux" }, 414*5409fb4eSMark Brown { "Left Mixer", "Right Playback Switch", "Right DAC" }, 415*5409fb4eSMark Brown { "Left Mixer", "Right Bypass Switch", "Right Line Mux" }, 416*5409fb4eSMark Brown 417*5409fb4eSMark Brown { "Right Mixer", "Left Playback Switch", "Left DAC" }, 418*5409fb4eSMark Brown { "Right Mixer", "Left Bypass Switch", "Left Line Mux" }, 419*5409fb4eSMark Brown { "Right Mixer", "Playback Switch", "Right DAC" }, 420*5409fb4eSMark Brown { "Right Mixer", "Right Bypass Switch", "Right Line Mux" }, 421*5409fb4eSMark Brown 422*5409fb4eSMark Brown { "Left Out 1", NULL, "Left Mixer" }, 423*5409fb4eSMark Brown { "LOUT1", NULL, "Left Out 1" }, 424*5409fb4eSMark Brown { "Right Out 1", NULL, "Right Mixer" }, 425*5409fb4eSMark Brown { "ROUT1", NULL, "Right Out 1" }, 426*5409fb4eSMark Brown 427*5409fb4eSMark Brown { "Left Out 2", NULL, "Left Mixer" }, 428*5409fb4eSMark Brown { "LOUT2", NULL, "Left Out 2" }, 429*5409fb4eSMark Brown { "Right Out 2", NULL, "Right Mixer" }, 430*5409fb4eSMark Brown { "ROUT2", NULL, "Right Out 2" }, 431*5409fb4eSMark Brown }; 432*5409fb4eSMark Brown 433*5409fb4eSMark Brown struct _coeff_div { 434*5409fb4eSMark Brown u32 mclk; 435*5409fb4eSMark Brown u32 rate; 436*5409fb4eSMark Brown u16 fs; 437*5409fb4eSMark Brown u8 sr:5; 438*5409fb4eSMark Brown u8 usb:1; 439*5409fb4eSMark Brown }; 440*5409fb4eSMark Brown 441*5409fb4eSMark Brown /* codec hifi mclk clock divider coefficients */ 442*5409fb4eSMark Brown static const struct _coeff_div coeff_div[] = { 443*5409fb4eSMark Brown /* 8k */ 444*5409fb4eSMark Brown {12288000, 8000, 1536, 0x6, 0x0}, 445*5409fb4eSMark Brown {11289600, 8000, 1408, 0x16, 0x0}, 446*5409fb4eSMark Brown {18432000, 8000, 2304, 0x7, 0x0}, 447*5409fb4eSMark Brown {16934400, 8000, 2112, 0x17, 0x0}, 448*5409fb4eSMark Brown {12000000, 8000, 1500, 0x6, 0x1}, 449*5409fb4eSMark Brown 450*5409fb4eSMark Brown /* 11.025k */ 451*5409fb4eSMark Brown {11289600, 11025, 1024, 0x18, 0x0}, 452*5409fb4eSMark Brown {16934400, 11025, 1536, 0x19, 0x0}, 453*5409fb4eSMark Brown {12000000, 11025, 1088, 0x19, 0x1}, 454*5409fb4eSMark Brown 455*5409fb4eSMark Brown /* 16k */ 456*5409fb4eSMark Brown {12288000, 16000, 768, 0xa, 0x0}, 457*5409fb4eSMark Brown {18432000, 16000, 1152, 0xb, 0x0}, 458*5409fb4eSMark Brown {12000000, 16000, 750, 0xa, 0x1}, 459*5409fb4eSMark Brown 460*5409fb4eSMark Brown /* 22.05k */ 461*5409fb4eSMark Brown {11289600, 22050, 512, 0x1a, 0x0}, 462*5409fb4eSMark Brown {16934400, 22050, 768, 0x1b, 0x0}, 463*5409fb4eSMark Brown {12000000, 22050, 544, 0x1b, 0x1}, 464*5409fb4eSMark Brown 465*5409fb4eSMark Brown /* 32k */ 466*5409fb4eSMark Brown {12288000, 32000, 384, 0xc, 0x0}, 467*5409fb4eSMark Brown {18432000, 32000, 576, 0xd, 0x0}, 468*5409fb4eSMark Brown {12000000, 32000, 375, 0xa, 0x1}, 469*5409fb4eSMark Brown 470*5409fb4eSMark Brown /* 44.1k */ 471*5409fb4eSMark Brown {11289600, 44100, 256, 0x10, 0x0}, 472*5409fb4eSMark Brown {16934400, 44100, 384, 0x11, 0x0}, 473*5409fb4eSMark Brown {12000000, 44100, 272, 0x11, 0x1}, 474*5409fb4eSMark Brown 475*5409fb4eSMark Brown /* 48k */ 476*5409fb4eSMark Brown {12288000, 48000, 256, 0x0, 0x0}, 477*5409fb4eSMark Brown {18432000, 48000, 384, 0x1, 0x0}, 478*5409fb4eSMark Brown {12000000, 48000, 250, 0x0, 0x1}, 479*5409fb4eSMark Brown 480*5409fb4eSMark Brown /* 88.2k */ 481*5409fb4eSMark Brown {11289600, 88200, 128, 0x1e, 0x0}, 482*5409fb4eSMark Brown {16934400, 88200, 192, 0x1f, 0x0}, 483*5409fb4eSMark Brown {12000000, 88200, 136, 0x1f, 0x1}, 484*5409fb4eSMark Brown 485*5409fb4eSMark Brown /* 96k */ 486*5409fb4eSMark Brown {12288000, 96000, 128, 0xe, 0x0}, 487*5409fb4eSMark Brown {18432000, 96000, 192, 0xf, 0x0}, 488*5409fb4eSMark Brown {12000000, 96000, 125, 0xe, 0x1}, 489*5409fb4eSMark Brown }; 490*5409fb4eSMark Brown 491*5409fb4eSMark Brown static inline int get_coeff(int mclk, int rate) 492*5409fb4eSMark Brown { 493*5409fb4eSMark Brown int i; 494*5409fb4eSMark Brown 495*5409fb4eSMark Brown for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { 496*5409fb4eSMark Brown if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) 497*5409fb4eSMark Brown return i; 498*5409fb4eSMark Brown } 499*5409fb4eSMark Brown 500*5409fb4eSMark Brown return -EINVAL; 501*5409fb4eSMark Brown } 502*5409fb4eSMark Brown 503*5409fb4eSMark Brown /* The set of rates we can generate from the above for each SYSCLK */ 504*5409fb4eSMark Brown 505*5409fb4eSMark Brown static unsigned int rates_12288[] = { 506*5409fb4eSMark Brown 8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000, 507*5409fb4eSMark Brown }; 508*5409fb4eSMark Brown 509*5409fb4eSMark Brown static struct snd_pcm_hw_constraint_list constraints_12288 = { 510*5409fb4eSMark Brown .count = ARRAY_SIZE(rates_12288), 511*5409fb4eSMark Brown .list = rates_12288, 512*5409fb4eSMark Brown }; 513*5409fb4eSMark Brown 514*5409fb4eSMark Brown static unsigned int rates_112896[] = { 515*5409fb4eSMark Brown 8000, 11025, 22050, 44100, 516*5409fb4eSMark Brown }; 517*5409fb4eSMark Brown 518*5409fb4eSMark Brown static struct snd_pcm_hw_constraint_list constraints_112896 = { 519*5409fb4eSMark Brown .count = ARRAY_SIZE(rates_112896), 520*5409fb4eSMark Brown .list = rates_112896, 521*5409fb4eSMark Brown }; 522*5409fb4eSMark Brown 523*5409fb4eSMark Brown static unsigned int rates_12[] = { 524*5409fb4eSMark Brown 8000, 11025, 12000, 16000, 22050, 2400, 32000, 41100, 48000, 525*5409fb4eSMark Brown 48000, 88235, 96000, 526*5409fb4eSMark Brown }; 527*5409fb4eSMark Brown 528*5409fb4eSMark Brown static struct snd_pcm_hw_constraint_list constraints_12 = { 529*5409fb4eSMark Brown .count = ARRAY_SIZE(rates_12), 530*5409fb4eSMark Brown .list = rates_12, 531*5409fb4eSMark Brown }; 532*5409fb4eSMark Brown 533*5409fb4eSMark Brown /* 534*5409fb4eSMark Brown * Note that this should be called from init rather than from hw_params. 535*5409fb4eSMark Brown */ 536*5409fb4eSMark Brown static int wm8988_set_dai_sysclk(struct snd_soc_dai *codec_dai, 537*5409fb4eSMark Brown int clk_id, unsigned int freq, int dir) 538*5409fb4eSMark Brown { 539*5409fb4eSMark Brown struct snd_soc_codec *codec = codec_dai->codec; 540*5409fb4eSMark Brown struct wm8988_priv *wm8988 = codec->private_data; 541*5409fb4eSMark Brown 542*5409fb4eSMark Brown switch (freq) { 543*5409fb4eSMark Brown case 11289600: 544*5409fb4eSMark Brown case 18432000: 545*5409fb4eSMark Brown case 22579200: 546*5409fb4eSMark Brown case 36864000: 547*5409fb4eSMark Brown wm8988->sysclk_constraints = &constraints_112896; 548*5409fb4eSMark Brown wm8988->sysclk = freq; 549*5409fb4eSMark Brown return 0; 550*5409fb4eSMark Brown 551*5409fb4eSMark Brown case 12288000: 552*5409fb4eSMark Brown case 16934400: 553*5409fb4eSMark Brown case 24576000: 554*5409fb4eSMark Brown case 33868800: 555*5409fb4eSMark Brown wm8988->sysclk_constraints = &constraints_12288; 556*5409fb4eSMark Brown wm8988->sysclk = freq; 557*5409fb4eSMark Brown return 0; 558*5409fb4eSMark Brown 559*5409fb4eSMark Brown case 12000000: 560*5409fb4eSMark Brown case 24000000: 561*5409fb4eSMark Brown wm8988->sysclk_constraints = &constraints_12; 562*5409fb4eSMark Brown wm8988->sysclk = freq; 563*5409fb4eSMark Brown return 0; 564*5409fb4eSMark Brown } 565*5409fb4eSMark Brown return -EINVAL; 566*5409fb4eSMark Brown } 567*5409fb4eSMark Brown 568*5409fb4eSMark Brown static int wm8988_set_dai_fmt(struct snd_soc_dai *codec_dai, 569*5409fb4eSMark Brown unsigned int fmt) 570*5409fb4eSMark Brown { 571*5409fb4eSMark Brown struct snd_soc_codec *codec = codec_dai->codec; 572*5409fb4eSMark Brown u16 iface = 0; 573*5409fb4eSMark Brown 574*5409fb4eSMark Brown /* set master/slave audio interface */ 575*5409fb4eSMark Brown switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 576*5409fb4eSMark Brown case SND_SOC_DAIFMT_CBM_CFM: 577*5409fb4eSMark Brown iface = 0x0040; 578*5409fb4eSMark Brown break; 579*5409fb4eSMark Brown case SND_SOC_DAIFMT_CBS_CFS: 580*5409fb4eSMark Brown break; 581*5409fb4eSMark Brown default: 582*5409fb4eSMark Brown return -EINVAL; 583*5409fb4eSMark Brown } 584*5409fb4eSMark Brown 585*5409fb4eSMark Brown /* interface format */ 586*5409fb4eSMark Brown switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 587*5409fb4eSMark Brown case SND_SOC_DAIFMT_I2S: 588*5409fb4eSMark Brown iface |= 0x0002; 589*5409fb4eSMark Brown break; 590*5409fb4eSMark Brown case SND_SOC_DAIFMT_RIGHT_J: 591*5409fb4eSMark Brown break; 592*5409fb4eSMark Brown case SND_SOC_DAIFMT_LEFT_J: 593*5409fb4eSMark Brown iface |= 0x0001; 594*5409fb4eSMark Brown break; 595*5409fb4eSMark Brown case SND_SOC_DAIFMT_DSP_A: 596*5409fb4eSMark Brown iface |= 0x0003; 597*5409fb4eSMark Brown break; 598*5409fb4eSMark Brown case SND_SOC_DAIFMT_DSP_B: 599*5409fb4eSMark Brown iface |= 0x0013; 600*5409fb4eSMark Brown break; 601*5409fb4eSMark Brown default: 602*5409fb4eSMark Brown return -EINVAL; 603*5409fb4eSMark Brown } 604*5409fb4eSMark Brown 605*5409fb4eSMark Brown /* clock inversion */ 606*5409fb4eSMark Brown switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 607*5409fb4eSMark Brown case SND_SOC_DAIFMT_NB_NF: 608*5409fb4eSMark Brown break; 609*5409fb4eSMark Brown case SND_SOC_DAIFMT_IB_IF: 610*5409fb4eSMark Brown iface |= 0x0090; 611*5409fb4eSMark Brown break; 612*5409fb4eSMark Brown case SND_SOC_DAIFMT_IB_NF: 613*5409fb4eSMark Brown iface |= 0x0080; 614*5409fb4eSMark Brown break; 615*5409fb4eSMark Brown case SND_SOC_DAIFMT_NB_IF: 616*5409fb4eSMark Brown iface |= 0x0010; 617*5409fb4eSMark Brown break; 618*5409fb4eSMark Brown default: 619*5409fb4eSMark Brown return -EINVAL; 620*5409fb4eSMark Brown } 621*5409fb4eSMark Brown 622*5409fb4eSMark Brown wm8988_write(codec, WM8988_IFACE, iface); 623*5409fb4eSMark Brown return 0; 624*5409fb4eSMark Brown } 625*5409fb4eSMark Brown 626*5409fb4eSMark Brown static int wm8988_pcm_startup(struct snd_pcm_substream *substream, 627*5409fb4eSMark Brown struct snd_soc_dai *dai) 628*5409fb4eSMark Brown { 629*5409fb4eSMark Brown struct snd_soc_codec *codec = dai->codec; 630*5409fb4eSMark Brown struct wm8988_priv *wm8988 = codec->private_data; 631*5409fb4eSMark Brown 632*5409fb4eSMark Brown /* The set of sample rates that can be supported depends on the 633*5409fb4eSMark Brown * MCLK supplied to the CODEC - enforce this. 634*5409fb4eSMark Brown */ 635*5409fb4eSMark Brown if (!wm8988->sysclk) { 636*5409fb4eSMark Brown dev_err(codec->dev, 637*5409fb4eSMark Brown "No MCLK configured, call set_sysclk() on init\n"); 638*5409fb4eSMark Brown return -EINVAL; 639*5409fb4eSMark Brown } 640*5409fb4eSMark Brown 641*5409fb4eSMark Brown snd_pcm_hw_constraint_list(substream->runtime, 0, 642*5409fb4eSMark Brown SNDRV_PCM_HW_PARAM_RATE, 643*5409fb4eSMark Brown wm8988->sysclk_constraints); 644*5409fb4eSMark Brown 645*5409fb4eSMark Brown return 0; 646*5409fb4eSMark Brown } 647*5409fb4eSMark Brown 648*5409fb4eSMark Brown static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream, 649*5409fb4eSMark Brown struct snd_pcm_hw_params *params, 650*5409fb4eSMark Brown struct snd_soc_dai *dai) 651*5409fb4eSMark Brown { 652*5409fb4eSMark Brown struct snd_soc_pcm_runtime *rtd = substream->private_data; 653*5409fb4eSMark Brown struct snd_soc_device *socdev = rtd->socdev; 654*5409fb4eSMark Brown struct snd_soc_codec *codec = socdev->card->codec; 655*5409fb4eSMark Brown struct wm8988_priv *wm8988 = codec->private_data; 656*5409fb4eSMark Brown u16 iface = wm8988_read_reg_cache(codec, WM8988_IFACE) & 0x1f3; 657*5409fb4eSMark Brown u16 srate = wm8988_read_reg_cache(codec, WM8988_SRATE) & 0x180; 658*5409fb4eSMark Brown int coeff; 659*5409fb4eSMark Brown 660*5409fb4eSMark Brown coeff = get_coeff(wm8988->sysclk, params_rate(params)); 661*5409fb4eSMark Brown if (coeff < 0) { 662*5409fb4eSMark Brown coeff = get_coeff(wm8988->sysclk / 2, params_rate(params)); 663*5409fb4eSMark Brown srate |= 0x40; 664*5409fb4eSMark Brown } 665*5409fb4eSMark Brown if (coeff < 0) { 666*5409fb4eSMark Brown dev_err(codec->dev, 667*5409fb4eSMark Brown "Unable to configure sample rate %dHz with %dHz MCLK\n", 668*5409fb4eSMark Brown params_rate(params), wm8988->sysclk); 669*5409fb4eSMark Brown return coeff; 670*5409fb4eSMark Brown } 671*5409fb4eSMark Brown 672*5409fb4eSMark Brown /* bit size */ 673*5409fb4eSMark Brown switch (params_format(params)) { 674*5409fb4eSMark Brown case SNDRV_PCM_FORMAT_S16_LE: 675*5409fb4eSMark Brown break; 676*5409fb4eSMark Brown case SNDRV_PCM_FORMAT_S20_3LE: 677*5409fb4eSMark Brown iface |= 0x0004; 678*5409fb4eSMark Brown break; 679*5409fb4eSMark Brown case SNDRV_PCM_FORMAT_S24_LE: 680*5409fb4eSMark Brown iface |= 0x0008; 681*5409fb4eSMark Brown break; 682*5409fb4eSMark Brown case SNDRV_PCM_FORMAT_S32_LE: 683*5409fb4eSMark Brown iface |= 0x000c; 684*5409fb4eSMark Brown break; 685*5409fb4eSMark Brown } 686*5409fb4eSMark Brown 687*5409fb4eSMark Brown /* set iface & srate */ 688*5409fb4eSMark Brown wm8988_write(codec, WM8988_IFACE, iface); 689*5409fb4eSMark Brown if (coeff >= 0) 690*5409fb4eSMark Brown wm8988_write(codec, WM8988_SRATE, srate | 691*5409fb4eSMark Brown (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb); 692*5409fb4eSMark Brown 693*5409fb4eSMark Brown return 0; 694*5409fb4eSMark Brown } 695*5409fb4eSMark Brown 696*5409fb4eSMark Brown static int wm8988_mute(struct snd_soc_dai *dai, int mute) 697*5409fb4eSMark Brown { 698*5409fb4eSMark Brown struct snd_soc_codec *codec = dai->codec; 699*5409fb4eSMark Brown u16 mute_reg = wm8988_read_reg_cache(codec, WM8988_ADCDAC) & 0xfff7; 700*5409fb4eSMark Brown 701*5409fb4eSMark Brown if (mute) 702*5409fb4eSMark Brown wm8988_write(codec, WM8988_ADCDAC, mute_reg | 0x8); 703*5409fb4eSMark Brown else 704*5409fb4eSMark Brown wm8988_write(codec, WM8988_ADCDAC, mute_reg); 705*5409fb4eSMark Brown return 0; 706*5409fb4eSMark Brown } 707*5409fb4eSMark Brown 708*5409fb4eSMark Brown static int wm8988_set_bias_level(struct snd_soc_codec *codec, 709*5409fb4eSMark Brown enum snd_soc_bias_level level) 710*5409fb4eSMark Brown { 711*5409fb4eSMark Brown u16 pwr_reg = wm8988_read_reg_cache(codec, WM8988_PWR1) & ~0x1c1; 712*5409fb4eSMark Brown 713*5409fb4eSMark Brown switch (level) { 714*5409fb4eSMark Brown case SND_SOC_BIAS_ON: 715*5409fb4eSMark Brown break; 716*5409fb4eSMark Brown 717*5409fb4eSMark Brown case SND_SOC_BIAS_PREPARE: 718*5409fb4eSMark Brown /* VREF, VMID=2x50k, digital enabled */ 719*5409fb4eSMark Brown wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x00c0); 720*5409fb4eSMark Brown break; 721*5409fb4eSMark Brown 722*5409fb4eSMark Brown case SND_SOC_BIAS_STANDBY: 723*5409fb4eSMark Brown if (codec->bias_level == SND_SOC_BIAS_OFF) { 724*5409fb4eSMark Brown /* VREF, VMID=2x5k */ 725*5409fb4eSMark Brown wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x1c1); 726*5409fb4eSMark Brown 727*5409fb4eSMark Brown /* Charge caps */ 728*5409fb4eSMark Brown msleep(100); 729*5409fb4eSMark Brown } 730*5409fb4eSMark Brown 731*5409fb4eSMark Brown /* VREF, VMID=2*500k, digital stopped */ 732*5409fb4eSMark Brown wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x0141); 733*5409fb4eSMark Brown break; 734*5409fb4eSMark Brown 735*5409fb4eSMark Brown case SND_SOC_BIAS_OFF: 736*5409fb4eSMark Brown wm8988_write(codec, WM8988_PWR1, 0x0000); 737*5409fb4eSMark Brown break; 738*5409fb4eSMark Brown } 739*5409fb4eSMark Brown codec->bias_level = level; 740*5409fb4eSMark Brown return 0; 741*5409fb4eSMark Brown } 742*5409fb4eSMark Brown 743*5409fb4eSMark Brown #define WM8988_RATES SNDRV_PCM_RATE_8000_96000 744*5409fb4eSMark Brown 745*5409fb4eSMark Brown #define WM8988_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 746*5409fb4eSMark Brown SNDRV_PCM_FMTBIT_S24_LE) 747*5409fb4eSMark Brown 748*5409fb4eSMark Brown static struct snd_soc_dai_ops wm8988_ops = { 749*5409fb4eSMark Brown .startup = wm8988_pcm_startup, 750*5409fb4eSMark Brown .hw_params = wm8988_pcm_hw_params, 751*5409fb4eSMark Brown .set_fmt = wm8988_set_dai_fmt, 752*5409fb4eSMark Brown .set_sysclk = wm8988_set_dai_sysclk, 753*5409fb4eSMark Brown .digital_mute = wm8988_mute, 754*5409fb4eSMark Brown }; 755*5409fb4eSMark Brown 756*5409fb4eSMark Brown struct snd_soc_dai wm8988_dai = { 757*5409fb4eSMark Brown .name = "WM8988", 758*5409fb4eSMark Brown .playback = { 759*5409fb4eSMark Brown .stream_name = "Playback", 760*5409fb4eSMark Brown .channels_min = 1, 761*5409fb4eSMark Brown .channels_max = 2, 762*5409fb4eSMark Brown .rates = WM8988_RATES, 763*5409fb4eSMark Brown .formats = WM8988_FORMATS, 764*5409fb4eSMark Brown }, 765*5409fb4eSMark Brown .capture = { 766*5409fb4eSMark Brown .stream_name = "Capture", 767*5409fb4eSMark Brown .channels_min = 1, 768*5409fb4eSMark Brown .channels_max = 2, 769*5409fb4eSMark Brown .rates = WM8988_RATES, 770*5409fb4eSMark Brown .formats = WM8988_FORMATS, 771*5409fb4eSMark Brown }, 772*5409fb4eSMark Brown .ops = &wm8988_ops, 773*5409fb4eSMark Brown .symmetric_rates = 1, 774*5409fb4eSMark Brown }; 775*5409fb4eSMark Brown EXPORT_SYMBOL_GPL(wm8988_dai); 776*5409fb4eSMark Brown 777*5409fb4eSMark Brown static int wm8988_suspend(struct platform_device *pdev, pm_message_t state) 778*5409fb4eSMark Brown { 779*5409fb4eSMark Brown struct snd_soc_device *socdev = platform_get_drvdata(pdev); 780*5409fb4eSMark Brown struct snd_soc_codec *codec = socdev->card->codec; 781*5409fb4eSMark Brown 782*5409fb4eSMark Brown wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF); 783*5409fb4eSMark Brown return 0; 784*5409fb4eSMark Brown } 785*5409fb4eSMark Brown 786*5409fb4eSMark Brown static int wm8988_resume(struct platform_device *pdev) 787*5409fb4eSMark Brown { 788*5409fb4eSMark Brown struct snd_soc_device *socdev = platform_get_drvdata(pdev); 789*5409fb4eSMark Brown struct snd_soc_codec *codec = socdev->card->codec; 790*5409fb4eSMark Brown int i; 791*5409fb4eSMark Brown u8 data[2]; 792*5409fb4eSMark Brown u16 *cache = codec->reg_cache; 793*5409fb4eSMark Brown 794*5409fb4eSMark Brown /* Sync reg_cache with the hardware */ 795*5409fb4eSMark Brown for (i = 0; i < WM8988_NUM_REG; i++) { 796*5409fb4eSMark Brown if (i == WM8988_RESET) 797*5409fb4eSMark Brown continue; 798*5409fb4eSMark Brown data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); 799*5409fb4eSMark Brown data[1] = cache[i] & 0x00ff; 800*5409fb4eSMark Brown codec->hw_write(codec->control_data, data, 2); 801*5409fb4eSMark Brown } 802*5409fb4eSMark Brown 803*5409fb4eSMark Brown wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 804*5409fb4eSMark Brown 805*5409fb4eSMark Brown return 0; 806*5409fb4eSMark Brown } 807*5409fb4eSMark Brown 808*5409fb4eSMark Brown static struct snd_soc_codec *wm8988_codec; 809*5409fb4eSMark Brown 810*5409fb4eSMark Brown static int wm8988_probe(struct platform_device *pdev) 811*5409fb4eSMark Brown { 812*5409fb4eSMark Brown struct snd_soc_device *socdev = platform_get_drvdata(pdev); 813*5409fb4eSMark Brown struct snd_soc_codec *codec; 814*5409fb4eSMark Brown int ret = 0; 815*5409fb4eSMark Brown 816*5409fb4eSMark Brown if (wm8988_codec == NULL) { 817*5409fb4eSMark Brown dev_err(&pdev->dev, "Codec device not registered\n"); 818*5409fb4eSMark Brown return -ENODEV; 819*5409fb4eSMark Brown } 820*5409fb4eSMark Brown 821*5409fb4eSMark Brown socdev->card->codec = wm8988_codec; 822*5409fb4eSMark Brown codec = wm8988_codec; 823*5409fb4eSMark Brown 824*5409fb4eSMark Brown /* register pcms */ 825*5409fb4eSMark Brown ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); 826*5409fb4eSMark Brown if (ret < 0) { 827*5409fb4eSMark Brown dev_err(codec->dev, "failed to create pcms: %d\n", ret); 828*5409fb4eSMark Brown goto pcm_err; 829*5409fb4eSMark Brown } 830*5409fb4eSMark Brown 831*5409fb4eSMark Brown snd_soc_add_controls(codec, wm8988_snd_controls, 832*5409fb4eSMark Brown ARRAY_SIZE(wm8988_snd_controls)); 833*5409fb4eSMark Brown snd_soc_dapm_new_controls(codec, wm8988_dapm_widgets, 834*5409fb4eSMark Brown ARRAY_SIZE(wm8988_dapm_widgets)); 835*5409fb4eSMark Brown snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); 836*5409fb4eSMark Brown snd_soc_dapm_new_widgets(codec); 837*5409fb4eSMark Brown 838*5409fb4eSMark Brown ret = snd_soc_init_card(socdev); 839*5409fb4eSMark Brown if (ret < 0) { 840*5409fb4eSMark Brown dev_err(codec->dev, "failed to register card: %d\n", ret); 841*5409fb4eSMark Brown goto card_err; 842*5409fb4eSMark Brown } 843*5409fb4eSMark Brown 844*5409fb4eSMark Brown return ret; 845*5409fb4eSMark Brown 846*5409fb4eSMark Brown card_err: 847*5409fb4eSMark Brown snd_soc_free_pcms(socdev); 848*5409fb4eSMark Brown snd_soc_dapm_free(socdev); 849*5409fb4eSMark Brown pcm_err: 850*5409fb4eSMark Brown return ret; 851*5409fb4eSMark Brown } 852*5409fb4eSMark Brown 853*5409fb4eSMark Brown static int wm8988_remove(struct platform_device *pdev) 854*5409fb4eSMark Brown { 855*5409fb4eSMark Brown struct snd_soc_device *socdev = platform_get_drvdata(pdev); 856*5409fb4eSMark Brown 857*5409fb4eSMark Brown snd_soc_free_pcms(socdev); 858*5409fb4eSMark Brown snd_soc_dapm_free(socdev); 859*5409fb4eSMark Brown 860*5409fb4eSMark Brown return 0; 861*5409fb4eSMark Brown } 862*5409fb4eSMark Brown 863*5409fb4eSMark Brown struct snd_soc_codec_device soc_codec_dev_wm8988 = { 864*5409fb4eSMark Brown .probe = wm8988_probe, 865*5409fb4eSMark Brown .remove = wm8988_remove, 866*5409fb4eSMark Brown .suspend = wm8988_suspend, 867*5409fb4eSMark Brown .resume = wm8988_resume, 868*5409fb4eSMark Brown }; 869*5409fb4eSMark Brown EXPORT_SYMBOL_GPL(soc_codec_dev_wm8988); 870*5409fb4eSMark Brown 871*5409fb4eSMark Brown static int wm8988_register(struct wm8988_priv *wm8988) 872*5409fb4eSMark Brown { 873*5409fb4eSMark Brown struct snd_soc_codec *codec = &wm8988->codec; 874*5409fb4eSMark Brown int ret; 875*5409fb4eSMark Brown u16 reg; 876*5409fb4eSMark Brown 877*5409fb4eSMark Brown if (wm8988_codec) { 878*5409fb4eSMark Brown dev_err(codec->dev, "Another WM8988 is registered\n"); 879*5409fb4eSMark Brown ret = -EINVAL; 880*5409fb4eSMark Brown goto err; 881*5409fb4eSMark Brown } 882*5409fb4eSMark Brown 883*5409fb4eSMark Brown mutex_init(&codec->mutex); 884*5409fb4eSMark Brown INIT_LIST_HEAD(&codec->dapm_widgets); 885*5409fb4eSMark Brown INIT_LIST_HEAD(&codec->dapm_paths); 886*5409fb4eSMark Brown 887*5409fb4eSMark Brown codec->private_data = wm8988; 888*5409fb4eSMark Brown codec->name = "WM8988"; 889*5409fb4eSMark Brown codec->owner = THIS_MODULE; 890*5409fb4eSMark Brown codec->read = wm8988_read_reg_cache; 891*5409fb4eSMark Brown codec->write = wm8988_write; 892*5409fb4eSMark Brown codec->dai = &wm8988_dai; 893*5409fb4eSMark Brown codec->num_dai = 1; 894*5409fb4eSMark Brown codec->reg_cache_size = ARRAY_SIZE(wm8988->reg_cache); 895*5409fb4eSMark Brown codec->reg_cache = &wm8988->reg_cache; 896*5409fb4eSMark Brown codec->bias_level = SND_SOC_BIAS_OFF; 897*5409fb4eSMark Brown codec->set_bias_level = wm8988_set_bias_level; 898*5409fb4eSMark Brown 899*5409fb4eSMark Brown memcpy(codec->reg_cache, wm8988_reg, 900*5409fb4eSMark Brown sizeof(wm8988_reg)); 901*5409fb4eSMark Brown 902*5409fb4eSMark Brown ret = wm8988_reset(codec); 903*5409fb4eSMark Brown if (ret < 0) { 904*5409fb4eSMark Brown dev_err(codec->dev, "Failed to issue reset\n"); 905*5409fb4eSMark Brown return ret; 906*5409fb4eSMark Brown } 907*5409fb4eSMark Brown 908*5409fb4eSMark Brown /* set the update bits (we always update left then right) */ 909*5409fb4eSMark Brown reg = wm8988_read_reg_cache(codec, WM8988_RADC); 910*5409fb4eSMark Brown wm8988_write(codec, WM8988_RADC, reg | 0x100); 911*5409fb4eSMark Brown reg = wm8988_read_reg_cache(codec, WM8988_RDAC); 912*5409fb4eSMark Brown wm8988_write(codec, WM8988_RDAC, reg | 0x0100); 913*5409fb4eSMark Brown reg = wm8988_read_reg_cache(codec, WM8988_ROUT1V); 914*5409fb4eSMark Brown wm8988_write(codec, WM8988_ROUT1V, reg | 0x0100); 915*5409fb4eSMark Brown reg = wm8988_read_reg_cache(codec, WM8988_ROUT2V); 916*5409fb4eSMark Brown wm8988_write(codec, WM8988_ROUT2V, reg | 0x0100); 917*5409fb4eSMark Brown reg = wm8988_read_reg_cache(codec, WM8988_RINVOL); 918*5409fb4eSMark Brown wm8988_write(codec, WM8988_RINVOL, reg | 0x0100); 919*5409fb4eSMark Brown 920*5409fb4eSMark Brown wm8988_set_bias_level(&wm8988->codec, SND_SOC_BIAS_STANDBY); 921*5409fb4eSMark Brown 922*5409fb4eSMark Brown wm8988_dai.dev = codec->dev; 923*5409fb4eSMark Brown 924*5409fb4eSMark Brown wm8988_codec = codec; 925*5409fb4eSMark Brown 926*5409fb4eSMark Brown ret = snd_soc_register_codec(codec); 927*5409fb4eSMark Brown if (ret != 0) { 928*5409fb4eSMark Brown dev_err(codec->dev, "Failed to register codec: %d\n", ret); 929*5409fb4eSMark Brown return ret; 930*5409fb4eSMark Brown } 931*5409fb4eSMark Brown 932*5409fb4eSMark Brown ret = snd_soc_register_dai(&wm8988_dai); 933*5409fb4eSMark Brown if (ret != 0) { 934*5409fb4eSMark Brown dev_err(codec->dev, "Failed to register DAI: %d\n", ret); 935*5409fb4eSMark Brown snd_soc_unregister_codec(codec); 936*5409fb4eSMark Brown return ret; 937*5409fb4eSMark Brown } 938*5409fb4eSMark Brown 939*5409fb4eSMark Brown return 0; 940*5409fb4eSMark Brown 941*5409fb4eSMark Brown err: 942*5409fb4eSMark Brown kfree(wm8988); 943*5409fb4eSMark Brown return ret; 944*5409fb4eSMark Brown } 945*5409fb4eSMark Brown 946*5409fb4eSMark Brown static void wm8988_unregister(struct wm8988_priv *wm8988) 947*5409fb4eSMark Brown { 948*5409fb4eSMark Brown wm8988_set_bias_level(&wm8988->codec, SND_SOC_BIAS_OFF); 949*5409fb4eSMark Brown snd_soc_unregister_dai(&wm8988_dai); 950*5409fb4eSMark Brown snd_soc_unregister_codec(&wm8988->codec); 951*5409fb4eSMark Brown kfree(wm8988); 952*5409fb4eSMark Brown wm8988_codec = NULL; 953*5409fb4eSMark Brown } 954*5409fb4eSMark Brown 955*5409fb4eSMark Brown #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 956*5409fb4eSMark Brown static int wm8988_i2c_probe(struct i2c_client *i2c, 957*5409fb4eSMark Brown const struct i2c_device_id *id) 958*5409fb4eSMark Brown { 959*5409fb4eSMark Brown struct wm8988_priv *wm8988; 960*5409fb4eSMark Brown struct snd_soc_codec *codec; 961*5409fb4eSMark Brown 962*5409fb4eSMark Brown wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL); 963*5409fb4eSMark Brown if (wm8988 == NULL) 964*5409fb4eSMark Brown return -ENOMEM; 965*5409fb4eSMark Brown 966*5409fb4eSMark Brown codec = &wm8988->codec; 967*5409fb4eSMark Brown codec->hw_write = (hw_write_t)i2c_master_send; 968*5409fb4eSMark Brown 969*5409fb4eSMark Brown i2c_set_clientdata(i2c, wm8988); 970*5409fb4eSMark Brown codec->control_data = i2c; 971*5409fb4eSMark Brown 972*5409fb4eSMark Brown codec->dev = &i2c->dev; 973*5409fb4eSMark Brown 974*5409fb4eSMark Brown return wm8988_register(wm8988); 975*5409fb4eSMark Brown } 976*5409fb4eSMark Brown 977*5409fb4eSMark Brown static int wm8988_i2c_remove(struct i2c_client *client) 978*5409fb4eSMark Brown { 979*5409fb4eSMark Brown struct wm8988_priv *wm8988 = i2c_get_clientdata(client); 980*5409fb4eSMark Brown wm8988_unregister(wm8988); 981*5409fb4eSMark Brown return 0; 982*5409fb4eSMark Brown } 983*5409fb4eSMark Brown 984*5409fb4eSMark Brown static const struct i2c_device_id wm8988_i2c_id[] = { 985*5409fb4eSMark Brown { "wm8988", 0 }, 986*5409fb4eSMark Brown { } 987*5409fb4eSMark Brown }; 988*5409fb4eSMark Brown MODULE_DEVICE_TABLE(i2c, wm8988_i2c_id); 989*5409fb4eSMark Brown 990*5409fb4eSMark Brown static struct i2c_driver wm8988_i2c_driver = { 991*5409fb4eSMark Brown .driver = { 992*5409fb4eSMark Brown .name = "WM8988", 993*5409fb4eSMark Brown .owner = THIS_MODULE, 994*5409fb4eSMark Brown }, 995*5409fb4eSMark Brown .probe = wm8988_i2c_probe, 996*5409fb4eSMark Brown .remove = wm8988_i2c_remove, 997*5409fb4eSMark Brown .id_table = wm8988_i2c_id, 998*5409fb4eSMark Brown }; 999*5409fb4eSMark Brown #endif 1000*5409fb4eSMark Brown 1001*5409fb4eSMark Brown #if defined(CONFIG_SPI_MASTER) 1002*5409fb4eSMark Brown static int wm8988_spi_write(struct spi_device *spi, const char *data, int len) 1003*5409fb4eSMark Brown { 1004*5409fb4eSMark Brown struct spi_transfer t; 1005*5409fb4eSMark Brown struct spi_message m; 1006*5409fb4eSMark Brown u8 msg[2]; 1007*5409fb4eSMark Brown 1008*5409fb4eSMark Brown if (len <= 0) 1009*5409fb4eSMark Brown return 0; 1010*5409fb4eSMark Brown 1011*5409fb4eSMark Brown msg[0] = data[0]; 1012*5409fb4eSMark Brown msg[1] = data[1]; 1013*5409fb4eSMark Brown 1014*5409fb4eSMark Brown spi_message_init(&m); 1015*5409fb4eSMark Brown memset(&t, 0, (sizeof t)); 1016*5409fb4eSMark Brown 1017*5409fb4eSMark Brown t.tx_buf = &msg[0]; 1018*5409fb4eSMark Brown t.len = len; 1019*5409fb4eSMark Brown 1020*5409fb4eSMark Brown spi_message_add_tail(&t, &m); 1021*5409fb4eSMark Brown spi_sync(spi, &m); 1022*5409fb4eSMark Brown 1023*5409fb4eSMark Brown return len; 1024*5409fb4eSMark Brown } 1025*5409fb4eSMark Brown 1026*5409fb4eSMark Brown static int __devinit wm8988_spi_probe(struct spi_device *spi) 1027*5409fb4eSMark Brown { 1028*5409fb4eSMark Brown struct wm8988_priv *wm8988; 1029*5409fb4eSMark Brown struct snd_soc_codec *codec; 1030*5409fb4eSMark Brown 1031*5409fb4eSMark Brown wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL); 1032*5409fb4eSMark Brown if (wm8988 == NULL) 1033*5409fb4eSMark Brown return -ENOMEM; 1034*5409fb4eSMark Brown 1035*5409fb4eSMark Brown codec = &wm8988->codec; 1036*5409fb4eSMark Brown codec->hw_write = (hw_write_t)wm8988_spi_write; 1037*5409fb4eSMark Brown codec->control_data = spi; 1038*5409fb4eSMark Brown codec->dev = &spi->dev; 1039*5409fb4eSMark Brown 1040*5409fb4eSMark Brown spi->dev.driver_data = wm8988; 1041*5409fb4eSMark Brown 1042*5409fb4eSMark Brown return wm8988_register(wm8988); 1043*5409fb4eSMark Brown } 1044*5409fb4eSMark Brown 1045*5409fb4eSMark Brown static int __devexit wm8988_spi_remove(struct spi_device *spi) 1046*5409fb4eSMark Brown { 1047*5409fb4eSMark Brown struct wm8988_priv *wm8988 = spi->dev.driver_data; 1048*5409fb4eSMark Brown 1049*5409fb4eSMark Brown wm8988_unregister(wm8988); 1050*5409fb4eSMark Brown 1051*5409fb4eSMark Brown return 0; 1052*5409fb4eSMark Brown } 1053*5409fb4eSMark Brown 1054*5409fb4eSMark Brown static struct spi_driver wm8988_spi_driver = { 1055*5409fb4eSMark Brown .driver = { 1056*5409fb4eSMark Brown .name = "wm8988", 1057*5409fb4eSMark Brown .bus = &spi_bus_type, 1058*5409fb4eSMark Brown .owner = THIS_MODULE, 1059*5409fb4eSMark Brown }, 1060*5409fb4eSMark Brown .probe = wm8988_spi_probe, 1061*5409fb4eSMark Brown .remove = __devexit_p(wm8988_spi_remove), 1062*5409fb4eSMark Brown }; 1063*5409fb4eSMark Brown #endif 1064*5409fb4eSMark Brown 1065*5409fb4eSMark Brown static int __init wm8988_modinit(void) 1066*5409fb4eSMark Brown { 1067*5409fb4eSMark Brown int ret; 1068*5409fb4eSMark Brown 1069*5409fb4eSMark Brown #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 1070*5409fb4eSMark Brown ret = i2c_add_driver(&wm8988_i2c_driver); 1071*5409fb4eSMark Brown if (ret != 0) 1072*5409fb4eSMark Brown pr_err("WM8988: Unable to register I2C driver: %d\n", ret); 1073*5409fb4eSMark Brown #endif 1074*5409fb4eSMark Brown #if defined(CONFIG_SPI_MASTER) 1075*5409fb4eSMark Brown ret = spi_register_driver(&wm8988_spi_driver); 1076*5409fb4eSMark Brown if (ret != 0) 1077*5409fb4eSMark Brown pr_err("WM8988: Unable to register SPI driver: %d\n", ret); 1078*5409fb4eSMark Brown #endif 1079*5409fb4eSMark Brown return ret; 1080*5409fb4eSMark Brown } 1081*5409fb4eSMark Brown module_init(wm8988_modinit); 1082*5409fb4eSMark Brown 1083*5409fb4eSMark Brown static void __exit wm8988_exit(void) 1084*5409fb4eSMark Brown { 1085*5409fb4eSMark Brown #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 1086*5409fb4eSMark Brown i2c_del_driver(&wm8988_i2c_driver); 1087*5409fb4eSMark Brown #endif 1088*5409fb4eSMark Brown #if defined(CONFIG_SPI_MASTER) 1089*5409fb4eSMark Brown spi_unregister_driver(&wm8988_spi_driver); 1090*5409fb4eSMark Brown #endif 1091*5409fb4eSMark Brown } 1092*5409fb4eSMark Brown module_exit(wm8988_exit); 1093*5409fb4eSMark Brown 1094*5409fb4eSMark Brown 1095*5409fb4eSMark Brown MODULE_DESCRIPTION("ASoC WM8988 driver"); 1096*5409fb4eSMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 1097*5409fb4eSMark Brown MODULE_LICENSE("GPL"); 1098