1abadfc92SRichard Purdie /* 2abadfc92SRichard Purdie * wm8750.c -- WM8750 ALSA SoC audio driver 3abadfc92SRichard Purdie * 4abadfc92SRichard Purdie * Copyright 2005 Openedhand Ltd. 5abadfc92SRichard Purdie * 6abadfc92SRichard Purdie * Author: Richard Purdie <richard@openedhand.com> 7abadfc92SRichard Purdie * 8abadfc92SRichard Purdie * Based on WM8753.c 9abadfc92SRichard Purdie * 10abadfc92SRichard Purdie * This program is free software; you can redistribute it and/or modify 11abadfc92SRichard Purdie * it under the terms of the GNU General Public License version 2 as 12abadfc92SRichard Purdie * published by the Free Software Foundation. 13abadfc92SRichard Purdie */ 14abadfc92SRichard Purdie 15abadfc92SRichard Purdie #include <linux/module.h> 16abadfc92SRichard Purdie #include <linux/moduleparam.h> 17abadfc92SRichard Purdie #include <linux/init.h> 18abadfc92SRichard Purdie #include <linux/delay.h> 19abadfc92SRichard Purdie #include <linux/pm.h> 20abadfc92SRichard Purdie #include <linux/i2c.h> 21abadfc92SRichard Purdie #include <linux/platform_device.h> 22abadfc92SRichard Purdie #include <sound/core.h> 23abadfc92SRichard Purdie #include <sound/pcm.h> 24abadfc92SRichard Purdie #include <sound/pcm_params.h> 25abadfc92SRichard Purdie #include <sound/soc.h> 26abadfc92SRichard Purdie #include <sound/soc-dapm.h> 27abadfc92SRichard Purdie #include <sound/initval.h> 28abadfc92SRichard Purdie 29abadfc92SRichard Purdie #include "wm8750.h" 30abadfc92SRichard Purdie 31abadfc92SRichard Purdie #define AUDIO_NAME "WM8750" 324422b606SLiam Girdwood #define WM8750_VERSION "0.12" 33abadfc92SRichard Purdie 344422b606SLiam Girdwood /* codec private data */ 354422b606SLiam Girdwood struct wm8750_priv { 364422b606SLiam Girdwood unsigned int sysclk; 374422b606SLiam Girdwood }; 384422b606SLiam Girdwood 39abadfc92SRichard Purdie /* 40abadfc92SRichard Purdie * wm8750 register cache 41abadfc92SRichard Purdie * We can't read the WM8750 register space when we 42abadfc92SRichard Purdie * are using 2 wire for device control, so we cache them instead. 43abadfc92SRichard Purdie */ 44abadfc92SRichard Purdie static const u16 wm8750_reg[] = { 45abadfc92SRichard Purdie 0x0097, 0x0097, 0x0079, 0x0079, /* 0 */ 46abadfc92SRichard Purdie 0x0000, 0x0008, 0x0000, 0x000a, /* 4 */ 47abadfc92SRichard Purdie 0x0000, 0x0000, 0x00ff, 0x00ff, /* 8 */ 48abadfc92SRichard Purdie 0x000f, 0x000f, 0x0000, 0x0000, /* 12 */ 49abadfc92SRichard Purdie 0x0000, 0x007b, 0x0000, 0x0032, /* 16 */ 50abadfc92SRichard Purdie 0x0000, 0x00c3, 0x00c3, 0x00c0, /* 20 */ 51abadfc92SRichard Purdie 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */ 52abadfc92SRichard Purdie 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */ 53abadfc92SRichard Purdie 0x0000, 0x0000, 0x0050, 0x0050, /* 32 */ 54abadfc92SRichard Purdie 0x0050, 0x0050, 0x0050, 0x0050, /* 36 */ 55abadfc92SRichard Purdie 0x0079, 0x0079, 0x0079, /* 40 */ 56abadfc92SRichard Purdie }; 57abadfc92SRichard Purdie 58abadfc92SRichard Purdie /* 59abadfc92SRichard Purdie * read wm8750 register cache 60abadfc92SRichard Purdie */ 61abadfc92SRichard Purdie static inline unsigned int wm8750_read_reg_cache(struct snd_soc_codec *codec, 62abadfc92SRichard Purdie unsigned int reg) 63abadfc92SRichard Purdie { 64abadfc92SRichard Purdie u16 *cache = codec->reg_cache; 65abadfc92SRichard Purdie if (reg > WM8750_CACHE_REGNUM) 66abadfc92SRichard Purdie return -1; 67abadfc92SRichard Purdie return cache[reg]; 68abadfc92SRichard Purdie } 69abadfc92SRichard Purdie 70abadfc92SRichard Purdie /* 71abadfc92SRichard Purdie * write wm8750 register cache 72abadfc92SRichard Purdie */ 73abadfc92SRichard Purdie static inline void wm8750_write_reg_cache(struct snd_soc_codec *codec, 74abadfc92SRichard Purdie unsigned int reg, unsigned int value) 75abadfc92SRichard Purdie { 76abadfc92SRichard Purdie u16 *cache = codec->reg_cache; 77abadfc92SRichard Purdie if (reg > WM8750_CACHE_REGNUM) 78abadfc92SRichard Purdie return; 79abadfc92SRichard Purdie cache[reg] = value; 80abadfc92SRichard Purdie } 81abadfc92SRichard Purdie 82abadfc92SRichard Purdie static int wm8750_write(struct snd_soc_codec *codec, unsigned int reg, 83abadfc92SRichard Purdie unsigned int value) 84abadfc92SRichard Purdie { 85abadfc92SRichard Purdie u8 data[2]; 86abadfc92SRichard Purdie 87abadfc92SRichard Purdie /* data is 88abadfc92SRichard Purdie * D15..D9 WM8753 register offset 89abadfc92SRichard Purdie * D8...D0 register data 90abadfc92SRichard Purdie */ 91abadfc92SRichard Purdie data[0] = (reg << 1) | ((value >> 8) & 0x0001); 92abadfc92SRichard Purdie data[1] = value & 0x00ff; 93abadfc92SRichard Purdie 94abadfc92SRichard Purdie wm8750_write_reg_cache(codec, reg, value); 95abadfc92SRichard Purdie if (codec->hw_write(codec->control_data, data, 2) == 2) 96abadfc92SRichard Purdie return 0; 97abadfc92SRichard Purdie else 98abadfc92SRichard Purdie return -EIO; 99abadfc92SRichard Purdie } 100abadfc92SRichard Purdie 101abadfc92SRichard Purdie #define wm8750_reset(c) wm8750_write(c, WM8750_RESET, 0) 102abadfc92SRichard Purdie 103abadfc92SRichard Purdie /* 104abadfc92SRichard Purdie * WM8750 Controls 105abadfc92SRichard Purdie */ 106abadfc92SRichard Purdie static const char *wm8750_bass[] = {"Linear Control", "Adaptive Boost"}; 107abadfc92SRichard Purdie static const char *wm8750_bass_filter[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" }; 108abadfc92SRichard Purdie static const char *wm8750_treble[] = {"8kHz", "4kHz"}; 109abadfc92SRichard Purdie static const char *wm8750_3d_lc[] = {"200Hz", "500Hz"}; 110abadfc92SRichard Purdie static const char *wm8750_3d_uc[] = {"2.2kHz", "1.5kHz"}; 111abadfc92SRichard Purdie static const char *wm8750_3d_func[] = {"Capture", "Playback"}; 112abadfc92SRichard Purdie static const char *wm8750_alc_func[] = {"Off", "Right", "Left", "Stereo"}; 113abadfc92SRichard Purdie static const char *wm8750_ng_type[] = {"Constant PGA Gain", 114abadfc92SRichard Purdie "Mute ADC Output"}; 115abadfc92SRichard Purdie static const char *wm8750_line_mux[] = {"Line 1", "Line 2", "Line 3", "PGA", 116abadfc92SRichard Purdie "Differential"}; 117abadfc92SRichard Purdie static const char *wm8750_pga_sel[] = {"Line 1", "Line 2", "Line 3", 118abadfc92SRichard Purdie "Differential"}; 119abadfc92SRichard Purdie static const char *wm8750_out3[] = {"VREF", "ROUT1 + Vol", "MonoOut", 120abadfc92SRichard Purdie "ROUT1"}; 121abadfc92SRichard Purdie static const char *wm8750_diff_sel[] = {"Line 1", "Line 2"}; 122abadfc92SRichard Purdie static const char *wm8750_adcpol[] = {"Normal", "L Invert", "R Invert", 123abadfc92SRichard Purdie "L + R Invert"}; 124abadfc92SRichard Purdie static const char *wm8750_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; 125abadfc92SRichard Purdie static const char *wm8750_mono_mux[] = {"Stereo", "Mono (Left)", 126abadfc92SRichard Purdie "Mono (Right)", "Digital Mono"}; 127abadfc92SRichard Purdie 128abadfc92SRichard Purdie static const struct soc_enum wm8750_enum[] = { 129abadfc92SRichard Purdie SOC_ENUM_SINGLE(WM8750_BASS, 7, 2, wm8750_bass), 130abadfc92SRichard Purdie SOC_ENUM_SINGLE(WM8750_BASS, 6, 2, wm8750_bass_filter), 131abadfc92SRichard Purdie SOC_ENUM_SINGLE(WM8750_TREBLE, 6, 2, wm8750_treble), 132abadfc92SRichard Purdie SOC_ENUM_SINGLE(WM8750_3D, 5, 2, wm8750_3d_lc), 133abadfc92SRichard Purdie SOC_ENUM_SINGLE(WM8750_3D, 6, 2, wm8750_3d_uc), 134abadfc92SRichard Purdie SOC_ENUM_SINGLE(WM8750_3D, 7, 2, wm8750_3d_func), 135abadfc92SRichard Purdie SOC_ENUM_SINGLE(WM8750_ALC1, 7, 4, wm8750_alc_func), 136abadfc92SRichard Purdie SOC_ENUM_SINGLE(WM8750_NGATE, 1, 2, wm8750_ng_type), 137abadfc92SRichard Purdie SOC_ENUM_SINGLE(WM8750_LOUTM1, 0, 5, wm8750_line_mux), 138abadfc92SRichard Purdie SOC_ENUM_SINGLE(WM8750_ROUTM1, 0, 5, wm8750_line_mux), 139abadfc92SRichard Purdie SOC_ENUM_SINGLE(WM8750_LADCIN, 6, 4, wm8750_pga_sel), /* 10 */ 140abadfc92SRichard Purdie SOC_ENUM_SINGLE(WM8750_RADCIN, 6, 4, wm8750_pga_sel), 141abadfc92SRichard Purdie SOC_ENUM_SINGLE(WM8750_ADCTL2, 7, 4, wm8750_out3), 142abadfc92SRichard Purdie SOC_ENUM_SINGLE(WM8750_ADCIN, 8, 2, wm8750_diff_sel), 143abadfc92SRichard Purdie SOC_ENUM_SINGLE(WM8750_ADCDAC, 5, 4, wm8750_adcpol), 144abadfc92SRichard Purdie SOC_ENUM_SINGLE(WM8750_ADCDAC, 1, 4, wm8750_deemph), 145abadfc92SRichard Purdie SOC_ENUM_SINGLE(WM8750_ADCIN, 6, 4, wm8750_mono_mux), /* 16 */ 146abadfc92SRichard Purdie 147abadfc92SRichard Purdie }; 148abadfc92SRichard Purdie 149abadfc92SRichard Purdie static const struct snd_kcontrol_new wm8750_snd_controls[] = { 150abadfc92SRichard Purdie 151abadfc92SRichard Purdie SOC_DOUBLE_R("Capture Volume", WM8750_LINVOL, WM8750_RINVOL, 0, 63, 0), 152abadfc92SRichard Purdie SOC_DOUBLE_R("Capture ZC Switch", WM8750_LINVOL, WM8750_RINVOL, 6, 1, 0), 153abadfc92SRichard Purdie SOC_DOUBLE_R("Capture Switch", WM8750_LINVOL, WM8750_RINVOL, 7, 1, 1), 154abadfc92SRichard Purdie 155bd903b6eSLiam Girdwood SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8750_LOUT1V, 156abadfc92SRichard Purdie WM8750_ROUT1V, 7, 1, 0), 157bd903b6eSLiam Girdwood SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8750_LOUT2V, 158abadfc92SRichard Purdie WM8750_ROUT2V, 7, 1, 0), 159abadfc92SRichard Purdie 160abadfc92SRichard Purdie SOC_ENUM("Playback De-emphasis", wm8750_enum[15]), 161abadfc92SRichard Purdie 162abadfc92SRichard Purdie SOC_ENUM("Capture Polarity", wm8750_enum[14]), 163abadfc92SRichard Purdie SOC_SINGLE("Playback 6dB Attenuate", WM8750_ADCDAC, 7, 1, 0), 164abadfc92SRichard Purdie SOC_SINGLE("Capture 6dB Attenuate", WM8750_ADCDAC, 8, 1, 0), 165abadfc92SRichard Purdie 166abadfc92SRichard Purdie SOC_DOUBLE_R("PCM Volume", WM8750_LDAC, WM8750_RDAC, 0, 255, 0), 167abadfc92SRichard Purdie 168abadfc92SRichard Purdie SOC_ENUM("Bass Boost", wm8750_enum[0]), 169abadfc92SRichard Purdie SOC_ENUM("Bass Filter", wm8750_enum[1]), 170abadfc92SRichard Purdie SOC_SINGLE("Bass Volume", WM8750_BASS, 0, 15, 1), 171abadfc92SRichard Purdie 1726a7b8cf4SStanislav Brabec SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 1), 173abadfc92SRichard Purdie SOC_ENUM("Treble Cut-off", wm8750_enum[2]), 174abadfc92SRichard Purdie 175abadfc92SRichard Purdie SOC_SINGLE("3D Switch", WM8750_3D, 0, 1, 0), 176abadfc92SRichard Purdie SOC_SINGLE("3D Volume", WM8750_3D, 1, 15, 0), 177abadfc92SRichard Purdie SOC_ENUM("3D Lower Cut-off", wm8750_enum[3]), 178abadfc92SRichard Purdie SOC_ENUM("3D Upper Cut-off", wm8750_enum[4]), 179abadfc92SRichard Purdie SOC_ENUM("3D Mode", wm8750_enum[5]), 180abadfc92SRichard Purdie 181abadfc92SRichard Purdie SOC_SINGLE("ALC Capture Target Volume", WM8750_ALC1, 0, 7, 0), 182abadfc92SRichard Purdie SOC_SINGLE("ALC Capture Max Volume", WM8750_ALC1, 4, 7, 0), 183abadfc92SRichard Purdie SOC_ENUM("ALC Capture Function", wm8750_enum[6]), 184abadfc92SRichard Purdie SOC_SINGLE("ALC Capture ZC Switch", WM8750_ALC2, 7, 1, 0), 185abadfc92SRichard Purdie SOC_SINGLE("ALC Capture Hold Time", WM8750_ALC2, 0, 15, 0), 186abadfc92SRichard Purdie SOC_SINGLE("ALC Capture Decay Time", WM8750_ALC3, 4, 15, 0), 187abadfc92SRichard Purdie SOC_SINGLE("ALC Capture Attack Time", WM8750_ALC3, 0, 15, 0), 188abadfc92SRichard Purdie SOC_SINGLE("ALC Capture NG Threshold", WM8750_NGATE, 3, 31, 0), 189abadfc92SRichard Purdie SOC_ENUM("ALC Capture NG Type", wm8750_enum[4]), 190abadfc92SRichard Purdie SOC_SINGLE("ALC Capture NG Switch", WM8750_NGATE, 0, 1, 0), 191abadfc92SRichard Purdie 192abadfc92SRichard Purdie SOC_SINGLE("Left ADC Capture Volume", WM8750_LADC, 0, 255, 0), 193abadfc92SRichard Purdie SOC_SINGLE("Right ADC Capture Volume", WM8750_RADC, 0, 255, 0), 194abadfc92SRichard Purdie 195abadfc92SRichard Purdie SOC_SINGLE("ZC Timeout Switch", WM8750_ADCTL1, 0, 1, 0), 196abadfc92SRichard Purdie SOC_SINGLE("Playback Invert Switch", WM8750_ADCTL1, 1, 1, 0), 197abadfc92SRichard Purdie 198bd903b6eSLiam Girdwood SOC_SINGLE("Right Speaker Playback Invert Switch", WM8750_ADCTL2, 4, 1, 0), 199abadfc92SRichard Purdie 200abadfc92SRichard Purdie /* Unimplemented */ 201abadfc92SRichard Purdie /* ADCDAC Bit 0 - ADCHPD */ 202abadfc92SRichard Purdie /* ADCDAC Bit 4 - HPOR */ 203abadfc92SRichard Purdie /* ADCTL1 Bit 2,3 - DATSEL */ 204abadfc92SRichard Purdie /* ADCTL1 Bit 4,5 - DMONOMIX */ 205abadfc92SRichard Purdie /* ADCTL1 Bit 6,7 - VSEL */ 206abadfc92SRichard Purdie /* ADCTL2 Bit 2 - LRCM */ 207abadfc92SRichard Purdie /* ADCTL2 Bit 3 - TRI */ 208abadfc92SRichard Purdie /* ADCTL3 Bit 5 - HPFLREN */ 209abadfc92SRichard Purdie /* ADCTL3 Bit 6 - VROI */ 210abadfc92SRichard Purdie /* ADCTL3 Bit 7,8 - ADCLRM */ 211abadfc92SRichard Purdie /* ADCIN Bit 4 - LDCM */ 212abadfc92SRichard Purdie /* ADCIN Bit 5 - RDCM */ 213abadfc92SRichard Purdie 214abadfc92SRichard Purdie SOC_DOUBLE_R("Mic Boost", WM8750_LADCIN, WM8750_RADCIN, 4, 3, 0), 215abadfc92SRichard Purdie 216abadfc92SRichard Purdie SOC_DOUBLE_R("Bypass Left Playback Volume", WM8750_LOUTM1, 217abadfc92SRichard Purdie WM8750_LOUTM2, 4, 7, 1), 218abadfc92SRichard Purdie SOC_DOUBLE_R("Bypass Right Playback Volume", WM8750_ROUTM1, 219abadfc92SRichard Purdie WM8750_ROUTM2, 4, 7, 1), 220abadfc92SRichard Purdie SOC_DOUBLE_R("Bypass Mono Playback Volume", WM8750_MOUTM1, 221abadfc92SRichard Purdie WM8750_MOUTM2, 4, 7, 1), 222abadfc92SRichard Purdie 223abadfc92SRichard Purdie SOC_SINGLE("Mono Playback ZC Switch", WM8750_MOUTV, 7, 1, 0), 224abadfc92SRichard Purdie 225bd903b6eSLiam Girdwood SOC_DOUBLE_R("Headphone Playback Volume", WM8750_LOUT1V, WM8750_ROUT1V, 226bd903b6eSLiam Girdwood 0, 127, 0), 227bd903b6eSLiam Girdwood SOC_DOUBLE_R("Speaker Playback Volume", WM8750_LOUT2V, WM8750_ROUT2V, 228bd903b6eSLiam Girdwood 0, 127, 0), 229abadfc92SRichard Purdie 230abadfc92SRichard Purdie SOC_SINGLE("Mono Playback Volume", WM8750_MOUTV, 0, 127, 0), 231abadfc92SRichard Purdie 232abadfc92SRichard Purdie }; 233abadfc92SRichard Purdie 234abadfc92SRichard Purdie /* add non dapm controls */ 235abadfc92SRichard Purdie static int wm8750_add_controls(struct snd_soc_codec *codec) 236abadfc92SRichard Purdie { 237abadfc92SRichard Purdie int err, i; 238abadfc92SRichard Purdie 239abadfc92SRichard Purdie for (i = 0; i < ARRAY_SIZE(wm8750_snd_controls); i++) { 240abadfc92SRichard Purdie err = snd_ctl_add(codec->card, 24142f3030fSMark Brown snd_soc_cnew(&wm8750_snd_controls[i], 24242f3030fSMark Brown codec, NULL)); 243abadfc92SRichard Purdie if (err < 0) 244abadfc92SRichard Purdie return err; 245abadfc92SRichard Purdie } 246abadfc92SRichard Purdie return 0; 247abadfc92SRichard Purdie } 248abadfc92SRichard Purdie 249abadfc92SRichard Purdie /* 250abadfc92SRichard Purdie * DAPM Controls 251abadfc92SRichard Purdie */ 252abadfc92SRichard Purdie 253abadfc92SRichard Purdie /* Left Mixer */ 254abadfc92SRichard Purdie static const struct snd_kcontrol_new wm8750_left_mixer_controls[] = { 255abadfc92SRichard Purdie SOC_DAPM_SINGLE("Playback Switch", WM8750_LOUTM1, 8, 1, 0), 256abadfc92SRichard Purdie SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_LOUTM1, 7, 1, 0), 257abadfc92SRichard Purdie SOC_DAPM_SINGLE("Right Playback Switch", WM8750_LOUTM2, 8, 1, 0), 258abadfc92SRichard Purdie SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_LOUTM2, 7, 1, 0), 259abadfc92SRichard Purdie }; 260abadfc92SRichard Purdie 261abadfc92SRichard Purdie /* Right Mixer */ 262abadfc92SRichard Purdie static const struct snd_kcontrol_new wm8750_right_mixer_controls[] = { 263abadfc92SRichard Purdie SOC_DAPM_SINGLE("Left Playback Switch", WM8750_ROUTM1, 8, 1, 0), 264abadfc92SRichard Purdie SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_ROUTM1, 7, 1, 0), 265abadfc92SRichard Purdie SOC_DAPM_SINGLE("Playback Switch", WM8750_ROUTM2, 8, 1, 0), 266abadfc92SRichard Purdie SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_ROUTM2, 7, 1, 0), 267abadfc92SRichard Purdie }; 268abadfc92SRichard Purdie 269abadfc92SRichard Purdie /* Mono Mixer */ 270abadfc92SRichard Purdie static const struct snd_kcontrol_new wm8750_mono_mixer_controls[] = { 271abadfc92SRichard Purdie SOC_DAPM_SINGLE("Left Playback Switch", WM8750_MOUTM1, 8, 1, 0), 272abadfc92SRichard Purdie SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_MOUTM1, 7, 1, 0), 273abadfc92SRichard Purdie SOC_DAPM_SINGLE("Right Playback Switch", WM8750_MOUTM2, 8, 1, 0), 274abadfc92SRichard Purdie SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_MOUTM2, 7, 1, 0), 275abadfc92SRichard Purdie }; 276abadfc92SRichard Purdie 277abadfc92SRichard Purdie /* Left Line Mux */ 278abadfc92SRichard Purdie static const struct snd_kcontrol_new wm8750_left_line_controls = 279abadfc92SRichard Purdie SOC_DAPM_ENUM("Route", wm8750_enum[8]); 280abadfc92SRichard Purdie 281abadfc92SRichard Purdie /* Right Line Mux */ 282abadfc92SRichard Purdie static const struct snd_kcontrol_new wm8750_right_line_controls = 283abadfc92SRichard Purdie SOC_DAPM_ENUM("Route", wm8750_enum[9]); 284abadfc92SRichard Purdie 285abadfc92SRichard Purdie /* Left PGA Mux */ 286abadfc92SRichard Purdie static const struct snd_kcontrol_new wm8750_left_pga_controls = 287abadfc92SRichard Purdie SOC_DAPM_ENUM("Route", wm8750_enum[10]); 288abadfc92SRichard Purdie 289abadfc92SRichard Purdie /* Right PGA Mux */ 290abadfc92SRichard Purdie static const struct snd_kcontrol_new wm8750_right_pga_controls = 291abadfc92SRichard Purdie SOC_DAPM_ENUM("Route", wm8750_enum[11]); 292abadfc92SRichard Purdie 293abadfc92SRichard Purdie /* Out 3 Mux */ 294abadfc92SRichard Purdie static const struct snd_kcontrol_new wm8750_out3_controls = 295abadfc92SRichard Purdie SOC_DAPM_ENUM("Route", wm8750_enum[12]); 296abadfc92SRichard Purdie 297abadfc92SRichard Purdie /* Differential Mux */ 298abadfc92SRichard Purdie static const struct snd_kcontrol_new wm8750_diffmux_controls = 299abadfc92SRichard Purdie SOC_DAPM_ENUM("Route", wm8750_enum[13]); 300abadfc92SRichard Purdie 301abadfc92SRichard Purdie /* Mono ADC Mux */ 302abadfc92SRichard Purdie static const struct snd_kcontrol_new wm8750_monomux_controls = 303abadfc92SRichard Purdie SOC_DAPM_ENUM("Route", wm8750_enum[16]); 304abadfc92SRichard Purdie 305abadfc92SRichard Purdie static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { 306abadfc92SRichard Purdie SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, 307abadfc92SRichard Purdie &wm8750_left_mixer_controls[0], 308abadfc92SRichard Purdie ARRAY_SIZE(wm8750_left_mixer_controls)), 309abadfc92SRichard Purdie SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, 310abadfc92SRichard Purdie &wm8750_right_mixer_controls[0], 311abadfc92SRichard Purdie ARRAY_SIZE(wm8750_right_mixer_controls)), 312abadfc92SRichard Purdie SND_SOC_DAPM_MIXER("Mono Mixer", WM8750_PWR2, 2, 0, 313abadfc92SRichard Purdie &wm8750_mono_mixer_controls[0], 314abadfc92SRichard Purdie ARRAY_SIZE(wm8750_mono_mixer_controls)), 315abadfc92SRichard Purdie 316abadfc92SRichard Purdie SND_SOC_DAPM_PGA("Right Out 2", WM8750_PWR2, 3, 0, NULL, 0), 317abadfc92SRichard Purdie SND_SOC_DAPM_PGA("Left Out 2", WM8750_PWR2, 4, 0, NULL, 0), 318abadfc92SRichard Purdie SND_SOC_DAPM_PGA("Right Out 1", WM8750_PWR2, 5, 0, NULL, 0), 319abadfc92SRichard Purdie SND_SOC_DAPM_PGA("Left Out 1", WM8750_PWR2, 6, 0, NULL, 0), 320abadfc92SRichard Purdie SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8750_PWR2, 7, 0), 321abadfc92SRichard Purdie SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8750_PWR2, 8, 0), 322abadfc92SRichard Purdie 323abadfc92SRichard Purdie SND_SOC_DAPM_MICBIAS("Mic Bias", WM8750_PWR1, 1, 0), 324abadfc92SRichard Purdie SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8750_PWR1, 2, 0), 325abadfc92SRichard Purdie SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8750_PWR1, 3, 0), 326abadfc92SRichard Purdie 327abadfc92SRichard Purdie SND_SOC_DAPM_MUX("Left PGA Mux", WM8750_PWR1, 5, 0, 328abadfc92SRichard Purdie &wm8750_left_pga_controls), 329abadfc92SRichard Purdie SND_SOC_DAPM_MUX("Right PGA Mux", WM8750_PWR1, 4, 0, 330abadfc92SRichard Purdie &wm8750_right_pga_controls), 331abadfc92SRichard Purdie SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, 332abadfc92SRichard Purdie &wm8750_left_line_controls), 333abadfc92SRichard Purdie SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, 334abadfc92SRichard Purdie &wm8750_right_line_controls), 335abadfc92SRichard Purdie 336abadfc92SRichard Purdie SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, &wm8750_out3_controls), 337abadfc92SRichard Purdie SND_SOC_DAPM_PGA("Out 3", WM8750_PWR2, 1, 0, NULL, 0), 338abadfc92SRichard Purdie SND_SOC_DAPM_PGA("Mono Out 1", WM8750_PWR2, 2, 0, NULL, 0), 339abadfc92SRichard Purdie 340abadfc92SRichard Purdie SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, 341abadfc92SRichard Purdie &wm8750_diffmux_controls), 342abadfc92SRichard Purdie SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, 343abadfc92SRichard Purdie &wm8750_monomux_controls), 344abadfc92SRichard Purdie SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, 345abadfc92SRichard Purdie &wm8750_monomux_controls), 346abadfc92SRichard Purdie 347abadfc92SRichard Purdie SND_SOC_DAPM_OUTPUT("LOUT1"), 348abadfc92SRichard Purdie SND_SOC_DAPM_OUTPUT("ROUT1"), 349abadfc92SRichard Purdie SND_SOC_DAPM_OUTPUT("LOUT2"), 350abadfc92SRichard Purdie SND_SOC_DAPM_OUTPUT("ROUT2"), 35123ba79bdSDmitry Baryshkov SND_SOC_DAPM_OUTPUT("MONO1"), 352abadfc92SRichard Purdie SND_SOC_DAPM_OUTPUT("OUT3"), 35304489eebSDmitry Baryshkov SND_SOC_DAPM_OUTPUT("VREF"), 354abadfc92SRichard Purdie 355abadfc92SRichard Purdie SND_SOC_DAPM_INPUT("LINPUT1"), 356abadfc92SRichard Purdie SND_SOC_DAPM_INPUT("LINPUT2"), 357abadfc92SRichard Purdie SND_SOC_DAPM_INPUT("LINPUT3"), 358abadfc92SRichard Purdie SND_SOC_DAPM_INPUT("RINPUT1"), 359abadfc92SRichard Purdie SND_SOC_DAPM_INPUT("RINPUT2"), 360abadfc92SRichard Purdie SND_SOC_DAPM_INPUT("RINPUT3"), 361abadfc92SRichard Purdie }; 362abadfc92SRichard Purdie 363a65f0568SMark Brown static const struct snd_soc_dapm_route audio_map[] = { 364abadfc92SRichard Purdie /* left mixer */ 365abadfc92SRichard Purdie {"Left Mixer", "Playback Switch", "Left DAC"}, 366abadfc92SRichard Purdie {"Left Mixer", "Left Bypass Switch", "Left Line Mux"}, 367abadfc92SRichard Purdie {"Left Mixer", "Right Playback Switch", "Right DAC"}, 368abadfc92SRichard Purdie {"Left Mixer", "Right Bypass Switch", "Right Line Mux"}, 369abadfc92SRichard Purdie 370abadfc92SRichard Purdie /* right mixer */ 371abadfc92SRichard Purdie {"Right Mixer", "Left Playback Switch", "Left DAC"}, 372abadfc92SRichard Purdie {"Right Mixer", "Left Bypass Switch", "Left Line Mux"}, 373abadfc92SRichard Purdie {"Right Mixer", "Playback Switch", "Right DAC"}, 374abadfc92SRichard Purdie {"Right Mixer", "Right Bypass Switch", "Right Line Mux"}, 375abadfc92SRichard Purdie 376abadfc92SRichard Purdie /* left out 1 */ 377abadfc92SRichard Purdie {"Left Out 1", NULL, "Left Mixer"}, 378abadfc92SRichard Purdie {"LOUT1", NULL, "Left Out 1"}, 379abadfc92SRichard Purdie 380abadfc92SRichard Purdie /* left out 2 */ 381abadfc92SRichard Purdie {"Left Out 2", NULL, "Left Mixer"}, 382abadfc92SRichard Purdie {"LOUT2", NULL, "Left Out 2"}, 383abadfc92SRichard Purdie 384abadfc92SRichard Purdie /* right out 1 */ 385abadfc92SRichard Purdie {"Right Out 1", NULL, "Right Mixer"}, 386abadfc92SRichard Purdie {"ROUT1", NULL, "Right Out 1"}, 387abadfc92SRichard Purdie 388abadfc92SRichard Purdie /* right out 2 */ 389abadfc92SRichard Purdie {"Right Out 2", NULL, "Right Mixer"}, 390abadfc92SRichard Purdie {"ROUT2", NULL, "Right Out 2"}, 391abadfc92SRichard Purdie 392abadfc92SRichard Purdie /* mono mixer */ 393abadfc92SRichard Purdie {"Mono Mixer", "Left Playback Switch", "Left DAC"}, 394abadfc92SRichard Purdie {"Mono Mixer", "Left Bypass Switch", "Left Line Mux"}, 395abadfc92SRichard Purdie {"Mono Mixer", "Right Playback Switch", "Right DAC"}, 396abadfc92SRichard Purdie {"Mono Mixer", "Right Bypass Switch", "Right Line Mux"}, 397abadfc92SRichard Purdie 398abadfc92SRichard Purdie /* mono out */ 399abadfc92SRichard Purdie {"Mono Out 1", NULL, "Mono Mixer"}, 400abadfc92SRichard Purdie {"MONO1", NULL, "Mono Out 1"}, 401abadfc92SRichard Purdie 402abadfc92SRichard Purdie /* out 3 */ 403abadfc92SRichard Purdie {"Out3 Mux", "VREF", "VREF"}, 404abadfc92SRichard Purdie {"Out3 Mux", "ROUT1 + Vol", "ROUT1"}, 405abadfc92SRichard Purdie {"Out3 Mux", "ROUT1", "Right Mixer"}, 406abadfc92SRichard Purdie {"Out3 Mux", "MonoOut", "MONO1"}, 407abadfc92SRichard Purdie {"Out 3", NULL, "Out3 Mux"}, 408abadfc92SRichard Purdie {"OUT3", NULL, "Out 3"}, 409abadfc92SRichard Purdie 410abadfc92SRichard Purdie /* Left Line Mux */ 411abadfc92SRichard Purdie {"Left Line Mux", "Line 1", "LINPUT1"}, 412abadfc92SRichard Purdie {"Left Line Mux", "Line 2", "LINPUT2"}, 413abadfc92SRichard Purdie {"Left Line Mux", "Line 3", "LINPUT3"}, 414abadfc92SRichard Purdie {"Left Line Mux", "PGA", "Left PGA Mux"}, 415abadfc92SRichard Purdie {"Left Line Mux", "Differential", "Differential Mux"}, 416abadfc92SRichard Purdie 417abadfc92SRichard Purdie /* Right Line Mux */ 418abadfc92SRichard Purdie {"Right Line Mux", "Line 1", "RINPUT1"}, 419abadfc92SRichard Purdie {"Right Line Mux", "Line 2", "RINPUT2"}, 420abadfc92SRichard Purdie {"Right Line Mux", "Line 3", "RINPUT3"}, 421abadfc92SRichard Purdie {"Right Line Mux", "PGA", "Right PGA Mux"}, 422abadfc92SRichard Purdie {"Right Line Mux", "Differential", "Differential Mux"}, 423abadfc92SRichard Purdie 424abadfc92SRichard Purdie /* Left PGA Mux */ 425abadfc92SRichard Purdie {"Left PGA Mux", "Line 1", "LINPUT1"}, 426abadfc92SRichard Purdie {"Left PGA Mux", "Line 2", "LINPUT2"}, 427abadfc92SRichard Purdie {"Left PGA Mux", "Line 3", "LINPUT3"}, 428abadfc92SRichard Purdie {"Left PGA Mux", "Differential", "Differential Mux"}, 429abadfc92SRichard Purdie 430abadfc92SRichard Purdie /* Right PGA Mux */ 431abadfc92SRichard Purdie {"Right PGA Mux", "Line 1", "RINPUT1"}, 432abadfc92SRichard Purdie {"Right PGA Mux", "Line 2", "RINPUT2"}, 433abadfc92SRichard Purdie {"Right PGA Mux", "Line 3", "RINPUT3"}, 434abadfc92SRichard Purdie {"Right PGA Mux", "Differential", "Differential Mux"}, 435abadfc92SRichard Purdie 436abadfc92SRichard Purdie /* Differential Mux */ 437abadfc92SRichard Purdie {"Differential Mux", "Line 1", "LINPUT1"}, 438abadfc92SRichard Purdie {"Differential Mux", "Line 1", "RINPUT1"}, 439abadfc92SRichard Purdie {"Differential Mux", "Line 2", "LINPUT2"}, 440abadfc92SRichard Purdie {"Differential Mux", "Line 2", "RINPUT2"}, 441abadfc92SRichard Purdie 442abadfc92SRichard Purdie /* Left ADC Mux */ 443abadfc92SRichard Purdie {"Left ADC Mux", "Stereo", "Left PGA Mux"}, 444abadfc92SRichard Purdie {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"}, 445abadfc92SRichard Purdie {"Left ADC Mux", "Digital Mono", "Left PGA Mux"}, 446abadfc92SRichard Purdie 447abadfc92SRichard Purdie /* Right ADC Mux */ 448abadfc92SRichard Purdie {"Right ADC Mux", "Stereo", "Right PGA Mux"}, 449abadfc92SRichard Purdie {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"}, 450abadfc92SRichard Purdie {"Right ADC Mux", "Digital Mono", "Right PGA Mux"}, 451abadfc92SRichard Purdie 452abadfc92SRichard Purdie /* ADC */ 453abadfc92SRichard Purdie {"Left ADC", NULL, "Left ADC Mux"}, 454abadfc92SRichard Purdie {"Right ADC", NULL, "Right ADC Mux"}, 455abadfc92SRichard Purdie }; 456abadfc92SRichard Purdie 457abadfc92SRichard Purdie static int wm8750_add_widgets(struct snd_soc_codec *codec) 458abadfc92SRichard Purdie { 459a65f0568SMark Brown snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets, 460a65f0568SMark Brown ARRAY_SIZE(wm8750_dapm_widgets)); 461abadfc92SRichard Purdie 462a65f0568SMark Brown snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); 463abadfc92SRichard Purdie 464abadfc92SRichard Purdie snd_soc_dapm_new_widgets(codec); 465abadfc92SRichard Purdie return 0; 466abadfc92SRichard Purdie } 467abadfc92SRichard Purdie 468abadfc92SRichard Purdie struct _coeff_div { 469abadfc92SRichard Purdie u32 mclk; 470abadfc92SRichard Purdie u32 rate; 471abadfc92SRichard Purdie u16 fs; 472abadfc92SRichard Purdie u8 sr:5; 473abadfc92SRichard Purdie u8 usb:1; 474abadfc92SRichard Purdie }; 475abadfc92SRichard Purdie 476abadfc92SRichard Purdie /* codec hifi mclk clock divider coefficients */ 477abadfc92SRichard Purdie static const struct _coeff_div coeff_div[] = { 478abadfc92SRichard Purdie /* 8k */ 479abadfc92SRichard Purdie {12288000, 8000, 1536, 0x6, 0x0}, 480abadfc92SRichard Purdie {11289600, 8000, 1408, 0x16, 0x0}, 481abadfc92SRichard Purdie {18432000, 8000, 2304, 0x7, 0x0}, 482abadfc92SRichard Purdie {16934400, 8000, 2112, 0x17, 0x0}, 483abadfc92SRichard Purdie {12000000, 8000, 1500, 0x6, 0x1}, 484abadfc92SRichard Purdie 485abadfc92SRichard Purdie /* 11.025k */ 486abadfc92SRichard Purdie {11289600, 11025, 1024, 0x18, 0x0}, 487abadfc92SRichard Purdie {16934400, 11025, 1536, 0x19, 0x0}, 488abadfc92SRichard Purdie {12000000, 11025, 1088, 0x19, 0x1}, 489abadfc92SRichard Purdie 490abadfc92SRichard Purdie /* 16k */ 491abadfc92SRichard Purdie {12288000, 16000, 768, 0xa, 0x0}, 492abadfc92SRichard Purdie {18432000, 16000, 1152, 0xb, 0x0}, 493abadfc92SRichard Purdie {12000000, 16000, 750, 0xa, 0x1}, 494abadfc92SRichard Purdie 495abadfc92SRichard Purdie /* 22.05k */ 496abadfc92SRichard Purdie {11289600, 22050, 512, 0x1a, 0x0}, 497abadfc92SRichard Purdie {16934400, 22050, 768, 0x1b, 0x0}, 498abadfc92SRichard Purdie {12000000, 22050, 544, 0x1b, 0x1}, 499abadfc92SRichard Purdie 500abadfc92SRichard Purdie /* 32k */ 501abadfc92SRichard Purdie {12288000, 32000, 384, 0xc, 0x0}, 502abadfc92SRichard Purdie {18432000, 32000, 576, 0xd, 0x0}, 503abadfc92SRichard Purdie {12000000, 32000, 375, 0xa, 0x1}, 504abadfc92SRichard Purdie 505abadfc92SRichard Purdie /* 44.1k */ 506abadfc92SRichard Purdie {11289600, 44100, 256, 0x10, 0x0}, 507abadfc92SRichard Purdie {16934400, 44100, 384, 0x11, 0x0}, 508abadfc92SRichard Purdie {12000000, 44100, 272, 0x11, 0x1}, 509abadfc92SRichard Purdie 510abadfc92SRichard Purdie /* 48k */ 511abadfc92SRichard Purdie {12288000, 48000, 256, 0x0, 0x0}, 512abadfc92SRichard Purdie {18432000, 48000, 384, 0x1, 0x0}, 513abadfc92SRichard Purdie {12000000, 48000, 250, 0x0, 0x1}, 514abadfc92SRichard Purdie 515abadfc92SRichard Purdie /* 88.2k */ 516abadfc92SRichard Purdie {11289600, 88200, 128, 0x1e, 0x0}, 517abadfc92SRichard Purdie {16934400, 88200, 192, 0x1f, 0x0}, 518abadfc92SRichard Purdie {12000000, 88200, 136, 0x1f, 0x1}, 519abadfc92SRichard Purdie 520abadfc92SRichard Purdie /* 96k */ 521abadfc92SRichard Purdie {12288000, 96000, 128, 0xe, 0x0}, 522abadfc92SRichard Purdie {18432000, 96000, 192, 0xf, 0x0}, 523abadfc92SRichard Purdie {12000000, 96000, 125, 0xe, 0x1}, 524abadfc92SRichard Purdie }; 525abadfc92SRichard Purdie 526abadfc92SRichard Purdie static inline int get_coeff(int mclk, int rate) 527abadfc92SRichard Purdie { 528abadfc92SRichard Purdie int i; 529abadfc92SRichard Purdie 530abadfc92SRichard Purdie for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { 531abadfc92SRichard Purdie if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) 532abadfc92SRichard Purdie return i; 533abadfc92SRichard Purdie } 534a71a468aSLiam Girdwood 535a71a468aSLiam Girdwood printk(KERN_ERR "wm8750: could not get coeff for mclk %d @ rate %d\n", 536a71a468aSLiam Girdwood mclk, rate); 537abadfc92SRichard Purdie return -EINVAL; 538abadfc92SRichard Purdie } 539abadfc92SRichard Purdie 540e550e17fSLiam Girdwood static int wm8750_set_dai_sysclk(struct snd_soc_dai *codec_dai, 5414422b606SLiam Girdwood int clk_id, unsigned int freq, int dir) 542abadfc92SRichard Purdie { 5434422b606SLiam Girdwood struct snd_soc_codec *codec = codec_dai->codec; 5444422b606SLiam Girdwood struct wm8750_priv *wm8750 = codec->private_data; 5454422b606SLiam Girdwood 5464422b606SLiam Girdwood switch (freq) { 5474422b606SLiam Girdwood case 11289600: 5484422b606SLiam Girdwood case 12000000: 5494422b606SLiam Girdwood case 12288000: 5504422b606SLiam Girdwood case 16934400: 5514422b606SLiam Girdwood case 18432000: 5524422b606SLiam Girdwood wm8750->sysclk = freq; 5534422b606SLiam Girdwood return 0; 5544422b606SLiam Girdwood } 5554422b606SLiam Girdwood return -EINVAL; 556abadfc92SRichard Purdie } 557abadfc92SRichard Purdie 558e550e17fSLiam Girdwood static int wm8750_set_dai_fmt(struct snd_soc_dai *codec_dai, 5594422b606SLiam Girdwood unsigned int fmt) 560abadfc92SRichard Purdie { 5614422b606SLiam Girdwood struct snd_soc_codec *codec = codec_dai->codec; 5624422b606SLiam Girdwood u16 iface = 0; 563abadfc92SRichard Purdie 564abadfc92SRichard Purdie /* set master/slave audio interface */ 5654422b606SLiam Girdwood switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 566abadfc92SRichard Purdie case SND_SOC_DAIFMT_CBM_CFM: 567abadfc92SRichard Purdie iface = 0x0040; 568abadfc92SRichard Purdie break; 569abadfc92SRichard Purdie case SND_SOC_DAIFMT_CBS_CFS: 570abadfc92SRichard Purdie break; 5714422b606SLiam Girdwood default: 5724422b606SLiam Girdwood return -EINVAL; 573abadfc92SRichard Purdie } 574abadfc92SRichard Purdie 575abadfc92SRichard Purdie /* interface format */ 5764422b606SLiam Girdwood switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 577abadfc92SRichard Purdie case SND_SOC_DAIFMT_I2S: 578abadfc92SRichard Purdie iface |= 0x0002; 579abadfc92SRichard Purdie break; 580abadfc92SRichard Purdie case SND_SOC_DAIFMT_RIGHT_J: 581abadfc92SRichard Purdie break; 582abadfc92SRichard Purdie case SND_SOC_DAIFMT_LEFT_J: 583abadfc92SRichard Purdie iface |= 0x0001; 584abadfc92SRichard Purdie break; 585abadfc92SRichard Purdie case SND_SOC_DAIFMT_DSP_A: 586abadfc92SRichard Purdie iface |= 0x0003; 587abadfc92SRichard Purdie break; 588abadfc92SRichard Purdie case SND_SOC_DAIFMT_DSP_B: 589abadfc92SRichard Purdie iface |= 0x0013; 590abadfc92SRichard Purdie break; 5914422b606SLiam Girdwood default: 5924422b606SLiam Girdwood return -EINVAL; 593abadfc92SRichard Purdie } 594abadfc92SRichard Purdie 595abadfc92SRichard Purdie /* clock inversion */ 5964422b606SLiam Girdwood switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 597abadfc92SRichard Purdie case SND_SOC_DAIFMT_NB_NF: 598abadfc92SRichard Purdie break; 599abadfc92SRichard Purdie case SND_SOC_DAIFMT_IB_IF: 600abadfc92SRichard Purdie iface |= 0x0090; 601abadfc92SRichard Purdie break; 602abadfc92SRichard Purdie case SND_SOC_DAIFMT_IB_NF: 603abadfc92SRichard Purdie iface |= 0x0080; 604abadfc92SRichard Purdie break; 605abadfc92SRichard Purdie case SND_SOC_DAIFMT_NB_IF: 606abadfc92SRichard Purdie iface |= 0x0010; 607abadfc92SRichard Purdie break; 6084422b606SLiam Girdwood default: 6094422b606SLiam Girdwood return -EINVAL; 610abadfc92SRichard Purdie } 611abadfc92SRichard Purdie 6124422b606SLiam Girdwood wm8750_write(codec, WM8750_IFACE, iface); 6134422b606SLiam Girdwood return 0; 6144422b606SLiam Girdwood } 6154422b606SLiam Girdwood 6164422b606SLiam Girdwood static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream, 6174422b606SLiam Girdwood struct snd_pcm_hw_params *params) 6184422b606SLiam Girdwood { 6194422b606SLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 6204422b606SLiam Girdwood struct snd_soc_device *socdev = rtd->socdev; 6214422b606SLiam Girdwood struct snd_soc_codec *codec = socdev->codec; 6224422b606SLiam Girdwood struct wm8750_priv *wm8750 = codec->private_data; 6234422b606SLiam Girdwood u16 iface = wm8750_read_reg_cache(codec, WM8750_IFACE) & 0x1f3; 6244422b606SLiam Girdwood u16 srate = wm8750_read_reg_cache(codec, WM8750_SRATE) & 0x1c0; 6254422b606SLiam Girdwood int coeff = get_coeff(wm8750->sysclk, params_rate(params)); 6264422b606SLiam Girdwood 6274422b606SLiam Girdwood /* bit size */ 6284422b606SLiam Girdwood switch (params_format(params)) { 6294422b606SLiam Girdwood case SNDRV_PCM_FORMAT_S16_LE: 630abadfc92SRichard Purdie break; 6314422b606SLiam Girdwood case SNDRV_PCM_FORMAT_S20_3LE: 6324422b606SLiam Girdwood iface |= 0x0004; 633abadfc92SRichard Purdie break; 6344422b606SLiam Girdwood case SNDRV_PCM_FORMAT_S24_LE: 6354422b606SLiam Girdwood iface |= 0x0008; 636abadfc92SRichard Purdie break; 6374422b606SLiam Girdwood case SNDRV_PCM_FORMAT_S32_LE: 6384422b606SLiam Girdwood iface |= 0x000c; 639abadfc92SRichard Purdie break; 640abadfc92SRichard Purdie } 641abadfc92SRichard Purdie 642abadfc92SRichard Purdie /* set iface & srate */ 643abadfc92SRichard Purdie wm8750_write(codec, WM8750_IFACE, iface); 6444422b606SLiam Girdwood if (coeff >= 0) 645abadfc92SRichard Purdie wm8750_write(codec, WM8750_SRATE, srate | 6464422b606SLiam Girdwood (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb); 647abadfc92SRichard Purdie 648abadfc92SRichard Purdie return 0; 649abadfc92SRichard Purdie } 650abadfc92SRichard Purdie 651e550e17fSLiam Girdwood static int wm8750_mute(struct snd_soc_dai *dai, int mute) 652abadfc92SRichard Purdie { 6534422b606SLiam Girdwood struct snd_soc_codec *codec = dai->codec; 654abadfc92SRichard Purdie u16 mute_reg = wm8750_read_reg_cache(codec, WM8750_ADCDAC) & 0xfff7; 6554422b606SLiam Girdwood 656abadfc92SRichard Purdie if (mute) 657abadfc92SRichard Purdie wm8750_write(codec, WM8750_ADCDAC, mute_reg | 0x8); 658abadfc92SRichard Purdie else 659abadfc92SRichard Purdie wm8750_write(codec, WM8750_ADCDAC, mute_reg); 660abadfc92SRichard Purdie return 0; 661abadfc92SRichard Purdie } 662abadfc92SRichard Purdie 6630be9898aSMark Brown static int wm8750_set_bias_level(struct snd_soc_codec *codec, 6640be9898aSMark Brown enum snd_soc_bias_level level) 665abadfc92SRichard Purdie { 666abadfc92SRichard Purdie u16 pwr_reg = wm8750_read_reg_cache(codec, WM8750_PWR1) & 0xfe3e; 667abadfc92SRichard Purdie 6680be9898aSMark Brown switch (level) { 6690be9898aSMark Brown case SND_SOC_BIAS_ON: 670abadfc92SRichard Purdie /* set vmid to 50k and unmute dac */ 671abadfc92SRichard Purdie wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x00c0); 672abadfc92SRichard Purdie break; 6730be9898aSMark Brown case SND_SOC_BIAS_PREPARE: 674abadfc92SRichard Purdie /* set vmid to 5k for quick power up */ 675abadfc92SRichard Purdie wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x01c1); 676abadfc92SRichard Purdie break; 6770be9898aSMark Brown case SND_SOC_BIAS_STANDBY: 678abadfc92SRichard Purdie /* mute dac and set vmid to 500k, enable VREF */ 679abadfc92SRichard Purdie wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x0141); 680abadfc92SRichard Purdie break; 6810be9898aSMark Brown case SND_SOC_BIAS_OFF: 682abadfc92SRichard Purdie wm8750_write(codec, WM8750_PWR1, 0x0001); 683abadfc92SRichard Purdie break; 684abadfc92SRichard Purdie } 6850be9898aSMark Brown codec->bias_level = level; 686abadfc92SRichard Purdie return 0; 687abadfc92SRichard Purdie } 688abadfc92SRichard Purdie 6894422b606SLiam Girdwood #define WM8750_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ 6904422b606SLiam Girdwood SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ 6914422b606SLiam Girdwood SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) 6924422b606SLiam Girdwood 6934422b606SLiam Girdwood #define WM8750_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 6944422b606SLiam Girdwood SNDRV_PCM_FMTBIT_S24_LE) 6954422b606SLiam Girdwood 696e550e17fSLiam Girdwood struct snd_soc_dai wm8750_dai = { 697abadfc92SRichard Purdie .name = "WM8750", 698abadfc92SRichard Purdie .playback = { 699abadfc92SRichard Purdie .stream_name = "Playback", 700abadfc92SRichard Purdie .channels_min = 1, 701abadfc92SRichard Purdie .channels_max = 2, 7024422b606SLiam Girdwood .rates = WM8750_RATES, 7034422b606SLiam Girdwood .formats = WM8750_FORMATS,}, 704abadfc92SRichard Purdie .capture = { 705abadfc92SRichard Purdie .stream_name = "Capture", 706abadfc92SRichard Purdie .channels_min = 1, 707abadfc92SRichard Purdie .channels_max = 2, 7084422b606SLiam Girdwood .rates = WM8750_RATES, 7094422b606SLiam Girdwood .formats = WM8750_FORMATS,}, 710abadfc92SRichard Purdie .ops = { 7114422b606SLiam Girdwood .hw_params = wm8750_pcm_hw_params, 712abadfc92SRichard Purdie }, 7134422b606SLiam Girdwood .dai_ops = { 7144422b606SLiam Girdwood .digital_mute = wm8750_mute, 7154422b606SLiam Girdwood .set_fmt = wm8750_set_dai_fmt, 7164422b606SLiam Girdwood .set_sysclk = wm8750_set_dai_sysclk, 717abadfc92SRichard Purdie }, 718abadfc92SRichard Purdie }; 719abadfc92SRichard Purdie EXPORT_SYMBOL_GPL(wm8750_dai); 720abadfc92SRichard Purdie 7211321b160STakashi Iwai static void wm8750_work(struct work_struct *work) 722abadfc92SRichard Purdie { 7231321b160STakashi Iwai struct snd_soc_codec *codec = 7241321b160STakashi Iwai container_of(work, struct snd_soc_codec, delayed_work.work); 7250be9898aSMark Brown wm8750_set_bias_level(codec, codec->bias_level); 726abadfc92SRichard Purdie } 727abadfc92SRichard Purdie 728abadfc92SRichard Purdie static int wm8750_suspend(struct platform_device *pdev, pm_message_t state) 729abadfc92SRichard Purdie { 730abadfc92SRichard Purdie struct snd_soc_device *socdev = platform_get_drvdata(pdev); 731abadfc92SRichard Purdie struct snd_soc_codec *codec = socdev->codec; 732abadfc92SRichard Purdie 7330be9898aSMark Brown wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF); 734abadfc92SRichard Purdie return 0; 735abadfc92SRichard Purdie } 736abadfc92SRichard Purdie 737abadfc92SRichard Purdie static int wm8750_resume(struct platform_device *pdev) 738abadfc92SRichard Purdie { 739abadfc92SRichard Purdie struct snd_soc_device *socdev = platform_get_drvdata(pdev); 740abadfc92SRichard Purdie struct snd_soc_codec *codec = socdev->codec; 741abadfc92SRichard Purdie int i; 742abadfc92SRichard Purdie u8 data[2]; 743abadfc92SRichard Purdie u16 *cache = codec->reg_cache; 744abadfc92SRichard Purdie 745abadfc92SRichard Purdie /* Sync reg_cache with the hardware */ 746abadfc92SRichard Purdie for (i = 0; i < ARRAY_SIZE(wm8750_reg); i++) { 747abadfc92SRichard Purdie if (i == WM8750_RESET) 748abadfc92SRichard Purdie continue; 749abadfc92SRichard Purdie data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); 750abadfc92SRichard Purdie data[1] = cache[i] & 0x00ff; 751abadfc92SRichard Purdie codec->hw_write(codec->control_data, data, 2); 752abadfc92SRichard Purdie } 753abadfc92SRichard Purdie 7540be9898aSMark Brown wm8750_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 755abadfc92SRichard Purdie 756abadfc92SRichard Purdie /* charge wm8750 caps */ 7570be9898aSMark Brown if (codec->suspend_bias_level == SND_SOC_BIAS_ON) { 7580be9898aSMark Brown wm8750_set_bias_level(codec, SND_SOC_BIAS_PREPARE); 7590be9898aSMark Brown codec->bias_level = SND_SOC_BIAS_ON; 76042f3030fSMark Brown schedule_delayed_work(&codec->delayed_work, 76142f3030fSMark Brown msecs_to_jiffies(1000)); 762abadfc92SRichard Purdie } 763abadfc92SRichard Purdie 764abadfc92SRichard Purdie return 0; 765abadfc92SRichard Purdie } 766abadfc92SRichard Purdie 767abadfc92SRichard Purdie /* 768abadfc92SRichard Purdie * initialise the WM8750 driver 769abadfc92SRichard Purdie * register the mixer and dsp interfaces with the kernel 770abadfc92SRichard Purdie */ 771abadfc92SRichard Purdie static int wm8750_init(struct snd_soc_device *socdev) 772abadfc92SRichard Purdie { 773abadfc92SRichard Purdie struct snd_soc_codec *codec = socdev->codec; 774abadfc92SRichard Purdie int reg, ret = 0; 775abadfc92SRichard Purdie 776abadfc92SRichard Purdie codec->name = "WM8750"; 777abadfc92SRichard Purdie codec->owner = THIS_MODULE; 778abadfc92SRichard Purdie codec->read = wm8750_read_reg_cache; 779abadfc92SRichard Purdie codec->write = wm8750_write; 7800be9898aSMark Brown codec->set_bias_level = wm8750_set_bias_level; 781abadfc92SRichard Purdie codec->dai = &wm8750_dai; 782abadfc92SRichard Purdie codec->num_dai = 1; 783d751b233SMark Brown codec->reg_cache_size = ARRAY_SIZE(wm8750_reg); 784713fb939SAndrew Morton codec->reg_cache = kmemdup(wm8750_reg, sizeof(wm8750_reg), GFP_KERNEL); 785abadfc92SRichard Purdie if (codec->reg_cache == NULL) 786abadfc92SRichard Purdie return -ENOMEM; 787abadfc92SRichard Purdie 788abadfc92SRichard Purdie wm8750_reset(codec); 789abadfc92SRichard Purdie 790abadfc92SRichard Purdie /* register pcms */ 791abadfc92SRichard Purdie ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); 792abadfc92SRichard Purdie if (ret < 0) { 793e35115a5SLiam Girdwood printk(KERN_ERR "wm8750: failed to create pcms\n"); 794e35115a5SLiam Girdwood goto pcm_err; 795abadfc92SRichard Purdie } 796abadfc92SRichard Purdie 797abadfc92SRichard Purdie /* charge output caps */ 7980be9898aSMark Brown wm8750_set_bias_level(codec, SND_SOC_BIAS_PREPARE); 7990be9898aSMark Brown codec->bias_level = SND_SOC_BIAS_STANDBY; 8001321b160STakashi Iwai schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000)); 801abadfc92SRichard Purdie 802abadfc92SRichard Purdie /* set the update bits */ 803abadfc92SRichard Purdie reg = wm8750_read_reg_cache(codec, WM8750_LDAC); 804abadfc92SRichard Purdie wm8750_write(codec, WM8750_LDAC, reg | 0x0100); 805abadfc92SRichard Purdie reg = wm8750_read_reg_cache(codec, WM8750_RDAC); 806abadfc92SRichard Purdie wm8750_write(codec, WM8750_RDAC, reg | 0x0100); 807abadfc92SRichard Purdie reg = wm8750_read_reg_cache(codec, WM8750_LOUT1V); 808abadfc92SRichard Purdie wm8750_write(codec, WM8750_LOUT1V, reg | 0x0100); 809abadfc92SRichard Purdie reg = wm8750_read_reg_cache(codec, WM8750_ROUT1V); 810abadfc92SRichard Purdie wm8750_write(codec, WM8750_ROUT1V, reg | 0x0100); 811abadfc92SRichard Purdie reg = wm8750_read_reg_cache(codec, WM8750_LOUT2V); 812abadfc92SRichard Purdie wm8750_write(codec, WM8750_LOUT2V, reg | 0x0100); 813abadfc92SRichard Purdie reg = wm8750_read_reg_cache(codec, WM8750_ROUT2V); 814abadfc92SRichard Purdie wm8750_write(codec, WM8750_ROUT2V, reg | 0x0100); 815abadfc92SRichard Purdie reg = wm8750_read_reg_cache(codec, WM8750_LINVOL); 816abadfc92SRichard Purdie wm8750_write(codec, WM8750_LINVOL, reg | 0x0100); 817abadfc92SRichard Purdie reg = wm8750_read_reg_cache(codec, WM8750_RINVOL); 818abadfc92SRichard Purdie wm8750_write(codec, WM8750_RINVOL, reg | 0x0100); 819abadfc92SRichard Purdie 820abadfc92SRichard Purdie wm8750_add_controls(codec); 821abadfc92SRichard Purdie wm8750_add_widgets(codec); 822abadfc92SRichard Purdie ret = snd_soc_register_card(socdev); 823abadfc92SRichard Purdie if (ret < 0) { 824e35115a5SLiam Girdwood printk(KERN_ERR "wm8750: failed to register card\n"); 825e35115a5SLiam Girdwood goto card_err; 826e35115a5SLiam Girdwood } 827e35115a5SLiam Girdwood return ret; 828e35115a5SLiam Girdwood 829e35115a5SLiam Girdwood card_err: 830abadfc92SRichard Purdie snd_soc_free_pcms(socdev); 831abadfc92SRichard Purdie snd_soc_dapm_free(socdev); 832e35115a5SLiam Girdwood pcm_err: 833e35115a5SLiam Girdwood kfree(codec->reg_cache); 834abadfc92SRichard Purdie return ret; 835abadfc92SRichard Purdie } 836abadfc92SRichard Purdie 837abadfc92SRichard Purdie /* If the i2c layer weren't so broken, we could pass this kind of data 838abadfc92SRichard Purdie around */ 839abadfc92SRichard Purdie static struct snd_soc_device *wm8750_socdev; 840abadfc92SRichard Purdie 841abadfc92SRichard Purdie #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 842abadfc92SRichard Purdie 843abadfc92SRichard Purdie /* 844abadfc92SRichard Purdie * WM8731 2 wire address is determined by GPIO5 845abadfc92SRichard Purdie * state during powerup. 846abadfc92SRichard Purdie * low = 0x1a 847abadfc92SRichard Purdie * high = 0x1b 848abadfc92SRichard Purdie */ 849abadfc92SRichard Purdie 850*ee1d0099SJean Delvare static int wm8750_i2c_probe(struct i2c_client *i2c, 851*ee1d0099SJean Delvare const struct i2c_device_id *id) 852abadfc92SRichard Purdie { 853abadfc92SRichard Purdie struct snd_soc_device *socdev = wm8750_socdev; 854abadfc92SRichard Purdie struct snd_soc_codec *codec = socdev->codec; 855abadfc92SRichard Purdie int ret; 856abadfc92SRichard Purdie 857abadfc92SRichard Purdie i2c_set_clientdata(i2c, codec); 858abadfc92SRichard Purdie codec->control_data = i2c; 859abadfc92SRichard Purdie 860abadfc92SRichard Purdie ret = wm8750_init(socdev); 861*ee1d0099SJean Delvare if (ret < 0) 862a5c95e90SMark Brown pr_err("failed to initialise WM8750\n"); 863abadfc92SRichard Purdie 864abadfc92SRichard Purdie return ret; 865abadfc92SRichard Purdie } 866abadfc92SRichard Purdie 867*ee1d0099SJean Delvare static int wm8750_i2c_remove(struct i2c_client *client) 868abadfc92SRichard Purdie { 869abadfc92SRichard Purdie struct snd_soc_codec *codec = i2c_get_clientdata(client); 870abadfc92SRichard Purdie kfree(codec->reg_cache); 871abadfc92SRichard Purdie return 0; 872abadfc92SRichard Purdie } 873abadfc92SRichard Purdie 874*ee1d0099SJean Delvare static const struct i2c_device_id wm8750_i2c_id[] = { 875*ee1d0099SJean Delvare { "wm8750", 0 }, 876*ee1d0099SJean Delvare { } 877*ee1d0099SJean Delvare }; 878*ee1d0099SJean Delvare MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id); 879abadfc92SRichard Purdie 880abadfc92SRichard Purdie static struct i2c_driver wm8750_i2c_driver = { 881abadfc92SRichard Purdie .driver = { 882abadfc92SRichard Purdie .name = "WM8750 I2C Codec", 883abadfc92SRichard Purdie .owner = THIS_MODULE, 884abadfc92SRichard Purdie }, 885*ee1d0099SJean Delvare .probe = wm8750_i2c_probe, 886*ee1d0099SJean Delvare .remove = wm8750_i2c_remove, 887*ee1d0099SJean Delvare .id_table = wm8750_i2c_id, 888abadfc92SRichard Purdie }; 889abadfc92SRichard Purdie 890*ee1d0099SJean Delvare static int wm8750_add_i2c_device(struct platform_device *pdev, 891*ee1d0099SJean Delvare const struct wm8750_setup_data *setup) 892*ee1d0099SJean Delvare { 893*ee1d0099SJean Delvare struct i2c_board_info info; 894*ee1d0099SJean Delvare struct i2c_adapter *adapter; 895*ee1d0099SJean Delvare struct i2c_client *client; 896*ee1d0099SJean Delvare int ret; 897*ee1d0099SJean Delvare 898*ee1d0099SJean Delvare ret = i2c_add_driver(&wm8750_i2c_driver); 899*ee1d0099SJean Delvare if (ret != 0) { 900*ee1d0099SJean Delvare dev_err(&pdev->dev, "can't add i2c driver\n"); 901*ee1d0099SJean Delvare return ret; 902*ee1d0099SJean Delvare } 903*ee1d0099SJean Delvare 904*ee1d0099SJean Delvare memset(&info, 0, sizeof(struct i2c_board_info)); 905*ee1d0099SJean Delvare info.addr = setup->i2c_address; 906*ee1d0099SJean Delvare strlcpy(info.type, "wm8750", I2C_NAME_SIZE); 907*ee1d0099SJean Delvare 908*ee1d0099SJean Delvare adapter = i2c_get_adapter(setup->i2c_bus); 909*ee1d0099SJean Delvare if (!adapter) { 910*ee1d0099SJean Delvare dev_err(&pdev->dev, "can't get i2c adapter %d\n", 911*ee1d0099SJean Delvare setup->i2c_bus); 912*ee1d0099SJean Delvare goto err_driver; 913*ee1d0099SJean Delvare } 914*ee1d0099SJean Delvare 915*ee1d0099SJean Delvare client = i2c_new_device(adapter, &info); 916*ee1d0099SJean Delvare i2c_put_adapter(adapter); 917*ee1d0099SJean Delvare if (!client) { 918*ee1d0099SJean Delvare dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", 919*ee1d0099SJean Delvare (unsigned int)info.addr); 920*ee1d0099SJean Delvare goto err_driver; 921*ee1d0099SJean Delvare } 922*ee1d0099SJean Delvare 923*ee1d0099SJean Delvare return 0; 924*ee1d0099SJean Delvare 925*ee1d0099SJean Delvare err_driver: 926*ee1d0099SJean Delvare i2c_del_driver(&wm8750_i2c_driver); 927*ee1d0099SJean Delvare return -ENODEV; 928*ee1d0099SJean Delvare } 929abadfc92SRichard Purdie #endif 930abadfc92SRichard Purdie 931abadfc92SRichard Purdie static int wm8750_probe(struct platform_device *pdev) 932abadfc92SRichard Purdie { 933abadfc92SRichard Purdie struct snd_soc_device *socdev = platform_get_drvdata(pdev); 934abadfc92SRichard Purdie struct wm8750_setup_data *setup = socdev->codec_data; 935abadfc92SRichard Purdie struct snd_soc_codec *codec; 9364422b606SLiam Girdwood struct wm8750_priv *wm8750; 937abadfc92SRichard Purdie int ret = 0; 938abadfc92SRichard Purdie 939a5c95e90SMark Brown pr_info("WM8750 Audio Codec %s", WM8750_VERSION); 940abadfc92SRichard Purdie codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); 941abadfc92SRichard Purdie if (codec == NULL) 942abadfc92SRichard Purdie return -ENOMEM; 943abadfc92SRichard Purdie 9444422b606SLiam Girdwood wm8750 = kzalloc(sizeof(struct wm8750_priv), GFP_KERNEL); 9454422b606SLiam Girdwood if (wm8750 == NULL) { 9464422b606SLiam Girdwood kfree(codec); 9474422b606SLiam Girdwood return -ENOMEM; 9484422b606SLiam Girdwood } 9494422b606SLiam Girdwood 9504422b606SLiam Girdwood codec->private_data = wm8750; 951abadfc92SRichard Purdie socdev->codec = codec; 952abadfc92SRichard Purdie mutex_init(&codec->mutex); 953abadfc92SRichard Purdie INIT_LIST_HEAD(&codec->dapm_widgets); 954abadfc92SRichard Purdie INIT_LIST_HEAD(&codec->dapm_paths); 955abadfc92SRichard Purdie wm8750_socdev = socdev; 9561321b160STakashi Iwai INIT_DELAYED_WORK(&codec->delayed_work, wm8750_work); 9574422b606SLiam Girdwood 958abadfc92SRichard Purdie #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 959abadfc92SRichard Purdie if (setup->i2c_address) { 960abadfc92SRichard Purdie codec->hw_write = (hw_write_t)i2c_master_send; 961*ee1d0099SJean Delvare ret = wm8750_add_i2c_device(pdev, setup); 962abadfc92SRichard Purdie } 963abadfc92SRichard Purdie #else 964abadfc92SRichard Purdie /* Add other interfaces here */ 965abadfc92SRichard Purdie #endif 966abadfc92SRichard Purdie 9673051e41aSJean Delvare if (ret != 0) { 9683051e41aSJean Delvare kfree(codec->private_data); 9693051e41aSJean Delvare kfree(codec); 9703051e41aSJean Delvare } 971abadfc92SRichard Purdie return ret; 972abadfc92SRichard Purdie } 973abadfc92SRichard Purdie 9744422b606SLiam Girdwood /* 9754422b606SLiam Girdwood * This function forces any delayed work to be queued and run. 9764422b606SLiam Girdwood */ 9774422b606SLiam Girdwood static int run_delayed_work(struct delayed_work *dwork) 9784422b606SLiam Girdwood { 9794422b606SLiam Girdwood int ret; 9804422b606SLiam Girdwood 9814422b606SLiam Girdwood /* cancel any work waiting to be queued. */ 9824422b606SLiam Girdwood ret = cancel_delayed_work(dwork); 9834422b606SLiam Girdwood 9844422b606SLiam Girdwood /* if there was any work waiting then we run it now and 9854422b606SLiam Girdwood * wait for it's completion */ 9864422b606SLiam Girdwood if (ret) { 9874422b606SLiam Girdwood schedule_delayed_work(dwork, 0); 9884422b606SLiam Girdwood flush_scheduled_work(); 9894422b606SLiam Girdwood } 9904422b606SLiam Girdwood return ret; 9914422b606SLiam Girdwood } 9924422b606SLiam Girdwood 993abadfc92SRichard Purdie /* power down chip */ 994abadfc92SRichard Purdie static int wm8750_remove(struct platform_device *pdev) 995abadfc92SRichard Purdie { 996abadfc92SRichard Purdie struct snd_soc_device *socdev = platform_get_drvdata(pdev); 997abadfc92SRichard Purdie struct snd_soc_codec *codec = socdev->codec; 998abadfc92SRichard Purdie 999abadfc92SRichard Purdie if (codec->control_data) 10000be9898aSMark Brown wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF); 10014422b606SLiam Girdwood run_delayed_work(&codec->delayed_work); 1002abadfc92SRichard Purdie snd_soc_free_pcms(socdev); 1003abadfc92SRichard Purdie snd_soc_dapm_free(socdev); 1004abadfc92SRichard Purdie #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 1005*ee1d0099SJean Delvare i2c_unregister_device(codec->control_data); 1006abadfc92SRichard Purdie i2c_del_driver(&wm8750_i2c_driver); 1007abadfc92SRichard Purdie #endif 10084422b606SLiam Girdwood kfree(codec->private_data); 1009abadfc92SRichard Purdie kfree(codec); 1010abadfc92SRichard Purdie 1011abadfc92SRichard Purdie return 0; 1012abadfc92SRichard Purdie } 1013abadfc92SRichard Purdie 1014abadfc92SRichard Purdie struct snd_soc_codec_device soc_codec_dev_wm8750 = { 1015abadfc92SRichard Purdie .probe = wm8750_probe, 1016abadfc92SRichard Purdie .remove = wm8750_remove, 1017abadfc92SRichard Purdie .suspend = wm8750_suspend, 1018abadfc92SRichard Purdie .resume = wm8750_resume, 1019abadfc92SRichard Purdie }; 1020abadfc92SRichard Purdie EXPORT_SYMBOL_GPL(soc_codec_dev_wm8750); 1021abadfc92SRichard Purdie 1022abadfc92SRichard Purdie MODULE_DESCRIPTION("ASoC WM8750 driver"); 1023abadfc92SRichard Purdie MODULE_AUTHOR("Liam Girdwood"); 1024abadfc92SRichard Purdie MODULE_LICENSE("GPL"); 1025