Commit ce6e74d0 authored by Shreyas NC's avatar Shreyas NC Committed by Vinod Koul

soundwire: Add support for multi link bank switch

In cases of multiple Masters in a stream, synchronization
between multiple Master(s) is achieved by performing bank switch
together and using Master methods.

Add sdw_ml_bank_switch() to wait for completion of bank switch.
Signed-off-by: default avatarSanyog Kale <sanyog.r.kale@intel.com>
Signed-off-by: default avatarShreyas NC <shreyas.nc@intel.com>
Acked-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: default avatarVinod Koul <vkoul@kernel.org>
parent 48949722
...@@ -35,6 +35,11 @@ int sdw_add_bus_master(struct sdw_bus *bus) ...@@ -35,6 +35,11 @@ int sdw_add_bus_master(struct sdw_bus *bus)
INIT_LIST_HEAD(&bus->slaves); INIT_LIST_HEAD(&bus->slaves);
INIT_LIST_HEAD(&bus->m_rt_list); INIT_LIST_HEAD(&bus->m_rt_list);
/*
* Initialize multi_link flag
* TODO: populate this flag by reading property from FW node
*/
bus->multi_link = false;
if (bus->ops->read_prop) { if (bus->ops->read_prop) {
ret = bus->ops->read_prop(bus); ret = bus->ops->read_prop(bus);
if (ret < 0) { if (ret < 0) {
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#ifndef __SDW_BUS_H #ifndef __SDW_BUS_H
#define __SDW_BUS_H #define __SDW_BUS_H
#define DEFAULT_BANK_SWITCH_TIMEOUT 3000
#if IS_ENABLED(CONFIG_ACPI) #if IS_ENABLED(CONFIG_ACPI)
int sdw_acpi_find_slaves(struct sdw_bus *bus); int sdw_acpi_find_slaves(struct sdw_bus *bus);
#else #else
......
...@@ -626,9 +626,10 @@ static int sdw_program_params(struct sdw_bus *bus) ...@@ -626,9 +626,10 @@ static int sdw_program_params(struct sdw_bus *bus)
return ret; return ret;
} }
static int sdw_bank_switch(struct sdw_bus *bus) static int sdw_bank_switch(struct sdw_bus *bus, int m_rt_count)
{ {
int col_index, row_index; int col_index, row_index;
bool multi_link;
struct sdw_msg *wr_msg; struct sdw_msg *wr_msg;
u8 *wbuf = NULL; u8 *wbuf = NULL;
int ret = 0; int ret = 0;
...@@ -638,6 +639,8 @@ static int sdw_bank_switch(struct sdw_bus *bus) ...@@ -638,6 +639,8 @@ static int sdw_bank_switch(struct sdw_bus *bus)
if (!wr_msg) if (!wr_msg)
return -ENOMEM; return -ENOMEM;
bus->defer_msg.msg = wr_msg;
wbuf = kzalloc(sizeof(*wbuf), GFP_KERNEL); wbuf = kzalloc(sizeof(*wbuf), GFP_KERNEL);
if (!wbuf) { if (!wbuf) {
ret = -ENOMEM; ret = -ENOMEM;
...@@ -658,17 +661,29 @@ static int sdw_bank_switch(struct sdw_bus *bus) ...@@ -658,17 +661,29 @@ static int sdw_bank_switch(struct sdw_bus *bus)
SDW_MSG_FLAG_WRITE, wbuf); SDW_MSG_FLAG_WRITE, wbuf);
wr_msg->ssp_sync = true; wr_msg->ssp_sync = true;
ret = sdw_transfer(bus, wr_msg); /*
* Set the multi_link flag only when both the hardware supports
* and there is a stream handled by multiple masters
*/
multi_link = bus->multi_link && (m_rt_count > 1);
if (multi_link)
ret = sdw_transfer_defer(bus, wr_msg, &bus->defer_msg);
else
ret = sdw_transfer(bus, wr_msg);
if (ret < 0) { if (ret < 0) {
dev_err(bus->dev, "Slave frame_ctrl reg write failed"); dev_err(bus->dev, "Slave frame_ctrl reg write failed");
goto error; goto error;
} }
kfree(wr_msg); if (!multi_link) {
kfree(wbuf); kfree(wr_msg);
bus->defer_msg.msg = NULL; kfree(wbuf);
bus->params.curr_bank = !bus->params.curr_bank; bus->defer_msg.msg = NULL;
bus->params.next_bank = !bus->params.next_bank; bus->params.curr_bank = !bus->params.curr_bank;
bus->params.next_bank = !bus->params.next_bank;
}
return 0; return 0;
...@@ -679,36 +694,87 @@ static int sdw_bank_switch(struct sdw_bus *bus) ...@@ -679,36 +694,87 @@ static int sdw_bank_switch(struct sdw_bus *bus)
return ret; return ret;
} }
/**
* sdw_ml_sync_bank_switch: Multilink register bank switch
*
* @bus: SDW bus instance
*
* Caller function should free the buffers on error
*/
static int sdw_ml_sync_bank_switch(struct sdw_bus *bus)
{
unsigned long time_left;
if (!bus->multi_link)
return 0;
/* Wait for completion of transfer */
time_left = wait_for_completion_timeout(&bus->defer_msg.complete,
bus->bank_switch_timeout);
if (!time_left) {
dev_err(bus->dev, "Controller Timed out on bank switch");
return -ETIMEDOUT;
}
bus->params.curr_bank = !bus->params.curr_bank;
bus->params.next_bank = !bus->params.next_bank;
if (bus->defer_msg.msg) {
kfree(bus->defer_msg.msg->buf);
kfree(bus->defer_msg.msg);
}
return 0;
}
static int do_bank_switch(struct sdw_stream_runtime *stream) static int do_bank_switch(struct sdw_stream_runtime *stream)
{ {
struct sdw_master_runtime *m_rt = NULL; struct sdw_master_runtime *m_rt = NULL;
const struct sdw_master_ops *ops; const struct sdw_master_ops *ops;
struct sdw_bus *bus = NULL; struct sdw_bus *bus = NULL;
bool multi_link = false;
int ret = 0; int ret = 0;
list_for_each_entry(m_rt, &stream->master_list, stream_node) { list_for_each_entry(m_rt, &stream->master_list, stream_node) {
bus = m_rt->bus; bus = m_rt->bus;
ops = bus->ops; ops = bus->ops;
if (bus->multi_link) {
multi_link = true;
mutex_lock(&bus->msg_lock);
}
/* Pre-bank switch */ /* Pre-bank switch */
if (ops->pre_bank_switch) { if (ops->pre_bank_switch) {
ret = ops->pre_bank_switch(bus); ret = ops->pre_bank_switch(bus);
if (ret < 0) { if (ret < 0) {
dev_err(bus->dev, dev_err(bus->dev,
"Pre bank switch op failed: %d", ret); "Pre bank switch op failed: %d", ret);
return ret; goto msg_unlock;
} }
} }
/* Bank switch */ /*
ret = sdw_bank_switch(bus); * Perform Bank switch operation.
* For multi link cases, the actual bank switch is
* synchronized across all Masters and happens later as a
* part of post_bank_switch ops.
*/
ret = sdw_bank_switch(bus, stream->m_rt_count);
if (ret < 0) { if (ret < 0) {
dev_err(bus->dev, "Bank switch failed: %d", ret); dev_err(bus->dev, "Bank switch failed: %d", ret);
return ret; goto error;
} }
} }
/*
* For multi link cases, it is expected that the bank switch is
* triggered by the post_bank_switch for the first Master in the list
* and for the other Masters the post_bank_switch() should return doing
* nothing.
*/
list_for_each_entry(m_rt, &stream->master_list, stream_node) { list_for_each_entry(m_rt, &stream->master_list, stream_node) {
bus = m_rt->bus; bus = m_rt->bus;
ops = bus->ops; ops = bus->ops;
...@@ -719,7 +785,47 @@ static int do_bank_switch(struct sdw_stream_runtime *stream) ...@@ -719,7 +785,47 @@ static int do_bank_switch(struct sdw_stream_runtime *stream)
if (ret < 0) { if (ret < 0) {
dev_err(bus->dev, dev_err(bus->dev,
"Post bank switch op failed: %d", ret); "Post bank switch op failed: %d", ret);
goto error;
} }
} else if (bus->multi_link && stream->m_rt_count > 1) {
dev_err(bus->dev,
"Post bank switch ops not implemented");
goto error;
}
/* Set the bank switch timeout to default, if not set */
if (!bus->bank_switch_timeout)
bus->bank_switch_timeout = DEFAULT_BANK_SWITCH_TIMEOUT;
/* Check if bank switch was successful */
ret = sdw_ml_sync_bank_switch(bus);
if (ret < 0) {
dev_err(bus->dev,
"multi link bank switch failed: %d", ret);
goto error;
}
mutex_unlock(&bus->msg_lock);
}
return ret;
error:
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
bus = m_rt->bus;
kfree(bus->defer_msg.msg->buf);
kfree(bus->defer_msg.msg);
}
msg_unlock:
if (multi_link) {
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
bus = m_rt->bus;
if (mutex_is_locked(&bus->msg_lock))
mutex_unlock(&bus->msg_lock);
} }
} }
...@@ -964,6 +1070,7 @@ int sdw_stream_remove_master(struct sdw_bus *bus, ...@@ -964,6 +1070,7 @@ int sdw_stream_remove_master(struct sdw_bus *bus,
sdw_master_port_release(bus, m_rt); sdw_master_port_release(bus, m_rt);
sdw_release_master_stream(m_rt, stream); sdw_release_master_stream(m_rt, stream);
stream->m_rt_count--;
} }
if (list_empty(&stream->master_list)) if (list_empty(&stream->master_list))
...@@ -1150,6 +1257,18 @@ int sdw_stream_add_master(struct sdw_bus *bus, ...@@ -1150,6 +1257,18 @@ int sdw_stream_add_master(struct sdw_bus *bus,
mutex_lock(&bus->bus_lock); mutex_lock(&bus->bus_lock);
/*
* For multi link streams, add the second master only if
* the bus supports it.
* Check if bus->multi_link is set
*/
if (!bus->multi_link && stream->m_rt_count > 0) {
dev_err(bus->dev,
"Multilink not supported, link %d", bus->link_id);
ret = -EINVAL;
goto unlock;
}
m_rt = sdw_alloc_master_rt(bus, stream_config, stream); m_rt = sdw_alloc_master_rt(bus, stream_config, stream);
if (!m_rt) { if (!m_rt) {
dev_err(bus->dev, dev_err(bus->dev,
...@@ -1167,6 +1286,8 @@ int sdw_stream_add_master(struct sdw_bus *bus, ...@@ -1167,6 +1286,8 @@ int sdw_stream_add_master(struct sdw_bus *bus,
if (ret) if (ret)
goto stream_error; goto stream_error;
stream->m_rt_count++;
goto unlock; goto unlock;
stream_error: stream_error:
......
...@@ -678,6 +678,9 @@ struct sdw_master_ops { ...@@ -678,6 +678,9 @@ struct sdw_master_ops {
* @defer_msg: Defer message * @defer_msg: Defer message
* @clk_stop_timeout: Clock stop timeout computed * @clk_stop_timeout: Clock stop timeout computed
* @bank_switch_timeout: Bank switch timeout computed * @bank_switch_timeout: Bank switch timeout computed
* @multi_link: Store bus property that indicates if multi links
* are supported. This flag is populated by drivers after reading
* appropriate firmware (ACPI/DT).
*/ */
struct sdw_bus { struct sdw_bus {
struct device *dev; struct device *dev;
...@@ -694,6 +697,7 @@ struct sdw_bus { ...@@ -694,6 +697,7 @@ struct sdw_bus {
struct sdw_defer defer_msg; struct sdw_defer defer_msg;
unsigned int clk_stop_timeout; unsigned int clk_stop_timeout;
u32 bank_switch_timeout; u32 bank_switch_timeout;
bool multi_link;
}; };
int sdw_add_bus_master(struct sdw_bus *bus); int sdw_add_bus_master(struct sdw_bus *bus);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment