diff --git a/MAINTAINERS b/MAINTAINERS index ab180e1c6ea9..7a91f588421c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13994,3 +13994,11 @@ F: driver/amlogic/ddr_window/Kconfig AMLOGIC AUDIO INFO M: wang xing F: drivers/amlogic/audioinfo/ + +AMLOGIC AXG ADD LOOPBACK INTERFACE +M: wang xing +F: arch/arm64/boot/dts/amlogic/axg_s400.dts +F: sound/soc/amlogic/auge/* +F: sound/soc/codecs/amlogic/pdm_dummy.c +F: sound/soc/codecs/amlogic/tlv320adc3101.c + diff --git a/arch/arm64/boot/dts/amlogic/axg_s400.dts b/arch/arm64/boot/dts/amlogic/axg_s400.dts index 99304676568f..904b5e44b826 100644 --- a/arch/arm64/boot/dts/amlogic/axg_s400.dts +++ b/arch/arm64/boot/dts/amlogic/axg_s400.dts @@ -446,6 +446,8 @@ 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 = <512>; @@ -984,6 +986,40 @@ 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 = <2>; + datalb_chnum = <8>; + datalb_chmask = <0x3>; + + status = "okay"; + }; }; /* end of audiobus */ &pinctrl_periphs { diff --git a/sound/soc/amlogic/auge/Makefile b/sound/soc/amlogic/auge/Makefile index ec978fb913d6..63a2c3789ec3 100644 --- a/sound/soc/amlogic/auge/Makefile +++ b/sound/soc/amlogic/auge/Makefile @@ -11,4 +11,6 @@ obj-$(CONFIG_AMLOGIC_SND_SOC_AUGE) += audio_controller.o \ pdm_hw.o \ pdm_hw_coeff.o \ iomap.o \ - ddr_mngr.o + ddr_mngr.o \ + loopback_hw.o \ + audio_utils.o diff --git a/sound/soc/amlogic/auge/audio_utils.c b/sound/soc/amlogic/auge/audio_utils.c new file mode 100644 index 000000000000..b25dc8f1bc7a --- /dev/null +++ b/sound/soc/amlogic/auge/audio_utils.c @@ -0,0 +1,331 @@ +/* + * sound/soc/amlogic/auge/audio_utils.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * 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 "audio_utils.h" +#include "regs.h" +#include "iomap.h" +#include "loopback_hw.h" + +static unsigned int loopback_enable; + +static const char *const loopback_enable_texts[] = { + "Disable", + "Enable", +}; + +static const struct soc_enum loopback_enable_enum = + SOC_ENUM_SINGLE(EE_AUDIO_LB_CTRL0, + 31, + ARRAY_SIZE(loopback_enable_texts), + loopback_enable_texts); + + +static int loopback_enable_get_enum( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = loopback_enable; + + return 0; +} + +static int loopback_enable_set_enum( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + loopback_enable = ucontrol->value.enumerated.item[0]; + + return 0; +} + + +static unsigned int loopback_datain; + +static const char *const loopback_datain_texts[] = { + "Tdmin A", + "Tdmin B", + "Tdmin C", + "Spdif In", + "Pdm In", +}; + +static const struct soc_enum loopback_datain_enum = + SOC_ENUM_SINGLE(EE_AUDIO_LB_CTRL0, 0, ARRAY_SIZE(loopback_datain_texts), + loopback_datain_texts); + + +static int loopback_datain_get_enum( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = loopback_datain; + + return 0; +} + +static int loopback_datain_set_enum( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + loopback_datain = ucontrol->value.enumerated.item[0]; + audiobus_update_bits(EE_AUDIO_LB_CTRL0, 0, loopback_datain); + + return 0; +} + +static unsigned int loopback_tdminlb; + +static const char *const loopback_tdminlb_texts[] = { + "tdmoutA", + "tdmoutB", + "tdmoutC", + "tdminA", + "tdminB", + "tdminC", +}; + +static const struct soc_enum loopback_tdminlb_enum = + SOC_ENUM_SINGLE(EE_AUDIO_TDMIN_LB_CTRL, + 20, + ARRAY_SIZE(loopback_tdminlb_texts), + loopback_tdminlb_texts); + + +static int loopback_tdminlb_get_enum( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = loopback_tdminlb; + + return 0; +} + +static int loopback_tdminlb_set_enum( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + loopback_tdminlb = ucontrol->value.enumerated.item[0]; + audiobus_update_bits(EE_AUDIO_TDMIN_LB_CTRL, + 0xf << 20, + loopback_datain); + + return 0; +} + + +static const struct snd_kcontrol_new snd_auge_controls[] = { + /* loopback enable */ + SOC_ENUM_EXT("Loopback Enable", + loopback_enable_enum, + loopback_enable_get_enum, + loopback_enable_set_enum), + + /* loopback data in source */ + SOC_ENUM_EXT("Loopback datain source", + loopback_datain_enum, + loopback_datain_get_enum, + loopback_datain_set_enum), + + /* loopback data tdmin lb */ + SOC_ENUM_EXT("Loopback tmdin lb source", + loopback_tdminlb_enum, + loopback_tdminlb_get_enum, + loopback_tdminlb_set_enum), + +}; + + +int snd_card_add_kcontrols(struct snd_soc_card *card) +{ + pr_info("%s card:%p\n", __func__, card); + return snd_soc_add_card_controls(card, + snd_auge_controls, ARRAY_SIZE(snd_auge_controls)); + +} + +int loopback_parse_of(struct device_node *node, + struct loopback_cfg *lb_cfg) +{ + int ret; + + ret = of_property_read_u32(node, "lb_mode", + &lb_cfg->lb_mode); + if (ret) { + pr_err("failed to get lb_mode, set it default\n"); + lb_cfg->lb_mode = 0; + ret = 0; + } + + ret = of_property_read_u32(node, "datain_src", + &lb_cfg->datain_src); + if (ret) { + pr_err("failed to get datain_src\n"); + ret = -EINVAL; + goto fail; + } + ret = of_property_read_u32(node, "datain_chnum", + &lb_cfg->datain_chnum); + if (ret) { + pr_err("failed to get datain_chnum\n"); + ret = -EINVAL; + goto fail; + } + ret = of_property_read_u32(node, "datain_chmask", + &lb_cfg->datain_chmask); + if (ret) { + pr_err("failed to get datain_chmask\n"); + ret = -EINVAL; + goto fail; + } + + ret = of_property_read_u32(node, "datalb_src", + &lb_cfg->datalb_src); + if (ret) { + pr_err("failed to get datalb_src\n"); + ret = -EINVAL; + goto fail; + } + ret = of_property_read_u32(node, "datalb_chnum", + &lb_cfg->datalb_chnum); + if (ret) { + pr_err("failed to get datalb_chnum\n"); + ret = -EINVAL; + goto fail; + } + ret = of_property_read_u32(node, "datalb_chmask", + &lb_cfg->datalb_chmask); + if (ret) { + pr_err("failed to get datalb_chmask\n"); + ret = -EINVAL; + goto fail; + } + + + loopback_datain = lb_cfg->datain_src; + loopback_tdminlb = lb_cfg->datalb_src; + + pr_info("parse loopback:, \tlb mode:%d\n", + lb_cfg->lb_mode); + pr_info("\tdatain_src:%d, datain_chnum:%d, datain_chumask:%x\n", + lb_cfg->datain_src, + lb_cfg->datain_chnum, + lb_cfg->datain_chmask); + pr_info("\tdatalb_src:%d, datalb_chnum:%d, datalb_chumask:%x\n", + lb_cfg->datalb_src, + lb_cfg->datalb_chnum, + lb_cfg->datalb_chmask); + +fail: + return ret; +} + +int loopback_prepare( + struct snd_pcm_substream *substream, + struct loopback_cfg *lb_cfg) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int bitwidth; + unsigned int datain_toddr_type, datalb_toddr_type; + unsigned int datain_msb, datain_lsb, datalb_msb, datalb_lsb; + struct lb_cfg datain_cfg; + struct lb_cfg datalb_cfg; + struct audio_data ddrdata; + struct data_in datain; + struct data_lb datalb; + + if (!lb_cfg) + return -EINVAL; + + bitwidth = snd_pcm_format_width(runtime->format); + switch (lb_cfg->datain_src) { + case DATAIN_TDMA: + case DATAIN_TDMB: + case DATAIN_TDMC: + case DATAIN_PDM: + datain_toddr_type = 0; + datain_msb = 32 - 1; + datain_lsb = 0; + break; + case DATAIN_SPDIF: + datain_toddr_type = 3; + datain_msb = 27; + datain_lsb = 4; + if (bitwidth <= 24) + datain_lsb = 28 - bitwidth; + else + datain_lsb = 4; + break; + default: + pr_err("unsupport data in source:%d\n", + lb_cfg->datain_src); + return -EINVAL; + } + + switch (lb_cfg->datalb_src) { + case TDMINLB_TDMOUTA: + case TDMINLB_TDMOUTB: + case TDMINLB_TDMOUTC: + case TDMINLB_PAD_TDMINA: + case TDMINLB_PAD_TDMINB: + case TDMINLB_PAD_TDMINC: + if (bitwidth == 24) { + datalb_toddr_type = 4; + datalb_msb = 32 - 1; + datalb_lsb = 32 - bitwidth; + } else { + datalb_toddr_type = 0; + datalb_msb = 32 - 1; + datalb_lsb = 0; + } + break; + default: + pr_err("unsupport data lb source:%d\n", + lb_cfg->datalb_src); + return -EINVAL; + } + + datain_cfg.ext_signed = 0; + datain_cfg.chnum = runtime->channels; + datain_cfg.chmask = lb_cfg->datain_chmask; + ddrdata.combined_type = datain_toddr_type; + ddrdata.msb = datain_msb; + ddrdata.lsb = datain_lsb; + ddrdata.src = lb_cfg->datain_src; + datain.config = &datain_cfg; + datain.ddrdata = &ddrdata; + + datalb_cfg.ext_signed = 0; + datalb_cfg.chnum = lb_cfg->datalb_chnum; + datalb_cfg.chmask = lb_cfg->datalb_chmask; + datalb.config = &datalb_cfg; + datalb.ddr_type = datalb_toddr_type; + datalb.msb = datalb_msb; + datalb.lsb = datalb_lsb; + + datain_config(&datain); + datalb_config(&datalb); + + datalb_ctrl(lb_cfg->datalb_src, bitwidth); + lb_enable_ex(lb_cfg->lb_mode, true); + + return 0; +} + +int loopback_is_enable(void) +{ + return (loopback_enable == 1); +} diff --git a/sound/soc/amlogic/auge/audio_utils.h b/sound/soc/amlogic/auge/audio_utils.h new file mode 100644 index 000000000000..04a012493494 --- /dev/null +++ b/sound/soc/amlogic/auge/audio_utils.h @@ -0,0 +1,137 @@ +/* + * sound/soc/amlogic/auge/audio_utils.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef __AML_AUDIO_UTILS_H__ +#define __AML_AUDIO_UTILS_H__ + +#include +#include + +/* datain src + * [4]: pdmin; + * [3]: spdifin; + * [2]: tdmin_c; + * [1]: tdmin_b; + * [0]: tdmin_a; + */ +enum datain_src { + DATAIN_TDMA = 0, + DATAIN_TDMB, + DATAIN_TDMC, + DATAIN_SPDIF, + DATAIN_PDM, +}; + +/* datalb src/tdmlb src + * [0]: tdmoutA + * [1]: tdmoutB + * [2]: tdmoutC + * [3]: PAD_tdminA + * [4]: PAD_tdminB + * [5]: PAD_tdminC + */ +enum tdmin_lb_src { + TDMINLB_TDMOUTA = 0, + TDMINLB_TDMOUTB, + TDMINLB_TDMOUTC, + TDMINLB_PAD_TDMINA, + TDMINLB_PAD_TDMINB, + TDMINLB_PAD_TDMINC, +}; + +/* toddr_src_sel + * [7]: loopback; + * [6]: tdmin_lb; + * [5]: reserved; + * [4]: pdmin; + * [3]: spdifin; + * [2]: tdmin_c; + * [1]: tdmin_b; + * [0]: tdmin_a; + */ +enum fifoin_src { + FIFOIN_TDMINA = 0, + FIFOIN_TDMINB = 1, + FIFOIN_TDMINC = 2, + FIFOIN_SPDIF = 3, + FIFOIN_PDM = 4, + FIFOIN_TDMINLB = 6, + FIFOIN_LOOPBACK = 7 +}; + +/* audio data selected to ddr */ +struct audio_data { + unsigned int resample; + /* reg_dat_sel + * 0: combined data[m:n] without gap; + * like S0[m:n],S1[m:n],S2[m:n], + * 1: combined data[m:n] as 16bits; + * like {S0[11:0],4'd0},{S1[11:0],4'd0} + * 2: combined data[m:n] as 16bits; + * like {4'd0,S0[11:0]},{4'd0,{S1[11:0]} + * 3: combined data[m:n] as 32bits; + * like {S0[27:4],8'd0},{S1[27:4],8'd0} + * 4: combined data[m:n] as 32bits; + * like {8'd0,S0[27:4]},{8'd0,{S1[27:4]} + */ + unsigned int combined_type; + /* the msb positioin in data */ + unsigned int msb; + /* the lsb position in data */ + unsigned int lsb; + /* toddr_src_sel + * [7]: loopback; + * [6]: tdmin_lb; + * [5]: reserved; + * [4]: pdmin; + * [3]: spdifin; + * [2]: tdmin_c; + * [1]: tdmin_b; + * [0]: tdmin_a; + */ + unsigned int src; +}; + +/**/ +struct loopback_cfg { + /* lb_mode + * 0: out rate = in data rate; + * 1: out rate = loopback data rate; + */ + unsigned int lb_mode; + + enum datain_src datain_src; + unsigned int datain_chnum; + unsigned int datain_chmask; + + enum tdmin_lb_src datalb_src; + unsigned int datalb_chnum; + unsigned int datalb_chmask; +}; + +extern int loopback_is_enable(void); + +extern int snd_card_add_kcontrols(struct snd_soc_card *card); + +extern int loopback_parse_of(struct device_node *node, + struct loopback_cfg *lb_cfg); + +extern int loopback_prepare( + struct snd_pcm_substream *substream, + struct loopback_cfg *lb_cfg); + +#endif diff --git a/sound/soc/amlogic/auge/card.c b/sound/soc/amlogic/auge/card.c index 3417ff87dd03..5aa494b22e1a 100644 --- a/sound/soc/amlogic/auge/card.c +++ b/sound/soc/amlogic/auge/card.c @@ -47,6 +47,7 @@ struct aml_card_data { struct snd_soc_dai_link *dai_link; int spk_mute_gpio; bool spk_mute_active_low; + struct loopback_cfg lb_cfg; }; #define aml_priv_to_dev(priv) ((priv)->snd_card.dev) @@ -217,10 +218,21 @@ err: return ret; } +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); + + return 0; +} + static struct snd_soc_ops aml_card_ops = { - .startup = aml_card_startup, - .shutdown = aml_card_shutdown, - .hw_params = aml_card_hw_params, + .startup = aml_card_startup, + .shutdown = aml_card_shutdown, + .hw_params = aml_card_hw_params, + .prepare = aml_card_prepare, }; static int aml_card_dai_init(struct snd_soc_pcm_runtime *rtd) @@ -466,6 +478,7 @@ static int aml_card_parse_of(struct device_node *node, { struct device *dev = aml_priv_to_dev(priv); struct device_node *dai_link; + struct device_node *lb_link; int ret; if (!node) @@ -492,6 +505,14 @@ static int aml_card_parse_of(struct device_node *node, /* Factor to mclk, used in hw_params() */ of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs); + /* Loopback */ + lb_link = of_parse_phandle(node, PREFIX "loopback", 0); + if (lb_link) { + ret = loopback_parse_of(lb_link, &priv->lb_cfg); + if (ret < 0) + pr_err("failed parse loopback, ignore it\n"); + } + /* Single/Muti DAI link(s) & New style of DT node */ if (dai_link) { struct device_node *np = NULL; @@ -522,6 +543,7 @@ static int aml_card_parse_of(struct device_node *node, card_parse_end: of_node_put(dai_link); + of_node_put(lb_link); return ret; } @@ -608,6 +630,14 @@ static int aml_card_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); if (ret < 0) { + dev_err(dev, "failed to register sound card\n"); + goto err; + } + + /* Add controls */ + ret = aml_card_add_controls(&priv->snd_card); + if (ret < 0) { + dev_err(dev, "failed to register mixer kcontrols\n"); goto err; } diff --git a/sound/soc/amlogic/auge/card_utils.c b/sound/soc/amlogic/auge/card_utils.c index 68cea02e6ed0..c30d9dd9624a 100644 --- a/sound/soc/amlogic/auge/card_utils.c +++ b/sound/soc/amlogic/auge/card_utils.c @@ -18,6 +18,8 @@ #include #include #include +#include + #include "card_utils.h" int aml_card_parse_daifmt(struct device *dev, @@ -288,3 +290,16 @@ int aml_card_clean_reference(struct snd_soc_card *card) return 0; } + +int aml_card_add_controls(struct snd_soc_card *card) +{ + int ret; + + ret = snd_card_add_kcontrols(card); + if (ret < 0) { + pr_info("failed register kcontrols\n"); + return ret; + } + + return 0; +} diff --git a/sound/soc/amlogic/auge/card_utils.h b/sound/soc/amlogic/auge/card_utils.h index 49dafd6ed6fa..f249eeb64327 100644 --- a/sound/soc/amlogic/auge/card_utils.h +++ b/sound/soc/amlogic/auge/card_utils.h @@ -19,6 +19,7 @@ #define __AML_CARD_CORE_H #include +#include "audio_utils.h" struct aml_dai { const char *name; @@ -78,4 +79,6 @@ void aml_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link, int aml_card_clean_reference(struct snd_soc_card *card); +extern int aml_card_add_controls(struct snd_soc_card *card); + #endif /* __AML_CARD_CORE_H */ diff --git a/sound/soc/amlogic/auge/ddr_mngr.c b/sound/soc/amlogic/auge/ddr_mngr.c index 3f4ff751e515..110c3c4e38f7 100644 --- a/sound/soc/amlogic/auge/ddr_mngr.c +++ b/sound/soc/amlogic/auge/ddr_mngr.c @@ -19,6 +19,7 @@ #include #include "regs.h" #include "ddr_mngr.h" +#include "audio_utils.h" static DEFINE_MUTEX(ddr_mutex); @@ -218,6 +219,10 @@ void aml_toddr_select_src(struct toddr *to, enum toddr_src src) unsigned int reg_base = to->reg_base; unsigned int reg; + /* check whether loopback enable */ + if (loopback_is_enable()) + src = LOOPBACK; + reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0, reg_base); aml_audiobus_update_bits(actrl, reg, 0x7, src & 0x7); } diff --git a/sound/soc/amlogic/auge/loopback_hw.c b/sound/soc/amlogic/auge/loopback_hw.c new file mode 100644 index 000000000000..ecff8a9ac94c --- /dev/null +++ b/sound/soc/amlogic/auge/loopback_hw.c @@ -0,0 +1,125 @@ +/* + * sound/soc/amlogic/auge/loopback_hw.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * 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 "regs.h" +#include "loopback_hw.h" +#include "iomap.h" + +void datain_config(struct data_in *datain) +{ + audiobus_write( + EE_AUDIO_LB_CTRL0, + datain->config->ext_signed << 29 | + (datain->config->chnum - 1) << 24 | + datain->config->chmask << 16 | + datain->ddrdata->combined_type << 13 | + datain->ddrdata->msb << 8 | + datain->ddrdata->lsb << 3 | + datain->ddrdata->src << 0 + ); +} + +void datalb_config(struct data_lb *datalb) +{ + audiobus_write( + EE_AUDIO_LB_CTRL1, + datalb->config->ext_signed << 29 | + (datalb->config->chnum - 1) << 24 | + datalb->config->chmask << 16 | + datalb->ddr_type << 13 | + datalb->msb << 8 | + datalb->lsb << 3 + ); +} + +void datalb_ctrl(int lb_src, int bitdepth) +{ + //tdmin lb, same as tdm out + audiobus_update_bits( + EE_AUDIO_CLK_TDMIN_LB_CTRL, + 0x3 << 30 | 0 << 29 | 0xf << 24 | 0xf << 20, + 0x3 << 30 | 0 << 29 | 2 << 24 | 2 << 20 + ); + //tdmin ctrl, from tdmout + //reset + audiobus_update_bits(EE_AUDIO_TDMIN_LB_CTRL, 3<<28, 0); + audiobus_update_bits(EE_AUDIO_TDMIN_LB_CTRL, 1<<29, 1<<29); + audiobus_update_bits(EE_AUDIO_TDMIN_LB_CTRL, 1<<28, 1<<28); + + audiobus_write( + EE_AUDIO_TDMIN_LB_CTRL, + 1 << 31 | + 0 << 30 | /*0:tdm mode; 1: i2s mode;*/ + 1 << 29 | + 1 << 28 | + lb_src << 20| + 4 << 16| + (bitdepth - 1) << 0 + ); + audiobus_write( + EE_AUDIO_TDMIN_LB_MASK0, + 0xff); + +} + +void lb_enable_ex(int mode, bool is_enable) +{ + audiobus_update_bits( + EE_AUDIO_LB_CTRL0, + 0x3 << 30, + is_enable << 31 | + mode << 30 + ); +} + + +void lb_set_datain_src(int src) +{ +} + +void lb_set_tdminlb_src(int src) +{ +} + +void lb_set_tdminlb_enable(bool is_enable) +{ +} + +void lb_enable(bool is_enable) +{ + if (is_enable) + audiobus_update_bits( + EE_AUDIO_LB_CTRL0, + 0x1 << 31, + is_enable << 31 + ); + else + audiobus_write( + EE_AUDIO_LB_CTRL0, + 0); +} + +void lb_set_mode(int mode) +{ + audiobus_update_bits( + EE_AUDIO_LB_CTRL0, + 0x1 << 30, + mode << 30 + ); +} + diff --git a/sound/soc/amlogic/auge/loopback_hw.h b/sound/soc/amlogic/auge/loopback_hw.h new file mode 100644 index 000000000000..3d42d95b0596 --- /dev/null +++ b/sound/soc/amlogic/auge/loopback_hw.h @@ -0,0 +1,64 @@ +/* + * sound/soc/amlogic/auge/loopback_hw.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef __AML_LOOPBACK_HW_H__ +#define __AML_LOOPBACK_HW_H__ + +#include "audio_utils.h" + +struct lb_cfg { + /* + * 0: extend bits as "0" + * 1: extend bits as "msb" + */ + unsigned int ext_signed; + /* total channel number */ + unsigned int chnum; + /* which channel is selected for loopback */ + unsigned int chmask; +}; + +struct data_in { + struct lb_cfg *config; + struct audio_data *ddrdata; +}; + +struct data_lb { + struct lb_cfg *config; + unsigned int ddr_type; + unsigned int msb; + unsigned int lsb; +}; + +struct loopback { + struct device *dev; + unsigned int lb_mode; + + struct data_in *datain; + struct data_lb *datalb; +}; + + +extern void datain_config(struct data_in *datain); + +extern void datalb_config(struct data_lb *datalb); + +extern void datalb_ctrl(int lb_src, int bitdepth); + +extern void lb_enable_ex(int mode, bool is_enable); + +#endif diff --git a/sound/soc/amlogic/auge/pdm.c b/sound/soc/amlogic/auge/pdm.c index bb0faf44762a..81e7418ac92a 100644 --- a/sound/soc/amlogic/auge/pdm.c +++ b/sound/soc/amlogic/auge/pdm.c @@ -47,11 +47,11 @@ static struct snd_pcm_hardware aml_pdm_hardware = { SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_S32, - .rate_min = 8000, - .rate_max = 48000, + .rate_min = 8000, + .rate_max = 48000, - .channels_min = 1, - .channels_max = 8, + .channels_min = PDM_CHANNELS_MIN, + .channels_max = PDM_CHANNELS_MAX, .buffer_bytes_max = 32 * 1024, .period_bytes_max = 16 * 1024, @@ -473,10 +473,10 @@ static int aml_pdm_dai_prepare( switch (bitwidth) { case 16: - toddr_type = 2; + case 32: + toddr_type = 0; break; case 24: - case 32: toddr_type = 4; break; default: diff --git a/sound/soc/amlogic/auge/pdm.h b/sound/soc/amlogic/auge/pdm.h index fc5963516404..d78c694a977b 100644 --- a/sound/soc/amlogic/auge/pdm.h +++ b/sound/soc/amlogic/auge/pdm.h @@ -27,9 +27,13 @@ #define DEFAULT_FS_RATIO 256 #define PDM_CHANNELS_MIN 1 -#define PDM_CHANNELS_MAX 8 +#define PDM_CHANNELS_MAX (8 + 8) /* 8ch pdm in, 8 ch tdmin_lb */ + +#define PDM_RATES (SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_8000) -#define PDM_RATES SNDRV_PCM_RATE_8000_48000 #define PDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S24_LE |\ SNDRV_PCM_FMTBIT_S32_LE) diff --git a/sound/soc/amlogic/auge/spdif.c b/sound/soc/amlogic/auge/spdif.c index ad6d68c60088..edf1d8ff91c8 100644 --- a/sound/soc/amlogic/auge/spdif.c +++ b/sound/soc/amlogic/auge/spdif.c @@ -34,7 +34,7 @@ #include "ddr_mngr.h" #include "spdif_hw.h" - +#include "audio_utils.h" #define DRV_NAME "aml_spdif" @@ -412,36 +412,55 @@ static int aml_dai_spdif_prepare( aml_frddr_set_fifos(fr, 0x40, 0x20); } else { struct toddr *to = p_spdif->tddr; - unsigned int lsb, toddr_type; + unsigned int msb, lsb, toddr_type; - switch (bit_depth) { - case 8: - toddr_type = 0; - break; - case 16: - toddr_type = 1; - break; - case 24: - toddr_type = 4; - break; - case 32: - toddr_type = 3; - break; - default: - dev_err(p_spdif->dev, - "runtime format invalid bit_depth: %d\n", - bit_depth); - return -EINVAL; + if (loopback_is_enable()) { + switch (bit_depth) { + case 8: + case 16: + case 32: + toddr_type = 0; + break; + case 24: + toddr_type = 4; + break; + default: + pr_err( + "runtime format invalid bit_depth: %d\n", + bit_depth); + return -EINVAL; + } + msb = 32 - 1; + lsb = 32 - bit_depth; + } else { + switch (bit_depth) { + case 8: + case 16: + toddr_type = 0; + break; + case 24: + toddr_type = 4; + break; + case 32: + toddr_type = 3; + break; + default: + dev_err(p_spdif->dev, + "runtime format invalid bit_depth: %d\n", + bit_depth); + return -EINVAL; + } + + msb = 28 - 1; + if (bit_depth <= 24) + lsb = 28 - bit_depth; + else + lsb = 4; } - if (bit_depth <= 24) - lsb = 28 - bit_depth; - else - lsb = 4; - // to ddr spdifin aml_toddr_select_src(to, SPDIFIN); - aml_toddr_set_format(to, toddr_type, 27, lsb); + aml_toddr_set_format(to, toddr_type, msb, lsb); aml_toddr_set_fifos(to, 0x40); } @@ -576,7 +595,8 @@ static struct snd_soc_dai_driver aml_spdif_dai = { }, .capture = { .channels_min = 1, - .channels_max = 2, + /* spdif 2ch + tdmin_lb 8ch(fake for loopback) */ + .channels_max = 10, .rates = AML_DAI_SPDIF_RATES, .formats = AML_DAI_SPDIF_FORMATS, }, diff --git a/sound/soc/amlogic/auge/tdm.c b/sound/soc/amlogic/auge/tdm.c index c6b5141f7bfc..a26faa56e2bb 100644 --- a/sound/soc/amlogic/auge/tdm.c +++ b/sound/soc/amlogic/auge/tdm.c @@ -403,13 +403,11 @@ static int aml_dai_tdm_prepare(struct snd_pcm_substream *substream, switch (bit_depth) { case 8: + case 16: + case 32: toddr_type = 0; break; - case 16: - toddr_type = 2; - break; case 24: - case 32: toddr_type = 4; break; default: @@ -816,7 +814,7 @@ static struct snd_soc_dai_driver aml_tdm_dai[] = { }, .capture = { .channels_min = 1, - .channels_max = 8, + .channels_max = 32, .rates = AML_DAI_TDM_RATES, .formats = AML_DAI_TDM_FORMATS, }, @@ -836,7 +834,7 @@ static struct snd_soc_dai_driver aml_tdm_dai[] = { }, .capture = { .channels_min = 1, - .channels_max = 8, + .channels_max = 32, .rates = AML_DAI_TDM_RATES, .formats = AML_DAI_TDM_FORMATS, }, diff --git a/sound/soc/codecs/amlogic/pdm_dummy.c b/sound/soc/codecs/amlogic/pdm_dummy.c index e47ac80278fa..0200d1c04976 100644 --- a/sound/soc/codecs/amlogic/pdm_dummy.c +++ b/sound/soc/codecs/amlogic/pdm_dummy.c @@ -20,7 +20,11 @@ #include #include -#define DUMMY_RATES SNDRV_PCM_RATE_8000_48000 +#define DUMMY_RATES (SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_8000) + #define DUMMY_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S24_LE |\ SNDRV_PCM_FMTBIT_S32_LE) @@ -59,7 +63,7 @@ struct snd_soc_dai_driver pdm_dummy_dai = { .capture = { .stream_name = "PDM Capture", .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rates = DUMMY_RATES, .formats = DUMMY_FORMATS, }, diff --git a/sound/soc/codecs/amlogic/tlv320adc3101.c b/sound/soc/codecs/amlogic/tlv320adc3101.c index ca099caaf52b..fb0c7d1e01bf 100644 --- a/sound/soc/codecs/amlogic/tlv320adc3101.c +++ b/sound/soc/codecs/amlogic/tlv320adc3101.c @@ -565,7 +565,7 @@ static int adc3101_mute(struct snd_soc_dai *dai, int mute) static int adc3101_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - pr_info("%s ..\n", __func__); + pr_debug("%s ..\n", __func__); switch (level) { case SND_SOC_BIAS_ON: /* Switch on PLL */