diff options
Diffstat (limited to 'sound/soc/codecs/wm8731.c')
| -rw-r--r-- | sound/soc/codecs/wm8731.c | 838 |
1 files changed, 448 insertions, 390 deletions
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 9c33fe87492..5ada6161132 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -2,6 +2,7 @@ * wm8731.c -- WM8731 ALSA SoC Audio driver * * Copyright 2005 Openedhand Ltd. + * Copyright 2006-12 Wolfson Microelectronics, plc * * Author: Richard Purdie <richard@openedhand.com> * @@ -18,150 +19,167 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> -#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/of_device.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> +#include <sound/tlv.h> #include "wm8731.h" -#define AUDIO_NAME "wm8731" -#define WM8731_VERSION "0.13" - -/* - * Debug - */ - -#define WM8731_DEBUG 0 - -#ifdef WM8731_DEBUG -#define dbg(format, arg...) \ - printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg) -#else -#define dbg(format, arg...) do {} while (0) -#endif -#define err(format, arg...) \ - printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg) -#define info(format, arg...) \ - printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg) -#define warn(format, arg...) \ - printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg) - -struct snd_soc_codec_device soc_codec_dev_wm8731; +#define WM8731_NUM_SUPPLIES 4 +static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { + "AVDD", + "HPVDD", + "DCVDD", + "DBVDD", +}; /* codec private data */ struct wm8731_priv { + struct regmap *regmap; + struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; + const struct snd_pcm_hw_constraint_list *constraints; unsigned int sysclk; + int sysclk_type; + int playback_fs; + bool deemph; }; + /* * wm8731 register cache - * We can't read the WM8731 register space when we are - * using 2 wire for device control, so we cache them instead. - * There is no point in caching the reset register */ -static const u16 wm8731_reg[WM8731_CACHEREGNUM] = { - 0x0097, 0x0097, 0x0079, 0x0079, - 0x000a, 0x0008, 0x009f, 0x000a, - 0x0000, 0x0000 +static const struct reg_default wm8731_reg_defaults[] = { + { 0, 0x0097 }, + { 1, 0x0097 }, + { 2, 0x0079 }, + { 3, 0x0079 }, + { 4, 0x000a }, + { 5, 0x0008 }, + { 6, 0x009f }, + { 7, 0x000a }, + { 8, 0x0000 }, + { 9, 0x0000 }, }; -/* - * read wm8731 register cache - */ -static inline unsigned int wm8731_read_reg_cache(struct snd_soc_codec *codec, - unsigned int reg) +static bool wm8731_volatile(struct device *dev, unsigned int reg) { - u16 *cache = codec->reg_cache; - if (reg == WM8731_RESET) - return 0; - if (reg >= WM8731_CACHEREGNUM) - return -1; - return cache[reg]; + return reg == WM8731_RESET; } -/* - * write wm8731 register cache - */ -static inline void wm8731_write_reg_cache(struct snd_soc_codec *codec, - u16 reg, unsigned int value) +static bool wm8731_writeable(struct device *dev, unsigned int reg) { - u16 *cache = codec->reg_cache; - if (reg >= WM8731_CACHEREGNUM) - return; - cache[reg] = value; + return reg <= WM8731_RESET; } -/* - * write to the WM8731 register space - */ -static int wm8731_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) +#define wm8731_reset(c) snd_soc_write(c, WM8731_RESET, 0) + +static const char *wm8731_input_select[] = {"Line In", "Mic"}; + +static SOC_ENUM_SINGLE_DECL(wm8731_insel_enum, + WM8731_APANA, 2, wm8731_input_select); + +static int wm8731_deemph[] = { 0, 32000, 44100, 48000 }; + +static int wm8731_set_deemph(struct snd_soc_codec *codec) { - u8 data[2]; + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); + int val, i, best; - /* data is - * D15..D9 WM8731 register offset - * D8...D0 register data + /* If we're using deemphasis select the nearest available sample + * rate. */ - data[0] = (reg << 1) | ((value >> 8) & 0x0001); - data[1] = value & 0x00ff; + if (wm8731->deemph) { + best = 1; + for (i = 2; i < ARRAY_SIZE(wm8731_deemph); i++) { + if (abs(wm8731_deemph[i] - wm8731->playback_fs) < + abs(wm8731_deemph[best] - wm8731->playback_fs)) + best = i; + } + + val = best << 1; + } else { + best = 0; + val = 0; + } - wm8731_write_reg_cache (codec, reg, value); - if (codec->hw_write(codec->control_data, data, 2) == 2) - return 0; - else - return -EIO; + dev_dbg(codec->dev, "Set deemphasis %d (%dHz)\n", + best, wm8731_deemph[best]); + + return snd_soc_update_bits(codec, WM8731_APDIGI, 0x6, val); } -#define wm8731_reset(c) wm8731_write(c, WM8731_RESET, 0) +static int wm8731_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); -static const char *wm8731_input_select[] = {"Line In", "Mic"}; -static const char *wm8731_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; + ucontrol->value.enumerated.item[0] = wm8731->deemph; -static const struct soc_enum wm8731_enum[] = { - SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select), - SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph), -}; + return 0; +} + +static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); + int deemph = ucontrol->value.enumerated.item[0]; + int ret = 0; + + if (deemph > 1) + return -EINVAL; + + mutex_lock(&codec->mutex); + if (wm8731->deemph != deemph) { + wm8731->deemph = deemph; + + wm8731_set_deemph(codec); + + ret = 1; + } + mutex_unlock(&codec->mutex); + + return ret; +} + +static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); +static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 2000, 0); static const struct snd_kcontrol_new wm8731_snd_controls[] = { -SOC_DOUBLE_R("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V, - 0, 127, 0), +SOC_DOUBLE_R_TLV("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V, + 0, 127, 0, out_tlv), SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V, 7, 1, 0), -SOC_DOUBLE_R("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0), +SOC_DOUBLE_R_TLV("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0, + in_tlv), SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1), -SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0), -SOC_SINGLE("Capture Mic Switch", WM8731_APANA, 1, 1, 1), +SOC_SINGLE_TLV("Mic Boost Volume", WM8731_APANA, 0, 1, 0, mic_tlv), +SOC_SINGLE("Mic Capture Switch", WM8731_APANA, 1, 1, 1), -SOC_SINGLE("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1), +SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1, + sidetone_tlv), SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1), SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0), -SOC_ENUM("Playback De-emphasis", wm8731_enum[1]), +SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0, + wm8731_get_deemph, wm8731_put_deemph), }; -/* add non dapm controls */ -static int wm8731_add_controls(struct snd_soc_codec *codec) -{ - int err, i; - - for (i = 0; i < ARRAY_SIZE(wm8731_snd_controls); i++) { - if ((err = snd_ctl_add(codec->card, - snd_soc_cnew(&wm8731_snd_controls[i],codec, NULL))) < 0) - return err; - } - - return 0; -} - /* Output Mixer */ static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = { SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0), @@ -171,9 +189,11 @@ SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0), /* Input mux */ static const struct snd_kcontrol_new wm8731_input_mux_controls = -SOC_DAPM_ENUM("Input Select", wm8731_enum[0]); +SOC_DAPM_ENUM("Input Select", wm8731_insel_enum); static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("ACTIVE",WM8731_ACTIVE, 0, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("OSC", WM8731_PWR, 5, 1, NULL, 0), SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, &wm8731_output_mixer_controls[0], ARRAY_SIZE(wm8731_output_mixer_controls)), @@ -191,7 +211,20 @@ SND_SOC_DAPM_INPUT("RLINEIN"), SND_SOC_DAPM_INPUT("LLINEIN"), }; -static const char *intercon[][3] = { +static int wm8731_check_osc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(source->codec); + + return wm8731->sysclk_type == WM8731_SYSCLK_XTAL; +} + +static const struct snd_soc_dapm_route wm8731_intercon[] = { + {"DAC", NULL, "OSC", wm8731_check_osc}, + {"ADC", NULL, "OSC", wm8731_check_osc}, + {"DAC", NULL, "ACTIVE"}, + {"ADC", NULL, "ACTIVE"}, + /* output mixer */ {"Output Mixer", "Line Bypass Switch", "Line Input"}, {"Output Mixer", "HiFi Playback Switch", "DAC"}, @@ -212,29 +245,8 @@ static const char *intercon[][3] = { {"Line Input", NULL, "LLINEIN"}, {"Line Input", NULL, "RLINEIN"}, {"Mic Bias", NULL, "MICIN"}, - - /* terminator */ - {NULL, NULL, NULL}, }; -static int wm8731_add_widgets(struct snd_soc_codec *codec) -{ - int i; - - for(i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) { - snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]); - } - - /* set up audio path interconnects */ - for(i = 0; intercon[i][0] != NULL; i++) { - snd_soc_dapm_connect_input(codec, intercon[i][0], - intercon[i][1], intercon[i][2]); - } - - snd_soc_dapm_new_widgets(codec); - return 0; -} - struct _coeff_div { u32 mclk; u32 rate; @@ -279,6 +291,36 @@ static const struct _coeff_div coeff_div[] = { {12000000, 88200, 136, 0xf, 0x1, 0x1}, }; +/* rates constraints */ +static const unsigned int wm8731_rates_12000000[] = { + 8000, 32000, 44100, 48000, 96000, 88200, +}; + +static const unsigned int wm8731_rates_12288000_18432000[] = { + 8000, 32000, 48000, 96000, +}; + +static const unsigned int wm8731_rates_11289600_16934400[] = { + 8000, 44100, 88200, +}; + +static const struct snd_pcm_hw_constraint_list wm8731_constraints_12000000 = { + .list = wm8731_rates_12000000, + .count = ARRAY_SIZE(wm8731_rates_12000000), +}; + +static const +struct snd_pcm_hw_constraint_list wm8731_constraints_12288000_18432000 = { + .list = wm8731_rates_12288000_18432000, + .count = ARRAY_SIZE(wm8731_rates_12288000_18432000), +}; + +static const +struct snd_pcm_hw_constraint_list wm8731_constraints_11289600_16934400 = { + .list = wm8731_rates_11289600_16934400, + .count = ARRAY_SIZE(wm8731_rates_11289600_16934400), +}; + static inline int get_coeff(int mclk, int rate) { int i; @@ -291,18 +333,19 @@ static inline int get_coeff(int mclk, int rate) } static int wm8731_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_device *socdev = rtd->socdev; - struct snd_soc_codec *codec = socdev->codec; - struct wm8731_priv *wm8731 = codec->private_data; - u16 iface = wm8731_read_reg_cache(codec, WM8731_IFACE) & 0xfff3; + struct snd_soc_codec *codec = dai->codec; + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); + u16 iface = snd_soc_read(codec, WM8731_IFACE) & 0xfff3; int i = get_coeff(wm8731->sysclk, params_rate(params)); u16 srate = (coeff_div[i].sr << 2) | (coeff_div[i].bosr << 1) | coeff_div[i].usb; - wm8731_write(codec, WM8731_SRATE, srate); + wm8731->playback_fs = params_rate(params); + + snd_soc_write(codec, WM8731_SRATE, srate); /* bit size */ switch (params_format(params)) { @@ -316,67 +359,67 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream, break; } - wm8731_write(codec, WM8731_IFACE, iface); - return 0; -} - -static int wm8731_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_device *socdev = rtd->socdev; - struct snd_soc_codec *codec = socdev->codec; - - /* set active */ - wm8731_write(codec, WM8731_ACTIVE, 0x0001); + wm8731_set_deemph(codec); + snd_soc_write(codec, WM8731_IFACE, iface); return 0; } -static void wm8731_shutdown(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_device *socdev = rtd->socdev; - struct snd_soc_codec *codec = socdev->codec; - - /* deactivate */ - if (!codec->active) { - udelay(50); - wm8731_write(codec, WM8731_ACTIVE, 0x0); - } -} - -static int wm8731_mute(struct snd_soc_codec_dai *dai, int mute) +static int wm8731_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; - u16 mute_reg = wm8731_read_reg_cache(codec, WM8731_APDIGI) & 0xfff7; + u16 mute_reg = snd_soc_read(codec, WM8731_APDIGI) & 0xfff7; if (mute) - wm8731_write(codec, WM8731_APDIGI, mute_reg | 0x8); + snd_soc_write(codec, WM8731_APDIGI, mute_reg | 0x8); else - wm8731_write(codec, WM8731_APDIGI, mute_reg); + snd_soc_write(codec, WM8731_APDIGI, mute_reg); return 0; } -static int wm8731_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai, +static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = codec_dai->codec; - struct wm8731_priv *wm8731 = codec->private_data; + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); + + switch (clk_id) { + case WM8731_SYSCLK_XTAL: + case WM8731_SYSCLK_MCLK: + wm8731->sysclk_type = clk_id; + break; + default: + return -EINVAL; + } switch (freq) { - case 11289600: + case 0: + wm8731->constraints = NULL; + break; case 12000000: + wm8731->constraints = &wm8731_constraints_12000000; + break; case 12288000: - case 16934400: case 18432000: - wm8731->sysclk = freq; - return 0; + wm8731->constraints = &wm8731_constraints_12288000_18432000; + break; + case 16934400: + case 11289600: + wm8731->constraints = &wm8731_constraints_11289600_16934400; + break; + default: + return -EINVAL; } - return -EINVAL; + + wm8731->sysclk = freq; + + snd_soc_dapm_sync(&codec->dapm); + + return 0; } -static int wm8731_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, +static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; @@ -404,10 +447,10 @@ static int wm8731_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, iface |= 0x0001; break; case SND_SOC_DAIFMT_DSP_A: - iface |= 0x0003; + iface |= 0x0013; break; case SND_SOC_DAIFMT_DSP_B: - iface |= 0x0013; + iface |= 0x0003; break; default: return -EINVAL; @@ -431,47 +474,75 @@ static int wm8731_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, } /* set iface */ - wm8731_write(codec, WM8731_IFACE, iface); + snd_soc_write(codec, WM8731_IFACE, iface); return 0; } -static int wm8731_dapm_event(struct snd_soc_codec *codec, int event) +static int wm8731_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) { - u16 reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f; + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); + int ret; + u16 reg; - switch (event) { - case SNDRV_CTL_POWER_D0: /* full On */ - /* vref/mid, osc on, dac unmute */ - wm8731_write(codec, WM8731_PWR, reg); + switch (level) { + case SND_SOC_BIAS_ON: break; - case SNDRV_CTL_POWER_D1: /* partial On */ - case SNDRV_CTL_POWER_D2: /* partial On */ + case SND_SOC_BIAS_PREPARE: break; - case SNDRV_CTL_POWER_D3hot: /* Off, with power */ - /* everything off except vref/vmid, */ - wm8731_write(codec, WM8731_PWR, reg | 0x0040); + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); + if (ret != 0) + return ret; + + regcache_sync(wm8731->regmap); + } + + /* Clear PWROFF, gate CLKOUT, everything else as-is */ + reg = snd_soc_read(codec, WM8731_PWR) & 0xff7f; + snd_soc_write(codec, WM8731_PWR, reg | 0x0040); break; - case SNDRV_CTL_POWER_D3cold: /* Off, without power */ - /* everything off, dac mute, inactive */ - wm8731_write(codec, WM8731_ACTIVE, 0x0); - wm8731_write(codec, WM8731_PWR, 0xffff); + case SND_SOC_BIAS_OFF: + snd_soc_write(codec, WM8731_PWR, 0xffff); + regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); + regcache_mark_dirty(wm8731->regmap); break; } - codec->dapm_state = event; + codec->dapm.bias_level = level; + return 0; +} + +static int wm8731_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(dai->codec); + + if (wm8731->constraints) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + wm8731->constraints); + return 0; } -#define WM8731_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ - SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ - SNDRV_PCM_RATE_96000) +#define WM8731_RATES SNDRV_PCM_RATE_8000_96000 #define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE) -struct snd_soc_codec_dai wm8731_dai = { - .name = "WM8731", +static const struct snd_soc_dai_ops wm8731_dai_ops = { + .startup = wm8731_startup, + .hw_params = wm8731_hw_params, + .digital_mute = wm8731_mute, + .set_sysclk = wm8731_set_dai_sysclk, + .set_fmt = wm8731_set_dai_fmt, +}; + +static struct snd_soc_dai_driver wm8731_dai = { + .name = "wm8731-hifi", .playback = { .stream_name = "Playback", .channels_min = 1, @@ -484,273 +555,260 @@ struct snd_soc_codec_dai wm8731_dai = { .channels_max = 2, .rates = WM8731_RATES, .formats = WM8731_FORMATS,}, - .ops = { - .prepare = wm8731_pcm_prepare, - .hw_params = wm8731_hw_params, - .shutdown = wm8731_shutdown, - }, - .dai_ops = { - .digital_mute = wm8731_mute, - .set_sysclk = wm8731_set_dai_sysclk, - .set_fmt = wm8731_set_dai_fmt, - } + .ops = &wm8731_dai_ops, + .symmetric_rates = 1, }; -EXPORT_SYMBOL_GPL(wm8731_dai); -static int wm8731_suspend(struct platform_device *pdev, pm_message_t state) +#ifdef CONFIG_PM +static int wm8731_suspend(struct snd_soc_codec *codec) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec = socdev->codec; + wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); - wm8731_write(codec, WM8731_ACTIVE, 0x0); - wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold); return 0; } -static int wm8731_resume(struct platform_device *pdev) +static int wm8731_resume(struct snd_soc_codec *codec) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec = socdev->codec; - int i; - u8 data[2]; - u16 *cache = codec->reg_cache; - - /* Sync reg_cache with the hardware */ - for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) { - data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); - data[1] = cache[i] & 0x00ff; - codec->hw_write(codec->control_data, data, 2); - } - wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot); - wm8731_dapm_event(codec, codec->suspend_dapm_state); + wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; } +#else +#define wm8731_suspend NULL +#define wm8731_resume NULL +#endif -/* - * initialise the WM8731 driver - * register the mixer and dsp interfaces with the kernel - */ -static int wm8731_init(struct snd_soc_device *socdev) +static int wm8731_probe(struct snd_soc_codec *codec) { - struct snd_soc_codec *codec = socdev->codec; - int reg, ret = 0; - - codec->name = "WM8731"; - codec->owner = THIS_MODULE; - codec->read = wm8731_read_reg_cache; - codec->write = wm8731_write; - codec->dapm_event = wm8731_dapm_event; - codec->dai = &wm8731_dai; - codec->num_dai = 1; - codec->reg_cache_size = sizeof(wm8731_reg); - codec->reg_cache = kmemdup(wm8731_reg, sizeof(wm8731_reg), GFP_KERNEL); - if (codec->reg_cache == NULL) - return -ENOMEM; + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); + int ret = 0, i; - wm8731_reset(codec); + for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++) + wm8731->supplies[i].supply = wm8731_supply_names[i]; - /* register pcms */ - ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); - if (ret < 0) { - printk(KERN_ERR "wm8731: failed to create pcms\n"); - goto pcm_err; + ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); + return ret; } - /* power on device */ - wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot); - - /* set the update bits */ - reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V); - wm8731_write(codec, WM8731_LOUT1V, reg & ~0x0100); - reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V); - wm8731_write(codec, WM8731_ROUT1V, reg & ~0x0100); - reg = wm8731_read_reg_cache(codec, WM8731_LINVOL); - wm8731_write(codec, WM8731_LINVOL, reg & ~0x0100); - reg = wm8731_read_reg_cache(codec, WM8731_RINVOL); - wm8731_write(codec, WM8731_RINVOL, reg & ~0x0100); - - wm8731_add_controls(codec); - wm8731_add_widgets(codec); - ret = snd_soc_register_card(socdev); + ret = wm8731_reset(codec); if (ret < 0) { - printk(KERN_ERR "wm8731: failed to register card\n"); - goto card_err; + dev_err(codec->dev, "Failed to issue reset: %d\n", ret); + goto err_regulator_enable; } - return ret; + wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + /* Latch the update bits */ + snd_soc_update_bits(codec, WM8731_LOUT1V, 0x100, 0); + snd_soc_update_bits(codec, WM8731_ROUT1V, 0x100, 0); + snd_soc_update_bits(codec, WM8731_LINVOL, 0x100, 0); + snd_soc_update_bits(codec, WM8731_RINVOL, 0x100, 0); + + /* Disable bypass path by default */ + snd_soc_update_bits(codec, WM8731_APANA, 0x8, 0); + + /* Regulators will have been enabled by bias management */ + regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); + + return 0; + +err_regulator_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); -pcm_err: - kfree(codec->reg_cache); return ret; } -static struct snd_soc_device *wm8731_socdev; +/* power down chip */ +static int wm8731_remove(struct snd_soc_codec *codec) +{ + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); + + wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); -#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) + regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); -/* - * WM8731 2 wire address is determined by GPIO5 - * state during powerup. - * low = 0x1a - * high = 0x1b - */ -static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; + return 0; +} -/* Magic definition of all other variables and things */ -I2C_CLIENT_INSMOD; +static struct snd_soc_codec_driver soc_codec_dev_wm8731 = { + .probe = wm8731_probe, + .remove = wm8731_remove, + .suspend = wm8731_suspend, + .resume = wm8731_resume, + .set_bias_level = wm8731_set_bias_level, + .dapm_widgets = wm8731_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets), + .dapm_routes = wm8731_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8731_intercon), + .controls = wm8731_snd_controls, + .num_controls = ARRAY_SIZE(wm8731_snd_controls), +}; -static struct i2c_driver wm8731_i2c_driver; -static struct i2c_client client_template; +static const struct of_device_id wm8731_of_match[] = { + { .compatible = "wlf,wm8731", }, + { } +}; -/* If the i2c layer weren't so broken, we could pass this kind of data - around */ +MODULE_DEVICE_TABLE(of, wm8731_of_match); -static int wm8731_codec_probe(struct i2c_adapter *adap, int addr, int kind) -{ - struct snd_soc_device *socdev = wm8731_socdev; - struct wm8731_setup_data *setup = socdev->codec_data; - struct snd_soc_codec *codec = socdev->codec; - struct i2c_client *i2c; - int ret; +static const struct regmap_config wm8731_regmap = { + .reg_bits = 7, + .val_bits = 9, - if (addr != setup->i2c_address) - return -ENODEV; + .max_register = WM8731_RESET, + .volatile_reg = wm8731_volatile, + .writeable_reg = wm8731_writeable, - client_template.adapter = adap; - client_template.addr = addr; + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wm8731_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8731_reg_defaults), +}; - i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); - if (i2c == NULL) { - kfree(codec); +#if defined(CONFIG_SPI_MASTER) +static int wm8731_spi_probe(struct spi_device *spi) +{ + struct wm8731_priv *wm8731; + int ret; + + wm8731 = devm_kzalloc(&spi->dev, sizeof(struct wm8731_priv), + GFP_KERNEL); + if (wm8731 == NULL) return -ENOMEM; - } - i2c_set_clientdata(i2c, codec); - codec->control_data = i2c; - ret = i2c_attach_client(i2c); - if (ret < 0) { - err("failed to attach codec at addr %x\n", addr); - goto err; + wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap); + if (IS_ERR(wm8731->regmap)) { + ret = PTR_ERR(wm8731->regmap); + dev_err(&spi->dev, "Failed to allocate register map: %d\n", + ret); + return ret; } - ret = wm8731_init(socdev); - if (ret < 0) { - err("failed to initialise WM8731\n"); - goto err; + spi_set_drvdata(spi, wm8731); + + ret = snd_soc_register_codec(&spi->dev, + &soc_codec_dev_wm8731, &wm8731_dai, 1); + if (ret != 0) { + dev_err(&spi->dev, "Failed to register CODEC: %d\n", ret); + return ret; } - return ret; -err: - kfree(codec); - kfree(i2c); - return ret; + return 0; } -static int wm8731_i2c_detach(struct i2c_client *client) +static int wm8731_spi_remove(struct spi_device *spi) { - struct snd_soc_codec* codec = i2c_get_clientdata(client); - i2c_detach_client(client); - kfree(codec->reg_cache); - kfree(client); + snd_soc_unregister_codec(&spi->dev); return 0; } -static int wm8731_i2c_attach(struct i2c_adapter *adap) +static struct spi_driver wm8731_spi_driver = { + .driver = { + .name = "wm8731", + .owner = THIS_MODULE, + .of_match_table = wm8731_of_match, + }, + .probe = wm8731_spi_probe, + .remove = wm8731_spi_remove, +}; +#endif /* CONFIG_SPI_MASTER */ + +#if IS_ENABLED(CONFIG_I2C) +static int wm8731_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) { - return i2c_probe(adap, &addr_data, wm8731_codec_probe); + struct wm8731_priv *wm8731; + int ret; + + wm8731 = devm_kzalloc(&i2c->dev, sizeof(struct wm8731_priv), + GFP_KERNEL); + if (wm8731 == NULL) + return -ENOMEM; + + wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap); + if (IS_ERR(wm8731->regmap)) { + ret = PTR_ERR(wm8731->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + i2c_set_clientdata(i2c, wm8731); + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_wm8731, &wm8731_dai, 1); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); + return ret; + } + + return 0; } -/* corgi i2c codec control layer */ +static int wm8731_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id wm8731_i2c_id[] = { + { "wm8731", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id); + static struct i2c_driver wm8731_i2c_driver = { .driver = { - .name = "WM8731 I2C Codec", + .name = "wm8731", .owner = THIS_MODULE, + .of_match_table = wm8731_of_match, }, - .id = I2C_DRIVERID_WM8731, - .attach_adapter = wm8731_i2c_attach, - .detach_client = wm8731_i2c_detach, - .command = NULL, -}; - -static struct i2c_client client_template = { - .name = "WM8731", - .driver = &wm8731_i2c_driver, + .probe = wm8731_i2c_probe, + .remove = wm8731_i2c_remove, + .id_table = wm8731_i2c_id, }; #endif -static int wm8731_probe(struct platform_device *pdev) +static int __init wm8731_modinit(void) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct wm8731_setup_data *setup; - struct snd_soc_codec *codec; - struct wm8731_priv *wm8731; int ret = 0; - - info("WM8731 Audio Codec %s", WM8731_VERSION); - - setup = socdev->codec_data; - codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); - if (codec == NULL) - return -ENOMEM; - - wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL); - if (wm8731 == NULL) { - kfree(codec); - return -ENOMEM; +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&wm8731_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8731 I2C driver: %d\n", + ret); } - - codec->private_data = wm8731; - socdev->codec = codec; - mutex_init(&codec->mutex); - INIT_LIST_HEAD(&codec->dapm_widgets); - INIT_LIST_HEAD(&codec->dapm_paths); - - wm8731_socdev = socdev; -#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) - if (setup->i2c_address) { - normal_i2c[0] = setup->i2c_address; - codec->hw_write = (hw_write_t)i2c_master_send; - ret = i2c_add_driver(&wm8731_i2c_driver); - if (ret != 0) - printk(KERN_ERR "can't add i2c driver"); +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8731_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8731 SPI driver: %d\n", + ret); } -#else - /* Add other interfaces here */ #endif return ret; } +module_init(wm8731_modinit); -/* power down chip */ -static int wm8731_remove(struct platform_device *pdev) +static void __exit wm8731_exit(void) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec = socdev->codec; - - if (codec->control_data) - wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold); - - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); -#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8731_i2c_driver); #endif - kfree(codec->private_data); - kfree(codec); - - return 0; +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8731_spi_driver); +#endif } - -struct snd_soc_codec_device soc_codec_dev_wm8731 = { - .probe = wm8731_probe, - .remove = wm8731_remove, - .suspend = wm8731_suspend, - .resume = wm8731_resume, -}; - -EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731); +module_exit(wm8731_exit); MODULE_DESCRIPTION("ASoC WM8731 driver"); MODULE_AUTHOR("Richard Purdie"); |
