diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 399aa42c6414a..dbde77c4b7fa5 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -59,6 +59,8 @@ struct tas2764_priv { struct regmap *regmap; struct device *dev; int irq; + /* Set while the component holds the fault IRQ */ + bool irq_requested; enum tas2764_devid devid; int v_sense_slot; @@ -937,31 +939,23 @@ static int tas2764_codec_probe(struct snd_soc_component *component) if (tas2764->irq) { ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK0, 0x00); if (ret < 0) - return ret; + goto err_sdz; ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK1, 0xff); if (ret < 0) - return ret; + goto err_sdz; ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK2, 0xff); if (ret < 0) - return ret; + goto err_sdz; ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK3, 0xff); if (ret < 0) - return ret; + goto err_sdz; ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK4, 0xff); if (ret < 0) - return ret; - - ret = devm_request_threaded_irq(tas2764->dev, tas2764->irq, NULL, tas2764_irq, - IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, - "tas2764", tas2764); - if (ret && tas2764->sdz_group) - return dev_err_probe(tas2764->dev, ret, "SDZ group needs its fault IRQ\n"); - if (ret) - dev_warn(tas2764->dev, "failed to request IRQ: %d\n", ret); + goto err_sdz; } if (tas2764->i_sense_slot != -1 && tas2764->v_sense_slot != -1) { @@ -969,7 +963,7 @@ static int tas2764_codec_probe(struct snd_soc_component *component) tas2764->v_sense_slot); if (ret < 0) - return ret; + goto err_sdz; } switch (tas2764->devid) { @@ -978,7 +972,7 @@ static int tas2764_codec_probe(struct snd_soc_component *component) TAS2764_PWR_CTRL_BOP_SRC, TAS2764_PWR_CTRL_BOP_SRC); if (ret < 0) - return ret; + goto err_sdz; for (i = 0; i < ARRAY_SIZE(sn012776_bop_presets); i++) { ret = snd_soc_component_write(component, @@ -986,27 +980,58 @@ static int tas2764_codec_probe(struct snd_soc_component *component) sn012776_bop_presets[i]); if (ret < 0) - return ret; + goto err_sdz; } /* Apply all enabled Apple quirks */ ret = tas2764_apply_init_quirks(tas2764); if (ret < 0) - return ret; + goto err_sdz; break; default: break; } + /* + * Last, so that no failure leaves the handler behind: the IRQ belongs to the I2C + * device, which outlives the component, and the remove frees it. + */ + if (tas2764->irq) { + ret = devm_request_threaded_irq(tas2764->dev, tas2764->irq, NULL, tas2764_irq, + IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, + "tas2764", tas2764); + if (ret && tas2764->sdz_group) { + dev_err_probe(tas2764->dev, ret, "SDZ group needs its fault IRQ\n"); + goto err_sdz; + } + if (ret) + dev_warn(tas2764->dev, "failed to request IRQ: %d\n", ret); + else + tas2764->irq_requested = true; + } + return 0; + +err_sdz: + /* Undo the power-up: ASoC doesn't call the remove after a failed probe */ + if (tas2764->sdz_gpio) + gpiod_set_value_cansleep(tas2764->sdz_gpio, 0); + if (!tas2764_group_failed(tas2764)) + regulator_disable(tas2764->sdz_reg); + return ret; } static void tas2764_codec_remove(struct snd_soc_component *component) { struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + if (tas2764->irq_requested) { + devm_free_irq(tas2764->dev, tas2764->irq, tas2764); + tas2764->irq_requested = false; + } + /* A failed group's supply was forced off: there is no enable left to balance. */ if (tas2764_group_failed(tas2764)) return;