1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2 // Copyright(c) 2015-2020 Intel Corporation.
5 * Bandwidth management algorithm based on 2^n gears
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>
16 #define SDW_STRM_RATE_GROUPING 1
18 struct sdw_group_params
{
27 unsigned int max_size
;
31 struct sdw_transport_data
{
38 static void sdw_compute_slave_ports(struct sdw_master_runtime
*m_rt
,
39 struct sdw_transport_data
*t_data
)
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 struct sdw_bus_params
*b_params
= &m_rt
->bus
->params
;
48 port_bo
= t_data
->block_offset
;
50 list_for_each_entry(s_rt
, &m_rt
->slave_rt_list
, m_rt_node
) {
51 rate
= m_rt
->stream
->params
.rate
;
52 bps
= m_rt
->stream
->params
.bps
;
53 sample_int
= (m_rt
->bus
->params
.curr_dr_freq
/ rate
);
56 list_for_each_entry(p_rt
, &s_rt
->port_list
, port_node
) {
57 ch
= sdw_ch_mask_to_ch(p_rt
->ch_mask
);
59 sdw_fill_xport_params(&p_rt
->transport_params
,
62 sample_int
, port_bo
, port_bo
>> 8,
65 (SDW_BLK_GRP_CNT_1
* ch
), 0x0);
67 sdw_fill_port_params(&p_rt
->port_params
,
69 SDW_PORT_FLOW_MODE_ISOCH
,
70 b_params
->s_data_mode
);
76 if (m_rt
->direction
== SDW_DATA_DIR_TX
&&
77 m_rt
->ch_count
== slave_total_ch
) {
79 * Slave devices were configured to access all channels
80 * of the stream, which indicates that they operate in
81 * 'mirror mode'. Make sure we reset the port offset for
82 * the next device in the list
84 port_bo
= t_data
->block_offset
;
89 static void sdw_compute_master_ports(struct sdw_master_runtime
*m_rt
,
90 struct sdw_group_params
*params
,
91 int port_bo
, int hstop
)
93 struct sdw_transport_data t_data
= {0};
94 struct sdw_port_runtime
*p_rt
;
95 struct sdw_bus
*bus
= m_rt
->bus
;
96 struct sdw_bus_params
*b_params
= &bus
->params
;
97 int sample_int
, hstart
= 0;
98 unsigned int rate
, bps
, ch
, no_ch
;
100 rate
= m_rt
->stream
->params
.rate
;
101 bps
= m_rt
->stream
->params
.bps
;
103 sample_int
= (bus
->params
.curr_dr_freq
/ rate
);
105 if (rate
!= params
->rate
)
108 t_data
.hstop
= hstop
;
109 hstart
= hstop
- params
->hwidth
+ 1;
110 t_data
.hstart
= hstart
;
112 list_for_each_entry(p_rt
, &m_rt
->port_list
, port_node
) {
113 no_ch
= sdw_ch_mask_to_ch(p_rt
->ch_mask
);
115 sdw_fill_xport_params(&p_rt
->transport_params
, p_rt
->num
,
116 false, SDW_BLK_GRP_CNT_1
, sample_int
,
117 port_bo
, port_bo
>> 8, hstart
, hstop
,
118 (SDW_BLK_GRP_CNT_1
* no_ch
), 0x0);
120 sdw_fill_port_params(&p_rt
->port_params
,
122 SDW_PORT_FLOW_MODE_ISOCH
,
123 b_params
->m_data_mode
);
125 /* Check for first entry */
126 if (!(p_rt
== list_first_entry(&m_rt
->port_list
,
127 struct sdw_port_runtime
,
133 t_data
.hstart
= hstart
;
134 t_data
.hstop
= hstop
;
135 t_data
.block_offset
= port_bo
;
136 t_data
.sub_block_offset
= 0;
140 sdw_compute_slave_ports(m_rt
, &t_data
);
143 static void _sdw_compute_port_params(struct sdw_bus
*bus
,
144 struct sdw_group_params
*params
, int count
)
146 struct sdw_master_runtime
*m_rt
= NULL
;
147 int hstop
= bus
->params
.col
- 1;
148 int block_offset
, port_bo
, i
;
150 /* Run loop for all groups to compute transport parameters */
151 for (i
= 0; i
< count
; i
++) {
155 list_for_each_entry(m_rt
, &bus
->m_rt_list
, bus_node
) {
156 sdw_compute_master_ports(m_rt
, ¶ms
[i
],
159 block_offset
+= m_rt
->ch_count
*
160 m_rt
->stream
->params
.bps
;
161 port_bo
= block_offset
;
164 hstop
= hstop
- params
[i
].hwidth
;
168 static int sdw_compute_group_params(struct sdw_bus
*bus
,
169 struct sdw_group_params
*params
,
170 int *rates
, int count
)
172 struct sdw_master_runtime
*m_rt
= NULL
;
173 int sel_col
= bus
->params
.col
;
174 unsigned int rate
, bps
, ch
;
175 int i
, column_needed
= 0;
177 /* Calculate bandwidth per group */
178 for (i
= 0; i
< count
; i
++) {
179 params
[i
].rate
= rates
[i
];
180 params
[i
].full_bw
= bus
->params
.curr_dr_freq
/ params
[i
].rate
;
183 list_for_each_entry(m_rt
, &bus
->m_rt_list
, bus_node
) {
184 rate
= m_rt
->stream
->params
.rate
;
185 bps
= m_rt
->stream
->params
.bps
;
188 for (i
= 0; i
< count
; i
++) {
189 if (rate
== params
[i
].rate
)
190 params
[i
].payload_bw
+= bps
* ch
;
194 for (i
= 0; i
< count
; i
++) {
195 params
[i
].hwidth
= (sel_col
*
196 params
[i
].payload_bw
+ params
[i
].full_bw
- 1) /
199 column_needed
+= params
[i
].hwidth
;
202 if (column_needed
> sel_col
- 1)
208 static int sdw_add_element_group_count(struct sdw_group
*group
,
211 int num
= group
->count
;
214 for (i
= 0; i
<= num
; i
++) {
215 if (rate
== group
->rates
[i
])
221 if (group
->count
>= group
->max_size
) {
224 group
->max_size
+= 1;
225 rates
= krealloc(group
->rates
,
226 (sizeof(int) * group
->max_size
),
230 group
->rates
= rates
;
233 group
->rates
[group
->count
++] = rate
;
239 static int sdw_get_group_count(struct sdw_bus
*bus
,
240 struct sdw_group
*group
)
242 struct sdw_master_runtime
*m_rt
;
247 group
->max_size
= SDW_STRM_RATE_GROUPING
;
248 group
->rates
= kcalloc(group
->max_size
, sizeof(int), GFP_KERNEL
);
252 list_for_each_entry(m_rt
, &bus
->m_rt_list
, bus_node
) {
253 rate
= m_rt
->stream
->params
.rate
;
254 if (m_rt
== list_first_entry(&bus
->m_rt_list
,
255 struct sdw_master_runtime
,
257 group
->rates
[group
->count
++] = rate
;
260 ret
= sdw_add_element_group_count(group
, rate
);
272 * sdw_compute_port_params: Compute transport and port parameters
274 * @bus: SDW Bus instance
276 static int sdw_compute_port_params(struct sdw_bus
*bus
)
278 struct sdw_group_params
*params
= NULL
;
279 struct sdw_group group
;
282 ret
= sdw_get_group_count(bus
, &group
);
286 if (group
.count
== 0)
289 params
= kcalloc(group
.count
, sizeof(*params
), GFP_KERNEL
);
295 /* Compute transport parameters for grouped streams */
296 ret
= sdw_compute_group_params(bus
, params
,
297 &group
.rates
[0], group
.count
);
301 _sdw_compute_port_params(bus
, params
, group
.count
);
311 static int sdw_select_row_col(struct sdw_bus
*bus
, int clk_freq
)
313 struct sdw_master_prop
*prop
= &bus
->prop
;
314 int frame_int
, frame_freq
;
317 for (c
= 0; c
< SDW_FRAME_COLS
; c
++) {
318 for (r
= 0; r
< SDW_FRAME_ROWS
; r
++) {
319 if (sdw_rows
[r
] != prop
->default_row
||
320 sdw_cols
[c
] != prop
->default_col
)
323 frame_int
= sdw_rows
[r
] * sdw_cols
[c
];
324 frame_freq
= clk_freq
/ frame_int
;
326 if ((clk_freq
- (frame_freq
* SDW_FRAME_CTRL_BITS
)) <
327 bus
->params
.bandwidth
)
330 bus
->params
.row
= sdw_rows
[r
];
331 bus
->params
.col
= sdw_cols
[c
];
340 * sdw_compute_bus_params: Compute bus parameters
342 * @bus: SDW Bus instance
344 static int sdw_compute_bus_params(struct sdw_bus
*bus
)
346 unsigned int max_dr_freq
, curr_dr_freq
= 0;
347 struct sdw_master_prop
*mstr_prop
= &bus
->prop
;
348 int i
, clk_values
, ret
;
349 bool is_gear
= false;
352 if (mstr_prop
->num_clk_gears
) {
353 clk_values
= mstr_prop
->num_clk_gears
;
354 clk_buf
= mstr_prop
->clk_gears
;
356 } else if (mstr_prop
->num_clk_freq
) {
357 clk_values
= mstr_prop
->num_clk_freq
;
358 clk_buf
= mstr_prop
->clk_freq
;
364 max_dr_freq
= mstr_prop
->max_clk_freq
* SDW_DOUBLE_RATE_FACTOR
;
366 for (i
= 0; i
< clk_values
; i
++) {
368 curr_dr_freq
= max_dr_freq
;
370 curr_dr_freq
= (is_gear
) ?
371 (max_dr_freq
>> clk_buf
[i
]) :
372 clk_buf
[i
] * SDW_DOUBLE_RATE_FACTOR
;
374 if (curr_dr_freq
<= bus
->params
.bandwidth
)
380 * TODO: Check all the Slave(s) port(s) audio modes and find
381 * whether given clock rate is supported with glitchless
389 ret
= sdw_select_row_col(bus
, curr_dr_freq
);
393 bus
->params
.curr_dr_freq
= curr_dr_freq
;
398 * sdw_compute_params: Compute bus, transport and port parameters
400 * @bus: SDW Bus instance
402 int sdw_compute_params(struct sdw_bus
*bus
)
406 /* Computes clock frequency, frame shape and frame frequency */
407 ret
= sdw_compute_bus_params(bus
);
409 dev_err(bus
->dev
, "Compute bus params failed: %d", ret
);
413 /* Compute transport and port params */
414 ret
= sdw_compute_port_params(bus
);
416 dev_err(bus
->dev
, "Compute transport params failed: %d", ret
);
422 EXPORT_SYMBOL(sdw_compute_params
);
424 MODULE_LICENSE("Dual BSD/GPL");
425 MODULE_DESCRIPTION("SoundWire Generic Bandwidth Allocation");