18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci// Copyright(c) 2015-2020 Intel Corporation. 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* 58c2ecf20Sopenharmony_ci * Bandwidth management algorithm based on 2^n gears 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/soundwire/sdw.h> 148c2ecf20Sopenharmony_ci#include "bus.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define SDW_STRM_RATE_GROUPING 1 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistruct sdw_group_params { 198c2ecf20Sopenharmony_ci unsigned int rate; 208c2ecf20Sopenharmony_ci int full_bw; 218c2ecf20Sopenharmony_ci int payload_bw; 228c2ecf20Sopenharmony_ci int hwidth; 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct sdw_group { 268c2ecf20Sopenharmony_ci unsigned int count; 278c2ecf20Sopenharmony_ci unsigned int max_size; 288c2ecf20Sopenharmony_ci unsigned int *rates; 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct sdw_transport_data { 328c2ecf20Sopenharmony_ci int hstart; 338c2ecf20Sopenharmony_ci int hstop; 348c2ecf20Sopenharmony_ci int block_offset; 358c2ecf20Sopenharmony_ci int sub_block_offset; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt, 398c2ecf20Sopenharmony_ci struct sdw_transport_data *t_data) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct sdw_slave_runtime *s_rt = NULL; 428c2ecf20Sopenharmony_ci struct sdw_port_runtime *p_rt; 438c2ecf20Sopenharmony_ci int port_bo, sample_int; 448c2ecf20Sopenharmony_ci unsigned int rate, bps, ch = 0; 458c2ecf20Sopenharmony_ci unsigned int slave_total_ch; 468c2ecf20Sopenharmony_ci struct sdw_bus_params *b_params = &m_rt->bus->params; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci port_bo = t_data->block_offset; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { 518c2ecf20Sopenharmony_ci rate = m_rt->stream->params.rate; 528c2ecf20Sopenharmony_ci bps = m_rt->stream->params.bps; 538c2ecf20Sopenharmony_ci sample_int = (m_rt->bus->params.curr_dr_freq / rate); 548c2ecf20Sopenharmony_ci slave_total_ch = 0; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci list_for_each_entry(p_rt, &s_rt->port_list, port_node) { 578c2ecf20Sopenharmony_ci ch = sdw_ch_mask_to_ch(p_rt->ch_mask); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci sdw_fill_xport_params(&p_rt->transport_params, 608c2ecf20Sopenharmony_ci p_rt->num, false, 618c2ecf20Sopenharmony_ci SDW_BLK_GRP_CNT_1, 628c2ecf20Sopenharmony_ci sample_int, port_bo, port_bo >> 8, 638c2ecf20Sopenharmony_ci t_data->hstart, 648c2ecf20Sopenharmony_ci t_data->hstop, 658c2ecf20Sopenharmony_ci (SDW_BLK_GRP_CNT_1 * ch), 0x0); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci sdw_fill_port_params(&p_rt->port_params, 688c2ecf20Sopenharmony_ci p_rt->num, bps, 698c2ecf20Sopenharmony_ci SDW_PORT_FLOW_MODE_ISOCH, 708c2ecf20Sopenharmony_ci b_params->s_data_mode); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci port_bo += bps * ch; 738c2ecf20Sopenharmony_ci slave_total_ch += ch; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (m_rt->direction == SDW_DATA_DIR_TX && 778c2ecf20Sopenharmony_ci m_rt->ch_count == slave_total_ch) { 788c2ecf20Sopenharmony_ci /* 798c2ecf20Sopenharmony_ci * Slave devices were configured to access all channels 808c2ecf20Sopenharmony_ci * of the stream, which indicates that they operate in 818c2ecf20Sopenharmony_ci * 'mirror mode'. Make sure we reset the port offset for 828c2ecf20Sopenharmony_ci * the next device in the list 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci port_bo = t_data->block_offset; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void sdw_compute_master_ports(struct sdw_master_runtime *m_rt, 908c2ecf20Sopenharmony_ci struct sdw_group_params *params, 918c2ecf20Sopenharmony_ci int port_bo, int hstop) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct sdw_transport_data t_data = {0}; 948c2ecf20Sopenharmony_ci struct sdw_port_runtime *p_rt; 958c2ecf20Sopenharmony_ci struct sdw_bus *bus = m_rt->bus; 968c2ecf20Sopenharmony_ci struct sdw_bus_params *b_params = &bus->params; 978c2ecf20Sopenharmony_ci int sample_int, hstart = 0; 988c2ecf20Sopenharmony_ci unsigned int rate, bps, ch, no_ch; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci rate = m_rt->stream->params.rate; 1018c2ecf20Sopenharmony_ci bps = m_rt->stream->params.bps; 1028c2ecf20Sopenharmony_ci ch = m_rt->ch_count; 1038c2ecf20Sopenharmony_ci sample_int = (bus->params.curr_dr_freq / rate); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (rate != params->rate) 1068c2ecf20Sopenharmony_ci return; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci t_data.hstop = hstop; 1098c2ecf20Sopenharmony_ci hstart = hstop - params->hwidth + 1; 1108c2ecf20Sopenharmony_ci t_data.hstart = hstart; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci list_for_each_entry(p_rt, &m_rt->port_list, port_node) { 1138c2ecf20Sopenharmony_ci no_ch = sdw_ch_mask_to_ch(p_rt->ch_mask); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci sdw_fill_xport_params(&p_rt->transport_params, p_rt->num, 1168c2ecf20Sopenharmony_ci false, SDW_BLK_GRP_CNT_1, sample_int, 1178c2ecf20Sopenharmony_ci port_bo, port_bo >> 8, hstart, hstop, 1188c2ecf20Sopenharmony_ci (SDW_BLK_GRP_CNT_1 * no_ch), 0x0); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci sdw_fill_port_params(&p_rt->port_params, 1218c2ecf20Sopenharmony_ci p_rt->num, bps, 1228c2ecf20Sopenharmony_ci SDW_PORT_FLOW_MODE_ISOCH, 1238c2ecf20Sopenharmony_ci b_params->m_data_mode); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* Check for first entry */ 1268c2ecf20Sopenharmony_ci if (!(p_rt == list_first_entry(&m_rt->port_list, 1278c2ecf20Sopenharmony_ci struct sdw_port_runtime, 1288c2ecf20Sopenharmony_ci port_node))) { 1298c2ecf20Sopenharmony_ci port_bo += bps * ch; 1308c2ecf20Sopenharmony_ci continue; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci t_data.hstart = hstart; 1348c2ecf20Sopenharmony_ci t_data.hstop = hstop; 1358c2ecf20Sopenharmony_ci t_data.block_offset = port_bo; 1368c2ecf20Sopenharmony_ci t_data.sub_block_offset = 0; 1378c2ecf20Sopenharmony_ci port_bo += bps * ch; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci sdw_compute_slave_ports(m_rt, &t_data); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic void _sdw_compute_port_params(struct sdw_bus *bus, 1448c2ecf20Sopenharmony_ci struct sdw_group_params *params, int count) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct sdw_master_runtime *m_rt = NULL; 1478c2ecf20Sopenharmony_ci int hstop = bus->params.col - 1; 1488c2ecf20Sopenharmony_ci int block_offset, port_bo, i; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* Run loop for all groups to compute transport parameters */ 1518c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 1528c2ecf20Sopenharmony_ci port_bo = 1; 1538c2ecf20Sopenharmony_ci block_offset = 1; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { 1568c2ecf20Sopenharmony_ci sdw_compute_master_ports(m_rt, ¶ms[i], 1578c2ecf20Sopenharmony_ci port_bo, hstop); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci block_offset += m_rt->ch_count * 1608c2ecf20Sopenharmony_ci m_rt->stream->params.bps; 1618c2ecf20Sopenharmony_ci port_bo = block_offset; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci hstop = hstop - params[i].hwidth; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int sdw_compute_group_params(struct sdw_bus *bus, 1698c2ecf20Sopenharmony_ci struct sdw_group_params *params, 1708c2ecf20Sopenharmony_ci int *rates, int count) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct sdw_master_runtime *m_rt = NULL; 1738c2ecf20Sopenharmony_ci int sel_col = bus->params.col; 1748c2ecf20Sopenharmony_ci unsigned int rate, bps, ch; 1758c2ecf20Sopenharmony_ci int i, column_needed = 0; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* Calculate bandwidth per group */ 1788c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 1798c2ecf20Sopenharmony_ci params[i].rate = rates[i]; 1808c2ecf20Sopenharmony_ci params[i].full_bw = bus->params.curr_dr_freq / params[i].rate; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { 1848c2ecf20Sopenharmony_ci rate = m_rt->stream->params.rate; 1858c2ecf20Sopenharmony_ci bps = m_rt->stream->params.bps; 1868c2ecf20Sopenharmony_ci ch = m_rt->ch_count; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 1898c2ecf20Sopenharmony_ci if (rate == params[i].rate) 1908c2ecf20Sopenharmony_ci params[i].payload_bw += bps * ch; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 1958c2ecf20Sopenharmony_ci params[i].hwidth = (sel_col * 1968c2ecf20Sopenharmony_ci params[i].payload_bw + params[i].full_bw - 1) / 1978c2ecf20Sopenharmony_ci params[i].full_bw; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci column_needed += params[i].hwidth; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (column_needed > sel_col - 1) 2038c2ecf20Sopenharmony_ci return -EINVAL; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int sdw_add_element_group_count(struct sdw_group *group, 2098c2ecf20Sopenharmony_ci unsigned int rate) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci int num = group->count; 2128c2ecf20Sopenharmony_ci int i; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci for (i = 0; i <= num; i++) { 2158c2ecf20Sopenharmony_ci if (rate == group->rates[i]) 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (i != num) 2198c2ecf20Sopenharmony_ci continue; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (group->count >= group->max_size) { 2228c2ecf20Sopenharmony_ci unsigned int *rates; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci group->max_size += 1; 2258c2ecf20Sopenharmony_ci rates = krealloc(group->rates, 2268c2ecf20Sopenharmony_ci (sizeof(int) * group->max_size), 2278c2ecf20Sopenharmony_ci GFP_KERNEL); 2288c2ecf20Sopenharmony_ci if (!rates) 2298c2ecf20Sopenharmony_ci return -ENOMEM; 2308c2ecf20Sopenharmony_ci group->rates = rates; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci group->rates[group->count++] = rate; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return 0; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic int sdw_get_group_count(struct sdw_bus *bus, 2408c2ecf20Sopenharmony_ci struct sdw_group *group) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci struct sdw_master_runtime *m_rt; 2438c2ecf20Sopenharmony_ci unsigned int rate; 2448c2ecf20Sopenharmony_ci int ret = 0; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci group->count = 0; 2478c2ecf20Sopenharmony_ci group->max_size = SDW_STRM_RATE_GROUPING; 2488c2ecf20Sopenharmony_ci group->rates = kcalloc(group->max_size, sizeof(int), GFP_KERNEL); 2498c2ecf20Sopenharmony_ci if (!group->rates) 2508c2ecf20Sopenharmony_ci return -ENOMEM; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { 2538c2ecf20Sopenharmony_ci rate = m_rt->stream->params.rate; 2548c2ecf20Sopenharmony_ci if (m_rt == list_first_entry(&bus->m_rt_list, 2558c2ecf20Sopenharmony_ci struct sdw_master_runtime, 2568c2ecf20Sopenharmony_ci bus_node)) { 2578c2ecf20Sopenharmony_ci group->rates[group->count++] = rate; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci } else { 2608c2ecf20Sopenharmony_ci ret = sdw_add_element_group_count(group, rate); 2618c2ecf20Sopenharmony_ci if (ret < 0) { 2628c2ecf20Sopenharmony_ci kfree(group->rates); 2638c2ecf20Sopenharmony_ci return ret; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return ret; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/** 2728c2ecf20Sopenharmony_ci * sdw_compute_port_params: Compute transport and port parameters 2738c2ecf20Sopenharmony_ci * 2748c2ecf20Sopenharmony_ci * @bus: SDW Bus instance 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_cistatic int sdw_compute_port_params(struct sdw_bus *bus) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct sdw_group_params *params = NULL; 2798c2ecf20Sopenharmony_ci struct sdw_group group; 2808c2ecf20Sopenharmony_ci int ret; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci ret = sdw_get_group_count(bus, &group); 2838c2ecf20Sopenharmony_ci if (ret < 0) 2848c2ecf20Sopenharmony_ci return ret; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (group.count == 0) 2878c2ecf20Sopenharmony_ci goto out; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci params = kcalloc(group.count, sizeof(*params), GFP_KERNEL); 2908c2ecf20Sopenharmony_ci if (!params) { 2918c2ecf20Sopenharmony_ci ret = -ENOMEM; 2928c2ecf20Sopenharmony_ci goto out; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* Compute transport parameters for grouped streams */ 2968c2ecf20Sopenharmony_ci ret = sdw_compute_group_params(bus, params, 2978c2ecf20Sopenharmony_ci &group.rates[0], group.count); 2988c2ecf20Sopenharmony_ci if (ret < 0) 2998c2ecf20Sopenharmony_ci goto free_params; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci _sdw_compute_port_params(bus, params, group.count); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cifree_params: 3048c2ecf20Sopenharmony_ci kfree(params); 3058c2ecf20Sopenharmony_ciout: 3068c2ecf20Sopenharmony_ci kfree(group.rates); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return ret; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic int sdw_select_row_col(struct sdw_bus *bus, int clk_freq) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct sdw_master_prop *prop = &bus->prop; 3148c2ecf20Sopenharmony_ci int frame_int, frame_freq; 3158c2ecf20Sopenharmony_ci int r, c; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci for (c = 0; c < SDW_FRAME_COLS; c++) { 3188c2ecf20Sopenharmony_ci for (r = 0; r < SDW_FRAME_ROWS; r++) { 3198c2ecf20Sopenharmony_ci if (sdw_rows[r] != prop->default_row || 3208c2ecf20Sopenharmony_ci sdw_cols[c] != prop->default_col) 3218c2ecf20Sopenharmony_ci continue; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci frame_int = sdw_rows[r] * sdw_cols[c]; 3248c2ecf20Sopenharmony_ci frame_freq = clk_freq / frame_int; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if ((clk_freq - (frame_freq * SDW_FRAME_CTRL_BITS)) < 3278c2ecf20Sopenharmony_ci bus->params.bandwidth) 3288c2ecf20Sopenharmony_ci continue; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci bus->params.row = sdw_rows[r]; 3318c2ecf20Sopenharmony_ci bus->params.col = sdw_cols[c]; 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return -EINVAL; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci/** 3408c2ecf20Sopenharmony_ci * sdw_compute_bus_params: Compute bus parameters 3418c2ecf20Sopenharmony_ci * 3428c2ecf20Sopenharmony_ci * @bus: SDW Bus instance 3438c2ecf20Sopenharmony_ci */ 3448c2ecf20Sopenharmony_cistatic int sdw_compute_bus_params(struct sdw_bus *bus) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci unsigned int max_dr_freq, curr_dr_freq = 0; 3478c2ecf20Sopenharmony_ci struct sdw_master_prop *mstr_prop = &bus->prop; 3488c2ecf20Sopenharmony_ci int i, clk_values, ret; 3498c2ecf20Sopenharmony_ci bool is_gear = false; 3508c2ecf20Sopenharmony_ci u32 *clk_buf; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (mstr_prop->num_clk_gears) { 3538c2ecf20Sopenharmony_ci clk_values = mstr_prop->num_clk_gears; 3548c2ecf20Sopenharmony_ci clk_buf = mstr_prop->clk_gears; 3558c2ecf20Sopenharmony_ci is_gear = true; 3568c2ecf20Sopenharmony_ci } else if (mstr_prop->num_clk_freq) { 3578c2ecf20Sopenharmony_ci clk_values = mstr_prop->num_clk_freq; 3588c2ecf20Sopenharmony_ci clk_buf = mstr_prop->clk_freq; 3598c2ecf20Sopenharmony_ci } else { 3608c2ecf20Sopenharmony_ci clk_values = 1; 3618c2ecf20Sopenharmony_ci clk_buf = NULL; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci max_dr_freq = mstr_prop->max_clk_freq * SDW_DOUBLE_RATE_FACTOR; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci for (i = 0; i < clk_values; i++) { 3678c2ecf20Sopenharmony_ci if (!clk_buf) 3688c2ecf20Sopenharmony_ci curr_dr_freq = max_dr_freq; 3698c2ecf20Sopenharmony_ci else 3708c2ecf20Sopenharmony_ci curr_dr_freq = (is_gear) ? 3718c2ecf20Sopenharmony_ci (max_dr_freq >> clk_buf[i]) : 3728c2ecf20Sopenharmony_ci clk_buf[i] * SDW_DOUBLE_RATE_FACTOR; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (curr_dr_freq <= bus->params.bandwidth) 3758c2ecf20Sopenharmony_ci continue; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci break; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* 3808c2ecf20Sopenharmony_ci * TODO: Check all the Slave(s) port(s) audio modes and find 3818c2ecf20Sopenharmony_ci * whether given clock rate is supported with glitchless 3828c2ecf20Sopenharmony_ci * transition. 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (i == clk_values) 3878c2ecf20Sopenharmony_ci return -EINVAL; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci ret = sdw_select_row_col(bus, curr_dr_freq); 3908c2ecf20Sopenharmony_ci if (ret < 0) 3918c2ecf20Sopenharmony_ci return -EINVAL; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci bus->params.curr_dr_freq = curr_dr_freq; 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci/** 3988c2ecf20Sopenharmony_ci * sdw_compute_params: Compute bus, transport and port parameters 3998c2ecf20Sopenharmony_ci * 4008c2ecf20Sopenharmony_ci * @bus: SDW Bus instance 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_ciint sdw_compute_params(struct sdw_bus *bus) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci int ret; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /* Computes clock frequency, frame shape and frame frequency */ 4078c2ecf20Sopenharmony_ci ret = sdw_compute_bus_params(bus); 4088c2ecf20Sopenharmony_ci if (ret < 0) { 4098c2ecf20Sopenharmony_ci dev_err(bus->dev, "Compute bus params failed: %d", ret); 4108c2ecf20Sopenharmony_ci return ret; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* Compute transport and port params */ 4148c2ecf20Sopenharmony_ci ret = sdw_compute_port_params(bus); 4158c2ecf20Sopenharmony_ci if (ret < 0) { 4168c2ecf20Sopenharmony_ci dev_err(bus->dev, "Compute transport params failed: %d", ret); 4178c2ecf20Sopenharmony_ci return ret; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sdw_compute_params); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 4258c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SoundWire Generic Bandwidth Allocation"); 426