ASoC: Make WM5100 interrupt path use regmap directly

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>
This commit is contained in:
Mark Brown 2012-01-18 14:53:08 +00:00
parent 218240e27f
commit 46c1a877c6

View File

@ -50,6 +50,7 @@ struct wm5100_fll {
/* codec private data */
struct wm5100_priv {
struct device *dev;
struct regmap *regmap;
struct snd_soc_codec *codec;
@ -855,48 +856,48 @@ 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,
@ -904,16 +905,17 @@ static int wm5100_post_ev(struct snd_soc_dapm_widget *w,
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;
}
@ -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;
}
@ -2181,7 +2187,7 @@ 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,
@ -2189,11 +2195,11 @@ static void wm5100_micd_irq(struct snd_soc_codec *codec)
/* 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);
}
@ -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;
}
@ -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);
}
}
}
@ -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 */
@ -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;
}
@ -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",
@ -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;
}
@ -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;
}
@ -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);