diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 12087cbd87b7..aeaae5a57c89 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -958,8 +958,31 @@ 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) +{ + enum sdw_clk_stop_mode mode; + + mode = slave->prop.clk_stop_mode1 ? SDW_CLK_STOP_MODE1 : SDW_CLK_STOP_MODE0; + + mutex_lock(&slave->sdw_dev_lock); + + /* + * Query for clock stop mode if Slave implements + * ops->get_clk_stop_mode, else read from property. + */ + if (slave->probed) { + struct sdw_driver *drv = drv_to_sdw_driver(slave->dev.driver); + + if (drv->ops && drv->ops->get_clk_stop_mode) + mode = drv->ops->get_clk_stop_mode(slave); + } + + mutex_unlock(&slave->sdw_dev_lock); + + 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) { int ret = 0; @@ -971,7 +994,7 @@ static int sdw_slave_clk_stop_callback(struct sdw_slave *slave, struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); if (drv->ops && drv->ops->clk_stop) - ret = drv->ops->clk_stop(slave, mode, type); + ret = drv->ops->clk_stop(slave, slave->clk_stop_mode, type); } mutex_unlock(&slave->sdw_dev_lock); @@ -980,7 +1003,6 @@ static int sdw_slave_clk_stop_callback(struct sdw_slave *slave, } static int sdw_slave_clk_stop_prepare(struct sdw_slave *slave, - enum sdw_clk_stop_mode mode, bool prepare) { bool wake_en; @@ -992,7 +1014,7 @@ static int sdw_slave_clk_stop_prepare(struct sdw_slave *slave, if (prepare) { val = SDW_SCP_SYSTEMCTRL_CLK_STP_PREP; - if (mode == SDW_CLK_STOP_MODE1) + if (slave->clk_stop_mode == SDW_CLK_STOP_MODE1) val |= SDW_SCP_SYSTEMCTRL_CLK_STP_MODE1; if (wake_en) @@ -1080,9 +1102,9 @@ 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, - SDW_CLK_PRE_PREPARE); + slave->clk_stop_mode = sdw_get_clk_stop_mode(slave); + + ret = sdw_slave_clk_stop_callback(slave, SDW_CLK_PRE_PREPARE); if (ret < 0 && ret != -ENODATA) { dev_err(&slave->dev, "clock stop pre-prepare cb failed:%d\n", ret); return ret; @@ -1092,9 +1114,7 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus) if (!slave->prop.simple_clk_stop_capable) { simple_clk_stop = false; - ret = sdw_slave_clk_stop_prepare(slave, - SDW_CLK_STOP_MODE0, - true); + ret = sdw_slave_clk_stop_prepare(slave, true); if (ret < 0 && ret != -ENODATA) { dev_err(&slave->dev, "clock stop prepare failed:%d\n", ret); return ret; @@ -1132,9 +1152,7 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus) slave->status != SDW_SLAVE_ALERT) continue; - ret = sdw_slave_clk_stop_callback(slave, - SDW_CLK_STOP_MODE0, - SDW_CLK_POST_PREPARE); + ret = sdw_slave_clk_stop_callback(slave, SDW_CLK_POST_PREPARE); if (ret < 0 && ret != -ENODATA) { dev_err(&slave->dev, "clock stop post-prepare cb failed:%d\n", ret); @@ -1206,8 +1224,7 @@ 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, - SDW_CLK_PRE_DEPREPARE); + ret = sdw_slave_clk_stop_callback(slave, SDW_CLK_PRE_DEPREPARE); if (ret < 0) dev_warn(&slave->dev, "clock stop pre-deprepare cb failed:%d\n", ret); @@ -1215,9 +1232,7 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus) if (!slave->prop.simple_clk_stop_capable) { simple_clk_stop = false; - ret = sdw_slave_clk_stop_prepare(slave, SDW_CLK_STOP_MODE0, - false); - + ret = sdw_slave_clk_stop_prepare(slave, false); if (ret < 0) dev_warn(&slave->dev, "clock stop deprepare failed:%d\n", ret); } @@ -1245,8 +1260,7 @@ 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, - SDW_CLK_POST_DEPREPARE); + ret = sdw_slave_clk_stop_callback(slave, 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 707ca6bd1e23..f710e5932b4b 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -618,6 +618,7 @@ struct sdw_bus_params { * @update_status: Update Slave status * @bus_config: Update the bus config for Slave * @port_prep: Prepare the port with parameters + * @get_clk_stop_mode: Get the clock stop mode of the Slave * @clk_stop: handle imp-def sequences before and after prepare and de-prepare */ struct sdw_slave_ops { @@ -631,6 +632,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); + enum sdw_clk_stop_mode (*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); @@ -649,6 +651,7 @@ struct sdw_slave_ops { * @node: node for bus list * @port_ready: Port ready completion flag for each Slave port * @m_port_map: static Master port map for each Slave port + * @clk_stop_mode: The clock stop mode of the Slave * @dev_num: Current Device Number, values can be 0 or dev_num_sticky * @dev_num_sticky: one-time static Device Number assigned by Bus * @probed: boolean tracking driver state @@ -683,6 +686,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 clk_stop_mode; u16 dev_num; u16 dev_num_sticky; bool probed;