diff options
Diffstat (limited to 'sound/soc/codecs/wm8903.c')
| -rw-r--r-- | sound/soc/codecs/wm8903.c | 481 |
1 files changed, 233 insertions, 248 deletions
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index c91fb2f99c1..b84940c359a 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -1,8 +1,8 @@ /* * wm8903.c -- WM8903 ALSA SoC Audio driver * - * Copyright 2008 Wolfson Microelectronics - * Copyright 2011 NVIDIA, Inc. + * Copyright 2008-12 Wolfson Microelectronics + * Copyright 2011-2012 NVIDIA, Inc. * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * @@ -116,6 +116,7 @@ static const struct reg_default wm8903_reg_defaults[] = { struct wm8903_priv { struct wm8903_platform_data *pdata; + struct device *dev; struct snd_soc_codec *codec; struct regmap *regmap; @@ -363,9 +364,7 @@ static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm, static int wm8903_class_w_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); u16 reg; int ret; @@ -402,10 +401,8 @@ static int wm8903_class_w_put(struct snd_kcontrol *kcontrol, } #define SOC_DAPM_SINGLE_W(xname, reg, shift, max, invert) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_soc_info_volsw, \ - .get = snd_soc_dapm_get_volsw, .put = wm8903_class_w_put, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + SOC_SINGLE_EXT(xname, reg, shift, max, invert, \ + snd_soc_dapm_get_volsw, wm8903_class_w_put) static int wm8903_deemph[] = { 0, 32000, 44100, 48000 }; @@ -442,7 +439,7 @@ static int wm8903_set_deemph(struct snd_soc_codec *codec) static int wm8903_get_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); ucontrol->value.enumerated.item[0] = wm8903->deemph; @@ -453,7 +450,7 @@ static int wm8903_get_deemph(struct snd_kcontrol *kcontrol, static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); int deemph = ucontrol->value.enumerated.item[0]; int ret = 0; @@ -477,6 +474,8 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, /* ALSA can only do steps of .01dB */ static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); +static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0); + static const DECLARE_TLV_DB_SCALE(digital_sidetone_tlv, -3600, 300, 0); static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0); @@ -490,28 +489,28 @@ static const char *hpf_mode_text[] = { "Hi-fi", "Voice 1", "Voice 2", "Voice 3" }; -static const struct soc_enum hpf_mode = - SOC_ENUM_SINGLE(WM8903_ADC_DIGITAL_0, 5, 4, hpf_mode_text); +static SOC_ENUM_SINGLE_DECL(hpf_mode, + WM8903_ADC_DIGITAL_0, 5, hpf_mode_text); static const char *osr_text[] = { "Low power", "High performance" }; -static const struct soc_enum adc_osr = - SOC_ENUM_SINGLE(WM8903_ANALOGUE_ADC_0, 0, 2, osr_text); +static SOC_ENUM_SINGLE_DECL(adc_osr, + WM8903_ANALOGUE_ADC_0, 0, osr_text); -static const struct soc_enum dac_osr = - SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 0, 2, osr_text); +static SOC_ENUM_SINGLE_DECL(dac_osr, + WM8903_DAC_DIGITAL_1, 0, osr_text); static const char *drc_slope_text[] = { "1", "1/2", "1/4", "1/8", "1/16", "0" }; -static const struct soc_enum drc_slope_r0 = - SOC_ENUM_SINGLE(WM8903_DRC_2, 3, 6, drc_slope_text); +static SOC_ENUM_SINGLE_DECL(drc_slope_r0, + WM8903_DRC_2, 3, drc_slope_text); -static const struct soc_enum drc_slope_r1 = - SOC_ENUM_SINGLE(WM8903_DRC_2, 0, 6, drc_slope_text); +static SOC_ENUM_SINGLE_DECL(drc_slope_r1, + WM8903_DRC_2, 0, drc_slope_text); static const char *drc_attack_text[] = { "instantaneous", @@ -519,125 +518,125 @@ static const char *drc_attack_text[] = { "46.4ms", "92.8ms", "185.6ms" }; -static const struct soc_enum drc_attack = - SOC_ENUM_SINGLE(WM8903_DRC_1, 12, 11, drc_attack_text); +static SOC_ENUM_SINGLE_DECL(drc_attack, + WM8903_DRC_1, 12, drc_attack_text); static const char *drc_decay_text[] = { "186ms", "372ms", "743ms", "1.49s", "2.97s", "5.94s", "11.89s", "23.87s", "47.56s" }; -static const struct soc_enum drc_decay = - SOC_ENUM_SINGLE(WM8903_DRC_1, 8, 9, drc_decay_text); +static SOC_ENUM_SINGLE_DECL(drc_decay, + WM8903_DRC_1, 8, drc_decay_text); static const char *drc_ff_delay_text[] = { "5 samples", "9 samples" }; -static const struct soc_enum drc_ff_delay = - SOC_ENUM_SINGLE(WM8903_DRC_0, 5, 2, drc_ff_delay_text); +static SOC_ENUM_SINGLE_DECL(drc_ff_delay, + WM8903_DRC_0, 5, drc_ff_delay_text); static const char *drc_qr_decay_text[] = { "0.725ms", "1.45ms", "5.8ms" }; -static const struct soc_enum drc_qr_decay = - SOC_ENUM_SINGLE(WM8903_DRC_1, 4, 3, drc_qr_decay_text); +static SOC_ENUM_SINGLE_DECL(drc_qr_decay, + WM8903_DRC_1, 4, drc_qr_decay_text); static const char *drc_smoothing_text[] = { "Low", "Medium", "High" }; -static const struct soc_enum drc_smoothing = - SOC_ENUM_SINGLE(WM8903_DRC_0, 11, 3, drc_smoothing_text); +static SOC_ENUM_SINGLE_DECL(drc_smoothing, + WM8903_DRC_0, 11, drc_smoothing_text); static const char *soft_mute_text[] = { "Fast (fs/2)", "Slow (fs/32)" }; -static const struct soc_enum soft_mute = - SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 10, 2, soft_mute_text); +static SOC_ENUM_SINGLE_DECL(soft_mute, + WM8903_DAC_DIGITAL_1, 10, soft_mute_text); static const char *mute_mode_text[] = { "Hard", "Soft" }; -static const struct soc_enum mute_mode = - SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 9, 2, mute_mode_text); +static SOC_ENUM_SINGLE_DECL(mute_mode, + WM8903_DAC_DIGITAL_1, 9, mute_mode_text); static const char *companding_text[] = { "ulaw", "alaw" }; -static const struct soc_enum dac_companding = - SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 0, 2, companding_text); +static SOC_ENUM_SINGLE_DECL(dac_companding, + WM8903_AUDIO_INTERFACE_0, 0, companding_text); -static const struct soc_enum adc_companding = - SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 2, 2, companding_text); +static SOC_ENUM_SINGLE_DECL(adc_companding, + WM8903_AUDIO_INTERFACE_0, 2, companding_text); static const char *input_mode_text[] = { "Single-Ended", "Differential Line", "Differential Mic" }; -static const struct soc_enum linput_mode_enum = - SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 0, 3, input_mode_text); +static SOC_ENUM_SINGLE_DECL(linput_mode_enum, + WM8903_ANALOGUE_LEFT_INPUT_1, 0, input_mode_text); -static const struct soc_enum rinput_mode_enum = - SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 0, 3, input_mode_text); +static SOC_ENUM_SINGLE_DECL(rinput_mode_enum, + WM8903_ANALOGUE_RIGHT_INPUT_1, 0, input_mode_text); static const char *linput_mux_text[] = { "IN1L", "IN2L", "IN3L" }; -static const struct soc_enum linput_enum = - SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 2, 3, linput_mux_text); +static SOC_ENUM_SINGLE_DECL(linput_enum, + WM8903_ANALOGUE_LEFT_INPUT_1, 2, linput_mux_text); -static const struct soc_enum linput_inv_enum = - SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 4, 3, linput_mux_text); +static SOC_ENUM_SINGLE_DECL(linput_inv_enum, + WM8903_ANALOGUE_LEFT_INPUT_1, 4, linput_mux_text); static const char *rinput_mux_text[] = { "IN1R", "IN2R", "IN3R" }; -static const struct soc_enum rinput_enum = - SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 2, 3, rinput_mux_text); +static SOC_ENUM_SINGLE_DECL(rinput_enum, + WM8903_ANALOGUE_RIGHT_INPUT_1, 2, rinput_mux_text); -static const struct soc_enum rinput_inv_enum = - SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 4, 3, rinput_mux_text); +static SOC_ENUM_SINGLE_DECL(rinput_inv_enum, + WM8903_ANALOGUE_RIGHT_INPUT_1, 4, rinput_mux_text); static const char *sidetone_text[] = { "None", "Left", "Right" }; -static const struct soc_enum lsidetone_enum = - SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 2, 3, sidetone_text); +static SOC_ENUM_SINGLE_DECL(lsidetone_enum, + WM8903_DAC_DIGITAL_0, 2, sidetone_text); -static const struct soc_enum rsidetone_enum = - SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 0, 3, sidetone_text); +static SOC_ENUM_SINGLE_DECL(rsidetone_enum, + WM8903_DAC_DIGITAL_0, 0, sidetone_text); static const char *adcinput_text[] = { "ADC", "DMIC" }; -static const struct soc_enum adcinput_enum = - SOC_ENUM_SINGLE(WM8903_CLOCK_RATE_TEST_4, 9, 2, adcinput_text); +static SOC_ENUM_SINGLE_DECL(adcinput_enum, + WM8903_CLOCK_RATE_TEST_4, 9, adcinput_text); static const char *aif_text[] = { "Left", "Right" }; -static const struct soc_enum lcapture_enum = - SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 7, 2, aif_text); +static SOC_ENUM_SINGLE_DECL(lcapture_enum, + WM8903_AUDIO_INTERFACE_0, 7, aif_text); -static const struct soc_enum rcapture_enum = - SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 6, 2, aif_text); +static SOC_ENUM_SINGLE_DECL(rcapture_enum, + WM8903_AUDIO_INTERFACE_0, 6, aif_text); -static const struct soc_enum lplay_enum = - SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 5, 2, aif_text); +static SOC_ENUM_SINGLE_DECL(lplay_enum, + WM8903_AUDIO_INTERFACE_0, 5, aif_text); -static const struct soc_enum rplay_enum = - SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 4, 2, aif_text); +static SOC_ENUM_SINGLE_DECL(rplay_enum, + WM8903_AUDIO_INTERFACE_0, 4, aif_text); static const struct snd_kcontrol_new wm8903_snd_controls[] = { @@ -697,6 +696,8 @@ SOC_ENUM("DAC Mute Mode", mute_mode), SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0), SOC_ENUM("DAC Companding Mode", dac_companding), SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0), +SOC_SINGLE_TLV("DAC Boost Volume", WM8903_AUDIO_INTERFACE_0, 9, 3, 0, + dac_boost_tlv), SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0, wm8903_get_deemph, wm8903_put_deemph), @@ -1082,6 +1083,8 @@ static const struct snd_soc_dapm_route wm8903_intercon[] = { { "ROP", NULL, "Right Speaker PGA" }, { "RON", NULL, "Right Speaker PGA" }, + { "Charge Pump", NULL, "CLK_DSP" }, + { "Left Headphone Output PGA", NULL, "Charge Pump" }, { "Right Headphone Output PGA", NULL, "Charge Pump" }, { "Left Line Output PGA", NULL, "Charge Pump" }, @@ -1432,8 +1435,7 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec =rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); int fs = params_rate(params); int bclk; @@ -1636,17 +1638,27 @@ EXPORT_SYMBOL_GPL(wm8903_mic_detect); static irqreturn_t wm8903_irq(int irq, void *data) { - struct snd_soc_codec *codec = data; - struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - int mic_report; - int int_pol; - int int_val = 0; - int mask = ~snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1_MASK); + struct wm8903_priv *wm8903 = data; + int mic_report, ret; + unsigned int int_val, mask, int_pol; + + ret = regmap_read(wm8903->regmap, WM8903_INTERRUPT_STATUS_1_MASK, + &mask); + if (ret != 0) { + dev_err(wm8903->dev, "Failed to read IRQ mask: %d\n", ret); + return IRQ_NONE; + } - int_val = snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1) & mask; + ret = regmap_read(wm8903->regmap, WM8903_INTERRUPT_STATUS_1, &int_val); + if (ret != 0) { + dev_err(wm8903->dev, "Failed to read IRQ status: %d\n", ret); + return IRQ_NONE; + } + + int_val &= ~mask; if (int_val & WM8903_WSEQ_BUSY_EINT) { - dev_warn(codec->dev, "Write sequencer done\n"); + dev_warn(wm8903->dev, "Write sequencer done\n"); } /* @@ -1657,22 +1669,28 @@ static irqreturn_t wm8903_irq(int irq, void *data) * the polarity register. */ mic_report = wm8903->mic_last_report; - int_pol = snd_soc_read(codec, WM8903_INTERRUPT_POLARITY_1); + ret = regmap_read(wm8903->regmap, WM8903_INTERRUPT_POLARITY_1, + &int_pol); + if (ret != 0) { + dev_err(wm8903->dev, "Failed to read interrupt polarity: %d\n", + ret); + return IRQ_HANDLED; + } #ifndef CONFIG_SND_SOC_WM8903_MODULE if (int_val & (WM8903_MICSHRT_EINT | WM8903_MICDET_EINT)) - trace_snd_soc_jack_irq(dev_name(codec->dev)); + trace_snd_soc_jack_irq(dev_name(wm8903->dev)); #endif if (int_val & WM8903_MICSHRT_EINT) { - dev_dbg(codec->dev, "Microphone short (pol=%x)\n", int_pol); + dev_dbg(wm8903->dev, "Microphone short (pol=%x)\n", int_pol); mic_report ^= wm8903->mic_short; int_pol ^= WM8903_MICSHRT_INV; } if (int_val & WM8903_MICDET_EINT) { - dev_dbg(codec->dev, "Microphone detect (pol=%x)\n", int_pol); + dev_dbg(wm8903->dev, "Microphone detect (pol=%x)\n", int_pol); mic_report ^= wm8903->mic_det; int_pol ^= WM8903_MICDET_INV; @@ -1680,8 +1698,8 @@ static irqreturn_t wm8903_irq(int irq, void *data) msleep(wm8903->mic_delay); } - snd_soc_update_bits(codec, WM8903_INTERRUPT_POLARITY_1, - WM8903_MICSHRT_INV | WM8903_MICDET_INV, int_pol); + regmap_update_bits(wm8903->regmap, WM8903_INTERRUPT_POLARITY_1, + WM8903_MICSHRT_INV | WM8903_MICDET_INV, int_pol); snd_soc_jack_report(wm8903->mic_jack, mic_report, wm8903->mic_short | wm8903->mic_det); @@ -1775,7 +1793,6 @@ static int wm8903_gpio_request(struct gpio_chip *chip, unsigned offset) static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset) { struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); - struct snd_soc_codec *codec = wm8903->codec; unsigned int mask, val; int ret; @@ -1783,8 +1800,8 @@ static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset) val = (WM8903_GPn_FN_GPIO_INPUT << WM8903_GP1_FN_SHIFT) | WM8903_GP1_DIR; - ret = snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, - mask, val); + ret = regmap_update_bits(wm8903->regmap, + WM8903_GPIO_CONTROL_1 + offset, mask, val); if (ret < 0) return ret; @@ -1794,10 +1811,9 @@ static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset) static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset) { struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); - struct snd_soc_codec *codec = wm8903->codec; - int reg; + unsigned int reg; - reg = snd_soc_read(codec, WM8903_GPIO_CONTROL_1 + offset); + regmap_read(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset, ®); return (reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT; } @@ -1806,7 +1822,6 @@ static int wm8903_gpio_direction_out(struct gpio_chip *chip, unsigned offset, int value) { struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); - struct snd_soc_codec *codec = wm8903->codec; unsigned int mask, val; int ret; @@ -1814,8 +1829,8 @@ static int wm8903_gpio_direction_out(struct gpio_chip *chip, val = (WM8903_GPn_FN_GPIO_OUTPUT << WM8903_GP1_FN_SHIFT) | (value << WM8903_GP2_LVL_SHIFT); - ret = snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, - mask, val); + ret = regmap_update_bits(wm8903->regmap, + WM8903_GPIO_CONTROL_1 + offset, mask, val); if (ret < 0) return ret; @@ -1825,11 +1840,10 @@ static int wm8903_gpio_direction_out(struct gpio_chip *chip, static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); - struct snd_soc_codec *codec = wm8903->codec; - snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, - WM8903_GP1_LVL_MASK, - !!value << WM8903_GP1_LVL_SHIFT); + regmap_update_bits(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset, + WM8903_GP1_LVL_MASK, + !!value << WM8903_GP1_LVL_SHIFT); } static struct gpio_chip wm8903_template_chip = { @@ -1843,15 +1857,14 @@ static struct gpio_chip wm8903_template_chip = { .can_sleep = 1, }; -static void wm8903_init_gpio(struct snd_soc_codec *codec) +static void wm8903_init_gpio(struct wm8903_priv *wm8903) { - struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); struct wm8903_platform_data *pdata = wm8903->pdata; int ret; wm8903->gpio_chip = wm8903_template_chip; wm8903->gpio_chip.ngpio = WM8903_NUM_GPIO; - wm8903->gpio_chip.dev = codec->dev; + wm8903->gpio_chip.dev = wm8903->dev; if (pdata->gpio_base) wm8903->gpio_chip.base = pdata->gpio_base; @@ -1860,24 +1873,23 @@ static void wm8903_init_gpio(struct snd_soc_codec *codec) ret = gpiochip_add(&wm8903->gpio_chip); if (ret != 0) - dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret); + dev_err(wm8903->dev, "Failed to add GPIOs: %d\n", ret); } -static void wm8903_free_gpio(struct snd_soc_codec *codec) +static void wm8903_free_gpio(struct wm8903_priv *wm8903) { - struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); int ret; ret = gpiochip_remove(&wm8903->gpio_chip); if (ret != 0) - dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret); + dev_err(wm8903->dev, "Failed to remove GPIOs: %d\n", ret); } #else -static void wm8903_init_gpio(struct snd_soc_codec *codec) +static void wm8903_init_gpio(struct wm8903_priv *wm8903) { } -static void wm8903_free_gpio(struct snd_soc_codec *codec) +static void wm8903_free_gpio(struct wm8903_priv *wm8903) { } #endif @@ -1885,136 +1897,19 @@ static void wm8903_free_gpio(struct snd_soc_codec *codec) static int wm8903_probe(struct snd_soc_codec *codec) { struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - struct wm8903_platform_data *pdata = wm8903->pdata; - int ret, i; - int trigger, irq_pol; - u16 val; - bool mic_gpio = false; wm8903->codec = codec; - codec->control_data = wm8903->regmap; - - ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP); - if (ret != 0) { - dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); - return ret; - } - - /* Set up GPIOs, detect if any are MIC detect outputs */ - for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) { - if ((!pdata->gpio_cfg[i]) || - (pdata->gpio_cfg[i] > WM8903_GPIO_CONFIG_ZERO)) - continue; - - snd_soc_write(codec, WM8903_GPIO_CONTROL_1 + i, - pdata->gpio_cfg[i] & 0x7fff); - - val = (pdata->gpio_cfg[i] & WM8903_GP1_FN_MASK) - >> WM8903_GP1_FN_SHIFT; - - switch (val) { - case WM8903_GPn_FN_MICBIAS_CURRENT_DETECT: - case WM8903_GPn_FN_MICBIAS_SHORT_DETECT: - mic_gpio = true; - break; - default: - break; - } - } - - /* Set up microphone detection */ - snd_soc_write(codec, WM8903_MIC_BIAS_CONTROL_0, - pdata->micdet_cfg); - - /* Microphone detection needs the WSEQ clock */ - if (pdata->micdet_cfg) - snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0, - WM8903_WSEQ_ENA, WM8903_WSEQ_ENA); - - /* If microphone detection is enabled by pdata but - * detected via IRQ then interrupts can be lost before - * the machine driver has set up microphone detection - * IRQs as the IRQs are clear on read. The detection - * will be enabled when the machine driver configures. - */ - WARN_ON(!mic_gpio && (pdata->micdet_cfg & WM8903_MICDET_ENA)); - - wm8903->mic_delay = pdata->micdet_delay; - - if (wm8903->irq) { - if (pdata->irq_active_low) { - trigger = IRQF_TRIGGER_LOW; - irq_pol = WM8903_IRQ_POL; - } else { - trigger = IRQF_TRIGGER_HIGH; - irq_pol = 0; - } - - snd_soc_update_bits(codec, WM8903_INTERRUPT_CONTROL, - WM8903_IRQ_POL, irq_pol); - - ret = request_threaded_irq(wm8903->irq, NULL, wm8903_irq, - trigger | IRQF_ONESHOT, - "wm8903", codec); - if (ret != 0) { - dev_err(codec->dev, "Failed to request IRQ: %d\n", - ret); - return ret; - } - - /* Enable write sequencer interrupts */ - snd_soc_update_bits(codec, WM8903_INTERRUPT_STATUS_1_MASK, - WM8903_IM_WSEQ_BUSY_EINT, 0); - } /* power on device */ wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - /* Latch volume update bits */ - val = snd_soc_read(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT); - val |= WM8903_ADCVU; - snd_soc_write(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT, val); - snd_soc_write(codec, WM8903_ADC_DIGITAL_VOLUME_RIGHT, val); - - val = snd_soc_read(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT); - val |= WM8903_DACVU; - snd_soc_write(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT, val); - snd_soc_write(codec, WM8903_DAC_DIGITAL_VOLUME_RIGHT, val); - - val = snd_soc_read(codec, WM8903_ANALOGUE_OUT1_LEFT); - val |= WM8903_HPOUTVU; - snd_soc_write(codec, WM8903_ANALOGUE_OUT1_LEFT, val); - snd_soc_write(codec, WM8903_ANALOGUE_OUT1_RIGHT, val); - - val = snd_soc_read(codec, WM8903_ANALOGUE_OUT2_LEFT); - val |= WM8903_LINEOUTVU; - snd_soc_write(codec, WM8903_ANALOGUE_OUT2_LEFT, val); - snd_soc_write(codec, WM8903_ANALOGUE_OUT2_RIGHT, val); - - val = snd_soc_read(codec, WM8903_ANALOGUE_OUT3_LEFT); - val |= WM8903_SPKVU; - snd_soc_write(codec, WM8903_ANALOGUE_OUT3_LEFT, val); - snd_soc_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val); - - /* Enable DAC soft mute by default */ - snd_soc_update_bits(codec, WM8903_DAC_DIGITAL_1, - WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE, - WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE); - - wm8903_init_gpio(codec); - - return ret; + return 0; } /* power down chip */ static int wm8903_remove(struct snd_soc_codec *codec) { - struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - - wm8903_free_gpio(codec); wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); - if (wm8903->irq) - free_irq(wm8903->irq, codec); return 0; } @@ -2119,20 +2014,23 @@ static int wm8903_set_pdata_from_of(struct i2c_client *i2c, return 0; } -static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8903_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) { struct wm8903_platform_data *pdata = dev_get_platdata(&i2c->dev); struct wm8903_priv *wm8903; - unsigned int val; - int ret; + int trigger; + bool mic_gpio = false; + unsigned int val, irq_pol; + int ret, i; wm8903 = devm_kzalloc(&i2c->dev, sizeof(struct wm8903_priv), GFP_KERNEL); if (wm8903 == NULL) return -ENOMEM; + wm8903->dev = &i2c->dev; - wm8903->regmap = regmap_init_i2c(i2c, &wm8903_regmap); + wm8903->regmap = devm_regmap_init_i2c(i2c, &wm8903_regmap); if (IS_ERR(wm8903->regmap)) { ret = PTR_ERR(wm8903->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", @@ -2141,7 +2039,6 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, } i2c_set_clientdata(i2c, wm8903); - wm8903->irq = i2c->irq; /* If no platform data was supplied, create storage for defaults */ if (pdata) { @@ -2168,6 +2065,8 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, } } + pdata = wm8903->pdata; + ret = regmap_read(wm8903->regmap, WM8903_SW_RESET_AND_ID, &val); if (ret != 0) { dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret); @@ -2190,6 +2089,107 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, /* Reset the device */ regmap_write(wm8903->regmap, WM8903_SW_RESET_AND_ID, 0x8903); + wm8903_init_gpio(wm8903); + + /* Set up GPIO pin state, detect if any are MIC detect outputs */ + for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) { + if ((!pdata->gpio_cfg[i]) || + (pdata->gpio_cfg[i] > WM8903_GPIO_CONFIG_ZERO)) + continue; + + regmap_write(wm8903->regmap, WM8903_GPIO_CONTROL_1 + i, + pdata->gpio_cfg[i] & 0x7fff); + + val = (pdata->gpio_cfg[i] & WM8903_GP1_FN_MASK) + >> WM8903_GP1_FN_SHIFT; + + switch (val) { + case WM8903_GPn_FN_MICBIAS_CURRENT_DETECT: + case WM8903_GPn_FN_MICBIAS_SHORT_DETECT: + mic_gpio = true; + break; + default: + break; + } + } + + /* Set up microphone detection */ + regmap_write(wm8903->regmap, WM8903_MIC_BIAS_CONTROL_0, + pdata->micdet_cfg); + + /* Microphone detection needs the WSEQ clock */ + if (pdata->micdet_cfg) + regmap_update_bits(wm8903->regmap, WM8903_WRITE_SEQUENCER_0, + WM8903_WSEQ_ENA, WM8903_WSEQ_ENA); + + /* If microphone detection is enabled by pdata but + * detected via IRQ then interrupts can be lost before + * the machine driver has set up microphone detection + * IRQs as the IRQs are clear on read. The detection + * will be enabled when the machine driver configures. + */ + WARN_ON(!mic_gpio && (pdata->micdet_cfg & WM8903_MICDET_ENA)); + + wm8903->mic_delay = pdata->micdet_delay; + + if (i2c->irq) { + if (pdata->irq_active_low) { + trigger = IRQF_TRIGGER_LOW; + irq_pol = WM8903_IRQ_POL; + } else { + trigger = IRQF_TRIGGER_HIGH; + irq_pol = 0; + } + + regmap_update_bits(wm8903->regmap, WM8903_INTERRUPT_CONTROL, + WM8903_IRQ_POL, irq_pol); + + ret = request_threaded_irq(i2c->irq, NULL, wm8903_irq, + trigger | IRQF_ONESHOT, + "wm8903", wm8903); + if (ret != 0) { + dev_err(wm8903->dev, "Failed to request IRQ: %d\n", + ret); + return ret; + } + + /* Enable write sequencer interrupts */ + regmap_update_bits(wm8903->regmap, + WM8903_INTERRUPT_STATUS_1_MASK, + WM8903_IM_WSEQ_BUSY_EINT, 0); + } + + /* Latch volume update bits */ + regmap_update_bits(wm8903->regmap, WM8903_ADC_DIGITAL_VOLUME_LEFT, + WM8903_ADCVU, WM8903_ADCVU); + regmap_update_bits(wm8903->regmap, WM8903_ADC_DIGITAL_VOLUME_RIGHT, + WM8903_ADCVU, WM8903_ADCVU); + + regmap_update_bits(wm8903->regmap, WM8903_DAC_DIGITAL_VOLUME_LEFT, + WM8903_DACVU, WM8903_DACVU); + regmap_update_bits(wm8903->regmap, WM8903_DAC_DIGITAL_VOLUME_RIGHT, + WM8903_DACVU, WM8903_DACVU); + + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT1_LEFT, + WM8903_HPOUTVU, WM8903_HPOUTVU); + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT1_RIGHT, + WM8903_HPOUTVU, WM8903_HPOUTVU); + + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT2_LEFT, + WM8903_LINEOUTVU, WM8903_LINEOUTVU); + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT2_RIGHT, + WM8903_LINEOUTVU, WM8903_LINEOUTVU); + + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT3_LEFT, + WM8903_SPKVU, WM8903_SPKVU); + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT3_RIGHT, + WM8903_SPKVU, WM8903_SPKVU); + + /* Enable DAC soft mute by default */ + regmap_update_bits(wm8903->regmap, WM8903_DAC_DIGITAL_1, + WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE, + WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE); + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8903, &wm8903_dai, 1); if (ret != 0) @@ -2197,15 +2197,16 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, return 0; err: - regmap_exit(wm8903->regmap); return ret; } -static __devexit int wm8903_i2c_remove(struct i2c_client *client) +static int wm8903_i2c_remove(struct i2c_client *client) { struct wm8903_priv *wm8903 = i2c_get_clientdata(client); - regmap_exit(wm8903->regmap); + if (client->irq) + free_irq(client->irq, wm8903); + wm8903_free_gpio(wm8903); snd_soc_unregister_codec(&client->dev); return 0; @@ -2230,27 +2231,11 @@ static struct i2c_driver wm8903_i2c_driver = { .of_match_table = wm8903_of_match, }, .probe = wm8903_i2c_probe, - .remove = __devexit_p(wm8903_i2c_remove), + .remove = wm8903_i2c_remove, .id_table = wm8903_i2c_id, }; -static int __init wm8903_modinit(void) -{ - int ret = 0; - ret = i2c_add_driver(&wm8903_i2c_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register wm8903 I2C driver: %d\n", - ret); - } - return ret; -} -module_init(wm8903_modinit); - -static void __exit wm8903_exit(void) -{ - i2c_del_driver(&wm8903_i2c_driver); -} -module_exit(wm8903_exit); +module_i2c_driver(wm8903_i2c_driver); MODULE_DESCRIPTION("ASoC WM8903 driver"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.cm>"); |
