Skip to content

Commit

Permalink
soundwire: bus: add send_async/wait APIs for BPT protocol
Browse files Browse the repository at this point in the history
Add definitions and helpers for the BPT/BRA protocol. Peripheral
drivers (aka ASoC codec drivers) can use this API to send bulk data
such as firmware or tables. The design intent is however NOT to
directly use this API but to rely on an intermediate regmap layer.

The API is only available when no other audio streams have been
allocated, and only one BTP/BRA stream is allowed per link. To avoid
the addition of yet another lock, the refcount tests are handled in
the stream master_runtime alloc/free routines where the bus_lock is
already held. Another benefit of this approach is that the same
bus_lock is used to handle runtime and port linked lists, which
reduces the potential for misaligned configurations.

In addition to exclusion with audio streams, BPT transfers have a lot
of overhead, specifically registers writes are needed to enable
transport in DP0. Most DMAs don't handle too well very small data sets
and they may have alignment limitations.

The size and alignment requirements are for now not handled by the
core but must be checked by platform-specific drivers.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Tested-by: shumingf@realtek.com
Link: https://lore.kernel.org/r/20250227140615.8147-8-yung-chuan.liao@linux.intel.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
  • Loading branch information
Pierre-Louis Bossart authored and Vinod Koul committed Mar 10, 2025
1 parent b422b72 commit 9a75628
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 0 deletions.
43 changes: 43 additions & 0 deletions drivers/soundwire/bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -2039,3 +2039,46 @@ void sdw_clear_slave_status(struct sdw_bus *bus, u32 request)
}
}
EXPORT_SYMBOL(sdw_clear_slave_status);

int sdw_bpt_send_async(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg)
{
if (msg->len > SDW_BPT_MSG_MAX_BYTES) {
dev_err(bus->dev, "Invalid BPT message length %d\n", msg->len);
return -EINVAL;
}

/* check device is enumerated */
if (slave->dev_num == SDW_ENUM_DEV_NUM ||
slave->dev_num > SDW_MAX_DEVICES) {
dev_err(&slave->dev, "Invalid device number %d\n", slave->dev_num);
return -ENODEV;
}

/* make sure all callbacks are defined */
if (!bus->ops->bpt_send_async ||
!bus->ops->bpt_wait) {
dev_err(bus->dev, "BPT callbacks not defined\n");
return -EOPNOTSUPP;
}

return bus->ops->bpt_send_async(bus, slave, msg);
}
EXPORT_SYMBOL(sdw_bpt_send_async);

int sdw_bpt_wait(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg)
{
return bus->ops->bpt_wait(bus, slave, msg);
}
EXPORT_SYMBOL(sdw_bpt_wait);

int sdw_bpt_send_sync(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg)
{
int ret;

ret = sdw_bpt_send_async(bus, slave, msg);
if (ret < 0)
return ret;

return sdw_bpt_wait(bus, slave, msg);
}
EXPORT_SYMBOL(sdw_bpt_send_sync);
18 changes: 18 additions & 0 deletions drivers/soundwire/bus.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@ struct sdw_msg {
bool page;
};

/**
* struct sdw_btp_msg - Message structure
* @addr: Start Register address accessed in the Slave
* @len: number of bytes to transfer. More than 64Kb can be transferred
* but a practical limit of SDW_BPT_MSG_MAX_BYTES is enforced.
* @dev_num: Slave device number
* @flags: transfer flags, indicate if xfer is read or write
* @buf: message data buffer (filled by host for write, filled
* by Peripheral hardware for reads)
*/
struct sdw_bpt_msg {
u32 addr;
u32 len;
u8 dev_num;
u8 flags;
u8 *buf;
};

#define SDW_DOUBLE_RATE_FACTOR 2
#define SDW_STRM_RATE_GROUPING 1

Expand Down
30 changes: 30 additions & 0 deletions drivers/soundwire/stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -1227,6 +1227,20 @@ static struct sdw_master_runtime
struct sdw_master_runtime *m_rt, *walk_m_rt;
struct list_head *insert_after;

if (stream->type == SDW_STREAM_BPT) {
if (bus->stream_refcount > 0 || bus->bpt_stream_refcount > 0) {
dev_err(bus->dev, "%s: %d/%d audio/BPT stream already allocated\n",
__func__, bus->stream_refcount, bus->bpt_stream_refcount);
return ERR_PTR(-EBUSY);
}
} else {
if (bus->bpt_stream_refcount > 0) {
dev_err(bus->dev, "%s: BPT stream already allocated\n",
__func__);
return ERR_PTR(-EAGAIN);
}
}

m_rt = kzalloc(sizeof(*m_rt), GFP_KERNEL);
if (!m_rt)
return NULL;
Expand Down Expand Up @@ -1255,6 +1269,8 @@ static struct sdw_master_runtime
m_rt->stream = stream;

bus->stream_refcount++;
if (stream->type == SDW_STREAM_BPT)
bus->bpt_stream_refcount++;

return m_rt;
}
Expand Down Expand Up @@ -1303,6 +1319,8 @@ static void sdw_master_rt_free(struct sdw_master_runtime *m_rt,
list_del(&m_rt->bus_node);
kfree(m_rt);

if (stream->type == SDW_STREAM_BPT)
bus->bpt_stream_refcount--;
bus->stream_refcount--;
}

Expand Down Expand Up @@ -2001,6 +2019,12 @@ int sdw_stream_add_master(struct sdw_bus *bus,
m_rt = sdw_master_rt_find(bus, stream);
if (!m_rt) {
m_rt = sdw_master_rt_alloc(bus, stream);
if (IS_ERR(m_rt)) {
ret = PTR_ERR(m_rt);
dev_err(bus->dev, "%s: Master runtime alloc failed for stream:%s: %d\n",
__func__, stream->name, ret);
goto unlock;
}
if (!m_rt) {
dev_err(bus->dev, "%s: Master runtime alloc failed for stream:%s\n",
__func__, stream->name);
Expand Down Expand Up @@ -2116,6 +2140,12 @@ int sdw_stream_add_slave(struct sdw_slave *slave,
* So, allocate m_rt and add Slave to it.
*/
m_rt = sdw_master_rt_alloc(slave->bus, stream);
if (IS_ERR(m_rt)) {
ret = PTR_ERR(m_rt);
dev_err(&slave->dev, "%s: Master runtime alloc failed for stream:%s: %d\n",
__func__, stream->name, ret);
goto unlock;
}
if (!m_rt) {
dev_err(&slave->dev, "%s: Master runtime alloc failed for stream:%s\n",
__func__, stream->name);
Expand Down
23 changes: 23 additions & 0 deletions include/linux/soundwire/sdw.h
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,15 @@ struct sdw_defer {
struct completion complete;
};

/*
* Add a practical limit to BPT transfer sizes. BPT is typically used
* to transfer firmware, and larger firmware transfers will increase
* the cold latency beyond typical OS or user requirements.
*/
#define SDW_BPT_MSG_MAX_BYTES (1024 * 1024)

struct sdw_bpt_msg;

/**
* struct sdw_master_ops - Master driver ops
* @read_prop: Read Master properties
Expand All @@ -839,6 +848,10 @@ struct sdw_defer {
* @get_device_num: Callback for vendor-specific device_number allocation
* @put_device_num: Callback for vendor-specific device_number release
* @new_peripheral_assigned: Callback to handle enumeration of new peripheral.
* @bpt_send_async: reserve resources for BPT stream and send message
* using BTP protocol
* @bpt_wait: wait for message completion using BTP protocol
* and release resources
*/
struct sdw_master_ops {
int (*read_prop)(struct sdw_bus *bus);
Expand All @@ -855,6 +868,9 @@ struct sdw_master_ops {
void (*new_peripheral_assigned)(struct sdw_bus *bus,
struct sdw_slave *slave,
int dev_num);
int (*bpt_send_async)(struct sdw_bus *bus, struct sdw_slave *slave,
struct sdw_bpt_msg *msg);
int (*bpt_wait)(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg);
};

int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent,
Expand Down Expand Up @@ -961,6 +977,8 @@ struct sdw_stream_runtime {
* @defer_msg: Defer message
* @params: Current bus parameters
* @stream_refcount: number of streams currently using this bus
* @btp_stream_refcount: number of BTP streams currently using this bus (should
* be zero or one, multiple streams per link is not supported).
* @ops: Master callback ops
* @port_ops: Master port callback ops
* @prop: Master properties
Expand Down Expand Up @@ -998,6 +1016,7 @@ struct sdw_bus {
struct sdw_defer defer_msg;
struct sdw_bus_params params;
int stream_refcount;
int bpt_stream_refcount;
const struct sdw_master_ops *ops;
const struct sdw_master_port_ops *port_ops;
struct sdw_master_prop prop;
Expand Down Expand Up @@ -1045,6 +1064,10 @@ int sdw_compare_devid(struct sdw_slave *slave, struct sdw_slave_id id);
void sdw_extract_slave_id(struct sdw_bus *bus, u64 addr, struct sdw_slave_id *id);
bool is_clock_scaling_supported_by_slave(struct sdw_slave *slave);

int sdw_bpt_send_async(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg);
int sdw_bpt_wait(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg);
int sdw_bpt_send_sync(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg);

#if IS_ENABLED(CONFIG_SOUNDWIRE)

int sdw_stream_add_slave(struct sdw_slave *slave,
Expand Down

0 comments on commit 9a75628

Please sign in to comment.