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;