Skip to content

Commit 6c07be8

Browse files
Vlad.Karpovichbroonie
authored andcommitted
ASoC: cs35l45: Hibernation support
Adds support for a low-power Hibernation State. Add support for a low-power hibernation state for the DSP. In this state the DSP RAM contents are maintained, such that firmware does not need to be re-downloaded, but the rest of the chip's register state is lost. Entry to this state is achieved via the register interface (either by an external driver using the control port, or the programmable DSP). Exit from this state is triggered by activity on device GPIO pins, intended SPI transaction, or I2C transaction with intended slave address. Signed-off-by: Vlad Karpovich <vkarpovi@opensource.cirrus.com> Link: https://lore.kernel.org/r/167933511185.26.10641185496218226278@mailman-core.alsa-project.org Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent 74b14e2 commit 6c07be8

File tree

5 files changed

+118
-0
lines changed

5 files changed

+118
-0
lines changed

sound/soc/codecs/cs35l45-i2c.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ static int cs35l45_i2c_probe(struct i2c_client *client)
3333

3434
cs35l45->dev = dev;
3535
cs35l45->irq = client->irq;
36+
cs35l45->bus_type = CONTROL_BUS_I2C;
37+
cs35l45->i2c_addr = client->addr;
3638

3739
return cs35l45_probe(cs35l45);
3840
}

sound/soc/codecs/cs35l45-spi.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ static int cs35l45_spi_probe(struct spi_device *spi)
3636

3737
cs35l45->dev = dev;
3838
cs35l45->irq = spi->irq;
39+
cs35l45->bus_type = CONTROL_BUS_SPI;
3940

4041
return cs35l45_probe(cs35l45);
4142
}

sound/soc/codecs/cs35l45-tables.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ static const struct reg_default cs35l45_defaults[] = {
4747
{ CS35L45_INTB_GPIO2_MCLK_REF, 0x00000005 },
4848
{ CS35L45_GPIO3, 0x00000005 },
4949
{ CS35L45_PWRMGT_CTL, 0x00000000 },
50+
{ CS35L45_WAKESRC_CTL, 0x00000008 },
51+
{ CS35L45_WKI2C_CTL, 0x00000030 },
5052
{ CS35L45_REFCLK_INPUT, 0x00000510 },
5153
{ CS35L45_GLOBAL_SAMPLE_RATE, 0x00000003 },
5254
{ CS35L45_ASP_ENABLES1, 0x00000000 },
@@ -126,6 +128,9 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
126128
case CS35L45_INTB_GPIO2_MCLK_REF:
127129
case CS35L45_GPIO3:
128130
case CS35L45_PWRMGT_CTL:
131+
case CS35L45_WAKESRC_CTL:
132+
case CS35L45_WKI2C_CTL:
133+
case CS35L45_PWRMGT_STS:
129134
case CS35L45_REFCLK_INPUT:
130135
case CS35L45_GLOBAL_SAMPLE_RATE:
131136
case CS35L45_ASP_ENABLES1:
@@ -210,6 +215,7 @@ static bool cs35l45_volatile_reg(struct device *dev, unsigned int reg)
210215
case CS35L45_GLOBAL_ENABLES:
211216
case CS35L45_ERROR_RELEASE:
212217
case CS35L45_AMP_PCM_HPF_TST: /* not cachable */
218+
case CS35L45_PWRMGT_STS:
213219
case CS35L45_IRQ1_STATUS:
214220
case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18:
215221
case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18:

sound/soc/codecs/cs35l45.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ static bool cs35l45_check_cspl_mbox_sts(const enum cs35l45_cspl_mboxcmd cmd,
3636
return (sts == CSPL_MBOX_STS_RUNNING);
3737
case CSPL_MBOX_CMD_STOP_PRE_REINIT:
3838
return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT);
39+
case CSPL_MBOX_CMD_HIBERNATE:
40+
return (sts == CSPL_MBOX_STS_HIBERNATE);
3941
default:
4042
return false;
4143
}
@@ -744,11 +746,81 @@ static const struct snd_soc_component_driver cs35l45_component = {
744746
.endianness = 1,
745747
};
746748

749+
static void cs35l45_setup_hibernate(struct cs35l45_private *cs35l45)
750+
{
751+
unsigned int wksrc;
752+
753+
if (cs35l45->bus_type == CONTROL_BUS_I2C)
754+
wksrc = CS35L45_WKSRC_I2C;
755+
else
756+
wksrc = CS35L45_WKSRC_SPI;
757+
758+
regmap_update_bits(cs35l45->regmap, CS35L45_WAKESRC_CTL,
759+
CS35L45_WKSRC_EN_MASK,
760+
wksrc << CS35L45_WKSRC_EN_SHIFT);
761+
762+
regmap_set_bits(cs35l45->regmap, CS35L45_WAKESRC_CTL,
763+
CS35L45_UPDT_WKCTL_MASK);
764+
765+
regmap_update_bits(cs35l45->regmap, CS35L45_WKI2C_CTL,
766+
CS35L45_WKI2C_ADDR_MASK, cs35l45->i2c_addr);
767+
768+
regmap_set_bits(cs35l45->regmap, CS35L45_WKI2C_CTL,
769+
CS35L45_UPDT_WKI2C_MASK);
770+
}
771+
772+
static int cs35l45_enter_hibernate(struct cs35l45_private *cs35l45)
773+
{
774+
dev_dbg(cs35l45->dev, "Enter hibernate\n");
775+
776+
cs35l45_setup_hibernate(cs35l45);
777+
778+
// Don't wait for ACK since bus activity would wake the device
779+
regmap_write(cs35l45->regmap, CS35L45_DSP_VIRT1_MBOX_1, CSPL_MBOX_CMD_HIBERNATE);
780+
781+
return 0;
782+
}
783+
784+
static int cs35l45_exit_hibernate(struct cs35l45_private *cs35l45)
785+
{
786+
const int wake_retries = 20;
787+
const int sleep_retries = 5;
788+
int ret, i, j;
789+
790+
for (i = 0; i < sleep_retries; i++) {
791+
dev_dbg(cs35l45->dev, "Exit hibernate\n");
792+
793+
for (j = 0; j < wake_retries; j++) {
794+
ret = cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap,
795+
CSPL_MBOX_CMD_OUT_OF_HIBERNATE);
796+
if (!ret) {
797+
dev_dbg(cs35l45->dev, "Wake success at cycle: %d\n", j);
798+
return 0;
799+
}
800+
usleep_range(100, 200);
801+
}
802+
803+
dev_err(cs35l45->dev, "Wake failed, re-enter hibernate: %d\n", ret);
804+
805+
cs35l45_setup_hibernate(cs35l45);
806+
}
807+
808+
dev_err(cs35l45->dev, "Timed out waking device\n");
809+
810+
return -ETIMEDOUT;
811+
}
812+
747813
static int __maybe_unused cs35l45_runtime_suspend(struct device *dev)
748814
{
749815
struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
750816

817+
if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running)
818+
return 0;
819+
820+
cs35l45_enter_hibernate(cs35l45);
821+
751822
regcache_cache_only(cs35l45->regmap, true);
823+
regcache_mark_dirty(cs35l45->regmap);
752824

753825
dev_dbg(cs35l45->dev, "Runtime suspended\n");
754826

@@ -760,9 +832,17 @@ static int __maybe_unused cs35l45_runtime_resume(struct device *dev)
760832
struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
761833
int ret;
762834

835+
if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running)
836+
return 0;
837+
763838
dev_dbg(cs35l45->dev, "Runtime resume\n");
764839

765840
regcache_cache_only(cs35l45->regmap, false);
841+
842+
ret = cs35l45_exit_hibernate(cs35l45);
843+
if (ret)
844+
return ret;
845+
766846
ret = regcache_sync(cs35l45->regmap);
767847
if (ret != 0)
768848
dev_warn(cs35l45->dev, "regcache_sync failed: %d\n", ret);

sound/soc/codecs/cs35l45.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
#define CS35L45_INTB_GPIO2_MCLK_REF 0x00002434
3131
#define CS35L45_GPIO3 0x00002438
3232
#define CS35L45_PWRMGT_CTL 0x00002900
33+
#define CS35L45_WAKESRC_CTL 0x00002904
34+
#define CS35L45_WKI2C_CTL 0x00002908
35+
#define CS35L45_PWRMGT_STS 0x0000290C
3336
#define CS35L45_REFCLK_INPUT 0x00002C04
3437
#define CS35L45_GLOBAL_SAMPLE_RATE 0x00002C0C
3538
#define CS35L45_BOOST_CCM_CFG 0x00003808
@@ -348,6 +351,25 @@
348351
#define CS35L45_POST_GLOBAL_EN_US 5000
349352
#define CS35L45_PRE_GLOBAL_DIS_US 3000
350353

354+
/* WAKESRC_CTL */
355+
#define CS35L45_WKSRC_SYNC_GPIO1 BIT(0)
356+
#define CS35L45_WKSRC_INT_GPIO2 BIT(1)
357+
#define CS35L45_WKSRC_GPIO3 BIT(2)
358+
#define CS35L45_WKSRC_SPI BIT(3)
359+
#define CS35L45_WKSRC_I2C BIT(4)
360+
#define CS35L45_UPDT_WKCTL_SHIFT 15
361+
#define CS35L45_UPDT_WKCTL_MASK BIT(15)
362+
#define CS35L45_WKSRC_EN_SHIFT 8
363+
#define CS35L45_WKSRC_EN_MASK GENMASK(12, 8)
364+
#define CS35L45_WKSRC_POL_SHIFT 0
365+
#define CS35L45_WKSRC_POL_MASK GENMASK(3, 0)
366+
367+
/* WAKEI2C_CTL */
368+
#define CS35L45_UPDT_WKI2C_SHIFT 15
369+
#define CS35L45_UPDT_WKI2C_MASK BIT(15)
370+
#define CS35L45_WKI2C_ADDR_SHIFT 0
371+
#define CS35L45_WKI2C_ADDR_MASK GENMASK(6, 0)
372+
351373
#define CS35L45_SPI_MAX_FREQ 4000000
352374

353375
enum cs35l45_cspl_mboxstate {
@@ -369,6 +391,11 @@ enum cs35l45_cspl_mboxcmd {
369391
CSPL_MBOX_CMD_INVALID_SEQUENCE = -2,
370392
};
371393

394+
enum control_bus_type {
395+
CONTROL_BUS_I2C = 0,
396+
CONTROL_BUS_SPI = 1,
397+
};
398+
372399
#define CS35L45_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
373400
SNDRV_PCM_FMTBIT_S24_3LE| \
374401
SNDRV_PCM_FMTBIT_S24_LE)
@@ -439,6 +466,8 @@ struct cs35l45_private {
439466
u8 slot_count;
440467
int irq_invert;
441468
int irq;
469+
unsigned int i2c_addr;
470+
enum control_bus_type bus_type;
442471
struct regmap_irq_chip_data *irq_data;
443472
};
444473

0 commit comments

Comments
 (0)