build/patch/kernel/archive/sunxi-5.10/check/0004-sun4i-i2s-improvements.patch.disabled

752 lines
25 KiB
Plaintext

From b5cb1681a065bd03b0eb84ca243bc50ab0fa54c1 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Tue, 17 Sep 2019 17:55:07 +0200
Subject: [PATCH] WIP: I2S improvements (to be mainlined)
Work done by Marcus Cooper (codekipper)
---
sound/soc/sunxi/sun4i-i2s.c | 454 ++++++++++++++++++++++++++++++++----
1 file changed, 412 insertions(+), 42 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index d0a8d5810c0a..9a715a6bdbf9 100644
--- 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)
@@ -48,6 +48,9 @@
#define SUN4I_I2S_FMT0_FMT_I2S (0 << 0)
#define SUN4I_I2S_FMT1_REG 0x08
+#define SUN4I_I2S_FMT1_REG_SEXT_MASK BIT(8)
+#define SUN4I_I2S_FMT1_REG_SEXT(sext) ((sext) << 8)
+
#define SUN4I_I2S_FIFO_TX_REG 0x0c
#define SUN4I_I2S_FIFO_RX_REG 0x10
@@ -100,22 +100,25 @@
#define SUN8I_I2S_CTRL_MODE_PCM (0 << 4)
#define SUN8I_I2S_FMT0_LRCLK_POLARITY_MASK BIT(19)
-#define SUN8I_I2S_FMT0_LRCLK_POLARITY_INVERTED (1 << 19)
-#define SUN8I_I2S_FMT0_LRCLK_POLARITY_NORMAL (0 << 19)
+#define SUN8I_I2S_FMT0_LRCLK_POLARITY_INVERTED (1 << 19)
+#define SUN8I_I2S_FMT0_LRCLK_POLARITY_NORMAL (0 << 19)
#define SUN8I_I2S_FMT0_LRCK_PERIOD_MASK GENMASK(17, 8)
#define SUN8I_I2S_FMT0_LRCK_PERIOD(period) ((period - 1) << 8)
#define SUN8I_I2S_FMT0_BCLK_POLARITY_MASK BIT(7)
-#define SUN8I_I2S_FMT0_BCLK_POLARITY_INVERTED (1 << 7)
-#define SUN8I_I2S_FMT0_BCLK_POLARITY_NORMAL (0 << 7)
+#define SUN8I_I2S_FMT0_BCLK_POLARITY_INVERTED (1 << 7)
+#define SUN8I_I2S_FMT0_BCLK_POLARITY_NORMAL (0 << 7)
+
+#define SUN8I_I2S_FMT1_REG_SEXT_MASK GENMASK(5, 4)
+#define SUN8I_I2S_FMT1_REG_SEXT(sext) ((sext) << 4)
#define SUN8I_I2S_INT_STA_REG 0x0c
#define SUN8I_I2S_FIFO_TX_REG 0x20
#define SUN8I_I2S_CHAN_CFG_REG 0x30
#define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK GENMASK(6, 4)
-#define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(chan) ((chan - 1) << 4)
+#define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(chan) (((chan) - 1) << 4)
#define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK GENMASK(2, 0)
-#define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(chan) (chan - 1)
+#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
@@ -123,10 +126,27 @@
#define SUN8I_I2S_TX_CHAN_OFFSET(offset) ((offset) << 12)
#define SUN8I_I2S_TX_CHAN_EN_MASK GENMASK(11, 4)
#define SUN8I_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1) << 4)
+#define SUN8I_I2S_TX_CHAN_SEL_MASK GENMASK(2, 0)
+#define SUN8I_I2S_TX_CHAN_SEL(chan) ((chan) - 1)
#define SUN8I_I2S_RX_CHAN_SEL_REG 0x54
#define SUN8I_I2S_RX_CHAN_MAP_REG 0x58
+/* Defines required for sun50i-h6 support */
+#define SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK GENMASK(21, 20)
+#define SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(offset) ((offset) << 20)
+#define SUN50I_H6_I2S_TX_CHAN_SEL_MASK GENMASK(19, 16)
+#define SUN50I_H6_I2S_TX_CHAN_SEL(chan) (((chan) - 1) << 16)
+#define SUN50I_H6_I2S_TX_CHAN_EN_MASK GENMASK(15, 0)
+#define SUN50I_H6_I2S_TX_CHAN_EN(num_chan) (((1 << (num_chan)) - 1))
+
+#define SUN50I_H6_I2S_TX_CHAN_MAP0_REG 0x44
+#define SUN50I_H6_I2S_TX_CHAN_MAP1_REG 0x48
+
+#define SUN50I_H6_I2S_RX_CHAN_SEL_REG 0x64
+#define SUN50I_H6_I2S_RX_CHAN_MAP0_REG 0x68
+#define SUN50I_H6_I2S_RX_CHAN_MAP1_REG 0x6C
+
struct sun4i_i2s;
/**
@@ -156,7 +179,16 @@ struct sun4i_i2s_quirks {
s8 (*get_wss)(const struct sun4i_i2s *, int);
int (*set_chan_cfg)(const struct sun4i_i2s *,
const struct snd_pcm_hw_params *);
- int (*set_fmt)(const struct sun4i_i2s *, unsigned int);
+ int (*set_fmt)(struct sun4i_i2s *, unsigned int);
+ void (*set_fmt_sext)(const struct sun4i_i2s *, unsigned int);
+ void (*set_txchanoffset)(const struct sun4i_i2s *, int);
+ void (*set_rxchanoffset)(const struct sun4i_i2s *);
+ void (*set_txchanen)(const struct sun4i_i2s *, int, int);
+ void (*set_rxchanen)(const struct sun4i_i2s *, int);
+ void (*set_txchansel)(const struct sun4i_i2s *, int, int);
+ void (*set_rxchansel)(const struct sun4i_i2s *, int);
+ void (*set_txchanmap)(const struct sun4i_i2s *, int, int);
+ void (*set_rxchanmap)(const struct sun4i_i2s *, int);
};
struct sun4i_i2s {
@@ -169,6 +201,7 @@ struct sun4i_i2s {
unsigned int mclk_freq;
unsigned int slots;
unsigned int slot_width;
+ unsigned int offset;
struct snd_dmaengine_dai_dma_data capture_dma_data;
struct snd_dmaengine_dai_dma_data playback_dma_data;
@@ -354,6 +387,9 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai,
regmap_field_write(i2s->field_clkdiv_mclk_en, 1);
+ /* Set sign extension to pad out LSB with 0 */
+ i2s->variant->set_fmt_sext(i2s, 0);
+
return 0;
}
@@ -396,8 +432,8 @@ static int sun4i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
unsigned int channels = params_channels(params);
/* Map the channels for playback and capture */
- regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_MAP_REG, 0x76543210);
- regmap_write(i2s->regmap, SUN4I_I2S_RX_CHAN_MAP_REG, 0x00003210);
+ i2s->variant->set_txchanmap(i2s, 0, 0x76543210);
+ i2s->variant->set_rxchanmap(i2s, 0x3210);
/* Configure the channels */
regmap_update_bits(i2s->regmap, SUN4I_I2S_TX_CHAN_SEL_REG,
@@ -421,16 +457,12 @@ static int sun8i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
slots = i2s->slots;
/* Map the channels for playback and capture */
- regmap_write(i2s->regmap, SUN8I_I2S_TX_CHAN_MAP_REG, 0x76543210);
- regmap_write(i2s->regmap, SUN8I_I2S_RX_CHAN_MAP_REG, 0x76543210);
+ i2s->variant->set_txchanmap(i2s, 0, 0x76543210);
+ i2s->variant->set_rxchanmap(i2s, 0x3210);
/* Configure the channels */
- regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
- SUN4I_I2S_CHAN_SEL_MASK,
- SUN4I_I2S_CHAN_SEL(channels));
- regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG,
- SUN4I_I2S_CHAN_SEL_MASK,
- SUN4I_I2S_CHAN_SEL(channels));
+ i2s->variant->set_txchansel(i2s, 0, channels);
+ i2s->variant->set_rxchansel(i2s, channels);
regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK,
@@ -448,7 +480,10 @@ static int sun8i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
break;
case SND_SOC_DAIFMT_I2S:
- lrck_period = params_physical_width(params);
+ if (i2s->slot_width)
+ lrck_period = i2s->slot_width;
+ else
+ lrck_period = params_physical_width(params);
break;
default:
@@ -466,6 +501,166 @@ static int sun8i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
return 0;
}
+static void sun8i_i2s_set_txchanoffset(const struct sun4i_i2s *i2s, int output)
+{
+ if (output >= 0 && output < 4)
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4),
+ SUN8I_I2S_TX_CHAN_OFFSET_MASK,
+ SUN8I_I2S_TX_CHAN_OFFSET(i2s->offset));
+}
+
+static void sun8i_i2s_set_rxchanoffset(const struct sun4i_i2s *i2s)
+{
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_RX_CHAN_SEL_REG,
+ SUN8I_I2S_TX_CHAN_OFFSET_MASK,
+ SUN8I_I2S_TX_CHAN_OFFSET(i2s->offset));
+}
+
+static void sun50i_h6_i2s_set_txchanoffset(const struct sun4i_i2s *i2s, int output)
+{
+ if (output >= 0 && output < 4)
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4),
+ SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK,
+ SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(i2s->offset));
+}
+
+static void sun50i_h6_i2s_set_rxchanoffset(const struct sun4i_i2s *i2s)
+{
+ 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(i2s->offset));
+}
+
+static void sun8i_i2s_set_txchanen(const struct sun4i_i2s *i2s, int output,
+ int channel)
+{
+ if (output >= 0 && output < 4)
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4),
+ SUN8I_I2S_TX_CHAN_EN_MASK,
+ SUN8I_I2S_TX_CHAN_EN(channel));
+}
+
+static void sun8i_i2s_set_rxchanen(const struct sun4i_i2s *i2s, int channel)
+{
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_RX_CHAN_SEL_REG,
+ SUN8I_I2S_TX_CHAN_EN_MASK,
+ SUN8I_I2S_TX_CHAN_EN(channel));
+}
+
+static void sun50i_h6_i2s_set_txchanen(const struct sun4i_i2s *i2s, int output,
+ int channel)
+{
+ if (output >= 0 && output < 4)
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4),
+ SUN50I_H6_I2S_TX_CHAN_EN_MASK,
+ SUN50I_H6_I2S_TX_CHAN_EN(channel));
+}
+
+static void sun50i_h6_i2s_set_rxchanen(const struct sun4i_i2s *i2s, int channel)
+{
+ regmap_update_bits(i2s->regmap,
+ SUN50I_H6_I2S_RX_CHAN_SEL_REG,
+ SUN50I_H6_I2S_TX_CHAN_EN_MASK,
+ SUN50I_H6_I2S_TX_CHAN_EN(channel));
+}
+
+static void sun4i_i2s_set_txchansel(const struct sun4i_i2s *i2s, int output,
+ int channel)
+{
+ /* Configure the channels */
+ regmap_write(i2s->regmap,
+ SUN4I_I2S_TX_CHAN_SEL_REG,
+ SUN4I_I2S_CHAN_SEL(channel));
+}
+
+static void sun8i_i2s_set_txchansel(const struct sun4i_i2s *i2s, int output,
+ int channel)
+{
+ if (output >= 0 && output < 4)
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4),
+ SUN8I_I2S_TX_CHAN_SEL_MASK,
+ SUN8I_I2S_TX_CHAN_SEL(channel));
+}
+
+static void sun4i_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int channel)
+{
+ /* Configure the channels */
+ regmap_write(i2s->regmap,
+ SUN4I_I2S_RX_CHAN_SEL_REG,
+ SUN4I_I2S_CHAN_SEL(channel));
+}
+
+static void sun8i_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int channel)
+{
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_RX_CHAN_SEL_REG,
+ SUN8I_I2S_TX_CHAN_SEL_MASK,
+ SUN8I_I2S_TX_CHAN_SEL(channel));
+}
+
+static void sun50i_h6_i2s_set_txchansel(const struct sun4i_i2s *i2s, int output,
+ int channel)
+{
+ if (output >= 0 && output < 4)
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4),
+ SUN50I_H6_I2S_TX_CHAN_SEL_MASK,
+ SUN50I_H6_I2S_TX_CHAN_SEL(channel));
+}
+
+static void sun50i_h6_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int channel)
+{
+ 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(channel));
+}
+
+static void sun4i_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int output,
+ int channel)
+{
+ regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_MAP_REG, channel);
+}
+
+static void sun8i_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int output,
+ int channel)
+{
+ if (output >= 0 && output < 4)
+ regmap_write(i2s->regmap,
+ SUN8I_I2S_TX_CHAN_MAP_REG + (output * 4), channel);
+}
+
+static void sun4i_i2s_set_rxchanmap(const struct sun4i_i2s *i2s, int channel)
+{
+ regmap_write(i2s->regmap, SUN4I_I2S_RX_CHAN_MAP_REG, channel);
+}
+
+static void sun8i_i2s_set_rxchanmap(const struct sun4i_i2s *i2s, int channel)
+{
+ regmap_write(i2s->regmap, SUN8I_I2S_RX_CHAN_MAP_REG, channel);
+}
+
+static void sun50i_h6_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int output,
+ int channel)
+{
+ if (output >= 0 && output < 4)
+ regmap_write(i2s->regmap,
+ SUN50I_H6_I2S_TX_CHAN_MAP1_REG + (output * 8), channel);
+}
+
+static void sun50i_h6_i2s_set_rxchanmap(const struct sun4i_i2s *i2s, int channel)
+{
+ regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP1_REG, channel);
+}
+
static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -477,6 +672,7 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
unsigned int slots = channels;
int ret, sr, wss;
u32 width;
+ int lines;
if (i2s->slots)
slots = i2s->slots;
@@ -490,10 +686,82 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
return ret;
}
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (channels > dai->driver->playback.channels_max ||
+ channels < dai->driver->playback.channels_min) {
+ dev_err(dai->dev, "Unsupported number of channels: %d\n",
+ channels);
+ return -EINVAL;
+ }
+
+ 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(lines));
+
+ i2s->variant->set_txchanmap(i2s, 0, 0x10);
+ i2s->variant->set_txchansel(i2s, 0, channels > 1 ? 2 : 1);
+
+ if (i2s->variant->set_txchanen)
+ i2s->variant->set_txchanen(i2s, 0, 2);
+
+ if (i2s->variant->set_txchanoffset) {
+ regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
+ SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK,
+ SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(channels));
+
+ if (channels > 2) {
+ i2s->variant->set_txchanmap(i2s, 1, 0x32);
+ i2s->variant->set_txchanoffset(i2s, 1);
+ i2s->variant->set_txchansel(i2s, 1,
+ channels > 3 ? 2 : 1);
+ i2s->variant->set_txchanen(i2s, 1, 2);
+ }
+ if (channels > 4) {
+ i2s->variant->set_txchanmap(i2s, 2, 0x54);
+ i2s->variant->set_txchanoffset(i2s, 2);
+ i2s->variant->set_txchansel(i2s, 2,
+ channels > 5 ? 2 : 1);
+ i2s->variant->set_txchanen(i2s, 2, 2);
+ }
+ if (channels > 6) {
+ i2s->variant->set_txchanmap(i2s, 3, 0x76);
+ i2s->variant->set_txchanoffset(i2s, 3);
+ i2s->variant->set_txchansel(i2s, 3,
+ channels > 6 ? 2 : 1);
+ i2s->variant->set_txchanen(i2s, 3, 2);
+ }
+ }
+ } else {
+ if (channels > dai->driver->capture.channels_max ||
+ channels < dai->driver->capture.channels_min) {
+ dev_err(dai->dev, "Unsupported number of channels: %d\n",
+ channels);
+ return -EINVAL;
+ }
+
+ /* Map the channels for capture */
+ i2s->variant->set_rxchanmap(i2s, 0x10);
+ i2s->variant->set_rxchansel(i2s, channels);
+
+ if (i2s->variant->set_rxchanen)
+ i2s->variant->set_rxchanen(i2s, channels);
+
+ if (i2s->variant->set_rxchanoffset)
+ regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
+ SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK,
+ SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(channels));
+ }
+
switch (params_physical_width(params)) {
case 16:
width = DMA_SLAVE_BUSWIDTH_2_BYTES;
break;
+ case 32:
+ width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ break;
default:
dev_err(dai->dev, "Unsupported physical sample width: %d\n",
params_physical_width(params));
@@ -516,7 +784,7 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
slots, slot_width);
}
-static int sun4i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
+static int sun4i_i2s_set_soc_fmt(struct sun4i_i2s *i2s,
unsigned int fmt)
{
u32 val;
@@ -589,11 +857,10 @@ static int sun4i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
return 0;
}
-static int sun8i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
+static int sun8i_i2s_set_soc_fmt(struct sun4i_i2s *i2s,
unsigned int fmt)
{
u32 mode, val;
- u8 offset;
/*
* DAI clock polarity
@@ -632,27 +899,27 @@ static int sun8i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
mode = SUN8I_I2S_CTRL_MODE_PCM;
- offset = 1;
+ i2s->offset = 1;
break;
case SND_SOC_DAIFMT_DSP_B:
mode = SUN8I_I2S_CTRL_MODE_PCM;
- offset = 0;
+ i2s->offset = 0;
break;
case SND_SOC_DAIFMT_I2S:
mode = SUN8I_I2S_CTRL_MODE_LEFT;
- offset = 1;
+ i2s->offset = 1;
break;
case SND_SOC_DAIFMT_LEFT_J:
mode = SUN8I_I2S_CTRL_MODE_LEFT;
- offset = 0;
+ i2s->offset = 0;
break;
case SND_SOC_DAIFMT_RIGHT_J:
mode = SUN8I_I2S_CTRL_MODE_RIGHT;
- offset = 0;
+ i2s->offset = 0;
break;
default:
@@ -913,12 +933,8 @@ static int sun8i_i2s_set_soc_fmt(struct sun4i_i2s *i2s,
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));
- regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG,
- SUN8I_I2S_TX_CHAN_OFFSET_MASK,
- SUN8I_I2S_TX_CHAN_OFFSET(offset));
+ i2s->variant->set_txchanoffset(i2s, 0);
+ i2s->variant->set_rxchanoffset(i2s);
/* DAI clock master masks */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -691,6 +954,22 @@ static int sun8i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
return 0;
}
+static void sun4i_i2s_set_fmt_sext(const struct sun4i_i2s *i2s,
+ unsigned int sext)
+{
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT1_REG,
+ SUN4I_I2S_FMT1_REG_SEXT_MASK,
+ SUN4I_I2S_FMT1_REG_SEXT(sext));
+}
+
+static void sun8i_i2s_set_fmt_sext(const struct sun4i_i2s *i2s,
+ unsigned int sext)
+{
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT1_REG,
+ SUN8I_I2S_FMT1_REG_SEXT_MASK,
+ SUN8I_I2S_FMT1_REG_SEXT(sext));
+}
+
static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
@@ -717,9 +996,9 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static void sun4i_i2s_start_capture(struct sun4i_i2s *i2s)
{
/* Flush RX FIFO */
- regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
- SUN4I_I2S_FIFO_CTRL_FLUSH_RX,
- SUN4I_I2S_FIFO_CTRL_FLUSH_RX);
+ regmap_write_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
+ SUN4I_I2S_FIFO_CTRL_FLUSH_RX,
+ SUN4I_I2S_FIFO_CTRL_FLUSH_RX);
/* Clear RX counter */
regmap_write(i2s->regmap, SUN4I_I2S_RX_CNT_REG, 0);
@@ -738,9 +1017,9 @@ static void sun4i_i2s_start_capture(struct sun4i_i2s *i2s)
static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s)
{
/* Flush TX FIFO */
- regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
- SUN4I_I2S_FIFO_CTRL_FLUSH_TX,
- SUN4I_I2S_FIFO_CTRL_FLUSH_TX);
+ regmap_write_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
+ SUN4I_I2S_FIFO_CTRL_FLUSH_TX,
+ SUN4I_I2S_FIFO_CTRL_FLUSH_TX);
/* Clear TX counter */
regmap_write(i2s->regmap, SUN4I_I2S_TX_CNT_REG, 0);
@@ -862,6 +1141,10 @@ static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+#define SUN4I_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
static struct snd_soc_dai_driver sun4i_i2s_dai = {
.probe = sun4i_i2s_dai_probe,
.capture = {
@@ -869,14 +1152,14 @@ static struct snd_soc_dai_driver sun4i_i2s_dai = {
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SUN4I_FORMATS,
},
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SUN4I_FORMATS,
},
.ops = &sun4i_i2s_dai_ops,
.symmetric_rates = 1,
@@ -971,6 +1254,22 @@ static const struct reg_default sun8i_i2s_reg_defaults[] = {
{ SUN8I_I2S_RX_CHAN_MAP_REG, 0x00000000 },
};
+static const struct reg_default sun50i_i2s_reg_defaults[] = {
+ { SUN4I_I2S_CTRL_REG, 0x00060000 },
+ { SUN4I_I2S_FMT0_REG, 0x00000033 },
+ { SUN4I_I2S_FMT1_REG, 0x00000030 },
+ { SUN4I_I2S_FIFO_CTRL_REG, 0x000400f0 },
+ { 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 },
+ { SUN50I_H6_I2S_TX_CHAN_MAP0_REG, 0x00000000 },
+ { SUN50I_H6_I2S_TX_CHAN_MAP1_REG, 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 },
+};
+
static const struct regmap_config sun4i_i2s_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
@@ -998,6 +1297,19 @@ static const struct regmap_config sun8i_i2s_regmap_config = {
.volatile_reg = sun8i_i2s_volatile_reg,
};
+static const struct regmap_config sun50i_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SUN50I_H6_I2S_RX_CHAN_MAP1_REG,
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = sun50i_i2s_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(sun50i_i2s_reg_defaults),
+ .writeable_reg = sun4i_i2s_wr_reg,
+ .readable_reg = sun8i_i2s_rd_reg,
+ .volatile_reg = sun8i_i2s_volatile_reg,
+};
+
static int sun4i_i2s_runtime_resume(struct device *dev)
{
struct sun4i_i2s *i2s = dev_get_drvdata(dev);
@@ -1077,6 +1389,11 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
.get_wss = sun4i_i2s_get_wss,
.set_chan_cfg = sun4i_i2s_set_chan_cfg,
.set_fmt = sun4i_i2s_set_soc_fmt,
+ .set_fmt_sext = sun4i_i2s_set_fmt_sext,
+ .set_txchansel = sun4i_i2s_set_txchansel,
+ .set_rxchansel = sun4i_i2s_set_rxchansel,
+ .set_txchanmap = sun4i_i2s_set_txchanmap,
+ .set_rxchanmap = sun4i_i2s_set_rxchanmap,
};
static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
@@ -1095,6 +1412,11 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
.get_wss = sun4i_i2s_get_wss,
.set_chan_cfg = sun4i_i2s_set_chan_cfg,
.set_fmt = sun4i_i2s_set_soc_fmt,
+ .set_fmt_sext = sun4i_i2s_set_fmt_sext,
+ .set_txchansel = sun4i_i2s_set_txchansel,
+ .set_rxchansel = sun4i_i2s_set_rxchansel,
+ .set_txchanmap = sun4i_i2s_set_txchanmap,
+ .set_rxchanmap = sun4i_i2s_set_rxchanmap,
};
/*
@@ -1118,6 +1440,9 @@ static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
.get_wss = sun4i_i2s_get_wss,
.set_chan_cfg = sun4i_i2s_set_chan_cfg,
.set_fmt = sun4i_i2s_set_soc_fmt,
+ .set_fmt_sext = sun4i_i2s_set_fmt_sext,
+ .set_txchansel = sun4i_i2s_set_txchansel,
+ .set_rxchansel = sun4i_i2s_set_rxchansel,
};
static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
@@ -1136,6 +1461,15 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
.get_wss = sun8i_i2s_get_sr_wss,
.set_chan_cfg = sun8i_i2s_set_chan_cfg,
.set_fmt = sun8i_i2s_set_soc_fmt,
+ .set_fmt_sext = sun8i_i2s_set_fmt_sext,
+ .set_txchanoffset = sun8i_i2s_set_txchanoffset,
+ .set_rxchanoffset = sun8i_i2s_set_rxchanoffset,
+ .set_txchanen = sun8i_i2s_set_txchanen,
+ .set_rxchanen = sun8i_i2s_set_rxchanen,
+ .set_txchansel = sun8i_i2s_set_txchansel,
+ .set_rxchansel = sun8i_i2s_set_rxchansel,
+ .set_txchanmap = sun8i_i2s_set_txchanmap,
+ .set_rxchanmap = sun8i_i2s_set_rxchanmap,
};
static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
@@ -1154,6 +1488,38 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
.get_wss = sun4i_i2s_get_wss,
.set_chan_cfg = sun4i_i2s_set_chan_cfg,
.set_fmt = sun4i_i2s_set_soc_fmt,
+ .set_fmt_sext = sun4i_i2s_set_fmt_sext,
+ .set_txchansel = sun4i_i2s_set_txchansel,
+ .set_rxchansel = sun4i_i2s_set_rxchansel,
+ .set_txchanmap = sun4i_i2s_set_txchanmap,
+ .set_rxchanmap = sun4i_i2s_set_rxchanmap,
+};
+
+static const struct sun4i_i2s_quirks sun50i_h6_i2s_quirks = {
+ .has_reset = true,
+ .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG,
+ .sun4i_i2s_regmap = &sun50i_i2s_regmap_config,
+ .field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8),
+ .field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 2),
+ .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6),
+ .bclk_dividers = sun8i_i2s_clk_div,
+ .num_bclk_dividers = ARRAY_SIZE(sun8i_i2s_clk_div),
+ .mclk_dividers = sun8i_i2s_clk_div,
+ .num_mclk_dividers = ARRAY_SIZE(sun8i_i2s_clk_div),
+ .get_bclk_parent_rate = sun8i_i2s_get_bclk_parent_rate,
+ .get_sr = sun8i_i2s_get_sr_wss,
+ .get_wss = sun8i_i2s_get_sr_wss,
+ .set_chan_cfg = sun8i_i2s_set_chan_cfg,
+ .set_fmt = sun8i_i2s_set_soc_fmt,
+ .set_fmt_sext = sun8i_i2s_set_fmt_sext,
+ .set_txchanoffset = sun50i_h6_i2s_set_txchanoffset,
+ .set_rxchanoffset = sun50i_h6_i2s_set_rxchanoffset,
+ .set_txchanen = sun50i_h6_i2s_set_txchanen,
+ .set_rxchanen = sun50i_h6_i2s_set_rxchanen,
+ .set_txchansel = sun50i_h6_i2s_set_txchansel,
+ .set_rxchansel = sun50i_h6_i2s_set_rxchansel,
+ .set_txchanmap = sun50i_h6_i2s_set_txchanmap,
+ .set_rxchanmap = sun50i_h6_i2s_set_rxchanmap,
};
static int sun4i_i2s_init_regmap_fields(struct device *dev,
@@ -1325,6 +1691,10 @@ static const struct of_device_id sun4i_i2s_match[] = {
.compatible = "allwinner,sun50i-a64-codec-i2s",
.data = &sun50i_a64_codec_i2s_quirks,
},
+ {
+ .compatible = "allwinner,sun50i-h6-i2s",
+ .data = &sun50i_h6_i2s_quirks,
+ },
{}
};
MODULE_DEVICE_TABLE(of, sun4i_i2s_match);
--
2.23.0
From 2da43795f6191505b2dd6e30938c3af4c90bf745 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Thu, 7 Nov 2019 07:36:11 +0100
Subject: [PATCH] sound: soc: sun4i-i2s: Fix rate calculation
Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
sound/soc/sunxi/sun4i-i2s.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index d0a8d5810c0a..6c4241cb6509 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -334,6 +334,9 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai,
return -EINVAL;
}
+ if (i2s->slot_width)
+ slot_width = i2s->slot_width;
+
bclk_parent_rate = i2s->variant->get_bclk_parent_rate(i2s);
bclk_div = sun4i_i2s_get_bclk_div(i2s, bclk_parent_rate,
rate, slots, slot_width);
--
2.24.0