Skip to content

Commit

Permalink
ASoC: Make WM5100 interrupt path use regmap directly
Browse files Browse the repository at this point in the history
This will allow us to move the interrupt allocation out of the ASoC part
of the driver and simplifies the locking by removing any reliance in the
bulk of the interrupt path on the big CODEC lock.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
  • Loading branch information
Mark Brown committed Jan 20, 2012
1 parent 218240e commit 46c1a87
Showing 1 changed file with 108 additions and 80 deletions.
188 changes: 108 additions & 80 deletions sound/soc/codecs/wm5100.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ struct wm5100_fll {

/* codec private data */
struct wm5100_priv {
struct device *dev;
struct regmap *regmap;
struct snd_soc_codec *codec;

Expand Down Expand Up @@ -855,65 +856,66 @@ static int wm5100_dbvdd_ev(struct snd_soc_dapm_widget *w,
}
}

static void wm5100_log_status3(struct snd_soc_codec *codec, int val)
static void wm5100_log_status3(struct wm5100_priv *wm5100, int val)
{
if (val & WM5100_SPK_SHUTDOWN_WARN_EINT)
dev_crit(codec->dev, "Speaker shutdown warning\n");
dev_crit(wm5100->dev, "Speaker shutdown warning\n");
if (val & WM5100_SPK_SHUTDOWN_EINT)
dev_crit(codec->dev, "Speaker shutdown\n");
dev_crit(wm5100->dev, "Speaker shutdown\n");
if (val & WM5100_CLKGEN_ERR_EINT)
dev_crit(codec->dev, "SYSCLK underclocked\n");
dev_crit(wm5100->dev, "SYSCLK underclocked\n");
if (val & WM5100_CLKGEN_ERR_ASYNC_EINT)
dev_crit(codec->dev, "ASYNCCLK underclocked\n");
dev_crit(wm5100->dev, "ASYNCCLK underclocked\n");
}

static void wm5100_log_status4(struct snd_soc_codec *codec, int val)
static void wm5100_log_status4(struct wm5100_priv *wm5100, int val)
{
if (val & WM5100_AIF3_ERR_EINT)
dev_err(codec->dev, "AIF3 configuration error\n");
dev_err(wm5100->dev, "AIF3 configuration error\n");
if (val & WM5100_AIF2_ERR_EINT)
dev_err(codec->dev, "AIF2 configuration error\n");
dev_err(wm5100->dev, "AIF2 configuration error\n");
if (val & WM5100_AIF1_ERR_EINT)
dev_err(codec->dev, "AIF1 configuration error\n");
dev_err(wm5100->dev, "AIF1 configuration error\n");
if (val & WM5100_CTRLIF_ERR_EINT)
dev_err(codec->dev, "Control interface error\n");
dev_err(wm5100->dev, "Control interface error\n");
if (val & WM5100_ISRC2_UNDERCLOCKED_EINT)
dev_err(codec->dev, "ISRC2 underclocked\n");
dev_err(wm5100->dev, "ISRC2 underclocked\n");
if (val & WM5100_ISRC1_UNDERCLOCKED_EINT)
dev_err(codec->dev, "ISRC1 underclocked\n");
dev_err(wm5100->dev, "ISRC1 underclocked\n");
if (val & WM5100_FX_UNDERCLOCKED_EINT)
dev_err(codec->dev, "FX underclocked\n");
dev_err(wm5100->dev, "FX underclocked\n");
if (val & WM5100_AIF3_UNDERCLOCKED_EINT)
dev_err(codec->dev, "AIF3 underclocked\n");
dev_err(wm5100->dev, "AIF3 underclocked\n");
if (val & WM5100_AIF2_UNDERCLOCKED_EINT)
dev_err(codec->dev, "AIF2 underclocked\n");
dev_err(wm5100->dev, "AIF2 underclocked\n");
if (val & WM5100_AIF1_UNDERCLOCKED_EINT)
dev_err(codec->dev, "AIF1 underclocked\n");
dev_err(wm5100->dev, "AIF1 underclocked\n");
if (val & WM5100_ASRC_UNDERCLOCKED_EINT)
dev_err(codec->dev, "ASRC underclocked\n");
dev_err(wm5100->dev, "ASRC underclocked\n");
if (val & WM5100_DAC_UNDERCLOCKED_EINT)
dev_err(codec->dev, "DAC underclocked\n");
dev_err(wm5100->dev, "DAC underclocked\n");
if (val & WM5100_ADC_UNDERCLOCKED_EINT)
dev_err(codec->dev, "ADC underclocked\n");
dev_err(wm5100->dev, "ADC underclocked\n");
if (val & WM5100_MIXER_UNDERCLOCKED_EINT)
dev_err(codec->dev, "Mixer underclocked\n");
dev_err(wm5100->dev, "Mixer underclocked\n");
}

static int wm5100_post_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
struct snd_soc_codec *codec = w->codec;
struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
int ret;

ret = snd_soc_read(codec, WM5100_INTERRUPT_RAW_STATUS_3);
ret &= WM5100_SPK_SHUTDOWN_WARN_STS |
WM5100_SPK_SHUTDOWN_STS | WM5100_CLKGEN_ERR_STS |
WM5100_CLKGEN_ERR_ASYNC_STS;
wm5100_log_status3(codec, ret);
wm5100_log_status3(wm5100, ret);

ret = snd_soc_read(codec, WM5100_INTERRUPT_RAW_STATUS_4);
wm5100_log_status4(codec, ret);
wm5100_log_status4(wm5100, ret);

return 0;
}
Expand Down Expand Up @@ -2123,55 +2125,59 @@ static int wm5100_dig_vu[] = {
WM5100_DAC_DIGITAL_VOLUME_6R,
};

static void wm5100_set_detect_mode(struct snd_soc_codec *codec, int the_mode)
static void wm5100_set_detect_mode(struct wm5100_priv *wm5100, int the_mode)
{
struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
struct wm5100_jack_mode *mode = &wm5100->pdata.jack_modes[the_mode];

BUG_ON(the_mode >= ARRAY_SIZE(wm5100->pdata.jack_modes));

gpio_set_value_cansleep(wm5100->pdata.hp_pol, mode->hp_pol);
snd_soc_update_bits(codec, WM5100_ACCESSORY_DETECT_MODE_1,
WM5100_ACCDET_BIAS_SRC_MASK |
WM5100_ACCDET_SRC,
(mode->bias << WM5100_ACCDET_BIAS_SRC_SHIFT) |
mode->micd_src << WM5100_ACCDET_SRC_SHIFT);
snd_soc_update_bits(codec, WM5100_MISC_CONTROL,
WM5100_HPCOM_SRC,
mode->micd_src << WM5100_HPCOM_SRC_SHIFT);
regmap_update_bits(wm5100->regmap, WM5100_ACCESSORY_DETECT_MODE_1,
WM5100_ACCDET_BIAS_SRC_MASK |
WM5100_ACCDET_SRC,
(mode->bias << WM5100_ACCDET_BIAS_SRC_SHIFT) |
mode->micd_src << WM5100_ACCDET_SRC_SHIFT);
regmap_update_bits(wm5100->regmap, WM5100_MISC_CONTROL,
WM5100_HPCOM_SRC,
mode->micd_src << WM5100_HPCOM_SRC_SHIFT);

wm5100->jack_mode = the_mode;

dev_dbg(codec->dev, "Set microphone polarity to %d\n",
dev_dbg(wm5100->dev, "Set microphone polarity to %d\n",
wm5100->jack_mode);
}

static void wm5100_micd_irq(struct snd_soc_codec *codec)
static void wm5100_micd_irq(struct wm5100_priv *wm5100)
{
struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
int val;
unsigned int val;
int ret;

val = snd_soc_read(codec, WM5100_MIC_DETECT_3);
ret = regmap_read(wm5100->regmap, WM5100_MIC_DETECT_3, &val);
if (ret != 0) {
dev_err(wm5100->dev, "Failed to read micropone status: %d\n",
ret);
return;
}

dev_dbg(codec->dev, "Microphone event: %x\n", val);
dev_dbg(wm5100->dev, "Microphone event: %x\n", val);

if (!(val & WM5100_ACCDET_VALID)) {
dev_warn(codec->dev, "Microphone detection state invalid\n");
dev_warn(wm5100->dev, "Microphone detection state invalid\n");
return;
}

/* No accessory, reset everything and report removal */
if (!(val & WM5100_ACCDET_STS)) {
dev_dbg(codec->dev, "Jack removal detected\n");
dev_dbg(wm5100->dev, "Jack removal detected\n");
wm5100->jack_mic = false;
wm5100->jack_detecting = true;
snd_soc_jack_report(wm5100->jack, 0,
SND_JACK_LINEOUT | SND_JACK_HEADSET |
SND_JACK_BTN_0);

snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
WM5100_ACCDET_RATE_MASK,
WM5100_ACCDET_RATE_MASK);
regmap_update_bits(wm5100->regmap, WM5100_MIC_DETECT_1,
WM5100_ACCDET_RATE_MASK,
WM5100_ACCDET_RATE_MASK);
return;
}

Expand All @@ -2181,19 +2187,19 @@ static void wm5100_micd_irq(struct snd_soc_codec *codec)
*/
if (val & 0x400) {
if (wm5100->jack_detecting) {
dev_dbg(codec->dev, "Microphone detected\n");
dev_dbg(wm5100->dev, "Microphone detected\n");
wm5100->jack_mic = true;
snd_soc_jack_report(wm5100->jack,
SND_JACK_HEADSET,
SND_JACK_HEADSET | SND_JACK_BTN_0);

/* Increase poll rate to give better responsiveness
* for buttons */
snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
WM5100_ACCDET_RATE_MASK,
5 << WM5100_ACCDET_RATE_SHIFT);
regmap_update_bits(wm5100->regmap, WM5100_MIC_DETECT_1,
WM5100_ACCDET_RATE_MASK,
5 << WM5100_ACCDET_RATE_SHIFT);
} else {
dev_dbg(codec->dev, "Mic button up\n");
dev_dbg(wm5100->dev, "Mic button up\n");
snd_soc_jack_report(wm5100->jack, 0, SND_JACK_BTN_0);
}

Expand All @@ -2206,7 +2212,7 @@ static void wm5100_micd_irq(struct snd_soc_codec *codec)
* plain headphones.
*/
if (wm5100->jack_detecting && (val & 0x3f8)) {
wm5100_set_detect_mode(codec, !wm5100->jack_mode);
wm5100_set_detect_mode(wm5100, !wm5100->jack_mode);

return;
}
Expand All @@ -2216,20 +2222,20 @@ static void wm5100_micd_irq(struct snd_soc_codec *codec)
*/
if (val & 0x3fc) {
if (wm5100->jack_mic) {
dev_dbg(codec->dev, "Mic button detected\n");
dev_dbg(wm5100->dev, "Mic button detected\n");
snd_soc_jack_report(wm5100->jack, SND_JACK_BTN_0,
SND_JACK_BTN_0);
} else if (wm5100->jack_detecting) {
dev_dbg(codec->dev, "Headphone detected\n");
dev_dbg(wm5100->dev, "Headphone detected\n");
snd_soc_jack_report(wm5100->jack, SND_JACK_HEADPHONE,
SND_JACK_HEADPHONE);

/* Increase the detection rate a bit for
* responsiveness.
*/
snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
WM5100_ACCDET_RATE_MASK,
7 << WM5100_ACCDET_RATE_SHIFT);
regmap_update_bits(wm5100->regmap, WM5100_MIC_DETECT_1,
WM5100_ACCDET_RATE_MASK,
7 << WM5100_ACCDET_RATE_SHIFT);
}
}
}
Expand All @@ -2242,7 +2248,7 @@ int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
wm5100->jack = jack;
wm5100->jack_detecting = true;

wm5100_set_detect_mode(codec, 0);
wm5100_set_detect_mode(wm5100, 0);

/* Slowest detection rate, gives debounce for initial
* detection */
Expand Down Expand Up @@ -2281,52 +2287,70 @@ int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)

static irqreturn_t wm5100_irq(int irq, void *data)
{
struct snd_soc_codec *codec = data;
struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
struct wm5100_priv *wm5100 = data;
irqreturn_t status = IRQ_NONE;
int irq_val;
unsigned int irq_val, mask_val;
int ret;

irq_val = snd_soc_read(codec, WM5100_INTERRUPT_STATUS_3);
if (irq_val < 0) {
dev_err(codec->dev, "Failed to read IRQ status 3: %d\n",
irq_val);
ret = regmap_read(wm5100->regmap, WM5100_INTERRUPT_STATUS_3, &irq_val);
if (ret < 0) {
dev_err(wm5100->dev, "Failed to read IRQ status 3: %d\n",
ret);
irq_val = 0;
}
irq_val &= ~snd_soc_read(codec, WM5100_INTERRUPT_STATUS_3_MASK);

snd_soc_write(codec, WM5100_INTERRUPT_STATUS_3, irq_val);
ret = regmap_read(wm5100->regmap, WM5100_INTERRUPT_STATUS_3_MASK,
&mask_val);
if (ret < 0) {
dev_err(wm5100->dev, "Failed to read IRQ mask 3: %d\n",
ret);
mask_val = 0xffff;
}

irq_val &= ~mask_val;

regmap_write(wm5100->regmap, WM5100_INTERRUPT_STATUS_3, irq_val);

if (irq_val)
status = IRQ_HANDLED;

wm5100_log_status3(codec, irq_val);
wm5100_log_status3(wm5100, irq_val);

if (irq_val & WM5100_FLL1_LOCK_EINT) {
dev_dbg(codec->dev, "FLL1 locked\n");
dev_dbg(wm5100->dev, "FLL1 locked\n");
complete(&wm5100->fll[0].lock);
}
if (irq_val & WM5100_FLL2_LOCK_EINT) {
dev_dbg(codec->dev, "FLL2 locked\n");
dev_dbg(wm5100->dev, "FLL2 locked\n");
complete(&wm5100->fll[1].lock);
}

if (irq_val & WM5100_ACCDET_EINT)
wm5100_micd_irq(codec);
wm5100_micd_irq(wm5100);

irq_val = snd_soc_read(codec, WM5100_INTERRUPT_STATUS_4);
if (irq_val < 0) {
dev_err(codec->dev, "Failed to read IRQ status 4: %d\n",
irq_val);
ret = regmap_read(wm5100->regmap, WM5100_INTERRUPT_STATUS_4, &irq_val);
if (ret < 0) {
dev_err(wm5100->dev, "Failed to read IRQ status 4: %d\n",
ret);
irq_val = 0;
}
irq_val &= ~snd_soc_read(codec, WM5100_INTERRUPT_STATUS_4_MASK);

ret = regmap_read(wm5100->regmap, WM5100_INTERRUPT_STATUS_4_MASK,
&mask_val);
if (ret < 0) {
dev_err(wm5100->dev, "Failed to read IRQ mask 4: %d\n",
ret);
mask_val = 0xffff;
}

irq_val &= ~mask_val;

if (irq_val)
status = IRQ_HANDLED;

snd_soc_write(codec, WM5100_INTERRUPT_STATUS_4, irq_val);
regmap_write(wm5100->regmap, WM5100_INTERRUPT_STATUS_4, irq_val);

wm5100_log_status4(codec, irq_val);
wm5100_log_status4(wm5100, irq_val);

return status;
}
Expand Down Expand Up @@ -2485,11 +2509,12 @@ static int wm5100_probe(struct snd_soc_codec *codec)

if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))
ret = request_threaded_irq(i2c->irq, NULL,
wm5100_edge_irq,
irq_flags, "wm5100", codec);
wm5100_edge_irq, irq_flags,
"wm5100", wm5100);
else
ret = request_threaded_irq(i2c->irq, NULL, wm5100_irq,
irq_flags, "wm5100", codec);
irq_flags, "wm5100",
wm5100);

if (ret != 0) {
dev_err(codec->dev, "Failed to request IRQ %d: %d\n",
Expand Down Expand Up @@ -2552,7 +2577,7 @@ static int wm5100_probe(struct snd_soc_codec *codec)

err_gpio:
if (i2c->irq)
free_irq(i2c->irq, codec);
free_irq(i2c->irq, wm5100);

return ret;
}
Expand All @@ -2566,7 +2591,8 @@ static int wm5100_remove(struct snd_soc_codec *codec)
gpio_free(wm5100->pdata.hp_pol);
}
if (i2c->irq)
free_irq(i2c->irq, codec);
free_irq(i2c->irq, wm5100);

return 0;
}

Expand Down Expand Up @@ -2622,6 +2648,8 @@ static __devinit int wm5100_i2c_probe(struct i2c_client *i2c,
if (wm5100 == NULL)
return -ENOMEM;

wm5100->dev = &i2c->dev;

wm5100->regmap = regmap_init_i2c(i2c, &wm5100_regmap);
if (IS_ERR(wm5100->regmap)) {
ret = PTR_ERR(wm5100->regmap);
Expand Down

0 comments on commit 46c1a87

Please sign in to comment.