Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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.

+63 -4
+45 -4
drivers/firmware/cirrus/cs_dsp.c
··· 515 515 516 516 debugfs_create_bool("booted", 0444, root, &dsp->booted); 517 517 debugfs_create_bool("running", 0444, root, &dsp->running); 518 + debugfs_create_bool("hibernating", 0444, root, &dsp->hibernating); 518 519 debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id); 519 520 debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version); 520 521 ··· 704 703 705 704 lockdep_assert_held(&dsp->pwr_lock); 706 705 707 - if (!dsp->running) 706 + if (!dsp->running || dsp->hibernating) 708 707 return -EPERM; 709 708 710 709 ret = cs_dsp_coeff_base_reg(ctl, &reg, 0); ··· 828 827 } 829 828 830 829 ctl->set = 1; 831 - if (ctl->enabled && ctl->dsp->running) 830 + if (ctl->enabled && ctl->dsp->running && !ctl->dsp->hibernating) 832 831 ret = cs_dsp_coeff_write_ctrl_raw(ctl, off, buf, len); 833 832 834 833 if (ret < 0) ··· 921 920 return -EINVAL; 922 921 923 922 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 924 - if (ctl->enabled && ctl->dsp->running) 923 + if (ctl->enabled && ctl->dsp->running && !ctl->dsp->hibernating) 925 924 return cs_dsp_coeff_read_ctrl_raw(ctl, off, buf, len); 926 925 else 927 926 return -EPERM; 928 927 } else { 929 - if (!ctl->flags && ctl->enabled && ctl->dsp->running) 928 + if (!ctl->flags && ctl->enabled && ctl->dsp->running && !ctl->dsp->hibernating) 930 929 ret = cs_dsp_coeff_read_ctrl_raw(ctl, 0, ctl->cache, ctl->len); 931 930 932 931 if (buf != ctl->cache) ··· 1108 1107 1109 1108 return ret; 1110 1109 } 1110 + 1111 + 1112 + /** 1113 + * cs_dsp_hibernate() - Disable or enable all controls for a DSP 1114 + * @dsp: pointer to DSP structure 1115 + * @hibernate: whether to set controls to cache only mode 1116 + * 1117 + * When @hibernate is true, the DSP is entering hibernation mode where the 1118 + * regmap is inaccessible, and all controls become cache only. 1119 + * When @hibernate is false, the DSP has exited hibernation mode. If the DSP 1120 + * is running, all controls are re-synced to the DSP. 1121 + * 1122 + */ 1123 + void cs_dsp_hibernate(struct cs_dsp *dsp, bool hibernate) 1124 + { 1125 + mutex_lock(&dsp->pwr_lock); 1126 + 1127 + if (!dsp->running) { 1128 + cs_dsp_dbg(dsp, "Cannot hibernate, DSP not running\n"); 1129 + goto out; 1130 + } 1131 + 1132 + if (dsp->hibernating == hibernate) 1133 + goto out; 1134 + 1135 + cs_dsp_dbg(dsp, "Set hibernating to %d\n", hibernate); 1136 + dsp->hibernating = hibernate; 1137 + 1138 + if (!dsp->hibernating && dsp->running) { 1139 + int ret = cs_dsp_coeff_sync_controls(dsp); 1140 + 1141 + if (ret) 1142 + cs_dsp_err(dsp, "Error syncing controls: %d\n", ret); 1143 + } 1144 + out: 1145 + mutex_unlock(&dsp->pwr_lock); 1146 + } 1147 + EXPORT_SYMBOL_NS_GPL(cs_dsp_hibernate, "FW_CS_DSP"); 1111 1148 1112 1149 struct cs_dsp_coeff_parsed_alg { 1113 1150 int id; ··· 2537 2498 goto err_ena; 2538 2499 2539 2500 dsp->booted = true; 2501 + dsp->hibernating = false; 2540 2502 2541 2503 /* Start the core running */ 2542 2504 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, ··· 2816 2776 dsp->ops->disable_core(dsp); 2817 2777 2818 2778 dsp->booted = true; 2779 + dsp->hibernating = false; 2819 2780 2820 2781 mutex_unlock(&dsp->pwr_lock); 2821 2782
+3
include/linux/firmware/cirrus/cs_dsp.h
··· 179 179 180 180 bool booted; 181 181 bool running; 182 + bool hibernating; 182 183 183 184 struct list_head ctl_list; 184 185 ··· 354 353 int cs_dsp_chunk_write(struct cs_dsp_chunk *ch, int nbits, u32 val); 355 354 int cs_dsp_chunk_flush(struct cs_dsp_chunk *ch); 356 355 int cs_dsp_chunk_read(struct cs_dsp_chunk *ch, int nbits); 356 + 357 + void cs_dsp_hibernate(struct cs_dsp *dsp, bool hibernating); 357 358 358 359 #endif
+5
sound/soc/codecs/cs35l41.c
··· 1404 1404 if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running) 1405 1405 return 0; 1406 1406 1407 + wm_adsp_hibernate(&cs35l41->dsp, true); 1407 1408 cs35l41_enter_hibernate(dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type); 1408 1409 1409 1410 regcache_cache_only(cs35l41->regmap, true); ··· 1433 1432 cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap); 1434 1433 ret = regcache_sync(cs35l41->regmap); 1435 1434 cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap); 1435 + 1436 + wm_adsp_hibernate(&cs35l41->dsp, false); 1437 + 1436 1438 if (ret) { 1437 1439 dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret); 1438 1440 return ret; 1439 1441 } 1442 + 1440 1443 cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, &cs35l41->hw_cfg); 1441 1444 1442 1445 return 0;
+3
sound/soc/codecs/cs35l45.c
··· 984 984 if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running) 985 985 return 0; 986 986 987 + wm_adsp_hibernate(&cs35l45->dsp, true); 987 988 cs35l45_enter_hibernate(cs35l45); 988 989 989 990 regcache_cache_only(cs35l45->regmap, true); ··· 1014 1013 ret = regcache_sync(cs35l45->regmap); 1015 1014 if (ret != 0) 1016 1015 dev_warn(cs35l45->dev, "regcache_sync failed: %d\n", ret); 1016 + 1017 + wm_adsp_hibernate(&cs35l45->dsp, false); 1017 1018 1018 1019 /* Clear global error status */ 1019 1020 regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK);
+6
sound/soc/codecs/wm_adsp.c
··· 1100 1100 } 1101 1101 EXPORT_SYMBOL_GPL(wm_adsp_stop); 1102 1102 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 + 1103 1109 int wm_adsp_event(struct snd_soc_dapm_widget *w, 1104 1110 struct snd_kcontrol *kcontrol, int event) 1105 1111 {
+1
sound/soc/codecs/wm_adsp.h
··· 103 103 104 104 int wm_adsp_run(struct wm_adsp *dsp); 105 105 void wm_adsp_stop(struct wm_adsp *dsp); 106 + void wm_adsp_hibernate(struct wm_adsp *dsp, bool hibernate); 106 107 int wm_adsp_event(struct snd_soc_dapm_widget *w, 107 108 struct snd_kcontrol *kcontrol, int event); 108 109