From a19b428a02224e0e01fff4738bbf0632942d18a1 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Fri, 15 Jan 2021 00:13:54 +0100
Subject: [PATCH 43/44] WIP: I2S multi channel

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 sound/soc/sunxi/sun4i-i2s.c | 137 +++++++++++++++++++++++++-----------
 1 file changed, 97 insertions(+), 40 deletions(-)

--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -23,7 +23,7 @@
 
 #define SUN4I_I2S_CTRL_REG		0x00
 #define SUN4I_I2S_CTRL_SDO_EN_MASK		GENMASK(11, 8)
-#define SUN4I_I2S_CTRL_SDO_EN(sdo)			BIT(8 + (sdo))
+#define SUN4I_I2S_CTRL_SDO_EN(lines)		(((1 << (lines)) - 1) << 8)
 #define SUN4I_I2S_CTRL_MODE_MASK		BIT(5)
 #define SUN4I_I2S_CTRL_MODE_SLAVE			(1 << 5)
 #define SUN4I_I2S_CTRL_MODE_MASTER			(0 << 5)
@@ -120,8 +120,8 @@
 #define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK	GENMASK(3, 0)
 #define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(chan)	((chan) - 1)
 
-#define SUN8I_I2S_TX_CHAN_MAP_REG	0x44
-#define SUN8I_I2S_TX_CHAN_SEL_REG	0x34
+#define SUN8I_I2S_TX_CHAN_MAP_REG(i)	(0x44 + 4 * (i))
+#define SUN8I_I2S_TX_CHAN_SEL_REG(i)	(0x34 + 4 * (i))
 #define SUN8I_I2S_TX_CHAN_OFFSET_MASK		GENMASK(13, 12)
 #define SUN8I_I2S_TX_CHAN_OFFSET(offset)	((offset) << 12)
 #define SUN8I_I2S_TX_CHAN_EN_MASK		GENMASK(11, 4)
@@ -198,7 +198,7 @@ struct sun4i_i2s_quirks {
 	 * @slots: channels per frame + padding slots, regardless of format
 	 * @slot_width: bits per sample + padding bits, regardless of format
 	 */
-	int	(*set_chan_cfg)(const struct sun4i_i2s *i2s,
+	int	(*set_chan_cfg)(struct sun4i_i2s *i2s,
 				unsigned int channels,	unsigned int slots,
 				unsigned int slot_width);
 	int	(*set_fmt)(const struct sun4i_i2s *i2s, unsigned int fmt);
@@ -214,6 +214,7 @@ struct sun4i_i2s {
 	unsigned int	mclk_freq;
 	unsigned int	slots;
 	unsigned int	slot_width;
+	unsigned int	lines;
 
 	struct snd_dmaengine_dai_dma_data	capture_dma_data;
 	struct snd_dmaengine_dai_dma_data	playback_dma_data;
@@ -454,7 +455,7 @@ static int sun8i_i2s_get_sr_wss(unsigned
 	return -EINVAL;
 }
 
-static int sun4i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
+static int sun4i_i2s_set_chan_cfg(struct sun4i_i2s *i2s,
 				  unsigned int channels, unsigned int slots,
 				  unsigned int slot_width)
 {
@@ -473,20 +474,42 @@ static int sun4i_i2s_set_chan_cfg(const
 	return 0;
 }
 
-static int sun8i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
+static int sun8i_i2s_set_chan_cfg(struct sun4i_i2s *i2s,
 				  unsigned int channels, unsigned int slots,
 				  unsigned int slot_width)
 {
-	unsigned int lrck_period;
+	unsigned int lrck_period, val;
+	int i;
+
+	i2s->lines = (channels + 1) / 2;
+
+	/* Enable the required output lines */
+	regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+			   SUN4I_I2S_CTRL_SDO_EN_MASK,
+			   SUN4I_I2S_CTRL_SDO_EN(i2s->lines));
 
 	/* Map the channels for playback and capture */
-	regmap_write(i2s->regmap, SUN8I_I2S_TX_CHAN_MAP_REG, 0x76543210);
+	regmap_write(i2s->regmap, SUN8I_I2S_TX_CHAN_MAP_REG(0), 0x76543210);
+	regmap_write(i2s->regmap, SUN8I_I2S_TX_CHAN_MAP_REG(1), 0x32);
+	regmap_write(i2s->regmap, SUN8I_I2S_TX_CHAN_MAP_REG(2), 0x54);
+	regmap_write(i2s->regmap, SUN8I_I2S_TX_CHAN_MAP_REG(3), 0x76);
 	regmap_write(i2s->regmap, SUN8I_I2S_RX_CHAN_MAP_REG, 0x76543210);
 
 	/* Configure the channels */
-	regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
-			   SUN4I_I2S_CHAN_SEL_MASK,
-			   SUN4I_I2S_CHAN_SEL(channels));
+	for (i = 0; i < 4; i++) {
+		if (channels <= i * 2)
+			val = 0;
+		else if (channels == i * 2 + 1)
+			val = 1;
+		else
+			val = 2;
+		regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG(i),
+				   SUN4I_I2S_CHAN_SEL_MASK |
+				   SUN8I_I2S_TX_CHAN_EN_MASK,
+				   SUN4I_I2S_CHAN_SEL(val) |
+				   SUN8I_I2S_TX_CHAN_EN(val));
+	}
+
 	regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG,
 			   SUN4I_I2S_CHAN_SEL_MASK,
 			   SUN4I_I2S_CHAN_SEL(channels));
@@ -518,36 +541,54 @@ static int sun8i_i2s_set_chan_cfg(const
 			   SUN8I_I2S_FMT0_LRCK_PERIOD_MASK,
 			   SUN8I_I2S_FMT0_LRCK_PERIOD(lrck_period));
 
-	regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
-			   SUN8I_I2S_TX_CHAN_EN_MASK,
-			   SUN8I_I2S_TX_CHAN_EN(channels));
-
 	return 0;
 }
 
-static int sun50i_h6_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
+static int sun50i_h6_i2s_set_chan_cfg(struct sun4i_i2s *i2s,
 				      unsigned int channels, unsigned int slots,
 				      unsigned int slot_width)
 {
-	unsigned int lrck_period;
+	unsigned int lrck_period, val;
+	int i;
+
+	i2s->lines = (channels + 1) / 2;
+
+	/* Enable the required output lines */
+	regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+			   SUN4I_I2S_CTRL_SDO_EN_MASK,
+			   SUN4I_I2S_CTRL_SDO_EN(i2s->lines));
 
 	/* Map the channels for playback and capture */
 	regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP0_REG(0), 0xFEDCBA98);
 	regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP1_REG(0), 0x76543210);
+	regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP1_REG(1), 0x32);
+	regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP1_REG(2), 0x54);
+	regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP1_REG(3), 0x76);
 	if (i2s->variant->num_din_pins > 1) {
 		regmap_write(i2s->regmap, SUN50I_R329_I2S_RX_CHAN_MAP0_REG, 0x0F0E0D0C);
 		regmap_write(i2s->regmap, SUN50I_R329_I2S_RX_CHAN_MAP1_REG, 0x0B0A0908);
 		regmap_write(i2s->regmap, SUN50I_R329_I2S_RX_CHAN_MAP2_REG, 0x07060504);
 		regmap_write(i2s->regmap, SUN50I_R329_I2S_RX_CHAN_MAP3_REG, 0x03020100);
 	} else {
 		regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0xFEDCBA98);
 		regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x76543210);
 	}
 
 	/* Configure the channels */
-	regmap_update_bits(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_SEL_REG(0),
-			   SUN50I_H6_I2S_TX_CHAN_SEL_MASK,
-			   SUN50I_H6_I2S_TX_CHAN_SEL(channels));
+	for (i = 0; i < 4; i++) {
+		if (channels <= i * 2)
+			val = 0;
+		else if (channels == i * 2 + 1)
+			val = 1;
+		else
+			val = 2;
+		regmap_update_bits(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_SEL_REG(i),
+				   SUN50I_H6_I2S_TX_CHAN_SEL_MASK |
+				   SUN50I_H6_I2S_TX_CHAN_EN_MASK,
+				   SUN50I_H6_I2S_TX_CHAN_SEL(val) |
+				   SUN50I_H6_I2S_TX_CHAN_EN(val));
+	}
+
 	regmap_update_bits(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_SEL_REG,
 			   SUN50I_H6_I2S_TX_CHAN_SEL_MASK,
 			   SUN50I_H6_I2S_TX_CHAN_SEL(channels));
@@ -579,10 +620,6 @@ static int sun50i_h6_i2s_set_chan_cfg(co
 			   SUN8I_I2S_FMT0_LRCK_PERIOD_MASK,
 			   SUN8I_I2S_FMT0_LRCK_PERIOD(lrck_period));
 
-	regmap_update_bits(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_SEL_REG(0),
-			   SUN50I_H6_I2S_TX_CHAN_EN_MASK,
-			   SUN50I_H6_I2S_TX_CHAN_EN(channels));
-
 	return 0;
 }
 
@@ -727,6 +764,7 @@ static int sun8i_i2s_set_soc_fmt(const s
 {
 	u32 mode, val;
 	u8 offset;
+	int i;
 
 	/*
 	 * DAI clock polarity
@@ -794,9 +832,10 @@ static int sun8i_i2s_set_soc_fmt(const s
 
 	regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
 			   SUN8I_I2S_CTRL_MODE_MASK, mode);
-	regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
-			   SUN8I_I2S_TX_CHAN_OFFSET_MASK,
-			   SUN8I_I2S_TX_CHAN_OFFSET(offset));
+	for (i = 0; i < 4; i++)
+		regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG(i),
+				   SUN8I_I2S_TX_CHAN_OFFSET_MASK,
+				   SUN8I_I2S_TX_CHAN_OFFSET(offset));
 	regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG,
 			   SUN8I_I2S_TX_CHAN_OFFSET_MASK,
 			   SUN8I_I2S_TX_CHAN_OFFSET(offset));
@@ -834,6 +873,7 @@ static int sun50i_h6_i2s_set_soc_fmt(con
 {
 	u32 mode, val;
 	u8 offset;
+	int i;
 
 	/*
 	 * DAI clock polarity
@@ -901,9 +941,10 @@ static int sun50i_h6_i2s_set_soc_fmt(con
 
 	regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
 			   SUN8I_I2S_CTRL_MODE_MASK, mode);
-	regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
-			   SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK,
-			   SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(offset));
+	for (i = 0; i < 4; i++)
+		regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG(i),
+				   SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK,
+				   SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(offset));
 	regmap_update_bits(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_SEL_REG,
 			   SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK,
 			   SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(offset));
@@ -1212,8 +1253,14 @@ static const struct reg_default sun8i_i2
 	{ SUN4I_I2S_DMA_INT_CTRL_REG, 0x00000000 },
 	{ SUN4I_I2S_CLK_DIV_REG, 0x00000000 },
 	{ SUN8I_I2S_CHAN_CFG_REG, 0x00000000 },
-	{ SUN8I_I2S_TX_CHAN_SEL_REG, 0x00000000 },
-	{ SUN8I_I2S_TX_CHAN_MAP_REG, 0x00000000 },
+	{ SUN8I_I2S_TX_CHAN_SEL_REG(0), 0x00000000 },
+	{ SUN8I_I2S_TX_CHAN_SEL_REG(1), 0x00000000 },
+	{ SUN8I_I2S_TX_CHAN_SEL_REG(2), 0x00000000 },
+	{ SUN8I_I2S_TX_CHAN_SEL_REG(3), 0x00000000 },
+	{ SUN8I_I2S_TX_CHAN_MAP_REG(0), 0x00000000 },
+	{ SUN8I_I2S_TX_CHAN_MAP_REG(1), 0x00000000 },
+	{ SUN8I_I2S_TX_CHAN_MAP_REG(2), 0x00000000 },
+	{ SUN8I_I2S_TX_CHAN_MAP_REG(3), 0x00000000 },
 	{ SUN8I_I2S_RX_CHAN_SEL_REG, 0x00000000 },
 	{ SUN8I_I2S_RX_CHAN_MAP_REG, 0x00000000 },
 };
@@ -1227,8 +1274,17 @@ static const struct reg_default sun50i_h
 	{ SUN4I_I2S_CLK_DIV_REG, 0x00000000 },
 	{ SUN8I_I2S_CHAN_CFG_REG, 0x00000000 },
 	{ SUN50I_H6_I2S_TX_CHAN_SEL_REG(0), 0x00000000 },
+	{ SUN50I_H6_I2S_TX_CHAN_SEL_REG(1), 0x00000000 },
+	{ SUN50I_H6_I2S_TX_CHAN_SEL_REG(2), 0x00000000 },
+	{ SUN50I_H6_I2S_TX_CHAN_SEL_REG(3), 0x00000000 },
 	{ SUN50I_H6_I2S_TX_CHAN_MAP0_REG(0), 0x00000000 },
 	{ SUN50I_H6_I2S_TX_CHAN_MAP1_REG(0), 0x00000000 },
+	{ SUN50I_H6_I2S_TX_CHAN_MAP0_REG(1), 0x00000000 },
+	{ SUN50I_H6_I2S_TX_CHAN_MAP1_REG(1), 0x00000000 },
+	{ SUN50I_H6_I2S_TX_CHAN_MAP0_REG(2), 0x00000000 },
+	{ SUN50I_H6_I2S_TX_CHAN_MAP1_REG(2), 0x00000000 },
+	{ SUN50I_H6_I2S_TX_CHAN_MAP0_REG(3), 0x00000000 },
+	{ SUN50I_H6_I2S_TX_CHAN_MAP1_REG(3), 0x00000000 },
 	{ SUN50I_H6_I2S_RX_CHAN_SEL_REG, 0x00000000 },
 	{ SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0x00000000 },
 	{ SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x00000000 },
@@ -1301,7 +1357,7 @@ static int sun4i_i2s_runtime_resume(stru
 	/* Enable the first output line */
 	regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
 			   SUN4I_I2S_CTRL_SDO_EN_MASK,
-			   SUN4I_I2S_CTRL_SDO_EN(0));
+			   SUN4I_I2S_CTRL_SDO_EN(i2s->lines));
 
 	ret = clk_prepare_enable(i2s->mod_clk);
 	if (ret) {
@@ -1562,6 +1618,7 @@ static int sun4i_i2s_probe(struct platfo
 
 	i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG;
 	i2s->capture_dma_data.maxburst = 8;
+	i2s->lines = 1;
 
 	pm_runtime_enable(&pdev->dev);
 	if (!pm_runtime_enabled(&pdev->dev)) {
