diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 4fb4c1acd3bb90..1b4c5efe497ffb 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); } } @@ -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; @@ -216,10 +216,23 @@ 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); } 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; + } } } } @@ -258,6 +271,8 @@ static int sdw_compute_group_params(struct sdw_bus *bus, continue; } list_for_each_entry(p_rt, &m_rt->port_list, port_node) { + if (m_rt->stream->type == SDW_STREAM_BPT) + continue; rate = m_rt->stream->params.rate; bps = m_rt->stream->params.bps; ch = hweight32(p_rt->ch_mask); @@ -355,6 +370,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; @@ -411,7 +429,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); @@ -549,8 +567,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; @@ -566,11 +586,34 @@ 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 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; + } + } + /* If dynamic scaling is not supported, don't try higher freq */ 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 @@ -578,11 +621,30 @@ 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; + + /* + * 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; 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 @@ -638,9 +700,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; @@ -655,6 +714,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 * @@ -670,9 +733,20 @@ int sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream) if (ret < 0) return ret; - 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 */ @@ -682,6 +756,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/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/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index cbf7bd3d4e7bac..20c4ce5bdb2eea 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); @@ -1480,6 +1472,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream, struct sdw_bus *bus; struct sdw_master_prop *prop; struct sdw_bus_params params; + int prev_bpt_hstop; int ret; /* Prepare Master(s) and Slave(s) port(s) associated with stream */ @@ -1487,6 +1480,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream, bus = m_rt->bus; prop = &bus->prop; memcpy(¶ms, &bus->params, sizeof(params)); + prev_bpt_hstop = bus->bpt_hstop; /* TODO: Support Asynchronous mode */ if ((prop->max_clk_freq % stream->params.rate) != 0) { @@ -1497,8 +1491,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) { @@ -1543,6 +1540,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream, restore_params: memcpy(&bus->params, ¶ms, sizeof(params)); + bus->bpt_hstop = prev_bpt_hstop; return ret; } @@ -1787,6 +1785,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; @@ -1802,6 +1804,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); 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;