diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 6013f963f4821b..0d4d0ac7dbe9d1 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -13,6 +13,9 @@ #include "irq.h" #include "sysfs_local.h" +#define DEFAULT_BRA_WRITE_THRESHOLD 800 +#define DEFAULT_BRA_READ_THRESHOLD 400 + static DEFINE_IDA(sdw_bus_ida); static int sdw_get_id(struct sdw_bus *bus) @@ -87,6 +90,8 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, lockdep_register_key(&bus->bus_lock_key); __mutex_init(&bus->bus_lock, "bus_lock", &bus->bus_lock_key); + mutex_init(&bus->bpt_lock); + INIT_LIST_HEAD(&bus->slaves); INIT_LIST_HEAD(&bus->m_rt_list); @@ -163,6 +168,11 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, bus->params.curr_bank = SDW_BANK0; bus->params.next_bank = SDW_BANK1; + if (!bus->bra_w_threshold) + bus->bra_w_threshold = DEFAULT_BRA_WRITE_THRESHOLD; + if (!bus->bra_r_threshold) + bus->bra_r_threshold = DEFAULT_BRA_READ_THRESHOLD; + return 0; } EXPORT_SYMBOL(sdw_bus_master_add); @@ -439,6 +449,46 @@ static int sdw_ntransfer_no_pm(struct sdw_slave *slave, u32 addr, u8 flags, return 0; } +static int sdw_ntransfer_no_pm_bpt(struct sdw_slave *slave, u32 addr, u8 flags, + size_t count, u8 *val) +{ + struct sdw_bpt_section sec; + struct sdw_bpt_msg msg; + size_t size; + int retry = 5; + int ret; + + msg.sections = 1; + msg.dev_num = slave->dev_num; + msg.flags = flags; + msg.sec = &sec; + + while (count) { + size = min_t(size_t, count, SDW_BPT_MSG_MAX_BYTES); + + sec.addr = addr; + sec.len = size; + sec.buf = val; + + do { + ret = sdw_bpt_send_sync(slave->bus, slave, &msg); + if (ret == -EAGAIN) + msleep(10); + retry--; + } while (ret == -EAGAIN && retry > 0); + + if (ret < 0) + return ret; + + addr += size; + val += size; + count -= size; + retry = 5; + } + + return 0; +} + /** * sdw_nread_no_pm() - Read "n" contiguous SDW Slave registers with no PM * @slave: SDW Slave @@ -447,10 +497,26 @@ static int sdw_ntransfer_no_pm(struct sdw_slave *slave, u32 addr, u8 flags, * @val: Buffer for values to be read * * Note that if the message crosses a page boundary each page will be - * transferred under a separate invocation of the msg_lock. + * transferred under a separate invocation of the msg_lock if it is not + * transferred via BPT. */ int sdw_nread_no_pm(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) { + struct sdw_bus *bus = slave->bus; + int ret; + + if (!bus->ops->bpt_send_async || !bus->ops->bpt_wait || + count < bus->bra_r_threshold) + goto fallback; + + ret = sdw_ntransfer_no_pm_bpt(slave, addr, SDW_MSG_FLAG_READ, count, val); + if (!ret) + return 0; + + dev_dbg(&slave->dev, + "BPT read failed for addr %x, count %zu, ret %d fallback to normal read\n", + addr, count, ret); +fallback: return sdw_ntransfer_no_pm(slave, addr, SDW_MSG_FLAG_READ, count, val); } EXPORT_SYMBOL(sdw_nread_no_pm); @@ -463,10 +529,26 @@ EXPORT_SYMBOL(sdw_nread_no_pm); * @val: Buffer for values to be written * * Note that if the message crosses a page boundary each page will be - * transferred under a separate invocation of the msg_lock. + * transferred under a separate invocation of the msg_lock if it is not + * transferred via BPT. */ int sdw_nwrite_no_pm(struct sdw_slave *slave, u32 addr, size_t count, const u8 *val) { + struct sdw_bus *bus = slave->bus; + int ret; + + if (!bus->ops->bpt_send_async || !bus->ops->bpt_wait || + count < bus->bra_w_threshold) + goto fallback; + + ret = sdw_ntransfer_no_pm_bpt(slave, addr, SDW_MSG_FLAG_WRITE, count, (u8 *)val); + if (!ret) + return 0; + + dev_dbg(&slave->dev, + "BPT write failed for addr %x, count %zu, ret %d fallback to normal write\n", + addr, count, ret); +fallback: return sdw_ntransfer_no_pm(slave, addr, SDW_MSG_FLAG_WRITE, count, (u8 *)val); } EXPORT_SYMBOL(sdw_nwrite_no_pm); @@ -604,7 +686,8 @@ EXPORT_SYMBOL(sdw_update); * This version of the function will take a PM reference to the slave * device. * Note that if the message crosses a page boundary each page will be - * transferred under a separate invocation of the msg_lock. + * transferred under a separate invocation of the msg_lock if it is not + * transferred via BPT. */ int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) { @@ -635,7 +718,8 @@ EXPORT_SYMBOL(sdw_nread); * This version of the function will take a PM reference to the slave * device. * Note that if the message crosses a page boundary each page will be - * transferred under a separate invocation of the msg_lock. + * transferred under a separate invocation of the msg_lock if it is not + * transferred via BPT. */ int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, const u8 *val) { @@ -2094,6 +2178,7 @@ EXPORT_SYMBOL(sdw_clear_slave_status); int sdw_bpt_send_async(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg) { int len = 0; + int ret; int i; for (i = 0; i < msg->sections; i++) @@ -2118,13 +2203,26 @@ int sdw_bpt_send_async(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_ return -EOPNOTSUPP; } - return bus->ops->bpt_send_async(bus, slave, msg); + /* Serialize BPT/BRA transfers per bus: PDIs and DMA resources are shared */ + mutex_lock(&bus->bpt_lock); + + ret = bus->ops->bpt_send_async(bus, slave, msg); + if (ret < 0) + mutex_unlock(&bus->bpt_lock); + + /* on success the lock is held until sdw_bpt_wait() */ + return ret; } 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); + int ret; + + ret = bus->ops->bpt_wait(bus, slave, msg); + mutex_unlock(&bus->bpt_lock); + + return ret; } EXPORT_SYMBOL(sdw_bpt_wait); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 3e0d21132ef2f7..dad9806311af73 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -1028,6 +1028,10 @@ struct sdw_stream_runtime { * are supported. This flag is populated by drivers after reading * appropriate firmware (ACPI/DT). * @lane_used_bandwidth: how much bandwidth in bits per second is used by each lane + * @bra_w_threshold: Message-size threshold (bytes) above which BPT write is used. + * If set to 0, a default is used. + * @bra_r_threshold: Message-size threshold (bytes) above which BPT read is used. + * If set to 0, a default is used. */ struct sdw_bus { struct device *dev; @@ -1044,6 +1048,7 @@ struct sdw_bus { int stream_refcount; int bpt_stream_refcount; struct sdw_stream_runtime *bpt_stream; + struct mutex bpt_lock; /* serialize BPT/BRA transfers per bus */ const struct sdw_master_ops *ops; const struct sdw_master_port_ops *port_ops; struct sdw_master_prop prop; @@ -1063,6 +1068,8 @@ struct sdw_bus { #endif bool multi_link; unsigned int lane_used_bandwidth[SDW_MAX_LANES]; + unsigned int bra_w_threshold; + unsigned int bra_r_threshold; }; struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name, enum sdw_stream_type type); diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c index bb52ae5dc79d9a..245d97ea4188c2 100644 --- a/sound/soc/sof/intel/hda-sdw-bpt.c +++ b/sound/soc/sof/intel/hda-sdw-bpt.c @@ -273,6 +273,11 @@ int hda_sdw_bpt_open(struct device *dev, int link_id, struct hdac_ext_stream **b int ret1; int ret; + if (!sdev->dspless_mode_selected && sdev->first_boot) { + dev_dbg(dev, "SOF FW boot not complete yet\n"); + return -ENXIO; + } + num_channels_tx = DIV_ROUND_UP(tx_dma_bandwidth, BPT_FREQUENCY * 32); ret = hda_sdw_bpt_dma_prepare(dev, bpt_tx_stream, dmab_tx_bdl, bpt_tx_num_bytes,