From b5cb1681a065bd03b0eb84ca243bc50ab0fa54c1 Mon Sep 17 00:00:00 2001 From: Jernej Skrabec 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 Date: Thu, 7 Nov 2019 07:36:11 +0100 Subject: [PATCH] sound: soc: sun4i-i2s: Fix rate calculation Signed-off-by: Jernej Skrabec --- 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