From 1f93a7dfd1f8d5ff7a5c53246c7534fe2332d6f4 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Mon, 11 Dec 2023 02:46:07 +0000
Subject: [PATCH] add audio

---
 kernel/sound/soc/rockchip/rockchip_sai.c |  465 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 425 insertions(+), 40 deletions(-)

diff --git a/kernel/sound/soc/rockchip/rockchip_sai.c b/kernel/sound/soc/rockchip/rockchip_sai.c
index fa0a8bb..3e50ba0 100644
--- a/kernel/sound/soc/rockchip/rockchip_sai.c
+++ b/kernel/sound/soc/rockchip/rockchip_sai.c
@@ -24,6 +24,12 @@
 
 #define FW_RATIO_MAX		8
 #define FW_RATIO_MIN		1
+#define MAXBURST_PER_FIFO	8
+
+#define DEFAULT_FS		48000
+#define TIMEOUT_US		1000
+#define WAIT_TIME_MS_MAX	10000
+#define QUIRK_ALWAYS_ON		BIT(0)
 
 enum fpw_mode {
 	FPW_ONE_BCLK_WIDTH,
@@ -41,14 +47,30 @@
 	struct snd_dmaengine_dai_dma_data capture_dma_data;
 	struct snd_dmaengine_dai_dma_data playback_dma_data;
 	struct snd_pcm_substream *substreams[SNDRV_PCM_STREAM_LAST + 1];
+	unsigned int wait_time[SNDRV_PCM_STREAM_LAST + 1];
+	unsigned int tx_lanes;
+	unsigned int rx_lanes;
+	unsigned int quirks;
 	enum fpw_mode fpw;
-	int fw_ratio;
+	int  fw_ratio;
 	bool has_capture;
 	bool has_playback;
 	bool is_master_mode;
+	bool is_tdm;
+	bool is_clk_auto;
 };
 
-static int sai_runtime_suspend(struct device *dev)
+static const struct sai_of_quirks {
+	char *quirk;
+	int id;
+} of_quirks[] = {
+	{
+		.quirk = "rockchip,always-on",
+		.id = QUIRK_ALWAYS_ON,
+	},
+};
+
+static int rockchip_sai_runtime_suspend(struct device *dev)
 {
 	struct rk_sai_dev *sai = dev_get_drvdata(dev);
 	unsigned int val;
@@ -62,20 +84,40 @@
 				   SAI_XFER_FSS_DIS);
 
 	ret = regmap_read_poll_timeout_atomic(sai->regmap, SAI_XFER, val,
-					      (val & SAI_XFER_FS_IDLE), 10, 100);
+					      (val & SAI_XFER_FS_IDLE), 10, TIMEOUT_US);
 	if (ret < 0)
 		dev_warn(sai->dev, "Failed to idle FS\n");
 
 	regcache_cache_only(sai->regmap, true);
+	/*
+	 * After FS idle, should wait at least 2 BCLK cycle to make sure
+	 * the CLK gate operation done, and then disable mclk.
+	 *
+	 * Otherwise, the BCLK is still ungated. once the mclk is enabled,
+	 * there maybe a risk that a few BCLK cycle leak. especially for
+	 * low speed situation, such as 8k samplerate.
+	 *
+	 * The best way is to use delay per samplerate, but, the max time
+	 * is quite a tiny value, so, let's make it simple to use the max
+	 * time.
+	 *
+	 * The max BCLK cycle time is: 31us @ 8K-8Bit (64K BCLK)
+	 */
+	udelay(40);
 	clk_disable_unprepare(sai->mclk);
+	clk_disable_unprepare(sai->hclk);
 
 	return 0;
 }
 
-static int sai_runtime_resume(struct device *dev)
+static int rockchip_sai_runtime_resume(struct device *dev)
 {
 	struct rk_sai_dev *sai = dev_get_drvdata(dev);
 	int ret;
+
+	ret = clk_prepare_enable(sai->hclk);
+	if (ret)
+		goto err_hclk;
 
 	ret = clk_prepare_enable(sai->mclk);
 	if (ret)
@@ -87,7 +129,7 @@
 	if (ret)
 		goto err_regmap;
 
-	if (sai->is_master_mode)
+	if (sai->quirks & QUIRK_ALWAYS_ON && sai->is_master_mode)
 		regmap_update_bits(sai->regmap, SAI_XFER,
 				   SAI_XFER_CLK_MASK |
 				   SAI_XFER_FSS_MASK,
@@ -99,6 +141,8 @@
 err_regmap:
 	clk_disable_unprepare(sai->mclk);
 err_mclk:
+	clk_disable_unprepare(sai->hclk);
+err_hclk:
 	return ret;
 }
 
@@ -178,7 +222,7 @@
 
 	regmap_update_bits(sai->regmap, SAI_CLR, clr, clr);
 	ret = regmap_read_poll_timeout_atomic(sai->regmap, SAI_CLR, val,
-					      !(val & clr), 10, 100);
+					      !(val & clr), 10, TIMEOUT_US);
 	if (ret < 0) {
 		dev_warn(sai->dev, "Failed to clear %u\n", clr);
 		goto reset;
@@ -224,7 +268,7 @@
 
 	regmap_update_bits(sai->regmap, SAI_XFER, msk, val);
 	ret = regmap_read_poll_timeout_atomic(sai->regmap, SAI_XFER, val,
-					      (val & idle), 10, 100);
+					      (val & idle), 10, TIMEOUT_US);
 	if (ret < 0)
 		dev_warn(sai->dev, "Failed to idle stream %d\n", stream);
 
@@ -359,19 +403,42 @@
 	return ret;
 }
 
+static unsigned int rockchip_sai_lanes_auto(struct snd_pcm_hw_params *params,
+					    struct snd_soc_dai *dai)
+{
+	struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+	unsigned int lanes = 1;
+
+	if (!sai->is_tdm)
+		lanes = DIV_ROUND_UP(params_channels(params), 2);
+
+	return lanes;
+}
+
 static int rockchip_sai_hw_params(struct snd_pcm_substream *substream,
 				  struct snd_pcm_hw_params *params,
 				  struct snd_soc_dai *dai)
 {
 	struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+	struct snd_dmaengine_dai_dma_data *dma_data;
 	unsigned int mclk_rate, bclk_rate, div_bclk;
 	unsigned int ch_per_lane, lanes, slot_width;
 	unsigned int val, fscr, reg;
 
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+	dma_data = snd_soc_dai_get_dma_data(dai, substream);
+	dma_data->maxburst = MAXBURST_PER_FIFO * params_channels(params) / 2;
+
+	lanes = rockchip_sai_lanes_auto(params, dai);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		reg = SAI_TXCR;
-	else
+		if (sai->tx_lanes)
+			lanes = sai->tx_lanes;
+	} else {
 		reg = SAI_RXCR;
+		if (sai->rx_lanes)
+			lanes = sai->rx_lanes;
+	}
 
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S8:
@@ -385,18 +452,20 @@
 		val = SAI_XCR_VDW(24);
 		break;
 	case SNDRV_PCM_FORMAT_S32_LE:
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
 		val = SAI_XCR_VDW(32);
 		break;
 	default:
 		return -EINVAL;
 	}
 
-	regmap_update_bits(sai->regmap, reg, SAI_XCR_VDW_MASK, val);
+	val |= SAI_XCR_CSR(lanes);
+
+	regmap_update_bits(sai->regmap, reg, SAI_XCR_VDW_MASK | SAI_XCR_CSR_MASK, val);
 
 	regmap_read(sai->regmap, reg, &val);
 
 	slot_width = SAI_XCR_SBW_V(val);
-	lanes = SAI_XCR_CSR_V(val);
 	ch_per_lane = params_channels(params) / lanes;
 
 	regmap_update_bits(sai->regmap, reg, SAI_XCR_SNB_MASK,
@@ -424,6 +493,8 @@
 
 	if (sai->is_master_mode) {
 		bclk_rate = sai->fw_ratio * slot_width * ch_per_lane * params_rate(params);
+		if (sai->is_clk_auto)
+			clk_set_rate(sai->mclk, bclk_rate);
 		mclk_rate = clk_get_rate(sai->mclk);
 		if (mclk_rate < bclk_rate) {
 			dev_err(sai->dev, "Mismatch mclk: %u, expected %u at least\n",
@@ -435,6 +506,22 @@
 
 		regmap_update_bits(sai->regmap, SAI_CKR, SAI_CKR_MDIV_MASK,
 				   SAI_CKR_MDIV(div_bclk));
+		/*
+		 * Should wait for one BCLK ready after DIV and then ungate
+		 * output clk to achieve the clean clk.
+		 *
+		 * The best way is to use delay per samplerate, but, the max time
+		 * is quite a tiny value, so, let's make it simple to use the max
+		 * time.
+		 *
+		 * The max BCLK cycle time is: 15.6us @ 8K-8Bit (64K BCLK)
+		 */
+		udelay(20);
+		regmap_update_bits(sai->regmap, SAI_XFER,
+				   SAI_XFER_CLK_MASK |
+				   SAI_XFER_FSS_MASK,
+				   SAI_XFER_CLK_EN |
+				   SAI_XFER_FSS_EN);
 	}
 
 	return 0;
@@ -471,7 +558,7 @@
 	struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
 	int ret;
 
-	if (!freq)
+	if (!freq || sai->is_clk_auto)
 		return 0;
 
 	ret = clk_set_rate(sai->mclk, freq);
@@ -496,11 +583,15 @@
 				    struct snd_soc_dai *dai)
 {
 	struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+	int stream = substream->stream;
 
-	if (sai->substreams[substream->stream])
+	if (sai->substreams[stream])
 		return -EBUSY;
 
-	sai->substreams[substream->stream] = substream;
+	if (sai->wait_time[stream])
+		substream->wait_time = msecs_to_jiffies(sai->wait_time[stream]);
+
+	sai->substreams[stream] = substream;
 
 	return 0;
 }
@@ -525,6 +616,8 @@
 	regmap_update_bits(sai->regmap, SAI_RXCR, SAI_XCR_SBW_MASK,
 			   SAI_XCR_SBW(slot_width));
 	pm_runtime_put(dai->dev);
+
+	sai->is_tdm = true;
 
 	return 0;
 }
@@ -695,11 +788,12 @@
 		dai->playback.formats = SNDRV_PCM_FMTBIT_S8 |
 					SNDRV_PCM_FMTBIT_S16_LE |
 					SNDRV_PCM_FMTBIT_S24_LE |
-					SNDRV_PCM_FMTBIT_S32_LE;
+					SNDRV_PCM_FMTBIT_S32_LE |
+					SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
 
 		sai->playback_dma_data.addr = res->start + SAI_TXDR;
 		sai->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-		sai->playback_dma_data.maxburst = 8;
+		sai->playback_dma_data.maxburst = MAXBURST_PER_FIFO;
 	}
 
 	if (sai->has_capture) {
@@ -710,11 +804,12 @@
 		dai->capture.formats = SNDRV_PCM_FMTBIT_S8 |
 				       SNDRV_PCM_FMTBIT_S16_LE |
 				       SNDRV_PCM_FMTBIT_S24_LE |
-				       SNDRV_PCM_FMTBIT_S32_LE;
+				       SNDRV_PCM_FMTBIT_S32_LE |
+				       SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
 
 		sai->capture_dma_data.addr = res->start + SAI_RXDR;
 		sai->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-		sai->capture_dma_data.maxburst = 8;
+		sai->capture_dma_data.maxburst = MAXBURST_PER_FIFO;
 	}
 
 	regmap_update_bits(sai->regmap, SAI_DMACR, SAI_DMACR_TDL_MASK,
@@ -728,8 +823,8 @@
 	return 0;
 }
 
-static const char * const tcsr_text[] = { "SDOx1", "SDOx2", "SDOx3", "SDOx4" };
-static const char * const rcsr_text[] = { "SDIx1", "SDIx2", "SDIx3", "SDIx4" };
+static const char * const tx_lanes_text[] = { "Auto", "SDOx1", "SDOx2", "SDOx3", "SDOx4" };
+static const char * const rx_lanes_text[] = { "Auto", "SDIx1", "SDIx2", "SDIx3", "SDIx4" };
 static const char * const edge_text[] = { "Rising Edge", "Dual Edge" };
 static const char * const edge_shift_text[] = { "Normal", "Shift 1 Edge" };
 
@@ -743,8 +838,8 @@
 static const char * const vdj_text[] = { "Right J", "Left J" };
 
 static const char * const sbw_text[] = {
-	" 0", " 0", " 0", " 0", " 0", " 0", " 0", " 8",
-	" 9", "10", "11", "12", "13", "14", "15", "16",
+	 "0",  "0",  "0",  "0",  "0",  "0",  "0",  "8",
+	 "9", "10", "11", "12", "13", "14", "15", "16",
 	"17", "18", "19", "20", "21", "22", "23", "24",
 	"25", "26", "27", "28", "29", "30", "31", "32", };
 
@@ -752,7 +847,7 @@
 
 static DECLARE_TLV_DB_SCALE(rmss_tlv, 0, 128, 0);
 
-static const char * const mss_text[] = { "Master", "Slave" };
+static const char * const mss_text[] = { "Slave", "Master" };
 
 static const char * const ckp_text[] = { "Normal", "Inverted" };
 
@@ -771,7 +866,8 @@
 
 /* TXCR */
 static SOC_ENUM_SINGLE_DECL(tsft_enum, SAI_TXCR, 22, edge_shift_text);
-static SOC_ENUM_SINGLE_DECL(tcsr_enum, SAI_TXCR, 20, tcsr_text);
+static const struct soc_enum tx_lanes_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tx_lanes_text), tx_lanes_text);
 static SOC_ENUM_SINGLE_DECL(tsjm_enum, SAI_TXCR, 19, sjm_text);
 static SOC_ENUM_SINGLE_DECL(tfbm_enum, SAI_TXCR, 18, fbm_text);
 static SOC_ENUM_SINGLE_DECL(tvdj_enum, SAI_TXCR, 10, vdj_text);
@@ -786,7 +882,8 @@
 
 /* RXCR */
 static SOC_ENUM_SINGLE_DECL(rsft_enum, SAI_RXCR, 22, edge_shift_text);
-static SOC_ENUM_SINGLE_DECL(rcsr_enum, SAI_RXCR, 20, rcsr_text);
+static const struct soc_enum rx_lanes_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_lanes_text), rx_lanes_text);
 static SOC_ENUM_SINGLE_DECL(rsjm_enum, SAI_RXCR, 19, sjm_text);
 static SOC_ENUM_SINGLE_DECL(rfbm_enum, SAI_RXCR, 18, fbm_text);
 static SOC_ENUM_SINGLE_DECL(rvdj_enum, SAI_RXCR, 10, vdj_text);
@@ -797,7 +894,8 @@
 static SOC_ENUM_SINGLE_DECL(tmono_switch, SAI_MONO_CR, 0, mono_text);
 
 /* CKR */
-static SOC_ENUM_SINGLE_DECL(mss_switch, SAI_CKR, 2, mss_text);
+static const struct soc_enum mss_switch =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mss_text), mss_text);
 static SOC_ENUM_SINGLE_DECL(sp_switch,  SAI_CKR, 1, ckp_text);
 static SOC_ENUM_SINGLE_DECL(fp_switch,  SAI_CKR, 0, ckp_text);
 
@@ -874,19 +972,223 @@
 	return 1;
 }
 
+static int rockchip_sai_tx_lanes_get(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+
+	ucontrol->value.enumerated.item[0] = sai->tx_lanes;
+
+	return 0;
+}
+
+static int rockchip_sai_tx_lanes_put(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+	int num;
+
+	num = ucontrol->value.enumerated.item[0];
+	if (num >= ARRAY_SIZE(tx_lanes_text))
+		return -EINVAL;
+
+	sai->tx_lanes = num;
+
+	return 1;
+}
+
+static int rockchip_sai_rx_lanes_get(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+
+	ucontrol->value.enumerated.item[0] = sai->rx_lanes;
+
+	return 0;
+}
+
+static int rockchip_sai_rx_lanes_put(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+	int num;
+
+	num = ucontrol->value.enumerated.item[0];
+	if (num >= ARRAY_SIZE(rx_lanes_text))
+		return -EINVAL;
+
+	sai->rx_lanes = num;
+
+	return 1;
+}
+
+static int rockchip_sai_mss_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+
+	ucontrol->value.enumerated.item[0] = sai->is_master_mode;
+
+	return 0;
+}
+
+static int rockchip_sai_mss_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+	bool mss;
+
+	/* MUST: do not update mode while stream is running */
+	if (snd_soc_component_active(component))
+		return -EPERM;
+
+	mss = !!ucontrol->value.enumerated.item[0];
+	if (mss == sai->is_master_mode)
+		return 0;
+
+	sai->is_master_mode = mss;
+
+	pm_runtime_get_sync(sai->dev);
+	if (sai->is_master_mode) {
+		/* Switch from Slave to Master */
+		regmap_update_bits(sai->regmap, SAI_CKR,
+				   SAI_CKR_MSS_MASK,
+				   SAI_CKR_MSS_MASTER);
+		regmap_update_bits(sai->regmap, SAI_XFER,
+				   SAI_XFER_CLK_MASK |
+				   SAI_XFER_FSS_MASK,
+				   SAI_XFER_CLK_EN |
+				   SAI_XFER_FSS_EN);
+	} else {
+		/* Switch from Master to Slave */
+		regmap_update_bits(sai->regmap, SAI_CKR,
+				   SAI_CKR_MSS_MASK,
+				   SAI_CKR_MSS_SLAVE);
+		regmap_update_bits(sai->regmap, SAI_XFER,
+				   SAI_XFER_CLK_MASK |
+				   SAI_XFER_FSS_MASK,
+				   SAI_XFER_CLK_DIS |
+				   SAI_XFER_FSS_DIS);
+	}
+	pm_runtime_put(sai->dev);
+
+	return 1;
+}
+
+static int rockchip_sai_clk_auto_get(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+
+	ucontrol->value.integer.value[0] = sai->is_clk_auto;
+
+	return 0;
+}
+
+static int rockchip_sai_clk_auto_put(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+	bool clk_auto = ucontrol->value.integer.value[0];
+
+	if (clk_auto == sai->is_clk_auto)
+		return 0;
+
+	sai->is_clk_auto = clk_auto;
+
+	return 1;
+}
+
+static int rockchip_sai_wait_time_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = WAIT_TIME_MS_MAX;
+	uinfo->value.integer.step = 1;
+
+	return 0;
+}
+
+static int rockchip_sai_rd_wait_time_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+
+	ucontrol->value.integer.value[0] = sai->wait_time[SNDRV_PCM_STREAM_CAPTURE];
+
+	return 0;
+}
+
+static int rockchip_sai_rd_wait_time_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+
+	if (ucontrol->value.integer.value[0] > WAIT_TIME_MS_MAX)
+		return -EINVAL;
+
+	sai->wait_time[SNDRV_PCM_STREAM_CAPTURE] = ucontrol->value.integer.value[0];
+
+	return 1;
+}
+
+static int rockchip_sai_wr_wait_time_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+
+	ucontrol->value.integer.value[0] = sai->wait_time[SNDRV_PCM_STREAM_PLAYBACK];
+
+	return 0;
+}
+
+static int rockchip_sai_wr_wait_time_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+
+	if (ucontrol->value.integer.value[0] > WAIT_TIME_MS_MAX)
+		return -EINVAL;
+
+	sai->wait_time[SNDRV_PCM_STREAM_PLAYBACK] = ucontrol->value.integer.value[0];
+
+	return 1;
+}
+
+#define SAI_PCM_WAIT_TIME(xname, xhandler_get, xhandler_put)	\
+{	.iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname,	\
+	.info = rockchip_sai_wait_time_info,			\
+	.get = xhandler_get, .put = xhandler_put }
+
 static DECLARE_TLV_DB_SCALE(fs_shift_tlv, 0, 8192, 0);
 
 static const struct snd_kcontrol_new rockchip_sai_controls[] = {
 
 	SOC_ENUM("Transmit Edge Shift", tsft_enum),
-	SOC_ENUM("Transmit SDOx Select", tcsr_enum),
+	SOC_ENUM_EXT("Transmit SDOx Select", tx_lanes_enum,
+		     rockchip_sai_tx_lanes_get, rockchip_sai_tx_lanes_put),
 	SOC_ENUM("Transmit Store Justified Mode", tsjm_enum),
 	SOC_ENUM("Transmit First Bit Mode", tfbm_enum),
 	SOC_ENUM("Transmit Valid Data Justified", tvdj_enum),
 	SOC_ENUM("Transmit Slot Bit Width", tsbw_enum),
 
 	SOC_ENUM("Receive Edge Shift", rsft_enum),
-	SOC_ENUM("Receive SDIx Select", rcsr_enum),
+	SOC_ENUM_EXT("Receive SDIx Select", rx_lanes_enum,
+		     rockchip_sai_rx_lanes_get, rockchip_sai_rx_lanes_put),
 	SOC_ENUM("Receive Store Justified Mode", rsjm_enum),
 	SOC_ENUM("Receive First Bit Mode", rfbm_enum),
 	SOC_ENUM("Receive Valid Data Justified", rvdj_enum),
@@ -903,7 +1205,8 @@
 	SOC_ENUM("Receive Mono Switch", rmono_switch),
 	SOC_ENUM("Transmit Mono Switch", tmono_switch),
 
-	SOC_ENUM("Master / Slave Mode Select", mss_switch),
+	SOC_ENUM_EXT("Master / Slave Mode Select", mss_switch,
+		     rockchip_sai_mss_get, rockchip_sai_mss_put),
 	SOC_ENUM("Sclk Polarity", sp_switch),
 	SOC_ENUM("Frame Sync Polarity", fp_switch),
 
@@ -930,6 +1233,17 @@
 		       0, 8192, 0, fs_shift_tlv),
 	SOC_SINGLE_TLV("Receive Frame Shift Select", SAI_RX_SHIFT,
 		       0, 8192, 0, fs_shift_tlv),
+
+	SOC_SINGLE_BOOL_EXT("Clk Auto Switch", 0,
+			    rockchip_sai_clk_auto_get,
+			    rockchip_sai_clk_auto_put),
+
+	SAI_PCM_WAIT_TIME("PCM Read Wait Time MS",
+			  rockchip_sai_rd_wait_time_get,
+			  rockchip_sai_rd_wait_time_put),
+	SAI_PCM_WAIT_TIME("PCM Write Wait Time MS",
+			  rockchip_sai_wr_wait_time_get,
+			  rockchip_sai_wr_wait_time_put),
 };
 
 static const struct snd_soc_component_driver rockchip_sai_component = {
@@ -966,6 +1280,50 @@
 	return IRQ_HANDLED;
 }
 
+static int rockchip_sai_keep_clk_always_on(struct rk_sai_dev *sai)
+{
+	unsigned int mclk_rate, bclk_rate, div_bclk;
+
+	sai->is_master_mode = true;
+
+	/* init I2S fmt default */
+	rockchip_sai_fmt_create(sai, SND_SOC_DAIFMT_I2S);
+
+	regmap_update_bits(sai->regmap, SAI_FSCR,
+			   SAI_FSCR_FW_MASK |
+			   SAI_FSCR_FPW_MASK,
+			   SAI_FSCR_FW(64) |
+			   SAI_FSCR_FPW(32));
+
+	mclk_rate = clk_get_rate(sai->mclk);
+	bclk_rate = DEFAULT_FS * 64;
+	div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate);
+
+	regmap_update_bits(sai->regmap, SAI_CKR, SAI_CKR_MDIV_MASK,
+			   SAI_CKR_MDIV(div_bclk));
+
+	pm_runtime_forbid(sai->dev);
+
+	dev_info(sai->dev, "CLK-ALWAYS-ON: mclk: %d, bclk: %d, fsync: %d\n",
+		 mclk_rate, bclk_rate, DEFAULT_FS);
+
+	return 0;
+}
+
+static int rockchip_sai_parse_quirks(struct rk_sai_dev *sai)
+{
+	int ret = 0, i = 0;
+
+	for (i = 0; i < ARRAY_SIZE(of_quirks); i++)
+		if (device_property_read_bool(sai->dev, of_quirks[i].quirk))
+			sai->quirks |= of_quirks[i].id;
+
+	if (sai->quirks & QUIRK_ALWAYS_ON)
+		ret = rockchip_sai_keep_clk_always_on(sai);
+
+	return ret;
+}
+
 static int rockchip_sai_probe(struct platform_device *pdev)
 {
 	struct device_node *node = pdev->dev.of_node;
@@ -981,6 +1339,8 @@
 
 	sai->dev = &pdev->dev;
 	sai->fw_ratio = 1;
+	/* match to register default */
+	sai->is_master_mode = true;
 	dev_set_drvdata(&pdev->dev, sai);
 
 	sai->rst_h = devm_reset_control_get_optional_exclusive(&pdev->dev, "h");
@@ -1000,7 +1360,7 @@
 	if (IS_ERR(sai->regmap))
 		return PTR_ERR(sai->regmap);
 
-	irq = platform_get_irq(pdev, 0);
+	irq = platform_get_irq_optional(pdev, 0);
 	if (irq > 0) {
 		ret = devm_request_irq(&pdev->dev, irq, rockchip_sai_isr,
 				       IRQF_SHARED, node->name, sai);
@@ -1022,13 +1382,13 @@
 		return PTR_ERR(sai->hclk);
 	}
 
-	ret = clk_prepare_enable(sai->hclk);
+	ret = rockchip_sai_parse_quirks(sai);
 	if (ret)
 		return ret;
 
 	pm_runtime_enable(&pdev->dev);
 	if (!pm_runtime_enabled(&pdev->dev)) {
-		ret = sai_runtime_resume(&pdev->dev);
+		ret = rockchip_sai_runtime_resume(&pdev->dev);
 		if (ret)
 			goto err_runtime_disable;
 	}
@@ -1043,6 +1403,11 @@
 	if (ret)
 		goto err_runtime_suspend;
 
+	if (device_property_read_bool(&pdev->dev, "rockchip,no-dmaengine")) {
+		dev_info(&pdev->dev, "Used for Multi-DAI\n");
+		return 0;
+	}
+
 	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
 	if (ret)
 		goto err_runtime_suspend;
@@ -1051,29 +1416,49 @@
 
 err_runtime_suspend:
 	if (!pm_runtime_status_suspended(&pdev->dev))
-		sai_runtime_suspend(&pdev->dev);
+		rockchip_sai_runtime_suspend(&pdev->dev);
 err_runtime_disable:
 	pm_runtime_disable(&pdev->dev);
-	clk_disable_unprepare(sai->hclk);
 
 	return ret;
 }
 
 static int rockchip_sai_remove(struct platform_device *pdev)
 {
-	struct rk_sai_dev *sai = dev_get_drvdata(&pdev->dev);
-
 	pm_runtime_disable(&pdev->dev);
 	if (!pm_runtime_status_suspended(&pdev->dev))
-		sai_runtime_suspend(&pdev->dev);
-
-	clk_disable_unprepare(sai->hclk);
+		rockchip_sai_runtime_suspend(&pdev->dev);
 
 	return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int rockchip_sai_suspend(struct device *dev)
+{
+	struct rk_sai_dev *sai = dev_get_drvdata(dev);
+
+	regcache_mark_dirty(sai->regmap);
+
+	return 0;
+}
+
+static int rockchip_sai_resume(struct device *dev)
+{
+	struct rk_sai_dev *sai = dev_get_drvdata(dev);
+	int ret = pm_runtime_resume_and_get(dev);
+
+	if (ret < 0)
+		return ret;
+	ret = regcache_sync(sai->regmap);
+	pm_runtime_put(dev);
+
+	return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
 static const struct dev_pm_ops rockchip_sai_pm_ops = {
-	SET_RUNTIME_PM_OPS(sai_runtime_suspend, sai_runtime_resume, NULL)
+	SET_RUNTIME_PM_OPS(rockchip_sai_runtime_suspend, rockchip_sai_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(rockchip_sai_suspend, rockchip_sai_resume)
 };
 
 static struct platform_driver rockchip_sai_driver = {

--
Gitblit v1.6.2