diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index 747e10ec1a27..ce6383c2c60c 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -209,6 +209,9 @@ struct snd_ctl_elem_value; #define CS35L56_GPIO_FN_GPIO 0x00000001 +/* DSP1_FW_VER */ +#define CS35L56_FW_MAIN_VERSION_MASK GENMASK(19, 0) + /* Mixer input sources */ #define CS35L56_INPUT_SRC_NONE 0x00 #define CS35L56_INPUT_SRC_ASP1RX1 0x08 @@ -446,6 +449,7 @@ int cs35l56_cal_set_status_get(struct cs35l56_base *cs35l56_base, int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base, bool *fw_missing, unsigned int *fw_version); void cs35l56_warn_if_firmware_missing(struct cs35l56_base *cs35l56_base); +bool cs35l56_needs_wait_for_firmware_timer_expiry(struct cs35l56_base *cs35l56_base); void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp); int cs35l56_hw_init(struct cs35l56_base *cs35l56_base); int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base); diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index e10f04d677f4..a49ad69644b5 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,9 @@ #define CS35L56_LATE_READ_POLL_US 10 #define CS35L56_LATE_READ_TIMEOUT_US 1000 +static DEFINE_MUTEX(cs35l56_fw_idle_wait_lock); +static bool cs35l56_fw_idle_wait_pending; + static int cs35l56_sdw_poll_mem_status(struct sdw_slave *peripheral, unsigned int mask, unsigned int match) @@ -387,14 +391,34 @@ static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev) return 0; } +static int cs35l56_sdw_system_suspend_prepare(struct device *dev) +{ + cs35l56_fw_idle_wait_pending = true; + + return 0; +} + static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) { struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + int ret; if (cs35l56->sdw_attached) cs35l56_mask_soundwire_interrupts(cs35l56); - return cs35l56_system_suspend(dev); + ret = cs35l56_system_suspend(dev); + if (ret < 0) + return ret; + + scoped_guard(mutex, &cs35l56_fw_idle_wait_lock) { + if (cs35l56_fw_idle_wait_pending && cs35l56->needs_wait_for_fw_idle) { + dev_dbg(cs35l56->base.dev, "Wait for FW timer expiry\n"); + msleep(CS35L56_FW_REQ_ACTIVE_TIMEOUT_MS + 10); + cs35l56_fw_idle_wait_pending = false; + } + } + + return 0; } static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) @@ -460,6 +484,7 @@ static void cs35l56_sdw_remove(struct sdw_slave *peripheral) } static const struct dev_pm_ops cs35l56_sdw_pm = { + .prepare = cs35l56_sdw_system_suspend_prepare, SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL) SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_system_resume) LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early) diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 7b3e37d462d6..b37f65cd4d33 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -1379,6 +1379,44 @@ void cs35l56_warn_if_firmware_missing(struct cs35l56_base *cs35l56_base) } EXPORT_SYMBOL_NS_GPL(cs35l56_warn_if_firmware_missing, "SND_SOC_CS35L56_SHARED"); +bool cs35l56_needs_wait_for_firmware_timer_expiry(struct cs35l56_base *cs35l56_base) +{ + unsigned int fw_ver; + bool prot_sts; + int ret; + + switch (cs35l56_base->type) { + case 0x54: + case 0x56: + case 0x57: + switch (cs35l56_base->rev) { + case 0xb0: + ret = cs35l56_read_prot_status(cs35l56_base, &prot_sts, &fw_ver); + if (ret) + return true; + + if ((fw_ver & CS35L56_FW_MAIN_VERSION_MASK) < 0x30d07) + return true; + + return false; + case 0xb2: + ret = cs35l56_read_prot_status(cs35l56_base, &prot_sts, &fw_ver); + if (ret) + return true; + + if ((fw_ver & CS35L56_FW_MAIN_VERSION_MASK) < 0x40710) + return true; + + return false; + default: + return false; + } + default: + return false; + } +} +EXPORT_SYMBOL_NS_GPL(cs35l56_needs_wait_for_firmware_timer_expiry, "SND_SOC_CS35L56_SHARED"); + void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp) { __be32 pid, sid, tid; diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index dd264c310e1e..a4d25c287941 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1703,6 +1703,10 @@ int cs35l56_system_resume(struct device *dev) dev_dbg(dev, "system_resume\n"); + /* Assume the firmware reset to default until we can check it */ + if (cs35l56->base.init_done) + cs35l56->needs_wait_for_fw_idle = true; + /* * We might have done a hard reset or the CS35L56 was power-cycled * so wait for control port to be ready. @@ -1717,6 +1721,16 @@ int cs35l56_system_resume(struct device *dev) if (ret) return ret; + if (cs35l56->sdw_peripheral && cs35l56->base.init_done) { + ret = pm_runtime_resume_and_get(cs35l56->base.dev); + if (ret < 0) + return ret; + + cs35l56->needs_wait_for_fw_idle = + cs35l56_needs_wait_for_firmware_timer_expiry(&cs35l56->base); + pm_runtime_put_autosuspend(cs35l56->base.dev); + } + /* Firmware won't have been loaded if the component hasn't probed */ if (!cs35l56->component) return 0; @@ -2241,6 +2255,9 @@ int cs35l56_init(struct cs35l56_private *cs35l56) if (ret) return dev_err_probe(cs35l56->base.dev, ret, "Failed to write ASP1_CONTROL3\n"); + cs35l56->needs_wait_for_fw_idle = + cs35l56_needs_wait_for_firmware_timer_expiry(&cs35l56->base); + cs35l56->base.init_done = true; complete_all(&cs35l56->init_completion); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index f7cf8aa653e2..b46f410f0e09 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -43,6 +43,7 @@ struct cs35l56_private { bool soft_resetting; bool sdw_attached; bool component_registered; + bool needs_wait_for_fw_idle; struct completion init_completion; int speaker_id;