From: Marcus Cooper codekipper@gmail.com
Analog part of audiocodec is very similar to other codecs supports by this driver, but has different registers
Signed-off-by: Marcus Cooper codekipper@gmail.com Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com --- .../bindings/sound/sun8i-codec-analog.txt | 1 + sound/soc/sunxi/Kconfig | 2 +- sound/soc/sunxi/sun8i-codec-analog.c | 227 +++++++++++++++++++++ 3 files changed, 229 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt b/Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt index 07356758bd91..f38896850e4d 100644 --- a/Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt +++ b/Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt @@ -5,6 +5,7 @@ Required properties: - "allwinner,sun8i-a23-codec-analog" - "allwinner,sun8i-h3-codec-analog" - "allwinner,sun8i-v3s-codec-analog" + - "allwinner,sun50i-a64-codec-analog"
Required properties if not a sub-node of the PRCM node: - reg: must contain the registers location and length diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig index 22408bc2d6ec..26072b74e47f 100644 --- a/sound/soc/sunxi/Kconfig +++ b/sound/soc/sunxi/Kconfig @@ -12,7 +12,7 @@ config SND_SUN4I_CODEC config SND_SUN8I_CODEC tristate "Allwinner SUN8I audio codec" depends on OF - depends on MACH_SUN8I || COMPILE_TEST + depends on MACH_SUN8I || (ARM64 && ARCH_SUNXI) || COMPILE_TEST select REGMAP_MMIO help This option enables the digital part of the internal audio codec for diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c index 4e39d2668286..adb7fe087c73 100644 --- a/sound/soc/sunxi/sun8i-codec-analog.c +++ b/sound/soc/sunxi/sun8i-codec-analog.c @@ -30,6 +30,7 @@ /* Codec analog control register offsets and bit fields */ #define SUN8I_ADDA_HP_VOLC 0x00 #define SUN8I_ADDA_HP_VOLC_PA_CLK_GATE 7 +#define SUN50I_ADDA_HP_VOLC_HPPAEN 6 #define SUN8I_ADDA_HP_VOLC_HP_VOL 0 #define SUN8I_ADDA_LOMIXSC 0x01 #define SUN8I_ADDA_LOMIXSC_MIC1 6 @@ -48,6 +49,7 @@ #define SUN8I_ADDA_ROMIXSC_DACR 1 #define SUN8I_ADDA_ROMIXSC_DACL 0 #define SUN8I_ADDA_DAC_PA_SRC 0x03 +#define SUN50I_ADDA_DAC_PA_SRC 0x0a #define SUN8I_ADDA_DAC_PA_SRC_DACAREN 7 #define SUN8I_ADDA_DAC_PA_SRC_DACALEN 6 #define SUN8I_ADDA_DAC_PA_SRC_RMIXEN 5 @@ -62,9 +64,16 @@ #define SUN8I_ADDA_LINEIN_GCTRL 0x05 #define SUN8I_ADDA_LINEIN_GCTRL_LINEING 4 #define SUN8I_ADDA_LINEIN_GCTRL_PHONEG 0 +#define SUN50I_ADDA_LINEOUT_CTRL0 0x05 +#define SUN50I_ADDA_LINEOUT_CTRL0_LEN 7 +#define SUN50I_ADDA_LINEOUT_CTRL0_REN 6 +#define SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL 5 +#define SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL 4 #define SUN8I_ADDA_MICIN_GCTRL 0x06 #define SUN8I_ADDA_MICIN_GCTRL_MIC1G 4 #define SUN8I_ADDA_MICIN_GCTRL_MIC2G 0 +#define SUN50I_ADDA_LINEOUT_CTRL1 0x06 +#define SUN50I_ADDA_LINEOUT_CTRL1_VOL 0 #define SUN8I_ADDA_PAEN_HP_CTRL 0x07 #define SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN 7 #define SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN 7 /* H3 specific */ @@ -364,6 +373,61 @@ static const struct snd_soc_dapm_widget sun8i_v3s_codec_mixer_widgets[] = { ARRAY_SIZE(sun8i_v3s_codec_adc_mixer_controls)), };
+static const struct snd_soc_dapm_widget sun50i_codec_common_widgets[] = { + /* ADC */ + SND_SOC_DAPM_ADC("Left ADC", NULL, SUN8I_ADDA_ADC_AP_EN, + SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0), + SND_SOC_DAPM_ADC("Right ADC", NULL, SUN8I_ADDA_ADC_AP_EN, + SUN8I_ADDA_ADC_AP_EN_ADCREN, 0), + + /* DAC */ + SND_SOC_DAPM_DAC("Left DAC", NULL, SUN50I_ADDA_DAC_PA_SRC, + SUN8I_ADDA_DAC_PA_SRC_DACALEN, 0), + SND_SOC_DAPM_DAC("Right DAC", NULL, SUN50I_ADDA_DAC_PA_SRC, + SUN8I_ADDA_DAC_PA_SRC_DACAREN, 0), + /* + * Due to this component and the codec belonging to separate DAPM + * contexts, we need to manually link the above widgets to their + * stream widgets at the card level. + */ + + /* Line In */ + SND_SOC_DAPM_INPUT("LINEIN"), + + /* Microphone inputs */ + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + + /* Microphone Bias */ + SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN, + 0, NULL, 0), + + /* Mic input path */ + SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL, + SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0), + + /* Mixers */ + SND_SOC_DAPM_MIXER("Left Mixer", SUN50I_ADDA_DAC_PA_SRC, + SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0, + sun8i_codec_mixer_controls, + ARRAY_SIZE(sun8i_codec_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Mixer", SUN50I_ADDA_DAC_PA_SRC, + SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0, + sun8i_codec_mixer_controls, + ARRAY_SIZE(sun8i_codec_mixer_controls)), + SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN, + SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0, + sun8i_codec_adc_mixer_controls, + ARRAY_SIZE(sun8i_codec_adc_mixer_controls)), + SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN, + SUN8I_ADDA_ADC_AP_EN_ADCREN, 0, + sun8i_codec_adc_mixer_controls, + ARRAY_SIZE(sun8i_codec_adc_mixer_controls)), +}; + static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = { /* Microphone Routes */ { "Mic1 Amplifier", NULL, "MIC1"}, @@ -408,6 +472,17 @@ static const struct snd_kcontrol_new sun8i_a23_codec_hp_ctrls[] = { SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE, 1, 0), };
+static const struct snd_kcontrol_new sun50i_a64_codec_hp_ctrls[] = { + SOC_SINGLE_TLV("Headphone Playback Volume", + SUN8I_ADDA_HP_VOLC, + SUN8I_ADDA_HP_VOLC_HP_VOL, 0x3f, 0, + sun8i_codec_hp_vol_scale), + SOC_DOUBLE("Headphone Playback Switch", + SUN50I_ADDA_DAC_PA_SRC, + SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE, + SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE, 1, 0), +}; + static const char * const sun8i_codec_hp_src_enum_text[] = { "DAC", "Mixer", }; @@ -461,6 +536,14 @@ static const struct snd_soc_dapm_widget sun8i_a23_codec_hp_widgets[] = { SND_SOC_DAPM_OUTPUT("HP"), };
+static const struct snd_soc_dapm_widget sun50i_a64_codec_hp_widgets[] = { + SND_SOC_DAPM_MUX("Headphone Source Playback Route", + SND_SOC_NOPM, 0, 0, sun8i_codec_hp_src), + SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN8I_ADDA_HP_VOLC, + SUN50I_ADDA_HP_VOLC_HPPAEN, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("HP"), +}; + static const struct snd_soc_dapm_route sun8i_codec_headphone_routes[] = { { "Headphone Source Playback Route", "DAC", "Left DAC" }, { "Headphone Source Playback Route", "DAC", "Right DAC" }, @@ -471,6 +554,15 @@ static const struct snd_soc_dapm_route sun8i_codec_headphone_routes[] = { { "HP", NULL, "Headphone Amp" }, };
+static const struct snd_soc_dapm_route sun50i_codec_headphone_routes[] = { + { "Headphone Source Playback Route", "DAC", "Left DAC" }, + { "Headphone Source Playback Route", "DAC", "Right DAC" }, + { "Headphone Source Playback Route", "Mixer", "Left Mixer" }, + { "Headphone Source Playback Route", "Mixer", "Right Mixer" }, + { "Headphone Amp", NULL, "Headphone Source Playback Route" }, + { "HP", NULL, "Headphone Amp" }, +}; + static int sun8i_a23_codec_add_headphone(struct snd_soc_component *cmpnt) { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); @@ -525,6 +617,41 @@ static int sun8i_codec_add_mbias(struct snd_soc_component *cmpnt) return ret; }
+static int sun50i_a64_codec_add_headphone(struct snd_soc_component *cmpnt) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); + struct device *dev = cmpnt->dev; + int ret; + + ret = snd_soc_add_component_controls(cmpnt, + sun50i_a64_codec_hp_ctrls, + ARRAY_SIZE( + sun50i_a64_codec_hp_ctrls)); + if (ret) { + dev_err(dev, "Failed to add Headphone controls: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_new_controls(dapm, + sun50i_a64_codec_hp_widgets, + ARRAY_SIZE( + sun50i_a64_codec_hp_widgets)); + if (ret) { + dev_err(dev, "Failed to add Headphone DAPM widgets: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(dapm, sun50i_codec_headphone_routes, + ARRAY_SIZE( + sun50i_codec_headphone_routes)); + if (ret) { + dev_err(dev, "Failed to add Headphone DAPM routes: %d\n", ret); + return ret; + } + + return 0; +} + /* hmic specific widget */ static const struct snd_soc_dapm_widget sun8i_codec_hmic_widgets[] = { SND_SOC_DAPM_SUPPLY("HBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, @@ -618,6 +745,17 @@ static const struct snd_kcontrol_new sun8i_codec_lineout_controls[] = { SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN, 1, 0), };
+static const struct snd_kcontrol_new sun50i_codec_lineout_ctrls[] = { + SOC_SINGLE_TLV("Line Out Playback Volume", + SUN50I_ADDA_LINEOUT_CTRL1, + SUN50I_ADDA_LINEOUT_CTRL1_VOL, 0x1f, 0, + sun8i_codec_lineout_vol_scale), + SOC_DOUBLE("Line Out Playback Switch", + SUN50I_ADDA_LINEOUT_CTRL0, + SUN50I_ADDA_LINEOUT_CTRL0_REN, + SUN50I_ADDA_LINEOUT_CTRL0_REN, 1, 0), +}; + static const char * const sun8i_codec_lineout_src_enum_text[] = { "Stereo", "Mono Differential", }; @@ -628,11 +766,22 @@ static SOC_ENUM_DOUBLE_DECL(sun8i_codec_lineout_src_enum, SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC, sun8i_codec_lineout_src_enum_text);
+static SOC_ENUM_DOUBLE_DECL(sun50i_codec_lineout_src_enum, + SUN50I_ADDA_LINEOUT_CTRL0, + SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL, + SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL, + sun8i_codec_lineout_src_enum_text); + static const struct snd_kcontrol_new sun8i_codec_lineout_src[] = { SOC_DAPM_ENUM("Line Out Source Playback Route", sun8i_codec_lineout_src_enum), };
+static const struct snd_kcontrol_new sun50i_codec_lineout_src[] = { + SOC_DAPM_ENUM("Line Out Source Playback Route", + sun50i_codec_lineout_src_enum), +}; + static const struct snd_soc_dapm_widget sun8i_codec_lineout_widgets[] = { SND_SOC_DAPM_MUX("Line Out Source Playback Route", SND_SOC_NOPM, 0, 0, sun8i_codec_lineout_src), @@ -642,6 +791,17 @@ static const struct snd_soc_dapm_widget sun8i_codec_lineout_widgets[] = { SND_SOC_DAPM_OUTPUT("LINEOUT"), };
+static const struct snd_soc_dapm_widget sun50i_codec_lineout_widgets[] = { + SND_SOC_DAPM_MUX("Line Out Source Playback Route", + SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src), + /* It is unclear if this is a buffer or gate, model it as a supply */ + SND_SOC_DAPM_SUPPLY("Left Line Out Enable", SUN50I_ADDA_LINEOUT_CTRL0, + SUN50I_ADDA_LINEOUT_CTRL0_LEN, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Right Line Out Enable", SUN50I_ADDA_LINEOUT_CTRL0, + SUN50I_ADDA_LINEOUT_CTRL0_REN, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("LINEOUT"), +}; + static const struct snd_soc_dapm_route sun8i_codec_lineout_routes[] = { { "Line Out Source Playback Route", "Stereo", "Left Mixer" }, { "Line Out Source Playback Route", "Stereo", "Right Mixer" }, @@ -651,6 +811,17 @@ static const struct snd_soc_dapm_route sun8i_codec_lineout_routes[] = { { "LINEOUT", NULL, "Line Out Enable", }, };
+static const struct snd_soc_dapm_route sun50i_codec_lineout_routes[] = { + { "Line Out Source Playback Route", "Stereo", "Left Mixer" }, + { "Line Out Source Playback Route", "Stereo", "Right Mixer" }, + { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" }, + { "Line Out Source Playback Route", "Mono Differential", + "Right Mixer" }, + { "LINEOUT", NULL, "Line Out Source Playback Route" }, + { "LINEOUT", NULL, "Left Line Out Enable", }, + { "LINEOUT", NULL, "Right Line Out Enable", }, +}; + static int sun8i_h3_codec_add_lineout(struct snd_soc_component *cmpnt) { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); @@ -747,6 +918,39 @@ static int sun8i_codec_add_mic2(struct snd_soc_component *cmpnt) return 0; }
+static int sun50i_a64_codec_add_lineout(struct snd_soc_component *cmpnt) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); + struct device *dev = cmpnt->dev; + int ret; + + ret = snd_soc_add_component_controls(cmpnt, + sun50i_codec_lineout_ctrls, + ARRAY_SIZE( + sun50i_codec_lineout_ctrls)); + if (ret) { + dev_err(dev, "Failed to add Line Out controls: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_new_controls(dapm, sun50i_codec_lineout_widgets, + ARRAY_SIZE( + sun50i_codec_lineout_widgets)); + if (ret) { + dev_err(dev, "Failed to add Line Out DAPM widgets: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(dapm, sun50i_codec_lineout_routes, + ARRAY_SIZE(sun50i_codec_lineout_routes)); + if (ret) { + dev_err(dev, "Failed to add Line Out DAPM routes: %d\n", ret); + return ret; + } + + return 0; +} + struct sun8i_codec_analog_quirks { bool has_headphone; bool has_hmic; @@ -868,6 +1072,16 @@ static const struct snd_soc_component_driver sun8i_codec_analog_cmpnt_drv = { .probe = sun8i_codec_analog_cmpnt_probe, };
+static const struct snd_soc_component_driver sun50i_codec_analog_cmpnt_drv = { + .controls = sun8i_codec_common_controls, + .num_controls = ARRAY_SIZE(sun8i_codec_common_controls), + .dapm_widgets = sun50i_codec_common_widgets, + .num_dapm_widgets = ARRAY_SIZE(sun50i_codec_common_widgets), + .dapm_routes = sun8i_codec_common_routes, + .num_dapm_routes = ARRAY_SIZE(sun8i_codec_common_routes), + .probe = sun8i_codec_analog_cmpnt_probe, +}; + static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = { .has_headphone = true, .has_hmic = true, @@ -893,6 +1107,15 @@ static const struct sun8i_codec_analog_quirks sun8i_v3s_quirks = { .cmpnt_drv = &sun8i_codec_analog_cmpnt_drv, };
+static const struct sun8i_codec_analog_quirks sun50i_a64_quirks = { + .has_headphone = true, + .has_hmic = false, + .has_lineout = true, + .add_headphone = sun50i_a64_codec_add_headphone, + .add_lineout = sun50i_a64_codec_add_lineout, + .cmpnt_drv = &sun50i_codec_analog_cmpnt_drv, +}; + static const struct of_device_id sun8i_codec_analog_of_match[] = { { .compatible = "allwinner,sun8i-a23-codec-analog", @@ -906,6 +1129,10 @@ static const struct of_device_id sun8i_codec_analog_of_match[] = { .compatible = "allwinner,sun8i-v3s-codec-analog", .data = &sun8i_v3s_quirks, }, + { + .compatible = "allwinner,sun50i-a64-codec-analog", + .data = &sun50i_a64_quirks, + }, {} }; MODULE_DEVICE_TABLE(of, sun8i_codec_analog_of_match);