soundwire updates for 6.14
- SoundWire multi lane support to use multiple lanes if supported - Stream handling of DEPREPARED state - amd wake register programming for power off mode -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmeac7MACgkQfBQHDyUj g0daUA//VD+ousUjyUrm07rNYOw+ZtwVChj+aVGSrauuuGxNOsp+RXrd+lLDjWgc ruHwegb8s9Dynq6RVJcuXQ8bKlop8FX6QMn2lewV/+dYkOk4PBwVr3gKER2lKnZV WZ4hI/bFcJyCmIf6v1pIwzc1XKiCLuZzhnPIbKTcL3cLEKssaqMTYQAsnZw15gCY GUccgFYGYe0+eNdt7BtxlAaDZN3X6ankgwxIh1/kXHz7Tiu5fbqWxBnmBAxBQtHx shK4mnK5ccWrNZYzcq0+CWvYErVg496FyZm6ZNOCZKqaOfvXwtC4ZEyEkDDCb2Ep h2eIaLaMIYOzDZt/zoV/pcwhgYtEMiyrmhsLRqgywAjweA1tf+wD/ti6fKN9L4tf YQ0Xbe13aSoFy9fQWF/26rf0WVE0oU38w6KlZ/uM/xY6gcYnzZgdOnlMNnmOZKfg GY5DvqMrUgscG6WAzwtu+Ro4zXURQWgqBY3+e4d1IVNk2u9q7pCfV5E2QhRri3q6 217+QPdPEqmQwad9vowoPfPpFpCXvs/duv7SYdU0Wm6h55ShC22hho6n2VcxQoZ7 U0DpilHWjbQxnBLLrgm3dcXC8d7V+AnA7CDk1vR1H2vDy+WzVY1xPmBGAyn1idUC Ly7oowR1yt56iqVig5cGzA1GjAsBqRX+KLyPL7EU9xQjWCBq80Q= =PnzK -----END PGP SIGNATURE----- Merge tag 'soundwire-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire Pull soundwire updates from Vinod Koul: - SoundWire multi lane support to use multiple lanes if supported - Stream handling of DEPREPARED state - AMD wake register programming for power off mode * tag 'soundwire-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire: soundwire: amd: clear wake enable register for power off mode soundwire: generic_bandwidth_allocation: count the bandwidth of active streams only SoundWire: pass stream to compute_params() soundwire: generic_bandwidth_allocation: add lane in sdw_group_params soundwire: generic_bandwidth_allocation: select data lane soundwire: generic_bandwidth_allocation: check required freq accurately soundwire: generic_bandwidth_allocation: correct clk_freq check in sdw_select_row_col Soundwire: generic_bandwidth_allocation: set frame shape on fly Soundwire: stream: program BUSCLOCK_SCALE Soundwire: add sdw_slave_get_scale_index helper soundwire: generic_bandwidth_allocation: skip DEPREPARED streams soundwire: stream: set DEPREPARED state earlier soundwire: add lane_used_bandwidth in struct sdw_bus soundwire: mipi_disco: read lane mapping properties from ACPI soundwire: add lane field in sdw_port_runtime soundwire: bus: Move irq mapping cleanup into devres
This commit is contained in:
commit
72deda0abe
11 changed files with 513 additions and 163 deletions
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
column_needed += params[i].hwidth;
|
||||
params[i].hwidth = (sel_col * params[i].payload_bw +
|
||||
params[i].full_bw - 1) / params[i].full_bw;
|
||||
|
||||
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 @@ free_params:
|
|||
kfree(params);
|
||||
out:
|
||||
kfree(group.rates);
|
||||
kfree(group.lanes);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -299,7 +373,6 @@ out:
|
|||
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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue