Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

soundwire: Add generic bandwidth allocation algorithm

This algorithm computes bus parameters like clock frequency, frame
shape and port transport parameters based on active stream(s) running
on the bus.

Developers can also implement their own .compute_params() callback for
specific resource management algorithm, and set if before calling
sdw_add_bus_master()

Credits: this patch is based on an earlier internal contribution by
Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. All hard-coded
values were removed from the initial contribution to use BIOS
information instead.

Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Acked-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20200908131520.5712-1-yung-chuan.liao@linux.intel.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Bard Liao and committed by
Vinod Koul
9026118f 32d2a893

+503 -2
+5
drivers/soundwire/Kconfig
··· 24 24 config SOUNDWIRE_INTEL 25 25 tristate "Intel SoundWire Master driver" 26 26 select SOUNDWIRE_CADENCE 27 + select SOUNDWIRE_GENERIC_ALLOCATION 27 28 depends on ACPI && SND_SOC 28 29 help 29 30 SoundWire Intel Master driver. ··· 41 40 If you have an Qualcomm platform which has a SoundWire Master then 42 41 enable this config option to get the SoundWire support for that 43 42 device 43 + 44 + config SOUNDWIRE_GENERIC_ALLOCATION 45 + tristate 46 + 44 47 endif
+3
drivers/soundwire/Makefile
··· 8 8 sysfs_slave.o sysfs_slave_dpn.o 9 9 obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o 10 10 11 + soundwire-generic-allocation-objs := generic_bandwidth_allocation.o 12 + obj-$(CONFIG_SOUNDWIRE_GENERIC_ALLOCATION) += soundwire-generic-allocation.o 13 + 11 14 ifdef CONFIG_DEBUG_FS 12 15 soundwire-bus-y += debugfs.o 13 16 endif
+6
drivers/soundwire/bus.c
··· 61 61 return -EINVAL; 62 62 } 63 63 64 + if (!bus->compute_params) { 65 + dev_err(bus->dev, 66 + "Bandwidth allocation not configured, compute_params no set\n"); 67 + return -EINVAL; 68 + } 69 + 64 70 mutex_init(&bus->msg_lock); 65 71 mutex_init(&bus->bus_lock); 66 72 INIT_LIST_HEAD(&bus->slaves);
+44 -2
drivers/soundwire/bus.h
··· 69 69 }; 70 70 71 71 #define SDW_DOUBLE_RATE_FACTOR 2 72 + #define SDW_STRM_RATE_GROUPING 1 72 73 73 74 extern int sdw_rows[SDW_FRAME_ROWS]; 74 75 extern int sdw_cols[SDW_FRAME_COLS]; ··· 155 154 int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave, 156 155 u32 addr, size_t count, u16 dev_num, u8 flags, u8 *buf); 157 156 157 + /* Retrieve and return channel count from channel mask */ 158 + static inline int sdw_ch_mask_to_ch(int ch_mask) 159 + { 160 + int c = 0; 161 + 162 + for (c = 0; ch_mask; ch_mask >>= 1) 163 + c += ch_mask & 1; 164 + 165 + return c; 166 + } 167 + 168 + /* Fill transport parameter data structure */ 169 + static inline void sdw_fill_xport_params(struct sdw_transport_params *params, 170 + int port_num, bool grp_ctrl_valid, 171 + int grp_ctrl, int sample_int, 172 + int off1, int off2, 173 + int hstart, int hstop, 174 + int pack_mode, int lane_ctrl) 175 + { 176 + params->port_num = port_num; 177 + params->blk_grp_ctrl_valid = grp_ctrl_valid; 178 + params->blk_grp_ctrl = grp_ctrl; 179 + params->sample_interval = sample_int; 180 + params->offset1 = off1; 181 + params->offset2 = off2; 182 + params->hstart = hstart; 183 + params->hstop = hstop; 184 + params->blk_pkg_mode = pack_mode; 185 + params->lane_ctrl = lane_ctrl; 186 + } 187 + 188 + /* Fill port parameter data structure */ 189 + static inline void sdw_fill_port_params(struct sdw_port_params *params, 190 + int port_num, int bps, 191 + int flow_mode, int data_mode) 192 + { 193 + params->num = port_num; 194 + params->bps = bps; 195 + params->flow_mode = flow_mode; 196 + params->data_mode = data_mode; 197 + } 198 + 158 199 /* Read-Modify-Write Slave register */ 159 - static inline int 160 - sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val) 200 + static inline int sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val) 161 201 { 162 202 int tmp; 163 203
+427
drivers/soundwire/generic_bandwidth_allocation.c
··· 1 + // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 2 + // Copyright(c) 2015-2020 Intel Corporation. 3 + 4 + /* 5 + * Bandwidth management algorithm based on 2^n gears 6 + * 7 + */ 8 + 9 + #include <linux/device.h> 10 + #include <linux/module.h> 11 + #include <linux/mod_devicetable.h> 12 + #include <linux/slab.h> 13 + #include <linux/soundwire/sdw.h> 14 + #include "bus.h" 15 + 16 + #define SDW_STRM_RATE_GROUPING 1 17 + 18 + struct sdw_group_params { 19 + unsigned int rate; 20 + int full_bw; 21 + int payload_bw; 22 + int hwidth; 23 + }; 24 + 25 + struct sdw_group { 26 + unsigned int count; 27 + unsigned int max_size; 28 + unsigned int *rates; 29 + }; 30 + 31 + struct sdw_transport_data { 32 + int hstart; 33 + int hstop; 34 + int block_offset; 35 + int sub_block_offset; 36 + }; 37 + 38 + static void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt, 39 + struct sdw_transport_data *t_data) 40 + { 41 + struct sdw_slave_runtime *s_rt = NULL; 42 + struct sdw_port_runtime *p_rt; 43 + int port_bo, sample_int; 44 + unsigned int rate, bps, ch = 0; 45 + unsigned int slave_total_ch; 46 + 47 + port_bo = t_data->block_offset; 48 + 49 + list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { 50 + rate = m_rt->stream->params.rate; 51 + bps = m_rt->stream->params.bps; 52 + sample_int = (m_rt->bus->params.curr_dr_freq / rate); 53 + slave_total_ch = 0; 54 + 55 + list_for_each_entry(p_rt, &s_rt->port_list, port_node) { 56 + ch = sdw_ch_mask_to_ch(p_rt->ch_mask); 57 + 58 + sdw_fill_xport_params(&p_rt->transport_params, 59 + p_rt->num, false, 60 + SDW_BLK_GRP_CNT_1, 61 + sample_int, port_bo, port_bo >> 8, 62 + t_data->hstart, 63 + t_data->hstop, 64 + (SDW_BLK_GRP_CNT_1 * ch), 0x0); 65 + 66 + sdw_fill_port_params(&p_rt->port_params, 67 + p_rt->num, bps, 68 + SDW_PORT_FLOW_MODE_ISOCH, 69 + SDW_PORT_DATA_MODE_NORMAL); 70 + 71 + port_bo += bps * ch; 72 + slave_total_ch += ch; 73 + } 74 + 75 + if (m_rt->direction == SDW_DATA_DIR_TX && 76 + m_rt->ch_count == slave_total_ch) { 77 + /* 78 + * Slave devices were configured to access all channels 79 + * of the stream, which indicates that they operate in 80 + * 'mirror mode'. Make sure we reset the port offset for 81 + * the next device in the list 82 + */ 83 + port_bo = t_data->block_offset; 84 + } 85 + } 86 + } 87 + 88 + static void sdw_compute_master_ports(struct sdw_master_runtime *m_rt, 89 + struct sdw_group_params *params, 90 + int port_bo, int hstop) 91 + { 92 + struct sdw_transport_data t_data = {0}; 93 + struct sdw_port_runtime *p_rt; 94 + struct sdw_bus *bus = m_rt->bus; 95 + int sample_int, hstart = 0; 96 + unsigned int rate, bps, ch, no_ch; 97 + 98 + rate = m_rt->stream->params.rate; 99 + bps = m_rt->stream->params.bps; 100 + ch = m_rt->ch_count; 101 + sample_int = (bus->params.curr_dr_freq / rate); 102 + 103 + if (rate != params->rate) 104 + return; 105 + 106 + t_data.hstop = hstop; 107 + hstart = hstop - params->hwidth + 1; 108 + t_data.hstart = hstart; 109 + 110 + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { 111 + no_ch = sdw_ch_mask_to_ch(p_rt->ch_mask); 112 + 113 + sdw_fill_xport_params(&p_rt->transport_params, p_rt->num, 114 + false, SDW_BLK_GRP_CNT_1, sample_int, 115 + port_bo, port_bo >> 8, hstart, hstop, 116 + (SDW_BLK_GRP_CNT_1 * no_ch), 0x0); 117 + 118 + sdw_fill_port_params(&p_rt->port_params, 119 + p_rt->num, bps, 120 + SDW_PORT_FLOW_MODE_ISOCH, 121 + SDW_PORT_DATA_MODE_NORMAL); 122 + 123 + /* Check for first entry */ 124 + if (!(p_rt == list_first_entry(&m_rt->port_list, 125 + struct sdw_port_runtime, 126 + port_node))) { 127 + port_bo += bps * ch; 128 + continue; 129 + } 130 + 131 + t_data.hstart = hstart; 132 + t_data.hstop = hstop; 133 + t_data.block_offset = port_bo; 134 + t_data.sub_block_offset = 0; 135 + port_bo += bps * ch; 136 + } 137 + 138 + sdw_compute_slave_ports(m_rt, &t_data); 139 + } 140 + 141 + static void _sdw_compute_port_params(struct sdw_bus *bus, 142 + struct sdw_group_params *params, int count) 143 + { 144 + struct sdw_master_runtime *m_rt = NULL; 145 + int hstop = bus->params.col - 1; 146 + int block_offset, port_bo, i; 147 + 148 + /* Run loop for all groups to compute transport parameters */ 149 + for (i = 0; i < count; i++) { 150 + port_bo = 1; 151 + block_offset = 1; 152 + 153 + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { 154 + sdw_compute_master_ports(m_rt, &params[i], 155 + port_bo, hstop); 156 + 157 + block_offset += m_rt->ch_count * 158 + m_rt->stream->params.bps; 159 + port_bo = block_offset; 160 + } 161 + 162 + hstop = hstop - params[i].hwidth; 163 + } 164 + } 165 + 166 + static int sdw_compute_group_params(struct sdw_bus *bus, 167 + struct sdw_group_params *params, 168 + int *rates, int count) 169 + { 170 + struct sdw_master_runtime *m_rt = NULL; 171 + int sel_col = bus->params.col; 172 + unsigned int rate, bps, ch; 173 + int i, column_needed = 0; 174 + 175 + /* Calculate bandwidth per group */ 176 + for (i = 0; i < count; i++) { 177 + params[i].rate = rates[i]; 178 + params[i].full_bw = bus->params.curr_dr_freq / params[i].rate; 179 + } 180 + 181 + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { 182 + rate = m_rt->stream->params.rate; 183 + bps = m_rt->stream->params.bps; 184 + ch = m_rt->ch_count; 185 + 186 + for (i = 0; i < count; i++) { 187 + if (rate == params[i].rate) 188 + params[i].payload_bw += bps * ch; 189 + } 190 + } 191 + 192 + for (i = 0; i < count; i++) { 193 + params[i].hwidth = (sel_col * 194 + params[i].payload_bw + params[i].full_bw - 1) / 195 + params[i].full_bw; 196 + 197 + column_needed += params[i].hwidth; 198 + } 199 + 200 + if (column_needed > sel_col - 1) 201 + return -EINVAL; 202 + 203 + return 0; 204 + } 205 + 206 + static int sdw_add_element_group_count(struct sdw_group *group, 207 + unsigned int rate) 208 + { 209 + int num = group->count; 210 + int i; 211 + 212 + for (i = 0; i <= num; i++) { 213 + if (rate == group->rates[i]) 214 + break; 215 + 216 + if (i != num) 217 + continue; 218 + 219 + if (group->count >= group->max_size) { 220 + unsigned int *rates; 221 + 222 + group->max_size += 1; 223 + rates = krealloc(group->rates, 224 + (sizeof(int) * group->max_size), 225 + GFP_KERNEL); 226 + if (!rates) 227 + return -ENOMEM; 228 + group->rates = rates; 229 + } 230 + 231 + group->rates[group->count++] = rate; 232 + } 233 + 234 + return 0; 235 + } 236 + 237 + static int sdw_get_group_count(struct sdw_bus *bus, 238 + struct sdw_group *group) 239 + { 240 + struct sdw_master_runtime *m_rt; 241 + unsigned int rate; 242 + int ret = 0; 243 + 244 + group->count = 0; 245 + group->max_size = SDW_STRM_RATE_GROUPING; 246 + group->rates = kcalloc(group->max_size, sizeof(int), GFP_KERNEL); 247 + if (!group->rates) 248 + return -ENOMEM; 249 + 250 + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { 251 + rate = m_rt->stream->params.rate; 252 + if (m_rt == list_first_entry(&bus->m_rt_list, 253 + struct sdw_master_runtime, 254 + bus_node)) { 255 + group->rates[group->count++] = rate; 256 + 257 + } else { 258 + ret = sdw_add_element_group_count(group, rate); 259 + if (ret < 0) { 260 + kfree(group->rates); 261 + return ret; 262 + } 263 + } 264 + } 265 + 266 + return ret; 267 + } 268 + 269 + /** 270 + * sdw_compute_port_params: Compute transport and port parameters 271 + * 272 + * @bus: SDW Bus instance 273 + */ 274 + static int sdw_compute_port_params(struct sdw_bus *bus) 275 + { 276 + struct sdw_group_params *params = NULL; 277 + struct sdw_group group; 278 + int ret; 279 + 280 + ret = sdw_get_group_count(bus, &group); 281 + if (ret < 0) 282 + return ret; 283 + 284 + if (group.count == 0) 285 + goto out; 286 + 287 + params = kcalloc(group.count, sizeof(*params), GFP_KERNEL); 288 + if (!params) { 289 + ret = -ENOMEM; 290 + goto out; 291 + } 292 + 293 + /* Compute transport parameters for grouped streams */ 294 + ret = sdw_compute_group_params(bus, params, 295 + &group.rates[0], group.count); 296 + if (ret < 0) 297 + goto free_params; 298 + 299 + _sdw_compute_port_params(bus, params, group.count); 300 + 301 + free_params: 302 + kfree(params); 303 + out: 304 + kfree(group.rates); 305 + 306 + return ret; 307 + } 308 + 309 + static int sdw_select_row_col(struct sdw_bus *bus, int clk_freq) 310 + { 311 + struct sdw_master_prop *prop = &bus->prop; 312 + int frame_int, frame_freq; 313 + int r, c; 314 + 315 + for (c = 0; c < SDW_FRAME_COLS; c++) { 316 + for (r = 0; r < SDW_FRAME_ROWS; r++) { 317 + if (sdw_rows[r] != prop->default_row || 318 + sdw_cols[c] != prop->default_col) 319 + continue; 320 + 321 + frame_int = sdw_rows[r] * sdw_cols[c]; 322 + frame_freq = clk_freq / frame_int; 323 + 324 + if ((clk_freq - (frame_freq * SDW_FRAME_CTRL_BITS)) < 325 + bus->params.bandwidth) 326 + continue; 327 + 328 + bus->params.row = sdw_rows[r]; 329 + bus->params.col = sdw_cols[c]; 330 + return 0; 331 + } 332 + } 333 + 334 + return -EINVAL; 335 + } 336 + 337 + /** 338 + * sdw_compute_bus_params: Compute bus parameters 339 + * 340 + * @bus: SDW Bus instance 341 + */ 342 + static int sdw_compute_bus_params(struct sdw_bus *bus) 343 + { 344 + unsigned int max_dr_freq, curr_dr_freq = 0; 345 + struct sdw_master_prop *mstr_prop = NULL; 346 + int i, clk_values, ret; 347 + bool is_gear = false; 348 + u32 *clk_buf; 349 + 350 + mstr_prop = &bus->prop; 351 + if (!mstr_prop) 352 + return -EINVAL; 353 + 354 + if (mstr_prop->num_clk_gears) { 355 + clk_values = mstr_prop->num_clk_gears; 356 + clk_buf = mstr_prop->clk_gears; 357 + is_gear = true; 358 + } else if (mstr_prop->num_clk_freq) { 359 + clk_values = mstr_prop->num_clk_freq; 360 + clk_buf = mstr_prop->clk_freq; 361 + } else { 362 + clk_values = 1; 363 + clk_buf = NULL; 364 + } 365 + 366 + max_dr_freq = mstr_prop->max_clk_freq * SDW_DOUBLE_RATE_FACTOR; 367 + 368 + for (i = 0; i < clk_values; i++) { 369 + if (!clk_buf) 370 + curr_dr_freq = max_dr_freq; 371 + else 372 + curr_dr_freq = (is_gear) ? 373 + (max_dr_freq >> clk_buf[i]) : 374 + clk_buf[i] * SDW_DOUBLE_RATE_FACTOR; 375 + 376 + if (curr_dr_freq <= bus->params.bandwidth) 377 + continue; 378 + 379 + break; 380 + 381 + /* 382 + * TODO: Check all the Slave(s) port(s) audio modes and find 383 + * whether given clock rate is supported with glitchless 384 + * transition. 385 + */ 386 + } 387 + 388 + if (i == clk_values) 389 + return -EINVAL; 390 + 391 + ret = sdw_select_row_col(bus, curr_dr_freq); 392 + if (ret < 0) 393 + return -EINVAL; 394 + 395 + bus->params.curr_dr_freq = curr_dr_freq; 396 + return 0; 397 + } 398 + 399 + /** 400 + * sdw_compute_params: Compute bus, transport and port parameters 401 + * 402 + * @bus: SDW Bus instance 403 + */ 404 + int sdw_compute_params(struct sdw_bus *bus) 405 + { 406 + int ret; 407 + 408 + /* Computes clock frequency, frame shape and frame frequency */ 409 + ret = sdw_compute_bus_params(bus); 410 + if (ret < 0) { 411 + dev_err(bus->dev, "Compute bus params failed: %d", ret); 412 + return ret; 413 + } 414 + 415 + /* Compute transport and port params */ 416 + ret = sdw_compute_port_params(bus); 417 + if (ret < 0) { 418 + dev_err(bus->dev, "Compute transport params failed: %d", ret); 419 + return ret; 420 + } 421 + 422 + return 0; 423 + } 424 + EXPORT_SYMBOL(sdw_compute_params); 425 + 426 + MODULE_LICENSE("Dual BSD/GPL"); 427 + MODULE_DESCRIPTION("SoundWire Generic Bandwidth Allocation");
+3
drivers/soundwire/intel.c
··· 1318 1318 /* set driver data, accessed by snd_soc_dai_get_drvdata() */ 1319 1319 dev_set_drvdata(dev, cdns); 1320 1320 1321 + /* use generic bandwidth allocation algorithm */ 1322 + sdw->cdns.bus.compute_params = sdw_compute_params; 1323 + 1321 1324 ret = sdw_bus_master_add(bus, dev, dev->fwnode); 1322 1325 if (ret) { 1323 1326 dev_err(dev, "sdw_bus_master_add fail: %d\n", ret);
+12
drivers/soundwire/stream.c
··· 25 25 int sdw_rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147, 26 26 96, 100, 120, 128, 150, 160, 250, 0, 27 27 192, 200, 240, 256, 72, 144, 90, 180}; 28 + EXPORT_SYMBOL(sdw_rows); 28 29 29 30 int sdw_cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; 31 + EXPORT_SYMBOL(sdw_cols); 30 32 31 33 int sdw_find_col_index(int col) 32 34 { ··· 1783 1781 /* TODO: Update this during Device-Device support */ 1784 1782 bus->params.bandwidth -= m_rt->stream->params.rate * 1785 1783 m_rt->ch_count * m_rt->stream->params.bps; 1784 + 1785 + /* Compute params */ 1786 + if (bus->compute_params) { 1787 + ret = bus->compute_params(bus); 1788 + if (ret < 0) { 1789 + dev_err(bus->dev, "Compute params failed: %d", 1790 + ret); 1791 + return ret; 1792 + } 1793 + } 1786 1794 1787 1795 /* Program params */ 1788 1796 ret = sdw_program_params(bus, false);
+3
include/linux/soundwire/sdw.h
··· 964 964 965 965 struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name); 966 966 void sdw_release_stream(struct sdw_stream_runtime *stream); 967 + 968 + int sdw_compute_params(struct sdw_bus *bus); 969 + 967 970 int sdw_stream_add_master(struct sdw_bus *bus, 968 971 struct sdw_stream_config *stream_config, 969 972 struct sdw_port_config *port_config,