/*
* 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>
*
struct wm8903_priv {
struct wm8903_platform_data *pdata;
+ struct device *dev;
struct snd_soc_codec *codec;
struct regmap *regmap;
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");
}
/*
* 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;
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);
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;
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;
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;
}
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;
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;
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 = {
.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;
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
{
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
struct wm8903_platform_data *pdata = wm8903->pdata;
- int ret, i;
+ int ret;
int trigger, irq_pol;
u16 val;
- bool mic_gpio = false;
wm8903->codec = codec;
codec->control_data = wm8903->regmap;
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;
ret = request_threaded_irq(wm8903->irq, NULL, wm8903_irq,
trigger | IRQF_ONESHOT,
- "wm8903", codec);
+ "wm8903", wm8903);
if (ret != 0) {
dev_err(codec->dev, "Failed to request IRQ: %d\n",
ret);
WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE,
WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE);
- wm8903_init_gpio(codec);
-
return ret;
}
{
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);
+ free_irq(wm8903->irq, wm8903);
return 0;
}
{
struct wm8903_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct wm8903_priv *wm8903;
+ bool mic_gpio = false;
unsigned int val;
- int ret;
+ 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",
}
}
+ 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);
/* 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;
+
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8903, &wm8903_dai, 1);
if (ret != 0)
return 0;
err:
- regmap_exit(wm8903->regmap);
return ret;
}
{
struct wm8903_priv *wm8903 = i2c_get_clientdata(client);
- regmap_exit(wm8903->regmap);
+ wm8903_free_gpio(wm8903);
snd_soc_unregister_codec(&client->dev);
return 0;