mirror of
https://github.com/torvalds/linux.git
synced 2026-05-26 08:02:27 +02:00
Support wm_adsp hibernation for runtime suspend
Merge series from Stefan Binding <sbinding@opensource.cirrus.com>: When the CS35L41 and CS35L45 drivers suspend, they are put into hibernation, and the regmap goes into cache_only, but the firmware is still running, and wm_adsp is not stopped. If userspace attempts to read a firmware control, it will perform a regmap_raw_read() and this will produce an error in the kernel log. To prevent these spurious errors, add an apis into cs_dsp and wm_adsp to allow wm_adsp to hibernate. In this hibernation mode, reads or writes to the dsp controls would be rejected with -EPERM rather than -EBUSY, and no error will be printed to the kernel log.
This commit is contained in:
commit
727d1a1c4e
|
|
@ -515,6 +515,7 @@ void cs_dsp_init_debugfs(struct cs_dsp *dsp, struct dentry *debugfs_root)
|
|||
|
||||
debugfs_create_bool("booted", 0444, root, &dsp->booted);
|
||||
debugfs_create_bool("running", 0444, root, &dsp->running);
|
||||
debugfs_create_bool("hibernating", 0444, root, &dsp->hibernating);
|
||||
debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id);
|
||||
debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version);
|
||||
|
||||
|
|
@ -703,7 +704,7 @@ int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, unsigned int
|
|||
|
||||
lockdep_assert_held(&dsp->pwr_lock);
|
||||
|
||||
if (!dsp->running)
|
||||
if (!dsp->running || dsp->hibernating)
|
||||
return -EPERM;
|
||||
|
||||
ret = cs_dsp_coeff_base_reg(ctl, ®, 0);
|
||||
|
|
@ -827,7 +828,7 @@ int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl,
|
|||
}
|
||||
|
||||
ctl->set = 1;
|
||||
if (ctl->enabled && ctl->dsp->running)
|
||||
if (ctl->enabled && ctl->dsp->running && !ctl->dsp->hibernating)
|
||||
ret = cs_dsp_coeff_write_ctrl_raw(ctl, off, buf, len);
|
||||
|
||||
if (ret < 0)
|
||||
|
|
@ -920,12 +921,12 @@ int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl,
|
|||
return -EINVAL;
|
||||
|
||||
if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
|
||||
if (ctl->enabled && ctl->dsp->running)
|
||||
if (ctl->enabled && ctl->dsp->running && !ctl->dsp->hibernating)
|
||||
return cs_dsp_coeff_read_ctrl_raw(ctl, off, buf, len);
|
||||
else
|
||||
return -EPERM;
|
||||
} else {
|
||||
if (!ctl->flags && ctl->enabled && ctl->dsp->running)
|
||||
if (!ctl->flags && ctl->enabled && ctl->dsp->running && !ctl->dsp->hibernating)
|
||||
ret = cs_dsp_coeff_read_ctrl_raw(ctl, 0, ctl->cache, ctl->len);
|
||||
|
||||
if (buf != ctl->cache)
|
||||
|
|
@ -1108,6 +1109,44 @@ static int cs_dsp_create_control(struct cs_dsp *dsp,
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cs_dsp_hibernate() - Disable or enable all controls for a DSP
|
||||
* @dsp: pointer to DSP structure
|
||||
* @hibernate: whether to set controls to cache only mode
|
||||
*
|
||||
* When @hibernate is true, the DSP is entering hibernation mode where the
|
||||
* regmap is inaccessible, and all controls become cache only.
|
||||
* When @hibernate is false, the DSP has exited hibernation mode. If the DSP
|
||||
* is running, all controls are re-synced to the DSP.
|
||||
*
|
||||
*/
|
||||
void cs_dsp_hibernate(struct cs_dsp *dsp, bool hibernate)
|
||||
{
|
||||
mutex_lock(&dsp->pwr_lock);
|
||||
|
||||
if (!dsp->running) {
|
||||
cs_dsp_dbg(dsp, "Cannot hibernate, DSP not running\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dsp->hibernating == hibernate)
|
||||
goto out;
|
||||
|
||||
cs_dsp_dbg(dsp, "Set hibernating to %d\n", hibernate);
|
||||
dsp->hibernating = hibernate;
|
||||
|
||||
if (!dsp->hibernating && dsp->running) {
|
||||
int ret = cs_dsp_coeff_sync_controls(dsp);
|
||||
|
||||
if (ret)
|
||||
cs_dsp_err(dsp, "Error syncing controls: %d\n", ret);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&dsp->pwr_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_hibernate, "FW_CS_DSP");
|
||||
|
||||
struct cs_dsp_coeff_parsed_alg {
|
||||
int id;
|
||||
const u8 *name;
|
||||
|
|
@ -2498,6 +2537,7 @@ int cs_dsp_adsp1_power_up(struct cs_dsp *dsp,
|
|||
goto err_ena;
|
||||
|
||||
dsp->booted = true;
|
||||
dsp->hibernating = false;
|
||||
|
||||
/* Start the core running */
|
||||
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
|
||||
|
|
@ -2776,6 +2816,7 @@ int cs_dsp_power_up(struct cs_dsp *dsp,
|
|||
dsp->ops->disable_core(dsp);
|
||||
|
||||
dsp->booted = true;
|
||||
dsp->hibernating = false;
|
||||
|
||||
mutex_unlock(&dsp->pwr_lock);
|
||||
|
||||
|
|
|
|||
|
|
@ -179,6 +179,7 @@ struct cs_dsp {
|
|||
|
||||
bool booted;
|
||||
bool running;
|
||||
bool hibernating;
|
||||
|
||||
struct list_head ctl_list;
|
||||
|
||||
|
|
@ -354,4 +355,6 @@ int cs_dsp_chunk_write(struct cs_dsp_chunk *ch, int nbits, u32 val);
|
|||
int cs_dsp_chunk_flush(struct cs_dsp_chunk *ch);
|
||||
int cs_dsp_chunk_read(struct cs_dsp_chunk *ch, int nbits);
|
||||
|
||||
void cs_dsp_hibernate(struct cs_dsp *dsp, bool hibernating);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1404,6 +1404,7 @@ static int cs35l41_runtime_suspend(struct device *dev)
|
|||
if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running)
|
||||
return 0;
|
||||
|
||||
wm_adsp_hibernate(&cs35l41->dsp, true);
|
||||
cs35l41_enter_hibernate(dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type);
|
||||
|
||||
regcache_cache_only(cs35l41->regmap, true);
|
||||
|
|
@ -1432,10 +1433,14 @@ static int cs35l41_runtime_resume(struct device *dev)
|
|||
cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
|
||||
ret = regcache_sync(cs35l41->regmap);
|
||||
cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
|
||||
|
||||
wm_adsp_hibernate(&cs35l41->dsp, false);
|
||||
|
||||
if (ret) {
|
||||
dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, &cs35l41->hw_cfg);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -984,6 +984,7 @@ static int cs35l45_runtime_suspend(struct device *dev)
|
|||
if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running)
|
||||
return 0;
|
||||
|
||||
wm_adsp_hibernate(&cs35l45->dsp, true);
|
||||
cs35l45_enter_hibernate(cs35l45);
|
||||
|
||||
regcache_cache_only(cs35l45->regmap, true);
|
||||
|
|
@ -1014,6 +1015,8 @@ static int cs35l45_runtime_resume(struct device *dev)
|
|||
if (ret != 0)
|
||||
dev_warn(cs35l45->dev, "regcache_sync failed: %d\n", ret);
|
||||
|
||||
wm_adsp_hibernate(&cs35l45->dsp, false);
|
||||
|
||||
/* Clear global error status */
|
||||
regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK);
|
||||
regmap_set_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK);
|
||||
|
|
|
|||
|
|
@ -1100,6 +1100,12 @@ void wm_adsp_stop(struct wm_adsp *dsp)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(wm_adsp_stop);
|
||||
|
||||
void wm_adsp_hibernate(struct wm_adsp *dsp, bool hibernate)
|
||||
{
|
||||
cs_dsp_hibernate(&dsp->cs_dsp, hibernate);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm_adsp_hibernate);
|
||||
|
||||
int wm_adsp_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ irqreturn_t wm_halo_wdt_expire(int irq, void *data);
|
|||
|
||||
int wm_adsp_run(struct wm_adsp *dsp);
|
||||
void wm_adsp_stop(struct wm_adsp *dsp);
|
||||
void wm_adsp_hibernate(struct wm_adsp *dsp, bool hibernate);
|
||||
int wm_adsp_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user