140e0aa64SRichard Purdie /* 240e0aa64SRichard Purdie * wm8731.c -- WM8731 ALSA SoC Audio driver 340e0aa64SRichard Purdie * 440e0aa64SRichard Purdie * Copyright 2005 Openedhand Ltd. 540e0aa64SRichard Purdie * 640e0aa64SRichard Purdie * Author: Richard Purdie <richard@openedhand.com> 740e0aa64SRichard Purdie * 840e0aa64SRichard Purdie * Based on wm8753.c by Liam Girdwood 940e0aa64SRichard Purdie * 1040e0aa64SRichard Purdie * This program is free software; you can redistribute it and/or modify 1140e0aa64SRichard Purdie * it under the terms of the GNU General Public License version 2 as 1240e0aa64SRichard Purdie * published by the Free Software Foundation. 1340e0aa64SRichard Purdie */ 1440e0aa64SRichard Purdie 1540e0aa64SRichard Purdie #include <linux/module.h> 1640e0aa64SRichard Purdie #include <linux/moduleparam.h> 1740e0aa64SRichard Purdie #include <linux/init.h> 1840e0aa64SRichard Purdie #include <linux/delay.h> 1940e0aa64SRichard Purdie #include <linux/pm.h> 2040e0aa64SRichard Purdie #include <linux/i2c.h> 215a0e3ad6STejun Heo #include <linux/slab.h> 2240e0aa64SRichard Purdie #include <linux/platform_device.h> 237dea7c01SMark Brown #include <linux/regulator/consumer.h> 24d2a40355SCliff Cai #include <linux/spi/spi.h> 2540e0aa64SRichard Purdie #include <sound/core.h> 2640e0aa64SRichard Purdie #include <sound/pcm.h> 2740e0aa64SRichard Purdie #include <sound/pcm_params.h> 2840e0aa64SRichard Purdie #include <sound/soc.h> 2940e0aa64SRichard Purdie #include <sound/initval.h> 30d00efa64SMark Brown #include <sound/tlv.h> 3140e0aa64SRichard Purdie 3240e0aa64SRichard Purdie #include "wm8731.h" 3340e0aa64SRichard Purdie 347dea7c01SMark Brown #define WM8731_NUM_SUPPLIES 4 357dea7c01SMark Brown static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { 367dea7c01SMark Brown "AVDD", 377dea7c01SMark Brown "HPVDD", 387dea7c01SMark Brown "DCVDD", 397dea7c01SMark Brown "DBVDD", 407dea7c01SMark Brown }; 417dea7c01SMark Brown 42b36d61d4SFrank Mandarino /* codec private data */ 43b36d61d4SFrank Mandarino struct wm8731_priv { 44f0fba2adSLiam Girdwood enum snd_soc_control_type control_type; 457dea7c01SMark Brown struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; 465998102bSMark Brown u16 reg_cache[WM8731_CACHEREGNUM]; 47b36d61d4SFrank Mandarino unsigned int sysclk; 489745e824SMark Brown int sysclk_type; 49*dd31b310SMark Brown int playback_fs; 50*dd31b310SMark Brown bool deemph; 51b36d61d4SFrank Mandarino }; 52b36d61d4SFrank Mandarino 53a8035c8fSMark Brown 5440e0aa64SRichard Purdie /* 5540e0aa64SRichard Purdie * wm8731 register cache 5640e0aa64SRichard Purdie * We can't read the WM8731 register space when we are 5740e0aa64SRichard Purdie * using 2 wire for device control, so we cache them instead. 5840e0aa64SRichard Purdie * There is no point in caching the reset register 5940e0aa64SRichard Purdie */ 6040e0aa64SRichard Purdie static const u16 wm8731_reg[WM8731_CACHEREGNUM] = { 6140e0aa64SRichard Purdie 0x0097, 0x0097, 0x0079, 0x0079, 6240e0aa64SRichard Purdie 0x000a, 0x0008, 0x009f, 0x000a, 6340e0aa64SRichard Purdie 0x0000, 0x0000 6440e0aa64SRichard Purdie }; 6540e0aa64SRichard Purdie 6617a52fd6SMark Brown #define wm8731_reset(c) snd_soc_write(c, WM8731_RESET, 0) 6740e0aa64SRichard Purdie 6840e0aa64SRichard Purdie static const char *wm8731_input_select[] = {"Line In", "Mic"}; 6959f72970SMark Brown 7059f72970SMark Brown static const struct soc_enum wm8731_insel_enum = 7159f72970SMark Brown SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select); 7259f72970SMark Brown 73*dd31b310SMark Brown static int wm8731_deemph[] = { 0, 32000, 44100, 48000 }; 7459f72970SMark Brown 75*dd31b310SMark Brown static int wm8731_set_deemph(struct snd_soc_codec *codec) 76*dd31b310SMark Brown { 77*dd31b310SMark Brown struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); 78*dd31b310SMark Brown int val, i, best; 79*dd31b310SMark Brown 80*dd31b310SMark Brown /* If we're using deemphasis select the nearest available sample 81*dd31b310SMark Brown * rate. 82*dd31b310SMark Brown */ 83*dd31b310SMark Brown if (wm8731->deemph) { 84*dd31b310SMark Brown best = 1; 85*dd31b310SMark Brown for (i = 2; i < ARRAY_SIZE(wm8731_deemph); i++) { 86*dd31b310SMark Brown if (abs(wm8731_deemph[i] - wm8731->playback_fs) < 87*dd31b310SMark Brown abs(wm8731_deemph[best] - wm8731->playback_fs)) 88*dd31b310SMark Brown best = i; 89*dd31b310SMark Brown } 90*dd31b310SMark Brown 91*dd31b310SMark Brown val = best << 1; 92*dd31b310SMark Brown } else { 93*dd31b310SMark Brown best = 0; 94*dd31b310SMark Brown val = 0; 95*dd31b310SMark Brown } 96*dd31b310SMark Brown 97*dd31b310SMark Brown dev_dbg(codec->dev, "Set deemphasis %d (%dHz)\n", 98*dd31b310SMark Brown best, wm8731_deemph[best]); 99*dd31b310SMark Brown 100*dd31b310SMark Brown return snd_soc_update_bits(codec, WM8731_APDIGI, 0x6, val); 101*dd31b310SMark Brown } 102*dd31b310SMark Brown 103*dd31b310SMark Brown static int wm8731_get_deemph(struct snd_kcontrol *kcontrol, 104*dd31b310SMark Brown struct snd_ctl_elem_value *ucontrol) 105*dd31b310SMark Brown { 106*dd31b310SMark Brown struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); 107*dd31b310SMark Brown struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); 108*dd31b310SMark Brown 109*dd31b310SMark Brown ucontrol->value.enumerated.item[0] = wm8731->deemph; 110*dd31b310SMark Brown 111*dd31b310SMark Brown return 0; 112*dd31b310SMark Brown } 113*dd31b310SMark Brown 114*dd31b310SMark Brown static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, 115*dd31b310SMark Brown struct snd_ctl_elem_value *ucontrol) 116*dd31b310SMark Brown { 117*dd31b310SMark Brown struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); 118*dd31b310SMark Brown struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); 119*dd31b310SMark Brown int deemph = ucontrol->value.enumerated.item[0]; 120*dd31b310SMark Brown int ret = 0; 121*dd31b310SMark Brown 122*dd31b310SMark Brown if (deemph > 1) 123*dd31b310SMark Brown return -EINVAL; 124*dd31b310SMark Brown 125*dd31b310SMark Brown mutex_lock(&codec->mutex); 126*dd31b310SMark Brown if (wm8731->deemph != deemph) { 127*dd31b310SMark Brown wm8731->deemph = deemph; 128*dd31b310SMark Brown 129*dd31b310SMark Brown wm8731_set_deemph(codec); 130*dd31b310SMark Brown 131*dd31b310SMark Brown ret = 1; 132*dd31b310SMark Brown } 133*dd31b310SMark Brown mutex_unlock(&codec->mutex); 134*dd31b310SMark Brown 135*dd31b310SMark Brown return ret; 136*dd31b310SMark Brown } 13740e0aa64SRichard Purdie 138d00efa64SMark Brown static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); 139d00efa64SMark Brown static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0); 140d00efa64SMark Brown static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); 141d921184eSMark Brown static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 2000, 0); 142d00efa64SMark Brown 14340e0aa64SRichard Purdie static const struct snd_kcontrol_new wm8731_snd_controls[] = { 14440e0aa64SRichard Purdie 145d00efa64SMark Brown SOC_DOUBLE_R_TLV("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V, 146d00efa64SMark Brown 0, 127, 0, out_tlv), 147bd903b6eSLiam Girdwood SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V, 148bd903b6eSLiam Girdwood 7, 1, 0), 14940e0aa64SRichard Purdie 150d00efa64SMark Brown SOC_DOUBLE_R_TLV("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0, 151d00efa64SMark Brown in_tlv), 15240e0aa64SRichard Purdie SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1), 15340e0aa64SRichard Purdie 154d921184eSMark Brown SOC_SINGLE_TLV("Mic Boost Volume", WM8731_APANA, 0, 1, 0, mic_tlv), 155ef38ed88SMark Brown SOC_SINGLE("Mic Capture Switch", WM8731_APANA, 1, 1, 1), 15640e0aa64SRichard Purdie 157d00efa64SMark Brown SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1, 158d00efa64SMark Brown sidetone_tlv), 15940e0aa64SRichard Purdie 16040e0aa64SRichard Purdie SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1), 16140e0aa64SRichard Purdie SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0), 16240e0aa64SRichard Purdie 163*dd31b310SMark Brown SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0, 164*dd31b310SMark Brown wm8731_get_deemph, wm8731_put_deemph), 16540e0aa64SRichard Purdie }; 16640e0aa64SRichard Purdie 16740e0aa64SRichard Purdie /* Output Mixer */ 16840e0aa64SRichard Purdie static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = { 16940e0aa64SRichard Purdie SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0), 17040e0aa64SRichard Purdie SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0), 17140e0aa64SRichard Purdie SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0), 17240e0aa64SRichard Purdie }; 17340e0aa64SRichard Purdie 17440e0aa64SRichard Purdie /* Input mux */ 17540e0aa64SRichard Purdie static const struct snd_kcontrol_new wm8731_input_mux_controls = 17659f72970SMark Brown SOC_DAPM_ENUM("Input Select", wm8731_insel_enum); 17740e0aa64SRichard Purdie 17840e0aa64SRichard Purdie static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { 1799745e824SMark Brown SND_SOC_DAPM_SUPPLY("OSC", WM8731_PWR, 5, 1, NULL, 0), 18040e0aa64SRichard Purdie SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, 18140e0aa64SRichard Purdie &wm8731_output_mixer_controls[0], 18240e0aa64SRichard Purdie ARRAY_SIZE(wm8731_output_mixer_controls)), 18340e0aa64SRichard Purdie SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8731_PWR, 3, 1), 18440e0aa64SRichard Purdie SND_SOC_DAPM_OUTPUT("LOUT"), 18540e0aa64SRichard Purdie SND_SOC_DAPM_OUTPUT("LHPOUT"), 18640e0aa64SRichard Purdie SND_SOC_DAPM_OUTPUT("ROUT"), 18740e0aa64SRichard Purdie SND_SOC_DAPM_OUTPUT("RHPOUT"), 18840e0aa64SRichard Purdie SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8731_PWR, 2, 1), 18940e0aa64SRichard Purdie SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &wm8731_input_mux_controls), 19040e0aa64SRichard Purdie SND_SOC_DAPM_PGA("Line Input", WM8731_PWR, 0, 1, NULL, 0), 19140e0aa64SRichard Purdie SND_SOC_DAPM_MICBIAS("Mic Bias", WM8731_PWR, 1, 1), 19240e0aa64SRichard Purdie SND_SOC_DAPM_INPUT("MICIN"), 19340e0aa64SRichard Purdie SND_SOC_DAPM_INPUT("RLINEIN"), 19440e0aa64SRichard Purdie SND_SOC_DAPM_INPUT("LLINEIN"), 19540e0aa64SRichard Purdie }; 19640e0aa64SRichard Purdie 1979745e824SMark Brown static int wm8731_check_osc(struct snd_soc_dapm_widget *source, 1989745e824SMark Brown struct snd_soc_dapm_widget *sink) 1999745e824SMark Brown { 2009745e824SMark Brown struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(source->codec); 2019745e824SMark Brown 2029745e824SMark Brown return wm8731->sysclk_type == WM8731_SYSCLK_MCLK; 2039745e824SMark Brown } 2049745e824SMark Brown 205a65f0568SMark Brown static const struct snd_soc_dapm_route intercon[] = { 2069745e824SMark Brown {"DAC", NULL, "OSC", wm8731_check_osc}, 2079745e824SMark Brown {"ADC", NULL, "OSC", wm8731_check_osc}, 2089745e824SMark Brown 20940e0aa64SRichard Purdie /* output mixer */ 21040e0aa64SRichard Purdie {"Output Mixer", "Line Bypass Switch", "Line Input"}, 21140e0aa64SRichard Purdie {"Output Mixer", "HiFi Playback Switch", "DAC"}, 21240e0aa64SRichard Purdie {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"}, 21340e0aa64SRichard Purdie 21440e0aa64SRichard Purdie /* outputs */ 21540e0aa64SRichard Purdie {"RHPOUT", NULL, "Output Mixer"}, 21640e0aa64SRichard Purdie {"ROUT", NULL, "Output Mixer"}, 21740e0aa64SRichard Purdie {"LHPOUT", NULL, "Output Mixer"}, 21840e0aa64SRichard Purdie {"LOUT", NULL, "Output Mixer"}, 21940e0aa64SRichard Purdie 22040e0aa64SRichard Purdie /* input mux */ 22140e0aa64SRichard Purdie {"Input Mux", "Line In", "Line Input"}, 22240e0aa64SRichard Purdie {"Input Mux", "Mic", "Mic Bias"}, 22340e0aa64SRichard Purdie {"ADC", NULL, "Input Mux"}, 22440e0aa64SRichard Purdie 22540e0aa64SRichard Purdie /* inputs */ 22640e0aa64SRichard Purdie {"Line Input", NULL, "LLINEIN"}, 22740e0aa64SRichard Purdie {"Line Input", NULL, "RLINEIN"}, 22840e0aa64SRichard Purdie {"Mic Bias", NULL, "MICIN"}, 22940e0aa64SRichard Purdie }; 23040e0aa64SRichard Purdie 23140e0aa64SRichard Purdie static int wm8731_add_widgets(struct snd_soc_codec *codec) 23240e0aa64SRichard Purdie { 233ce6120ccSLiam Girdwood struct snd_soc_dapm_context *dapm = &codec->dapm; 23440e0aa64SRichard Purdie 235ce6120ccSLiam Girdwood snd_soc_dapm_new_controls(dapm, wm8731_dapm_widgets, 236ce6120ccSLiam Girdwood ARRAY_SIZE(wm8731_dapm_widgets)); 237ce6120ccSLiam Girdwood snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); 23840e0aa64SRichard Purdie 23940e0aa64SRichard Purdie return 0; 24040e0aa64SRichard Purdie } 24140e0aa64SRichard Purdie 24240e0aa64SRichard Purdie struct _coeff_div { 24340e0aa64SRichard Purdie u32 mclk; 24440e0aa64SRichard Purdie u32 rate; 24540e0aa64SRichard Purdie u16 fs; 24640e0aa64SRichard Purdie u8 sr:4; 24740e0aa64SRichard Purdie u8 bosr:1; 24840e0aa64SRichard Purdie u8 usb:1; 24940e0aa64SRichard Purdie }; 25040e0aa64SRichard Purdie 25140e0aa64SRichard Purdie /* codec mclk clock divider coefficients */ 25240e0aa64SRichard Purdie static const struct _coeff_div coeff_div[] = { 25340e0aa64SRichard Purdie /* 48k */ 25440e0aa64SRichard Purdie {12288000, 48000, 256, 0x0, 0x0, 0x0}, 25540e0aa64SRichard Purdie {18432000, 48000, 384, 0x0, 0x1, 0x0}, 25640e0aa64SRichard Purdie {12000000, 48000, 250, 0x0, 0x0, 0x1}, 25740e0aa64SRichard Purdie 25840e0aa64SRichard Purdie /* 32k */ 25940e0aa64SRichard Purdie {12288000, 32000, 384, 0x6, 0x0, 0x0}, 26040e0aa64SRichard Purdie {18432000, 32000, 576, 0x6, 0x1, 0x0}, 261298a2c75SFrank Mandarino {12000000, 32000, 375, 0x6, 0x0, 0x1}, 26240e0aa64SRichard Purdie 26340e0aa64SRichard Purdie /* 8k */ 26440e0aa64SRichard Purdie {12288000, 8000, 1536, 0x3, 0x0, 0x0}, 26540e0aa64SRichard Purdie {18432000, 8000, 2304, 0x3, 0x1, 0x0}, 26640e0aa64SRichard Purdie {11289600, 8000, 1408, 0xb, 0x0, 0x0}, 26740e0aa64SRichard Purdie {16934400, 8000, 2112, 0xb, 0x1, 0x0}, 26840e0aa64SRichard Purdie {12000000, 8000, 1500, 0x3, 0x0, 0x1}, 26940e0aa64SRichard Purdie 27040e0aa64SRichard Purdie /* 96k */ 27140e0aa64SRichard Purdie {12288000, 96000, 128, 0x7, 0x0, 0x0}, 27240e0aa64SRichard Purdie {18432000, 96000, 192, 0x7, 0x1, 0x0}, 27340e0aa64SRichard Purdie {12000000, 96000, 125, 0x7, 0x0, 0x1}, 27440e0aa64SRichard Purdie 27540e0aa64SRichard Purdie /* 44.1k */ 27640e0aa64SRichard Purdie {11289600, 44100, 256, 0x8, 0x0, 0x0}, 27740e0aa64SRichard Purdie {16934400, 44100, 384, 0x8, 0x1, 0x0}, 27840e0aa64SRichard Purdie {12000000, 44100, 272, 0x8, 0x1, 0x1}, 27940e0aa64SRichard Purdie 28040e0aa64SRichard Purdie /* 88.2k */ 28140e0aa64SRichard Purdie {11289600, 88200, 128, 0xf, 0x0, 0x0}, 28240e0aa64SRichard Purdie {16934400, 88200, 192, 0xf, 0x1, 0x0}, 28340e0aa64SRichard Purdie {12000000, 88200, 136, 0xf, 0x1, 0x1}, 28440e0aa64SRichard Purdie }; 28540e0aa64SRichard Purdie 28640e0aa64SRichard Purdie static inline int get_coeff(int mclk, int rate) 28740e0aa64SRichard Purdie { 28840e0aa64SRichard Purdie int i; 28940e0aa64SRichard Purdie 29040e0aa64SRichard Purdie for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { 29140e0aa64SRichard Purdie if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) 29240e0aa64SRichard Purdie return i; 29340e0aa64SRichard Purdie } 29440e0aa64SRichard Purdie return 0; 29540e0aa64SRichard Purdie } 29640e0aa64SRichard Purdie 297b36d61d4SFrank Mandarino static int wm8731_hw_params(struct snd_pcm_substream *substream, 298dee89c4dSMark Brown struct snd_pcm_hw_params *params, 299dee89c4dSMark Brown struct snd_soc_dai *dai) 30040e0aa64SRichard Purdie { 301f0fba2adSLiam Girdwood struct snd_soc_codec *codec = dai->codec; 302b2c812e2SMark Brown struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); 30317a52fd6SMark Brown u16 iface = snd_soc_read(codec, WM8731_IFACE) & 0xfff3; 304b36d61d4SFrank Mandarino int i = get_coeff(wm8731->sysclk, params_rate(params)); 305b36d61d4SFrank Mandarino u16 srate = (coeff_div[i].sr << 2) | 306b36d61d4SFrank Mandarino (coeff_div[i].bosr << 1) | coeff_div[i].usb; 30740e0aa64SRichard Purdie 308*dd31b310SMark Brown wm8731->playback_fs = params_rate(params); 309*dd31b310SMark Brown 31017a52fd6SMark Brown snd_soc_write(codec, WM8731_SRATE, srate); 31140e0aa64SRichard Purdie 312b36d61d4SFrank Mandarino /* bit size */ 313b36d61d4SFrank Mandarino switch (params_format(params)) { 314b36d61d4SFrank Mandarino case SNDRV_PCM_FORMAT_S16_LE: 315b36d61d4SFrank Mandarino break; 316b36d61d4SFrank Mandarino case SNDRV_PCM_FORMAT_S20_3LE: 317b36d61d4SFrank Mandarino iface |= 0x0004; 318b36d61d4SFrank Mandarino break; 319b36d61d4SFrank Mandarino case SNDRV_PCM_FORMAT_S24_LE: 320b36d61d4SFrank Mandarino iface |= 0x0008; 321b36d61d4SFrank Mandarino break; 322b36d61d4SFrank Mandarino } 323b36d61d4SFrank Mandarino 324*dd31b310SMark Brown wm8731_set_deemph(codec); 325*dd31b310SMark Brown 32617a52fd6SMark Brown snd_soc_write(codec, WM8731_IFACE, iface); 327b36d61d4SFrank Mandarino return 0; 32840e0aa64SRichard Purdie } 32940e0aa64SRichard Purdie 330dee89c4dSMark Brown static int wm8731_pcm_prepare(struct snd_pcm_substream *substream, 331dee89c4dSMark Brown struct snd_soc_dai *dai) 33240e0aa64SRichard Purdie { 333f0fba2adSLiam Girdwood struct snd_soc_codec *codec = dai->codec; 33440e0aa64SRichard Purdie 33540e0aa64SRichard Purdie /* set active */ 33617a52fd6SMark Brown snd_soc_write(codec, WM8731_ACTIVE, 0x0001); 337b36d61d4SFrank Mandarino 33840e0aa64SRichard Purdie return 0; 33940e0aa64SRichard Purdie } 34040e0aa64SRichard Purdie 341dee89c4dSMark Brown static void wm8731_shutdown(struct snd_pcm_substream *substream, 342dee89c4dSMark Brown struct snd_soc_dai *dai) 34340e0aa64SRichard Purdie { 344f0fba2adSLiam Girdwood struct snd_soc_codec *codec = dai->codec; 34540e0aa64SRichard Purdie 34640e0aa64SRichard Purdie /* deactivate */ 34740e0aa64SRichard Purdie if (!codec->active) { 34840e0aa64SRichard Purdie udelay(50); 34917a52fd6SMark Brown snd_soc_write(codec, WM8731_ACTIVE, 0x0); 35040e0aa64SRichard Purdie } 35140e0aa64SRichard Purdie } 35240e0aa64SRichard Purdie 353e550e17fSLiam Girdwood static int wm8731_mute(struct snd_soc_dai *dai, int mute) 35440e0aa64SRichard Purdie { 355b36d61d4SFrank Mandarino struct snd_soc_codec *codec = dai->codec; 35617a52fd6SMark Brown u16 mute_reg = snd_soc_read(codec, WM8731_APDIGI) & 0xfff7; 357b36d61d4SFrank Mandarino 35840e0aa64SRichard Purdie if (mute) 35917a52fd6SMark Brown snd_soc_write(codec, WM8731_APDIGI, mute_reg | 0x8); 36040e0aa64SRichard Purdie else 36117a52fd6SMark Brown snd_soc_write(codec, WM8731_APDIGI, mute_reg); 36240e0aa64SRichard Purdie return 0; 36340e0aa64SRichard Purdie } 36440e0aa64SRichard Purdie 365e550e17fSLiam Girdwood static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai, 366b36d61d4SFrank Mandarino int clk_id, unsigned int freq, int dir) 367b36d61d4SFrank Mandarino { 368b36d61d4SFrank Mandarino struct snd_soc_codec *codec = codec_dai->codec; 369b2c812e2SMark Brown struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); 370b36d61d4SFrank Mandarino 3719745e824SMark Brown switch (clk_id) { 3729745e824SMark Brown case WM8731_SYSCLK_XTAL: 3739745e824SMark Brown case WM8731_SYSCLK_MCLK: 3749745e824SMark Brown wm8731->sysclk_type = clk_id; 3759745e824SMark Brown break; 3769745e824SMark Brown default: 3779745e824SMark Brown return -EINVAL; 3789745e824SMark Brown } 3799745e824SMark Brown 380b36d61d4SFrank Mandarino switch (freq) { 381b36d61d4SFrank Mandarino case 11289600: 382b36d61d4SFrank Mandarino case 12000000: 383b36d61d4SFrank Mandarino case 12288000: 384b36d61d4SFrank Mandarino case 16934400: 385b36d61d4SFrank Mandarino case 18432000: 386b36d61d4SFrank Mandarino wm8731->sysclk = freq; 3879745e824SMark Brown break; 3889745e824SMark Brown default: 389b36d61d4SFrank Mandarino return -EINVAL; 390b36d61d4SFrank Mandarino } 391b36d61d4SFrank Mandarino 392ce6120ccSLiam Girdwood snd_soc_dapm_sync(&codec->dapm); 3939745e824SMark Brown 3949745e824SMark Brown return 0; 3959745e824SMark Brown } 3969745e824SMark Brown 397b36d61d4SFrank Mandarino 398e550e17fSLiam Girdwood static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai, 399b36d61d4SFrank Mandarino unsigned int fmt) 400b36d61d4SFrank Mandarino { 401b36d61d4SFrank Mandarino struct snd_soc_codec *codec = codec_dai->codec; 402b36d61d4SFrank Mandarino u16 iface = 0; 403b36d61d4SFrank Mandarino 404b36d61d4SFrank Mandarino /* set master/slave audio interface */ 405b36d61d4SFrank Mandarino switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 406b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_CBM_CFM: 407b36d61d4SFrank Mandarino iface |= 0x0040; 408b36d61d4SFrank Mandarino break; 409b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_CBS_CFS: 410b36d61d4SFrank Mandarino break; 411b36d61d4SFrank Mandarino default: 412b36d61d4SFrank Mandarino return -EINVAL; 413b36d61d4SFrank Mandarino } 414b36d61d4SFrank Mandarino 415b36d61d4SFrank Mandarino /* interface format */ 416b36d61d4SFrank Mandarino switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 417b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_I2S: 418b36d61d4SFrank Mandarino iface |= 0x0002; 419b36d61d4SFrank Mandarino break; 420b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_RIGHT_J: 421b36d61d4SFrank Mandarino break; 422b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_LEFT_J: 423b36d61d4SFrank Mandarino iface |= 0x0001; 424b36d61d4SFrank Mandarino break; 425b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_DSP_A: 426b36d61d4SFrank Mandarino iface |= 0x0003; 427b36d61d4SFrank Mandarino break; 428b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_DSP_B: 429b36d61d4SFrank Mandarino iface |= 0x0013; 430b36d61d4SFrank Mandarino break; 431b36d61d4SFrank Mandarino default: 432b36d61d4SFrank Mandarino return -EINVAL; 433b36d61d4SFrank Mandarino } 434b36d61d4SFrank Mandarino 435b36d61d4SFrank Mandarino /* clock inversion */ 436b36d61d4SFrank Mandarino switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 437b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_NB_NF: 438b36d61d4SFrank Mandarino break; 439b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_IB_IF: 440b36d61d4SFrank Mandarino iface |= 0x0090; 441b36d61d4SFrank Mandarino break; 442b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_IB_NF: 443b36d61d4SFrank Mandarino iface |= 0x0080; 444b36d61d4SFrank Mandarino break; 445b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_NB_IF: 446b36d61d4SFrank Mandarino iface |= 0x0010; 447b36d61d4SFrank Mandarino break; 448b36d61d4SFrank Mandarino default: 449b36d61d4SFrank Mandarino return -EINVAL; 450b36d61d4SFrank Mandarino } 451b36d61d4SFrank Mandarino 452b36d61d4SFrank Mandarino /* set iface */ 45317a52fd6SMark Brown snd_soc_write(codec, WM8731_IFACE, iface); 454b36d61d4SFrank Mandarino return 0; 455b36d61d4SFrank Mandarino } 456b36d61d4SFrank Mandarino 4570be9898aSMark Brown static int wm8731_set_bias_level(struct snd_soc_codec *codec, 4580be9898aSMark Brown enum snd_soc_bias_level level) 45940e0aa64SRichard Purdie { 46006ae9988SMark Brown struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); 46106ae9988SMark Brown int i, ret; 46206ae9988SMark Brown u8 data[2]; 46306ae9988SMark Brown u16 *cache = codec->reg_cache; 46422d22ee5SMark Brown u16 reg; 46540e0aa64SRichard Purdie 4660be9898aSMark Brown switch (level) { 4670be9898aSMark Brown case SND_SOC_BIAS_ON: 46840e0aa64SRichard Purdie break; 4690be9898aSMark Brown case SND_SOC_BIAS_PREPARE: 47040e0aa64SRichard Purdie break; 4710be9898aSMark Brown case SND_SOC_BIAS_STANDBY: 472ce6120ccSLiam Girdwood if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { 47306ae9988SMark Brown ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), 47406ae9988SMark Brown wm8731->supplies); 47506ae9988SMark Brown if (ret != 0) 47606ae9988SMark Brown return ret; 47706ae9988SMark Brown 47806ae9988SMark Brown /* Sync reg_cache with the hardware */ 47906ae9988SMark Brown for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) { 48006ae9988SMark Brown if (cache[i] == wm8731_reg[i]) 48106ae9988SMark Brown continue; 48206ae9988SMark Brown 48306ae9988SMark Brown data[0] = (i << 1) | ((cache[i] >> 8) 48406ae9988SMark Brown & 0x0001); 48506ae9988SMark Brown data[1] = cache[i] & 0x00ff; 48606ae9988SMark Brown codec->hw_write(codec->control_data, data, 2); 48706ae9988SMark Brown } 48806ae9988SMark Brown } 48906ae9988SMark Brown 49022d22ee5SMark Brown /* Clear PWROFF, gate CLKOUT, everything else as-is */ 49117a52fd6SMark Brown reg = snd_soc_read(codec, WM8731_PWR) & 0xff7f; 49217a52fd6SMark Brown snd_soc_write(codec, WM8731_PWR, reg | 0x0040); 49340e0aa64SRichard Purdie break; 4940be9898aSMark Brown case SND_SOC_BIAS_OFF: 49517a52fd6SMark Brown snd_soc_write(codec, WM8731_ACTIVE, 0x0); 49617a52fd6SMark Brown snd_soc_write(codec, WM8731_PWR, 0xffff); 49706ae9988SMark Brown regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), 49806ae9988SMark Brown wm8731->supplies); 49940e0aa64SRichard Purdie break; 50040e0aa64SRichard Purdie } 501ce6120ccSLiam Girdwood codec->dapm.bias_level = level; 50240e0aa64SRichard Purdie return 0; 50340e0aa64SRichard Purdie } 50440e0aa64SRichard Purdie 505e135443eSBill Gatliff #define WM8731_RATES SNDRV_PCM_RATE_8000_96000 506b36d61d4SFrank Mandarino 507b36d61d4SFrank Mandarino #define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 508b36d61d4SFrank Mandarino SNDRV_PCM_FMTBIT_S24_LE) 509b36d61d4SFrank Mandarino 5106335d055SEric Miao static struct snd_soc_dai_ops wm8731_dai_ops = { 5116335d055SEric Miao .prepare = wm8731_pcm_prepare, 5126335d055SEric Miao .hw_params = wm8731_hw_params, 5136335d055SEric Miao .shutdown = wm8731_shutdown, 5146335d055SEric Miao .digital_mute = wm8731_mute, 5156335d055SEric Miao .set_sysclk = wm8731_set_dai_sysclk, 5166335d055SEric Miao .set_fmt = wm8731_set_dai_fmt, 5176335d055SEric Miao }; 5186335d055SEric Miao 519f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8731_dai = { 520f0fba2adSLiam Girdwood .name = "wm8731-hifi", 52140e0aa64SRichard Purdie .playback = { 52240e0aa64SRichard Purdie .stream_name = "Playback", 52340e0aa64SRichard Purdie .channels_min = 1, 52440e0aa64SRichard Purdie .channels_max = 2, 525b36d61d4SFrank Mandarino .rates = WM8731_RATES, 526b36d61d4SFrank Mandarino .formats = WM8731_FORMATS,}, 52740e0aa64SRichard Purdie .capture = { 52840e0aa64SRichard Purdie .stream_name = "Capture", 52940e0aa64SRichard Purdie .channels_min = 1, 53040e0aa64SRichard Purdie .channels_max = 2, 531b36d61d4SFrank Mandarino .rates = WM8731_RATES, 532b36d61d4SFrank Mandarino .formats = WM8731_FORMATS,}, 5336335d055SEric Miao .ops = &wm8731_dai_ops, 5344934482dSMark Brown .symmetric_rates = 1, 53540e0aa64SRichard Purdie }; 53640e0aa64SRichard Purdie 537b3b50b3fSMark Brown #ifdef CONFIG_PM 538f0fba2adSLiam Girdwood static int wm8731_suspend(struct snd_soc_codec *codec, pm_message_t state) 53940e0aa64SRichard Purdie { 5400be9898aSMark Brown wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); 54106ae9988SMark Brown 54240e0aa64SRichard Purdie return 0; 54340e0aa64SRichard Purdie } 54440e0aa64SRichard Purdie 545f0fba2adSLiam Girdwood static int wm8731_resume(struct snd_soc_codec *codec) 54640e0aa64SRichard Purdie { 5470be9898aSMark Brown wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 5487dea7c01SMark Brown 54940e0aa64SRichard Purdie return 0; 55040e0aa64SRichard Purdie } 551b3b50b3fSMark Brown #else 552b3b50b3fSMark Brown #define wm8731_suspend NULL 553b3b50b3fSMark Brown #define wm8731_resume NULL 554b3b50b3fSMark Brown #endif 55540e0aa64SRichard Purdie 556f0fba2adSLiam Girdwood static int wm8731_probe(struct snd_soc_codec *codec) 55740e0aa64SRichard Purdie { 558f0fba2adSLiam Girdwood struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); 559f0fba2adSLiam Girdwood int ret = 0, i; 56040e0aa64SRichard Purdie 561f0fba2adSLiam Girdwood ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8731->control_type); 56217a52fd6SMark Brown if (ret < 0) { 56317a52fd6SMark Brown dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); 564f0fba2adSLiam Girdwood return ret; 56517a52fd6SMark Brown } 56617a52fd6SMark Brown 5677dea7c01SMark Brown for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++) 5687dea7c01SMark Brown wm8731->supplies[i].supply = wm8731_supply_names[i]; 5697dea7c01SMark Brown 5707dea7c01SMark Brown ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies), 5717dea7c01SMark Brown wm8731->supplies); 5727dea7c01SMark Brown if (ret != 0) { 5737dea7c01SMark Brown dev_err(codec->dev, "Failed to request supplies: %d\n", ret); 574f0fba2adSLiam Girdwood return ret; 5757dea7c01SMark Brown } 5767dea7c01SMark Brown 5777dea7c01SMark Brown ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), 5787dea7c01SMark Brown wm8731->supplies); 5797dea7c01SMark Brown if (ret != 0) { 5807dea7c01SMark Brown dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); 5817dea7c01SMark Brown goto err_regulator_get; 5827dea7c01SMark Brown } 5837dea7c01SMark Brown 584519cf2dfSMark Brown ret = wm8731_reset(codec); 585519cf2dfSMark Brown if (ret < 0) { 586fe5422fcSMark Brown dev_err(codec->dev, "Failed to issue reset: %d\n", ret); 5877dea7c01SMark Brown goto err_regulator_enable; 588519cf2dfSMark Brown } 589519cf2dfSMark Brown 5905998102bSMark Brown wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 5915998102bSMark Brown 5925998102bSMark Brown /* Latch the update bits */ 59317a52fd6SMark Brown snd_soc_update_bits(codec, WM8731_LOUT1V, 0x100, 0); 59417a52fd6SMark Brown snd_soc_update_bits(codec, WM8731_ROUT1V, 0x100, 0); 59517a52fd6SMark Brown snd_soc_update_bits(codec, WM8731_LINVOL, 0x100, 0); 59617a52fd6SMark Brown snd_soc_update_bits(codec, WM8731_RINVOL, 0x100, 0); 5975998102bSMark Brown 598ce3bdaa8SMark Brown /* Disable bypass path by default */ 5992062ea52SDimitris Papastamos snd_soc_update_bits(codec, WM8731_APANA, 0x8, 0); 600ce3bdaa8SMark Brown 601f0fba2adSLiam Girdwood snd_soc_add_controls(codec, wm8731_snd_controls, 602f0fba2adSLiam Girdwood ARRAY_SIZE(wm8731_snd_controls)); 603f0fba2adSLiam Girdwood wm8731_add_widgets(codec); 6045998102bSMark Brown 60506ae9988SMark Brown /* Regulators will have been enabled by bias management */ 60606ae9988SMark Brown regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); 60706ae9988SMark Brown 608a8035c8fSMark Brown return 0; 609fe5422fcSMark Brown 6107dea7c01SMark Brown err_regulator_enable: 6117dea7c01SMark Brown regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); 6127dea7c01SMark Brown err_regulator_get: 6137dea7c01SMark Brown regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); 614f0fba2adSLiam Girdwood 615fe5422fcSMark Brown return ret; 616a8035c8fSMark Brown } 617a8035c8fSMark Brown 618f0fba2adSLiam Girdwood /* power down chip */ 619f0fba2adSLiam Girdwood static int wm8731_remove(struct snd_soc_codec *codec) 6205998102bSMark Brown { 621f0fba2adSLiam Girdwood struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); 622f0fba2adSLiam Girdwood 623f0fba2adSLiam Girdwood wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); 624f0fba2adSLiam Girdwood 625f0fba2adSLiam Girdwood regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); 6267dea7c01SMark Brown regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); 627f0fba2adSLiam Girdwood 628f0fba2adSLiam Girdwood return 0; 6295998102bSMark Brown } 630a8035c8fSMark Brown 631f0fba2adSLiam Girdwood static struct snd_soc_codec_driver soc_codec_dev_wm8731 = { 632f0fba2adSLiam Girdwood .probe = wm8731_probe, 633f0fba2adSLiam Girdwood .remove = wm8731_remove, 634f0fba2adSLiam Girdwood .suspend = wm8731_suspend, 635f0fba2adSLiam Girdwood .resume = wm8731_resume, 636f0fba2adSLiam Girdwood .set_bias_level = wm8731_set_bias_level, 637e5eec34cSDimitris Papastamos .reg_cache_size = ARRAY_SIZE(wm8731_reg), 638f0fba2adSLiam Girdwood .reg_word_size = sizeof(u16), 639f0fba2adSLiam Girdwood .reg_cache_default = wm8731_reg, 640f0fba2adSLiam Girdwood }; 641f0fba2adSLiam Girdwood 6425998102bSMark Brown #if defined(CONFIG_SPI_MASTER) 6435998102bSMark Brown static int __devinit wm8731_spi_probe(struct spi_device *spi) 6445998102bSMark Brown { 6455998102bSMark Brown struct wm8731_priv *wm8731; 646f0fba2adSLiam Girdwood int ret; 6475998102bSMark Brown 6485998102bSMark Brown wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL); 6495998102bSMark Brown if (wm8731 == NULL) 6505998102bSMark Brown return -ENOMEM; 6515998102bSMark Brown 652f0fba2adSLiam Girdwood wm8731->control_type = SND_SOC_SPI; 653f0fba2adSLiam Girdwood spi_set_drvdata(spi, wm8731); 6545998102bSMark Brown 655f0fba2adSLiam Girdwood ret = snd_soc_register_codec(&spi->dev, 656f0fba2adSLiam Girdwood &soc_codec_dev_wm8731, &wm8731_dai, 1); 657f0fba2adSLiam Girdwood if (ret < 0) 658f0fba2adSLiam Girdwood kfree(wm8731); 659f0fba2adSLiam Girdwood return ret; 6605998102bSMark Brown } 6615998102bSMark Brown 6625998102bSMark Brown static int __devexit wm8731_spi_remove(struct spi_device *spi) 6635998102bSMark Brown { 664f0fba2adSLiam Girdwood snd_soc_unregister_codec(&spi->dev); 665f0fba2adSLiam Girdwood kfree(spi_get_drvdata(spi)); 6665998102bSMark Brown return 0; 6675998102bSMark Brown } 6685998102bSMark Brown 6695998102bSMark Brown static struct spi_driver wm8731_spi_driver = { 6705998102bSMark Brown .driver = { 671f0fba2adSLiam Girdwood .name = "wm8731-codec", 6725998102bSMark Brown .owner = THIS_MODULE, 6735998102bSMark Brown }, 6745998102bSMark Brown .probe = wm8731_spi_probe, 6755998102bSMark Brown .remove = __devexit_p(wm8731_spi_remove), 6765998102bSMark Brown }; 677a8035c8fSMark Brown #endif /* CONFIG_SPI_MASTER */ 678a8035c8fSMark Brown 679a8035c8fSMark Brown #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 680c6f29811SMark Brown static __devinit int wm8731_i2c_probe(struct i2c_client *i2c, 681a8035c8fSMark Brown const struct i2c_device_id *id) 682a8035c8fSMark Brown { 6835998102bSMark Brown struct wm8731_priv *wm8731; 684f0fba2adSLiam Girdwood int ret; 685a8035c8fSMark Brown 6865998102bSMark Brown wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL); 6875998102bSMark Brown if (wm8731 == NULL) 6885998102bSMark Brown return -ENOMEM; 6895998102bSMark Brown 6905998102bSMark Brown i2c_set_clientdata(i2c, wm8731); 691f0fba2adSLiam Girdwood wm8731->control_type = SND_SOC_I2C; 692a8035c8fSMark Brown 693f0fba2adSLiam Girdwood ret = snd_soc_register_codec(&i2c->dev, 694f0fba2adSLiam Girdwood &soc_codec_dev_wm8731, &wm8731_dai, 1); 695f0fba2adSLiam Girdwood if (ret < 0) 696f0fba2adSLiam Girdwood kfree(wm8731); 697f0fba2adSLiam Girdwood return ret; 698a8035c8fSMark Brown } 699a8035c8fSMark Brown 700c6f29811SMark Brown static __devexit int wm8731_i2c_remove(struct i2c_client *client) 701a8035c8fSMark Brown { 702f0fba2adSLiam Girdwood snd_soc_unregister_codec(&client->dev); 703f0fba2adSLiam Girdwood kfree(i2c_get_clientdata(client)); 704a8035c8fSMark Brown return 0; 705a8035c8fSMark Brown } 706a8035c8fSMark Brown 707a8035c8fSMark Brown static const struct i2c_device_id wm8731_i2c_id[] = { 708a8035c8fSMark Brown { "wm8731", 0 }, 709a8035c8fSMark Brown { } 710a8035c8fSMark Brown }; 711a8035c8fSMark Brown MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id); 712a8035c8fSMark Brown 713a8035c8fSMark Brown static struct i2c_driver wm8731_i2c_driver = { 714a8035c8fSMark Brown .driver = { 715f0fba2adSLiam Girdwood .name = "wm8731-codec", 716a8035c8fSMark Brown .owner = THIS_MODULE, 717a8035c8fSMark Brown }, 718a8035c8fSMark Brown .probe = wm8731_i2c_probe, 719c6f29811SMark Brown .remove = __devexit_p(wm8731_i2c_remove), 720a8035c8fSMark Brown .id_table = wm8731_i2c_id, 721a8035c8fSMark Brown }; 722a8035c8fSMark Brown #endif 723a8035c8fSMark Brown 724c9b3a40fSTakashi Iwai static int __init wm8731_modinit(void) 72564089b84SMark Brown { 726f0fba2adSLiam Girdwood int ret = 0; 7275998102bSMark Brown #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 7285998102bSMark Brown ret = i2c_add_driver(&wm8731_i2c_driver); 7295998102bSMark Brown if (ret != 0) { 7305998102bSMark Brown printk(KERN_ERR "Failed to register WM8731 I2C driver: %d\n", 7315998102bSMark Brown ret); 7325998102bSMark Brown } 7335998102bSMark Brown #endif 7345998102bSMark Brown #if defined(CONFIG_SPI_MASTER) 7355998102bSMark Brown ret = spi_register_driver(&wm8731_spi_driver); 7365998102bSMark Brown if (ret != 0) { 7375998102bSMark Brown printk(KERN_ERR "Failed to register WM8731 SPI driver: %d\n", 7385998102bSMark Brown ret); 7395998102bSMark Brown } 7405998102bSMark Brown #endif 741f0fba2adSLiam Girdwood return ret; 74264089b84SMark Brown } 74364089b84SMark Brown module_init(wm8731_modinit); 74464089b84SMark Brown 74564089b84SMark Brown static void __exit wm8731_exit(void) 74664089b84SMark Brown { 7475998102bSMark Brown #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 7485998102bSMark Brown i2c_del_driver(&wm8731_i2c_driver); 7495998102bSMark Brown #endif 7505998102bSMark Brown #if defined(CONFIG_SPI_MASTER) 7515998102bSMark Brown spi_unregister_driver(&wm8731_spi_driver); 7525998102bSMark Brown #endif 75364089b84SMark Brown } 75464089b84SMark Brown module_exit(wm8731_exit); 75564089b84SMark Brown 75640e0aa64SRichard Purdie MODULE_DESCRIPTION("ASoC WM8731 driver"); 75740e0aa64SRichard Purdie MODULE_AUTHOR("Richard Purdie"); 75840e0aa64SRichard Purdie MODULE_LICENSE("GPL"); 759