@@ -33,6 +33,8 @@ static int cs35l45_i2c_probe(struct i2c_client *client)
cs35l45->dev = dev;
cs35l45->irq = client->irq;
+ cs35l45->bus_type = CONTROL_BUS_I2C;
+ cs35l45->i2c_addr = client->addr;
return cs35l45_probe(cs35l45);
}
@@ -36,6 +36,7 @@ static int cs35l45_spi_probe(struct spi_device *spi)
cs35l45->dev = dev;
cs35l45->irq = spi->irq;
+ cs35l45->bus_type = CONTROL_BUS_SPI;
return cs35l45_probe(cs35l45);
}
@@ -47,6 +47,8 @@ static const struct reg_default cs35l45_defaults[] = {
{ CS35L45_INTB_GPIO2_MCLK_REF, 0x00000005 },
{ CS35L45_GPIO3, 0x00000005 },
{ CS35L45_PWRMGT_CTL, 0x00000000 },
+ { CS35L45_WAKESRC_CTL, 0x00000008 },
+ { CS35L45_WKI2C_CTL, 0x00000030 },
{ CS35L45_REFCLK_INPUT, 0x00000510 },
{ CS35L45_GLOBAL_SAMPLE_RATE, 0x00000003 },
{ CS35L45_ASP_ENABLES1, 0x00000000 },
@@ -126,6 +128,9 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
case CS35L45_INTB_GPIO2_MCLK_REF:
case CS35L45_GPIO3:
case CS35L45_PWRMGT_CTL:
+ case CS35L45_WAKESRC_CTL:
+ case CS35L45_WKI2C_CTL:
+ case CS35L45_PWRMGT_STS:
case CS35L45_REFCLK_INPUT:
case CS35L45_GLOBAL_SAMPLE_RATE:
case CS35L45_ASP_ENABLES1:
@@ -210,6 +215,7 @@ static bool cs35l45_volatile_reg(struct device *dev, unsigned int reg)
case CS35L45_GLOBAL_ENABLES:
case CS35L45_ERROR_RELEASE:
case CS35L45_AMP_PCM_HPF_TST: /* not cachable */
+ case CS35L45_PWRMGT_STS:
case CS35L45_IRQ1_STATUS:
case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18:
case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18:
@@ -36,6 +36,8 @@ static bool cs35l45_check_cspl_mbox_sts(const enum cs35l45_cspl_mboxcmd cmd,
return (sts == CSPL_MBOX_STS_RUNNING);
case CSPL_MBOX_CMD_STOP_PRE_REINIT:
return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT);
+ case CSPL_MBOX_CMD_HIBERNATE:
+ return (sts == CSPL_MBOX_STS_HIBERNATE);
default:
return false;
}
@@ -744,11 +746,81 @@ static const struct snd_soc_component_driver cs35l45_component = {
.endianness = 1,
};
+static void cs35l45_setup_hibernate(struct cs35l45_private *cs35l45)
+{
+ unsigned int wksrc;
+
+ if (cs35l45->bus_type == CONTROL_BUS_I2C)
+ wksrc = CS35L45_WKSRC_I2C;
+ else
+ wksrc = CS35L45_WKSRC_SPI;
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_WAKESRC_CTL,
+ CS35L45_WKSRC_EN_MASK,
+ wksrc << CS35L45_WKSRC_EN_SHIFT);
+
+ regmap_set_bits(cs35l45->regmap, CS35L45_WAKESRC_CTL,
+ CS35L45_UPDT_WKCTL_MASK);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_WKI2C_CTL,
+ CS35L45_WKI2C_ADDR_MASK, cs35l45->i2c_addr);
+
+ regmap_set_bits(cs35l45->regmap, CS35L45_WKI2C_CTL,
+ CS35L45_UPDT_WKI2C_MASK);
+}
+
+static int cs35l45_enter_hibernate(struct cs35l45_private *cs35l45)
+{
+ dev_dbg(cs35l45->dev, "Enter hibernate\n");
+
+ cs35l45_setup_hibernate(cs35l45);
+
+ // Don't wait for ACK since bus activity would wake the device
+ regmap_write(cs35l45->regmap, CS35L45_DSP_VIRT1_MBOX_1, CSPL_MBOX_CMD_HIBERNATE);
+
+ return 0;
+}
+
+static int cs35l45_exit_hibernate(struct cs35l45_private *cs35l45)
+{
+ const int wake_retries = 20;
+ const int sleep_retries = 5;
+ int ret, i, j;
+
+ for (i = 0; i < sleep_retries; i++) {
+ dev_dbg(cs35l45->dev, "Exit hibernate\n");
+
+ for (j = 0; j < wake_retries; j++) {
+ ret = cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap,
+ CSPL_MBOX_CMD_OUT_OF_HIBERNATE);
+ if (!ret) {
+ dev_dbg(cs35l45->dev, "Wake success at cycle: %d\n", j);
+ return 0;
+ }
+ usleep_range(100, 200);
+ }
+
+ dev_err(cs35l45->dev, "Wake failed, re-enter hibernate: %d\n", ret);
+
+ cs35l45_setup_hibernate(cs35l45);
+ }
+
+ dev_err(cs35l45->dev, "Timed out waking device\n");
+
+ return -ETIMEDOUT;
+}
+
static int __maybe_unused cs35l45_runtime_suspend(struct device *dev)
{
struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+ if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running)
+ return 0;
+
+ cs35l45_enter_hibernate(cs35l45);
+
regcache_cache_only(cs35l45->regmap, true);
+ regcache_mark_dirty(cs35l45->regmap);
dev_dbg(cs35l45->dev, "Runtime suspended\n");
@@ -760,9 +832,17 @@ static int __maybe_unused cs35l45_runtime_resume(struct device *dev)
struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
int ret;
+ if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running)
+ return 0;
+
dev_dbg(cs35l45->dev, "Runtime resume\n");
regcache_cache_only(cs35l45->regmap, false);
+
+ ret = cs35l45_exit_hibernate(cs35l45);
+ if (ret)
+ return ret;
+
ret = regcache_sync(cs35l45->regmap);
if (ret != 0)
dev_warn(cs35l45->dev, "regcache_sync failed: %d\n", ret);
@@ -30,6 +30,9 @@
#define CS35L45_INTB_GPIO2_MCLK_REF 0x00002434
#define CS35L45_GPIO3 0x00002438
#define CS35L45_PWRMGT_CTL 0x00002900
+#define CS35L45_WAKESRC_CTL 0x00002904
+#define CS35L45_WKI2C_CTL 0x00002908
+#define CS35L45_PWRMGT_STS 0x0000290C
#define CS35L45_REFCLK_INPUT 0x00002C04
#define CS35L45_GLOBAL_SAMPLE_RATE 0x00002C0C
#define CS35L45_BOOST_CCM_CFG 0x00003808
@@ -348,6 +351,25 @@
#define CS35L45_POST_GLOBAL_EN_US 5000
#define CS35L45_PRE_GLOBAL_DIS_US 3000
+/* WAKESRC_CTL */
+#define CS35L45_WKSRC_SYNC_GPIO1 BIT(0)
+#define CS35L45_WKSRC_INT_GPIO2 BIT(1)
+#define CS35L45_WKSRC_GPIO3 BIT(2)
+#define CS35L45_WKSRC_SPI BIT(3)
+#define CS35L45_WKSRC_I2C BIT(4)
+#define CS35L45_UPDT_WKCTL_SHIFT 15
+#define CS35L45_UPDT_WKCTL_MASK BIT(15)
+#define CS35L45_WKSRC_EN_SHIFT 8
+#define CS35L45_WKSRC_EN_MASK GENMASK(12, 8)
+#define CS35L45_WKSRC_POL_SHIFT 0
+#define CS35L45_WKSRC_POL_MASK GENMASK(3, 0)
+
+/* WAKEI2C_CTL */
+#define CS35L45_UPDT_WKI2C_SHIFT 15
+#define CS35L45_UPDT_WKI2C_MASK BIT(15)
+#define CS35L45_WKI2C_ADDR_SHIFT 0
+#define CS35L45_WKI2C_ADDR_MASK GENMASK(6, 0)
+
#define CS35L45_SPI_MAX_FREQ 4000000
enum cs35l45_cspl_mboxstate {
@@ -369,6 +391,11 @@ enum cs35l45_cspl_mboxcmd {
CSPL_MBOX_CMD_INVALID_SEQUENCE = -2,
};
+enum control_bus_type {
+ CONTROL_BUS_I2C = 0,
+ CONTROL_BUS_SPI = 1,
+};
+
#define CS35L45_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_3LE| \
SNDRV_PCM_FMTBIT_S24_LE)
@@ -439,6 +466,8 @@ struct cs35l45_private {
u8 slot_count;
int irq_invert;
int irq;
+ unsigned int i2c_addr;
+ enum control_bus_type bus_type;
struct regmap_irq_chip_data *irq_data;
};