140e0aa64SRichard Purdie /* 240e0aa64SRichard Purdie * wm8731.c -- WM8731 ALSA SoC Audio driver 340e0aa64SRichard Purdie * 440e0aa64SRichard Purdie * Copyright 2005 Openedhand Ltd. 5*656baaebSMark Brown * Copyright 2006-12 Wolfson Microelectronics, plc 640e0aa64SRichard Purdie * 740e0aa64SRichard Purdie * Author: Richard Purdie <richard@openedhand.com> 840e0aa64SRichard Purdie * 940e0aa64SRichard Purdie * Based on wm8753.c by Liam Girdwood 1040e0aa64SRichard Purdie * 1140e0aa64SRichard Purdie * This program is free software; you can redistribute it and/or modify 1240e0aa64SRichard Purdie * it under the terms of the GNU General Public License version 2 as 1340e0aa64SRichard Purdie * published by the Free Software Foundation. 1440e0aa64SRichard Purdie */ 1540e0aa64SRichard Purdie 1640e0aa64SRichard Purdie #include <linux/module.h> 1740e0aa64SRichard Purdie #include <linux/moduleparam.h> 1840e0aa64SRichard Purdie #include <linux/init.h> 1940e0aa64SRichard Purdie #include <linux/delay.h> 2040e0aa64SRichard Purdie #include <linux/pm.h> 2140e0aa64SRichard Purdie #include <linux/i2c.h> 225a0e3ad6STejun Heo #include <linux/slab.h> 2305d448e2SMark Brown #include <linux/regmap.h> 247dea7c01SMark Brown #include <linux/regulator/consumer.h> 25d2a40355SCliff Cai #include <linux/spi/spi.h> 26a7f96e4dSMark Brown #include <linux/of_device.h> 2740e0aa64SRichard Purdie #include <sound/core.h> 2840e0aa64SRichard Purdie #include <sound/pcm.h> 2940e0aa64SRichard Purdie #include <sound/pcm_params.h> 3040e0aa64SRichard Purdie #include <sound/soc.h> 3140e0aa64SRichard Purdie #include <sound/initval.h> 32d00efa64SMark Brown #include <sound/tlv.h> 3340e0aa64SRichard Purdie 3440e0aa64SRichard Purdie #include "wm8731.h" 3540e0aa64SRichard Purdie 367dea7c01SMark Brown #define WM8731_NUM_SUPPLIES 4 377dea7c01SMark Brown static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { 387dea7c01SMark Brown "AVDD", 397dea7c01SMark Brown "HPVDD", 407dea7c01SMark Brown "DCVDD", 417dea7c01SMark Brown "DBVDD", 427dea7c01SMark Brown }; 437dea7c01SMark Brown 44b36d61d4SFrank Mandarino /* codec private data */ 45b36d61d4SFrank Mandarino struct wm8731_priv { 4605d448e2SMark Brown struct regmap *regmap; 477dea7c01SMark Brown struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; 48b36d61d4SFrank Mandarino unsigned int sysclk; 499745e824SMark Brown int sysclk_type; 50dd31b310SMark Brown int playback_fs; 51dd31b310SMark Brown bool deemph; 52b36d61d4SFrank Mandarino }; 53b36d61d4SFrank Mandarino 54a8035c8fSMark Brown 5540e0aa64SRichard Purdie /* 5640e0aa64SRichard Purdie * wm8731 register cache 5740e0aa64SRichard Purdie */ 5805d448e2SMark Brown static const struct reg_default wm8731_reg_defaults[] = { 5905d448e2SMark Brown { 0, 0x0097 }, 6005d448e2SMark Brown { 1, 0x0097 }, 6105d448e2SMark Brown { 2, 0x0079 }, 6205d448e2SMark Brown { 3, 0x0079 }, 6305d448e2SMark Brown { 4, 0x000a }, 6405d448e2SMark Brown { 5, 0x0008 }, 6505d448e2SMark Brown { 6, 0x009f }, 6605d448e2SMark Brown { 7, 0x000a }, 6705d448e2SMark Brown { 8, 0x0000 }, 6805d448e2SMark Brown { 9, 0x0000 }, 6940e0aa64SRichard Purdie }; 7040e0aa64SRichard Purdie 7105d448e2SMark Brown static bool wm8731_volatile(struct device *dev, unsigned int reg) 7205d448e2SMark Brown { 7305d448e2SMark Brown return reg == WM8731_RESET; 7405d448e2SMark Brown } 7505d448e2SMark Brown 7605d448e2SMark Brown static bool wm8731_writeable(struct device *dev, unsigned int reg) 7705d448e2SMark Brown { 7805d448e2SMark Brown return reg <= WM8731_RESET; 7905d448e2SMark Brown } 8005d448e2SMark Brown 8117a52fd6SMark Brown #define wm8731_reset(c) snd_soc_write(c, WM8731_RESET, 0) 8240e0aa64SRichard Purdie 8340e0aa64SRichard Purdie static const char *wm8731_input_select[] = {"Line In", "Mic"}; 8459f72970SMark Brown 8559f72970SMark Brown static const struct soc_enum wm8731_insel_enum = 8659f72970SMark Brown SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select); 8759f72970SMark Brown 88dd31b310SMark Brown static int wm8731_deemph[] = { 0, 32000, 44100, 48000 }; 8959f72970SMark Brown 90dd31b310SMark Brown static int wm8731_set_deemph(struct snd_soc_codec *codec) 91dd31b310SMark Brown { 92dd31b310SMark Brown struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); 93dd31b310SMark Brown int val, i, best; 94dd31b310SMark Brown 95dd31b310SMark Brown /* If we're using deemphasis select the nearest available sample 96dd31b310SMark Brown * rate. 97dd31b310SMark Brown */ 98dd31b310SMark Brown if (wm8731->deemph) { 99dd31b310SMark Brown best = 1; 100dd31b310SMark Brown for (i = 2; i < ARRAY_SIZE(wm8731_deemph); i++) { 101dd31b310SMark Brown if (abs(wm8731_deemph[i] - wm8731->playback_fs) < 102dd31b310SMark Brown abs(wm8731_deemph[best] - wm8731->playback_fs)) 103dd31b310SMark Brown best = i; 104dd31b310SMark Brown } 105dd31b310SMark Brown 106dd31b310SMark Brown val = best << 1; 107dd31b310SMark Brown } else { 108dd31b310SMark Brown best = 0; 109dd31b310SMark Brown val = 0; 110dd31b310SMark Brown } 111dd31b310SMark Brown 112dd31b310SMark Brown dev_dbg(codec->dev, "Set deemphasis %d (%dHz)\n", 113dd31b310SMark Brown best, wm8731_deemph[best]); 114dd31b310SMark Brown 115dd31b310SMark Brown return snd_soc_update_bits(codec, WM8731_APDIGI, 0x6, val); 116dd31b310SMark Brown } 117dd31b310SMark Brown 118dd31b310SMark Brown static int wm8731_get_deemph(struct snd_kcontrol *kcontrol, 119dd31b310SMark Brown struct snd_ctl_elem_value *ucontrol) 120dd31b310SMark Brown { 121dd31b310SMark Brown struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); 122dd31b310SMark Brown struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); 123dd31b310SMark Brown 124dd31b310SMark Brown ucontrol->value.enumerated.item[0] = wm8731->deemph; 125dd31b310SMark Brown 126dd31b310SMark Brown return 0; 127dd31b310SMark Brown } 128dd31b310SMark Brown 129dd31b310SMark Brown static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, 130dd31b310SMark Brown struct snd_ctl_elem_value *ucontrol) 131dd31b310SMark Brown { 132dd31b310SMark Brown struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); 133dd31b310SMark Brown struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); 134dd31b310SMark Brown int deemph = ucontrol->value.enumerated.item[0]; 135dd31b310SMark Brown int ret = 0; 136dd31b310SMark Brown 137dd31b310SMark Brown if (deemph > 1) 138dd31b310SMark Brown return -EINVAL; 139dd31b310SMark Brown 140dd31b310SMark Brown mutex_lock(&codec->mutex); 141dd31b310SMark Brown if (wm8731->deemph != deemph) { 142dd31b310SMark Brown wm8731->deemph = deemph; 143dd31b310SMark Brown 144dd31b310SMark Brown wm8731_set_deemph(codec); 145dd31b310SMark Brown 146dd31b310SMark Brown ret = 1; 147dd31b310SMark Brown } 148dd31b310SMark Brown mutex_unlock(&codec->mutex); 149dd31b310SMark Brown 150dd31b310SMark Brown return ret; 151dd31b310SMark Brown } 15240e0aa64SRichard Purdie 153d00efa64SMark Brown static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); 154d00efa64SMark Brown static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0); 155d00efa64SMark Brown static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); 156d921184eSMark Brown static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 2000, 0); 157d00efa64SMark Brown 15840e0aa64SRichard Purdie static const struct snd_kcontrol_new wm8731_snd_controls[] = { 15940e0aa64SRichard Purdie 160d00efa64SMark Brown SOC_DOUBLE_R_TLV("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V, 161d00efa64SMark Brown 0, 127, 0, out_tlv), 162bd903b6eSLiam Girdwood SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V, 163bd903b6eSLiam Girdwood 7, 1, 0), 16440e0aa64SRichard Purdie 165d00efa64SMark Brown SOC_DOUBLE_R_TLV("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0, 166d00efa64SMark Brown in_tlv), 16740e0aa64SRichard Purdie SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1), 16840e0aa64SRichard Purdie 169d921184eSMark Brown SOC_SINGLE_TLV("Mic Boost Volume", WM8731_APANA, 0, 1, 0, mic_tlv), 170ef38ed88SMark Brown SOC_SINGLE("Mic Capture Switch", WM8731_APANA, 1, 1, 1), 17140e0aa64SRichard Purdie 172d00efa64SMark Brown SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1, 173d00efa64SMark Brown sidetone_tlv), 17440e0aa64SRichard Purdie 17540e0aa64SRichard Purdie SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1), 17640e0aa64SRichard Purdie SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0), 17740e0aa64SRichard Purdie 178dd31b310SMark Brown SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0, 179dd31b310SMark Brown wm8731_get_deemph, wm8731_put_deemph), 18040e0aa64SRichard Purdie }; 18140e0aa64SRichard Purdie 18240e0aa64SRichard Purdie /* Output Mixer */ 18340e0aa64SRichard Purdie static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = { 18440e0aa64SRichard Purdie SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0), 18540e0aa64SRichard Purdie SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0), 18640e0aa64SRichard Purdie SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0), 18740e0aa64SRichard Purdie }; 18840e0aa64SRichard Purdie 18940e0aa64SRichard Purdie /* Input mux */ 19040e0aa64SRichard Purdie static const struct snd_kcontrol_new wm8731_input_mux_controls = 19159f72970SMark Brown SOC_DAPM_ENUM("Input Select", wm8731_insel_enum); 19240e0aa64SRichard Purdie 19340e0aa64SRichard Purdie static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { 1948a27bd9aSMark Brown SND_SOC_DAPM_SUPPLY("ACTIVE",WM8731_ACTIVE, 0, 0, NULL, 0), 1959745e824SMark Brown SND_SOC_DAPM_SUPPLY("OSC", WM8731_PWR, 5, 1, NULL, 0), 19640e0aa64SRichard Purdie SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, 19740e0aa64SRichard Purdie &wm8731_output_mixer_controls[0], 19840e0aa64SRichard Purdie ARRAY_SIZE(wm8731_output_mixer_controls)), 19940e0aa64SRichard Purdie SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8731_PWR, 3, 1), 20040e0aa64SRichard Purdie SND_SOC_DAPM_OUTPUT("LOUT"), 20140e0aa64SRichard Purdie SND_SOC_DAPM_OUTPUT("LHPOUT"), 20240e0aa64SRichard Purdie SND_SOC_DAPM_OUTPUT("ROUT"), 20340e0aa64SRichard Purdie SND_SOC_DAPM_OUTPUT("RHPOUT"), 20440e0aa64SRichard Purdie SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8731_PWR, 2, 1), 20540e0aa64SRichard Purdie SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &wm8731_input_mux_controls), 20640e0aa64SRichard Purdie SND_SOC_DAPM_PGA("Line Input", WM8731_PWR, 0, 1, NULL, 0), 20740e0aa64SRichard Purdie SND_SOC_DAPM_MICBIAS("Mic Bias", WM8731_PWR, 1, 1), 20840e0aa64SRichard Purdie SND_SOC_DAPM_INPUT("MICIN"), 20940e0aa64SRichard Purdie SND_SOC_DAPM_INPUT("RLINEIN"), 21040e0aa64SRichard Purdie SND_SOC_DAPM_INPUT("LLINEIN"), 21140e0aa64SRichard Purdie }; 21240e0aa64SRichard Purdie 2139745e824SMark Brown static int wm8731_check_osc(struct snd_soc_dapm_widget *source, 2149745e824SMark Brown struct snd_soc_dapm_widget *sink) 2159745e824SMark Brown { 2169745e824SMark Brown struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(source->codec); 2179745e824SMark Brown 2185a195b44SNicolas Ferre return wm8731->sysclk_type == WM8731_SYSCLK_XTAL; 2199745e824SMark Brown } 2209745e824SMark Brown 2215e251aecSMark Brown static const struct snd_soc_dapm_route wm8731_intercon[] = { 2229745e824SMark Brown {"DAC", NULL, "OSC", wm8731_check_osc}, 2239745e824SMark Brown {"ADC", NULL, "OSC", wm8731_check_osc}, 2248a27bd9aSMark Brown {"DAC", NULL, "ACTIVE"}, 2258a27bd9aSMark Brown {"ADC", NULL, "ACTIVE"}, 2269745e824SMark Brown 22740e0aa64SRichard Purdie /* output mixer */ 22840e0aa64SRichard Purdie {"Output Mixer", "Line Bypass Switch", "Line Input"}, 22940e0aa64SRichard Purdie {"Output Mixer", "HiFi Playback Switch", "DAC"}, 23040e0aa64SRichard Purdie {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"}, 23140e0aa64SRichard Purdie 23240e0aa64SRichard Purdie /* outputs */ 23340e0aa64SRichard Purdie {"RHPOUT", NULL, "Output Mixer"}, 23440e0aa64SRichard Purdie {"ROUT", NULL, "Output Mixer"}, 23540e0aa64SRichard Purdie {"LHPOUT", NULL, "Output Mixer"}, 23640e0aa64SRichard Purdie {"LOUT", NULL, "Output Mixer"}, 23740e0aa64SRichard Purdie 23840e0aa64SRichard Purdie /* input mux */ 23940e0aa64SRichard Purdie {"Input Mux", "Line In", "Line Input"}, 24040e0aa64SRichard Purdie {"Input Mux", "Mic", "Mic Bias"}, 24140e0aa64SRichard Purdie {"ADC", NULL, "Input Mux"}, 24240e0aa64SRichard Purdie 24340e0aa64SRichard Purdie /* inputs */ 24440e0aa64SRichard Purdie {"Line Input", NULL, "LLINEIN"}, 24540e0aa64SRichard Purdie {"Line Input", NULL, "RLINEIN"}, 24640e0aa64SRichard Purdie {"Mic Bias", NULL, "MICIN"}, 24740e0aa64SRichard Purdie }; 24840e0aa64SRichard Purdie 24940e0aa64SRichard Purdie struct _coeff_div { 25040e0aa64SRichard Purdie u32 mclk; 25140e0aa64SRichard Purdie u32 rate; 25240e0aa64SRichard Purdie u16 fs; 25340e0aa64SRichard Purdie u8 sr:4; 25440e0aa64SRichard Purdie u8 bosr:1; 25540e0aa64SRichard Purdie u8 usb:1; 25640e0aa64SRichard Purdie }; 25740e0aa64SRichard Purdie 25840e0aa64SRichard Purdie /* codec mclk clock divider coefficients */ 25940e0aa64SRichard Purdie static const struct _coeff_div coeff_div[] = { 26040e0aa64SRichard Purdie /* 48k */ 26140e0aa64SRichard Purdie {12288000, 48000, 256, 0x0, 0x0, 0x0}, 26240e0aa64SRichard Purdie {18432000, 48000, 384, 0x0, 0x1, 0x0}, 26340e0aa64SRichard Purdie {12000000, 48000, 250, 0x0, 0x0, 0x1}, 26440e0aa64SRichard Purdie 26540e0aa64SRichard Purdie /* 32k */ 26640e0aa64SRichard Purdie {12288000, 32000, 384, 0x6, 0x0, 0x0}, 26740e0aa64SRichard Purdie {18432000, 32000, 576, 0x6, 0x1, 0x0}, 268298a2c75SFrank Mandarino {12000000, 32000, 375, 0x6, 0x0, 0x1}, 26940e0aa64SRichard Purdie 27040e0aa64SRichard Purdie /* 8k */ 27140e0aa64SRichard Purdie {12288000, 8000, 1536, 0x3, 0x0, 0x0}, 27240e0aa64SRichard Purdie {18432000, 8000, 2304, 0x3, 0x1, 0x0}, 27340e0aa64SRichard Purdie {11289600, 8000, 1408, 0xb, 0x0, 0x0}, 27440e0aa64SRichard Purdie {16934400, 8000, 2112, 0xb, 0x1, 0x0}, 27540e0aa64SRichard Purdie {12000000, 8000, 1500, 0x3, 0x0, 0x1}, 27640e0aa64SRichard Purdie 27740e0aa64SRichard Purdie /* 96k */ 27840e0aa64SRichard Purdie {12288000, 96000, 128, 0x7, 0x0, 0x0}, 27940e0aa64SRichard Purdie {18432000, 96000, 192, 0x7, 0x1, 0x0}, 28040e0aa64SRichard Purdie {12000000, 96000, 125, 0x7, 0x0, 0x1}, 28140e0aa64SRichard Purdie 28240e0aa64SRichard Purdie /* 44.1k */ 28340e0aa64SRichard Purdie {11289600, 44100, 256, 0x8, 0x0, 0x0}, 28440e0aa64SRichard Purdie {16934400, 44100, 384, 0x8, 0x1, 0x0}, 28540e0aa64SRichard Purdie {12000000, 44100, 272, 0x8, 0x1, 0x1}, 28640e0aa64SRichard Purdie 28740e0aa64SRichard Purdie /* 88.2k */ 28840e0aa64SRichard Purdie {11289600, 88200, 128, 0xf, 0x0, 0x0}, 28940e0aa64SRichard Purdie {16934400, 88200, 192, 0xf, 0x1, 0x0}, 29040e0aa64SRichard Purdie {12000000, 88200, 136, 0xf, 0x1, 0x1}, 29140e0aa64SRichard Purdie }; 29240e0aa64SRichard Purdie 29340e0aa64SRichard Purdie static inline int get_coeff(int mclk, int rate) 29440e0aa64SRichard Purdie { 29540e0aa64SRichard Purdie int i; 29640e0aa64SRichard Purdie 29740e0aa64SRichard Purdie for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { 29840e0aa64SRichard Purdie if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) 29940e0aa64SRichard Purdie return i; 30040e0aa64SRichard Purdie } 30140e0aa64SRichard Purdie return 0; 30240e0aa64SRichard Purdie } 30340e0aa64SRichard Purdie 304b36d61d4SFrank Mandarino static int wm8731_hw_params(struct snd_pcm_substream *substream, 305dee89c4dSMark Brown struct snd_pcm_hw_params *params, 306dee89c4dSMark Brown struct snd_soc_dai *dai) 30740e0aa64SRichard Purdie { 308f0fba2adSLiam Girdwood struct snd_soc_codec *codec = dai->codec; 309b2c812e2SMark Brown struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); 31017a52fd6SMark Brown u16 iface = snd_soc_read(codec, WM8731_IFACE) & 0xfff3; 311b36d61d4SFrank Mandarino int i = get_coeff(wm8731->sysclk, params_rate(params)); 312b36d61d4SFrank Mandarino u16 srate = (coeff_div[i].sr << 2) | 313b36d61d4SFrank Mandarino (coeff_div[i].bosr << 1) | coeff_div[i].usb; 31440e0aa64SRichard Purdie 315dd31b310SMark Brown wm8731->playback_fs = params_rate(params); 316dd31b310SMark Brown 31717a52fd6SMark Brown snd_soc_write(codec, WM8731_SRATE, srate); 31840e0aa64SRichard Purdie 319b36d61d4SFrank Mandarino /* bit size */ 320b36d61d4SFrank Mandarino switch (params_format(params)) { 321b36d61d4SFrank Mandarino case SNDRV_PCM_FORMAT_S16_LE: 322b36d61d4SFrank Mandarino break; 323b36d61d4SFrank Mandarino case SNDRV_PCM_FORMAT_S20_3LE: 324b36d61d4SFrank Mandarino iface |= 0x0004; 325b36d61d4SFrank Mandarino break; 326b36d61d4SFrank Mandarino case SNDRV_PCM_FORMAT_S24_LE: 327b36d61d4SFrank Mandarino iface |= 0x0008; 328b36d61d4SFrank Mandarino break; 329b36d61d4SFrank Mandarino } 330b36d61d4SFrank Mandarino 331dd31b310SMark Brown wm8731_set_deemph(codec); 332dd31b310SMark Brown 33317a52fd6SMark Brown snd_soc_write(codec, WM8731_IFACE, iface); 334b36d61d4SFrank Mandarino return 0; 33540e0aa64SRichard Purdie } 33640e0aa64SRichard Purdie 337e550e17fSLiam Girdwood static int wm8731_mute(struct snd_soc_dai *dai, int mute) 33840e0aa64SRichard Purdie { 339b36d61d4SFrank Mandarino struct snd_soc_codec *codec = dai->codec; 34017a52fd6SMark Brown u16 mute_reg = snd_soc_read(codec, WM8731_APDIGI) & 0xfff7; 341b36d61d4SFrank Mandarino 34240e0aa64SRichard Purdie if (mute) 34317a52fd6SMark Brown snd_soc_write(codec, WM8731_APDIGI, mute_reg | 0x8); 34440e0aa64SRichard Purdie else 34517a52fd6SMark Brown snd_soc_write(codec, WM8731_APDIGI, mute_reg); 34640e0aa64SRichard Purdie return 0; 34740e0aa64SRichard Purdie } 34840e0aa64SRichard Purdie 349e550e17fSLiam Girdwood static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai, 350b36d61d4SFrank Mandarino int clk_id, unsigned int freq, int dir) 351b36d61d4SFrank Mandarino { 352b36d61d4SFrank Mandarino struct snd_soc_codec *codec = codec_dai->codec; 353b2c812e2SMark Brown struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); 354b36d61d4SFrank Mandarino 3559745e824SMark Brown switch (clk_id) { 3569745e824SMark Brown case WM8731_SYSCLK_XTAL: 3579745e824SMark Brown case WM8731_SYSCLK_MCLK: 3589745e824SMark Brown wm8731->sysclk_type = clk_id; 3599745e824SMark Brown break; 3609745e824SMark Brown default: 3619745e824SMark Brown return -EINVAL; 3629745e824SMark Brown } 3639745e824SMark Brown 364b36d61d4SFrank Mandarino switch (freq) { 365b36d61d4SFrank Mandarino case 11289600: 366b36d61d4SFrank Mandarino case 12000000: 367b36d61d4SFrank Mandarino case 12288000: 368b36d61d4SFrank Mandarino case 16934400: 369b36d61d4SFrank Mandarino case 18432000: 370b36d61d4SFrank Mandarino wm8731->sysclk = freq; 3719745e824SMark Brown break; 3729745e824SMark Brown default: 373b36d61d4SFrank Mandarino return -EINVAL; 374b36d61d4SFrank Mandarino } 375b36d61d4SFrank Mandarino 376ce6120ccSLiam Girdwood snd_soc_dapm_sync(&codec->dapm); 3779745e824SMark Brown 3789745e824SMark Brown return 0; 3799745e824SMark Brown } 3809745e824SMark Brown 381b36d61d4SFrank Mandarino 382e550e17fSLiam Girdwood static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai, 383b36d61d4SFrank Mandarino unsigned int fmt) 384b36d61d4SFrank Mandarino { 385b36d61d4SFrank Mandarino struct snd_soc_codec *codec = codec_dai->codec; 386b36d61d4SFrank Mandarino u16 iface = 0; 387b36d61d4SFrank Mandarino 388b36d61d4SFrank Mandarino /* set master/slave audio interface */ 389b36d61d4SFrank Mandarino switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 390b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_CBM_CFM: 391b36d61d4SFrank Mandarino iface |= 0x0040; 392b36d61d4SFrank Mandarino break; 393b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_CBS_CFS: 394b36d61d4SFrank Mandarino break; 395b36d61d4SFrank Mandarino default: 396b36d61d4SFrank Mandarino return -EINVAL; 397b36d61d4SFrank Mandarino } 398b36d61d4SFrank Mandarino 399b36d61d4SFrank Mandarino /* interface format */ 400b36d61d4SFrank Mandarino switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 401b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_I2S: 402b36d61d4SFrank Mandarino iface |= 0x0002; 403b36d61d4SFrank Mandarino break; 404b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_RIGHT_J: 405b36d61d4SFrank Mandarino break; 406b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_LEFT_J: 407b36d61d4SFrank Mandarino iface |= 0x0001; 408b36d61d4SFrank Mandarino break; 409b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_DSP_A: 410b36d61d4SFrank Mandarino iface |= 0x0003; 411b36d61d4SFrank Mandarino break; 412b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_DSP_B: 413b36d61d4SFrank Mandarino iface |= 0x0013; 414b36d61d4SFrank Mandarino break; 415b36d61d4SFrank Mandarino default: 416b36d61d4SFrank Mandarino return -EINVAL; 417b36d61d4SFrank Mandarino } 418b36d61d4SFrank Mandarino 419b36d61d4SFrank Mandarino /* clock inversion */ 420b36d61d4SFrank Mandarino switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 421b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_NB_NF: 422b36d61d4SFrank Mandarino break; 423b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_IB_IF: 424b36d61d4SFrank Mandarino iface |= 0x0090; 425b36d61d4SFrank Mandarino break; 426b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_IB_NF: 427b36d61d4SFrank Mandarino iface |= 0x0080; 428b36d61d4SFrank Mandarino break; 429b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_NB_IF: 430b36d61d4SFrank Mandarino iface |= 0x0010; 431b36d61d4SFrank Mandarino break; 432b36d61d4SFrank Mandarino default: 433b36d61d4SFrank Mandarino return -EINVAL; 434b36d61d4SFrank Mandarino } 435b36d61d4SFrank Mandarino 436b36d61d4SFrank Mandarino /* set iface */ 43717a52fd6SMark Brown snd_soc_write(codec, WM8731_IFACE, iface); 438b36d61d4SFrank Mandarino return 0; 439b36d61d4SFrank Mandarino } 440b36d61d4SFrank Mandarino 4410be9898aSMark Brown static int wm8731_set_bias_level(struct snd_soc_codec *codec, 4420be9898aSMark Brown enum snd_soc_bias_level level) 44340e0aa64SRichard Purdie { 44406ae9988SMark Brown struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); 4459bf311feSAxel Lin int ret; 44622d22ee5SMark Brown u16 reg; 44740e0aa64SRichard Purdie 4480be9898aSMark Brown switch (level) { 4490be9898aSMark Brown case SND_SOC_BIAS_ON: 45040e0aa64SRichard Purdie break; 4510be9898aSMark Brown case SND_SOC_BIAS_PREPARE: 45240e0aa64SRichard Purdie break; 4530be9898aSMark Brown case SND_SOC_BIAS_STANDBY: 454ce6120ccSLiam Girdwood if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { 45506ae9988SMark Brown ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), 45606ae9988SMark Brown wm8731->supplies); 45706ae9988SMark Brown if (ret != 0) 45806ae9988SMark Brown return ret; 45906ae9988SMark Brown 46005d448e2SMark Brown regcache_sync(wm8731->regmap); 46106ae9988SMark Brown } 46206ae9988SMark Brown 46322d22ee5SMark Brown /* Clear PWROFF, gate CLKOUT, everything else as-is */ 46417a52fd6SMark Brown reg = snd_soc_read(codec, WM8731_PWR) & 0xff7f; 46517a52fd6SMark Brown snd_soc_write(codec, WM8731_PWR, reg | 0x0040); 46640e0aa64SRichard Purdie break; 4670be9898aSMark Brown case SND_SOC_BIAS_OFF: 46817a52fd6SMark Brown snd_soc_write(codec, WM8731_PWR, 0xffff); 46906ae9988SMark Brown regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), 47006ae9988SMark Brown wm8731->supplies); 47105d448e2SMark Brown regcache_mark_dirty(wm8731->regmap); 47240e0aa64SRichard Purdie break; 47340e0aa64SRichard Purdie } 474ce6120ccSLiam Girdwood codec->dapm.bias_level = level; 47540e0aa64SRichard Purdie return 0; 47640e0aa64SRichard Purdie } 47740e0aa64SRichard Purdie 478e135443eSBill Gatliff #define WM8731_RATES SNDRV_PCM_RATE_8000_96000 479b36d61d4SFrank Mandarino 480b36d61d4SFrank Mandarino #define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 481b36d61d4SFrank Mandarino SNDRV_PCM_FMTBIT_S24_LE) 482b36d61d4SFrank Mandarino 48385e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm8731_dai_ops = { 4846335d055SEric Miao .hw_params = wm8731_hw_params, 4856335d055SEric Miao .digital_mute = wm8731_mute, 4866335d055SEric Miao .set_sysclk = wm8731_set_dai_sysclk, 4876335d055SEric Miao .set_fmt = wm8731_set_dai_fmt, 4886335d055SEric Miao }; 4896335d055SEric Miao 490f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8731_dai = { 491f0fba2adSLiam Girdwood .name = "wm8731-hifi", 49240e0aa64SRichard Purdie .playback = { 49340e0aa64SRichard Purdie .stream_name = "Playback", 49440e0aa64SRichard Purdie .channels_min = 1, 49540e0aa64SRichard Purdie .channels_max = 2, 496b36d61d4SFrank Mandarino .rates = WM8731_RATES, 497b36d61d4SFrank Mandarino .formats = WM8731_FORMATS,}, 49840e0aa64SRichard Purdie .capture = { 49940e0aa64SRichard Purdie .stream_name = "Capture", 50040e0aa64SRichard Purdie .channels_min = 1, 50140e0aa64SRichard Purdie .channels_max = 2, 502b36d61d4SFrank Mandarino .rates = WM8731_RATES, 503b36d61d4SFrank Mandarino .formats = WM8731_FORMATS,}, 5046335d055SEric Miao .ops = &wm8731_dai_ops, 5054934482dSMark Brown .symmetric_rates = 1, 50640e0aa64SRichard Purdie }; 50740e0aa64SRichard Purdie 508b3b50b3fSMark Brown #ifdef CONFIG_PM 50984b315eeSLars-Peter Clausen static int wm8731_suspend(struct snd_soc_codec *codec) 51040e0aa64SRichard Purdie { 5110be9898aSMark Brown wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); 51206ae9988SMark Brown 51340e0aa64SRichard Purdie return 0; 51440e0aa64SRichard Purdie } 51540e0aa64SRichard Purdie 516f0fba2adSLiam Girdwood static int wm8731_resume(struct snd_soc_codec *codec) 51740e0aa64SRichard Purdie { 5180be9898aSMark Brown wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 5197dea7c01SMark Brown 52040e0aa64SRichard Purdie return 0; 52140e0aa64SRichard Purdie } 522b3b50b3fSMark Brown #else 523b3b50b3fSMark Brown #define wm8731_suspend NULL 524b3b50b3fSMark Brown #define wm8731_resume NULL 525b3b50b3fSMark Brown #endif 52640e0aa64SRichard Purdie 527f0fba2adSLiam Girdwood static int wm8731_probe(struct snd_soc_codec *codec) 52840e0aa64SRichard Purdie { 529f0fba2adSLiam Girdwood struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); 530f0fba2adSLiam Girdwood int ret = 0, i; 53140e0aa64SRichard Purdie 53205d448e2SMark Brown codec->control_data = wm8731->regmap; 53305d448e2SMark Brown ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP); 53417a52fd6SMark Brown if (ret < 0) { 53517a52fd6SMark Brown dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); 536f0fba2adSLiam Girdwood return ret; 53717a52fd6SMark Brown } 53817a52fd6SMark Brown 5397dea7c01SMark Brown for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++) 5407dea7c01SMark Brown wm8731->supplies[i].supply = wm8731_supply_names[i]; 5417dea7c01SMark Brown 5427dea7c01SMark Brown ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies), 5437dea7c01SMark Brown wm8731->supplies); 5447dea7c01SMark Brown if (ret != 0) { 5457dea7c01SMark Brown dev_err(codec->dev, "Failed to request supplies: %d\n", ret); 546f0fba2adSLiam Girdwood return ret; 5477dea7c01SMark Brown } 5487dea7c01SMark Brown 5497dea7c01SMark Brown ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), 5507dea7c01SMark Brown wm8731->supplies); 5517dea7c01SMark Brown if (ret != 0) { 5527dea7c01SMark Brown dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); 5537dea7c01SMark Brown goto err_regulator_get; 5547dea7c01SMark Brown } 5557dea7c01SMark Brown 556519cf2dfSMark Brown ret = wm8731_reset(codec); 557519cf2dfSMark Brown if (ret < 0) { 558fe5422fcSMark Brown dev_err(codec->dev, "Failed to issue reset: %d\n", ret); 5597dea7c01SMark Brown goto err_regulator_enable; 560519cf2dfSMark Brown } 561519cf2dfSMark Brown 5625998102bSMark Brown wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 5635998102bSMark Brown 5645998102bSMark Brown /* Latch the update bits */ 56517a52fd6SMark Brown snd_soc_update_bits(codec, WM8731_LOUT1V, 0x100, 0); 56617a52fd6SMark Brown snd_soc_update_bits(codec, WM8731_ROUT1V, 0x100, 0); 56717a52fd6SMark Brown snd_soc_update_bits(codec, WM8731_LINVOL, 0x100, 0); 56817a52fd6SMark Brown snd_soc_update_bits(codec, WM8731_RINVOL, 0x100, 0); 5695998102bSMark Brown 570ce3bdaa8SMark Brown /* Disable bypass path by default */ 5712062ea52SDimitris Papastamos snd_soc_update_bits(codec, WM8731_APANA, 0x8, 0); 572ce3bdaa8SMark Brown 57306ae9988SMark Brown /* Regulators will have been enabled by bias management */ 57406ae9988SMark Brown regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); 57506ae9988SMark Brown 576a8035c8fSMark Brown return 0; 577fe5422fcSMark Brown 5787dea7c01SMark Brown err_regulator_enable: 5797dea7c01SMark Brown regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); 5807dea7c01SMark Brown err_regulator_get: 5817dea7c01SMark Brown regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); 582f0fba2adSLiam Girdwood 583fe5422fcSMark Brown return ret; 584a8035c8fSMark Brown } 585a8035c8fSMark Brown 586f0fba2adSLiam Girdwood /* power down chip */ 587f0fba2adSLiam Girdwood static int wm8731_remove(struct snd_soc_codec *codec) 5885998102bSMark Brown { 589f0fba2adSLiam Girdwood struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); 590f0fba2adSLiam Girdwood 591f0fba2adSLiam Girdwood wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); 592f0fba2adSLiam Girdwood 593f0fba2adSLiam Girdwood regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); 5947dea7c01SMark Brown regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); 595f0fba2adSLiam Girdwood 596f0fba2adSLiam Girdwood return 0; 5975998102bSMark Brown } 598a8035c8fSMark Brown 599f0fba2adSLiam Girdwood static struct snd_soc_codec_driver soc_codec_dev_wm8731 = { 600f0fba2adSLiam Girdwood .probe = wm8731_probe, 601f0fba2adSLiam Girdwood .remove = wm8731_remove, 602f0fba2adSLiam Girdwood .suspend = wm8731_suspend, 603f0fba2adSLiam Girdwood .resume = wm8731_resume, 604f0fba2adSLiam Girdwood .set_bias_level = wm8731_set_bias_level, 6055e251aecSMark Brown .dapm_widgets = wm8731_dapm_widgets, 6065e251aecSMark Brown .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets), 6075e251aecSMark Brown .dapm_routes = wm8731_intercon, 6085e251aecSMark Brown .num_dapm_routes = ARRAY_SIZE(wm8731_intercon), 609cb555318SMark Brown .controls = wm8731_snd_controls, 610cb555318SMark Brown .num_controls = ARRAY_SIZE(wm8731_snd_controls), 611f0fba2adSLiam Girdwood }; 612f0fba2adSLiam Girdwood 613a7f96e4dSMark Brown static const struct of_device_id wm8731_of_match[] = { 614a7f96e4dSMark Brown { .compatible = "wlf,wm8731", }, 615a7f96e4dSMark Brown { } 616a7f96e4dSMark Brown }; 617a7f96e4dSMark Brown 618a7f96e4dSMark Brown MODULE_DEVICE_TABLE(of, wm8731_of_match); 619a7f96e4dSMark Brown 62005d448e2SMark Brown static const struct regmap_config wm8731_regmap = { 62105d448e2SMark Brown .reg_bits = 7, 62205d448e2SMark Brown .val_bits = 9, 62305d448e2SMark Brown 62405d448e2SMark Brown .max_register = WM8731_RESET, 62505d448e2SMark Brown .volatile_reg = wm8731_volatile, 62605d448e2SMark Brown .writeable_reg = wm8731_writeable, 62705d448e2SMark Brown 62805d448e2SMark Brown .cache_type = REGCACHE_RBTREE, 62905d448e2SMark Brown .reg_defaults = wm8731_reg_defaults, 63005d448e2SMark Brown .num_reg_defaults = ARRAY_SIZE(wm8731_reg_defaults), 63105d448e2SMark Brown }; 63205d448e2SMark Brown 6335998102bSMark Brown #if defined(CONFIG_SPI_MASTER) 6345998102bSMark Brown static int __devinit wm8731_spi_probe(struct spi_device *spi) 6355998102bSMark Brown { 6365998102bSMark Brown struct wm8731_priv *wm8731; 637f0fba2adSLiam Girdwood int ret; 6385998102bSMark Brown 639f1992ddeSMark Brown wm8731 = devm_kzalloc(&spi->dev, sizeof(struct wm8731_priv), 640f1992ddeSMark Brown GFP_KERNEL); 6415998102bSMark Brown if (wm8731 == NULL) 6425998102bSMark Brown return -ENOMEM; 6435998102bSMark Brown 644f1992ddeSMark Brown wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap); 64505d448e2SMark Brown if (IS_ERR(wm8731->regmap)) { 64605d448e2SMark Brown ret = PTR_ERR(wm8731->regmap); 64705d448e2SMark Brown dev_err(&spi->dev, "Failed to allocate register map: %d\n", 64805d448e2SMark Brown ret); 649f1992ddeSMark Brown return ret; 65005d448e2SMark Brown } 65105d448e2SMark Brown 652f0fba2adSLiam Girdwood spi_set_drvdata(spi, wm8731); 6535998102bSMark Brown 654f0fba2adSLiam Girdwood ret = snd_soc_register_codec(&spi->dev, 655f0fba2adSLiam Girdwood &soc_codec_dev_wm8731, &wm8731_dai, 1); 65605d448e2SMark Brown if (ret != 0) { 65705d448e2SMark Brown dev_err(&spi->dev, "Failed to register CODEC: %d\n", ret); 658f1992ddeSMark Brown return ret; 65905d448e2SMark Brown } 66005d448e2SMark Brown 66105d448e2SMark Brown return 0; 6625998102bSMark Brown } 6635998102bSMark Brown 6645998102bSMark Brown static int __devexit wm8731_spi_remove(struct spi_device *spi) 6655998102bSMark Brown { 666f0fba2adSLiam Girdwood snd_soc_unregister_codec(&spi->dev); 6675998102bSMark Brown return 0; 6685998102bSMark Brown } 6695998102bSMark Brown 6705998102bSMark Brown static struct spi_driver wm8731_spi_driver = { 6715998102bSMark Brown .driver = { 67299b59f3cSMark Brown .name = "wm8731", 6735998102bSMark Brown .owner = THIS_MODULE, 674a7f96e4dSMark Brown .of_match_table = wm8731_of_match, 6755998102bSMark Brown }, 6765998102bSMark Brown .probe = wm8731_spi_probe, 6775998102bSMark Brown .remove = __devexit_p(wm8731_spi_remove), 6785998102bSMark Brown }; 679a8035c8fSMark Brown #endif /* CONFIG_SPI_MASTER */ 680a8035c8fSMark Brown 681a8035c8fSMark Brown #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 682c6f29811SMark Brown static __devinit int wm8731_i2c_probe(struct i2c_client *i2c, 683a8035c8fSMark Brown const struct i2c_device_id *id) 684a8035c8fSMark Brown { 6855998102bSMark Brown struct wm8731_priv *wm8731; 686f0fba2adSLiam Girdwood int ret; 687a8035c8fSMark Brown 688f1992ddeSMark Brown wm8731 = devm_kzalloc(&i2c->dev, sizeof(struct wm8731_priv), 689f1992ddeSMark Brown GFP_KERNEL); 6905998102bSMark Brown if (wm8731 == NULL) 6915998102bSMark Brown return -ENOMEM; 6925998102bSMark Brown 693f1992ddeSMark Brown wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap); 69405d448e2SMark Brown if (IS_ERR(wm8731->regmap)) { 69505d448e2SMark Brown ret = PTR_ERR(wm8731->regmap); 69605d448e2SMark Brown dev_err(&i2c->dev, "Failed to allocate register map: %d\n", 69705d448e2SMark Brown ret); 698f1992ddeSMark Brown return ret; 69905d448e2SMark Brown } 70005d448e2SMark Brown 7015998102bSMark Brown i2c_set_clientdata(i2c, wm8731); 702a8035c8fSMark Brown 703f0fba2adSLiam Girdwood ret = snd_soc_register_codec(&i2c->dev, 704f0fba2adSLiam Girdwood &soc_codec_dev_wm8731, &wm8731_dai, 1); 70505d448e2SMark Brown if (ret != 0) { 70605d448e2SMark Brown dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); 707f1992ddeSMark Brown return ret; 70805d448e2SMark Brown } 70905d448e2SMark Brown 71005d448e2SMark Brown return 0; 711a8035c8fSMark Brown } 712a8035c8fSMark Brown 713c6f29811SMark Brown static __devexit int wm8731_i2c_remove(struct i2c_client *client) 714a8035c8fSMark Brown { 715f0fba2adSLiam Girdwood snd_soc_unregister_codec(&client->dev); 716a8035c8fSMark Brown return 0; 717a8035c8fSMark Brown } 718a8035c8fSMark Brown 719a8035c8fSMark Brown static const struct i2c_device_id wm8731_i2c_id[] = { 720a8035c8fSMark Brown { "wm8731", 0 }, 721a8035c8fSMark Brown { } 722a8035c8fSMark Brown }; 723a8035c8fSMark Brown MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id); 724a8035c8fSMark Brown 725a8035c8fSMark Brown static struct i2c_driver wm8731_i2c_driver = { 726a8035c8fSMark Brown .driver = { 72799b59f3cSMark Brown .name = "wm8731", 728a8035c8fSMark Brown .owner = THIS_MODULE, 729a7f96e4dSMark Brown .of_match_table = wm8731_of_match, 730a8035c8fSMark Brown }, 731a8035c8fSMark Brown .probe = wm8731_i2c_probe, 732c6f29811SMark Brown .remove = __devexit_p(wm8731_i2c_remove), 733a8035c8fSMark Brown .id_table = wm8731_i2c_id, 734a8035c8fSMark Brown }; 735a8035c8fSMark Brown #endif 736a8035c8fSMark Brown 737c9b3a40fSTakashi Iwai static int __init wm8731_modinit(void) 73864089b84SMark Brown { 739f0fba2adSLiam Girdwood int ret = 0; 7405998102bSMark Brown #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 7415998102bSMark Brown ret = i2c_add_driver(&wm8731_i2c_driver); 7425998102bSMark Brown if (ret != 0) { 7435998102bSMark Brown printk(KERN_ERR "Failed to register WM8731 I2C driver: %d\n", 7445998102bSMark Brown ret); 7455998102bSMark Brown } 7465998102bSMark Brown #endif 7475998102bSMark Brown #if defined(CONFIG_SPI_MASTER) 7485998102bSMark Brown ret = spi_register_driver(&wm8731_spi_driver); 7495998102bSMark Brown if (ret != 0) { 7505998102bSMark Brown printk(KERN_ERR "Failed to register WM8731 SPI driver: %d\n", 7515998102bSMark Brown ret); 7525998102bSMark Brown } 7535998102bSMark Brown #endif 754f0fba2adSLiam Girdwood return ret; 75564089b84SMark Brown } 75664089b84SMark Brown module_init(wm8731_modinit); 75764089b84SMark Brown 75864089b84SMark Brown static void __exit wm8731_exit(void) 75964089b84SMark Brown { 7605998102bSMark Brown #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 7615998102bSMark Brown i2c_del_driver(&wm8731_i2c_driver); 7625998102bSMark Brown #endif 7635998102bSMark Brown #if defined(CONFIG_SPI_MASTER) 7645998102bSMark Brown spi_unregister_driver(&wm8731_spi_driver); 7655998102bSMark Brown #endif 76664089b84SMark Brown } 76764089b84SMark Brown module_exit(wm8731_exit); 76864089b84SMark Brown 76940e0aa64SRichard Purdie MODULE_DESCRIPTION("ASoC WM8731 driver"); 77040e0aa64SRichard Purdie MODULE_AUTHOR("Richard Purdie"); 77140e0aa64SRichard Purdie MODULE_LICENSE("GPL"); 772