audio: snd mixer controls for tdm/spdif
authorXing Wang <xing.wang@amlogic.com>
Wed, 16 Aug 2017 07:40:13 +0000 (15:40 +0800)
committerJianxin Pan <jianxin.pan@amlogic.com>
Thu, 17 Aug 2017 08:45:23 +0000 (01:45 -0700)
PD#147540: audio: add snd mixer controls for tdm/spdif
1. add gain/mute/swap/mask for tdm/spdif
2. channel status for spdif in/out
3. add loopback for s420, and fix datain channel number

Change-Id: I81d4432dc2b73c7cb0275087fc3d4e23ec83e400
Signed-off-by: Xing Wang <xing.wang@amlogic.com>
arch/arm64/boot/dts/amlogic/axg_s400.dts
arch/arm64/boot/dts/amlogic/axg_s420.dts
sound/soc/amlogic/auge/audio_utils.c
sound/soc/amlogic/auge/audio_utils.h
sound/soc/amlogic/auge/card.c
sound/soc/amlogic/auge/spdif_hw.c
sound/soc/amlogic/auge/spdif_hw.h
sound/soc/amlogic/auge/tdm.c
sound/soc/codecs/amlogic/dummy_codec.c

index dbc182f..7acc7dd 100644 (file)
                                dai-tdm-slot-width = <32>;
                        };
                        codec {
+                               /*
+                                * prefix-names = "3101_A", "3101_B",
+                                * "3101_C", "3101_D";
+                                * sound-dai = <&tlv320adc3101_32
+                                * &tlv320adc3101_30
+                                * &tlv320adc3101_34
+                                * &tlv320adc3101_36>;
+                                */
                                sound-dai = <&tlv320adc3101_32 &dummy_codec>;
-                               /*prefix-names = "3101_A", "3101_B",*/
-                                       /*"3101_C", "3101_D";*/
-                               /*sound-dai = <&tlv320adc3101_32*/
-                                       /*&tlv320adc3101_30*/
-                                       /*&tlv320adc3101_34*/
-                                       /*&tlv320adc3101_36>;*/
                        };
                };
 
index 329d5ea..893b065 100644 (file)
                aml-audio-card,name = "AML-AXGSOUND";
                //aml-audio-card,mclk-fs = <256>;
 
+               aml-audio-card,loopback = <&aml_loopback>;
+
                aml-audio-card,dai-link@0 {
                        format = "dsp_a";
                        mclk-fs = <256>;//512
                filter_mode = <1>; /* mode 0~4, defalut:1 */
                status = "okay";
        };
+
+       aml_loopback: loopback {
+               compatible = "amlogic, snd-loopback";
+               /*
+                * 0: out rate = in data rate;
+                * 1: out rate = loopback data rate;
+                */
+               lb_mode = <0>;
+
+               /* datain src
+                * 0: tdmin_a;
+                * 1: tdmin_b;
+                * 2: tdmin_c;
+                * 3: spdifin;
+                * 4: pdmin;
+                */
+               datain_src = <4>;
+               datain_chnum = <8>;
+               datain_chmask = <0xfc>;
+
+               /* tdmin_lb src
+                * 0: tdmoutA
+                * 1: tdmoutB
+                * 2: tdmoutC
+                * 3: PAD_tdminA
+                * 4: PAD_tdminB
+                * 5: PAD_tdminC
+                */
+               datalb_src = <1>;
+               datalb_chnum = <2>;
+               datalb_chmask = <0x3>;
+
+               status = "okay";
+       };
 }; /* end of audiobus */
 
 &pinctrl_periphs {
index b25dc8f..5007b89 100644 (file)
 #include "regs.h"
 #include "iomap.h"
 #include "loopback_hw.h"
+#include "spdif_hw.h"
+
+struct snd_elem_info {
+       struct soc_enum *ee;
+       int reg;
+       int shift;
+       u32 mask;
+};
 
 static unsigned int loopback_enable;
 
@@ -56,11 +64,11 @@ static int loopback_enable_set_enum(
 static unsigned int loopback_datain;
 
 static const char *const loopback_datain_texts[] = {
-       "Tdmin A",
-       "Tdmin B",
-       "Tdmin C",
-       "Spdif In",
-       "Pdm In",
+       "TDMIN_A",
+       "TDMIN_B",
+       "TDMIN_C",
+       "SPDIFIN",
+       "PDMIN",
 };
 
 static const struct soc_enum loopback_datain_enum =
@@ -90,12 +98,12 @@ static int loopback_datain_set_enum(
 static unsigned int loopback_tdminlb;
 
 static const char *const loopback_tdminlb_texts[] = {
-       "tdmoutA",
-       "tdmoutB",
-       "tdmoutC",
-       "tdminA",
-       "tdminB",
-       "tdminC",
+       "TDMOUT_A",
+       "TDMOUT_B",
+       "TDMOUT_C",
+       "TDMIN_A",
+       "TDMIN_B",
+       "TDMIN_C",
 };
 
 static const struct soc_enum loopback_tdminlb_enum =
@@ -126,6 +134,465 @@ static int loopback_tdminlb_set_enum(
        return 0;
 }
 
+static int snd_int_info(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 0xffffffff;
+       uinfo->count = 1;
+
+       return 0;
+}
+
+static int snd_int_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       int val;
+       unsigned int reg = kcontrol->private_value;
+
+       val = audiobus_read(reg);
+       ucontrol->value.integer.value[0] = val;
+
+       pr_info("%s:reg:0x%x, val:0x%x\n",
+               __func__,
+               reg,
+               val);
+
+       return 0;
+}
+
+static int snd_int_set(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       int val  = (int)ucontrol->value.integer.value[0];
+       unsigned int reg = kcontrol->private_value;
+
+       pr_info("%s:reg:0x%x, val:0x%x\n",
+               __func__,
+               reg,
+               val);
+
+       audiobus_write(reg, val);
+
+       return 0;
+}
+
+#define SND_INT(xname, type, func)     \
+{                                      \
+       .iface = SNDRV_CTL_ELEM_IFACE_PCM, \
+       .name  = xname,               \
+       .info  = snd_int_info,        \
+       .get   = snd_int_get,         \
+       .put   = snd_int_set,         \
+       .private_value = EE_AUDIO_##type##_##func, \
+}
+
+static int snd_byte_info(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 0xff;
+       uinfo->count = 1;
+
+       return 0;
+}
+
+static int snd_byte_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       int val;
+       struct snd_elem_info *einfo = (void *)kcontrol->private_value;
+
+       val = audiobus_read(einfo->reg);
+       val >>= einfo->shift;
+       val &=  einfo->mask;
+
+       ucontrol->value.integer.value[0] = val;
+
+       pr_info("%s:reg:0x%x, mask:0x%x, mask val:0x%x\n",
+               __func__,
+               einfo->reg,
+               einfo->mask,
+               val);
+
+       return 0;
+}
+
+static int snd_byte_set(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       int val  = (int)ucontrol->value.integer.value[0];
+       struct snd_elem_info *einfo = (void *)kcontrol->private_value;
+
+       if (val < 0)
+               val = 0;
+       if (val > 255)
+               val = 255;
+
+       pr_info("%s:reg:0x%x, mask:0x%x, mask val:0x%x\n",
+               __func__,
+               einfo->reg,
+               einfo->mask,
+               val);
+
+       audiobus_update_bits(
+               einfo->reg,
+               einfo->mask << einfo->shift,
+               val << einfo->shift);
+
+       return 0;
+}
+
+#define SND_BYTE(xname, type, func, xshift, xmask)   \
+{                                      \
+       .iface = SNDRV_CTL_ELEM_IFACE_PCM, \
+       .name  = xname,                \
+       .info  = snd_byte_info,        \
+       .get   = snd_byte_get,         \
+       .put   = snd_byte_set,         \
+       .private_value =               \
+       ((unsigned long)&(struct snd_elem_info) \
+               {.reg = EE_AUDIO_##type##_##func,   \
+                .shift = xshift, .mask = xmask })  \
+}
+
+static int snd_enum_info(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_info *uinfo)
+{
+       struct snd_elem_info *einfo = (void *)kcontrol->private_value;
+       struct soc_enum *e = (struct soc_enum *)einfo->ee;
+
+       return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2,
+                                        e->items, e->texts);
+
+}
+
+static int snd_enum_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       int val;
+       struct snd_elem_info *einfo = (void *)kcontrol->private_value;
+
+       val = audiobus_read(einfo->reg);
+       val >>= einfo->shift;
+       val &= einfo->mask;
+       ucontrol->value.integer.value[0] = val;
+
+       pr_info("%s:reg:0x%x, mask:0x%x val:0x%x\n",
+               __func__,
+               einfo->reg,
+               einfo->mask,
+               val);
+
+       return 0;
+}
+
+static int snd_enum_set(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_elem_info *einfo = (void *)kcontrol->private_value;
+       int val  = (int)ucontrol->value.integer.value[0];
+
+       pr_info("%s:reg:0x%x, swap mask:0x%x, val:0x%x\n",
+               __func__,
+               einfo->reg,
+               einfo->mask,
+               val);
+
+       audiobus_update_bits(
+               einfo->reg,
+               einfo->mask << einfo->shift,
+               val << einfo->shift);
+
+       return 0;
+}
+
+#define SND_ENUM(xname, type, func, xenum, xshift, xmask)   \
+{                                      \
+       .iface = SNDRV_CTL_ELEM_IFACE_PCM, \
+       .name  = xname,                    \
+       .info  = snd_enum_info,            \
+       .get   = snd_enum_get,             \
+       .put   = snd_enum_set,             \
+       .private_value = ((unsigned long)&(struct snd_elem_info) \
+               {.reg = EE_AUDIO_##type##_##func,   \
+                .ee = (struct soc_enum *)&xenum,   \
+                .shift = xshift, .mask = xmask }) \
+}
+
+static const char * const in_swap_channel_text[] = {
+       "Swap To Lane0 Left Channel",
+       "Swap To Lane0 Right Channel",
+       "Swap To Lane1 Left Channel",
+       "Swap To Lane1 Right Channel",
+       "Swap To Lane2 Left Channel",
+       "Swap To Lane2 Right Channel",
+       "Swap To Lane3 Left Channel",
+       "Swap To Lane3 Right Channel",
+};
+
+static const struct soc_enum in_swap_channel_enum =
+       SOC_ENUM_SINGLE_EXT(
+               ARRAY_SIZE(in_swap_channel_text),
+               in_swap_channel_text);
+
+static const char * const out_swap_channel_text[] = {
+       "Swap To CH0",
+       "Swap To CH1",
+       "Swap To CH2",
+       "Swap To CH3",
+       "Swap To CH4",
+       "Swap To CH5",
+       "Swap To CH6",
+       "Swap To CH7",
+};
+
+static const struct soc_enum out_swap_channel_enum =
+       SOC_ENUM_SINGLE_EXT(
+               ARRAY_SIZE(out_swap_channel_text),
+               out_swap_channel_text);
+
+static const char * const lane0_mixer_text[] = {
+       "Disable Mix",
+       "Lane0 Mix Left and Right Channel",
+};
+
+static const struct soc_enum lane0_mixer_enum =
+       SOC_ENUM_SINGLE_EXT(
+               ARRAY_SIZE(lane0_mixer_text),
+               lane0_mixer_text);
+
+static const char * const lane1_mixer_text[] = {
+       "Disable Mix",
+       "Lane1 Mix Left and Right Channel",
+};
+
+static const struct soc_enum lane1_mixer_enum =
+       SOC_ENUM_SINGLE_EXT(
+               ARRAY_SIZE(lane1_mixer_text),
+               lane1_mixer_text);
+
+static const char * const lane2_mixer_text[] = {
+       "Disable Mix",
+       "Lane2 Mix Left and Right Channel",
+};
+
+static const struct soc_enum lane2_mixer_enum =
+       SOC_ENUM_SINGLE_EXT(
+               ARRAY_SIZE(lane2_mixer_text),
+               lane2_mixer_text);
+
+static const char * const lane3_mixer_text[] = {
+       "Disable Mix",
+       "Lane3 Mix Left and Right Channel",
+};
+
+static const struct soc_enum lane3_mixer_enum =
+       SOC_ENUM_SINGLE_EXT(
+               ARRAY_SIZE(lane3_mixer_text),
+               lane3_mixer_text);
+
+static const char * const spdifin_sample_rate_text[] = {
+       "24000",
+       "32000",
+       "44100",
+       "46000",
+       "48000",
+       "96000",
+       "192000",
+};
+
+static const struct soc_enum spdifin_sample_rate_enum =
+       SOC_ENUM_SINGLE_EXT(
+               ARRAY_SIZE(spdifin_sample_rate_text),
+               spdifin_sample_rate_text);
+
+static int spdifin_sample_rate_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       int mode = spdifin_get_mode();
+
+       ucontrol->value.enumerated.item[0] = mode;
+       return 0;
+}
+
+static const char * const spdif_channel_status_text[] = {
+       "Channel A Status[31:0]",
+       "Channel A Status[63:32]",
+       "Channel A Status[95:64]",
+       "Channel A Status[127:96]",
+       "Channel A Status[159:128]",
+       "Channel A Status[191:160]",
+       "Channel B Status[31:0]",
+       "Channel B Status[63:32]",
+       "Channel B Status[95:64]",
+       "Channel B Status[127:96]",
+       "Channel B Status[159:128]",
+       "Channel B Status[191:160]",
+};
+
+static const struct soc_enum spdif_channel_status_enum =
+       SOC_ENUM_SINGLE_EXT(
+               ARRAY_SIZE(spdif_channel_status_text),
+               spdif_channel_status_text);
+
+static int spdifin_channel_status;
+
+static int spdifout_channel_status;
+
+#define SPDIFIN_CHSTS_REG \
+       EE_AUDIO_SPDIFIN_STAT1
+
+#define SPDIFOUT_CHSTS_REG(xinstance) \
+       (EE_AUDIO_SPDIFOUT_CHSTS0 + xinstance)
+
+static int spdif_channel_status_info(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_info *uinfo)
+{
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       int i;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 0xffffffff;
+       uinfo->count = 1;
+
+       for (i = 0; i < e->items; i++)
+               pr_info("Item:%d, %s\n", i, e->texts[i]);
+
+       return 0;
+}
+
+static int spdifin_channel_status_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       int reg, status;
+
+       pr_info("set which channel status you wanted to get firstly\n");
+       reg = SPDIFIN_CHSTS_REG;
+       status = spdif_get_channel_status(reg);
+
+       ucontrol->value.enumerated.item[0] = status;
+
+       /*channel status value in printk information*/
+       pr_info("%s: 0x%x\n",
+               e->texts[spdifin_channel_status],
+               status
+               );
+       return 0;
+}
+
+static int spdifin_channel_status_set(
+       struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       int chst = ucontrol->value.enumerated.item[0];
+       int ch, valid_bits;
+
+       if (chst < 0 || chst > e->items - 1) {
+               pr_err("out of value, fixed it\n");
+
+               if (chst < 0)
+                       chst = 0;
+               if (chst > e->items - 1)
+                       chst = e->items - 1;
+       }
+       ch = (chst >= 6);
+       valid_bits = (chst >= 6) ? (chst - 6) : chst;
+
+       spdifin_channel_status = chst;
+       pr_info("%s\n",
+               e->texts[spdifin_channel_status]);
+
+       spdifin_set_channel_status(ch, valid_bits);
+
+       return 0;
+}
+
+static int spdifout_channel_status_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       int reg, status;
+
+       pr_info("set which channel status you wanted to get firstly\n");
+       reg = SPDIFOUT_CHSTS_REG(spdifout_channel_status);
+       status = spdif_get_channel_status(reg);
+
+       ucontrol->value.enumerated.item[0] = status;
+
+       /*channel status value in printk information*/
+       pr_info("%s: reg:0x%x, status:0x%x\n",
+               e->texts[spdifout_channel_status],
+               reg,
+               status
+               );
+       return 0;
+}
+
+static int spdifout_channel_status_set(
+       struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       int chst = ucontrol->value.enumerated.item[0];
+
+       if (chst < 0 || chst > e->items - 1) {
+               pr_err("out of value, fixed it\n");
+
+               if (chst < 0)
+                       chst = 0;
+               if (chst > e->items - 1)
+                       chst = e->items - 1;
+       }
+
+       spdifout_channel_status = chst;
+       pr_info("%s\n",
+               e->texts[chst]);
+
+       return 0;
+}
+
+
+#define SPDIFIN_CHSTATUS(xname, xenum)   \
+{                                        \
+       .iface = SNDRV_CTL_ELEM_IFACE_PCM,   \
+       .name  = xname,                      \
+       .info  = spdif_channel_status_info,  \
+       .get   = spdifin_channel_status_get, \
+       .put   = spdifin_channel_status_set,   \
+       .private_value = (unsigned long)&xenum \
+}
+
+#define SPDIFOUT_CHSTATUS(xname, xenum)   \
+{                                         \
+       .iface = SNDRV_CTL_ELEM_IFACE_PCM,    \
+       .name  = xname,                       \
+       .info  = spdif_channel_status_info,   \
+       .get   = spdifout_channel_status_get, \
+       .put   = spdifout_channel_status_set, \
+       .private_value = (unsigned long)&xenum\
+}
+
+
+#define SND_MIX(xname, type, xenum, xshift, xmask)   \
+       SND_ENUM(xname, type, CTRL0, xenum, xshift, xmask)
+
+#define SND_SWAP(xname, type, xenum, xshift, xmask)   \
+       SND_ENUM(xname, type, SWAP, xenum, xshift, xmask)
+
+#define TDM_MASK(xname, type, func)   \
+       SND_INT(xname, type, func)
+
+#define TDM_MUTE(xname, type, func)   \
+       SND_INT(xname, type, func)
+
+#define TDM_GAIN(xname, type, func, xshift, xmask)   \
+       SND_BYTE(xname, type, func, xshift, xmask)
 
 static const struct snd_kcontrol_new snd_auge_controls[] = {
        /* loopback enable */
@@ -146,6 +613,267 @@ static const struct snd_kcontrol_new snd_auge_controls[] = {
                     loopback_tdminlb_get_enum,
                     loopback_tdminlb_set_enum),
 
+       /*TDMIN_A swap*/
+       SND_SWAP("TDMIN_A Ch0 Swap", TDMIN_A, in_swap_channel_enum, 0, 0x7),
+       SND_SWAP("TDMIN_A Ch1 Swap", TDMIN_A, in_swap_channel_enum, 4, 0x7),
+       SND_SWAP("TDMIN_A Ch2 Swap", TDMIN_A, in_swap_channel_enum, 8, 0x7),
+       SND_SWAP("TDMIN_A Ch3 Swap", TDMIN_A, in_swap_channel_enum, 12, 0x7),
+       SND_SWAP("TDMIN_A Ch4 Swap", TDMIN_A, in_swap_channel_enum, 16, 0x7),
+       SND_SWAP("TDMIN_A Ch5 Swap", TDMIN_A, in_swap_channel_enum, 20, 0x7),
+       SND_SWAP("TDMIN_A Ch6 Swap", TDMIN_A, in_swap_channel_enum, 24, 0x7),
+       SND_SWAP("TDMIN_A Ch7 Swap", TDMIN_A, in_swap_channel_enum, 28, 0x7),
+       /*TDMIN_A lane0~3 mask*/
+       TDM_MASK("TDMIN_A Lane0 Channel Mask", TDMIN_A, MASK0),
+       TDM_MASK("TDMIN_A Lane1 Channel Mask", TDMIN_A, MASK1),
+       TDM_MASK("TDMIN_A Lane2 Channel Mask", TDMIN_A, MASK2),
+       TDM_MASK("TDMIN_A Lane3 Channel Mask", TDMIN_A, MASK3),
+       /*TDMIN_A lane0~3 mute vale, when mute, the channel value*/
+       TDM_MUTE("TDMIN_A MUTE_VAL", TDMIN_A, MUTE_VAL),
+       /*TDMIN_A lane0~3 mute*/
+       TDM_MUTE("TDMIN_A Lane0 Mute Channel", TDMIN_A, MUTE0),
+       TDM_MUTE("TDMIN_A Lane1 Mute Channel", TDMIN_A, MUTE1),
+       TDM_MUTE("TDMIN_A Lane2 Mute Channel", TDMIN_A, MUTE2),
+       TDM_MUTE("TDMIN_A Lane3 Mute Channel", TDMIN_A, MUTE3),
+
+       /*TDMIN_B swap*/
+       SND_SWAP("TDMIN_B Ch0 Swap", TDMIN_B, in_swap_channel_enum, 0, 0x7),
+       SND_SWAP("TDMIN_B Ch1 Swap", TDMIN_B, in_swap_channel_enum, 4, 0x7),
+       SND_SWAP("TDMIN_B Ch2 Swap", TDMIN_B, in_swap_channel_enum, 8, 0x7),
+       SND_SWAP("TDMIN_B Ch3 Swap", TDMIN_B, in_swap_channel_enum, 12, 0x7),
+       SND_SWAP("TDMIN_B Ch4 Swap", TDMIN_B, in_swap_channel_enum, 16, 0x7),
+       SND_SWAP("TDMIN_B Ch5 Swap", TDMIN_B, in_swap_channel_enum, 20, 0x7),
+       SND_SWAP("TDMIN_B Ch6 Swap", TDMIN_B, in_swap_channel_enum, 24, 0x7),
+       SND_SWAP("TDMIN_B Ch7 Swap", TDMIN_B, in_swap_channel_enum, 28, 0x7),
+       /*TDMIN_B lane0~3 mask*/
+       TDM_MASK("TDMIN_B Lane0 Channel Mask", TDMIN_B, MASK0),
+       TDM_MASK("TDMIN_B Lane1 Channel Mask", TDMIN_B, MASK1),
+       TDM_MASK("TDMIN_B Lane2 Channel Mask", TDMIN_B, MASK2),
+       TDM_MASK("TDMIN_B Lane3 Channel Mask", TDMIN_B, MASK3),
+       /*TDMIN_B lane0~3 mute vale*/
+       TDM_MUTE("TDMIN_B MUTE_VAL", TDMIN_B, MUTE_VAL),
+       /*TDMIN_B lane0~3 mute*/
+       TDM_MUTE("TDMIN_B Lane0 Mute Channel", TDMIN_B, MUTE0),
+       TDM_MUTE("TDMIN_B Lane1 Mute Channel", TDMIN_B, MUTE1),
+       TDM_MUTE("TDMIN_B Lane2 Mute Channel", TDMIN_B, MUTE2),
+       TDM_MUTE("TDMIN_B Lane3 Mute Channel", TDMIN_B, MUTE3),
+
+       /*TDMIN_C swap*/
+       SND_SWAP("TDMIN_C Ch0 Swap", TDMIN_C, in_swap_channel_enum, 0, 0x7),
+       SND_SWAP("TDMIN_C Ch1 Swap", TDMIN_C, in_swap_channel_enum, 4, 0x7),
+       SND_SWAP("TDMIN_C Ch2 Swap", TDMIN_C, in_swap_channel_enum, 8, 0x7),
+       SND_SWAP("TDMIN_C Ch3 Swap", TDMIN_C, in_swap_channel_enum, 12, 0x7),
+       SND_SWAP("TDMIN_C Ch4 Swap", TDMIN_C, in_swap_channel_enum, 16, 0x7),
+       SND_SWAP("TDMIN_C Ch5 Swap", TDMIN_C, in_swap_channel_enum, 20, 0x7),
+       SND_SWAP("TDMIN_C Ch6 Swap", TDMIN_C, in_swap_channel_enum, 24, 0x7),
+       SND_SWAP("TDMIN_C Ch7 Swap", TDMIN_C, in_swap_channel_enum, 28, 0x7),
+       /*TDMIN_C lane0~3 mask*/
+       TDM_MASK("TDMIN_C Lane0 Channel Mask", TDMIN_C, MASK0),
+       TDM_MASK("TDMIN_C Lane1 Channel Mask", TDMIN_C, MASK1),
+       TDM_MASK("TDMIN_C Lane2 Channel Mask", TDMIN_C, MASK2),
+       TDM_MASK("TDMIN_C Lane3 Channel Mask", TDMIN_C, MASK3),
+       /*TDMIN_C lane0~3 mute vale*/
+       TDM_MUTE("TDMIN_C MUTE_VAL", TDMIN_C, MUTE_VAL),
+       /*TDMIN_C lane0~3 mute*/
+       TDM_MUTE("TDMIN_C Lane0 Mute Channel", TDMIN_C, MUTE0),
+       TDM_MUTE("TDMIN_C Lane1 Mute Channel", TDMIN_C, MUTE1),
+       TDM_MUTE("TDMIN_C Lane2 Mute Channel", TDMIN_C, MUTE2),
+       TDM_MUTE("TDMIN_C Lane3 Mute Channel", TDMIN_C, MUTE3),
+
+       /*TDMIN_LB swap*/
+       SND_SWAP("TDMIN_LB Ch0 Swap", TDMIN_LB, in_swap_channel_enum, 0, 0x7),
+       SND_SWAP("TDMIN_LB Ch1 Swap", TDMIN_LB, in_swap_channel_enum, 4, 0x7),
+       SND_SWAP("TDMIN_LB Ch2 Swap", TDMIN_LB, in_swap_channel_enum, 8, 0x7),
+       SND_SWAP("TDMIN_LB Ch3 Swap", TDMIN_LB, in_swap_channel_enum, 12, 0x7),
+       SND_SWAP("TDMIN_LB Ch4 Swap", TDMIN_LB, in_swap_channel_enum, 16, 0x7),
+       SND_SWAP("TDMIN_LB Ch5 Swap", TDMIN_LB, in_swap_channel_enum, 20, 0x7),
+       SND_SWAP("TDMIN_LB Ch6 Swap", TDMIN_LB, in_swap_channel_enum, 24, 0x7),
+       SND_SWAP("TDMIN_LB Ch7 Swap", TDMIN_LB, in_swap_channel_enum, 28, 0x7),
+       /*TDMIN_LB lane0~3 mask*/
+       TDM_MASK("TDMIN_LB Lane0 Channel Mask", TDMIN_LB, MASK0),
+       TDM_MASK("TDMIN_LB Lane1 Channel Mask", TDMIN_LB, MASK1),
+       TDM_MASK("TDMIN_LB Lane2 Channel Mask", TDMIN_LB, MASK2),
+       TDM_MASK("TDMIN_LB Lane3 Channel Mask", TDMIN_LB, MASK3),
+       /*TDMIN_LB lane0~3 mute vale*/
+       TDM_MUTE("TDMIN_LB MUTE_VAL", TDMIN_LB, MUTE_VAL),
+       /*TDMIN_LB lane0~3 mute*/
+       TDM_MUTE("TDMIN_LB Lane0 Mute Channel", TDMIN_LB, MUTE0),
+       TDM_MUTE("TDMIN_LB Lane1 Mute Channel", TDMIN_LB, MUTE1),
+       TDM_MUTE("TDMIN_LB Lane2 Mute Channel", TDMIN_LB, MUTE2),
+       TDM_MUTE("TDMIN_LB Lane3 Mute Channel", TDMIN_LB, MUTE3),
+
+       /*TDMOUT_A swap*/
+       SND_SWAP("TDMOUT_A Lane0 Left Channel Swap",
+               TDMOUT_A, out_swap_channel_enum, 0, 0x7),
+       SND_SWAP("TDMOUT_A Lane0 Right Channel Swap",
+               TDMOUT_A, out_swap_channel_enum, 4, 0x7),
+       SND_SWAP("TDMOUT_A Lane1 Left Channel Swap",
+               TDMOUT_A, out_swap_channel_enum, 8, 0x7),
+       SND_SWAP("TDMOUT_A Lane1 Right Channel Swap",
+               TDMOUT_A, out_swap_channel_enum, 12, 0x7),
+       SND_SWAP("TDMOUT_A Lane2 Left Channel Swap",
+               TDMOUT_A, out_swap_channel_enum, 16, 0x7),
+       SND_SWAP("TDMOUT_A Lane2 Right Channel Swap",
+               TDMOUT_A, out_swap_channel_enum, 20, 0x7),
+       SND_SWAP("TDMOUT_A Lane3 Left Channel Swap",
+               TDMOUT_A, out_swap_channel_enum, 24, 0x7),
+       SND_SWAP("TDMOUT_A Lane3 Right Channel Swap",
+               TDMOUT_A, out_swap_channel_enum, 28, 0x7),
+       /*TDMOUT_A mask value*/
+       TDM_MASK("TDMOUT_A Lane0 MASK_VAL", TDMOUT_A, MASK_VAL),
+       /*TDMOUT_A lane0~3 mask*/
+       TDM_MASK("TDMOUT_A Lane0 Channel Mask", TDMOUT_A, MASK0),
+       TDM_MASK("TDMOUT_A Lane1 Channel Mask", TDMOUT_A, MASK1),
+       TDM_MASK("TDMOUT_A Lane2 Channel Mask", TDMOUT_A, MASK2),
+       TDM_MASK("TDMOUT_A Lane3 Channel Mask", TDMOUT_A, MASK3),
+       /*TDMOUT_A gain, enable data * gain*/
+       TDM_GAIN("TDMOUT_A GAIN Enable", TDMOUT_A, CTRL1, 26, 0x1),
+       TDM_GAIN("TDMOUT_A GAIN CH0", TDMOUT_A, GAIN0, 0, 0xff),
+       TDM_GAIN("TDMOUT_A GAIN CH1", TDMOUT_A, GAIN0, 8, 0xff),
+       TDM_GAIN("TDMOUT_A GAIN CH2", TDMOUT_A, GAIN0, 16, 0xff),
+       TDM_GAIN("TDMOUT_A GAIN CH3", TDMOUT_A, GAIN0, 24, 0xff),
+       TDM_GAIN("TDMOUT_A GAIN CH4", TDMOUT_A, GAIN1, 0, 0xff),
+       TDM_GAIN("TDMOUT_A GAIN CH5", TDMOUT_A, GAIN1, 8, 0xff),
+       TDM_GAIN("TDMOUT_A GAIN CH6", TDMOUT_A, GAIN1, 16, 0xff),
+       TDM_GAIN("TDMOUT_A GAIN CH7", TDMOUT_A, GAIN1, 24, 0xff),
+       /*TDMOUT_A lane0~3 mute vale*/
+       TDM_MUTE("TDMOUT_A MUTE_VAL", TDMOUT_A, MUTE_VAL),
+       /*TDMOUT_A lane0~3 mute*/
+       TDM_MUTE("TDMOUT_A Lane0 Mute Channel", TDMOUT_A, MUTE0),
+       TDM_MUTE("TDMOUT_A Lane1 Mute Channel", TDMOUT_A, MUTE1),
+       TDM_MUTE("TDMOUT_A Lane2 Mute Channel", TDMOUT_A, MUTE2),
+       TDM_MUTE("TDMOUT_A Lane3 Mute Channel", TDMOUT_A, MUTE3),
+       /*TDMOUT_A lane0~3 mixer*/
+       SND_MIX("TDMOUT_A Lane0 Mixer Channel",
+               TDMOUT_A, lane0_mixer_enum, 20, 0x1),
+       SND_MIX("TDMOUT_A Lane1 Mixer Channel",
+               TDMOUT_A, lane1_mixer_enum, 21, 0x1),
+       SND_MIX("TDMOUT_A Lane2 Mixer Channel",
+               TDMOUT_A, lane2_mixer_enum, 22, 0x1),
+       SND_MIX("TDMOUT_A Lane3 Mixer Channel",
+               TDMOUT_A, lane3_mixer_enum, 23, 0x1),
+
+       /*TDMOUT_B swap*/
+       SND_SWAP("TDMOUT_B Lane0 Left Channel Swap",
+               TDMOUT_B, out_swap_channel_enum, 0, 0x7),
+       SND_SWAP("TDMOUT_B Lane0 Right Channel Swap",
+               TDMOUT_B, out_swap_channel_enum, 4, 0x7),
+       SND_SWAP("TDMOUT_B Lane1 Left Channel Swap",
+               TDMOUT_B, out_swap_channel_enum, 8, 0x7),
+       SND_SWAP("TDMOUT_B Lane1 Right Channel Swap",
+               TDMOUT_B, out_swap_channel_enum, 12, 0x7),
+       SND_SWAP("TDMOUT_B Lane2 Left Channel Swap",
+               TDMOUT_B, out_swap_channel_enum, 16, 0x7),
+       SND_SWAP("TDMOUT_B Lane2 Right Channel Swap",
+               TDMOUT_B, out_swap_channel_enum, 20, 0x7),
+       SND_SWAP("TDMOUT_B Lane3 Left Channel Swap",
+               TDMOUT_B, out_swap_channel_enum, 24, 0x7),
+       SND_SWAP("TDMOUT_B Lane3 Right Channel Swap",
+               TDMOUT_B, out_swap_channel_enum, 28, 0x7),
+       /*TDMOUT_B mask value*/
+       TDM_MASK("TDMOUT_B Lane0 MASK_VAL", TDMOUT_B, MASK_VAL),
+       /*TDMOUT_B lane0~3 mask*/
+       TDM_MASK("TDMOUT_B Lane0 Channel Mask", TDMOUT_B, MASK0),
+       TDM_MASK("TDMOUT_B Lane1 Channel Mask", TDMOUT_B, MASK1),
+       TDM_MASK("TDMOUT_B Lane2 Channel Mask", TDMOUT_B, MASK2),
+       TDM_MASK("TDMOUT_B Lane3 Channel Mask", TDMOUT_B, MASK3),
+       /*TDMOUT_B gain, enable data * gain*/
+       TDM_GAIN("TDMOUT_B GAIN Enable", TDMOUT_B, CTRL1, 26, 0x1),
+       TDM_GAIN("TDMOUT_B GAIN CH0", TDMOUT_B, GAIN0, 0, 0xff),
+       TDM_GAIN("TDMOUT_B GAIN CH1", TDMOUT_B, GAIN0, 8, 0xff),
+       TDM_GAIN("TDMOUT_B GAIN CH2", TDMOUT_B, GAIN0, 16, 0xff),
+       TDM_GAIN("TDMOUT_B GAIN CH3", TDMOUT_B, GAIN0, 24, 0xff),
+       TDM_GAIN("TDMOUT_B GAIN CH4", TDMOUT_B, GAIN1, 0, 0xff),
+       TDM_GAIN("TDMOUT_B GAIN CH5", TDMOUT_B, GAIN1, 8, 0xff),
+       TDM_GAIN("TDMOUT_B GAIN CH6", TDMOUT_B, GAIN1, 16, 0xff),
+       TDM_GAIN("TDMOUT_B GAIN CH7", TDMOUT_B, GAIN1, 24, 0xff),
+       /*TDMOUT_B lane0~3 mute vale*/
+       TDM_MUTE("TDMOUT_B MUTE_VAL", TDMOUT_B, MUTE_VAL),
+       /*TDMOUT_B lane0~3 mute*/
+       TDM_MUTE("TDMOUT_B Lane0 Mute Channel", TDMOUT_B, MUTE0),
+       TDM_MUTE("TDMOUT_B Lane1 Mute Channel", TDMOUT_B, MUTE1),
+       TDM_MUTE("TDMOUT_B Lane2 Mute Channel", TDMOUT_B, MUTE2),
+       TDM_MUTE("TDMOUT_B Lane3 Mute Channel", TDMOUT_B, MUTE3),
+       /*TDMOUT_B lane0~3 mixer*/
+       SND_MIX("TDMOUT_B Lane0 Mixer Channel",
+               TDMOUT_B, lane0_mixer_enum, 20, 0x1),
+       SND_MIX("TDMOUT_B Lane1 Mixer Channel",
+               TDMOUT_B, lane1_mixer_enum, 21, 0x1),
+       SND_MIX("TDMOUT_B Lane2 Mixer Channel",
+               TDMOUT_B, lane2_mixer_enum, 22, 0x1),
+       SND_MIX("TDMOUT_B Lane3 Mixer Channel",
+               TDMOUT_B, lane3_mixer_enum, 23, 0x1),
+
+       /*TDMOUT_C swap*/
+       SND_SWAP("TDMOUT_C Lane0 Left Channel Swap",
+               TDMOUT_C, out_swap_channel_enum, 0, 0x7),
+       SND_SWAP("TDMOUT_C Lane0 Right Channel Swap",
+               TDMOUT_C, out_swap_channel_enum, 4, 0x7),
+       SND_SWAP("TDMOUT_C Lane1 Left Channel Swap",
+               TDMOUT_C, out_swap_channel_enum, 8, 0x7),
+       SND_SWAP("TDMOUT_C Lane1 Right Channel Swap",
+               TDMOUT_C, out_swap_channel_enum, 12, 0x7),
+       SND_SWAP("TDMOUT_C Lane2 Left Channel Swap",
+               TDMOUT_C, out_swap_channel_enum, 16, 0x7),
+       SND_SWAP("TDMOUT_C Lane2 Right Channel Swap",
+               TDMOUT_C, out_swap_channel_enum, 20, 0x7),
+       SND_SWAP("TDMOUT_C Lane3 Left Channel Swap",
+               TDMOUT_C, out_swap_channel_enum, 24, 0x7),
+       SND_SWAP("TDMOUT_C Lane3 Right Channel Swap",
+               TDMOUT_C, out_swap_channel_enum, 28, 0x7),
+       /*TDMOUT_C mask value*/
+       TDM_MASK("TDMOUT_C Lane0 MASK_VAL", TDMOUT_C, MASK_VAL),
+       /*TDMOUT_C lane0~3 mask*/
+       TDM_MASK("TDMOUT_C Lane0 Channel Mask", TDMOUT_C, MASK0),
+       TDM_MASK("TDMOUT_C Lane1 Channel Mask", TDMOUT_C, MASK1),
+       TDM_MASK("TDMOUT_C Lane2 Channel Mask", TDMOUT_C, MASK2),
+       TDM_MASK("TDMOUT_C Lane3 Channel Mask", TDMOUT_C, MASK3),
+       /*TDMOUT_C gain, enable data * gain*/
+       TDM_GAIN("TDMOUT_C GAIN Enable", TDMOUT_C, CTRL1, 26, 0x1),
+       TDM_GAIN("TDMOUT_C GAIN CH0", TDMOUT_C, GAIN0, 0, 0xff),
+       TDM_GAIN("TDMOUT_C GAIN CH1", TDMOUT_C, GAIN0, 8, 0xff),
+       TDM_GAIN("TDMOUT_C GAIN CH2", TDMOUT_C, GAIN0, 16, 0xff),
+       TDM_GAIN("TDMOUT_C GAIN CH3", TDMOUT_C, GAIN0, 24, 0xff),
+       TDM_GAIN("TDMOUT_C GAIN CH4", TDMOUT_C, GAIN1, 0, 0xff),
+       TDM_GAIN("TDMOUT_C GAIN CH5", TDMOUT_C, GAIN1, 8, 0xff),
+       TDM_GAIN("TDMOUT_C GAIN CH6", TDMOUT_C, GAIN1, 16, 0xff),
+       TDM_GAIN("TDMOUT_C GAIN CH7", TDMOUT_C, GAIN1, 24, 0xff),
+       /*TDMOUT_C lane0~3 mute vale*/
+       TDM_MUTE("TDMOUT_C MUTE_VAL", TDMOUT_C, MUTE_VAL),
+       /*TDMOUT_C lane0~3 mute*/
+       TDM_MUTE("TDMOUT_C Lane0 Mute Channel", TDMOUT_C, MUTE0),
+       TDM_MUTE("TDMOUT_C Lane1 Mute Channel", TDMOUT_C, MUTE1),
+       TDM_MUTE("TDMOUT_C Lane2 Mute Channel", TDMOUT_C, MUTE2),
+       TDM_MUTE("TDMOUT_C Lane3 Mute Channel", TDMOUT_C, MUTE3),
+       /*TDMOUT_C lane0~3 mixer*/
+       SND_MIX("TDMOUT_C Lane0 Mixer Channel",
+               TDMOUT_C, lane0_mixer_enum, 20, 0x1),
+       SND_MIX("TDMOUT_C Lane1 Mixer Channel",
+               TDMOUT_C, lane1_mixer_enum, 21, 0x1),
+       SND_MIX("TDMOUT_C Lane2 Mixer Channel",
+               TDMOUT_C, lane2_mixer_enum, 22, 0x1),
+       SND_MIX("TDMOUT_C Lane3 Mixer Channel",
+               TDMOUT_C, lane3_mixer_enum, 23, 0x1),
+
+       /* SPDIFIN sample rate */
+       SOC_ENUM_EXT("SPDIFIN Sample Rate",
+                        spdifin_sample_rate_enum,
+                        spdifin_sample_rate_get,
+                        NULL),
+       /* SPDIFIN Channel Status */
+       SPDIFIN_CHSTATUS("SPDIFIN Channel Status",
+                               spdif_channel_status_enum),
+
+       /*SPDIFOUT swap*/
+       SND_SWAP("SPDIFOUT Lane0 Left Channel Swap",
+               SPDIFOUT, out_swap_channel_enum, 0, 0x7),
+       SND_SWAP("SPDIFOUT Lane0 Right Channel Swap",
+               SPDIFOUT, out_swap_channel_enum, 4, 0x7),
+       /*SPDIFOUT mixer*/
+       SND_MIX("SPDIFOUT Mixer Channel",
+               SPDIFOUT, lane0_mixer_enum, 23, 0x1),
+       /* SPDIFIN Channel Status */
+       SPDIFOUT_CHSTATUS("SPDIFOUT Channel Status",
+                               spdif_channel_status_enum),
 };
 
 
@@ -299,7 +1027,7 @@ int loopback_prepare(
        }
 
        datain_cfg.ext_signed    = 0;
-       datain_cfg.chnum         = runtime->channels;
+       datain_cfg.chnum         = lb_cfg->datain_chnum;
        datain_cfg.chmask        = lb_cfg->datain_chmask;
        ddrdata.combined_type    = datain_toddr_type;
        ddrdata.msb              = datain_msb;
index 04a0124..c85823f 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <linux/types.h>
 #include <sound/soc.h>
+#include <sound/tlv.h>
 
 /* datain src
  * [4]: pdmin;
index 5aa494b..6af38e9 100644 (file)
@@ -223,7 +223,8 @@ int aml_card_prepare(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct aml_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
 
-       loopback_prepare(substream, &priv->lb_cfg);
+       if (loopback_is_enable())
+               loopback_prepare(substream, &priv->lb_cfg);
 
        return 0;
 }
index cb8719b..84477f4 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <sound/soc.h>
 
+#include "iomap.h"
 #include "spdif_hw.h"
 
 void aml_spdif_enable(
@@ -175,3 +176,28 @@ void aml_spdif_fifo_ctrl(
        }
 
 }
+
+int spdifin_get_mode(void)
+{
+       int mode_val = audiobus_read(EE_AUDIO_SPDIFIN_STAT0);
+
+       mode_val >>= 28;
+       mode_val &= 0x7;
+
+       return mode_val;
+}
+
+int spdif_get_channel_status(int reg)
+{
+       return audiobus_read(reg);
+}
+
+void spdifin_set_channel_status(int ch, int bits)
+{
+       int ch_status_sel = (ch << 3 | bits) & 0xf;
+
+       /*which channel status would be got*/
+       audiobus_update_bits(EE_AUDIO_SPDIFIN_CTRL0,
+               0xf << 8,
+               ch_status_sel << 8);
+}
index 696483f..3e7fc80 100644 (file)
@@ -38,4 +38,10 @@ extern void aml_spdif_fifo_ctrl(
        struct aml_audio_controller *actrl,
        int bitwidth,
        int stream);
+
+extern int spdifin_get_mode(void);
+
+extern int spdif_get_channel_status(int reg);
+
+extern void spdifin_set_channel_status(int ch, int bits);
 #endif
index 731c327..20a1f0b 100644 (file)
@@ -101,7 +101,7 @@ static const struct snd_pcm_hardware aml_tdm_hardware = {
 
        .rate_min = 8000,
        .rate_max = 48000,
-       .channels_min = 2,
+       .channels_min = 1,
        .channels_max = 32,
 };
 
index ddc8320..4444a3f 100644 (file)
@@ -84,14 +84,14 @@ struct snd_soc_dai_driver dummy_codec_dai = {
        .playback = {
                     .stream_name = "HIFI Playback",
                     .channels_min = 1,
-                    .channels_max = 8,
+                    .channels_max = 32,
                     .rates = DUMMY_CODEC_RATES,
                     .formats = DUMMY_CODEC_FORMATS,
                     },
        .capture = {
                    .stream_name = "HIFI Capture",
                    .channels_min = 1,
-                   .channels_max = 8,
+                   .channels_max = 32,
                    .rates = DUMMY_CODEC_RATES,
                    .formats = DUMMY_CODEC_FORMATS,
                    },