soc: soundwire: Update bus clock frequency based on usecase
Some usecase needs soundwire bus clock frequency to run at
lower clock rate for better power and performance. Update
soundwire bus clock frequency based on usecase.
Change-Id: I2e786d9f5d5d2ec3841daa934802eeafa79a8f6c
Signed-off-by: Sudheer Papothi <spapothi@codeaurora.org>
diff --git a/soc/swr-mstr-ctrl.c b/soc/swr-mstr-ctrl.c
index 2ab1bee..cd3805f 100644
--- a/soc/swr-mstr-ctrl.c
+++ b/soc/swr-mstr-ctrl.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/irq.h>
@@ -25,6 +25,8 @@
#include "swrm_registers.h"
#include "swr-mstr-ctrl.h"
+#define SWR_NUM_PORTS 4 /* TODO - Get this info from DT */
+
#define SWRM_FRAME_SYNC_SEL 4000 /* 4KHz */
#define SWRM_FRAME_SYNC_SEL_NATIVE 3675 /* 3.675KHz */
#define SWRM_SYSTEM_RESUME_TIMEOUT_MS 700
@@ -94,6 +96,42 @@
static u32 swr_master_read(struct swr_mstr_ctrl *swrm, unsigned int reg_addr);
static void swr_master_write(struct swr_mstr_ctrl *swrm, u16 reg_addr, u32 val);
+
+static u8 swrm_get_clk_div(int mclk_freq, int bus_clk_freq)
+{
+ int clk_div = 0;
+ u8 div_val = 0;
+
+ if (!mclk_freq || !bus_clk_freq)
+ return 0;
+
+ clk_div = (mclk_freq / bus_clk_freq);
+
+ switch (clk_div) {
+ case 32:
+ div_val = 5;
+ break;
+ case 16:
+ div_val = 4;
+ break;
+ case 8:
+ div_val = 3;
+ break;
+ case 4:
+ div_val = 2;
+ break;
+ case 2:
+ div_val = 1;
+ break;
+ case 1:
+ default:
+ div_val = 0;
+ break;
+ }
+
+ return div_val;
+}
+
static bool swrm_is_msm_variant(int val)
{
return (val == SWRM_VERSION_1_3);
@@ -955,6 +993,49 @@
return is_removed;
}
+int swrm_get_clk_div_rate(int mclk_freq, int bus_clk_freq)
+{
+ if (!bus_clk_freq)
+ return mclk_freq;
+
+ if (mclk_freq == SWR_CLK_RATE_9P6MHZ) {
+ if (bus_clk_freq <= SWR_CLK_RATE_0P6MHZ)
+ bus_clk_freq = SWR_CLK_RATE_0P6MHZ;
+ else if (bus_clk_freq <= SWR_CLK_RATE_1P2MHZ)
+ bus_clk_freq = SWR_CLK_RATE_1P2MHZ;
+ else if (bus_clk_freq <= SWR_CLK_RATE_2P4MHZ)
+ bus_clk_freq = SWR_CLK_RATE_2P4MHZ;
+ else if(bus_clk_freq <= SWR_CLK_RATE_4P8MHZ)
+ bus_clk_freq = SWR_CLK_RATE_4P8MHZ;
+ else if(bus_clk_freq <= SWR_CLK_RATE_9P6MHZ)
+ bus_clk_freq = SWR_CLK_RATE_9P6MHZ;
+ } else if (mclk_freq == SWR_CLK_RATE_11P2896MHZ)
+ bus_clk_freq = SWR_CLK_RATE_11P2896MHZ;
+
+ return bus_clk_freq;
+}
+
+static int swrm_update_bus_clk(struct swr_mstr_ctrl *swrm)
+{
+ int ret = 0;
+ int agg_clk = 0;
+ int i;
+
+ for (i = 0; i < SWR_MSTR_PORT_LEN; i++)
+ agg_clk += swrm->mport_cfg[i].ch_rate;
+
+ if (agg_clk)
+ swrm->bus_clk = swrm_get_clk_div_rate(swrm->mclk_freq,
+ agg_clk);
+ else
+ swrm->bus_clk = swrm->mclk_freq;
+
+ dev_dbg(swrm->dev, "%s: all_port_clk: %d, bus_clk: %d\n",
+ __func__, agg_clk, swrm->bus_clk);
+
+ return ret;
+}
+
static void swrm_disable_ports(struct swr_master *master,
u8 bank)
{
@@ -1221,13 +1302,15 @@
static int swrm_slvdev_datapath_control(struct swr_master *master, bool enable)
{
u8 bank;
- u32 value, n_row, n_col;
+ u32 value = 0, n_row = 0, n_col = 0;
u32 row = 0, col = 0;
+ int bus_clk_div_factor;
int ret;
u8 ssp_period = 0;
struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master);
int mask = (SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK |
SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK |
+ SWRM_MCP_FRAME_CTRL_BANK_CLK_DIV_VALUE_BMSK |
SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_BMSK);
u8 inactive_bank;
int frame_sync = SWRM_FRAME_SYNC_SEL;
@@ -1288,13 +1371,17 @@
clear_bit(DISABLE_PENDING, &swrm->port_req_pending);
swrm_disable_ports(master, bank);
}
- dev_dbg(swrm->dev, "%s: enable: %d, cfg_devs: %d\n",
- __func__, enable, swrm->num_cfg_devs);
+ dev_dbg(swrm->dev, "%s: enable: %d, cfg_devs: %d freq %d\n",
+ __func__, enable, swrm->num_cfg_devs, swrm->mclk_freq);
if (enable) {
/* set col = 16 */
n_col = SWR_MAX_COL;
col = SWRM_COL_16;
+ if (swrm->bus_clk == MCLK_FREQ_LP) {
+ n_col = SWR_MIN_COL;
+ col = SWRM_COL_02;
+ }
} else {
/*
* Do not change to col = 2 if there are still active ports
@@ -1309,25 +1396,26 @@
}
/* Use default 50 * x, frame shape. Change based on mclk */
if (swrm->mclk_freq == MCLK_FREQ_NATIVE) {
- dev_dbg(swrm->dev, "setting 64 x %d frameshape\n",
- n_col ? 16 : 2);
+ dev_dbg(swrm->dev, "setting 64 x %d frameshape\n", col);
n_row = SWR_ROW_64;
row = SWRM_ROW_64;
frame_sync = SWRM_FRAME_SYNC_SEL_NATIVE;
} else {
- dev_dbg(swrm->dev, "setting 50 x %d frameshape\n",
- n_col ? 16 : 2);
+ dev_dbg(swrm->dev, "setting 50 x %d frameshape\n", col);
n_row = SWR_ROW_50;
row = SWRM_ROW_50;
frame_sync = SWRM_FRAME_SYNC_SEL;
}
ssp_period = swrm_get_ssp_period(swrm, row, col, frame_sync);
- dev_dbg(swrm->dev, "%s: ssp_period: %d\n", __func__, ssp_period);
-
+ bus_clk_div_factor = swrm_get_clk_div(swrm->mclk_freq, swrm->bus_clk);
+ dev_dbg(swrm->dev, "%s: ssp_period: %d, bus_clk_div:%d \n", __func__,
+ ssp_period, bus_clk_div_factor);
value = swr_master_read(swrm, SWRM_MCP_FRAME_CTRL_BANK_ADDR(bank));
value &= (~mask);
value |= ((n_row << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT) |
(n_col << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT) |
+ (bus_clk_div_factor <<
+ SWRM_MCP_FRAME_CTRL_BANK_CLK_DIV_VALUE_SHFT) |
((ssp_period - 1) << SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT));
swr_master_write(swrm, SWRM_MCP_FRAME_CTRL_BANK_ADDR(bank), value);
@@ -1431,6 +1519,11 @@
mport->port_en = true;
mport->req_ch |= mstr_ch_msk;
master->port_en_mask |= (1 << mstr_port_id);
+ if (swrm->clk_stop_mode0_supp &&
+ (mport->ch_rate < portinfo->ch_rate[i])) {
+ mport->ch_rate = portinfo->ch_rate[i];
+ swrm_update_bus_clk(swrm);
+ }
}
master->num_port += portinfo->num_port;
set_bit(ENABLE_PENDING, &swrm->port_req_pending);
@@ -1493,6 +1586,10 @@
}
port_req->req_ch &= ~portinfo->ch_en[i];
mport->req_ch &= ~mstr_ch_mask;
+ if (swrm->clk_stop_mode0_supp && !mport->req_ch) {
+ mport->ch_rate = 0;
+ swrm_update_bus_clk(swrm);
+ }
}
master->num_port -= portinfo->num_port;
set_bit(DISABLE_PENDING, &swrm->port_req_pending);
@@ -2402,9 +2499,10 @@
dev_dbg(&pdev->dev, "%s: Looking up %s property failed\n",
__func__, "qcom,swr-num-dev");
} else {
- if (swrm->num_dev > SWR_MAX_SLAVE_DEVICES) {
+ if (swrm->num_dev > SWRM_NUM_AUTO_ENUM_SLAVES) {
dev_err(&pdev->dev, "%s: num_dev %d > max limit %d\n",
- __func__, swrm->num_dev, SWR_MAX_SLAVE_DEVICES);
+ __func__, swrm->num_dev,
+ SWRM_NUM_AUTO_ENUM_SLAVES);
ret = -EINVAL;
goto err_pdata_fail;
}