[Sound-open-firmware] [PATCH] ssp: clean-up register setup

Pierre-Louis Bossart pierre-louis.bossart at linux.intel.com
Wed Nov 15 00:29:59 CET 2017


Clean-up register configurations for SSCR0..5, SSPSP, SFIFOTT

The results are ok in I2S and LEFT_J 24 bits
Tested with RT5645 and DA7212

TODO:
1. fix 16 bit issue (right channel lost)
2. test DSP modes
3. connect SSP and DMA watermarks

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart at linux.intel.com>
---
 src/drivers/ssp.c      | 264 ++++++++++++++++++++++++++++++++++++++++++-------
 src/include/reef/ssp.h |  40 ++++++--
 2 files changed, 259 insertions(+), 45 deletions(-)

diff --git a/src/drivers/ssp.c b/src/drivers/ssp.c
index 9eb4934..0ed570c 100644
--- a/src/drivers/ssp.c
+++ b/src/drivers/ssp.c
@@ -30,6 +30,7 @@
  */
 
 #include <errno.h>
+#include <stdbool.h>
 #include <reef/stream.h>
 #include <reef/ssp.h>
 #include <reef/alloc.h>
@@ -40,6 +41,19 @@
 #define trace_ssp_error(__e)	trace_error(TRACE_CLASS_SSP, __e)
 #define tracev_ssp(__e)	tracev_event(TRACE_CLASS_SSP, __e)
 
+/* FIXME: move this to a helper and optimize */
+static int hweight_32(uint32_t mask)
+{
+	int i;
+	int count = 0;
+
+	for (i = 0; i < 32; i++) {
+		count += mask&1;
+		mask >>= 1;
+	}
+	return count;
+}
+
 /* save SSP context prior to entering D3 */
 static int ssp_context_store(struct dai *dai)
 {
@@ -47,6 +61,8 @@ static int ssp_context_store(struct dai *dai)
 
 	ssp->sscr0 = ssp_read(dai, SSCR0);
 	ssp->sscr1 = ssp_read(dai, SSCR1);
+
+	/* FIXME: need to store sscr2,3,4,5 */
 	ssp->psp = ssp_read(dai, SSPSP);
 
 	return 0;
@@ -59,6 +75,7 @@ static int ssp_context_restore(struct dai *dai)
 
 	ssp_write(dai, SSCR0, ssp->sscr0);
 	ssp_write(dai, SSCR1, ssp->sscr1);
+	/* FIXME: need to restore sscr2,3,4,5 */
 	ssp_write(dai, SSPSP, ssp->psp);
 
 	return 0;
@@ -73,12 +90,20 @@ static inline int ssp_set_config(struct dai *dai,
 	uint32_t sscr1;
 	uint32_t sscr2;
 	uint32_t sscr3;
+	uint32_t sscr4;
+	uint32_t sscr5;
 	uint32_t sspsp;
 	uint32_t sfifott;
 	uint32_t mdiv;
 	uint32_t bdiv;
 	uint32_t data_size;
+	uint32_t start_delay;
+	uint32_t active_tx_slots = 2;
+	uint32_t active_rx_slots = 2;
 	uint32_t frame_len = 0;
+	bool inverted_frame = false;
+	bool cfs = false;
+	bool cbs = false;
 	int ret = 0;
 
 	spin_lock(&ssp->lock);
@@ -94,34 +119,98 @@ static inline int ssp_set_config(struct dai *dai,
 	trace_ssp("cos");
 
 	/* reset SSP settings */
-	sscr0 = SSCR0_RIM | SSCR0_TIM;
-	sscr1 = SSCR1_PINTE | SSCR1_RWOT;
-	sscr2 = 0x1c1;
-	sscr3 = 0x2c018;
-	sspsp = 0;
+	/* sscr0 dynamic settings are DSS, EDSS, SCR, FRDC, ECS */
+	/*
+	 * FIXME: MOD, ACS, NCS are not set,
+	 * no support for network mode for now
+	 */
+	sscr0 = SSCR0_PSP | SSCR0_RIM | SSCR0_TIM;
+
+	/*
+	 * FIXME: PINTE and RWOT are not set in sscr1
+	 *   sscr1 = SSCR1_PINTE | SSCR1_RWOT;
+	 */
+
+	/* sscr1 dynamic settings are TFT, RFT, SFRMDIR, SCLKDIR, SCFR */
+	sscr1 = SSCR1_TTE;
+#ifdef ENABLE_TIE_RIE /* FIXME: not enabled, difference with SST driver */
+	sscr1 |= SSCR1_TIE | SSCR1_RIE;
+#endif
+
+	/* sscr2 dynamic setting is SLV_EXT_CLK_RUN_EN */
+	sscr2 = SSCR2_URUN_FIX0;
+	sscr2 |= SSCR2_ASRC_INTR_MASK;
+#ifdef ENABLE_SSCR2_FIXES /* FIXME: is this needed ? */
+	sscr2 |= SSCR2_UNDRN_FIX_EN | SSCR2_FIFO_EMPTY_FIX_EN;
+#endif
+
+
+	/*
+	 * sscr3 dynamic settings are FRM_MS_EN, I2S_MODE_EN, I2S_FRM_POL,
+	 * I2S_TX_EN, I2S_RX_EN, I2S_CLK_MST
+	 */
+	sscr3 = SSCR3_I2S_TX_SS_FIX_EN | SSCR3_I2S_RX_SS_FIX_EN |
+		SSCR3_STRETCH_TX | SSCR3_STRETCH_RX |
+		SSCR3_SYN_FIX_EN;
+#ifdef ENABLE_CLK_EDGE_SEL /* FIXME: is this needed ? */
+	sscr3 |= SSCR3_CLK_EDGE_SEL;
+#endif
+
+	/* sscr4 dynamic settings is TOT_FRAME_PRD */
+	sscr4 = 0x0;
+
+	/* sscr4 dynamic settings are FRM_ASRT_CLOCKS and FRM_POLARITY */
+	sscr5 = 0x0;
+
+	/* sspsp dynamic settings are SCMODE, SFRMP, DMYSTRT, SFRMWDTH */
+	sspsp = SSPSP_ETDS; /* make sure SDO line is tri-stated when inactive */
 
 	ssp->config = *config;
 	ssp->params = config->ssp[0];
 
-	/* TODO: allow topology to define SSP clock type */
-	config->ssp[0].clk_id = SSP_CLK_EXT;
-
 	/* clock masters */
+	/*
+	 * On TNG/BYT/CHT, the SSP wrapper generates the fs even in master mode,
+	 * the master/slave choice depends on the clock type
+	 */
+	sscr1 |= SSCR1_SFRMDIR;
+
 	switch (config->format & SOF_DAI_FMT_MASTER_MASK) {
 	case SOF_DAI_FMT_CBM_CFM:
-		sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR;
+		sscr0 |= SSCR0_ECS; /* external clock used */
+		sscr1 |= SSCR1_SCLKDIR;
+		/*
+		 * FIXME: does SSRC1.SCFR need to be set
+		 * when codec is master ?
+		 */
+		sscr2 |= SSCR2_SLV_EXT_CLK_RUN_EN;
 		break;
 	case SOF_DAI_FMT_CBS_CFS:
+#ifdef ENABLE_SSRCR1_SCFR /* FIXME: is this needed ? */
 		sscr1 |= SSCR1_SCFR;
-		sscr3 |= SSCR3_I2S_FRM_MST | SSCR3_I2S_CLK_MST;
+#endif
+		sscr3 |= SSCR3_FRM_MST_EN;
+		cfs = true;
+		cbs = true;
 		break;
 	case SOF_DAI_FMT_CBM_CFS:
-		sscr1 |= SSCR1_SFRMDIR;
+		sscr0 |= SSCR0_ECS; /* external clock used */
+		sscr1 |= SSCR1_SCLKDIR;
+		/*
+		 * FIXME: does SSRC1.SCFR need to be set
+		 * when codec is master ?
+		 */
+		sscr2 |= SSCR2_SLV_EXT_CLK_RUN_EN;
+		sscr3 |= SSCR3_FRM_MST_EN;
+		cfs = true;
+		/* FIXME: this mode has not been tested */
 		break;
 	case SOF_DAI_FMT_CBS_CFM:
-		sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR | SSCR1_SCFR;
-		break;
-	case SSP_CLK_DEFAULT:
+#ifdef ENABLE_SSRCR1_SCFR /* FIXME: is this needed ? */
+		sscr1 |= SSCR1_SCFR;
+#endif
+		/* FIXME: this mode has not been tested */
+		cbs = true;
 		break;
 	default:
 		trace_ssp_error("ec2");
@@ -137,9 +226,11 @@ static inline int ssp_set_config(struct dai *dai,
 		break;
 	case SOF_DAI_FMT_IB_IF:
 		sspsp |= SSPSP_SCMODE(2);
+		inverted_frame = true; /* handled later with format */
 		break;
 	case SOF_DAI_FMT_IB_NF:
-		sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP;
+		sspsp |= SSPSP_SCMODE(2);
+		inverted_frame = true; /* handled later with format */
 		break;
 	default:
 		trace_ssp_error("ec3");
@@ -147,6 +238,10 @@ static inline int ssp_set_config(struct dai *dai,
 		goto out;
 	}
 
+#ifdef CLK_TYPE /* not enabled, keep the code for reference */
+	/* TODO: allow topology to define SSP clock type */
+	config->ssp[0].clk_id = SSP_CLK_EXT;
+
 	/* clock source */
 	switch (config->ssp[0].clk_id) {
 	case SSP_CLK_AUDIO:
@@ -166,6 +261,7 @@ static inline int ssp_set_config(struct dai *dai,
 		ret = -EINVAL;
 		goto out;
 	}
+#endif
 
 	/* BCLK is generated from MCLK - must be divisable */
 	if (config->mclk % config->bclk) {
@@ -211,20 +307,118 @@ static inline int ssp_set_config(struct dai *dai,
 	switch (config->format & SOF_DAI_FMT_FORMAT_MASK) {
 	case SOF_DAI_FMT_I2S:
 
+		start_delay = 1;
+
 		/* enable I2S mode */
-		sscr3 |= SSCR3_I2S_ENA | SSCR3_I2S_TX_ENA | SSCR3_I2S_RX_ENA;
-		sscr0 |= SSCR0_PSP;
+		sscr3 |= SSCR3_I2S_MODE_EN | SSCR3_I2S_TX_EN | SSCR3_I2S_RX_EN;
+
+		/* set asserted frame length */
+		frame_len = config->sample_container_bits;
+
+		/* handle frame polarity, I2S default is falling/active low */
+		sspsp |= SSPSP_SFRMP(!inverted_frame);
+		sscr3 |= SSCR3_I2S_FRM_POL(!inverted_frame);
+
+		if (cbs) {
+			/*
+			 * keep RX functioning on a TX underflow
+			 * (I2S/LEFT_J master only)
+			 */
+			sscr3 |= SSCR3_MST_CLK_EN;
+
+			/*
+			 * total frame period (both asserted and
+			 * deasserted time of frame
+			 */
+			sscr4 |= SSCR4_TOT_FRM_PRD(frame_len << 1);
+		}
+
+		break;
+
+	case SOF_DAI_FMT_LEFT_J:
+
+		start_delay = 0;
+
+		/* apparently we need the same initialization as for I2S */
+		sscr3 |= SSCR3_I2S_MODE_EN | SSCR3_I2S_TX_EN | SSCR3_I2S_RX_EN;
 
 		/* set asserted frame length */
 		frame_len = config->sample_container_bits;
+
+		/* LEFT_J default is rising/active high, opposite of I2S */
+		sspsp |= SSPSP_SFRMP(inverted_frame);
+		sscr3 |= SSCR3_I2S_FRM_POL(inverted_frame);
+
+		if (cbs) {
+			/*
+			 * keep RX functioning on a TX underflow
+			 * (I2S/LEFT_J master only)
+			 */
+			sscr3 |= SSCR3_MST_CLK_EN;
+
+			/*
+			 * total frame period (both asserted and
+			 * deasserted time of frame
+			 */
+			sscr4 |= SSCR4_TOT_FRM_PRD(frame_len << 1);
+		}
+
 		break;
 	case SOF_DAI_FMT_DSP_A:
-		sscr0 |= SSCR0_PSP | SSCR0_MOD | SSCR0_FRDC(config->num_slots);
-		sspsp |= SSPSP_SFRMWDTH(1) | SSPSP_SFRMDLY(2);
+
+		start_delay = 1;
+
+		sscr0 |= SSCR0_MOD | SSCR0_FRDC(config->num_slots);
+
+		/* set asserted frame length */
+		frame_len = 1;
+
+		/* handle frame polarity, DSP_A default is rising/active high */
+		sspsp |= SSPSP_SFRMP(inverted_frame);
+		if (cfs) {
+			/* set sscr frame polarity in DSP/master mode only */
+			sscr5 |= SSCR5_FRM_POLARITY(inverted_frame);
+		}
+
+		/*
+		 * total frame period (both asserted and
+		 * deasserted time of frame)
+		 */
+		if (cbs)
+			sscr4 |= SSCR4_TOT_FRM_PRD(config->num_slots *
+					   config->sample_container_bits);
+
+		active_tx_slots = hweight_32(config->tx_slot_mask);
+		active_rx_slots = hweight_32(config->rx_slot_mask);
+
 		break;
 	case SOF_DAI_FMT_DSP_B:
-		sscr0 |= SSCR0_PSP | SSCR0_MOD | SSCR0_FRDC(config->num_slots);
-		sspsp |= SSPSP_SFRMWDTH(1);
+
+		start_delay = 0;
+
+		sscr0 |= SSCR0_MOD | SSCR0_FRDC(config->num_slots);
+
+		/* set asserted frame length */
+		frame_len = 1;
+
+		/* handle frame polarity, DSP_A default is rising/active high */
+		sspsp |= SSPSP_SFRMP(inverted_frame);
+		if (cfs) {
+			/* set sscr frame polarity in DSP/master mode only */
+			sscr5 |= SSCR5_FRM_POLARITY(inverted_frame);
+		}
+
+		/*
+		 * total frame period (both asserted and
+		 * deasserted time of frame
+		 */
+		if (cbs)
+			sscr4 |= SSCR4_TOT_FRM_PRD(config->num_slots *
+					   config->sample_container_bits);
+
+		active_tx_slots = hweight_32(config->tx_slot_mask);
+		active_rx_slots = hweight_32(config->rx_slot_mask);
+
 		break;
 	default:
 		trace_ssp_error("eca");
@@ -232,35 +426,35 @@ static inline int ssp_set_config(struct dai *dai,
 		goto out;
 	}
 
-	/* set frame length and slot mask in I2s & PCM modes */
-	ssp_write(dai, SSCR4,
-		SSCR4_FRM_CLOCKS(config->sample_container_bits << 1));
-	ssp_write(dai, SSCR5, SSCR5_FRM_ASRT_CLOCKS(frame_len));
-	ssp_write(dai, SSTSA, config->tx_slot_mask);
-	ssp_write(dai, SSRSA, config->rx_slot_mask);
+	sspsp |= SSPSP_DMYSTRT(start_delay);
+	sspsp |= SSPSP_SFRMWDTH(frame_len);
+	sscr5 |= SSCR5_FRM_ASRT_CLOCKS(frame_len);
 
-	/* sample data size on SSP FIFO */
-	if (config->sample_valid_bits == 16)
-		/* 2 * 16bit packed into 32bit FIFO */
-		data_size = 32;
-	else
-		data_size = config->sample_container_bits;
+	data_size = config->sample_valid_bits;
 
 	if (data_size > 16)
 		sscr0 |= (SSCR0_EDSS | SSCR0_DSIZE(data_size - 16));
 	else
 		sscr0 |= SSCR0_DSIZE(data_size);
 
-	/* watermarks - (RFT + 1) should equal DMA SRC_MSIZE */
-	sfifott = (SFIFOTT_TX(4) | SFIFOTT_RX(12));
+	/* FIXME:
+	 * watermarks - (RFT + 1) should equal DMA SRC_MSIZE
+	 */
+	sfifott = (SFIFOTT_TX(2*active_tx_slots) |
+		   SFIFOTT_RX(2*active_rx_slots));
 
 	trace_ssp("coe");
+
 	ssp_write(dai, SSCR0, sscr0);
 	ssp_write(dai, SSCR1, sscr1);
 	ssp_write(dai, SSCR2, sscr2);
 	ssp_write(dai, SSCR3, sscr3);
+	ssp_write(dai, SSCR4, sscr4);
+	ssp_write(dai, SSCR5, sscr5);
 	ssp_write(dai, SSPSP, sspsp);
 	ssp_write(dai, SFIFOTT, sfifott);
+	ssp_write(dai, SSTSA, config->tx_slot_mask);
+	ssp_write(dai, SSRSA, config->rx_slot_mask);
 
 	ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_PREPARE;
 	ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_PREPARE;
diff --git a/src/include/reef/ssp.h b/src/include/reef/ssp.h
index e3da690..c00d14c 100644
--- a/src/include/reef/ssp.h
+++ b/src/include/reef/ssp.h
@@ -91,9 +91,9 @@ extern const struct dai_ops ssp_ops;
 #define SSCR1_SPH	(1 << 4)
 #define SSCR1_MWDS	(1 << 5)
 #define SSCR1_TFT_MASK	(0x000003c0)
-#define SSCR1_TX(x) (((x) - 1) << 6)
+#define SSCR1_TFT(x) (((x) - 1) << 6)
 #define SSCR1_RFT_MASK	(0x00003c00)
-#define SSCR1_RX(x) (((x) - 1) << 10)
+#define SSCR1_RFT(x) (((x) - 1) << 10)
 #define SSCR1_EFWR	(1 << 14)
 #define SSCR1_STRF	(1 << 15)
 #define SSCR1_IFS	(1 << 16)
@@ -112,6 +112,18 @@ extern const struct dai_ops ssp_ops;
 #define SSCR1_TTE	(1 << 30)
 #define SSCR1_TTELP	(1 << 31)
 
+/* SSCR2 bits */
+#define SSCR2_URUN_FIX0	(1 << 0)
+#define SSCR2_URUN_FIX1	(1 << 1)
+#define SSCR2_SLV_EXT_CLK_RUN_EN	(1 << 2)
+#define SSCR2_CLK_DEL_EN		(1 << 3)
+#define SSCR2_UNDRN_FIX_EN		(1 << 6)
+#define SSCR2_FIFO_EMPTY_FIX_EN		(1 << 7)
+#define SSCR2_ASRC_CNTR_EN		(1 << 8)
+#define SSCR2_ASRC_CNTR_CLR		(1 << 9)
+#define SSCR2_ASRC_FRM_CNRT_EN		(1 << 10)
+#define SSCR2_ASRC_INTR_MASK		(1 << 11)
+
 /* SSR bits */
 #define SSSR_TNF	(1 << 2)
 #define SSSR_RNE	(1 << 3)
@@ -122,7 +134,7 @@ extern const struct dai_ops ssp_ops;
 
 /* SSPSP bits */
 #define SSPSP_SCMODE(x)		((x) << 0)
-#define SSPSP_SFRMP		(1 << 2)
+#define SSPSP_SFRMP(x)		((x) << 2)
 #define SSPSP_ETDS		(1 << 3)
 #define SSPSP_STRTDLY(x)	((x) << 4)
 #define SSPSP_DMYSTRT(x)	((x) << 7)
@@ -132,18 +144,26 @@ extern const struct dai_ops ssp_ops;
 #define SSPSP_FSRT		(1 << 25)
 
 /* SSCR3 bits */
-#define SSCR3_I2S_FRM_MST	(1 << 0)
-#define SSCR3_I2S_ENA		(1 << 1)
-#define SSCR3_I2S_FRM_POL	(1 << 2)
-#define SSCR3_I2S_TX_ENA	(1 << 9)
-#define SSCR3_I2S_RX_ENA	(1 << 10)
-#define SSCR3_I2S_CLK_MST	(1 << 16)
+#define SSCR3_FRM_MST_EN	(1 << 0)
+#define SSCR3_I2S_MODE_EN	(1 << 1)
+#define SSCR3_I2S_FRM_POL(x)	((x) << 2)
+#define SSCR3_I2S_TX_SS_FIX_EN	(1 << 3)
+#define SSCR3_I2S_RX_SS_FIX_EN	(1 << 4)
+#define SSCR3_I2S_TX_EN		(1 << 9)
+#define SSCR3_I2S_RX_EN		(1 << 10)
+#define SSCR3_CLK_EDGE_SEL	(1 << 12)
+#define SSCR3_STRETCH_TX	(1 << 14)
+#define SSCR3_STRETCH_RX	(1 << 15)
+#define SSCR3_MST_CLK_EN	(1 << 16)
+#define SSCR3_SYN_FIX_EN	(1 << 17)
+
 
 /* SSCR4 bits */
-#define SSCR4_FRM_CLOCKS(x)	((x) << 7)
+#define SSCR4_TOT_FRM_PRD(x)	((x) << 7)
 
 /* SSCR5 bits */
 #define SSCR5_FRM_ASRT_CLOCKS(x)	(((x) - 1) << 1)
+#define SSCR5_FRM_POLARITY(x)	((x) << 0)
 
 /* SFIFOTT bits */
 #define SFIFOTT_TX(x)		((x) - 1)
-- 
2.14.1



More information about the Sound-open-firmware mailing list