diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 4fb4c1acd3bb90..3887dd3bd14c0f 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); } } @@ -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; @@ -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; @@ -216,10 +216,24 @@ 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->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->params.bpt_hstop + * should be hstart - 1. so we can set bpt_hstop to hstop + * directly. + */ + bus->params.bpt_hstop = hstop; + } } } } @@ -254,9 +268,22 @@ 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 */ + 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; @@ -270,8 +297,16 @@ 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; + + /* + * 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++) { @@ -285,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; } } @@ -355,6 +395,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 +454,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); @@ -510,7 +553,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 +564,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 @@ -549,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; @@ -566,11 +611,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 +646,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->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; 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 +725,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 +739,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 +758,25 @@ 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; + /* + * 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 */ @@ -682,6 +786,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); 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/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index cbf7bd3d4e7bac..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); @@ -1479,26 +1471,47 @@ 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) { /* 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) { @@ -1539,10 +1552,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; } @@ -1787,6 +1816,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; @@ -1794,14 +1827,15 @@ 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 */ 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..cb956a778f5931 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -589,6 +589,8 @@ 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 + * @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 @@ -602,6 +604,8 @@ 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]; + int bpt_hstop; unsigned int col; unsigned int row; int s_data_mode; @@ -1027,7 +1031,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 +1065,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);