Skip to content

Commit 727d1a1

Browse files
committed
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.
2 parents a8fd392 + 17c6bf4 commit 727d1a1

6 files changed

Lines changed: 63 additions & 4 deletions

File tree

drivers/firmware/cirrus/cs_dsp.c

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ void cs_dsp_init_debugfs(struct cs_dsp *dsp, struct dentry *debugfs_root)
515515

516516
debugfs_create_bool("booted", 0444, root, &dsp->booted);
517517
debugfs_create_bool("running", 0444, root, &dsp->running);
518+
debugfs_create_bool("hibernating", 0444, root, &dsp->hibernating);
518519
debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id);
519520
debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version);
520521

@@ -703,7 +704,7 @@ int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, unsigned int
703704

704705
lockdep_assert_held(&dsp->pwr_lock);
705706

706-
if (!dsp->running)
707+
if (!dsp->running || dsp->hibernating)
707708
return -EPERM;
708709

709710
ret = cs_dsp_coeff_base_reg(ctl, &reg, 0);
@@ -827,7 +828,7 @@ int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl,
827828
}
828829

829830
ctl->set = 1;
830-
if (ctl->enabled && ctl->dsp->running)
831+
if (ctl->enabled && ctl->dsp->running && !ctl->dsp->hibernating)
831832
ret = cs_dsp_coeff_write_ctrl_raw(ctl, off, buf, len);
832833

833834
if (ret < 0)
@@ -920,12 +921,12 @@ int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl,
920921
return -EINVAL;
921922

922923
if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
923-
if (ctl->enabled && ctl->dsp->running)
924+
if (ctl->enabled && ctl->dsp->running && !ctl->dsp->hibernating)
924925
return cs_dsp_coeff_read_ctrl_raw(ctl, off, buf, len);
925926
else
926927
return -EPERM;
927928
} else {
928-
if (!ctl->flags && ctl->enabled && ctl->dsp->running)
929+
if (!ctl->flags && ctl->enabled && ctl->dsp->running && !ctl->dsp->hibernating)
929930
ret = cs_dsp_coeff_read_ctrl_raw(ctl, 0, ctl->cache, ctl->len);
930931

931932
if (buf != ctl->cache)
@@ -1108,6 +1109,44 @@ static int cs_dsp_create_control(struct cs_dsp *dsp,
11081109
return ret;
11091110
}
11101111

1112+
1113+
/**
1114+
* cs_dsp_hibernate() - Disable or enable all controls for a DSP
1115+
* @dsp: pointer to DSP structure
1116+
* @hibernate: whether to set controls to cache only mode
1117+
*
1118+
* When @hibernate is true, the DSP is entering hibernation mode where the
1119+
* regmap is inaccessible, and all controls become cache only.
1120+
* When @hibernate is false, the DSP has exited hibernation mode. If the DSP
1121+
* is running, all controls are re-synced to the DSP.
1122+
*
1123+
*/
1124+
void cs_dsp_hibernate(struct cs_dsp *dsp, bool hibernate)
1125+
{
1126+
mutex_lock(&dsp->pwr_lock);
1127+
1128+
if (!dsp->running) {
1129+
cs_dsp_dbg(dsp, "Cannot hibernate, DSP not running\n");
1130+
goto out;
1131+
}
1132+
1133+
if (dsp->hibernating == hibernate)
1134+
goto out;
1135+
1136+
cs_dsp_dbg(dsp, "Set hibernating to %d\n", hibernate);
1137+
dsp->hibernating = hibernate;
1138+
1139+
if (!dsp->hibernating && dsp->running) {
1140+
int ret = cs_dsp_coeff_sync_controls(dsp);
1141+
1142+
if (ret)
1143+
cs_dsp_err(dsp, "Error syncing controls: %d\n", ret);
1144+
}
1145+
out:
1146+
mutex_unlock(&dsp->pwr_lock);
1147+
}
1148+
EXPORT_SYMBOL_NS_GPL(cs_dsp_hibernate, "FW_CS_DSP");
1149+
11111150
struct cs_dsp_coeff_parsed_alg {
11121151
int id;
11131152
const u8 *name;
@@ -2498,6 +2537,7 @@ int cs_dsp_adsp1_power_up(struct cs_dsp *dsp,
24982537
goto err_ena;
24992538

25002539
dsp->booted = true;
2540+
dsp->hibernating = false;
25012541

25022542
/* Start the core running */
25032543
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
@@ -2776,6 +2816,7 @@ int cs_dsp_power_up(struct cs_dsp *dsp,
27762816
dsp->ops->disable_core(dsp);
27772817

27782818
dsp->booted = true;
2819+
dsp->hibernating = false;
27792820

27802821
mutex_unlock(&dsp->pwr_lock);
27812822

include/linux/firmware/cirrus/cs_dsp.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ struct cs_dsp {
179179

180180
bool booted;
181181
bool running;
182+
bool hibernating;
182183

183184
struct list_head ctl_list;
184185

@@ -354,4 +355,6 @@ int cs_dsp_chunk_write(struct cs_dsp_chunk *ch, int nbits, u32 val);
354355
int cs_dsp_chunk_flush(struct cs_dsp_chunk *ch);
355356
int cs_dsp_chunk_read(struct cs_dsp_chunk *ch, int nbits);
356357

358+
void cs_dsp_hibernate(struct cs_dsp *dsp, bool hibernating);
359+
357360
#endif

sound/soc/codecs/cs35l41.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,6 +1404,7 @@ static int cs35l41_runtime_suspend(struct device *dev)
14041404
if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running)
14051405
return 0;
14061406

1407+
wm_adsp_hibernate(&cs35l41->dsp, true);
14071408
cs35l41_enter_hibernate(dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type);
14081409

14091410
regcache_cache_only(cs35l41->regmap, true);
@@ -1432,10 +1433,14 @@ static int cs35l41_runtime_resume(struct device *dev)
14321433
cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
14331434
ret = regcache_sync(cs35l41->regmap);
14341435
cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
1436+
1437+
wm_adsp_hibernate(&cs35l41->dsp, false);
1438+
14351439
if (ret) {
14361440
dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret);
14371441
return ret;
14381442
}
1443+
14391444
cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, &cs35l41->hw_cfg);
14401445

14411446
return 0;

sound/soc/codecs/cs35l45.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,7 @@ static int cs35l45_runtime_suspend(struct device *dev)
984984
if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running)
985985
return 0;
986986

987+
wm_adsp_hibernate(&cs35l45->dsp, true);
987988
cs35l45_enter_hibernate(cs35l45);
988989

989990
regcache_cache_only(cs35l45->regmap, true);
@@ -1014,6 +1015,8 @@ static int cs35l45_runtime_resume(struct device *dev)
10141015
if (ret != 0)
10151016
dev_warn(cs35l45->dev, "regcache_sync failed: %d\n", ret);
10161017

1018+
wm_adsp_hibernate(&cs35l45->dsp, false);
1019+
10171020
/* Clear global error status */
10181021
regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK);
10191022
regmap_set_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK);

sound/soc/codecs/wm_adsp.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,12 @@ void wm_adsp_stop(struct wm_adsp *dsp)
11001100
}
11011101
EXPORT_SYMBOL_GPL(wm_adsp_stop);
11021102

1103+
void wm_adsp_hibernate(struct wm_adsp *dsp, bool hibernate)
1104+
{
1105+
cs_dsp_hibernate(&dsp->cs_dsp, hibernate);
1106+
}
1107+
EXPORT_SYMBOL_GPL(wm_adsp_hibernate);
1108+
11031109
int wm_adsp_event(struct snd_soc_dapm_widget *w,
11041110
struct snd_kcontrol *kcontrol, int event)
11051111
{

sound/soc/codecs/wm_adsp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ irqreturn_t wm_halo_wdt_expire(int irq, void *data);
103103

104104
int wm_adsp_run(struct wm_adsp *dsp);
105105
void wm_adsp_stop(struct wm_adsp *dsp);
106+
void wm_adsp_hibernate(struct wm_adsp *dsp, bool hibernate);
106107
int wm_adsp_event(struct snd_soc_dapm_widget *w,
107108
struct snd_kcontrol *kcontrol, int event);
108109

0 commit comments

Comments
 (0)