From 37f3d7505325ba0b99b5b4113c385e0419c6007b Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 30 Jun 2026 14:41:05 +0800 Subject: [PATCH 1/9] 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 | 43 ++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index cbf7bd3d4e7bac..5452ce3478cb2c 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 cleanup_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,27 @@ 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)); + } + +cleanup_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 89fa927246ee815717c956f67354be12e68e803b Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 4 Nov 2025 14:20:05 +0800 Subject: [PATCH 2/9] 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 4fb4c1acd3bb90..c316f67451c7b0 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 ad8f81729e2bbc81983e0542a00caddd8d7c013e Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 3 Nov 2025 14:23:51 +0800 Subject: [PATCH 3/9] 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 c316f67451c7b0..a020534138fe05 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 218559665c16e8259e3325860dffb1db7685da9f Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 7 Nov 2025 11:40:15 +0800 Subject: [PATCH 4/9] 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 a020534138fe05..be6ee3955dd7d9 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 5452ce3478cb2c..394a1c6f944c9f 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) { @@ -1822,6 +1825,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; @@ -1837,6 +1844,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 2116e410ef7a4f19cfc412be50c44fda97c3aae0 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 26 Jun 2026 16:06:38 +0800 Subject: [PATCH 5/9] 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 be6ee3955dd7d9..53eb5a5a963041 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 7aa1a969e9accc87b651a7bb0a068bdfbcff8102 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 25 Jun 2026 19:47:47 +0800 Subject: [PATCH 6/9] soundwire: add bpt_hstop in struct sdw_bus 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->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 53eb5a5a963041..36623efad97799 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->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->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->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->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->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..76f0bba3771d30 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.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.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.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 3e0d21132ef2f7..f84704a7f3311e 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -1003,6 +1003,7 @@ struct sdw_stream_runtime { * @stream_refcount: number of streams currently using this bus * @bpt_stream_refcount: number of BTP streams currently using this bus (should * be zero or one, multiple streams per link is not supported). + * @bpt_hstop: The hstop of the BPT stream. * @bpt_stream: pointer stored to handle BTP streams. * @ops: Master callback ops * @port_ops: Master port callback ops @@ -1043,6 +1044,7 @@ struct sdw_bus { struct sdw_bus_params params; int stream_refcount; int bpt_stream_refcount; + int bpt_hstop; struct sdw_stream_runtime *bpt_stream; const struct sdw_master_ops *ops; const struct sdw_master_port_ops *port_ops; From 82b6d108e2b9352f6971044e03da482049fd228a Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 30 Jun 2026 15:14:23 +0800 Subject: [PATCH 7/9] soundwire: update bpt_hstop Update bus->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 | 48 ++++++++++++++++--- drivers/soundwire/stream.c | 3 ++ 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 36623efad97799..bf99171cea385e 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->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->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,20 @@ int sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream) if (ret < 0) return ret; - bus->bpt_hstop = bus->params.col - 1; - if (stream->type == SDW_STREAM_BPT) { - sdw_compute_dp0_port_params(bus); - return 0; + if (stream->type == SDW_STREAM_BPT && stream->state == SDW_STREAM_CONFIGURED) { + /* + * Set the initial bpt_hstop when the BPT stream is preparing and it will be + * updated in sdw_compute_port_params() below. + */ + bus->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->bpt_hstop -= SDW_COL_RESERVED_FOR_AUDIO; } /* Compute transport and port params */ @@ -713,6 +737,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_CONFIGURED) { + /* No usable data columns left */ + if (bus->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); diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 394a1c6f944c9f..8b60aad197d13a 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1486,6 +1486,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream, struct list_head list_node; struct sdw_bus *bus; struct sdw_bus_params params; + int bpt_hstop; } *params_entry, *temp_entry; LIST_HEAD(params_backup_list); @@ -1503,6 +1504,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream, } params_entry->bus = bus; memcpy(¶ms_entry->params, &bus->params, sizeof(params_entry->params)); + params_entry->bpt_hstop = bus->bpt_hstop; list_add_tail(¶ms_entry->list_node, ¶ms_backup_list); /* TODO: Support Asynchronous mode */ @@ -1573,6 +1575,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream, list_for_each_entry(params_entry, ¶ms_backup_list, list_node) { memcpy(¶ms_entry->bus->params, ¶ms_entry->params, sizeof(params_entry->params)); + params_entry->bus->bpt_hstop = params_entry->bpt_hstop; } cleanup_params: From 3ec94d66d4ac1f84801f4a5a46909248ba28f2ec Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 7 Nov 2025 14:11:22 +0800 Subject: [PATCH 8/9] 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 | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index bf99171cea385e..ed4dde3f507ecc 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -267,8 +267,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, exclude the BPT columns + * BPT: col 0.. bus->bpt_hstop + * Audio: col bus->bpt_hstop + 1 .. bus->params.col - 1 + */ + sel_col = bus->params.col - bus->bpt_hstop - 1; + } } /* Don't count BPT stream bandwidth, it will use the remaining bandwidth */ @@ -303,8 +311,12 @@ 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 control column on lane 0, but sel_col is already excludes + * column 0 when it is less than bus->params.col. + */ + if (sel_col < bus->params.col && params[i].lane == 0 && + column_needed > sel_col - 1) return -EINVAL; } } @@ -570,8 +582,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 +610,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 +638,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->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 447d4cda0092b444417da5478fea09b31995fa6a Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 25 Jun 2025 15:10:37 +0800 Subject: [PATCH 9/9] 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 8b60aad197d13a..867ee6d9579b2f 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);