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, &params[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