diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c index 96a7f9709720b..5a54b10daf77a 100644 --- a/drivers/soundwire/amd_manager.c +++ b/drivers/soundwire/amd_manager.c @@ -384,7 +384,7 @@ static u32 amd_sdw_read_ping_status(struct sdw_bus *bus) return slave_stat; } -static int amd_sdw_compute_params(struct sdw_bus *bus) +static int amd_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { struct sdw_transport_data t_data = {0}; struct sdw_master_runtime *m_rt; @@ -410,7 +410,7 @@ static int amd_sdw_compute_params(struct sdw_bus *bus) sdw_fill_xport_params(&p_rt->transport_params, p_rt->num, false, SDW_BLK_GRP_CNT_1, sample_int, port_bo, port_bo >> 8, hstart, hstop, - SDW_BLK_PKG_PER_PORT, 0x0); + SDW_BLK_PKG_PER_PORT, p_rt->lane); sdw_fill_port_params(&p_rt->port_params, p_rt->num, bps, @@ -1190,6 +1190,7 @@ static int __maybe_unused amd_resume_runtime(struct device *dev) if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) { return amd_sdw_clock_stop_exit(amd_manager); } else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) { + writel(0x00, amd_manager->acp_mmio + ACP_SW_WAKE_EN(amd_manager->instance)); val = readl(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL); if (val) { val |= AMD_SDW_CLK_RESUME_REQ; diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index d1dc62c34f1cf..9b295fc9acd53 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -813,6 +813,16 @@ void sdw_extract_slave_id(struct sdw_bus *bus, } EXPORT_SYMBOL(sdw_extract_slave_id); +bool is_clock_scaling_supported_by_slave(struct sdw_slave *slave) +{ + /* + * Dynamic scaling is a defined by SDCA. However, some devices expose the class ID but + * can't support dynamic scaling. We might need a quirk to handle such devices. + */ + return slave->id.class_id; +} +EXPORT_SYMBOL(is_clock_scaling_supported_by_slave); + static int sdw_program_device_num(struct sdw_bus *bus, bool *programmed) { u8 buf[SDW_NUM_DEV_ID_REGISTERS] = {0}; @@ -1276,23 +1286,12 @@ int sdw_configure_dpn_intr(struct sdw_slave *slave, return ret; } -static int sdw_slave_set_frequency(struct sdw_slave *slave) +int sdw_slave_get_scale_index(struct sdw_slave *slave, u8 *base) { u32 mclk_freq = slave->bus->prop.mclk_freq; u32 curr_freq = slave->bus->params.curr_dr_freq >> 1; unsigned int scale; u8 scale_index; - u8 base; - int ret; - - /* - * frequency base and scale registers are required for SDCA - * devices. They may also be used for 1.2+/non-SDCA devices. - * Driver can set the property, we will need a DisCo property - * to discover this case from platform firmware. - */ - if (!slave->id.class_id && !slave->prop.clock_reg_supported) - return 0; if (!mclk_freq) { dev_err(&slave->dev, @@ -1311,19 +1310,19 @@ static int sdw_slave_set_frequency(struct sdw_slave *slave) */ if (!(19200000 % mclk_freq)) { mclk_freq = 19200000; - base = SDW_SCP_BASE_CLOCK_19200000_HZ; + *base = SDW_SCP_BASE_CLOCK_19200000_HZ; } else if (!(22579200 % mclk_freq)) { mclk_freq = 22579200; - base = SDW_SCP_BASE_CLOCK_22579200_HZ; + *base = SDW_SCP_BASE_CLOCK_22579200_HZ; } else if (!(24576000 % mclk_freq)) { mclk_freq = 24576000; - base = SDW_SCP_BASE_CLOCK_24576000_HZ; + *base = SDW_SCP_BASE_CLOCK_24576000_HZ; } else if (!(32000000 % mclk_freq)) { mclk_freq = 32000000; - base = SDW_SCP_BASE_CLOCK_32000000_HZ; + *base = SDW_SCP_BASE_CLOCK_32000000_HZ; } else if (!(96000000 % mclk_freq)) { mclk_freq = 24000000; - base = SDW_SCP_BASE_CLOCK_24000000_HZ; + *base = SDW_SCP_BASE_CLOCK_24000000_HZ; } else { dev_err(&slave->dev, "Unsupported clock base, mclk %d\n", @@ -1354,6 +1353,34 @@ static int sdw_slave_set_frequency(struct sdw_slave *slave) } scale_index++; + dev_dbg(&slave->dev, + "Configured bus base %d, scale %d, mclk %d, curr_freq %d\n", + *base, scale_index, mclk_freq, curr_freq); + + return scale_index; +} +EXPORT_SYMBOL(sdw_slave_get_scale_index); + +static int sdw_slave_set_frequency(struct sdw_slave *slave) +{ + int scale_index; + u8 base; + int ret; + + /* + * frequency base and scale registers are required for SDCA + * devices. They may also be used for 1.2+/non-SDCA devices. + * Driver can set the property directly, for now there's no + * DisCo property to discover support for the scaling registers + * from platform firmware. + */ + if (!slave->id.class_id && !slave->prop.clock_reg_supported) + return 0; + + scale_index = sdw_slave_get_scale_index(slave, &base); + if (scale_index < 0) + return scale_index; + ret = sdw_write_no_pm(slave, SDW_SCP_BUS_CLOCK_BASE, base); if (ret < 0) { dev_err(&slave->dev, @@ -1373,10 +1400,6 @@ static int sdw_slave_set_frequency(struct sdw_slave *slave) dev_err(&slave->dev, "SDW_SCP_BUSCLOCK_SCALE_B1 write failed:%d\n", ret); - dev_dbg(&slave->dev, - "Configured bus base %d, scale %d, mclk %d, curr_freq %d\n", - base, scale_index, mclk_freq, curr_freq); - return ret; } diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index fda6b24ac2da9..fc990171b3f75 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -90,6 +90,7 @@ int sdw_find_col_index(int col); * @transport_params: Transport parameters * @port_params: Port parameters * @port_node: List node for Master or Slave port_list + * @lane: Which lane is used * * SoundWire spec has no mention of ports for Master interface but the * concept is logically extended. @@ -100,6 +101,7 @@ struct sdw_port_runtime { struct sdw_transport_params transport_params; struct sdw_port_params port_params; struct list_head port_node; + unsigned int lane; }; /** @@ -149,6 +151,7 @@ struct sdw_transport_data { int hstop; int block_offset; int sub_block_offset; + unsigned int lane; }; struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave, diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 77dc094075e13..e98d5db81b1ce 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -167,9 +167,6 @@ static int sdw_drv_remove(struct device *dev) slave->probed = false; - if (slave->prop.use_domain_irq) - sdw_irq_dispose_mapping(slave); - mutex_unlock(&slave->sdw_dev_lock); if (drv->remove) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index b9316207c3ab2..59965f43c2fb0 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -18,6 +18,7 @@ struct sdw_group_params { unsigned int rate; + unsigned int lane; int full_bw; int payload_bw; int hwidth; @@ -27,6 +28,7 @@ struct sdw_group { unsigned int count; unsigned int max_size; unsigned int *rates; + unsigned int *lanes; }; void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt, @@ -48,6 +50,9 @@ void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt, slave_total_ch = 0; list_for_each_entry(p_rt, &s_rt->port_list, port_node) { + if (p_rt->lane != t_data->lane) + continue; + ch = hweight32(p_rt->ch_mask); sdw_fill_xport_params(&p_rt->transport_params, @@ -56,7 +61,7 @@ void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt, sample_int, port_bo, port_bo >> 8, t_data->hstart, t_data->hstop, - SDW_BLK_PKG_PER_PORT, 0x0); + SDW_BLK_PKG_PER_PORT, p_rt->lane); sdw_fill_port_params(&p_rt->port_params, p_rt->num, bps, @@ -105,11 +110,13 @@ static void sdw_compute_master_ports(struct sdw_master_runtime *m_rt, t_data.hstart = hstart; list_for_each_entry(p_rt, &m_rt->port_list, port_node) { + if (p_rt->lane != params->lane) + continue; sdw_fill_xport_params(&p_rt->transport_params, p_rt->num, false, SDW_BLK_GRP_CNT_1, sample_int, *port_bo, (*port_bo) >> 8, hstart, hstop, - SDW_BLK_PKG_PER_PORT, 0x0); + SDW_BLK_PKG_PER_PORT, p_rt->lane); sdw_fill_port_params(&p_rt->port_params, p_rt->num, bps, @@ -131,6 +138,7 @@ static void sdw_compute_master_ports(struct sdw_master_runtime *m_rt, (*port_bo) += bps * ch; } + t_data.lane = params->lane; sdw_compute_slave_ports(m_rt, &t_data); } @@ -138,69 +146,107 @@ static void _sdw_compute_port_params(struct sdw_bus *bus, struct sdw_group_params *params, int count) { struct sdw_master_runtime *m_rt; - int hstop = bus->params.col - 1; - int port_bo, i; + int port_bo, i, l; + int hstop; /* Run loop for all groups to compute transport parameters */ - for (i = 0; i < count; i++) { - port_bo = 1; + for (l = 0; l < SDW_MAX_LANES; l++) { + if (l > 0 && !bus->lane_used_bandwidth[l]) + continue; + /* reset hstop for each lane */ + hstop = bus->params.col - 1; + for (i = 0; i < count; i++) { + if (params[i].lane != l) + continue; + port_bo = 1; - list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { - sdw_compute_master_ports(m_rt, ¶ms[i], &port_bo, hstop); - } + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + sdw_compute_master_ports(m_rt, ¶ms[i], &port_bo, hstop); + } - hstop = hstop - params[i].hwidth; + hstop = hstop - params[i].hwidth; + } } } static int sdw_compute_group_params(struct sdw_bus *bus, + struct sdw_stream_runtime *stream, struct sdw_group_params *params, - int *rates, int count) + struct sdw_group *group) { struct sdw_master_runtime *m_rt; + struct sdw_port_runtime *p_rt; int sel_col = bus->params.col; unsigned int rate, bps, ch; - int i, column_needed = 0; + int i, l, column_needed; /* Calculate bandwidth per group */ - for (i = 0; i < count; i++) { - params[i].rate = rates[i]; + for (i = 0; i < group->count; i++) { + params[i].rate = group->rates[i]; + params[i].lane = group->lanes[i]; params[i].full_bw = bus->params.curr_dr_freq / params[i].rate; } list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { - rate = m_rt->stream->params.rate; - bps = m_rt->stream->params.bps; - ch = m_rt->ch_count; + if (m_rt->stream == stream) { + /* Only runtime during prepare should be added */ + if (stream->state != SDW_STREAM_CONFIGURED) + continue; + } else { + /* + * Include runtimes with running (ENABLED state) and paused (DISABLED state) + * streams + */ + if (m_rt->stream->state != SDW_STREAM_ENABLED && + m_rt->stream->state != SDW_STREAM_DISABLED) + 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; + ch = hweight32(p_rt->ch_mask); - for (i = 0; i < count; i++) { - if (rate == params[i].rate) - params[i].payload_bw += bps * ch; + for (i = 0; i < group->count; i++) { + if (rate == params[i].rate && p_rt->lane == params[i].lane) + params[i].payload_bw += bps * ch; + } } } - for (i = 0; i < count; i++) { - params[i].hwidth = (sel_col * - params[i].payload_bw + params[i].full_bw - 1) / - params[i].full_bw; + for (l = 0; l < SDW_MAX_LANES; l++) { + if (l > 0 && !bus->lane_used_bandwidth[l]) + continue; + /* reset column_needed for each lane */ + column_needed = 0; + for (i = 0; i < group->count; i++) { + if (params[i].lane != l) + continue; + + params[i].hwidth = (sel_col * params[i].payload_bw + + params[i].full_bw - 1) / params[i].full_bw; - column_needed += params[i].hwidth; + column_needed += params[i].hwidth; + /* 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) + return -EINVAL; + } } - if (column_needed > sel_col - 1) - return -EINVAL; return 0; } static int sdw_add_element_group_count(struct sdw_group *group, - unsigned int rate) + unsigned int rate, unsigned int lane) { int num = group->count; int i; for (i = 0; i <= num; i++) { - if (rate == group->rates[i]) + if (rate == group->rates[i] && lane == group->lanes[i]) break; if (i != num) @@ -208,6 +254,7 @@ static int sdw_add_element_group_count(struct sdw_group *group, if (group->count >= group->max_size) { unsigned int *rates; + unsigned int *lanes; group->max_size += 1; rates = krealloc(group->rates, @@ -215,10 +262,20 @@ static int sdw_add_element_group_count(struct sdw_group *group, GFP_KERNEL); if (!rates) return -ENOMEM; + group->rates = rates; + + lanes = krealloc(group->lanes, + (sizeof(int) * group->max_size), + GFP_KERNEL); + if (!lanes) + return -ENOMEM; + + group->lanes = lanes; } - group->rates[group->count++] = rate; + group->rates[group->count] = rate; + group->lanes[group->count++] = lane; } return 0; @@ -228,6 +285,7 @@ static int sdw_get_group_count(struct sdw_bus *bus, struct sdw_group *group) { struct sdw_master_runtime *m_rt; + struct sdw_port_runtime *p_rt; unsigned int rate; int ret = 0; @@ -237,17 +295,32 @@ static int sdw_get_group_count(struct sdw_bus *bus, if (!group->rates) return -ENOMEM; + group->lanes = kcalloc(group->max_size, sizeof(int), GFP_KERNEL); + if (!group->lanes) { + kfree(group->rates); + group->rates = NULL; + return -ENOMEM; + } + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + if (m_rt->stream->state == SDW_STREAM_DEPREPARED) + continue; + rate = m_rt->stream->params.rate; if (m_rt == list_first_entry(&bus->m_rt_list, struct sdw_master_runtime, bus_node)) { group->rates[group->count++] = rate; - - } else { - ret = sdw_add_element_group_count(group, rate); + } + /* + * Different ports could use different lane, add group element + * even if m_rt is the first entry + */ + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { + ret = sdw_add_element_group_count(group, rate, p_rt->lane); if (ret < 0) { kfree(group->rates); + kfree(group->lanes); return ret; } } @@ -260,8 +333,9 @@ static int sdw_get_group_count(struct sdw_bus *bus, * sdw_compute_port_params: Compute transport and port parameters * * @bus: SDW Bus instance + * @stream: Soundwire stream */ -static int sdw_compute_port_params(struct sdw_bus *bus) +static int sdw_compute_port_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { struct sdw_group_params *params = NULL; struct sdw_group group; @@ -281,8 +355,7 @@ static int sdw_compute_port_params(struct sdw_bus *bus) } /* Compute transport parameters for grouped streams */ - ret = sdw_compute_group_params(bus, params, - &group.rates[0], group.count); + ret = sdw_compute_group_params(bus, stream, params, &group); if (ret < 0) goto free_params; @@ -292,6 +365,7 @@ static int sdw_compute_port_params(struct sdw_bus *bus) kfree(params); out: kfree(group.rates); + kfree(group.lanes); return ret; } @@ -299,7 +373,6 @@ static int sdw_compute_port_params(struct sdw_bus *bus) static int sdw_select_row_col(struct sdw_bus *bus, int clk_freq) { struct sdw_master_prop *prop = &bus->prop; - int frame_int, frame_freq; int r, c; for (c = 0; c < SDW_FRAME_COLS; c++) { @@ -308,11 +381,8 @@ static int sdw_select_row_col(struct sdw_bus *bus, int clk_freq) sdw_cols[c] != prop->default_col) continue; - frame_int = sdw_rows[r] * sdw_cols[c]; - frame_freq = clk_freq / frame_int; - - if ((clk_freq - (frame_freq * SDW_FRAME_CTRL_BITS)) < - bus->params.bandwidth) + if (clk_freq * (sdw_cols[c] - 1) < + bus->params.bandwidth * sdw_cols[c]) continue; bus->params.row = sdw_rows[r]; @@ -324,6 +394,95 @@ static int sdw_select_row_col(struct sdw_bus *bus, int clk_freq) return -EINVAL; } +static bool is_clock_scaling_supported(struct sdw_bus *bus) +{ + struct sdw_master_runtime *m_rt; + struct sdw_slave_runtime *s_rt; + + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) + list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) + if (!is_clock_scaling_supported_by_slave(s_rt->slave)) + return false; + + return true; +} + +/** + * is_lane_connected_to_all_peripherals: Check if the given manager lane connects to all peripherals + * So that all peripherals can use the manager lane. + * + * @m_rt: Manager runtime + * @lane: Lane number + */ +static bool is_lane_connected_to_all_peripherals(struct sdw_master_runtime *m_rt, unsigned int lane) +{ + struct sdw_slave_prop *slave_prop; + struct sdw_slave_runtime *s_rt; + int i; + + list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { + slave_prop = &s_rt->slave->prop; + for (i = 1; i < SDW_MAX_LANES; i++) { + if (slave_prop->lane_maps[i] == lane) { + dev_dbg(&s_rt->slave->dev, + "M lane %d is connected to P lane %d\n", + lane, i); + break; + } + } + if (i == SDW_MAX_LANES) { + dev_dbg(&s_rt->slave->dev, "M lane %d is not connected\n", lane); + return false; + } + } + return true; +} + +static int get_manager_lane(struct sdw_bus *bus, struct sdw_master_runtime *m_rt, + struct sdw_slave_runtime *s_rt, unsigned int curr_dr_freq) +{ + struct sdw_slave_prop *slave_prop = &s_rt->slave->prop; + struct sdw_port_runtime *m_p_rt; + unsigned int required_bandwidth; + int m_lane; + int l; + + for (l = 1; l < SDW_MAX_LANES; l++) { + if (!slave_prop->lane_maps[l]) + continue; + + required_bandwidth = 0; + list_for_each_entry(m_p_rt, &m_rt->port_list, port_node) { + required_bandwidth += m_rt->stream->params.rate * + hweight32(m_p_rt->ch_mask) * + m_rt->stream->params.bps; + } + if (required_bandwidth <= + curr_dr_freq - bus->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])) { + dev_dbg(bus->dev, + "Not all Peripherals are connected to M lane %d\n", + slave_prop->lane_maps[l]); + continue; + } + 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; + /* + * Use non-zero manager lane, subtract the lane 0 + * bandwidth that is already calculated + */ + bus->params.bandwidth -= required_bandwidth; + return m_lane; + } + } + + /* No available multi lane found, only lane 0 can be used */ + return 0; +} + /** * sdw_compute_bus_params: Compute bus parameters * @@ -331,10 +490,16 @@ static int sdw_select_row_col(struct sdw_bus *bus, int clk_freq) */ static int sdw_compute_bus_params(struct sdw_bus *bus) { - unsigned int curr_dr_freq = 0; struct sdw_master_prop *mstr_prop = &bus->prop; - int i, clk_values, ret; + struct sdw_slave_prop *slave_prop; + struct sdw_port_runtime *m_p_rt; + struct sdw_port_runtime *s_p_rt; + struct sdw_master_runtime *m_rt; + struct sdw_slave_runtime *s_rt; + unsigned int curr_dr_freq = 0; + int i, l, clk_values, ret; bool is_gear = false; + int m_lane = 0; u32 *clk_buf; if (mstr_prop->num_clk_gears) { @@ -349,6 +514,10 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) clk_buf = NULL; } + /* If dynamic scaling is not supported, don't try higher freq */ + if (!is_clock_scaling_supported(bus)) + clk_values = 1; + for (i = 0; i < clk_values; i++) { if (!clk_buf) curr_dr_freq = bus->params.max_dr_freq; @@ -357,10 +526,26 @@ 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 <= bus->params.bandwidth) - continue; + if (curr_dr_freq * (mstr_prop->default_col - 1) >= + bus->params.bandwidth * mstr_prop->default_col) + break; - break; + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + /* + * 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 + * multi-lane if we can't find any available lane for the first Peripheral. + */ + s_rt = list_first_entry(&m_rt->slave_rt_list, + struct sdw_slave_runtime, m_rt_node); + + /* + * Find the available Manager lane that connected to the first Peripheral. + */ + m_lane = get_manager_lane(bus, m_rt, s_rt, curr_dr_freq); + if (m_lane > 0) + goto out; + } /* * TODO: Check all the Slave(s) port(s) audio modes and find @@ -374,6 +559,38 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) __func__, bus->params.bandwidth); return -EINVAL; } +out: + /* multilane can be used */ + if (m_lane > 0) { + /* Set Peripheral lanes */ + list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { + slave_prop = &s_rt->slave->prop; + for (l = 1; l < SDW_MAX_LANES; l++) { + if (slave_prop->lane_maps[l] == m_lane) { + list_for_each_entry(s_p_rt, &s_rt->port_list, port_node) { + s_p_rt->lane = l; + dev_dbg(&s_rt->slave->dev, + "Set P lane %d for port %d\n", + l, s_p_rt->num); + } + break; + } + } + } + /* + * Set Manager lanes. Configure the last m_rt in bus->m_rt_list only since + * we don't want to touch other m_rts that are already working. + */ + list_for_each_entry(m_p_rt, &m_rt->port_list, port_node) { + m_p_rt->lane = m_lane; + } + } + + 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; ret = sdw_select_row_col(bus, curr_dr_freq); if (ret < 0) { @@ -390,8 +607,9 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) * sdw_compute_params: Compute bus, transport and port parameters * * @bus: SDW Bus instance + * @stream: Soundwire stream */ -int sdw_compute_params(struct sdw_bus *bus) +int sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { int ret; @@ -401,7 +619,7 @@ int sdw_compute_params(struct sdw_bus *bus) return ret; /* Compute transport and port params */ - ret = sdw_compute_port_params(bus); + ret = sdw_compute_port_params(bus, stream); if (ret < 0) { dev_err(bus->dev, "Compute transport params failed: %d\n", ret); return ret; diff --git a/drivers/soundwire/irq.c b/drivers/soundwire/irq.c index 0c08cebb1235c..c237e6d0766b3 100644 --- a/drivers/soundwire/irq.c +++ b/drivers/soundwire/irq.c @@ -46,14 +46,18 @@ void sdw_irq_delete(struct sdw_bus *bus) irq_domain_remove(bus->domain); } +static void sdw_irq_dispose_mapping(void *data) +{ + struct sdw_slave *slave = data; + + irq_dispose_mapping(irq_find_mapping(slave->bus->domain, slave->dev_num)); +} + void sdw_irq_create_mapping(struct sdw_slave *slave) { slave->irq = irq_create_mapping(slave->bus->domain, slave->dev_num); if (!slave->irq) dev_warn(&slave->dev, "Failed to map IRQ\n"); -} -void sdw_irq_dispose_mapping(struct sdw_slave *slave) -{ - irq_dispose_mapping(irq_find_mapping(slave->bus->domain, slave->dev_num)); + devm_add_action_or_reset(&slave->dev, sdw_irq_dispose_mapping, slave); } diff --git a/drivers/soundwire/irq.h b/drivers/soundwire/irq.h index 58a58046d92b8..86e2318409dab 100644 --- a/drivers/soundwire/irq.h +++ b/drivers/soundwire/irq.h @@ -16,7 +16,6 @@ int sdw_irq_create(struct sdw_bus *bus, struct fwnode_handle *fwnode); void sdw_irq_delete(struct sdw_bus *bus); void sdw_irq_create_mapping(struct sdw_slave *slave); -void sdw_irq_dispose_mapping(struct sdw_slave *slave); #else /* CONFIG_IRQ_DOMAIN */ @@ -34,10 +33,6 @@ static inline void sdw_irq_create_mapping(struct sdw_slave *slave) { } -static inline void sdw_irq_dispose_mapping(struct sdw_slave *slave) -{ -} - #endif /* CONFIG_IRQ_DOMAIN */ #endif /* __SDW_IRQ_H */ diff --git a/drivers/soundwire/mipi_disco.c b/drivers/soundwire/mipi_disco.c index 9d59f486edbe0..65afb28ef8fab 100644 --- a/drivers/soundwire/mipi_disco.c +++ b/drivers/soundwire/mipi_disco.c @@ -366,6 +366,44 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave, return 0; } +/* + * In MIPI DisCo spec for SoundWire, lane mapping for a slave device is done with + * mipi-sdw-lane-x-mapping properties, where x is 1..7, and the values for those + * properties are mipi-sdw-manager-lane-x or mipi-sdw-peripheral-link-y, where x + * is an integer between 1 to 7 if the lane is connected to a manager lane, y is a + * character between A to E if the lane is connected to another peripheral lane. + */ +int sdw_slave_read_lane_mapping(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + struct device *dev = &slave->dev; + char prop_name[30]; + const char *prop_val; + size_t len; + int ret, i; + u8 lane; + + for (i = 0; i < SDW_MAX_LANES; i++) { + snprintf(prop_name, sizeof(prop_name), "mipi-sdw-lane-%d-mapping", i); + ret = device_property_read_string(dev, prop_name, &prop_val); + if (ret) + continue; + + len = strlen(prop_val); + if (len < 1) + return -EINVAL; + + /* The last character is enough to identify the connection */ + ret = kstrtou8(&prop_val[len - 1], 10, &lane); + if (ret) + return ret; + if (in_range(lane, 1, SDW_MAX_LANES - 1)) + prop->lane_maps[i] = lane; + } + return 0; +} +EXPORT_SYMBOL(sdw_slave_read_lane_mapping); + /** * sdw_slave_read_prop() - Read Slave properties * @slave: SDW Slave @@ -486,6 +524,6 @@ int sdw_slave_read_prop(struct sdw_slave *slave) sdw_slave_read_dpn(slave, prop->sink_dpn_prop, nval, prop->sink_ports, "sink"); - return 0; + return sdw_slave_read_lane_mapping(slave); } EXPORT_SYMBOL(sdw_slave_read_prop); diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index e00c5ac496a68..0f45e3404756f 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -1072,7 +1072,7 @@ static const struct sdw_master_ops qcom_swrm_ops = { .pre_bank_switch = qcom_swrm_pre_bank_switch, }; -static int qcom_swrm_compute_params(struct sdw_bus *bus) +static int qcom_swrm_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus); struct sdw_master_runtime *m_rt; diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 7aa4900dcf317..e9df503332bbd 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -629,8 +629,44 @@ static int sdw_notify_config(struct sdw_master_runtime *m_rt) static int sdw_program_params(struct sdw_bus *bus, bool prepare) { struct sdw_master_runtime *m_rt; + struct sdw_slave *slave; int ret = 0; + u32 addr1; + + /* Check if all Peripherals comply with SDCA */ + list_for_each_entry(slave, &bus->slaves, node) { + if (!slave->dev_num_sticky) + continue; + if (!is_clock_scaling_supported_by_slave(slave)) { + dev_dbg(&slave->dev, "The Peripheral doesn't comply with SDCA\n"); + goto manager_runtime; + } + } + + if (bus->params.next_bank) + addr1 = SDW_SCP_BUSCLOCK_SCALE_B1; + else + addr1 = SDW_SCP_BUSCLOCK_SCALE_B0; + + /* Program SDW_SCP_BUSCLOCK_SCALE if all Peripherals comply with SDCA */ + list_for_each_entry(slave, &bus->slaves, node) { + int scale_index; + u8 base; + + if (!slave->dev_num_sticky) + continue; + scale_index = sdw_slave_get_scale_index(slave, &base); + if (scale_index < 0) + return scale_index; + + ret = sdw_write_no_pm(slave, addr1, scale_index); + if (ret < 0) { + dev_err(&slave->dev, "SDW_SCP_BUSCLOCK_SCALE register write failed\n"); + return ret; + } + } +manager_runtime: list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { /* @@ -1383,7 +1419,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream, /* Compute params */ if (bus->compute_params) { - ret = bus->compute_params(bus); + ret = bus->compute_params(bus, stream); if (ret < 0) { dev_err(bus->dev, "Compute params failed: %d\n", ret); @@ -1642,9 +1678,19 @@ EXPORT_SYMBOL(sdw_disable_stream); static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) { struct sdw_master_runtime *m_rt; + struct sdw_port_runtime *p_rt; + unsigned int multi_lane_bandwidth; + unsigned int bandwidth; struct sdw_bus *bus; + int state = stream->state; int ret = 0; + /* + * first mark the state as DEPREPARED so that it is not taken into account + * for bit allocation + */ + stream->state = SDW_STREAM_DEPREPARED; + list_for_each_entry(m_rt, &stream->master_list, stream_node) { bus = m_rt->bus; /* De-prepare port(s) */ @@ -1652,19 +1698,34 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) if (ret < 0) { dev_err(bus->dev, "De-prepare port(s) failed: %d\n", ret); + stream->state = state; return ret; } + multi_lane_bandwidth = 0; + + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { + if (!p_rt->lane) + continue; + + 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]) + p_rt->lane = 0; + } /* TODO: Update this during Device-Device support */ - bus->params.bandwidth -= m_rt->stream->params.rate * - m_rt->ch_count * m_rt->stream->params.bps; + bandwidth = m_rt->stream->params.rate * m_rt->ch_count * m_rt->stream->params.bps; + bus->params.bandwidth -= bandwidth - multi_lane_bandwidth; /* Compute params */ if (bus->compute_params) { - ret = bus->compute_params(bus); + ret = bus->compute_params(bus, stream); if (ret < 0) { dev_err(bus->dev, "Compute params failed: %d\n", ret); + stream->state = state; return ret; } } @@ -1673,11 +1734,11 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) ret = sdw_program_params(bus, false); if (ret < 0) { dev_err(bus->dev, "%s: Program params failed: %d\n", __func__, ret); + stream->state = state; return ret; } } - stream->state = SDW_STREAM_DEPREPARED; return do_bank_switch(stream); } diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index bd9836690da62..2d6c303177925 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -54,6 +54,8 @@ struct sdw_slave; #define SDW_MAX_PORTS 15 #define SDW_VALID_PORT_RANGE(n) ((n) < SDW_MAX_PORTS && (n) >= 1) +#define SDW_MAX_LANES 8 + enum { SDW_PORT_DIRN_SINK = 0, SDW_PORT_DIRN_SOURCE, @@ -356,6 +358,7 @@ struct sdw_dpn_prop { * and masks are supported * @commit_register_supported: is PCP_Commit register supported * @scp_int1_mask: SCP_INT1_MASK desired settings + * @lane_maps: Lane mapping for the slave, only valid if lane_control_support is set * @clock_reg_supported: the Peripheral implements the clock base and scale * registers introduced with the SoundWire 1.2 specification. SDCA devices * do not need to set this boolean property as the registers are required. @@ -385,6 +388,7 @@ struct sdw_slave_prop { u32 sdca_interrupt_register_list; u8 commit_register_supported; u8 scp_int1_mask; + u8 lane_maps[SDW_MAX_LANES]; bool clock_reg_supported; bool use_domain_irq; }; @@ -450,6 +454,7 @@ struct sdw_master_prop { int sdw_master_read_prop(struct sdw_bus *bus); int sdw_slave_read_prop(struct sdw_slave *slave); +int sdw_slave_read_lane_mapping(struct sdw_slave *slave); /* * SDW Slave Structures and APIs @@ -850,77 +855,6 @@ struct sdw_master_ops { int dev_num); }; -/** - * struct sdw_bus - SoundWire bus - * @dev: Shortcut to &bus->md->dev to avoid changing the entire code. - * @md: Master device - * @bus_lock_key: bus lock key associated to @bus_lock - * @bus_lock: bus lock - * @slaves: list of Slaves on this bus - * @msg_lock_key: message lock key associated to @msg_lock - * @msg_lock: message lock - * @m_rt_list: List of Master instance of all stream(s) running on Bus. This - * is used to compute and program bus bandwidth, clock, frame shape, - * transport and port parameters - * @defer_msg: Defer message - * @params: Current bus parameters - * @stream_refcount: number of streams currently using this bus - * @ops: Master callback ops - * @port_ops: Master port callback ops - * @prop: Master properties - * @vendor_specific_prop: pointer to non-standard properties - * @hw_sync_min_links: Number of links used by a stream above which - * hardware-based synchronization is required. This value is only - * meaningful if multi_link is set. If set to 1, hardware-based - * synchronization will be used even if a stream only uses a single - * SoundWire segment. - * @controller_id: system-unique controller ID. If set to -1, the bus @id will be used. - * @link_id: Link id number, can be 0 to N, unique for each Controller - * @id: bus system-wide unique id - * @compute_params: points to Bus resource management implementation - * @assigned: Bitmap for Slave device numbers. - * Bit set implies used number, bit clear implies unused number. - * @clk_stop_timeout: Clock stop timeout computed - * @bank_switch_timeout: Bank switch timeout computed - * @domain: IRQ domain - * @irq_chip: IRQ chip - * @debugfs: Bus debugfs (optional) - * @multi_link: Store bus property that indicates if multi links - * are supported. This flag is populated by drivers after reading - * appropriate firmware (ACPI/DT). - */ -struct sdw_bus { - struct device *dev; - struct sdw_master_device *md; - struct lock_class_key bus_lock_key; - struct mutex bus_lock; - struct list_head slaves; - struct lock_class_key msg_lock_key; - struct mutex msg_lock; - struct list_head m_rt_list; - struct sdw_defer defer_msg; - struct sdw_bus_params params; - int stream_refcount; - const struct sdw_master_ops *ops; - const struct sdw_master_port_ops *port_ops; - struct sdw_master_prop prop; - void *vendor_specific_prop; - int hw_sync_min_links; - int controller_id; - unsigned int link_id; - int id; - int (*compute_params)(struct sdw_bus *bus); - DECLARE_BITMAP(assigned, SDW_MAX_DEVICES); - unsigned int clk_stop_timeout; - u32 bank_switch_timeout; - struct irq_chip irq_chip; - struct irq_domain *domain; -#ifdef CONFIG_DEBUG_FS - struct dentry *debugfs; -#endif - bool multi_link; -}; - int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, struct fwnode_handle *fwnode); void sdw_bus_master_delete(struct sdw_bus *bus); @@ -1010,10 +944,83 @@ struct sdw_stream_runtime { struct list_head master_list; }; +/** + * struct sdw_bus - SoundWire bus + * @dev: Shortcut to &bus->md->dev to avoid changing the entire code. + * @md: Master device + * @bus_lock_key: bus lock key associated to @bus_lock + * @bus_lock: bus lock + * @slaves: list of Slaves on this bus + * @msg_lock_key: message lock key associated to @msg_lock + * @msg_lock: message lock + * @m_rt_list: List of Master instance of all stream(s) running on Bus. This + * is used to compute and program bus bandwidth, clock, frame shape, + * transport and port parameters + * @defer_msg: Defer message + * @params: Current bus parameters + * @stream_refcount: number of streams currently using this bus + * @ops: Master callback ops + * @port_ops: Master port callback ops + * @prop: Master properties + * @vendor_specific_prop: pointer to non-standard properties + * @hw_sync_min_links: Number of links used by a stream above which + * hardware-based synchronization is required. This value is only + * meaningful if multi_link is set. If set to 1, hardware-based + * synchronization will be used even if a stream only uses a single + * SoundWire segment. + * @controller_id: system-unique controller ID. If set to -1, the bus @id will be used. + * @link_id: Link id number, can be 0 to N, unique for each Controller + * @id: bus system-wide unique id + * @compute_params: points to Bus resource management implementation + * @assigned: Bitmap for Slave device numbers. + * Bit set implies used number, bit clear implies unused number. + * @clk_stop_timeout: Clock stop timeout computed + * @bank_switch_timeout: Bank switch timeout computed + * @domain: IRQ domain + * @irq_chip: IRQ chip + * @debugfs: Bus debugfs (optional) + * @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; + struct sdw_master_device *md; + struct lock_class_key bus_lock_key; + struct mutex bus_lock; + struct list_head slaves; + struct lock_class_key msg_lock_key; + struct mutex msg_lock; + struct list_head m_rt_list; + struct sdw_defer defer_msg; + struct sdw_bus_params params; + int stream_refcount; + const struct sdw_master_ops *ops; + const struct sdw_master_port_ops *port_ops; + struct sdw_master_prop prop; + void *vendor_specific_prop; + int hw_sync_min_links; + int controller_id; + unsigned int link_id; + int id; + int (*compute_params)(struct sdw_bus *bus, struct sdw_stream_runtime *stream); + DECLARE_BITMAP(assigned, SDW_MAX_DEVICES); + unsigned int clk_stop_timeout; + u32 bank_switch_timeout; + struct irq_chip irq_chip; + struct irq_domain *domain; +#ifdef CONFIG_DEBUG_FS + 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); void sdw_release_stream(struct sdw_stream_runtime *stream); -int sdw_compute_params(struct sdw_bus *bus); +int sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream); int sdw_stream_add_master(struct sdw_bus *bus, struct sdw_stream_config *stream_config, @@ -1034,6 +1041,7 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus); int sdw_compare_devid(struct sdw_slave *slave, struct sdw_slave_id id); void sdw_extract_slave_id(struct sdw_bus *bus, u64 addr, struct sdw_slave_id *id); +bool is_clock_scaling_supported_by_slave(struct sdw_slave *slave); #if IS_ENABLED(CONFIG_SOUNDWIRE) @@ -1045,6 +1053,8 @@ int sdw_stream_add_slave(struct sdw_slave *slave, int sdw_stream_remove_slave(struct sdw_slave *slave, struct sdw_stream_runtime *stream); +int sdw_slave_get_scale_index(struct sdw_slave *slave, u8 *base); + /* messaging and data APIs */ int sdw_read(struct sdw_slave *slave, u32 addr); int sdw_write(struct sdw_slave *slave, u32 addr, u8 value);