Prechádzať zdrojové kódy

soc: soundwire: Update bus clock frequency based on usecase

Some usecases need 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 <[email protected]>
Sudheer Papothi 5 rokov pred
rodič
commit
cdeb593d01
4 zmenil súbory, kde vykonal 122 pridanie a 15 odobranie
  1. 8 1
      include/soc/soundwire.h
  2. 2 1
      include/soc/swr-wcd.h
  3. 108 10
      soc/swr-mstr-ctrl.c
  4. 4 3
      soc/swr-mstr-ctrl.h

+ 8 - 1
include/soc/soundwire.h

@@ -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.
  */
 
 #ifndef _LINUX_SOUNDWIRE_H
@@ -11,6 +11,13 @@
 #include <linux/regmap.h>
 #include "audio_mod_devicetable.h"
 
+#define SWR_CLK_RATE_0P6MHZ       600000
+#define SWR_CLK_RATE_1P2MHZ      1200000
+#define SWR_CLK_RATE_2P4MHZ      2400000
+#define SWR_CLK_RATE_4P8MHZ      4800000
+#define SWR_CLK_RATE_9P6MHZ      9600000
+#define SWR_CLK_RATE_11P2896MHZ  1128960
+
 extern struct bus_type soundwire_type;
 struct swr_device;
 

+ 2 - 1
include/soc/swr-wcd.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2015, 2017-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015, 2017-2020 The Linux Foundation. All rights reserved.
  */
 
 #ifndef _LINUX_SWR_WCD_H
@@ -32,6 +32,7 @@ struct swr_mstr_port {
 };
 
 #define MCLK_FREQ		9600000
+#define MCLK_FREQ_LP		600000
 #define MCLK_FREQ_NATIVE	11289600
 
 #if (IS_ENABLED(CONFIG_SOUNDWIRE_WCD_CTRL) || \

+ 108 - 10
soc/swr-mstr-ctrl.c

@@ -26,6 +26,8 @@
 #include "swr-slave-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 */
 
@@ -65,10 +67,12 @@
 #define SWRM_MCP_SLV_STATUS_MASK    0x03
 #define SWRM_ROW_CTRL_MASK    0xF8
 #define SWRM_COL_CTRL_MASK    0x07
+#define SWRM_CLK_DIV_MASK     0x700
 #define SWRM_SSP_PERIOD_MASK  0xff0000
 #define SWRM_NUM_PINGS_MASK   0x3E0000
 #define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT    3
 #define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT    0
+#define SWRM_MCP_FRAME_CTRL_BANK_CLK_DIV_VALUE_SHFT 8
 #define SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT  16
 #define SWRM_NUM_PINGS_POS    0x11
 
@@ -117,6 +121,42 @@ static void swrm_unlock_sleep(struct swr_mstr_ctrl *swrm);
 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);
@@ -996,6 +1036,49 @@ end:
 	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)
 {
@@ -1268,13 +1351,14 @@ static void swrm_apply_port_config(struct swr_master *master)
 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_ROW_CTRL_MASK | SWRM_COL_CTRL_MASK |
-		    SWRM_SSP_PERIOD_MASK);
+		    SWRM_CLK_DIV_MASK | SWRM_SSP_PERIOD_MASK);
 	u8 inactive_bank;
 	int frame_sync = SWRM_FRAME_SYNC_SEL;
 
@@ -1334,13 +1418,17 @@ static int swrm_slvdev_datapath_control(struct swr_master *master, bool enable)
 		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
@@ -1355,25 +1443,26 @@ static int swrm_slvdev_datapath_control(struct swr_master *master, bool enable)
 	}
 	/* 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(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(bank), value);
 
@@ -1477,6 +1566,11 @@ static int swrm_connect_port(struct swr_master *master,
 		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);
@@ -1539,6 +1633,10 @@ static int swrm_disconnect_port(struct swr_master *master,
 		}
 		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);

+ 4 - 3
soc/swr-mstr-ctrl.h

@@ -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.
  */
 
 #ifndef _SWR_WCD_CTRL_H
@@ -26,6 +26,7 @@
 #define SWR_ROW_48		0
 #define SWR_ROW_50		1
 #define SWR_ROW_64		3
+#define SWR_COL_04		1 /* Cols = 4 */
 #define SWR_MAX_COL		7 /* Cols = 16 */
 #define SWR_MIN_COL		0 /* Cols = 2 */
 
@@ -42,7 +43,7 @@
 
 #define SWR_MAX_CH_PER_PORT 8
 
-#define SWR_MAX_SLAVE_DEVICES 11
+#define SWRM_NUM_AUTO_ENUM_SLAVES    6
 
 enum {
 	SWR_MSTR_PAUSE,
@@ -86,7 +87,6 @@ struct swrm_mports {
 	bool port_en;
 	u8 ch_en;
 	u8 req_ch;
-	u8 ch_rate;
 	u8 offset1;
 	u8 offset2;
 	u8 sinterval;
@@ -98,6 +98,7 @@ struct swrm_mports {
 	u8 lane_ctrl;
 	u8 dir;
 	u8 stream_type;
+	u32 ch_rate;
 };
 
 struct swrm_port_type {