mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 04:10:18 +09:00
audio: add loopback
PD#147538: audio: loopback for pdm/spdif/tdm 1. pdm/spdif/tdm as loopback datain soruce, tdmin_lb as datalb source 2. add mixer kcontrols for loopback Change-Id: I579db913080b3bb02bc99885eb330e24af8a0edb Signed-off-by: Xing Wang <xing.wang@amlogic.com>
This commit is contained in:
@@ -13994,3 +13994,11 @@ F: driver/amlogic/ddr_window/Kconfig
|
||||
AMLOGIC AUDIO INFO
|
||||
M: wang xing <xing.wang@amlogic.com>
|
||||
F: drivers/amlogic/audioinfo/
|
||||
|
||||
AMLOGIC AXG ADD LOOPBACK INTERFACE
|
||||
M: wang xing <xing.wang@amlogic.com>
|
||||
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
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
331
sound/soc/amlogic/auge/audio_utils.c
Normal file
331
sound/soc/amlogic/auge/audio_utils.c
Normal file
@@ -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);
|
||||
}
|
||||
137
sound/soc/amlogic/auge/audio_utils.h
Normal file
137
sound/soc/amlogic/auge/audio_utils.h
Normal file
@@ -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 <linux/types.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
/* 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#define __AML_CARD_CORE_H
|
||||
|
||||
#include <sound/soc.h>
|
||||
#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 */
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <linux/slab.h>
|
||||
#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);
|
||||
}
|
||||
|
||||
125
sound/soc/amlogic/auge/loopback_hw.c
Normal file
125
sound/soc/amlogic/auge/loopback_hw.c
Normal file
@@ -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 <sound/soc.h>
|
||||
|
||||
#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
|
||||
);
|
||||
}
|
||||
|
||||
64
sound/soc/amlogic/auge/loopback_hw.h
Normal file
64
sound/soc/amlogic/auge/loopback_hw.h
Normal file
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -20,7 +20,11 @@
|
||||
#include <linux/of.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#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,
|
||||
},
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user