diff mbox series

[RFC,20/34] ASoC: sun8i-codec: Support 8/20/24-bit word sizes

Message ID 20200217064250.15516-21-samuel@sholland.org
State New
Headers show
Series sun8i-codec fixes and new features | expand

Commit Message

Samuel Holland Feb. 17, 2020, 6:42 a.m. UTC
The codec hardware natively supports 8, 16, 20, and 24-bit word sizes.
However, it only supports slot widths that are a multiple of 16 bits.
So we can only support the 20-bit and 24-bit formats that are padded to
32 bits. This doesn't cost anything, because the DMA controller on the
CPU side only supports power-of-two byte sizes anyway.

S8, S16_LE, and S24_LE were tested using a modified version of the
sun4i-i2s driver as the CPU end of the DAI link. S20_LE was not tested
due to poor userspace support; it should work the same.

In 8 bit mono mode, the computed BCLK/LRCK divider will be less than the
minimum value 16. This is fine; there will just be padding after the
data bits, similar to how S20_LE and S24_LE always have padding.

Signed-off-by: Samuel Holland <samuel@sholland.org>
---
 sound/soc/sunxi/sun8i-codec.c | 47 ++++++++++++++++++++++++-----------
 1 file changed, 32 insertions(+), 15 deletions(-)
diff mbox series

Patch

diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index 2df899daec67..b915e62fa005 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -45,7 +45,6 @@ 
 #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV		9
 #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV		6
 #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ		4
-#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16		(1 << 4)
 #define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT		2
 #define SUN8I_AIF1CLK_CTRL_AIF1_MONO_PCM		1
 #define SUN8I_AIF1_ADCDAT_CTRL				0x044
@@ -87,6 +86,10 @@ 
 #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK	GENMASK(5, 4)
 #define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK	GENMASK(3, 2)
 
+#define SUN8I_AIF_PCM_FMTS  (SNDRV_PCM_FMTBIT_S8|\
+			     SNDRV_PCM_FMTBIT_S16_LE|\
+			     SNDRV_PCM_FMTBIT_S20_LE|\
+			     SNDRV_PCM_FMTBIT_S24_LE)
 #define SUN8I_AIF_PCM_RATES (SNDRV_PCM_RATE_8000_48000|\
 			     SNDRV_PCM_RATE_96000|\
 			     SNDRV_PCM_RATE_192000|\
@@ -307,7 +310,9 @@  static int sun8i_codec_get_lrck_div(unsigned int channels,
 {
 	unsigned int div = word_size * channels;
 
-	if (div < 16 || div > 256)
+	if (div < 16)
+		div = 16;
+	if (div > 256)
 		return -EINVAL;
 
 	return ilog2(div) - 4;
@@ -318,27 +323,19 @@  static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
 				 struct snd_soc_dai *dai)
 {
 	struct sun8i_codec *scodec = snd_soc_component_get_drvdata(dai->component);
+	unsigned int slot_width = params_physical_width(params);
 	unsigned int channels = params_channels(params);
 	int sample_rate, lrck_div;
 	u8 bclk_div;
 	u32 value;
 
-	/*
-	 * The CPU DAI handles only a sample of 16 bits. Configure the
-	 * codec to handle this type of sample resolution.
-	 */
-	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
-			   SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK,
-			   SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16);
-
 	bclk_div = sun8i_codec_get_bclk_div(scodec, params_rate(params),
-					    channels, 16);
+					    channels, slot_width);
 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
 			   SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
 			   bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);
 
-	lrck_div = sun8i_codec_get_lrck_div(channels,
-					    params_physical_width(params));
+	lrck_div = sun8i_codec_get_lrck_div(channels, slot_width);
 	if (lrck_div < 0)
 		return lrck_div;
 
@@ -346,6 +343,26 @@  static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
 			   SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK,
 			   lrck_div << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV);
 
+	switch (params_width(params)) {
+	case 8:
+		value = 0x0;
+		break;
+	case 16:
+		value = 0x1;
+		break;
+	case 20:
+		value = 0x2;
+		break;
+	case 24:
+		value = 0x3;
+		break;
+	default:
+		return -EINVAL;
+	}
+	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
+			   SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK,
+			   value << SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ);
+
 	value = channels == 1;
 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
 			   BIT(SUN8I_AIF1CLK_CTRL_AIF1_MONO_PCM),
@@ -533,7 +550,7 @@  static struct snd_soc_dai_driver sun8i_codec_dai = {
 		.channels_min = 1,
 		.channels_max = 2,
 		.rates = SUN8I_AIF_PCM_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.formats = SUN8I_AIF_PCM_FMTS,
 	},
 	/* capture capabilities */
 	.capture = {
@@ -541,7 +558,7 @@  static struct snd_soc_dai_driver sun8i_codec_dai = {
 		.channels_min = 1,
 		.channels_max = 2,
 		.rates = SUN8I_AIF_PCM_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.formats = SUN8I_AIF_PCM_FMTS,
 		.sig_bits = 24,
 	},
 	/* pcm operations */