@@ -2038,3 +2038,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);
@@ -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
@@ -1226,6 +1226,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;
@@ -1254,6 +1268,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;
}
@@ -1302,6 +1318,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--;
}
@@ -2000,6 +2018,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);
@@ -2115,6 +2139,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);
@@ -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
@@ -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);
@@ -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,
@@ -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
@@ -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;
@@ -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,