From: Marcus Cooper <codekip...@gmail.com>

The i2s block can be used to pass PCM data over multiple channels
and is sometimes used for the audio side of an HDMI connection.

Signed-off-by: Marcus Cooper <codekip...@gmail.com>
---
 sound/soc/sunxi/sun4i-i2s.c | 93 +++++++++++++++++++++++++------------
 1 file changed, 63 insertions(+), 30 deletions(-)

diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index a020c3b372a8..a71969167053 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -617,41 +617,74 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream 
*substream,
        int lines;
 
        channels = params_channels(params);
-       if (channels != 2) {
-               dev_err(dai->dev, "Unsupported number of channels: %d\n",
-                       channels);
-               return -EINVAL;
-       }
-
-       lines = (channels + 1) / 2;
+       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;
+               }
 
-       /* 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));
-
-       if (i2s->variant->has_chcfg) {
-               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));
-               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));
-       }
+               lines = (channels + 1) / 2;
 
-       /* Map the channels for playback and capture */
-       i2s->variant->set_txchanmap(i2s, 0, 0x76543210);
-       i2s->variant->set_rxchanmap(i2s, 0x00003210);
+               /* 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->has_chcfg) {
+                       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;
+               }
 
-       /* Configure the channels */
-       i2s->variant->set_txchansel(i2s, 0, channels);
-       i2s->variant->set_rxchansel(i2s, channels);
+               /* Map the channels for capture */
+               i2s->variant->set_rxchanmap(i2s, 0x10);
+               i2s->variant->set_rxchansel(i2s, channels);
 
-       if (i2s->variant->set_txchanen)
-               i2s->variant->set_txchanen(i2s, 0, channels);
+               if (i2s->variant->set_rxchanen)
+                       i2s->variant->set_rxchanen(i2s, channels);
 
-       if (i2s->variant->set_rxchanen)
-               i2s->variant->set_rxchanen(i2s, channels);
+               if (i2s->variant->has_chcfg)
+                       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:
-- 
2.22.0

-- 
You received this message because you are subscribed to the Google Groups 
"linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to linux-sunxi+unsubscr...@googlegroups.com.
To view this discussion on the web, visit 
https://groups.google.com/d/msgid/linux-sunxi/20190814060854.26345-14-codekipper%40gmail.com.

Reply via email to