[Sound-open-firmware] [RFC PATCH 2/4] apl-ssp: fix MCLK and BCLK source selection

Pierre-Louis Bossart pierre-louis.bossart at linux.intel.com
Wed May 23 02:31:30 CEST 2018


MCLK and BCLK can be independentely divided from Xtal or Audio
cardinal clock. With this patch MCLK can be divided by at most a
factor of 8.

The M/N dividers remain bypassed since they introduce an irregular
duty-cycle for the BCLK, which isn't desirable if the BCLK is used as
a reference by an external device.

The MCLK1 output was not tested since I don't have hardware to test
this configuration but the register address is set according to the
specification.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart at linux.intel.com>
---
 src/drivers/apl-ssp.c | 123 +++++++++++++++++++++++++++++++++++++++-----------
 src/include/sof/ssp.h |   5 ++
 2 files changed, 101 insertions(+), 27 deletions(-)

diff --git a/src/drivers/apl-ssp.c b/src/drivers/apl-ssp.c
index 1190596..df0c0f8 100644
--- a/src/drivers/apl-ssp.c
+++ b/src/drivers/apl-ssp.c
@@ -44,6 +44,10 @@
 #define trace_ssp_error(__e)	trace_error(TRACE_CLASS_SSP, __e)
 #define tracev_ssp(__e)	tracev_event(TRACE_CLASS_SSP, __e)
 
+#define F_19200_kHz 19200000
+#define F_24000_kHz 24000000
+#define F_24576_kHz 24576000
+
 /* FIXME: move this to a helper and optimize */
 static int hweight_32(uint32_t mask)
 {
@@ -103,6 +107,7 @@ static inline int ssp_set_config(struct dai *dai,
 	uint32_t bdiv;
 	uint32_t mdivc;
 	uint32_t mdivr;
+	uint32_t mdivr_val;
 	uint32_t i2s_m;
 	uint32_t i2s_n;
 	uint32_t data_size;
@@ -232,21 +237,104 @@ static inline int ssp_set_config(struct dai *dai,
 		goto out;
 	}
 
-if CONFIG_APOLLOLAKE
-	sscr0 |= SSCR0_MOD | SSCR0_ACS | SSCR0_ECS;
-#else
 	sscr0 |= SSCR0_MOD | SSCR0_ACS;
+
+	mdivc = 0x1;
+#ifdef CONFIG_CANNONLAKE
+	if (!config->ssp.mclk_rate || config->ssp.mclk_rate > F_24000_kHz) {
+		trace_ssp_error("eci");
+		ret = -EINVAL;
+		goto out;
+	}
+	if (!config->ssp.bclk_rate ||
+	    config->ssp.bclk_rate > config->ssp.mclk_rate) {
+		trace_ssp_error("ecj");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (F_24000_kHz % config->ssp.mclk_rate == 0) {
+		mdivr_val = F_24000_kHz / config->ssp.mclk_rate;
+	} else {
+		trace_ssp_error("eck");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (F_24000_kHz % config->ssp.bclk_rate == 0) {
+		mdiv = F_24000_kHz / config->ssp.bclk_rate;
+	} else {
+		trace_ssp_error("ecl");
+		ret = -EINVAL;
+		goto out;
+	}
+#else
+	if (!config->ssp.mclk_rate || config->ssp.mclk_rate > F_24576_kHz) {
+		trace_ssp_error("eci");
+		ret = -EINVAL;
+		goto out;
+	}
+	if (!config->ssp.bclk_rate ||
+	    config->ssp.bclk_rate > config->ssp.mclk_rate) {
+		trace_ssp_error("ecj");
+		ret = -EINVAL;
+		goto out;
+	}
+	if (F_24576_kHz % config->ssp.mclk_rate == 0) {
+		/* select Audio Cardinal clock for MCLK */
+		mdivc |= MCDSS(1);
+		mdivr_val = F_24576_kHz / config->ssp.mclk_rate;
+	} else if (config->ssp.mclk_rate <= F_19200_kHz &&
+		   F_19200_kHz % config->ssp.mclk_rate == 0) {
+		mdivr_val = F_19200_kHz / config->ssp.mclk_rate;
+	} else {
+		trace_ssp_error("eck");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (F_24576_kHz % config->ssp.bclk_rate == 0) {
+		/* select Audio Cardinal clock for M/N dividers */
+		mdivc |= MNDSS(1);
+		mdiv = F_24576_kHz / config->ssp.bclk_rate;
+		/* select M/N output for bclk */
+		sscr0 |= SSCR0_ECS;
+	} else if (F_19200_kHz % config->ssp.bclk_rate == 0) {
+		mdiv = F_19200_kHz / config->ssp.bclk_rate;
+	} else {
+		trace_ssp_error("ecl");
+		ret = -EINVAL;
+		goto out;
+	}
 #endif
 
-	/* BCLK is generated from MCLK - must be divisable */
-	if (config->ssp.mclk_rate % config->ssp.bclk_rate) {
-		trace_ssp_error("ec5");
+	switch (mdivr_val) {
+	case 1:
+		mdivr = 0x00000fff; /* bypass divider for MCLK */
+		break;
+	case 2:
+		mdivr = 0x0; /* 1/2 */
+		break;
+	case 4:
+		mdivr = 0x2; /* 1/4 */
+		break;
+	case 8:
+		mdivr = 0x6; /* 1/8 */
+		break;
+	default:
+		trace_ssp_error("ecm");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (config->ssp.mclk_id > 1) {
+		trace_ssp_error("ecn");
 		ret = -EINVAL;
 		goto out;
 	}
 
 	/* divisor must be within SCR range */
-	mdiv = (config->ssp.mclk_rate / config->ssp.bclk_rate) - 1;
+	mdiv -= 1;
 	if (mdiv > (SSCR0_SCR_MASK >> 8)) {
 		trace_ssp_error("ec6");
 		ret = -EINVAL;
@@ -263,7 +351,6 @@ if CONFIG_APOLLOLAKE
 		goto out;
 	}
 
-
 	/* must be enough BCLKs for data */
 	bdiv = config->ssp.bclk_rate / config->ssp.fsync_rate;
 	if (bdiv < config->ssp.tdm_slot_width * config->ssp.tdm_slots) {
@@ -457,24 +544,6 @@ if CONFIG_APOLLOLAKE
 	else
 		sscr0 |= SSCR0_DSIZE(data_size);
 
-#ifdef CONFIG_CANNONLAKE
-	mdivc = 0x1;
-#else
-	if (config->ssp.mclk_rate == 24576000) {
-		/* enable PLL, bypass M/N dividers */
-		mdivc = 0x00100001;
-	} else if (config->ssp.mclk_rate == 19200000) {
-		/* no PLL, use XTAl oscillator as source */
-		mdivc = 0;
-	} else {
-		trace_ssp_error("eci");
-		ret = -EINVAL;
-		goto out;
-	}
-#endif
-	/* bypass divider for MCLK */
-	mdivr = 0x00000fff;
-
 	/* setting TFT and RFT */
 	switch (config->ssp.sample_valid_bits) {
 	case 16:
@@ -522,7 +591,7 @@ if CONFIG_APOLLOLAKE
 
 	/* TODO: move this into M/N driver */
 	mn_reg_write(0x0, mdivc);
-	mn_reg_write(0x80, mdivr);
+	mn_reg_write(0x80 + config->ssp.mclk_id * 0x4, mdivr);
 	mn_reg_write(0x100 + config->id * 0x8 + 0x0, i2s_m);
 	mn_reg_write(0x100 + config->id * 0x8 + 0x4, i2s_n);
 
diff --git a/src/include/sof/ssp.h b/src/include/sof/ssp.h
index d34b011..5244296 100644
--- a/src/include/sof/ssp.h
+++ b/src/include/sof/ssp.h
@@ -215,6 +215,11 @@ extern const struct dai_ops ssp_ops;
 #define SSIOC_SCOE	BIT(5)
 #endif
 
+#if defined CONFIG_APOLLOLAKE || defined CONFIG_CANNONLAKE
+#define MNDSS(x)	((x) << 20)
+#define MCDSS(x)	((x) << 16)
+#endif
+
 /* tracing */
 #define trace_ssp(__e)	trace_event(TRACE_CLASS_SSP, __e)
 #define trace_ssp_error(__e)	trace_error(TRACE_CLASS_SSP, __e)
-- 
2.14.1



More information about the Sound-open-firmware mailing list