From 6de6e7bc202964a7c7afaf2306decf156c57ecac Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 12 Dec 2025 18:55:05 +0800 Subject: [PATCH] soundwire: bus: add CLOCK_STOP_MODE1 support back CLOCK_STOP_MODE1 is used when the Peripheral might have entered a deeper power-saving mode that does not retain state while the Clock is stopped. It is useful when the device is more power consumption sensitive. Add it back to allow the Peripheral use CLOCK_STOP_MODE1. Signed-off-by: Bard Liao --- drivers/soundwire/bus.c | 49 ++++++++++++++++++++++++++++------- include/linux/soundwire/sdw.h | 2 ++ 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index fb68738dfb9b84..672179df10dece 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -956,6 +956,28 @@ static void sdw_modify_slave_status(struct sdw_slave *slave, mutex_unlock(&bus->bus_lock); } +static enum sdw_clk_stop_mode sdw_get_clk_stop_mode(struct sdw_slave *slave) +{ + struct device *dev = &slave->dev; + struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); + enum sdw_clk_stop_mode mode; + + /* + * Query for clock stop mode if Slave implements + * ops->get_clk_stop_mode, else read from property. + */ + if (drv->ops && drv->ops->get_clk_stop_mode) { + mode = drv->ops->get_clk_stop_mode(slave); + } else { + if (slave->prop.clk_stop_mode1) + mode = SDW_CLK_STOP_MODE1; + else + mode = SDW_CLK_STOP_MODE0; + } + + return mode; +} + static int sdw_slave_clk_stop_callback(struct sdw_slave *slave, enum sdw_clk_stop_mode mode, enum sdw_clk_stop_type type) @@ -1054,6 +1076,7 @@ static int sdw_bus_wait_for_clk_prep_deprep(struct sdw_bus *bus, u16 dev_num, bo */ int sdw_bus_prep_clk_stop(struct sdw_bus *bus) { + enum sdw_clk_stop_mode mode; bool simple_clk_stop = true; struct sdw_slave *slave; bool is_slave = false; @@ -1078,8 +1101,10 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus) /* Identify if Slave(s) are available on Bus */ is_slave = true; - ret = sdw_slave_clk_stop_callback(slave, - SDW_CLK_STOP_MODE0, + mode = sdw_get_clk_stop_mode(slave); + slave->curr_clk_stop_mode = mode; + + ret = sdw_slave_clk_stop_callback(slave, mode, SDW_CLK_PRE_PREPARE); if (ret < 0 && ret != -ENODATA) { dev_err(&slave->dev, "clock stop pre-prepare cb failed:%d\n", ret); @@ -1091,8 +1116,7 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus) simple_clk_stop = false; ret = sdw_slave_clk_stop_prepare(slave, - SDW_CLK_STOP_MODE0, - true); + mode, true); if (ret < 0 && ret != -ENODATA) { dev_err(&slave->dev, "clock stop prepare failed:%d\n", ret); return ret; @@ -1129,9 +1153,9 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus) if (slave->status != SDW_SLAVE_ATTACHED && slave->status != SDW_SLAVE_ALERT) continue; + mode = slave->curr_clk_stop_mode; - ret = sdw_slave_clk_stop_callback(slave, - SDW_CLK_STOP_MODE0, + ret = sdw_slave_clk_stop_callback(slave, mode, SDW_CLK_POST_PREPARE); if (ret < 0 && ret != -ENODATA) { @@ -1183,6 +1207,7 @@ EXPORT_SYMBOL(sdw_bus_clk_stop); */ int sdw_bus_exit_clk_stop(struct sdw_bus *bus) { + enum sdw_clk_stop_mode mode; bool simple_clk_stop = true; struct sdw_slave *slave; bool is_slave = false; @@ -1204,18 +1229,20 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus) /* Identify if Slave(s) are available on Bus */ is_slave = true; - ret = sdw_slave_clk_stop_callback(slave, SDW_CLK_STOP_MODE0, + mode = slave->curr_clk_stop_mode; + + ret = sdw_slave_clk_stop_callback(slave, mode, SDW_CLK_PRE_DEPREPARE); if (ret < 0) dev_warn(&slave->dev, "clock stop pre-deprepare cb failed:%d\n", ret); + /* Only de-prepare a Slave device if needed */ if (!slave->prop.simple_clk_stop_capable) { simple_clk_stop = false; - ret = sdw_slave_clk_stop_prepare(slave, SDW_CLK_STOP_MODE0, + ret = sdw_slave_clk_stop_prepare(slave, mode, false); - if (ret < 0) dev_warn(&slave->dev, "clock stop deprepare failed:%d\n", ret); } @@ -1243,7 +1270,9 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus) slave->status != SDW_SLAVE_ALERT) continue; - ret = sdw_slave_clk_stop_callback(slave, SDW_CLK_STOP_MODE0, + mode = slave->curr_clk_stop_mode; + + ret = sdw_slave_clk_stop_callback(slave, mode, SDW_CLK_POST_DEPREPARE); if (ret < 0) dev_warn(&slave->dev, "clock stop post-deprepare cb failed:%d\n", ret); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index e6a3476bcef1ae..bbae2510cbfe49 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -624,6 +624,7 @@ struct sdw_slave_ops { int (*port_prep)(struct sdw_slave *slave, struct sdw_prepare_ch *prepare_ch, enum sdw_port_prep_ops pre_ops); + int (*get_clk_stop_mode)(struct sdw_slave *slave); int (*clk_stop)(struct sdw_slave *slave, enum sdw_clk_stop_mode mode, enum sdw_clk_stop_type type); @@ -676,6 +677,7 @@ struct sdw_slave { struct list_head node; struct completion port_ready[SDW_MAX_PORTS]; unsigned int m_port_map[SDW_MAX_PORTS]; + enum sdw_clk_stop_mode curr_clk_stop_mode; u16 dev_num; u16 dev_num_sticky; bool probed;