diff options
-rw-r--r-- | sound/soc/codecs/rt711-sdca-sdw.c | 46 | ||||
-rw-r--r-- | sound/soc/codecs/rt711-sdca.c | 4 | ||||
-rw-r--r-- | sound/soc/codecs/rt711-sdca.h | 2 |
3 files changed, 50 insertions, 2 deletions
diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index 03cd3e0142f9..aaf5af153d3f 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -256,6 +256,15 @@ static int rt711_sdca_interrupt_callback(struct sdw_slave *slave, scp_sdca_stat2 = rt711->scp_sdca_stat2; } + /* + * The critical section below intentionally protects a rather large piece of code. + * We don't want to allow the system suspend to disable an interrupt while we are + * processing it, which could be problematic given the quirky SoundWire interrupt + * scheme. We do want however to prevent new workqueues from being scheduled if + * the disable_irq flag was set during system suspend. + */ + mutex_lock(&rt711->disable_irq_lock); + ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT1); if (ret < 0) goto io_error; @@ -314,13 +323,16 @@ static int rt711_sdca_interrupt_callback(struct sdw_slave *slave, "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, rt711->scp_sdca_stat1, rt711->scp_sdca_stat2); - if (status->sdca_cascade) + if (status->sdca_cascade && !rt711->disable_irq) mod_delayed_work(system_power_efficient_wq, &rt711->jack_detect_work, msecs_to_jiffies(30)); + mutex_unlock(&rt711->disable_irq_lock); + return 0; io_error: + mutex_unlock(&rt711->disable_irq_lock); pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); return ret; } @@ -382,6 +394,36 @@ static int __maybe_unused rt711_sdca_dev_suspend(struct device *dev) return 0; } +static int __maybe_unused rt711_sdca_dev_system_suspend(struct device *dev) +{ + struct rt711_sdca_priv *rt711_sdca = dev_get_drvdata(dev); + struct sdw_slave *slave = dev_to_sdw_dev(dev); + int ret1, ret2; + + if (!rt711_sdca->hw_init) + return 0; + + /* + * prevent new interrupts from being handled after the + * deferred work completes and before the parent disables + * interrupts on the link + */ + mutex_lock(&rt711_sdca->disable_irq_lock); + rt711_sdca->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8, 0); + mutex_unlock(&rt711_sdca->disable_irq_lock); + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ + dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__); + } + + return rt711_sdca_dev_suspend(dev); +} + #define RT711_PROBE_TIMEOUT 5000 static int __maybe_unused rt711_sdca_dev_resume(struct device *dev) @@ -413,7 +455,7 @@ regmap_sync: } static const struct dev_pm_ops rt711_sdca_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt711_sdca_dev_suspend, rt711_sdca_dev_resume) + SET_SYSTEM_SLEEP_PM_OPS(rt711_sdca_dev_system_suspend, rt711_sdca_dev_resume) SET_RUNTIME_PM_OPS(rt711_sdca_dev_suspend, rt711_sdca_dev_resume, NULL) }; diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c index 0b0c230dcf71..2e992589f1e4 100644 --- a/sound/soc/codecs/rt711-sdca.c +++ b/sound/soc/codecs/rt711-sdca.c @@ -1411,6 +1411,8 @@ int rt711_sdca_init(struct device *dev, struct regmap *regmap, rt711->regmap = regmap; rt711->mbq_regmap = mbq_regmap; + mutex_init(&rt711->disable_irq_lock); + /* * Mark hw_init to false * HW init will be performed when device reports present @@ -1494,6 +1496,8 @@ int rt711_sdca_io_init(struct device *dev, struct sdw_slave *slave) int ret = 0; unsigned int val; + rt711->disable_irq = false; + if (rt711->hw_init) return 0; diff --git a/sound/soc/codecs/rt711-sdca.h b/sound/soc/codecs/rt711-sdca.h index 43ae82b7fdb3..498ca687c47b 100644 --- a/sound/soc/codecs/rt711-sdca.h +++ b/sound/soc/codecs/rt711-sdca.h @@ -27,6 +27,8 @@ struct rt711_sdca_priv { struct delayed_work jack_detect_work; struct delayed_work jack_btn_check_work; struct mutex calibrate_mutex; /* for headset calibration */ + struct mutex disable_irq_lock; /* SDCA irq lock protection */ + bool disable_irq; int jack_type, jd_src; unsigned int scp_sdca_stat1, scp_sdca_stat2; int hw_ver; |