Skip to content

Commit 4886310

Browse files
rfvirgilbroonie
authored andcommitted
ASoC: cs35l56: Support clock stop mode 1 if enabled in ACPI
Report the ability to support SoundWire clock-stop mode 1 if this is enabled in ACPI. Mode 1 allows the device to lose state, so it can reduce power consumption in clock-stop. Also add the necessary handling to wait for re-enumeration on resume. This does not use sdw_slave_read_prop(), because that also fills in other properties from ACPI that were not previously set by the driver and this has been observed to break some systems. Instead, the "mipi-sdw-clock-stop-mode1-supported" property is checked directly. When a SoundWire peripheral has been put into clock-stop mode 1 it must be re-enumerated after the clock is restarted. A new flag sdw_in_clock_stop_1 is set to true in cs35l56_sdw_clk_stop() if the SoundWire core notifies that it is entering clock-stop 1. cs35l56_sdw_handle_unattach() will wait for re-enumeration if sdw_in_clock_stop_1 is true. sdw_in_clock_stop_1 will be reset to false when an ATTACH notification is received in cs35l56_sdw_update_status(). Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com> Link: https://patch.msgid.link/20260311142153.2201761-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent 94ef278 commit 4886310

2 files changed

Lines changed: 30 additions & 5 deletions

File tree

sound/soc/codecs/cs35l56-sdw.c

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/soundwire/sdw.h>
1515
#include <linux/soundwire/sdw_registers.h>
1616
#include <linux/soundwire/sdw_type.h>
17+
#include <linux/string_choices.h>
1718
#include <linux/swab.h>
1819
#include <linux/types.h>
1920
#include <linux/workqueue.h>
@@ -340,6 +341,14 @@ static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral)
340341
struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
341342
struct sdw_slave_prop *prop = &peripheral->prop;
342343
struct sdw_dpn_prop *ports;
344+
u8 clock_stop_1 = false;
345+
int ret;
346+
347+
ret = fwnode_property_read_u8(dev_fwnode(cs35l56->base.dev),
348+
"mipi-sdw-clock-stop-mode1-supported",
349+
&clock_stop_1);
350+
if (ret == 0)
351+
prop->clk_stop_mode1 = !!clock_stop_1;
343352

344353
ports = devm_kcalloc(cs35l56->base.dev, 2, sizeof(*ports), GFP_KERNEL);
345354
if (!ports)
@@ -363,6 +372,9 @@ static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral)
363372
ports[1].ch_prep_timeout = 10;
364373
prop->src_dpn_prop = &ports[1];
365374

375+
dev_dbg(&peripheral->dev, "clock stop mode 1 supported: %s\n",
376+
str_yes_no(prop->clk_stop_mode1));
377+
366378
return 0;
367379
}
368380

@@ -374,6 +386,7 @@ static int cs35l56_sdw_update_status(struct sdw_slave *peripheral,
374386
switch (status) {
375387
case SDW_SLAVE_ATTACHED:
376388
dev_dbg(cs35l56->base.dev, "%s: ATTACHED\n", __func__);
389+
cs35l56->sdw_in_clock_stop_1 = false;
377390
if (cs35l56->sdw_attached)
378391
break;
379392

@@ -399,25 +412,35 @@ static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral,
399412
{
400413
struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
401414

402-
dev_dbg(cs35l56->base.dev, "%s: mode:%d type:%d\n", __func__, mode, type);
415+
dev_dbg(cs35l56->base.dev, "%s: clock_stop_mode%d stage:%d\n", __func__, mode, type);
403416

404-
return 0;
417+
switch (type) {
418+
case SDW_CLK_POST_PREPARE:
419+
if (mode == SDW_CLK_STOP_MODE1)
420+
cs35l56->sdw_in_clock_stop_1 = true;
421+
else
422+
cs35l56->sdw_in_clock_stop_1 = false;
423+
return 0;
424+
default:
425+
return 0;
426+
}
405427
}
406428

407429
static const struct sdw_slave_ops cs35l56_sdw_ops = {
408430
.read_prop = cs35l56_sdw_read_prop,
409431
.interrupt_callback = cs35l56_sdw_interrupt,
410432
.update_status = cs35l56_sdw_update_status,
411-
#ifdef DEBUG
412433
.clk_stop = cs35l56_sdw_clk_stop,
413-
#endif
414434
};
415435

416436
static int __maybe_unused cs35l56_sdw_handle_unattach(struct cs35l56_private *cs35l56)
417437
{
418438
struct sdw_slave *peripheral = cs35l56->sdw_peripheral;
419439

420-
if (peripheral->unattach_request) {
440+
dev_dbg(cs35l56->base.dev, "attached:%u unattach_request:%u in_clock_stop_1:%u\n",
441+
cs35l56->sdw_attached, peripheral->unattach_request, cs35l56->sdw_in_clock_stop_1);
442+
443+
if (cs35l56->sdw_in_clock_stop_1 || peripheral->unattach_request) {
421444
/* Cannot access registers until bus is re-initialized. */
422445
dev_dbg(cs35l56->base.dev, "Wait for initialization_complete\n");
423446
if (!wait_for_completion_timeout(&peripheral->initialization_complete,
@@ -427,6 +450,7 @@ static int __maybe_unused cs35l56_sdw_handle_unattach(struct cs35l56_private *cs
427450
}
428451

429452
peripheral->unattach_request = 0;
453+
cs35l56->sdw_in_clock_stop_1 = false;
430454

431455
/*
432456
* Don't call regcache_mark_dirty(), we can't be sure that the

sound/soc/codecs/cs35l56.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ struct cs35l56_private {
4242
bool sdw_irq_no_unmask;
4343
bool soft_resetting;
4444
bool sdw_attached;
45+
bool sdw_in_clock_stop_1;
4546
struct completion init_completion;
4647

4748
int speaker_id;

0 commit comments

Comments
 (0)