diff --git a/arch/arm/boot/dts/amlogic/tl1_pxp.dts b/arch/arm/boot/dts/amlogic/tl1_pxp.dts index 4a89853e704b..d32721920de4 100644 --- a/arch/arm/boot/dts/amlogic/tl1_pxp.dts +++ b/arch/arm/boot/dts/amlogic/tl1_pxp.dts @@ -138,6 +138,14 @@ status = "okay"; }; + tl1_codec:codec { + #sound-dai-cells = <0>; + compatible = "amlogic, tl1_acodec"; + status = "disabled"; + reg = <0xff632000 0x1c>; + tdmout_index = <1>; + }; + auge_sound { compatible = "amlogic, tl1-sound-card"; aml-audio-card,name = "AML-AUGESOUND"; diff --git a/arch/arm/configs/meson64_a32_defconfig b/arch/arm/configs/meson64_a32_defconfig index fc40c3f2b1aa..079bdcc407b7 100644 --- a/arch/arm/configs/meson64_a32_defconfig +++ b/arch/arm/configs/meson64_a32_defconfig @@ -474,6 +474,7 @@ CONFIG_AMLOGIC_SND_CODEC_PDM_DUMMY_CODEC=y CONFIG_AMLOGIC_SND_CODEC_AMLT9015=y CONFIG_AMLOGIC_SND_CODEC_AMLT9015S=y CONFIG_AMLOGIC_SND_CODEC_TXLX_ACODEC=y +CONFIG_AMLOGIC_SND_CODEC_TL1_ACODEC=y CONFIG_AMLOGIC_SND_SOC_TAS5707=y CONFIG_AMLOGIC_SND_SOC_TLV320ADC3101=y CONFIG_AMLOGIC_SND_SOC_PCM186X=y diff --git a/sound/soc/codecs/amlogic/Kconfig b/sound/soc/codecs/amlogic/Kconfig index 6e7160b0b282..ac0d8467953e 100644 --- a/sound/soc/codecs/amlogic/Kconfig +++ b/sound/soc/codecs/amlogic/Kconfig @@ -78,6 +78,16 @@ config AMLOGIC_SND_CODEC_TXLX_ACODEC AML txlx acodec, this codec is internal +config AMLOGIC_SND_CODEC_TL1_ACODEC + bool "Amlogic Audio tl1 acodec" + depends on AMLOGIC_SND_SOC_CODECS + default n + help + Amlogic Audio codec, + AML tl1 acodec, + AML tl1 acodec, + this codec is internal + #Third part codecs # Amlogic add codecs config AMLOGIC_SND_SOC_TAS5707 diff --git a/sound/soc/codecs/amlogic/Makefile b/sound/soc/codecs/amlogic/Makefile index 1ba930ad73fd..915e6461f554 100644 --- a/sound/soc/codecs/amlogic/Makefile +++ b/sound/soc/codecs/amlogic/Makefile @@ -9,6 +9,7 @@ snd-soc-aml_t9015-objs := aml_codec_t9015.o snd-soc-aml_t9015s-objs := aml_codec_t9015S.o snd-soc-pmu3-objs := aml_pmu3.o snd-soc-aml_codec_txlx_acodec-objs := aml_codec_txlx_acodec.o +snd-soc-aml_codec_tl1_acodec-objs := aml_codec_tl1_acodec.o #Third part codecs snd-soc-tas5707-objs := tas5707.o @@ -26,6 +27,7 @@ obj-$(CONFIG_AMLOGIC_SND_CODEC_AMLT9015) += snd-soc-aml_t9015.o obj-$(CONFIG_AMLOGIC_SND_CODEC_AMLT9015S) += snd-soc-aml_t9015s.o obj-$(CONFIG_AMLOGIC_SND_CODEC_PMU3) += snd-soc-pmu3.o obj-$(CONFIG_AMLOGIC_SND_CODEC_TXLX_ACODEC) += snd-soc-aml_codec_txlx_acodec.o +obj-$(CONFIG_AMLOGIC_SND_CODEC_TL1_ACODEC) += snd-soc-aml_codec_tl1_acodec.o #Third part codecs obj-$(CONFIG_AMLOGIC_SND_SOC_TAS5707) += snd-soc-tas5707.o diff --git a/sound/soc/codecs/amlogic/aml_codec_tl1_acodec.c b/sound/soc/codecs/amlogic/aml_codec_tl1_acodec.c new file mode 100644 index 000000000000..4869c6f645da --- /dev/null +++ b/sound/soc/codecs/amlogic/aml_codec_tl1_acodec.c @@ -0,0 +1,778 @@ +/* + * linux/sound/soc/codecs/aml_codec_tl1_acodec.c + * + * Copyright 2017 AMLogic, Inc. + * + * Author: shuyu.li + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "aml_codec_tl1_acodec.h" + +struct tl1_acodec_priv { + struct snd_soc_codec *codec; + struct snd_pcm_hw_params *params; + struct regmap *regmap; + + int tdmout_index; +}; + +static const struct reg_default tl1_acodec_init_list[] = { + {ACODEC_0, 0x3403BFCF}, + {ACODEC_1, 0x50502929}, + {ACODEC_2, 0xFBFB0000}, + {ACODEC_3, 0x00002222}, + {ACODEC_4, 0x00010000}, + {ACODEC_5, 0xFBFB0033}, + {ACODEC_6, 0x0}, + {ACODEC_7, 0x0} +}; + +static int tl1_acodec_reg_init(struct snd_soc_codec *codec) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tl1_acodec_init_list); i++) + snd_soc_write(codec, tl1_acodec_init_list[i].reg, + tl1_acodec_init_list[i].def); + + return 0; +} + +static int aml_DAC_Gain_get_enum( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + + u32 reg_addr = ACODEC_1; + u32 val = snd_soc_read(codec, reg_addr); + u32 val1 = (val & (0x1 << REG_DAC_GAIN_SEL_0)) + >> REG_DAC_GAIN_SEL_0; + u32 val2 = (val & (0x1 << REG_DAC_GAIN_SEL_1)) + >> (REG_DAC_GAIN_SEL_1); + val = val1 | (val2<<1); + + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +static int aml_DAC_Gain_set_enum( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + u32 reg_addr = ACODEC_1; + u32 val = snd_soc_read(codec, reg_addr); + + if (ucontrol->value.enumerated.item[0] == 0) { + val &= ~(0x1 << REG_DAC_GAIN_SEL_1); + val &= ~(0x1 << REG_DAC_GAIN_SEL_0); + } else if (ucontrol->value.enumerated.item[0] == 1) { + val &= ~(0x1 << REG_DAC_GAIN_SEL_1); + val |= (0x1 << REG_DAC_GAIN_SEL_0); + pr_info("It has risk of distortion!\n"); + } else if (ucontrol->value.enumerated.item[0] == 2) { + val |= (0x1 << REG_DAC_GAIN_SEL_1); + val &= ~(0x1 << REG_DAC_GAIN_SEL_0); + pr_info("It has risk of distortion!\n"); + } else if (ucontrol->value.enumerated.item[0] == 3) { + val |= (0x1 << REG_DAC_GAIN_SEL_1); + val |= (0x1 << REG_DAC_GAIN_SEL_0); + pr_info("It has risk of distortion!\n"); + } + + snd_soc_write(codec, val, reg_addr); + return 0; +} + +static int aml_DAC2_Gain_get_enum( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + + u32 reg_addr = ACODEC_7; + u32 val = snd_soc_read(codec, reg_addr); + u32 val1 = (val & (0x1 << REG_DAC2_GAIN_SEL_0)) + >> REG_DAC_GAIN_SEL_0; + u32 val2 = (val & (0x1 << REG_DAC2_GAIN_SEL_1)) + >> (REG_DAC2_GAIN_SEL_1); + val = val1 | (val2<<1); + + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +static int aml_DAC2_Gain_set_enum( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + u32 reg_addr = ACODEC_7; + u32 val = snd_soc_read(codec, reg_addr); + + if (ucontrol->value.enumerated.item[0] == 0) { + val &= ~(0x1 << REG_DAC2_GAIN_SEL_1); + val &= ~(0x1 << REG_DAC2_GAIN_SEL_0); + } else if (ucontrol->value.enumerated.item[0] == 1) { + val &= ~(0x1 << REG_DAC2_GAIN_SEL_1); + val |= (0x1 << REG_DAC2_GAIN_SEL_0); + pr_info("It has risk of distortion!\n"); + } else if (ucontrol->value.enumerated.item[0] == 2) { + val |= (0x1 << REG_DAC2_GAIN_SEL_1); + val &= ~(0x1 << REG_DAC2_GAIN_SEL_0); + pr_info("It has risk of distortion!\n"); + } else if (ucontrol->value.enumerated.item[0] == 3) { + val |= (0x1 << REG_DAC2_GAIN_SEL_1); + val |= (0x1 << REG_DAC2_GAIN_SEL_0); + pr_info("It has risk of distortion!\n"); + } + + snd_soc_write(codec, val, reg_addr); + return 0; +} + + +static const DECLARE_TLV_DB_SCALE(pga_in_tlv, -1200, 250, 1); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -29625, 375, 1); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -95250, 375, 1); +static const DECLARE_TLV_DB_SCALE(dac2_vol_tlv, -95250, 375, 1); + +static const char *const DAC_Gain_texts[] = { "0dB", "6dB", "12dB", "18dB" }; +static const char *const DAC2_Gain_texts[] = { "0dB", "6dB", "12dB", "18dB" }; + +static const struct soc_enum DAC_Gain_enum = SOC_ENUM_SINGLE( + SND_SOC_NOPM, 0, ARRAY_SIZE(DAC_Gain_texts), + DAC_Gain_texts); +static const struct soc_enum DAC2_Gain_enum = SOC_ENUM_SINGLE( + SND_SOC_NOPM, 0, ARRAY_SIZE(DAC2_Gain_texts), + DAC2_Gain_texts); + +static const struct snd_kcontrol_new tl1_acodec_snd_controls[] = { + /*PGA_IN Gain */ + SOC_DOUBLE_TLV("PGA IN Gain", ACODEC_1, + PGAL_IN_GAIN, PGAR_IN_GAIN, + 0x1f, 0, pga_in_tlv), + + /*ADC Digital Volume control */ + SOC_DOUBLE_TLV("ADC Digital Capture Volume", ACODEC_1, + ADCL_VC, ADCR_VC, + 0x7f, 0, adc_vol_tlv), + + /*DAC Digital Volume control */ + SOC_DOUBLE_TLV("DAC Digital Playback Volume", + ACODEC_2, + DACL_VC, DACR_VC, + 0xff, 0, dac_vol_tlv), + + /*DAC 2 Digital Volume control */ + SOC_DOUBLE_TLV("DAC 2 Digital Playback Volume", + ACODEC_5, + DAC2L_VC, DAC2R_VC, + 0xff, 0, dac2_vol_tlv), + + /*DAC extra Digital Gain control */ + SOC_ENUM_EXT("DAC Extra Digital Gain", + DAC_Gain_enum, + aml_DAC_Gain_get_enum, + aml_DAC_Gain_set_enum), + + /* TODO: DAC 2 extra Digital Gain control */ + SOC_ENUM_EXT("DAC2 Extra Digital Gain", + DAC2_Gain_enum, + aml_DAC2_Gain_get_enum, + aml_DAC2_Gain_set_enum), +}; + +/*pgain Left Channel Input */ +static const char * const linein_left_txt[] = { + "None", "AIL1", "AIL2", "AIL3", "AIL4", +}; + +static const SOC_ENUM_SINGLE_DECL(linein_left_enum, + ACODEC_1, + PGAL_IN_SEL, linein_left_txt); + +static const struct snd_kcontrol_new lil_mux = +SOC_DAPM_ENUM("ROUTE_L", linein_left_enum); + +/*pgain right Channel Input */ +static const char * const linein_right_txt[] = { + "None", "AIR1", "AIR2", "AIR3", "AIR4", +}; + +static const SOC_ENUM_SINGLE_DECL(linein_right_enum, + ACODEC_1, + PGAR_IN_SEL, linein_right_txt); + +static const struct snd_kcontrol_new lir_mux = +SOC_DAPM_ENUM("ROUTE_R", linein_right_enum); + + +/*line out 1 Left mux */ +static const char * const out_lo1l_txt[] = { + "None", "LO1L_SEL_INL", "LO1L_SEL_DACL", "Reserved", "LO1L_SEL_DACR_INV" +}; + +static const SOC_ENUM_SINGLE_DECL(out_lo1l_enum, ACODEC_3, + LO1L_SEL_INL, out_lo1l_txt); + +static const struct snd_kcontrol_new lo1l_mux = +SOC_DAPM_ENUM("LO1L_MUX", out_lo1l_enum); + +/*line out 1 right mux */ +static const char * const out_lo1r_txt[] = { + "None", "LO1R_SEL_INR", "LO1R_SEL_DACR", "Reserved", "LO1R_SEL_DACL_INV" +}; + +static const SOC_ENUM_SINGLE_DECL(out_lo1r_enum, ACODEC_3, + LO1R_SEL_INR, out_lo1r_txt); + +static const struct snd_kcontrol_new lo1r_mux = +SOC_DAPM_ENUM("LO1R_MUX", out_lo1r_enum); + +/*line out 2 left mux */ +static const char * const out_lo2l_txt[] = { + "None", "LO2L_SEL_INL", "LO2L_SEL_DAC2L", "Reserved", + "LO2L_SEL_DAC2R_INV" +}; + +static const SOC_ENUM_SINGLE_DECL(out_lo2l_enum, ACODEC_3, + LO2L_SEL_INL, out_lo2l_txt); + +static const struct snd_kcontrol_new lo2l_mux = +SOC_DAPM_ENUM("LO2L_MUX", out_lo2l_enum); + +/*line out 2 Right mux */ +static const char * const out_lo2r_txt[] = { + "None", "LO2R_SEL_INR", "LO2R_SEL_DAC2R", "Reserved", + "LO2R_SEL_DAC2L_INV" +}; + +static const SOC_ENUM_SINGLE_DECL(out_lo2r_enum, ACODEC_3, + LO2R_SEL_INR, out_lo2r_txt); + +static const struct snd_kcontrol_new lo2r_mux = +SOC_DAPM_ENUM("LO2R_MUX", out_lo2r_enum); + + +static const struct snd_soc_dapm_widget tl1_acodec_dapm_widgets[] = { + + /* Input */ + SND_SOC_DAPM_INPUT("Linein left 1"), + SND_SOC_DAPM_INPUT("Linein left 2"), + SND_SOC_DAPM_INPUT("Linein left 3"), + SND_SOC_DAPM_INPUT("Linein left 4"), + + SND_SOC_DAPM_INPUT("Linein right 1"), + SND_SOC_DAPM_INPUT("Linein right 2"), + SND_SOC_DAPM_INPUT("Linein right 3"), + SND_SOC_DAPM_INPUT("Linein right 4"), + + /*PGA input */ + SND_SOC_DAPM_PGA("PGAL_IN_EN", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGAR_IN_EN", SND_SOC_NOPM, + 0, 0, NULL, 0), + + /*PGA input source select */ + SND_SOC_DAPM_MUX("Linein left switch", SND_SOC_NOPM, + 0, 0, &lil_mux), + SND_SOC_DAPM_MUX("Linein right switch", SND_SOC_NOPM, + 0, 0, &lir_mux), + + /*ADC capture stream */ + SND_SOC_DAPM_ADC("Left ADC", "Capture", ACODEC_0, + ADCL_EN, 0), + SND_SOC_DAPM_ADC("Right ADC", "Capture", ACODEC_0, + ADCR_EN, 0), + + /*Output */ + SND_SOC_DAPM_OUTPUT("Lineout 1 left"), + SND_SOC_DAPM_OUTPUT("Lineout 1 right"), + SND_SOC_DAPM_OUTPUT("Lineout 2 left"), + SND_SOC_DAPM_OUTPUT("Lineout 2 right"), + + /*DAC playback stream */ + SND_SOC_DAPM_DAC("Left DAC", "Playback", + ACODEC_0, + DACL_EN, 0), + SND_SOC_DAPM_DAC("Right DAC", "Playback", + ACODEC_0, + DACR_EN, 0), + + /*DAC 2 playback stream */ + SND_SOC_DAPM_DAC("Left DAC2", "Playback", + ACODEC_5, + DAC2L_EN, 0), + SND_SOC_DAPM_DAC("Right DAC2", "Playback", + ACODEC_5, + DAC2R_EN, 0), + + /*DRV output */ + SND_SOC_DAPM_OUT_DRV("LO1L_OUT_EN", ACODEC_0, + LO1L_EN, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("LO1R_OUT_EN", ACODEC_0, + LO1R_EN, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("LO2L_OUT_EN", ACODEC_0, + LO2L_EN, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("LO2R_OUT_EN", ACODEC_0, + LO2R_EN, 0, NULL, 0), + + /*MUX output source select */ + SND_SOC_DAPM_MUX("Lineout 1 left switch", SND_SOC_NOPM, + 0, 0, &lo1l_mux), + SND_SOC_DAPM_MUX("Lineout 1 right switch", SND_SOC_NOPM, + 0, 0, &lo1r_mux), + SND_SOC_DAPM_MUX("Lineout 2 left switch", SND_SOC_NOPM, + 0, 0, &lo2l_mux), + SND_SOC_DAPM_MUX("Lineout 2 right switch", SND_SOC_NOPM, + 0, 0, &lo2r_mux), + +}; + +static const struct snd_soc_dapm_route tl1_acodec_dapm_routes[] = { +/* Input path */ + {"Linein left switch", "AIL1", "Linein left 1"}, + {"Linein left switch", "AIL2", "Linein left 2"}, + {"Linein left switch", "AIL3", "Linein left 3"}, + {"Linein left switch", "AIL4", "Linein left 4"}, + + {"Linein right switch", "AIR1", "Linein right 1"}, + {"Linein right switch", "AIR2", "Linein right 2"}, + {"Linein right switch", "AIR3", "Linein right 3"}, + {"Linein right switch", "AIR4", "Linein right 4"}, + + {"PGAL_IN_EN", NULL, "Linein left switch"}, + {"PGAR_IN_EN", NULL, "Linein right switch"}, + + {"Left ADC", NULL, "PGAL_IN_EN"}, + {"Right ADC", NULL, "PGAR_IN_EN"}, + +/*Output path*/ + {"Lineout 1 left switch", NULL, "Left DAC"}, + {"Lineout 1 left switch", NULL, "Right DAC"}, + {"Lineout 1 left switch", NULL, "PGAL_IN_EN"}, + + {"Lineout 1 right switch", NULL, "Right DAC"}, + {"Lineout 1 right switch", NULL, "Left DAC"}, + {"Lineout 1 right switch", NULL, "PGAR_IN_EN"}, + + {"Lineout 2 left switch", NULL, "Left DAC2"}, + {"Lineout 2 left switch", NULL, "Right DAC2"}, + {"Lineout 2 left switch", NULL, "PGAL_IN_EN"}, + + {"Lineout 2 right switch", NULL, "Right DAC2"}, + {"Lineout 2 right switch", NULL, "Left DAC2"}, + {"Lineout 2 right switch", NULL, "PGAR_IN_EN"}, + + {"LO1L_OUT_EN", NULL, "Lineout 1 left switch"}, + {"LO1R_OUT_EN", NULL, "Lineout 1 right switch"}, + {"LO2L_OUT_EN", NULL, "Lineout 2 left switch"}, + {"LO2R_OUT_EN", NULL, "Lineout 2 right switch"}, + + {"Lineout 1 left", NULL, "LO1L_OUT_EN"}, + {"Lineout 1 right", NULL, "LO1R_OUT_EN"}, + {"Lineout 2 left", NULL, "LO2L_OUT_EN"}, + {"Lineout 2 right", NULL, "LO2R_OUT_EN"}, +}; + +static int tl1_acodec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + u32 val = snd_soc_read(codec, ACODEC_0); + + pr_debug("%s, format:%x, codec = %p\n", __func__, fmt, codec); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + val |= (0x1 << I2S_MODE); + break; + case SND_SOC_DAIFMT_CBS_CFS: + val &= ~(0x1 << I2S_MODE); + break; + default: + return -EINVAL; + } + + snd_soc_write(codec, ACODEC_0, val); + + return 0; +} + +static int tl1_acodec_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + return 0; +} + +static int tl1_acodec_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + struct tl1_acodec_priv *aml_acodec = + snd_soc_codec_get_drvdata(codec); + + pr_debug("%s!\n", __func__); + + aml_acodec->params = params; + + return 0; +} + +static int tl1_acodec_dai_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + + break; + + case SND_SOC_BIAS_PREPARE: + + break; + + case SND_SOC_BIAS_STANDBY: + if (codec->component.dapm.bias_level == SND_SOC_BIAS_OFF) { + snd_soc_cache_sync(codec); + } + break; + + case SND_SOC_BIAS_OFF: + snd_soc_write(codec, ACODEC_0, 0); + break; + + default: + break; + } + codec->component.dapm.bias_level = level; + + return 0; +} + +static int tl1_acodec_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return 0; +} + +//TODO, need to check +static int tl1_acodec_reset(struct snd_soc_codec *codec) +{ + struct tl1_acodec_priv *tl1_acodec = + snd_soc_codec_get_drvdata(codec); + if (tl1_acodec) + auge_acodec_reset(); + udelay(1000); + return 0; +} +//TODO, need to check +static int tl1_acodec_start_up(struct snd_soc_codec *codec) +{ + snd_soc_write(codec, ACODEC_0, 0xF000); + msleep(200); + snd_soc_write(codec, ACODEC_0, 0xB000); + + return 0; +} + +static int tl1_acodec_dai_mute_stream(struct snd_soc_dai *dai, int mute, + int stream) +{ + struct tl1_acodec_priv *aml_acodec = + snd_soc_codec_get_drvdata(dai->codec); + u32 reg_val; + + pr_debug("%s, mute:%d\n", __func__, mute); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* DAC 1 */ + regmap_read(aml_acodec->regmap, + ACODEC_2, + ®_val); + if (mute) + reg_val |= DAC_SOFT_MUTE; + else + reg_val &= ~DAC_SOFT_MUTE; + + regmap_write(aml_acodec->regmap, + ACODEC_2, + reg_val); + + + /* DAC 2 */ + regmap_read(aml_acodec->regmap, + ACODEC_6, + ®_val); + if (mute) + reg_val |= DAC2_SOFT_MUTE; + else + reg_val &= ~DAC2_SOFT_MUTE; + + regmap_write(aml_acodec->regmap, + ACODEC_6, + reg_val); + } + + return 0; +} + +struct snd_soc_dai_ops tl1_acodec_dai_ops = { + .hw_params = tl1_acodec_dai_hw_params, + .prepare = tl1_acodec_dai_prepare, + .set_fmt = tl1_acodec_dai_set_fmt, + .set_sysclk = tl1_acodec_dai_set_sysclk, + .mute_stream = tl1_acodec_dai_mute_stream, +}; + +static int tl1_acodec_probe(struct snd_soc_codec *codec) +{ + struct tl1_acodec_priv *aml_acodec = + snd_soc_codec_get_drvdata(codec); + + if (!aml_acodec) { + pr_err("Failed to get tl1 acodec pri\n"); + return -EINVAL; + } + + /*reset audio codec register*/ + tl1_acodec_reset(codec); + tl1_acodec_start_up(codec); + tl1_acodec_reg_init(codec); + + if (aml_acodec) + auge_toacodec_ctrl(aml_acodec->tdmout_index); + + aml_acodec->codec = codec; + tl1_acodec_dai_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} + +static int tl1_acodec_remove(struct snd_soc_codec *codec) +{ + pr_info("%s!\n", __func__); + + tl1_acodec_dai_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int tl1_acodec_suspend(struct snd_soc_codec *codec) +{ + pr_info("%s!\n", __func__); + + tl1_acodec_dai_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int tl1_acodec_resume(struct snd_soc_codec *codec) +{ + pr_info("%s!\n", __func__); + + tl1_acodec_reset(codec); + tl1_acodec_start_up(codec); + tl1_acodec_reg_init(codec); + + tl1_acodec_dai_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_tl1_acodec = { + .probe = tl1_acodec_probe, + .remove = tl1_acodec_remove, + .suspend = tl1_acodec_suspend, + .resume = tl1_acodec_resume, + .set_bias_level = tl1_acodec_dai_set_bias_level, + .component_driver = { + .controls = tl1_acodec_snd_controls, + .num_controls = ARRAY_SIZE(tl1_acodec_snd_controls), + .dapm_widgets = tl1_acodec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tl1_acodec_dapm_widgets), + .dapm_routes = tl1_acodec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tl1_acodec_dapm_routes), + } +}; + +static const struct regmap_config tl1_acodec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1c, + .reg_defaults = tl1_acodec_init_list, + .num_reg_defaults = ARRAY_SIZE(tl1_acodec_init_list), + .cache_type = REGCACHE_RBTREE, +}; + +#define TL1_ACODEC_RATES SNDRV_PCM_RATE_8000_96000 +#define TL1_ACODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE \ + | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE \ + | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S32_LE) + +struct snd_soc_dai_driver aml_tl1_acodec_dai = { + .name = "tl1-acodec-hifi", + .id = 0, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = TL1_ACODEC_RATES, + .formats = TL1_ACODEC_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 8, + .rates = TL1_ACODEC_RATES, + .formats = TL1_ACODEC_FORMATS, + }, + .ops = &tl1_acodec_dai_ops, +}; + +static int aml_tl1_acodec_probe(struct platform_device *pdev) +{ + struct tl1_acodec_priv *aml_acodec; + struct resource *res_mem; + struct device_node *np; + void __iomem *regs; + int ret = 0; + + dev_info(&pdev->dev, "%s\n", __func__); + + np = pdev->dev.of_node; + + aml_acodec = devm_kzalloc(&pdev->dev, sizeof(struct tl1_acodec_priv), + GFP_KERNEL); + if (!aml_acodec) + return -ENOMEM; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mem) + return -ENODEV; + + regs = devm_ioremap_resource(&pdev->dev, res_mem); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + aml_acodec->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &tl1_acodec_regmap_config); + + of_property_read_u32( + pdev->dev.of_node, + "tdmout_index", + &aml_acodec->tdmout_index); + + pr_info("aml_tl1_acodec is used by tdmout:%d\n", + aml_acodec->tdmout_index); + + if (IS_ERR(aml_acodec->regmap)) + return PTR_ERR(aml_acodec->regmap); + + platform_set_drvdata(pdev, aml_acodec); + + ret = snd_soc_register_codec(&pdev->dev, + &soc_codec_dev_tl1_acodec, + &aml_tl1_acodec_dai, 1); + + return ret; +} + +static int aml_tl1_acodec_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + + return 0; +} + +static void aml_tl1_acodec_shutdown(struct platform_device *pdev) +{ + struct tl1_acodec_priv *aml_acodec; + struct snd_soc_codec *codec; + + aml_acodec = platform_get_drvdata(pdev); + codec = aml_acodec->codec; + if (codec) + tl1_acodec_remove(codec); +} + +static const struct of_device_id aml_tl1_acodec_dt_match[] = { + {.compatible = "amlogic, tl1_codec",}, + {}, +}; + +static struct platform_driver aml_tl1_acodec_platform_driver = { + .driver = { + .name = "tl1_codec", + .owner = THIS_MODULE, + .of_match_table = aml_tl1_acodec_dt_match, + }, + .probe = aml_tl1_acodec_probe, + .remove = aml_tl1_acodec_remove, + .shutdown = aml_tl1_acodec_shutdown, +}; + +static int __init aml_tl1_acodec_modinit(void) +{ + int ret = 0; + + ret = platform_driver_register(&aml_tl1_acodec_platform_driver); + if (ret != 0) { + pr_err( + "Failed to register AML tl1 acodec platform driver: %d\n", + ret); + } + + return ret; +} + +module_init(aml_tl1_acodec_modinit); + +static void __exit aml_tl1_acodec_modexit(void) +{ + platform_driver_unregister(&aml_tl1_acodec_platform_driver); +} + +module_exit(aml_tl1_acodec_modexit); + +MODULE_DESCRIPTION("ASoC AML TL1 audio codec driver"); +MODULE_AUTHOR("AMLogic, Inc."); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/amlogic/aml_codec_tl1_acodec.h b/sound/soc/codecs/amlogic/aml_codec_tl1_acodec.h new file mode 100644 index 000000000000..c133fb17631f --- /dev/null +++ b/sound/soc/codecs/amlogic/aml_codec_tl1_acodec.h @@ -0,0 +1,138 @@ +/* + * aml_codec_tl1_acodec.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _TL1_ACODEC_H +#define _TL1_ACODEC_H + +#define DEV_NAME "tl1_acodec" + +/* AML TL1 CODEC register space (in decimal to match datasheet) */ +//#define ACODEC_BASE_ADD 0xFF632000 +#define ACODEC_TOP_ADDR(x) (x) + + +/* AML TL1 CODEC register define */ +#define ACODEC_0 ACODEC_TOP_ADDR(0x00) +#define ACODEC_1 ACODEC_TOP_ADDR(0x04) +#define ACODEC_2 ACODEC_TOP_ADDR(0x08) +#define ACODEC_3 ACODEC_TOP_ADDR(0x0c) +#define ACODEC_4 ACODEC_TOP_ADDR(0x10) +#define ACODEC_5 ACODEC_TOP_ADDR(0x14) +#define ACODEC_6 ACODEC_TOP_ADDR(0x18) +#define ACODEC_7 ACODEC_TOP_ADDR(0x1C) + +/* AML TL1 CODEC register-bitfield define */ + +// bitfield def of ACODEC_0 +#define MCLK_FREQ 31 +#define I2S_MODE 30 +#define ADC_HPF_EN 29 +#define ADC_HPF_MODE 28 +#define ADC_OVERLOAD_DET_EN 27 +#define ADC_DEM_EN 26 +#define ADC_CLK_TO_GPIO_EN 25 +#define DAC_CLK_TO_GPIO_EN 24 +#define DACL_DATA_SOURCE 23 +#define DACR_DATA_SOURCE 22 +#define DACL_INV 21 +#define DACR_INV 20 +#define ADCDATL_SOURCE 19 +#define ADCDATR_SOURCE 18 +#define ADCL_INV 17 +#define ADCR_INV 16 +#define VMID_GEN_EN 15 +#define VMID_GEN_FAST 14 +#define BIAS_CURRENT_EN 13 +#define REFP_BUF_EN 12 +#define PGAL_IN_EN 11 +#define PGAR_IN_EN 10 +#define PGAL_IN_ZC_EN 9 +#define PGAR_IN_ZC_EN 8 +#define ADCL_EN 7 +#define ADCR_EN 6 +//#define DACL_EN 5 +//#define DACR_EN 4 +#define LO1L_EN 3 +#define LO1R_EN 2 +#define LO2L_EN 1 +#define LO2R_EN 0 + +// bitfield def of ACODEC_1 +#define REG_DAC_GAIN_SEL_1 31 +#define ADCL_VC 24 /* bit 30-24 */ +#define REG_DAC_GAIN_SEL_0 23 +#define ADCR_VC 16 /* bit 22-16 */ +#define PGAL_IN_SEL 13 /* bit 15-13 */ +#define PGAL_IN_GAIN 8 /* bit 12-8 */ +#define PGAR_IN_SEL 5 /* bit 7-5 */ +#define PGAR_IN_GAIN 0 /* bit 4-0 */ + +// bitfield def of ACODEC_2 +#define DACL_VC 24 /* bit 31-24 */ +#define DACR_VC 16 /* bit 23-16 */ +#define DAC_SOFT_MUTE 15 +#define DAC_UNMUTE_MODE 14 +#define DAC_MUTE_MODE 13 +#define DAC_VC_RAMP_MODE 12 +#define DAC_RAMP_RATE 10 /* bit 11-10 */ +#define DAC_MONO 8 +#define MUTE_DAC_PD_EN 7 + +// bitfield def of ACODEC_3 +#define REG_MICBIAS_EN 31 +#define REG_MICBIAS_SEL 29 /* bit 29, 30 */ +//#define REG_ANA_RESERVED 16 /* bit 16 ~ 28 */ +#define LO1L_SEL_DAC1R_INV 14 +#define LO1L_SEL_DAC1L 13 +#define LO1L_SEL_INL 12 +#define LO1R_SEL_DAC1L_INV 10 +#define LO1R_SEL_DAC1R 9 +#define LO1R_SEL_INR 8 +#define LO2L_SEL_DAC2R_INV 6 +#define LO2L_SEL_DAC2L 5 +#define LO2L_SEL_INL 4 +#define LO2R_SEL_DAC2L_INV 2 +#define LO2R_SEL_DAC2R 1 +#define LO2R_SEL_INR 0 + +// bitfield def of ACODEC_4 +#define MUTE_DAC_WHEN_POWER_DOWN 31 +#define IB_CON 16 /* bit 16, 17 */ +#define REG_ADCL_SAT_SEL 2 /* bit 2, 3 */ +#define REG_ADCR_SAT_SEL 0 /* bit 0, 1 */ + +// bitfield def of ACODEC_5 +#define DAC2L_VC 24 /* bit 24~31 */ +#define DAC2R_VC 16 /* bit 16~23 */ +#define DAC2L_EN 5 +#define DAC2R_EN 4 +#define DACL_EN 1 +#define DACR_EN 0 + + + +// bitfield def of ACODEC_6 +#define DAC2_SOFT_MUTE 31 +#define DAC2_UNMUTE_MODE 30 +#define DAC2_MUTE_MODE 29 +#define DAC2_VC_RAMP_MODE 28 +#define DAC2_RAMP_RATE 26 /* bit 27-26 */ +#define DAC2_MONO 24 +#define MUTE_DAC2_PD_EN 23 +#define DAC2_CLK_TO_GPIO_EN 8 +#define DAC2L_DATA_SOURCE 7 +#define DAC2R_DATA_SOURCE 6 +#define DAC2L_INV 5 +#define DAC2R_INV 4 + +// bitfield def of ACODEC_7 +#define DEBUG_BUS_SEL 16 /* bit 16~18 */ +#define REG_DAC2_GAIN_SEL_1 15 +#define REG_DAC2_GAIN_SEL_0 7 + +#endif /*_TL1_ACODEC_H*/