ASoC: rt1015: support TDM slot configuration
authorShuming Fan <shumingf@realtek.com>
Wed, 4 Nov 2020 09:20:05 +0000 (17:20 +0800)
committerMark Brown <broonie@kernel.org>
Wed, 4 Nov 2020 17:51:55 +0000 (17:51 +0000)
Add TDM slot callback function to support TDM configuration

Signed-off-by: Shuming Fan <shumingf@realtek.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/r/20201104092005.2227-1-shumingf@realtek.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/rt1015.c
sound/soc/codecs/rt1015.h

index 25fe2dd..a9cd6ad 100644 (file)
@@ -944,6 +944,106 @@ static int rt1015_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
        return 0;
 }
 
+static int rt1015_set_tdm_slot(struct snd_soc_dai *dai,
+       unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+       struct snd_soc_component *component = dai->component;
+       unsigned int val = 0, rx_slotnum, tx_slotnum;
+       int ret = 0, first_bit;
+
+       switch (slots) {
+       case 2:
+               val |= RT1015_I2S_TX_2CH;
+               break;
+       case 4:
+               val |= RT1015_I2S_TX_4CH;
+               break;
+       case 6:
+               val |= RT1015_I2S_TX_6CH;
+               break;
+       case 8:
+               val |= RT1015_I2S_TX_8CH;
+               break;
+       default:
+               ret = -EINVAL;
+               goto _set_tdm_err_;
+       }
+
+       switch (slot_width) {
+       case 16:
+               val |= RT1015_I2S_CH_TX_LEN_16B;
+               break;
+       case 20:
+               val |= RT1015_I2S_CH_TX_LEN_20B;
+               break;
+       case 24:
+               val |= RT1015_I2S_CH_TX_LEN_24B;
+               break;
+       case 32:
+               val |= RT1015_I2S_CH_TX_LEN_32B;
+               break;
+       default:
+               ret = -EINVAL;
+               goto _set_tdm_err_;
+       }
+
+       /* Rx slot configuration */
+       rx_slotnum = hweight_long(rx_mask);
+       if (rx_slotnum != 1) {
+               ret = -EINVAL;
+               dev_err(component->dev, "too many rx slots or zero slot\n");
+               goto _set_tdm_err_;
+       }
+
+       /* This is an assumption that the system sends stereo audio to the amplifier typically.
+        * And the stereo audio is placed in slot 0/2/4/6 as the starting slot.
+        * The users could select the channel from L/R/L+R by "Mono LR Select" control.
+        */
+       first_bit = __ffs(rx_mask);
+       switch (first_bit) {
+       case 0:
+       case 2:
+       case 4:
+       case 6:
+               snd_soc_component_update_bits(component,
+                       RT1015_TDM1_4,
+                       RT1015_TDM_I2S_TX_L_DAC1_1_MASK |
+                       RT1015_TDM_I2S_TX_R_DAC1_1_MASK,
+                       (first_bit << RT1015_TDM_I2S_TX_L_DAC1_1_SFT) |
+                       ((first_bit+1) << RT1015_TDM_I2S_TX_R_DAC1_1_SFT));
+               break;
+       case 1:
+       case 3:
+       case 5:
+       case 7:
+               snd_soc_component_update_bits(component,
+                       RT1015_TDM1_4,
+                       RT1015_TDM_I2S_TX_L_DAC1_1_MASK |
+                       RT1015_TDM_I2S_TX_R_DAC1_1_MASK,
+                       ((first_bit-1) << RT1015_TDM_I2S_TX_L_DAC1_1_SFT) |
+                       (first_bit << RT1015_TDM_I2S_TX_R_DAC1_1_SFT));
+               break;
+       default:
+               ret = -EINVAL;
+               goto _set_tdm_err_;
+       }
+
+       /* Tx slot configuration */
+       tx_slotnum = hweight_long(tx_mask);
+       if (tx_slotnum) {
+               ret = -EINVAL;
+               dev_err(component->dev, "doesn't need to support tx slots\n");
+               goto _set_tdm_err_;
+       }
+
+       snd_soc_component_update_bits(component, RT1015_TDM1_1,
+               RT1015_I2S_CH_TX_MASK | RT1015_I2S_CH_RX_MASK |
+               RT1015_I2S_CH_TX_LEN_MASK | RT1015_I2S_CH_RX_LEN_MASK, val);
+
+_set_tdm_err_:
+       return ret;
+}
+
 static int rt1015_probe(struct snd_soc_component *component)
 {
        struct rt1015_priv *rt1015 =
@@ -975,6 +1075,7 @@ static struct snd_soc_dai_ops rt1015_aif_dai_ops = {
        .hw_params = rt1015_hw_params,
        .set_fmt = rt1015_set_dai_fmt,
        .set_bclk_ratio = rt1015_set_bclk_ratio,
+       .set_tdm_slot = rt1015_set_tdm_slot,
 };
 
 static struct snd_soc_dai_driver rt1015_dai[] = {
index d3fdd30..ad8274c 100644 (file)
 #define RT1015_ID_VERA                         0x0
 #define RT1015_ID_VERB                         0x1
 
+/* 0x00f2 */
+#define RT1015_MONO_LR_SEL_MASK                        (0x3 << 4)
+#define RT1015_MONO_L_CHANNEL                  (0x0 << 4)
+#define RT1015_MONO_R_CHANNEL                  (0x1 << 4)
+#define RT1015_MONO_LR_MIX_CHANNEL                     (0x2 << 4)
+
 /* 0x0102 */
 #define RT1015_DAC_VOL_MASK                    (0x7f << 9)
 #define RT1015_DAC_VOL_SFT                     9
 #define RT1015_TDM_INV_BCLK_MASK               (0x1 << 15)
 #define RT1015_TDM_INV_BCLK_SFT                        15
 #define RT1015_TDM_INV_BCLK                    (0x1 << 15)
+#define RT1015_I2S_CH_TX_MASK                  (0x3 << 10)
+#define RT1015_I2S_CH_TX_SFT                   10
+#define RT1015_I2S_TX_2CH                      (0x0 << 10)
+#define RT1015_I2S_TX_4CH                      (0x1 << 10)
+#define RT1015_I2S_TX_6CH                      (0x2 << 10)
+#define RT1015_I2S_TX_8CH                      (0x3 << 10)
+#define RT1015_I2S_CH_RX_MASK                  (0x3 << 8)
+#define RT1015_I2S_CH_RX_SFT                   8
+#define RT1015_I2S_RX_2CH                      (0x0 << 8)
+#define RT1015_I2S_RX_4CH                      (0x1 << 8)
+#define RT1015_I2S_RX_6CH                      (0x2 << 8)
+#define RT1015_I2S_RX_8CH                      (0x3 << 8)
+#define RT1015_I2S_LR_CH_SEL_MASK                      (0x1 << 7)
+#define RT1015_I2S_LR_CH_SEL_SFT                       7
+#define RT1015_I2S_LEFT_CH_SEL                 (0x0 << 7)
+#define RT1015_I2S_RIGHT_CH_SEL                        (0x1 << 7)
+#define RT1015_I2S_CH_TX_LEN_MASK                      (0x7 << 4)
+#define RT1015_I2S_CH_TX_LEN_SFT                       4
+#define RT1015_I2S_CH_TX_LEN_16B                       (0x0 << 4)
+#define RT1015_I2S_CH_TX_LEN_20B                       (0x1 << 4)
+#define RT1015_I2S_CH_TX_LEN_24B                       (0x2 << 4)
+#define RT1015_I2S_CH_TX_LEN_32B                       (0x3 << 4)
+#define RT1015_I2S_CH_TX_LEN_8B                        (0x4 << 4)
+#define RT1015_I2S_CH_RX_LEN_MASK                      (0x7 << 0)
+#define RT1015_I2S_CH_RX_LEN_SFT                       0
+#define RT1015_I2S_CH_RX_LEN_16B                       (0x0 << 0)
+#define RT1015_I2S_CH_RX_LEN_20B                       (0x1 << 0)
+#define RT1015_I2S_CH_RX_LEN_24B                       (0x2 << 0)
+#define RT1015_I2S_CH_RX_LEN_32B                       (0x3 << 0)
+#define RT1015_I2S_CH_RX_LEN_8B                        (0x4 << 0)
+
+/* TDM1 Setting-4 (0x011a) */
+#define RT1015_TDM_I2S_TX_L_DAC1_1_MASK                        (0x7 << 12)
+#define RT1015_TDM_I2S_TX_R_DAC1_1_MASK                        (0x7 << 8)
+#define RT1015_TDM_I2S_TX_L_DAC1_1_SFT 12
+#define RT1015_TDM_I2S_TX_R_DAC1_1_SFT 8
 
 /* 0x0330 */
 #define RT1015_ABST_AUTO_EN_MASK               (0x1 << 13)