diff mbox

Applied "ASoC: stm32: sai: add h7 support" to the asoc tree

Message ID E1dLvNR-0005Z4-6j@debutante (mailing list archive)
State New, archived
Headers show

Commit Message

Mark Brown June 16, 2017, 5:49 p.m. UTC
The patch

   ASoC: stm32: sai: add h7 support

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

From fe09d505b0827354c22c2efc0cf41167a8a016cc Mon Sep 17 00:00:00 2001
From: olivier moysan <olivier.moysan@st.com>
Date: Fri, 16 Jun 2017 14:16:24 +0200
Subject: [PATCH] ASoC: stm32: sai: add h7 support

Add support of SAI on STM32H7 family.

Signed-off-by: olivier moysan <olivier.moysan@st.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 sound/soc/stm/stm32_sai.c     | 13 +++++-
 sound/soc/stm/stm32_sai.h     | 72 ++++++++++++++++++++++++++++++---
 sound/soc/stm/stm32_sai_sub.c | 92 ++++++++++++++++++++++++++++++++++++-------
 3 files changed, 155 insertions(+), 22 deletions(-)
diff mbox

Patch

diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c
index 6159d66c2c54..f7713314913b 100644
--- a/sound/soc/stm/stm32_sai.c
+++ b/sound/soc/stm/stm32_sai.c
@@ -27,8 +27,17 @@ 
 
 #include "stm32_sai.h"
 
+static const struct stm32_sai_conf stm32_sai_conf_f4 = {
+	.version = SAI_STM32F4,
+};
+
+static const struct stm32_sai_conf stm32_sai_conf_h7 = {
+	.version = SAI_STM32H7,
+};
+
 static const struct of_device_id stm32_sai_ids[] = {
-	{ .compatible = "st,stm32f4-sai", .data = (void *)SAI_STM32F4 },
+	{ .compatible = "st,stm32f4-sai", .data = (void *)&stm32_sai_conf_f4 },
+	{ .compatible = "st,stm32h7-sai", .data = (void *)&stm32_sai_conf_h7 },
 	{}
 };
 
@@ -52,7 +61,7 @@  static int stm32_sai_probe(struct platform_device *pdev)
 
 	of_id = of_match_device(stm32_sai_ids, &pdev->dev);
 	if (of_id)
-		sai->version = (enum stm32_sai_version)of_id->data;
+		sai->conf = (struct stm32_sai_conf *)of_id->data;
 	else
 		return -EINVAL;
 
diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h
index 270be93b845e..889974dc62d9 100644
--- a/sound/soc/stm/stm32_sai.h
+++ b/sound/soc/stm/stm32_sai.h
@@ -31,6 +31,10 @@ 
 #define STM_SAI_CLRFR_REGX	0x18
 #define STM_SAI_DR_REGX		0x1C
 
+/* Sub-block A registers, relative to sub-block A address */
+#define STM_SAI_PDMCR_REGX	0x40
+#define STM_SAI_PDMLY_REGX	0x44
+
 /******************** Bit definition for SAI_GCR register *******************/
 #define SAI_GCR_SYNCIN_SHIFT	0
 #define SAI_GCR_SYNCIN_MASK	GENMASK(1, SAI_GCR_SYNCIN_SHIFT)
@@ -75,10 +79,11 @@ 
 #define SAI_XCR1_NODIV		BIT(SAI_XCR1_NODIV_SHIFT)
 
 #define SAI_XCR1_MCKDIV_SHIFT	20
-#define SAI_XCR1_MCKDIV_WIDTH	4
-#define SAI_XCR1_MCKDIV_MASK	GENMASK(24, SAI_XCR1_MCKDIV_SHIFT)
+#define SAI_XCR1_MCKDIV_WIDTH(x)	(((x) == SAI_STM32F4) ? 4 : 6)
+#define SAI_XCR1_MCKDIV_MASK(x) GENMASK((SAI_XCR1_MCKDIV_SHIFT + (x) - 1),\
+				SAI_XCR1_MCKDIV_SHIFT)
 #define SAI_XCR1_MCKDIV_SET(x)	((x) << SAI_XCR1_MCKDIV_SHIFT)
-#define SAI_XCR1_MCKDIV_MAX	((1 << SAI_XCR1_MCKDIV_WIDTH) - 1)
+#define SAI_XCR1_MCKDIV_MAX(x)	((1 << SAI_XCR1_MCKDIV_WIDTH(x)) - 1)
 
 #define SAI_XCR1_OSR_SHIFT	26
 #define SAI_XCR1_OSR		BIT(SAI_XCR1_OSR_SHIFT)
@@ -178,8 +183,65 @@ 
 #define SAI_XCLRFR_SHIFT	0
 #define SAI_XCLRFR_MASK		GENMASK(6, SAI_XCLRFR_SHIFT)
 
+/****************** Bit definition for SAI_PDMCR register ******************/
+#define SAI_PDMCR_PDMEN		BIT(0)
+
+#define SAI_PDMCR_MICNBR_SHIFT	4
+#define SAI_PDMCR_MICNBR_MASK	GENMASK(5, SAI_PDMCR_MICNBR_SHIFT)
+#define SAI_PDMCR_MICNBR_SET(x)	((x) << SAI_PDMCR_MICNBR_SHIFT)
+
+#define SAI_PDMCR_CKEN1		BIT(8)
+#define SAI_PDMCR_CKEN2		BIT(9)
+#define SAI_PDMCR_CKEN3		BIT(10)
+#define SAI_PDMCR_CKEN4		BIT(11)
+
+/****************** Bit definition for (SAI_PDMDLY register ****************/
+#define SAI_PDMDLY_1L_SHIFT	0
+#define SAI_PDMDLY_1L_MASK	GENMASK(2, SAI_PDMDLY_1L_SHIFT)
+#define SAI_PDMDLY_1L_WIDTH	3
+
+#define SAI_PDMDLY_1R_SHIFT	4
+#define SAI_PDMDLY_1R_MASK	GENMASK(6, SAI_PDMDLY_1R_SHIFT)
+#define SAI_PDMDLY_1R_WIDTH	3
+
+#define SAI_PDMDLY_2L_SHIFT	8
+#define SAI_PDMDLY_2L_MASK	GENMASK(10, SAI_PDMDLY_2L_SHIFT)
+#define SAI_PDMDLY_2L_WIDTH	3
+
+#define SAI_PDMDLY_2R_SHIFT	12
+#define SAI_PDMDLY_2R_MASK	GENMASK(14, SAI_PDMDLY_2R_SHIFT)
+#define SAI_PDMDLY_2R_WIDTH	3
+
+#define SAI_PDMDLY_3L_SHIFT	16
+#define SAI_PDMDLY_3L_MASK	GENMASK(18, SAI_PDMDLY_3L_SHIFT)
+#define SAI_PDMDLY_3L_WIDTH	3
+
+#define SAI_PDMDLY_3R_SHIFT	20
+#define SAI_PDMDLY_3R_MASK	GENMASK(22, SAI_PDMDLY_3R_SHIFT)
+#define SAI_PDMDLY_3R_WIDTH	3
+
+#define SAI_PDMDLY_4L_SHIFT	24
+#define SAI_PDMDLY_4L_MASK	GENMASK(26, SAI_PDMDLY_4L_SHIFT)
+#define SAI_PDMDLY_4L_WIDTH	3
+
+#define SAI_PDMDLY_4R_SHIFT	28
+#define SAI_PDMDLY_4R_MASK	GENMASK(30, SAI_PDMDLY_4R_SHIFT)
+#define SAI_PDMDLY_4R_WIDTH	3
+
+#define STM_SAI_IS_F4(ip)	((ip)->conf->version == SAI_STM32F4)
+#define STM_SAI_IS_H7(ip)	((ip)->conf->version == SAI_STM32H7)
+
 enum stm32_sai_version {
-	SAI_STM32F4
+	SAI_STM32F4,
+	SAI_STM32H7
+};
+
+/**
+ * struct stm32_sai_conf - SAI configuration
+ * @version: SAI version
+ */
+struct stm32_sai_conf {
+	int version;
 };
 
 /**
@@ -194,6 +256,6 @@  struct stm32_sai_data {
 	struct platform_device *pdev;
 	struct clk *clk_x8k;
 	struct clk *clk_x11k;
-	int version;
+	struct stm32_sai_conf *conf;
 	int irq;
 };
diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c
index ce48c02db051..ba3fdc777ed8 100644
--- a/sound/soc/stm/stm32_sai_sub.c
+++ b/sound/soc/stm/stm32_sai_sub.c
@@ -51,12 +51,15 @@ 
 #define STM_SAI_A_ID		0x0
 #define STM_SAI_B_ID		0x1
 
+#define STM_SAI_IS_SUB_A(x)	((x)->id == STM_SAI_A_ID)
+#define STM_SAI_IS_SUB_B(x)	((x)->id == STM_SAI_B_ID)
 #define STM_SAI_BLOCK_NAME(x)	(((x)->id == STM_SAI_A_ID) ? "A" : "B")
 
 /**
  * struct stm32_sai_sub_data - private data of SAI sub block (block A or B)
  * @pdev: device data pointer
  * @regmap: SAI register map pointer
+ * @regmap_config: SAI sub block register map configuration pointer
  * @dma_params: dma configuration data for rx or tx channel
  * @cpu_dai_drv: DAI driver data pointer
  * @cpu_dai: DAI runtime data pointer
@@ -79,6 +82,7 @@ 
 struct stm32_sai_sub_data {
 	struct platform_device *pdev;
 	struct regmap *regmap;
+	const struct regmap_config *regmap_config;
 	struct snd_dmaengine_dai_dma_data dma_params;
 	struct snd_soc_dai_driver *cpu_dai_drv;
 	struct snd_soc_dai *cpu_dai;
@@ -118,6 +122,8 @@  static bool stm32_sai_sub_readable_reg(struct device *dev, unsigned int reg)
 	case STM_SAI_SR_REGX:
 	case STM_SAI_CLRFR_REGX:
 	case STM_SAI_DR_REGX:
+	case STM_SAI_PDMCR_REGX:
+	case STM_SAI_PDMLY_REGX:
 		return true;
 	default:
 		return false;
@@ -145,13 +151,15 @@  static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg)
 	case STM_SAI_SR_REGX:
 	case STM_SAI_CLRFR_REGX:
 	case STM_SAI_DR_REGX:
+	case STM_SAI_PDMCR_REGX:
+	case STM_SAI_PDMLY_REGX:
 		return true;
 	default:
 		return false;
 	}
 }
 
-static const struct regmap_config stm32_sai_sub_regmap_config = {
+static const struct regmap_config stm32_sai_sub_regmap_config_f4 = {
 	.reg_bits = 32,
 	.reg_stride = 4,
 	.val_bits = 32,
@@ -162,6 +170,17 @@  static const struct regmap_config stm32_sai_sub_regmap_config = {
 	.fast_io = true,
 };
 
+static const struct regmap_config stm32_sai_sub_regmap_config_h7 = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = STM_SAI_PDMLY_REGX,
+	.readable_reg = stm32_sai_sub_readable_reg,
+	.volatile_reg = stm32_sai_sub_volatile_reg,
+	.writeable_reg = stm32_sai_sub_writeable_reg,
+	.fast_io = true,
+};
+
 static irqreturn_t stm32_sai_isr(int irq, void *devid)
 {
 	struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid;
@@ -551,7 +570,8 @@  static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
 {
 	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
 	int cr1, mask, div = 0;
-	int sai_clk_rate, ret;
+	int sai_clk_rate, mclk_ratio, den, ret;
+	int version = sai->pdata->conf->version;
 
 	if (!sai->mclk_rate) {
 		dev_err(cpu_dai->dev, "Mclk rate is null\n");
@@ -564,22 +584,54 @@  static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
 		clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k);
 	sai_clk_rate = clk_get_rate(sai->sai_ck);
 
-	/*
-	 * mclk_rate = 256 * fs
-	 * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate
-	 * MCKDIV = sai_ck / (2 * mclk_rate) otherwise
-	 */
-	if (2 * sai_clk_rate >= 3 * sai->mclk_rate)
-		div = DIV_ROUND_CLOSEST(sai_clk_rate, 2 * sai->mclk_rate);
-
-	if (div > SAI_XCR1_MCKDIV_MAX) {
+	if (STM_SAI_IS_F4(sai->pdata)) {
+		/*
+		 * mclk_rate = 256 * fs
+		 * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate
+		 * MCKDIV = sai_ck / (2 * mclk_rate) otherwise
+		 */
+		if (2 * sai_clk_rate >= 3 * sai->mclk_rate)
+			div = DIV_ROUND_CLOSEST(sai_clk_rate,
+						2 * sai->mclk_rate);
+	} else {
+		/*
+		 * TDM mode :
+		 *   mclk on
+		 *      MCKDIV = sai_ck / (ws x 256)	(NOMCK=0. OSR=0)
+		 *      MCKDIV = sai_ck / (ws x 512)	(NOMCK=0. OSR=1)
+		 *   mclk off
+		 *      MCKDIV = sai_ck / (frl x ws)	(NOMCK=1)
+		 * Note: NOMCK/NODIV correspond to same bit.
+		 */
+		if (sai->mclk_rate) {
+			mclk_ratio = sai->mclk_rate / params_rate(params);
+			if (mclk_ratio != 256) {
+				if (mclk_ratio == 512) {
+					mask = SAI_XCR1_OSR;
+					cr1 = SAI_XCR1_OSR;
+				} else {
+					dev_err(cpu_dai->dev,
+						"Wrong mclk ratio %d\n",
+						mclk_ratio);
+					return -EINVAL;
+				}
+			}
+			div = DIV_ROUND_CLOSEST(sai_clk_rate, sai->mclk_rate);
+		} else {
+			/* mclk-fs not set, master clock not active. NOMCK=1 */
+			den = sai->fs_length * params_rate(params);
+			div = DIV_ROUND_CLOSEST(sai_clk_rate, den);
+		}
+	}
+
+	if (div > SAI_XCR1_MCKDIV_MAX(version)) {
 		dev_err(cpu_dai->dev, "Divider %d out of range\n", div);
 		return -EINVAL;
 	}
 	dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div);
 
-	mask = SAI_XCR1_MCKDIV_MASK;
-	cr1 = SAI_XCR1_MCKDIV_SET(div);
+	mask = SAI_XCR1_MCKDIV_MASK(SAI_XCR1_MCKDIV_WIDTH(version));
+				    cr1 = SAI_XCR1_MCKDIV_SET(div);
 	ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1);
 	if (ret < 0) {
 		dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
@@ -780,8 +832,18 @@  static int stm32_sai_sub_parse_of(struct platform_device *pdev,
 		return PTR_ERR(base);
 
 	sai->phys_addr = res->start;
-	sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "sai_ck", base,
-						&stm32_sai_sub_regmap_config);
+
+	sai->regmap_config = &stm32_sai_sub_regmap_config_f4;
+	/* Note: PDM registers not available for H7 sub-block B */
+	if (STM_SAI_IS_H7(sai->pdata) && STM_SAI_IS_SUB_A(sai))
+		sai->regmap_config = &stm32_sai_sub_regmap_config_h7;
+
+	sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "sai_ck",
+						base, sai->regmap_config);
+	if (IS_ERR(sai->regmap)) {
+		dev_err(&pdev->dev, "Failed to initialize MMIO\n");
+		return PTR_ERR(sai->regmap);
+	}
 
 	/* Get direction property */
 	if (of_property_match_string(np, "dma-names", "tx") >= 0) {