From 91aa32736affe87111b7b872752be39aef461d6c Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 2 Jul 2026 10:42:49 +0800 Subject: [PATCH 01/10] soundwire: move lane_used_bandwidth to sdw_bus_params The lane_used_bandwidth variable store the used bandwidth of each lane. It should belong to the sdw_bus_params struct instead of in the sdw_bus struct directly. Besides, _sdw_prepare_stream() will restore the bus->params, and the lane_used_bandwidth values will not be restored if it is not in the params. Signed-off-by: Bard Liao --- drivers/soundwire/generic_bandwidth_allocation.c | 8 ++++---- drivers/soundwire/stream.c | 4 ++-- include/linux/soundwire/sdw.h | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 4fb4c1acd3bb90..8be4ba44d6efe7 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -199,7 +199,7 @@ static void _sdw_compute_port_params(struct sdw_bus *bus, /* Run loop for all groups to compute transport parameters */ for (l = 0; l < SDW_MAX_LANES; l++) { - if (l > 0 && !bus->lane_used_bandwidth[l]) + if (l > 0 && !bus->params.lane_used_bandwidth[l]) continue; /* reset hstop for each lane */ hstop = bus->params.col - 1; @@ -270,7 +270,7 @@ static int sdw_compute_group_params(struct sdw_bus *bus, } for (l = 0; l < SDW_MAX_LANES; l++) { - if (l > 0 && !bus->lane_used_bandwidth[l]) + if (l > 0 && !bus->params.lane_used_bandwidth[l]) continue; /* reset column_needed for each lane */ column_needed = 0; @@ -510,7 +510,7 @@ static int get_manager_lane(struct sdw_bus *bus, struct sdw_master_runtime *m_rt m_rt->stream->params.bps; } if (required_bandwidth <= - curr_dr_freq - bus->lane_used_bandwidth[l]) { + curr_dr_freq - bus->params.lane_used_bandwidth[l]) { /* Check if m_lane is connected to all Peripherals */ if (!is_lane_connected_to_all_peripherals(m_rt, slave_prop->lane_maps[l])) { @@ -521,7 +521,7 @@ static int get_manager_lane(struct sdw_bus *bus, struct sdw_master_runtime *m_rt } m_lane = slave_prop->lane_maps[l]; dev_dbg(&s_rt->slave->dev, "M lane %d is used\n", m_lane); - bus->lane_used_bandwidth[l] += required_bandwidth; + bus->params.lane_used_bandwidth[l] += required_bandwidth; /* * Use non-zero manager lane, subtract the lane 0 * bandwidth that is already calculated diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index cbf7bd3d4e7bac..e5ab12134e6c43 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1794,8 +1794,8 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) bandwidth = m_rt->stream->params.rate * hweight32(p_rt->ch_mask) * m_rt->stream->params.bps; multi_lane_bandwidth += bandwidth; - bus->lane_used_bandwidth[p_rt->lane] -= bandwidth; - if (!bus->lane_used_bandwidth[p_rt->lane]) + bus->params.lane_used_bandwidth[p_rt->lane] -= bandwidth; + if (!bus->params.lane_used_bandwidth[p_rt->lane]) p_rt->lane = 0; } /* TODO: Update this during Device-Device support */ diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 3e0d21132ef2f7..07c280cd3c640e 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -589,6 +589,7 @@ enum sdw_port_prep_ops { * @max_dr_freq: Maximum double rate clock frequency supported, in Hz * @curr_dr_freq: Current double rate clock frequency, in Hz * @bandwidth: Current bandwidth + * @lane_used_bandwidth: how much bandwidth in bits per second is used by each lane * @col: Active columns * @row: Active rows * @s_data_mode: NORMAL, STATIC or PRBS mode for all Slave ports @@ -602,6 +603,7 @@ struct sdw_bus_params { unsigned int max_dr_freq; unsigned int curr_dr_freq; unsigned int bandwidth; + unsigned int lane_used_bandwidth[SDW_MAX_LANES]; unsigned int col; unsigned int row; int s_data_mode; @@ -1027,7 +1029,6 @@ struct sdw_stream_runtime { * @multi_link: Store bus property that indicates if multi links * 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 */ struct sdw_bus { struct device *dev; @@ -1062,7 +1063,6 @@ struct sdw_bus { struct dentry *debugfs; #endif bool multi_link; - unsigned int lane_used_bandwidth[SDW_MAX_LANES]; }; struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name, enum sdw_stream_type type); From e1bbcb2cdef0fb01767ad50f35e16ba6ad7cf905 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 30 Jun 2026 14:41:05 +0800 Subject: [PATCH 02/10] soundwire: stream: restore bus_params of each bus Restore_params restores bus->params and bus->bpt_hstop from single backups that are overwritten in the first master_list loop. If failure happens later (e.g. do_bank_switch() or port prepare in the second loop), the current bus may not match the saved snapshot, so multi-link streams can restore the wrong state into the wrong bus and leave other buses partially reconfigured. Signed-off-by: Bard Liao --- drivers/soundwire/stream.c | 42 ++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index e5ab12134e6c43..984eb6309121ec 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1479,19 +1479,37 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream, struct sdw_master_runtime *m_rt; struct sdw_bus *bus; struct sdw_master_prop *prop; - struct sdw_bus_params params; int ret; + /* Local structure to store backup params for error recovery */ + struct { + struct list_head list_node; + struct sdw_bus *bus; + struct sdw_bus_params params; + } *params_entry, *temp_entry; + + LIST_HEAD(params_backup_list); + /* Prepare Master(s) and Slave(s) port(s) associated with stream */ list_for_each_entry(m_rt, &stream->master_list, stream_node) { bus = m_rt->bus; prop = &bus->prop; - memcpy(¶ms, &bus->params, sizeof(params)); + + /* Allocate and save current params for error recovery */ + params_entry = kzalloc_obj(*params_entry); + if (!params_entry) { + ret = -ENOMEM; + goto restore_params; + } + params_entry->bus = bus; + memcpy(¶ms_entry->params, &bus->params, sizeof(params_entry->params)); + list_add_tail(¶ms_entry->list_node, ¶ms_backup_list); /* TODO: Support Asynchronous mode */ if ((prop->max_clk_freq % stream->params.rate) != 0) { dev_err(bus->dev, "Async mode not supported\n"); - return -EINVAL; + ret = -EINVAL; + goto restore_params; } if (update_params) { @@ -1539,10 +1557,26 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream, stream->state = SDW_STREAM_PREPARED; + /* Free the backup list on success */ + list_for_each_entry_safe(params_entry, temp_entry, ¶ms_backup_list, list_node) { + list_del(¶ms_entry->list_node); + kfree(params_entry); + } + return ret; restore_params: - memcpy(&bus->params, ¶ms, sizeof(params)); + /* Restore all bus params from the backup list */ + list_for_each_entry(params_entry, ¶ms_backup_list, list_node) { + memcpy(¶ms_entry->bus->params, ¶ms_entry->params, + sizeof(params_entry->params)); + } + + /* Free the backup list on error */ + list_for_each_entry_safe(params_entry, temp_entry, ¶ms_backup_list, list_node) { + list_del(¶ms_entry->list_node); + kfree(params_entry); + } return ret; } From 7f1bbc3953533a6a3f60a99f6ccfded5a9f7d864 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 4 Nov 2025 14:20:05 +0800 Subject: [PATCH 03/10] soundwire: use maximum sdw bus rate when BPT stream is running We should get as much as bandwidth for the BPT stream. Signed-off-by: Bard Liao --- drivers/soundwire/generic_bandwidth_allocation.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 8be4ba44d6efe7..8d16dfbda3a054 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -566,6 +566,18 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) clk_buf = NULL; } + /* + * Use the maximum freq to get maximum bandwidth and no need to try another freq + * if any BPT stream is running + */ + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + if (m_rt->stream->type == SDW_STREAM_BPT && + m_rt->stream->state < SDW_STREAM_DEPREPARED) { + clk_values = 1; + clk_buf = NULL; + } + } + /* If dynamic scaling is not supported, don't try higher freq */ if (!is_clock_scaling_supported(bus)) clk_values = 1; From 534ecb499069da0ee0fedc99c6b211edd4fcbe8f Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 3 Nov 2025 14:23:51 +0800 Subject: [PATCH 04/10] soundwire: generic_bandwidth_allocation: don't deal with BPT stream The DP0 (BPT) params are computed in sdw_compute_dp0_port_params(). We should exclude the BPT stream when calculating the audio streams. Signed-off-by: Bard Liao --- drivers/soundwire/generic_bandwidth_allocation.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 8d16dfbda3a054..8f31fd1df2da41 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -216,6 +216,9 @@ static void _sdw_compute_port_params(struct sdw_bus *bus, if (m_rt->stream->state > SDW_STREAM_DISABLED || m_rt->stream->state < SDW_STREAM_CONFIGURED) continue; + /* BPT stream is handled in sdw_compute_dp0_port_params */ + if (m_rt->stream->type == SDW_STREAM_BPT) + continue; sdw_compute_master_ports(m_rt, ¶ms[i], &port_bo, hstop); } @@ -355,6 +358,9 @@ static int sdw_get_group_count(struct sdw_bus *bus, } list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + if (m_rt->stream->type == SDW_STREAM_BPT) + continue; + if (m_rt->stream->state == SDW_STREAM_DEPREPARED) continue; From c8b36b8ac264581cd477ee2ad1929c3c0cc5ecae Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 7 Nov 2025 11:40:15 +0800 Subject: [PATCH 05/10] soundwire: don't count BPT bandwidth We just need to conunt the audio stream bandwidth and BRA stream will use the remaining bandwidth. Signed-off-by: Bard Liao --- drivers/soundwire/generic_bandwidth_allocation.c | 8 ++++++++ drivers/soundwire/stream.c | 12 ++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 8f31fd1df2da41..c92dee6ff3d279 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -260,6 +260,11 @@ static int sdw_compute_group_params(struct sdw_bus *bus, m_rt->stream->state != SDW_STREAM_DISABLED) continue; } + + /* Don't count BPT stream bandwidth, it will use the remaining bandwidth */ + if (m_rt->stream->type == SDW_STREAM_BPT) + continue; + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { rate = m_rt->stream->params.rate; bps = m_rt->stream->params.bps; @@ -601,6 +606,9 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) break; list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + /* BPT stream always uses lane 0 */ + if (m_rt->stream->type == SDW_STREAM_BPT) + continue; /* * Get the first s_rt that will be used to find the available lane that * can be used. No need to check all Peripherals because we can't use diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 984eb6309121ec..47f03bcf198184 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1515,8 +1515,11 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream, if (update_params) { /* Increment cumulative bus bandwidth */ /* TODO: Update this during Device-Device support */ - bus->params.bandwidth += m_rt->stream->params.rate * - m_rt->ch_count * m_rt->stream->params.bps; + /* Don't count BPT stream bandwidth, it will use the remaining bandwidth */ + if (m_rt->stream->type != SDW_STREAM_BPT) { + bus->params.bandwidth += m_rt->stream->params.rate * + m_rt->ch_count * m_rt->stream->params.bps; + } /* Compute params */ if (bus->compute_params) { @@ -1821,6 +1824,10 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) multi_lane_bandwidth = 0; + /* Don't count BPT stream bandwidth, it will use the remaining bandwidth */ + if (m_rt->stream->type == SDW_STREAM_BPT) + goto skip_bpt_stream; + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { if (!p_rt->lane) continue; @@ -1836,6 +1843,7 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) bandwidth = m_rt->stream->params.rate * m_rt->ch_count * m_rt->stream->params.bps; bus->params.bandwidth -= bandwidth - multi_lane_bandwidth; +skip_bpt_stream: /* Compute params */ if (bus->compute_params) { ret = bus->compute_params(bus, stream); From 621993e8b41516e6a80cc10e7f442546722f02cf Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 26 Jun 2026 16:06:38 +0800 Subject: [PATCH 06/10] soundwire: generic_bandwidth_allocation: check bandwidth with real colume The existing code assumes the column number will not change, but it could change if curr_dr_freq changes. Calculate the new column number before checking the bandwidth to make the checking be more accurate. Signed-off-by: Bard Liao --- drivers/soundwire/generic_bandwidth_allocation.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index c92dee6ff3d279..f14db7e3585add 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -593,7 +593,12 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) if (!is_clock_scaling_supported(bus)) clk_values = 1; + if (!mstr_prop->default_frame_rate || !mstr_prop->default_row) + return -EINVAL; + for (i = 0; i < clk_values; i++) { + int total_col; + if (!clk_buf) curr_dr_freq = bus->params.max_dr_freq; else @@ -601,8 +606,10 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) (bus->params.max_dr_freq >> clk_buf[i]) : clk_buf[i] * SDW_DOUBLE_RATE_FACTOR; - if (curr_dr_freq * (mstr_prop->default_col - 1) >= - bus->params.bandwidth * mstr_prop->default_col) + total_col = curr_dr_freq / mstr_prop->default_frame_rate / mstr_prop->default_row; + + if (curr_dr_freq * (total_col - 1) >= + bus->params.bandwidth * total_col) break; list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { @@ -664,9 +671,6 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) } } - if (!mstr_prop->default_frame_rate || !mstr_prop->default_row) - return -EINVAL; - mstr_prop->default_col = curr_dr_freq / mstr_prop->default_frame_rate / mstr_prop->default_row; From 7efcda4a522a2ff863fe80c97afbf8abbcc3d098 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 25 Jun 2026 19:47:47 +0800 Subject: [PATCH 07/10] soundwire: add bpt_hstop in struct sdw_bus_params To allow BPT and audio stream work simultaneously, we need to record the hstop of the BPT stream. And use column bpt_hstop + 1 to the last column for audio streams. No function changed since bus->params.bpt_hstop is set to bus->params.col - 1 for now. Will update bus->audio_stream_hstart in the follow up commit. Signed-off-by: Bard Liao --- drivers/soundwire/generic_bandwidth_allocation.c | 9 +++++---- drivers/soundwire/intel_ace2x.c | 6 +++--- include/linux/soundwire/sdw.h | 2 ++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index f14db7e3585add..07e4aa6e7c3118 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -97,9 +97,9 @@ static void sdw_compute_dp0_slave_ports(struct sdw_master_runtime *m_rt) list_for_each_entry(p_rt, &s_rt->port_list, port_node) { sdw_fill_xport_params(&p_rt->transport_params, p_rt->num, false, SDW_BLK_GRP_CNT_1, bus->params.col, 0, 0, 1, - bus->params.col - 1, SDW_BLK_PKG_PER_PORT, 0x0); + bus->params.bpt_hstop, SDW_BLK_PKG_PER_PORT, 0x0); - sdw_fill_port_params(&p_rt->port_params, p_rt->num, bus->params.col - 1, + sdw_fill_port_params(&p_rt->port_params, p_rt->num, bus->params.bpt_hstop, SDW_PORT_FLOW_MODE_ISOCH, SDW_PORT_DATA_MODE_NORMAL); } } @@ -113,9 +113,9 @@ static void sdw_compute_dp0_master_ports(struct sdw_master_runtime *m_rt) list_for_each_entry(p_rt, &m_rt->port_list, port_node) { sdw_fill_xport_params(&p_rt->transport_params, p_rt->num, false, SDW_BLK_GRP_CNT_1, bus->params.col, 0, 0, 1, - bus->params.col - 1, SDW_BLK_PKG_PER_PORT, 0x0); + bus->params.bpt_hstop, SDW_BLK_PKG_PER_PORT, 0x0); - sdw_fill_port_params(&p_rt->port_params, p_rt->num, bus->params.col - 1, + sdw_fill_port_params(&p_rt->port_params, p_rt->num, bus->params.bpt_hstop, SDW_PORT_FLOW_MODE_ISOCH, SDW_PORT_DATA_MODE_NORMAL); } } @@ -700,6 +700,7 @@ int sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream) if (ret < 0) return ret; + bus->params.bpt_hstop = bus->params.col - 1; if (stream->type == SDW_STREAM_BPT) { sdw_compute_dp0_port_params(bus); return 0; diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 642b33ab552606..cfbc83d9343aea 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -159,7 +159,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * command = (msg->flags & SDW_MSG_FLAG_WRITE) ? 0 : 1; ret = sdw_cdns_bpt_find_bandwidth(command, cdns->bus.params.row, - cdns->bus.params.col, + cdns->bus.params.bpt_hstop + 1, prop->default_frame_rate, &tx_dma_bandwidth, &rx_dma_bandwidth); if (ret < 0) @@ -185,7 +185,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * /* Add up pdi buffer size and frame numbers of each BPT sections */ for (i = 0; i < msg->sections; i++) { ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row, - cdns->bus.params.col, + cdns->bus.params.bpt_hstop + 1, msg->sec[i].len, max_data_per_frame, slave->prop.bra_block_alignment, &data_per_frame, &pdi0_buffer_size_, @@ -210,7 +210,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * if (command) { /* read */ /* Get buffer size of a full frame */ ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row, - cdns->bus.params.col, + cdns->bus.params.bpt_hstop + 1, data_per_frame, max_data_per_frame, slave->prop.bra_block_alignment, &data_per_frame, &pdi0_buf_size_pre_frame, diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 07c280cd3c640e..cb956a778f5931 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -590,6 +590,7 @@ enum sdw_port_prep_ops { * @curr_dr_freq: Current double rate clock frequency, in Hz * @bandwidth: Current bandwidth * @lane_used_bandwidth: how much bandwidth in bits per second is used by each lane + * @bpt_hstop: The hstop of the BPT stream * @col: Active columns * @row: Active rows * @s_data_mode: NORMAL, STATIC or PRBS mode for all Slave ports @@ -604,6 +605,7 @@ struct sdw_bus_params { unsigned int curr_dr_freq; unsigned int bandwidth; unsigned int lane_used_bandwidth[SDW_MAX_LANES]; + int bpt_hstop; unsigned int col; unsigned int row; int s_data_mode; From 97d68a4f30bfa0e69381801289023654f9eb0201 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 2 Jul 2026 11:10:39 +0800 Subject: [PATCH 08/10] soundwire: update bpt_hstop Update bus->params.bpt_hstop to record the hstop of the BPT stream. And return -EAGAIN when there is no bandwidth for the BPT stream. Signed-off-by: Bard Liao --- .../soundwire/generic_bandwidth_allocation.c | 53 ++++++++++++++++--- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 07e4aa6e7c3118..41fdca0bed17d6 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -190,8 +190,8 @@ static void sdw_compute_master_ports(struct sdw_master_runtime *m_rt, sdw_compute_slave_ports(m_rt, &t_data); } -static void _sdw_compute_port_params(struct sdw_bus *bus, - struct sdw_group_params *params, int count) +static void _sdw_compute_port_params(struct sdw_bus *bus, struct sdw_group_params *params, + int count, bool update_bpt_hstop) { struct sdw_master_runtime *m_rt; int port_bo, i, l; @@ -223,6 +223,16 @@ static void _sdw_compute_port_params(struct sdw_bus *bus, } hstop = hstop - params[i].hwidth; + if (l == 0 && update_bpt_hstop && bus->params.bpt_hstop > hstop) { + /* Assume BPT stream uses lane 0 */ + /* + * hstart = hstop - params->hwidth + 1. + * At this point after hstop = hstop - params[i].hwidth above, + * the hstart is equal to hstop + 1, and bus->bpt_hstop should + * be hstart - 1. so we can set bpt_hstop to hstop directly. + */ + bus->params.bpt_hstop = hstop; + } } } } @@ -422,7 +432,7 @@ static int sdw_compute_port_params(struct sdw_bus *bus, struct sdw_stream_runtim if (ret < 0) goto free_params; - _sdw_compute_port_params(bus, params, group.count); + _sdw_compute_port_params(bus, params, group.count, stream->type == SDW_STREAM_BPT); free_params: kfree(params); @@ -685,6 +695,10 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) return 0; } +#define SDW_DEFAULT_COL 4 +#define SDW_COL_RESERVED_FOR_AUDIO 2 + + /** * sdw_compute_params: Compute bus, transport and port parameters * @@ -700,10 +714,25 @@ int sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream) if (ret < 0) return ret; - bus->params.bpt_hstop = bus->params.col - 1; - if (stream->type == SDW_STREAM_BPT) { - sdw_compute_dp0_port_params(bus); - return 0; + /* + * stream->state is set to SDW_STREAM_DEPREPARED at the beginning of _sdw_deprepare_stream + * In other words, if the stream->type != SDW_STREAM_DEPAREPARED means sdw_compute_params() + * is called from _sdw_prepare_stream() and the stream is preparing. + */ + if (stream->type == SDW_STREAM_BPT && stream->state != SDW_STREAM_DEPREPARED) { + /* + * Set the initial bpt_hstop when the BPT stream is preparing and it will be + * updated in sdw_compute_port_params() below. + */ + bus->params.bpt_hstop = bus->params.col - 1; + /* + * Reserve 2 columns for future audio stream if the bus->params.col is greater + * than SDW_DEFAULT_COL (4) + reserved columns (2). And don't reserve columns + * for future use otherwise. This ensures that the BPT stream will not meet the + * bandwidth issue when there is no audio stream is open. + */ + if (bus->params.col >= (SDW_DEFAULT_COL + SDW_COL_RESERVED_FOR_AUDIO)) + bus->params.bpt_hstop -= SDW_COL_RESERVED_FOR_AUDIO; } /* Compute transport and port params */ @@ -713,6 +742,16 @@ int sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream) return ret; } + if (stream->type == SDW_STREAM_BPT && stream->state != SDW_STREAM_DEPREPARED) { + /* No usable data columns left */ + if (bus->params.bpt_hstop < 1) { + dev_err(bus->dev, "%s: No bandwidth for BPT stream\n", + __func__); + return -EAGAIN; + } + sdw_compute_dp0_port_params(bus); + } + return 0; } EXPORT_SYMBOL(sdw_compute_params); From e880ea3eabca478100ada5c0e6418f3facedc0c1 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 7 Nov 2025 14:11:22 +0800 Subject: [PATCH 09/10] soundwire: subtract BPT columns in bandwidth calculation When a BPT stream is running, we should subtract the columns that is used by the BPT stream in bandwidth calculation. Signed-off-by: Bard Liao --- .../soundwire/generic_bandwidth_allocation.c | 56 +++++++++++++++++-- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 41fdca0bed17d6..3887dd3bd14c0f 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -228,8 +228,9 @@ static void _sdw_compute_port_params(struct sdw_bus *bus, struct sdw_group_param /* * hstart = hstop - params->hwidth + 1. * At this point after hstop = hstop - params[i].hwidth above, - * the hstart is equal to hstop + 1, and bus->bpt_hstop should - * be hstart - 1. so we can set bpt_hstop to hstop directly. + * the hstart is equal to hstop + 1, and bus->params.bpt_hstop + * should be hstart - 1. so we can set bpt_hstop to hstop + * directly. */ bus->params.bpt_hstop = hstop; } @@ -267,8 +268,16 @@ static int sdw_compute_group_params(struct sdw_bus *bus, */ if (m_rt->stream->state != SDW_STREAM_ENABLED && m_rt->stream->state != SDW_STREAM_PREPARED && - m_rt->stream->state != SDW_STREAM_DISABLED) + m_rt->stream->state != SDW_STREAM_DISABLED) { continue; + } else if (m_rt->stream->type == SDW_STREAM_BPT) { + /* + * If any BPT stream is running or pause, exclude the BPT columns + * BPT: col 0.. bus->params.bpt_hstop + * Audio: col bus->params.bpt_hstop + 1 .. bus->params.col - 1 + */ + sel_col = bus->params.col - bus->params.bpt_hstop - 1; + } } /* Don't count BPT stream bandwidth, it will use the remaining bandwidth */ @@ -290,6 +299,14 @@ static int sdw_compute_group_params(struct sdw_bus *bus, for (l = 0; l < SDW_MAX_LANES; l++) { if (l > 0 && !bus->params.lane_used_bandwidth[l]) continue; + + /* + * Currently, BPT stream is only implemented on lane 0 which means all columns + * are available for lane 1 and above. Set sel_col back. + */ + if (l > 0) + sel_col = bus->params.col; + /* reset column_needed for each lane */ column_needed = 0; for (i = 0; i < group->count; i++) { @@ -303,8 +320,13 @@ static int sdw_compute_group_params(struct sdw_bus *bus, /* There is no control column for lane 1 and above */ if (column_needed > sel_col) return -EINVAL; - /* Column 0 is control column on lane 0 */ - if (params[i].lane == 0 && column_needed > sel_col - 1) + /* + * Column 0 is the control column on lane 0. However, when sel_col is + * reduced (e.g. due to a running BPT stream), sel_col already represents + * usable audio columns. + */ + if (sel_col == bus->params.col && params[i].lane == 0 && + column_needed > sel_col - 1) return -EINVAL; } } @@ -570,8 +592,10 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) struct sdw_master_runtime *m_rt; struct sdw_slave_runtime *s_rt; unsigned int curr_dr_freq = 0; + bool is_bpt_running = false; int i, l, clk_values, ret; bool is_gear = false; + int available_col; int m_lane = 0; u32 *clk_buf; @@ -596,6 +620,12 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) m_rt->stream->state < SDW_STREAM_DEPREPARED) { clk_values = 1; clk_buf = NULL; + /* + * If any BPT stream is active, the available audio columns should exclude + * the BPT columns + */ + if (m_rt->stream->state >= SDW_STREAM_PREPARED) + is_bpt_running = true; } } @@ -618,7 +648,21 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) total_col = curr_dr_freq / mstr_prop->default_frame_rate / mstr_prop->default_row; - if (curr_dr_freq * (total_col - 1) >= + /* + * available columns for audio stream on lane 0: + * - exclude control column 0 + * - if BPT is active, also exclude columns 1..bpt_hstop used by DP0 + */ + if (is_bpt_running) + available_col = total_col - bus->params.bpt_hstop - 1; + else + available_col = total_col - 1; + + if (available_col <= 0) + continue; + + /* Keep formula consistent with sdw_select_row_col() */ + if (curr_dr_freq * available_col >= bus->params.bandwidth * total_col) break; From b923d9f8407b9784c61eeed47166f0f1f3fdc10d Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 25 Jun 2025 15:10:37 +0800 Subject: [PATCH 10/10] soundwire: allow BPT and audio stream run simultaneously Now the SoundWire BPT stream and the audio stream can share the SoundWire bus bandwidth. However, it is still not allowed to have more than 1 BPT stream running simultaneously. Signed-off-by: Bard Liao --- drivers/soundwire/stream.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 47f03bcf198184..d69a371b54ad98 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1250,18 +1250,10 @@ 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); - } + if (stream->type == SDW_STREAM_BPT && bus->bpt_stream_refcount > 0) { + dev_err(bus->dev, "%s: BPT stream already allocated\n", + __func__); + return ERR_PTR(-EAGAIN); } m_rt = kzalloc_obj(*m_rt);