Commit a8b1b9ce authored by Mark Brown's avatar Mark Brown

ALSA: hda: cirrus: Add initial DSP support and firmware loading

Merge series from Vitaly Rodionov <vitalyr@opensource.cirrus.com>:

The CS35L41 Amplifier contains a DSP, capable of running firmware.
The firmware can run algorithms such as Speaker Protection, to ensure
that playback at high gains do not harm the speakers.  This exports some
interfaces to allow more use of this DSP by the HDA version of the driver.
parents ac8a2ea4 97076475
...@@ -881,6 +881,9 @@ void cs35l41_configure_cs_dsp(struct device *dev, struct regmap *reg, struct cs_ ...@@ -881,6 +881,9 @@ void cs35l41_configure_cs_dsp(struct device *dev, struct regmap *reg, struct cs_
int cs35l41_set_cspl_mbox_cmd(struct device *dev, struct regmap *regmap, int cs35l41_set_cspl_mbox_cmd(struct device *dev, struct regmap *regmap,
enum cs35l41_cspl_mbox_cmd cmd); enum cs35l41_cspl_mbox_cmd cmd);
int cs35l41_write_fs_errata(struct device *dev, struct regmap *regmap); int cs35l41_write_fs_errata(struct device *dev, struct regmap *regmap);
int cs35l41_enter_hibernate(struct device *dev, struct regmap *regmap,
enum cs35l41_boost_type b_type);
int cs35l41_exit_hibernate(struct device *dev, struct regmap *regmap);
int cs35l41_init_boost(struct device *dev, struct regmap *regmap, int cs35l41_init_boost(struct device *dev, struct regmap *regmap,
struct cs35l41_hw_cfg *hw_cfg); struct cs35l41_hw_cfg *hw_cfg);
bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type); bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type);
......
...@@ -1302,6 +1302,7 @@ int cs35l41_set_cspl_mbox_cmd(struct device *dev, struct regmap *regmap, ...@@ -1302,6 +1302,7 @@ int cs35l41_set_cspl_mbox_cmd(struct device *dev, struct regmap *regmap,
return 0; return 0;
} }
if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
dev_err(dev, "Failed to set mailbox cmd %u (status %u)\n", cmd, sts); dev_err(dev, "Failed to set mailbox cmd %u (status %u)\n", cmd, sts);
return -ENOMSG; return -ENOMSG;
...@@ -1321,6 +1322,85 @@ int cs35l41_write_fs_errata(struct device *dev, struct regmap *regmap) ...@@ -1321,6 +1322,85 @@ int cs35l41_write_fs_errata(struct device *dev, struct regmap *regmap)
} }
EXPORT_SYMBOL_GPL(cs35l41_write_fs_errata); EXPORT_SYMBOL_GPL(cs35l41_write_fs_errata);
int cs35l41_enter_hibernate(struct device *dev, struct regmap *regmap,
enum cs35l41_boost_type b_type)
{
if (!cs35l41_safe_reset(regmap, b_type)) {
dev_dbg(dev, "System does not support Suspend\n");
return -EINVAL;
}
dev_dbg(dev, "Enter hibernate\n");
regmap_write(regmap, CS35L41_WAKESRC_CTL, 0x0088);
regmap_write(regmap, CS35L41_WAKESRC_CTL, 0x0188);
// Don't wait for ACK since bus activity would wake the device
regmap_write(regmap, CS35L41_DSP_VIRT1_MBOX_1, CSPL_MBOX_CMD_HIBERNATE);
return 0;
}
EXPORT_SYMBOL_GPL(cs35l41_enter_hibernate);
static void cs35l41_wait_for_pwrmgt_sts(struct device *dev, struct regmap *regmap)
{
const int pwrmgt_retries = 10;
unsigned int sts;
int i, ret;
for (i = 0; i < pwrmgt_retries; i++) {
ret = regmap_read(regmap, CS35L41_PWRMGT_STS, &sts);
if (ret)
dev_err(dev, "Failed to read PWRMGT_STS: %d\n", ret);
else if (!(sts & CS35L41_WR_PEND_STS_MASK))
return;
udelay(20);
}
dev_err(dev, "Timed out reading PWRMGT_STS\n");
}
int cs35l41_exit_hibernate(struct device *dev, struct regmap *regmap)
{
const int wake_retries = 20;
const int sleep_retries = 5;
int ret, i, j;
for (i = 0; i < sleep_retries; i++) {
dev_dbg(dev, "Exit hibernate\n");
for (j = 0; j < wake_retries; j++) {
ret = cs35l41_set_cspl_mbox_cmd(dev, regmap,
CSPL_MBOX_CMD_OUT_OF_HIBERNATE);
if (!ret)
break;
usleep_range(100, 200);
}
if (j < wake_retries) {
dev_dbg(dev, "Wake success at cycle: %d\n", j);
return 0;
}
dev_err(dev, "Wake failed, re-enter hibernate: %d\n", ret);
cs35l41_wait_for_pwrmgt_sts(dev, regmap);
regmap_write(regmap, CS35L41_WAKESRC_CTL, 0x0088);
cs35l41_wait_for_pwrmgt_sts(dev, regmap);
regmap_write(regmap, CS35L41_WAKESRC_CTL, 0x0188);
cs35l41_wait_for_pwrmgt_sts(dev, regmap);
regmap_write(regmap, CS35L41_PWRMGT_CTL, 0x3);
}
dev_err(dev, "Timed out waking device\n");
return -ETIMEDOUT;
}
EXPORT_SYMBOL_GPL(cs35l41_exit_hibernate);
MODULE_DESCRIPTION("CS35L41 library"); MODULE_DESCRIPTION("CS35L41 library");
MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>"); MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>"); MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>");
......
...@@ -1335,15 +1335,7 @@ static int __maybe_unused cs35l41_runtime_suspend(struct device *dev) ...@@ -1335,15 +1335,7 @@ static int __maybe_unused cs35l41_runtime_suspend(struct device *dev)
if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running) if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running)
return 0; return 0;
dev_dbg(cs35l41->dev, "Enter hibernate\n"); cs35l41_enter_hibernate(dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type);
cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0088);
regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0188);
// Don't wait for ACK since bus activity would wake the device
regmap_write(cs35l41->regmap, CS35L41_DSP_VIRT1_MBOX_1,
CSPL_MBOX_CMD_HIBERNATE);
regcache_cache_only(cs35l41->regmap, true); regcache_cache_only(cs35l41->regmap, true);
regcache_mark_dirty(cs35l41->regmap); regcache_mark_dirty(cs35l41->regmap);
...@@ -1351,65 +1343,6 @@ static int __maybe_unused cs35l41_runtime_suspend(struct device *dev) ...@@ -1351,65 +1343,6 @@ static int __maybe_unused cs35l41_runtime_suspend(struct device *dev)
return 0; return 0;
} }
static void cs35l41_wait_for_pwrmgt_sts(struct cs35l41_private *cs35l41)
{
const int pwrmgt_retries = 10;
unsigned int sts;
int i, ret;
for (i = 0; i < pwrmgt_retries; i++) {
ret = regmap_read(cs35l41->regmap, CS35L41_PWRMGT_STS, &sts);
if (ret)
dev_err(cs35l41->dev, "Failed to read PWRMGT_STS: %d\n", ret);
else if (!(sts & CS35L41_WR_PEND_STS_MASK))
return;
udelay(20);
}
dev_err(cs35l41->dev, "Timed out reading PWRMGT_STS\n");
}
static int cs35l41_exit_hibernate(struct cs35l41_private *cs35l41)
{
const int wake_retries = 20;
const int sleep_retries = 5;
int ret, i, j;
for (i = 0; i < sleep_retries; i++) {
dev_dbg(cs35l41->dev, "Exit hibernate\n");
for (j = 0; j < wake_retries; j++) {
ret = cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap,
CSPL_MBOX_CMD_OUT_OF_HIBERNATE);
if (!ret)
break;
usleep_range(100, 200);
}
if (j < wake_retries) {
dev_dbg(cs35l41->dev, "Wake success at cycle: %d\n", j);
return 0;
}
dev_err(cs35l41->dev, "Wake failed, re-enter hibernate: %d\n", ret);
cs35l41_wait_for_pwrmgt_sts(cs35l41);
regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0088);
cs35l41_wait_for_pwrmgt_sts(cs35l41);
regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0188);
cs35l41_wait_for_pwrmgt_sts(cs35l41);
regmap_write(cs35l41->regmap, CS35L41_PWRMGT_CTL, 0x3);
}
dev_err(cs35l41->dev, "Timed out waking device\n");
return -ETIMEDOUT;
}
static int __maybe_unused cs35l41_runtime_resume(struct device *dev) static int __maybe_unused cs35l41_runtime_resume(struct device *dev)
{ {
struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
...@@ -1422,7 +1355,7 @@ static int __maybe_unused cs35l41_runtime_resume(struct device *dev) ...@@ -1422,7 +1355,7 @@ static int __maybe_unused cs35l41_runtime_resume(struct device *dev)
regcache_cache_only(cs35l41->regmap, false); regcache_cache_only(cs35l41->regmap, false);
ret = cs35l41_exit_hibernate(cs35l41); ret = cs35l41_exit_hibernate(cs35l41->dev, cs35l41->regmap);
if (ret) if (ret)
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment