A1's internal codec is very close to t9015. The main difference, that it
has ADC. This commit introduces support for capturing from it.
Signed-off-by: Jan Dakinevich <jan.dakinevich@salutedevices.com>
---
sound/soc/meson/t9015.c | 259 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 259 insertions(+)
diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c
index 48f6767bd858..365955bfeb78 100644
--- a/sound/soc/meson/t9015.c
+++ b/sound/soc/meson/t9015.c
@@ -19,16 +19,33 @@
#define LOLP_EN 3
#define DACR_EN 4
#define DACL_EN 5
+#define ADCR_EN 6
+#define ADCL_EN 7
+#define PGAR_ZCD_EN 8
+#define PGAL_ZCD_EN 9
+#define PGAR_EN 10
+#define PGAL_EN 11
+#define ADCR_INV 16
+#define ADCL_INV 17
+#define ADCR_SRC 18
+#define ADCL_SRC 19
#define DACR_INV 20
#define DACL_INV 21
#define DACR_SRC 22
#define DACL_SRC 23
+#define ADC_DEM_EN 26
+#define ADC_FILTER_MODE 28
+#define ADC_FILTER_EN 29
#define REFP_BUF_EN BIT(12)
#define BIAS_CURRENT_EN BIT(13)
#define VMID_GEN_FAST BIT(14)
#define VMID_GEN_EN BIT(15)
#define I2S_MODE BIT(30)
#define VOL_CTRL0 0x04
+#define PGAR_VC 0
+#define PGAL_VC 8
+#define ADCR_VC 16
+#define ADCL_VC 24
#define GAIN_H 31
#define GAIN_L 23
#define VOL_CTRL1 0x08
@@ -46,6 +63,28 @@
#define LOLN_POL 8
#define LOLP_POL 12
#define POWER_CFG 0x10
+#define LINEIN_CFG 0x14
+#define MICBIAS_LEVEL 0
+#define MICBIAS_EN 3
+#define PGAR_CTVMN 8
+#define PGAR_CTVMP 9
+#define PGAL_CTVMN 10
+#define PGAL_CTVMP 11
+#define PGAR_CTVIN 12
+#define PGAR_CTVIP 13
+#define PGAL_CTVIN 14
+#define PGAL_CTVIP 15
+
+#define PGAR_MASK (BIT(PGAR_CTVMP) | BIT(PGAR_CTVMN) | \
+ BIT(PGAR_CTVIP) | BIT(PGAR_CTVIN))
+#define PGAR_DIFF (BIT(PGAR_CTVIP) | BIT(PGAR_CTVIN))
+#define PGAR_POSITIVE (BIT(PGAR_CTVIP) | BIT(PGAR_CTVMN))
+#define PGAR_NEGATIVE (BIT(PGAR_CTVIN) | BIT(PGAR_CTVMP))
+#define PGAL_MASK (BIT(PGAL_CTVMP) | BIT(PGAL_CTVMN) | \
+ BIT(PGAL_CTVIP) | BIT(PGAL_CTVIN))
+#define PGAL_DIFF (BIT(PGAL_CTVIP) | BIT(PGAL_CTVIN))
+#define PGAL_POSITIVE (BIT(PGAL_CTVIP) | BIT(PGAL_CTVMN))
+#define PGAL_NEGATIVE (BIT(PGAL_CTVIN) | BIT(PGAL_CTVMP))
struct t9015 {
struct regulator *avdd;
@@ -103,6 +142,31 @@ static struct snd_soc_dai_driver t9015_dai = {
.ops = &t9015_dai_ops,
};
+static struct snd_soc_dai_driver a1_t9015_dai = {
+ .name = "t9015-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = (SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = (SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ },
+ .ops = &t9015_dai_ops,
+};
+
static const DECLARE_TLV_DB_MINMAX_MUTE(dac_vol_tlv, -9525, 0);
static const char * const ramp_rate_txt[] = { "Fast", "Slow" };
@@ -179,6 +243,166 @@ static const struct snd_soc_dapm_route t9015_dapm_routes[] = {
{ "LOLP", NULL, "Left+ Driver", },
};
+static const char * const a1_right_driver_txt[] = { "None", "Right DAC",
+ "Left DAC Inverted" };
+static const unsigned int a1_right_driver_values[] = { 0, 2, 4 };
+
+static const char * const a1_left_driver_txt[] = { "None", "Left DAC",
+ "Right DAC Inverted" };
+static const unsigned int a1_left_driver_values[] = { 0, 2, 4 };
+
+static SOC_VALUE_ENUM_SINGLE_DECL(a1_right_driver, LINEOUT_CFG, 12, 0x7,
+ a1_right_driver_txt, a1_right_driver_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(a1_left_driver, LINEOUT_CFG, 4, 0x7,
+ a1_left_driver_txt, a1_left_driver_values);
+
+static const struct snd_kcontrol_new a1_right_driver_mux =
+ SOC_DAPM_ENUM("Right Driver+ Source", a1_right_driver);
+static const struct snd_kcontrol_new a1_left_driver_mux =
+ SOC_DAPM_ENUM("Left Driver+ Source", a1_left_driver);
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(a1_adc_vol_tlv, -29625, 0);
+static const DECLARE_TLV_DB_MINMAX_MUTE(a1_adc_pga_vol_tlv, -1200, 0);
+
+static const char * const a1_adc_right_txt[] = { "Right", "Left" };
+static SOC_ENUM_SINGLE_DECL(a1_adc_right, BLOCK_EN, ADCR_SRC, a1_adc_right_txt);
+
+static const char * const a1_adc_left_txt[] = { "Left", "Right" };
+static SOC_ENUM_SINGLE_DECL(a1_adc_left, BLOCK_EN, ADCL_SRC, a1_adc_left_txt);
+
+static const struct snd_kcontrol_new a1_adc_right_mux =
+ SOC_DAPM_ENUM("ADC Right Source", a1_adc_right);
+static const struct snd_kcontrol_new a1_adc_left_mux =
+ SOC_DAPM_ENUM("ADC Left Source", a1_adc_left);
+
+static const char * const a1_adc_filter_mode_txt[] = { "Voice", "HiFi"};
+static SOC_ENUM_SINGLE_DECL(a1_adc_filter_mode, BLOCK_EN, ADC_FILTER_MODE,
+ a1_adc_filter_mode_txt);
+
+static const char * const a1_adc_mic_bias_level_txt[] = { "2.0V", "2.1V",
+ "2.3V", "2.5V", "2.8V" };
+static const unsigned int a1_adc_mic_bias_level_values[] = { 0, 1, 2, 3, 7 };
+static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_mic_bias_level,
+ LINEIN_CFG, MICBIAS_LEVEL, 0x7,
+ a1_adc_mic_bias_level_txt,
+ a1_adc_mic_bias_level_values);
+
+static const char * const a1_adc_pga_txt[] = { "None", "Differential",
+ "Positive", "Negative" };
+static const unsigned int a1_adc_pga_right_values[] = { 0, PGAR_DIFF,
+ PGAR_POSITIVE, PGAR_NEGATIVE };
+static const unsigned int a1_adc_pga_left_values[] = { 0, PGAL_DIFF,
+ PGAL_POSITIVE, PGAL_NEGATIVE };
+
+static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_pga_right, LINEIN_CFG, 0, PGAR_MASK,
+ a1_adc_pga_txt, a1_adc_pga_right_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_pga_left, LINEIN_CFG, 0, PGAL_MASK,
+ a1_adc_pga_txt, a1_adc_pga_left_values);
+
+static const struct snd_kcontrol_new a1_adc_pga_right_mux =
+ SOC_DAPM_ENUM("ADC PGA Right Source", a1_adc_pga_right);
+static const struct snd_kcontrol_new a1_adc_pga_left_mux =
+ SOC_DAPM_ENUM("ADC PGA Left Source", a1_adc_pga_left);
+
+static const struct snd_kcontrol_new a1_t9015_snd_controls[] = {
+ /* Volume Controls */
+ SOC_ENUM("Playback Channel Mode", mono_enum),
+ SOC_SINGLE("Playback Switch", VOL_CTRL1, DAC_SOFT_MUTE, 1, 1),
+ SOC_DOUBLE_TLV("Playback Volume", VOL_CTRL1, DACL_VC, DACR_VC,
+ 0xff, 0, dac_vol_tlv),
+
+ /* Ramp Controls */
+ SOC_ENUM("Ramp Rate", ramp_rate_enum),
+ SOC_SINGLE("Volume Ramp Switch", VOL_CTRL1, VC_RAMP_MODE, 1, 0),
+ SOC_SINGLE("Mute Ramp Switch", VOL_CTRL1, MUTE_MODE, 1, 0),
+ SOC_SINGLE("Unmute Ramp Switch", VOL_CTRL1, UNMUTE_MODE, 1, 0),
+
+ /* ADC Controls */
+ SOC_DOUBLE_TLV("ADC Volume", VOL_CTRL0, ADCL_VC, ADCR_VC,
+ 0x7f, 0, a1_adc_vol_tlv),
+ SOC_SINGLE("ADC Filter Switch", BLOCK_EN, ADC_FILTER_EN, 1, 0),
+ SOC_ENUM("ADC Filter Mode", a1_adc_filter_mode),
+ SOC_SINGLE("ADC Mic Bias Switch", LINEIN_CFG, MICBIAS_EN, 1, 0),
+ SOC_ENUM("ADC Mic Bias Level", a1_adc_mic_bias_level),
+ SOC_SINGLE("ADC DEM Switch", BLOCK_EN, ADC_DEM_EN, 1, 0),
+ SOC_DOUBLE_TLV("ADC PGA Volume", VOL_CTRL0, PGAR_VC, PGAL_VC,
+ 0x1f, 0, a1_adc_pga_vol_tlv),
+ SOC_DOUBLE("ADC PGA Zero Cross-detection Switch", BLOCK_EN,
+ PGAL_ZCD_EN, PGAR_ZCD_EN, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget a1_t9015_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("Right IN", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Left IN", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("Right DAC Sel", SND_SOC_NOPM, 0, 0,
+ &t9015_right_dac_mux),
+ SND_SOC_DAPM_MUX("Left DAC Sel", SND_SOC_NOPM, 0, 0,
+ &t9015_left_dac_mux),
+ SND_SOC_DAPM_DAC("Right DAC", NULL, BLOCK_EN, DACR_EN, 0),
+ SND_SOC_DAPM_DAC("Left DAC", NULL, BLOCK_EN, DACL_EN, 0),
+ SND_SOC_DAPM_MUX("Right+ Driver Sel", SND_SOC_NOPM, 0, 0,
+ &a1_right_driver_mux),
+ SND_SOC_DAPM_MUX("Left+ Driver Sel", SND_SOC_NOPM, 0, 0,
+ &a1_left_driver_mux),
+ SND_SOC_DAPM_OUT_DRV("Right+ Driver", BLOCK_EN, LORP_EN, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Left+ Driver", BLOCK_EN, LOLP_EN, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("LORP"),
+ SND_SOC_DAPM_OUTPUT("LOLP"),
+
+ SND_SOC_DAPM_INPUT("ADC IN Right"),
+ SND_SOC_DAPM_INPUT("ADC IN Left"),
+ SND_SOC_DAPM_MUX("ADC PGA Right Sel", SND_SOC_NOPM, 0, 0,
+ &a1_adc_pga_right_mux),
+ SND_SOC_DAPM_MUX("ADC PGA Left Sel", SND_SOC_NOPM, 0, 0,
+ &a1_adc_pga_left_mux),
+ SND_SOC_DAPM_PGA("ADC PGA Right", BLOCK_EN, PGAR_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC PGA Left", BLOCK_EN, PGAL_EN, 0, NULL, 0),
+ SND_SOC_DAPM_ADC("ADC Right", NULL, BLOCK_EN, ADCR_EN, 0),
+ SND_SOC_DAPM_ADC("ADC Left", NULL, BLOCK_EN, ADCL_EN, 0),
+ SND_SOC_DAPM_MUX("ADC Right Sel", SND_SOC_NOPM, 0, 0, &a1_adc_right_mux),
+ SND_SOC_DAPM_MUX("ADC Left Sel", SND_SOC_NOPM, 0, 0, &a1_adc_left_mux),
+ SND_SOC_DAPM_AIF_OUT("ADC OUT Right", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("ADC OUT Left", NULL, 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route a1_t9015_dapm_routes[] = {
+ { "Right IN", NULL, "Playback" },
+ { "Left IN", NULL, "Playback" },
+ { "Right DAC Sel", "Right", "Right IN" },
+ { "Right DAC Sel", "Left", "Left IN" },
+ { "Left DAC Sel", "Right", "Right IN" },
+ { "Left DAC Sel", "Left", "Left IN" },
+ { "Right DAC", NULL, "Right DAC Sel" },
+ { "Left DAC", NULL, "Left DAC Sel" },
+ { "Right+ Driver Sel", "Right DAC", "Right DAC" },
+ { "Right+ Driver Sel", "Left DAC Inverted", "Right DAC" },
+ { "Left+ Driver Sel", "Left DAC", "Left DAC" },
+ { "Left+ Driver Sel", "Right DAC Inverted", "Left DAC" },
+ { "Right+ Driver", NULL, "Right+ Driver Sel" },
+ { "Left+ Driver", NULL, "Left+ Driver Sel" },
+ { "LORP", NULL, "Right+ Driver", },
+ { "LOLP", NULL, "Left+ Driver", },
+
+ { "ADC PGA Right Sel", "Differential", "ADC IN Right" },
+ { "ADC PGA Right Sel", "Positive", "ADC IN Right" },
+ { "ADC PGA Right Sel", "Negative", "ADC IN Right" },
+ { "ADC PGA Left Sel", "Differential", "ADC IN Left" },
+ { "ADC PGA Left Sel", "Positive", "ADC IN Left" },
+ { "ADC PGA Left Sel", "Negative", "ADC IN Left" },
+ { "ADC PGA Right", NULL, "ADC PGA Right Sel" },
+ { "ADC PGA Left", NULL, "ADC PGA Left Sel" },
+ { "ADC Right", NULL, "ADC PGA Right" },
+ { "ADC Left", NULL, "ADC PGA Left" },
+ { "ADC Right Sel", "Right", "ADC Right" },
+ { "ADC Right Sel", "Left", "ADC Left" },
+ { "ADC Left Sel", "Right", "ADC Right" },
+ { "ADC Left Sel", "Left", "ADC Left" },
+ { "ADC OUT Right", NULL, "ADC Right Sel" },
+ { "ADC OUT Left", NULL, "ADC Left Sel" },
+ { "Capture", NULL, "ADC OUT Right" },
+ { "Capture", NULL, "ADC OUT Left" },
+};
+
static int t9015_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
@@ -241,6 +465,18 @@ static int t9015_component_probe(struct snd_soc_component *component)
return 0;
}
+static int a1_t9015_component_probe(struct snd_soc_component *component)
+{
+ /*
+ * This configuration was stealed from original Amlogic's driver to
+ * reproduce the behavior of the driver more accurately. However, it is
+ * not known for certain what it actually affects.
+ */
+ snd_soc_component_write(component, POWER_CFG, 0x00010000);
+
+ return 0;
+}
+
static const struct snd_soc_component_driver t9015_codec_driver = {
.probe = t9015_component_probe,
.set_bias_level = t9015_set_bias_level,
@@ -254,6 +490,19 @@ static const struct snd_soc_component_driver t9015_codec_driver = {
.endianness = 1,
};
+static const struct snd_soc_component_driver a1_t9015_codec_driver = {
+ .probe = a1_t9015_component_probe,
+ .set_bias_level = t9015_set_bias_level,
+ .controls = a1_t9015_snd_controls,
+ .num_controls = ARRAY_SIZE(a1_t9015_snd_controls),
+ .dapm_widgets = a1_t9015_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(a1_t9015_dapm_widgets),
+ .dapm_routes = a1_t9015_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(a1_t9015_dapm_routes),
+ .suspend_bias_off = 1,
+ .endianness = 1,
+};
+
static int t9015_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -315,11 +564,21 @@ static const struct t9015_match_data t9015_match_data = {
.max_register = POWER_CFG,
};
+static const struct t9015_match_data a1_t9015_match_data = {
+ .component_drv = &a1_t9015_codec_driver,
+ .dai_drv = &a1_t9015_dai,
+ .max_register = LINEIN_CFG,
+};
+
static const struct of_device_id t9015_ids[] __maybe_unused = {
{
.compatible = "amlogic,t9015",
.data = &t9015_match_data,
},
+ {
+ .compatible = "amlogic,t9015-a1",
+ .data = &a1_t9015_match_data,
+ },
{ }
};
MODULE_DEVICE_TABLE(of, t9015_ids);
--
2.34.1
On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <jan.dakinevich@salutedevices.com> wrote:
> A1's internal codec is very close to t9015. The main difference, that it
> has ADC. This commit introduces support for capturing from it.
This is mis-leading.
It does not look like the change is A1 specific but rather a extension
of the support for t9015. It also mixes several different topics like line
configuration, capture support, etc ...
Again, the t9015 changes should be a separated series from the rest, and
there should be one patch per topic.
As Mark, if something is meant to be configured based on the HW layout,
then there a good change a kcontrol is not appropriate, and this should
rather be part of the platform description, like DT.
It was also suggested here:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/sound/soc/meson/t9015.c?h=v6.8#n298
>
> Signed-off-by: Jan Dakinevich <jan.dakinevich@salutedevices.com>
> ---
> sound/soc/meson/t9015.c | 259 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 259 insertions(+)
>
> diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c
> index 48f6767bd858..365955bfeb78 100644
> --- a/sound/soc/meson/t9015.c
> +++ b/sound/soc/meson/t9015.c
> @@ -19,16 +19,33 @@
> #define LOLP_EN 3
> #define DACR_EN 4
> #define DACL_EN 5
> +#define ADCR_EN 6
> +#define ADCL_EN 7
> +#define PGAR_ZCD_EN 8
> +#define PGAL_ZCD_EN 9
> +#define PGAR_EN 10
> +#define PGAL_EN 11
> +#define ADCR_INV 16
> +#define ADCL_INV 17
> +#define ADCR_SRC 18
> +#define ADCL_SRC 19
> #define DACR_INV 20
> #define DACL_INV 21
> #define DACR_SRC 22
> #define DACL_SRC 23
> +#define ADC_DEM_EN 26
> +#define ADC_FILTER_MODE 28
> +#define ADC_FILTER_EN 29
> #define REFP_BUF_EN BIT(12)
> #define BIAS_CURRENT_EN BIT(13)
> #define VMID_GEN_FAST BIT(14)
> #define VMID_GEN_EN BIT(15)
> #define I2S_MODE BIT(30)
> #define VOL_CTRL0 0x04
> +#define PGAR_VC 0
> +#define PGAL_VC 8
> +#define ADCR_VC 16
> +#define ADCL_VC 24
> #define GAIN_H 31
> #define GAIN_L 23
> #define VOL_CTRL1 0x08
> @@ -46,6 +63,28 @@
> #define LOLN_POL 8
> #define LOLP_POL 12
> #define POWER_CFG 0x10
> +#define LINEIN_CFG 0x14
> +#define MICBIAS_LEVEL 0
> +#define MICBIAS_EN 3
> +#define PGAR_CTVMN 8
> +#define PGAR_CTVMP 9
> +#define PGAL_CTVMN 10
> +#define PGAL_CTVMP 11
> +#define PGAR_CTVIN 12
> +#define PGAR_CTVIP 13
> +#define PGAL_CTVIN 14
> +#define PGAL_CTVIP 15
> +
> +#define PGAR_MASK (BIT(PGAR_CTVMP) | BIT(PGAR_CTVMN) | \
> + BIT(PGAR_CTVIP) | BIT(PGAR_CTVIN))
> +#define PGAR_DIFF (BIT(PGAR_CTVIP) | BIT(PGAR_CTVIN))
> +#define PGAR_POSITIVE (BIT(PGAR_CTVIP) | BIT(PGAR_CTVMN))
> +#define PGAR_NEGATIVE (BIT(PGAR_CTVIN) | BIT(PGAR_CTVMP))
> +#define PGAL_MASK (BIT(PGAL_CTVMP) | BIT(PGAL_CTVMN) | \
> + BIT(PGAL_CTVIP) | BIT(PGAL_CTVIN))
> +#define PGAL_DIFF (BIT(PGAL_CTVIP) | BIT(PGAL_CTVIN))
> +#define PGAL_POSITIVE (BIT(PGAL_CTVIP) | BIT(PGAL_CTVMN))
> +#define PGAL_NEGATIVE (BIT(PGAL_CTVIN) | BIT(PGAL_CTVMP))
>
> struct t9015 {
> struct regulator *avdd;
> @@ -103,6 +142,31 @@ static struct snd_soc_dai_driver t9015_dai = {
> .ops = &t9015_dai_ops,
> };
>
> +static struct snd_soc_dai_driver a1_t9015_dai = {
> + .name = "t9015-hifi",
> + .playback = {
> + .stream_name = "Playback",
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_96000,
> + .formats = (SNDRV_PCM_FMTBIT_S8 |
> + SNDRV_PCM_FMTBIT_S16_LE |
> + SNDRV_PCM_FMTBIT_S20_LE |
> + SNDRV_PCM_FMTBIT_S24_LE),
> + },
> + .capture = {
> + .stream_name = "Capture",
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_96000,
> + .formats = (SNDRV_PCM_FMTBIT_S8 |
> + SNDRV_PCM_FMTBIT_S16_LE |
> + SNDRV_PCM_FMTBIT_S20_LE |
> + SNDRV_PCM_FMTBIT_S24_LE),
> + },
> + .ops = &t9015_dai_ops,
> +};
> +
> static const DECLARE_TLV_DB_MINMAX_MUTE(dac_vol_tlv, -9525, 0);
>
> static const char * const ramp_rate_txt[] = { "Fast", "Slow" };
> @@ -179,6 +243,166 @@ static const struct snd_soc_dapm_route t9015_dapm_routes[] = {
> { "LOLP", NULL, "Left+ Driver", },
> };
>
> +static const char * const a1_right_driver_txt[] = { "None", "Right DAC",
> + "Left DAC Inverted" };
> +static const unsigned int a1_right_driver_values[] = { 0, 2, 4 };
> +
> +static const char * const a1_left_driver_txt[] = { "None", "Left DAC",
> + "Right DAC Inverted" };
> +static const unsigned int a1_left_driver_values[] = { 0, 2, 4 };
> +
> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_right_driver, LINEOUT_CFG, 12, 0x7,
> + a1_right_driver_txt, a1_right_driver_values);
> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_left_driver, LINEOUT_CFG, 4, 0x7,
> + a1_left_driver_txt, a1_left_driver_values);
> +
> +static const struct snd_kcontrol_new a1_right_driver_mux =
> + SOC_DAPM_ENUM("Right Driver+ Source", a1_right_driver);
> +static const struct snd_kcontrol_new a1_left_driver_mux =
> + SOC_DAPM_ENUM("Left Driver+ Source", a1_left_driver);
> +
> +static const DECLARE_TLV_DB_MINMAX_MUTE(a1_adc_vol_tlv, -29625, 0);
> +static const DECLARE_TLV_DB_MINMAX_MUTE(a1_adc_pga_vol_tlv, -1200, 0);
> +
> +static const char * const a1_adc_right_txt[] = { "Right", "Left" };
> +static SOC_ENUM_SINGLE_DECL(a1_adc_right, BLOCK_EN, ADCR_SRC, a1_adc_right_txt);
> +
> +static const char * const a1_adc_left_txt[] = { "Left", "Right" };
> +static SOC_ENUM_SINGLE_DECL(a1_adc_left, BLOCK_EN, ADCL_SRC, a1_adc_left_txt);
> +
> +static const struct snd_kcontrol_new a1_adc_right_mux =
> + SOC_DAPM_ENUM("ADC Right Source", a1_adc_right);
> +static const struct snd_kcontrol_new a1_adc_left_mux =
> + SOC_DAPM_ENUM("ADC Left Source", a1_adc_left);
> +
> +static const char * const a1_adc_filter_mode_txt[] = { "Voice", "HiFi"};
> +static SOC_ENUM_SINGLE_DECL(a1_adc_filter_mode, BLOCK_EN, ADC_FILTER_MODE,
> + a1_adc_filter_mode_txt);
> +
> +static const char * const a1_adc_mic_bias_level_txt[] = { "2.0V", "2.1V",
> + "2.3V", "2.5V", "2.8V" };
> +static const unsigned int a1_adc_mic_bias_level_values[] = { 0, 1, 2, 3, 7 };
> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_mic_bias_level,
> + LINEIN_CFG, MICBIAS_LEVEL, 0x7,
> + a1_adc_mic_bias_level_txt,
> + a1_adc_mic_bias_level_values);
> +
> +static const char * const a1_adc_pga_txt[] = { "None", "Differential",
> + "Positive", "Negative" };
> +static const unsigned int a1_adc_pga_right_values[] = { 0, PGAR_DIFF,
> + PGAR_POSITIVE, PGAR_NEGATIVE };
> +static const unsigned int a1_adc_pga_left_values[] = { 0, PGAL_DIFF,
> + PGAL_POSITIVE, PGAL_NEGATIVE };
> +
> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_pga_right, LINEIN_CFG, 0, PGAR_MASK,
> + a1_adc_pga_txt, a1_adc_pga_right_values);
> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_pga_left, LINEIN_CFG, 0, PGAL_MASK,
> + a1_adc_pga_txt, a1_adc_pga_left_values);
> +
> +static const struct snd_kcontrol_new a1_adc_pga_right_mux =
> + SOC_DAPM_ENUM("ADC PGA Right Source", a1_adc_pga_right);
> +static const struct snd_kcontrol_new a1_adc_pga_left_mux =
> + SOC_DAPM_ENUM("ADC PGA Left Source", a1_adc_pga_left);
> +
> +static const struct snd_kcontrol_new a1_t9015_snd_controls[] = {
> + /* Volume Controls */
> + SOC_ENUM("Playback Channel Mode", mono_enum),
> + SOC_SINGLE("Playback Switch", VOL_CTRL1, DAC_SOFT_MUTE, 1, 1),
> + SOC_DOUBLE_TLV("Playback Volume", VOL_CTRL1, DACL_VC, DACR_VC,
> + 0xff, 0, dac_vol_tlv),
> +
> + /* Ramp Controls */
> + SOC_ENUM("Ramp Rate", ramp_rate_enum),
> + SOC_SINGLE("Volume Ramp Switch", VOL_CTRL1, VC_RAMP_MODE, 1, 0),
> + SOC_SINGLE("Mute Ramp Switch", VOL_CTRL1, MUTE_MODE, 1, 0),
> + SOC_SINGLE("Unmute Ramp Switch", VOL_CTRL1, UNMUTE_MODE, 1, 0),
> +
> + /* ADC Controls */
> + SOC_DOUBLE_TLV("ADC Volume", VOL_CTRL0, ADCL_VC, ADCR_VC,
> + 0x7f, 0, a1_adc_vol_tlv),
> + SOC_SINGLE("ADC Filter Switch", BLOCK_EN, ADC_FILTER_EN, 1, 0),
> + SOC_ENUM("ADC Filter Mode", a1_adc_filter_mode),
> + SOC_SINGLE("ADC Mic Bias Switch", LINEIN_CFG, MICBIAS_EN, 1, 0),
> + SOC_ENUM("ADC Mic Bias Level", a1_adc_mic_bias_level),
> + SOC_SINGLE("ADC DEM Switch", BLOCK_EN, ADC_DEM_EN, 1, 0),
> + SOC_DOUBLE_TLV("ADC PGA Volume", VOL_CTRL0, PGAR_VC, PGAL_VC,
> + 0x1f, 0, a1_adc_pga_vol_tlv),
> + SOC_DOUBLE("ADC PGA Zero Cross-detection Switch", BLOCK_EN,
> + PGAL_ZCD_EN, PGAR_ZCD_EN, 1, 0),
> +};
> +
> +static const struct snd_soc_dapm_widget a1_t9015_dapm_widgets[] = {
> + SND_SOC_DAPM_AIF_IN("Right IN", NULL, 0, SND_SOC_NOPM, 0, 0),
> + SND_SOC_DAPM_AIF_IN("Left IN", NULL, 0, SND_SOC_NOPM, 0, 0),
> + SND_SOC_DAPM_MUX("Right DAC Sel", SND_SOC_NOPM, 0, 0,
> + &t9015_right_dac_mux),
> + SND_SOC_DAPM_MUX("Left DAC Sel", SND_SOC_NOPM, 0, 0,
> + &t9015_left_dac_mux),
> + SND_SOC_DAPM_DAC("Right DAC", NULL, BLOCK_EN, DACR_EN, 0),
> + SND_SOC_DAPM_DAC("Left DAC", NULL, BLOCK_EN, DACL_EN, 0),
> + SND_SOC_DAPM_MUX("Right+ Driver Sel", SND_SOC_NOPM, 0, 0,
> + &a1_right_driver_mux),
> + SND_SOC_DAPM_MUX("Left+ Driver Sel", SND_SOC_NOPM, 0, 0,
> + &a1_left_driver_mux),
> + SND_SOC_DAPM_OUT_DRV("Right+ Driver", BLOCK_EN, LORP_EN, 0, NULL, 0),
> + SND_SOC_DAPM_OUT_DRV("Left+ Driver", BLOCK_EN, LOLP_EN, 0, NULL, 0),
> + SND_SOC_DAPM_OUTPUT("LORP"),
> + SND_SOC_DAPM_OUTPUT("LOLP"),
> +
> + SND_SOC_DAPM_INPUT("ADC IN Right"),
> + SND_SOC_DAPM_INPUT("ADC IN Left"),
> + SND_SOC_DAPM_MUX("ADC PGA Right Sel", SND_SOC_NOPM, 0, 0,
> + &a1_adc_pga_right_mux),
> + SND_SOC_DAPM_MUX("ADC PGA Left Sel", SND_SOC_NOPM, 0, 0,
> + &a1_adc_pga_left_mux),
> + SND_SOC_DAPM_PGA("ADC PGA Right", BLOCK_EN, PGAR_EN, 0, NULL, 0),
> + SND_SOC_DAPM_PGA("ADC PGA Left", BLOCK_EN, PGAL_EN, 0, NULL, 0),
> + SND_SOC_DAPM_ADC("ADC Right", NULL, BLOCK_EN, ADCR_EN, 0),
> + SND_SOC_DAPM_ADC("ADC Left", NULL, BLOCK_EN, ADCL_EN, 0),
> + SND_SOC_DAPM_MUX("ADC Right Sel", SND_SOC_NOPM, 0, 0, &a1_adc_right_mux),
> + SND_SOC_DAPM_MUX("ADC Left Sel", SND_SOC_NOPM, 0, 0, &a1_adc_left_mux),
> + SND_SOC_DAPM_AIF_OUT("ADC OUT Right", NULL, 0, SND_SOC_NOPM, 0, 0),
> + SND_SOC_DAPM_AIF_OUT("ADC OUT Left", NULL, 0, SND_SOC_NOPM, 0, 0),
> +};
> +
> +static const struct snd_soc_dapm_route a1_t9015_dapm_routes[] = {
> + { "Right IN", NULL, "Playback" },
> + { "Left IN", NULL, "Playback" },
> + { "Right DAC Sel", "Right", "Right IN" },
> + { "Right DAC Sel", "Left", "Left IN" },
> + { "Left DAC Sel", "Right", "Right IN" },
> + { "Left DAC Sel", "Left", "Left IN" },
> + { "Right DAC", NULL, "Right DAC Sel" },
> + { "Left DAC", NULL, "Left DAC Sel" },
> + { "Right+ Driver Sel", "Right DAC", "Right DAC" },
> + { "Right+ Driver Sel", "Left DAC Inverted", "Right DAC" },
> + { "Left+ Driver Sel", "Left DAC", "Left DAC" },
> + { "Left+ Driver Sel", "Right DAC Inverted", "Left DAC" },
> + { "Right+ Driver", NULL, "Right+ Driver Sel" },
> + { "Left+ Driver", NULL, "Left+ Driver Sel" },
> + { "LORP", NULL, "Right+ Driver", },
> + { "LOLP", NULL, "Left+ Driver", },
> +
> + { "ADC PGA Right Sel", "Differential", "ADC IN Right" },
> + { "ADC PGA Right Sel", "Positive", "ADC IN Right" },
> + { "ADC PGA Right Sel", "Negative", "ADC IN Right" },
> + { "ADC PGA Left Sel", "Differential", "ADC IN Left" },
> + { "ADC PGA Left Sel", "Positive", "ADC IN Left" },
> + { "ADC PGA Left Sel", "Negative", "ADC IN Left" },
> + { "ADC PGA Right", NULL, "ADC PGA Right Sel" },
> + { "ADC PGA Left", NULL, "ADC PGA Left Sel" },
> + { "ADC Right", NULL, "ADC PGA Right" },
> + { "ADC Left", NULL, "ADC PGA Left" },
> + { "ADC Right Sel", "Right", "ADC Right" },
> + { "ADC Right Sel", "Left", "ADC Left" },
> + { "ADC Left Sel", "Right", "ADC Right" },
> + { "ADC Left Sel", "Left", "ADC Left" },
> + { "ADC OUT Right", NULL, "ADC Right Sel" },
> + { "ADC OUT Left", NULL, "ADC Left Sel" },
> + { "Capture", NULL, "ADC OUT Right" },
> + { "Capture", NULL, "ADC OUT Left" },
> +};
> +
> static int t9015_set_bias_level(struct snd_soc_component *component,
> enum snd_soc_bias_level level)
> {
> @@ -241,6 +465,18 @@ static int t9015_component_probe(struct snd_soc_component *component)
> return 0;
> }
>
> +static int a1_t9015_component_probe(struct snd_soc_component *component)
> +{
> + /*
> + * This configuration was stealed from original Amlogic's driver to
> + * reproduce the behavior of the driver more accurately. However, it is
> + * not known for certain what it actually affects.
> + */
> + snd_soc_component_write(component, POWER_CFG, 0x00010000);
> +
> + return 0;
> +}
> +
> static const struct snd_soc_component_driver t9015_codec_driver = {
> .probe = t9015_component_probe,
> .set_bias_level = t9015_set_bias_level,
> @@ -254,6 +490,19 @@ static const struct snd_soc_component_driver t9015_codec_driver = {
> .endianness = 1,
> };
>
> +static const struct snd_soc_component_driver a1_t9015_codec_driver = {
> + .probe = a1_t9015_component_probe,
> + .set_bias_level = t9015_set_bias_level,
> + .controls = a1_t9015_snd_controls,
> + .num_controls = ARRAY_SIZE(a1_t9015_snd_controls),
> + .dapm_widgets = a1_t9015_dapm_widgets,
> + .num_dapm_widgets = ARRAY_SIZE(a1_t9015_dapm_widgets),
> + .dapm_routes = a1_t9015_dapm_routes,
> + .num_dapm_routes = ARRAY_SIZE(a1_t9015_dapm_routes),
> + .suspend_bias_off = 1,
> + .endianness = 1,
> +};
> +
> static int t9015_probe(struct platform_device *pdev)
> {
> struct device *dev = &pdev->dev;
> @@ -315,11 +564,21 @@ static const struct t9015_match_data t9015_match_data = {
> .max_register = POWER_CFG,
> };
>
> +static const struct t9015_match_data a1_t9015_match_data = {
> + .component_drv = &a1_t9015_codec_driver,
> + .dai_drv = &a1_t9015_dai,
> + .max_register = LINEIN_CFG,
> +};
> +
> static const struct of_device_id t9015_ids[] __maybe_unused = {
> {
> .compatible = "amlogic,t9015",
> .data = &t9015_match_data,
> },
> + {
> + .compatible = "amlogic,t9015-a1",
> + .data = &a1_t9015_match_data,
> + },
> { }
> };
> MODULE_DEVICE_TABLE(of, t9015_ids);
--
Jerome
On 3/18/24 13:46, Jerome Brunet wrote:
>
> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <jan.dakinevich@salutedevices.com> wrote:
>
>> A1's internal codec is very close to t9015. The main difference, that it
>> has ADC. This commit introduces support for capturing from it.
>
> This is mis-leading.
>
> It does not look like the change is A1 specific but rather a extension
> of the support for t9015. It also mixes several different topics like line
> configuration, capture support, etc ...
>
First, it is not only extentsion. Some bits are changed comparing to
existing t9015, so new compatible string is still required.
Second, I don't know anything about about ADC in t9015 on other SoCs and
even don't sure that it exist there (may be I am inattentive, but I'm
unable to find audio input pin on sm1/g12a's pinout).
> Again, the t9015 changes should be a separated series from the rest, and
> there should be one patch per topic.
>
> As Mark, if something is meant to be configured based on the HW layout,
> then there a good change a kcontrol is not appropriate, and this should
> rather be part of the platform description, like DT.
>
> It was also suggested here:
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/sound/soc/meson/t9015.c?h=v6.8#n298
>
Ok. By the way, on a1 LINEOUT_CFG would have another value.
>>
>> Signed-off-by: Jan Dakinevich <jan.dakinevich@salutedevices.com>
>> ---
>> sound/soc/meson/t9015.c | 259 ++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 259 insertions(+)
>>
>> diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c
>> index 48f6767bd858..365955bfeb78 100644
>> --- a/sound/soc/meson/t9015.c
>> +++ b/sound/soc/meson/t9015.c
>> @@ -19,16 +19,33 @@
>> #define LOLP_EN 3
>> #define DACR_EN 4
>> #define DACL_EN 5
>> +#define ADCR_EN 6
>> +#define ADCL_EN 7
>> +#define PGAR_ZCD_EN 8
>> +#define PGAL_ZCD_EN 9
>> +#define PGAR_EN 10
>> +#define PGAL_EN 11
>> +#define ADCR_INV 16
>> +#define ADCL_INV 17
>> +#define ADCR_SRC 18
>> +#define ADCL_SRC 19
>> #define DACR_INV 20
>> #define DACL_INV 21
>> #define DACR_SRC 22
>> #define DACL_SRC 23
>> +#define ADC_DEM_EN 26
>> +#define ADC_FILTER_MODE 28
>> +#define ADC_FILTER_EN 29
>> #define REFP_BUF_EN BIT(12)
>> #define BIAS_CURRENT_EN BIT(13)
>> #define VMID_GEN_FAST BIT(14)
>> #define VMID_GEN_EN BIT(15)
>> #define I2S_MODE BIT(30)
>> #define VOL_CTRL0 0x04
>> +#define PGAR_VC 0
>> +#define PGAL_VC 8
>> +#define ADCR_VC 16
>> +#define ADCL_VC 24
>> #define GAIN_H 31
>> #define GAIN_L 23
>> #define VOL_CTRL1 0x08
>> @@ -46,6 +63,28 @@
>> #define LOLN_POL 8
>> #define LOLP_POL 12
>> #define POWER_CFG 0x10
>> +#define LINEIN_CFG 0x14
>> +#define MICBIAS_LEVEL 0
>> +#define MICBIAS_EN 3
>> +#define PGAR_CTVMN 8
>> +#define PGAR_CTVMP 9
>> +#define PGAL_CTVMN 10
>> +#define PGAL_CTVMP 11
>> +#define PGAR_CTVIN 12
>> +#define PGAR_CTVIP 13
>> +#define PGAL_CTVIN 14
>> +#define PGAL_CTVIP 15
>> +
>> +#define PGAR_MASK (BIT(PGAR_CTVMP) | BIT(PGAR_CTVMN) | \
>> + BIT(PGAR_CTVIP) | BIT(PGAR_CTVIN))
>> +#define PGAR_DIFF (BIT(PGAR_CTVIP) | BIT(PGAR_CTVIN))
>> +#define PGAR_POSITIVE (BIT(PGAR_CTVIP) | BIT(PGAR_CTVMN))
>> +#define PGAR_NEGATIVE (BIT(PGAR_CTVIN) | BIT(PGAR_CTVMP))
>> +#define PGAL_MASK (BIT(PGAL_CTVMP) | BIT(PGAL_CTVMN) | \
>> + BIT(PGAL_CTVIP) | BIT(PGAL_CTVIN))
>> +#define PGAL_DIFF (BIT(PGAL_CTVIP) | BIT(PGAL_CTVIN))
>> +#define PGAL_POSITIVE (BIT(PGAL_CTVIP) | BIT(PGAL_CTVMN))
>> +#define PGAL_NEGATIVE (BIT(PGAL_CTVIN) | BIT(PGAL_CTVMP))
>>
>> struct t9015 {
>> struct regulator *avdd;
>> @@ -103,6 +142,31 @@ static struct snd_soc_dai_driver t9015_dai = {
>> .ops = &t9015_dai_ops,
>> };
>>
>> +static struct snd_soc_dai_driver a1_t9015_dai = {
>> + .name = "t9015-hifi",
>> + .playback = {
>> + .stream_name = "Playback",
>> + .channels_min = 1,
>> + .channels_max = 2,
>> + .rates = SNDRV_PCM_RATE_8000_96000,
>> + .formats = (SNDRV_PCM_FMTBIT_S8 |
>> + SNDRV_PCM_FMTBIT_S16_LE |
>> + SNDRV_PCM_FMTBIT_S20_LE |
>> + SNDRV_PCM_FMTBIT_S24_LE),
>> + },
>> + .capture = {
>> + .stream_name = "Capture",
>> + .channels_min = 1,
>> + .channels_max = 2,
>> + .rates = SNDRV_PCM_RATE_8000_96000,
>> + .formats = (SNDRV_PCM_FMTBIT_S8 |
>> + SNDRV_PCM_FMTBIT_S16_LE |
>> + SNDRV_PCM_FMTBIT_S20_LE |
>> + SNDRV_PCM_FMTBIT_S24_LE),
>> + },
>> + .ops = &t9015_dai_ops,
>> +};
>> +
>> static const DECLARE_TLV_DB_MINMAX_MUTE(dac_vol_tlv, -9525, 0);
>>
>> static const char * const ramp_rate_txt[] = { "Fast", "Slow" };
>> @@ -179,6 +243,166 @@ static const struct snd_soc_dapm_route t9015_dapm_routes[] = {
>> { "LOLP", NULL, "Left+ Driver", },
>> };
>>
>> +static const char * const a1_right_driver_txt[] = { "None", "Right DAC",
>> + "Left DAC Inverted" };
>> +static const unsigned int a1_right_driver_values[] = { 0, 2, 4 };
>> +
>> +static const char * const a1_left_driver_txt[] = { "None", "Left DAC",
>> + "Right DAC Inverted" };
>> +static const unsigned int a1_left_driver_values[] = { 0, 2, 4 };
>> +
>> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_right_driver, LINEOUT_CFG, 12, 0x7,
>> + a1_right_driver_txt, a1_right_driver_values);
>> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_left_driver, LINEOUT_CFG, 4, 0x7,
>> + a1_left_driver_txt, a1_left_driver_values);
>> +
>> +static const struct snd_kcontrol_new a1_right_driver_mux =
>> + SOC_DAPM_ENUM("Right Driver+ Source", a1_right_driver);
>> +static const struct snd_kcontrol_new a1_left_driver_mux =
>> + SOC_DAPM_ENUM("Left Driver+ Source", a1_left_driver);
>> +
>> +static const DECLARE_TLV_DB_MINMAX_MUTE(a1_adc_vol_tlv, -29625, 0);
>> +static const DECLARE_TLV_DB_MINMAX_MUTE(a1_adc_pga_vol_tlv, -1200, 0);
>> +
>> +static const char * const a1_adc_right_txt[] = { "Right", "Left" };
>> +static SOC_ENUM_SINGLE_DECL(a1_adc_right, BLOCK_EN, ADCR_SRC, a1_adc_right_txt);
>> +
>> +static const char * const a1_adc_left_txt[] = { "Left", "Right" };
>> +static SOC_ENUM_SINGLE_DECL(a1_adc_left, BLOCK_EN, ADCL_SRC, a1_adc_left_txt);
>> +
>> +static const struct snd_kcontrol_new a1_adc_right_mux =
>> + SOC_DAPM_ENUM("ADC Right Source", a1_adc_right);
>> +static const struct snd_kcontrol_new a1_adc_left_mux =
>> + SOC_DAPM_ENUM("ADC Left Source", a1_adc_left);
>> +
>> +static const char * const a1_adc_filter_mode_txt[] = { "Voice", "HiFi"};
>> +static SOC_ENUM_SINGLE_DECL(a1_adc_filter_mode, BLOCK_EN, ADC_FILTER_MODE,
>> + a1_adc_filter_mode_txt);
>> +
>> +static const char * const a1_adc_mic_bias_level_txt[] = { "2.0V", "2.1V",
>> + "2.3V", "2.5V", "2.8V" };
>> +static const unsigned int a1_adc_mic_bias_level_values[] = { 0, 1, 2, 3, 7 };
>> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_mic_bias_level,
>> + LINEIN_CFG, MICBIAS_LEVEL, 0x7,
>> + a1_adc_mic_bias_level_txt,
>> + a1_adc_mic_bias_level_values);
>> +
>> +static const char * const a1_adc_pga_txt[] = { "None", "Differential",
>> + "Positive", "Negative" };
>> +static const unsigned int a1_adc_pga_right_values[] = { 0, PGAR_DIFF,
>> + PGAR_POSITIVE, PGAR_NEGATIVE };
>> +static const unsigned int a1_adc_pga_left_values[] = { 0, PGAL_DIFF,
>> + PGAL_POSITIVE, PGAL_NEGATIVE };
>> +
>> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_pga_right, LINEIN_CFG, 0, PGAR_MASK,
>> + a1_adc_pga_txt, a1_adc_pga_right_values);
>> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_pga_left, LINEIN_CFG, 0, PGAL_MASK,
>> + a1_adc_pga_txt, a1_adc_pga_left_values);
>> +
>> +static const struct snd_kcontrol_new a1_adc_pga_right_mux =
>> + SOC_DAPM_ENUM("ADC PGA Right Source", a1_adc_pga_right);
>> +static const struct snd_kcontrol_new a1_adc_pga_left_mux =
>> + SOC_DAPM_ENUM("ADC PGA Left Source", a1_adc_pga_left);
>> +
>> +static const struct snd_kcontrol_new a1_t9015_snd_controls[] = {
>> + /* Volume Controls */
>> + SOC_ENUM("Playback Channel Mode", mono_enum),
>> + SOC_SINGLE("Playback Switch", VOL_CTRL1, DAC_SOFT_MUTE, 1, 1),
>> + SOC_DOUBLE_TLV("Playback Volume", VOL_CTRL1, DACL_VC, DACR_VC,
>> + 0xff, 0, dac_vol_tlv),
>> +
>> + /* Ramp Controls */
>> + SOC_ENUM("Ramp Rate", ramp_rate_enum),
>> + SOC_SINGLE("Volume Ramp Switch", VOL_CTRL1, VC_RAMP_MODE, 1, 0),
>> + SOC_SINGLE("Mute Ramp Switch", VOL_CTRL1, MUTE_MODE, 1, 0),
>> + SOC_SINGLE("Unmute Ramp Switch", VOL_CTRL1, UNMUTE_MODE, 1, 0),
>> +
>> + /* ADC Controls */
>> + SOC_DOUBLE_TLV("ADC Volume", VOL_CTRL0, ADCL_VC, ADCR_VC,
>> + 0x7f, 0, a1_adc_vol_tlv),
>> + SOC_SINGLE("ADC Filter Switch", BLOCK_EN, ADC_FILTER_EN, 1, 0),
>> + SOC_ENUM("ADC Filter Mode", a1_adc_filter_mode),
>> + SOC_SINGLE("ADC Mic Bias Switch", LINEIN_CFG, MICBIAS_EN, 1, 0),
>> + SOC_ENUM("ADC Mic Bias Level", a1_adc_mic_bias_level),
>> + SOC_SINGLE("ADC DEM Switch", BLOCK_EN, ADC_DEM_EN, 1, 0),
>> + SOC_DOUBLE_TLV("ADC PGA Volume", VOL_CTRL0, PGAR_VC, PGAL_VC,
>> + 0x1f, 0, a1_adc_pga_vol_tlv),
>> + SOC_DOUBLE("ADC PGA Zero Cross-detection Switch", BLOCK_EN,
>> + PGAL_ZCD_EN, PGAR_ZCD_EN, 1, 0),
>> +};
>> +
>> +static const struct snd_soc_dapm_widget a1_t9015_dapm_widgets[] = {
>> + SND_SOC_DAPM_AIF_IN("Right IN", NULL, 0, SND_SOC_NOPM, 0, 0),
>> + SND_SOC_DAPM_AIF_IN("Left IN", NULL, 0, SND_SOC_NOPM, 0, 0),
>> + SND_SOC_DAPM_MUX("Right DAC Sel", SND_SOC_NOPM, 0, 0,
>> + &t9015_right_dac_mux),
>> + SND_SOC_DAPM_MUX("Left DAC Sel", SND_SOC_NOPM, 0, 0,
>> + &t9015_left_dac_mux),
>> + SND_SOC_DAPM_DAC("Right DAC", NULL, BLOCK_EN, DACR_EN, 0),
>> + SND_SOC_DAPM_DAC("Left DAC", NULL, BLOCK_EN, DACL_EN, 0),
>> + SND_SOC_DAPM_MUX("Right+ Driver Sel", SND_SOC_NOPM, 0, 0,
>> + &a1_right_driver_mux),
>> + SND_SOC_DAPM_MUX("Left+ Driver Sel", SND_SOC_NOPM, 0, 0,
>> + &a1_left_driver_mux),
>> + SND_SOC_DAPM_OUT_DRV("Right+ Driver", BLOCK_EN, LORP_EN, 0, NULL, 0),
>> + SND_SOC_DAPM_OUT_DRV("Left+ Driver", BLOCK_EN, LOLP_EN, 0, NULL, 0),
>> + SND_SOC_DAPM_OUTPUT("LORP"),
>> + SND_SOC_DAPM_OUTPUT("LOLP"),
>> +
>> + SND_SOC_DAPM_INPUT("ADC IN Right"),
>> + SND_SOC_DAPM_INPUT("ADC IN Left"),
>> + SND_SOC_DAPM_MUX("ADC PGA Right Sel", SND_SOC_NOPM, 0, 0,
>> + &a1_adc_pga_right_mux),
>> + SND_SOC_DAPM_MUX("ADC PGA Left Sel", SND_SOC_NOPM, 0, 0,
>> + &a1_adc_pga_left_mux),
>> + SND_SOC_DAPM_PGA("ADC PGA Right", BLOCK_EN, PGAR_EN, 0, NULL, 0),
>> + SND_SOC_DAPM_PGA("ADC PGA Left", BLOCK_EN, PGAL_EN, 0, NULL, 0),
>> + SND_SOC_DAPM_ADC("ADC Right", NULL, BLOCK_EN, ADCR_EN, 0),
>> + SND_SOC_DAPM_ADC("ADC Left", NULL, BLOCK_EN, ADCL_EN, 0),
>> + SND_SOC_DAPM_MUX("ADC Right Sel", SND_SOC_NOPM, 0, 0, &a1_adc_right_mux),
>> + SND_SOC_DAPM_MUX("ADC Left Sel", SND_SOC_NOPM, 0, 0, &a1_adc_left_mux),
>> + SND_SOC_DAPM_AIF_OUT("ADC OUT Right", NULL, 0, SND_SOC_NOPM, 0, 0),
>> + SND_SOC_DAPM_AIF_OUT("ADC OUT Left", NULL, 0, SND_SOC_NOPM, 0, 0),
>> +};
>> +
>> +static const struct snd_soc_dapm_route a1_t9015_dapm_routes[] = {
>> + { "Right IN", NULL, "Playback" },
>> + { "Left IN", NULL, "Playback" },
>> + { "Right DAC Sel", "Right", "Right IN" },
>> + { "Right DAC Sel", "Left", "Left IN" },
>> + { "Left DAC Sel", "Right", "Right IN" },
>> + { "Left DAC Sel", "Left", "Left IN" },
>> + { "Right DAC", NULL, "Right DAC Sel" },
>> + { "Left DAC", NULL, "Left DAC Sel" },
>> + { "Right+ Driver Sel", "Right DAC", "Right DAC" },
>> + { "Right+ Driver Sel", "Left DAC Inverted", "Right DAC" },
>> + { "Left+ Driver Sel", "Left DAC", "Left DAC" },
>> + { "Left+ Driver Sel", "Right DAC Inverted", "Left DAC" },
>> + { "Right+ Driver", NULL, "Right+ Driver Sel" },
>> + { "Left+ Driver", NULL, "Left+ Driver Sel" },
>> + { "LORP", NULL, "Right+ Driver", },
>> + { "LOLP", NULL, "Left+ Driver", },
>> +
>> + { "ADC PGA Right Sel", "Differential", "ADC IN Right" },
>> + { "ADC PGA Right Sel", "Positive", "ADC IN Right" },
>> + { "ADC PGA Right Sel", "Negative", "ADC IN Right" },
>> + { "ADC PGA Left Sel", "Differential", "ADC IN Left" },
>> + { "ADC PGA Left Sel", "Positive", "ADC IN Left" },
>> + { "ADC PGA Left Sel", "Negative", "ADC IN Left" },
>> + { "ADC PGA Right", NULL, "ADC PGA Right Sel" },
>> + { "ADC PGA Left", NULL, "ADC PGA Left Sel" },
>> + { "ADC Right", NULL, "ADC PGA Right" },
>> + { "ADC Left", NULL, "ADC PGA Left" },
>> + { "ADC Right Sel", "Right", "ADC Right" },
>> + { "ADC Right Sel", "Left", "ADC Left" },
>> + { "ADC Left Sel", "Right", "ADC Right" },
>> + { "ADC Left Sel", "Left", "ADC Left" },
>> + { "ADC OUT Right", NULL, "ADC Right Sel" },
>> + { "ADC OUT Left", NULL, "ADC Left Sel" },
>> + { "Capture", NULL, "ADC OUT Right" },
>> + { "Capture", NULL, "ADC OUT Left" },
>> +};
>> +
>> static int t9015_set_bias_level(struct snd_soc_component *component,
>> enum snd_soc_bias_level level)
>> {
>> @@ -241,6 +465,18 @@ static int t9015_component_probe(struct snd_soc_component *component)
>> return 0;
>> }
>>
>> +static int a1_t9015_component_probe(struct snd_soc_component *component)
>> +{
>> + /*
>> + * This configuration was stealed from original Amlogic's driver to
>> + * reproduce the behavior of the driver more accurately. However, it is
>> + * not known for certain what it actually affects.
>> + */
>> + snd_soc_component_write(component, POWER_CFG, 0x00010000);
>> +
>> + return 0;
>> +}
>> +
>> static const struct snd_soc_component_driver t9015_codec_driver = {
>> .probe = t9015_component_probe,
>> .set_bias_level = t9015_set_bias_level,
>> @@ -254,6 +490,19 @@ static const struct snd_soc_component_driver t9015_codec_driver = {
>> .endianness = 1,
>> };
>>
>> +static const struct snd_soc_component_driver a1_t9015_codec_driver = {
>> + .probe = a1_t9015_component_probe,
>> + .set_bias_level = t9015_set_bias_level,
>> + .controls = a1_t9015_snd_controls,
>> + .num_controls = ARRAY_SIZE(a1_t9015_snd_controls),
>> + .dapm_widgets = a1_t9015_dapm_widgets,
>> + .num_dapm_widgets = ARRAY_SIZE(a1_t9015_dapm_widgets),
>> + .dapm_routes = a1_t9015_dapm_routes,
>> + .num_dapm_routes = ARRAY_SIZE(a1_t9015_dapm_routes),
>> + .suspend_bias_off = 1,
>> + .endianness = 1,
>> +};
>> +
>> static int t9015_probe(struct platform_device *pdev)
>> {
>> struct device *dev = &pdev->dev;
>> @@ -315,11 +564,21 @@ static const struct t9015_match_data t9015_match_data = {
>> .max_register = POWER_CFG,
>> };
>>
>> +static const struct t9015_match_data a1_t9015_match_data = {
>> + .component_drv = &a1_t9015_codec_driver,
>> + .dai_drv = &a1_t9015_dai,
>> + .max_register = LINEIN_CFG,
>> +};
>> +
>> static const struct of_device_id t9015_ids[] __maybe_unused = {
>> {
>> .compatible = "amlogic,t9015",
>> .data = &t9015_match_data,
>> },
>> + {
>> + .compatible = "amlogic,t9015-a1",
>> + .data = &a1_t9015_match_data,
>> + },
>> { }
>> };
>> MODULE_DEVICE_TABLE(of, t9015_ids);
>
>
--
Best regards
Jan Dakinevich
On Fri, Mar 15, 2024 at 02:21:48AM +0300, Jan Dakinevich wrote:
> +static const char * const a1_adc_mic_bias_level_txt[] = { "2.0V", "2.1V",
> + "2.3V", "2.5V", "2.8V" };
> +static const unsigned int a1_adc_mic_bias_level_values[] = { 0, 1, 2, 3, 7 };
Why would this be varied at runtime rather than being something fixed
when the system is designed?
> +static const char * const a1_adc_pga_txt[] = { "None", "Differential",
> + "Positive", "Negative" };
> +static const unsigned int a1_adc_pga_right_values[] = { 0, PGAR_DIFF,
> + PGAR_POSITIVE, PGAR_NEGATIVE };
> +static const unsigned int a1_adc_pga_left_values[] = { 0, PGAL_DIFF,
> + PGAL_POSITIVE, PGAL_NEGATIVE };
Similarly here.
> + SOC_SINGLE("ADC Mic Bias Switch", LINEIN_CFG, MICBIAS_EN, 1, 0),
> + SOC_ENUM("ADC Mic Bias Level", a1_adc_mic_bias_level),
Why would micbias be user controlled rather than a DAPM widget as
normal?
On 3/15/24 16:36, Mark Brown wrote:
> On Fri, Mar 15, 2024 at 02:21:48AM +0300, Jan Dakinevich wrote:
>
>> +static const char * const a1_adc_mic_bias_level_txt[] = { "2.0V", "2.1V",
>> + "2.3V", "2.5V", "2.8V" };
>> +static const unsigned int a1_adc_mic_bias_level_values[] = { 0, 1, 2, 3, 7 };
>
> Why would this be varied at runtime rather than being something fixed
> when the system is designed?
>
>> +static const char * const a1_adc_pga_txt[] = { "None", "Differential",
>> + "Positive", "Negative" };
>> +static const unsigned int a1_adc_pga_right_values[] = { 0, PGAR_DIFF,
>> + PGAR_POSITIVE, PGAR_NEGATIVE };
>> +static const unsigned int a1_adc_pga_left_values[] = { 0, PGAL_DIFF,
>> + PGAL_POSITIVE, PGAL_NEGATIVE };
>
> Similarly here.
>
Both mic bias and ADC's input mode depends on schematics and should be
configurable. What is the better way to give access to these parameters?
Device tree?
>> + SOC_SINGLE("ADC Mic Bias Switch", LINEIN_CFG, MICBIAS_EN, 1, 0),
>> + SOC_ENUM("ADC Mic Bias Level", a1_adc_mic_bias_level),
>
> Why would micbias be user controlled rather than a DAPM widget as
> normal?
Yes, I could use SND_SOC_DAPM_SUPPLY, but it supports only raw values,
and doesn't supports enums. Here, I want to use enum to restrict
possible values, because only these values mentioned in the
documentation that I have.
--
Best regards
Jan Dakinevich
On Sun, Mar 17, 2024 at 07:27:14PM +0300, Jan Dakinevich wrote:
> Both mic bias and ADC's input mode depends on schematics and should be
> configurable. What is the better way to give access to these parameters?
> Device tree?
Yes.
> >> + SOC_SINGLE("ADC Mic Bias Switch", LINEIN_CFG, MICBIAS_EN, 1, 0),
> >> + SOC_ENUM("ADC Mic Bias Level", a1_adc_mic_bias_level),
> > Why would micbias be user controlled rather than a DAPM widget as
> > normal?
> Yes, I could use SND_SOC_DAPM_SUPPLY, but it supports only raw values,
> and doesn't supports enums. Here, I want to use enum to restrict
> possible values, because only these values mentioned in the
> documentation that I have.
A supply is an on/off switch not an enum. Users should not be selecting
values at all.
On 3/18/24 16:48, Mark Brown wrote:
> On Sun, Mar 17, 2024 at 07:27:14PM +0300, Jan Dakinevich wrote:
>
>> Both mic bias and ADC's input mode depends on schematics and should be
>> configurable. What is the better way to give access to these parameters?
>> Device tree?
>
> Yes.
>
>>>> + SOC_SINGLE("ADC Mic Bias Switch", LINEIN_CFG, MICBIAS_EN, 1, 0),
>>>> + SOC_ENUM("ADC Mic Bias Level", a1_adc_mic_bias_level),
>
>>> Why would micbias be user controlled rather than a DAPM widget as
>>> normal?
>
>> Yes, I could use SND_SOC_DAPM_SUPPLY, but it supports only raw values,
>> and doesn't supports enums. Here, I want to use enum to restrict
>> possible values, because only these values mentioned in the
>> documentation that I have.
>
> A supply is an on/off switch not an enum. Users should not be selecting
> values at all.
Ok. For me it is great if I am free to move these kcontrols to device tree.
--
Best regards
Jan Dakinevich
© 2016 - 2026 Red Hat, Inc.