mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 11:50:43 +09:00
audio: add stream mute and continuous clk [1/1]
PD#SWPL-2952 Problem: Pop noise could be heard when switching between sources. Solution: 1. Add stream digital mute functions. 2. continuous clock to eliminate the clk reset issue. Verify: Local verified. Change-Id: I372f4c03aaf875d75aa903c9c2dfda00619af000 Signed-off-by: Shuai Li <shuai.li@amlogic.com>
This commit is contained in:
@@ -300,7 +300,7 @@
|
||||
aml-audio-card,dai-link@0 {
|
||||
format = "i2s";
|
||||
mclk-fs = <256>;
|
||||
//continuous-clock;
|
||||
continuous-clock;
|
||||
//bitclock-inversion;
|
||||
//frame-inversion;
|
||||
/* master mode */
|
||||
|
||||
@@ -302,7 +302,7 @@
|
||||
aml-audio-card,dai-link@0 {
|
||||
format = "i2s";
|
||||
mclk-fs = <256>;
|
||||
//continuous-clock;
|
||||
continuous-clock;
|
||||
//bitclock-inversion;
|
||||
//frame-inversion;
|
||||
/* master mode */
|
||||
|
||||
@@ -116,6 +116,9 @@ struct aml_tdm {
|
||||
/* virtual link for i2s to hdmitx */
|
||||
int i2s2hdmitx;
|
||||
int acodec_adc;
|
||||
uint last_mpll_freq;
|
||||
uint last_mclk_freq;
|
||||
uint last_fmt;
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hardware aml_tdm_hardware = {
|
||||
@@ -368,33 +371,6 @@ struct snd_soc_platform_driver aml_tdm_platform = {
|
||||
.pcm_new = aml_tdm_new,
|
||||
};
|
||||
|
||||
static int aml_dai_tdm_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(p_tdm->mclk);
|
||||
if (ret) {
|
||||
pr_err("Can't enable mclk: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
err:
|
||||
pr_err("failed enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void aml_dai_tdm_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
|
||||
/* disable clock and gate */
|
||||
clk_disable_unprepare(p_tdm->mclk);
|
||||
}
|
||||
|
||||
static int aml_dai_tdm_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
@@ -770,6 +746,15 @@ static int aml_dai_tdm_hw_params(struct snd_pcm_substream *substream,
|
||||
rate * ratio * mux);
|
||||
}
|
||||
|
||||
if (!p_tdm->contns_clk && !IS_ERR(p_tdm->mclk)) {
|
||||
pr_debug("%s(), enable mclk for %s", __func__, cpu_dai->name);
|
||||
ret = clk_prepare_enable(p_tdm->mclk);
|
||||
if (ret) {
|
||||
pr_err("Can't enable mclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -800,6 +785,12 @@ static int aml_dai_tdm_hw_free(struct snd_pcm_substream *substream,
|
||||
fr, p_tdm->samesource_sel);
|
||||
}
|
||||
|
||||
/* disable clock and gate */
|
||||
if (!p_tdm->contns_clk && !IS_ERR(p_tdm->mclk)) {
|
||||
pr_info("%s(), disable mclk for %s", __func__, cpu_dai->name);
|
||||
clk_disable_unprepare(p_tdm->mclk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -809,6 +800,12 @@ static int aml_dai_set_tdm_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
|
||||
|
||||
pr_debug("asoc aml_dai_set_tdm_fmt, %#x, %p, id(%d), clksel(%d)\n",
|
||||
fmt, p_tdm, p_tdm->id, p_tdm->clk_sel);
|
||||
if (p_tdm->last_fmt == fmt) {
|
||||
pr_debug("%s(), fmt not change\n", __func__);
|
||||
goto capture;
|
||||
} else
|
||||
p_tdm->last_fmt = fmt;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
|
||||
case SND_SOC_DAIFMT_CONT:
|
||||
p_tdm->contns_clk = true;
|
||||
@@ -825,9 +822,9 @@ static int aml_dai_set_tdm_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
|
||||
|
||||
aml_tdm_set_format(p_tdm->actrl,
|
||||
&(p_tdm->setting), p_tdm->clk_sel, p_tdm->id, fmt,
|
||||
cpu_dai->capture_active,
|
||||
cpu_dai->playback_active);
|
||||
1, 1);
|
||||
|
||||
capture:
|
||||
/* update skew for ACODEC_ADC */
|
||||
if (cpu_dai->capture_active
|
||||
&& p_tdm->chipinfo
|
||||
@@ -863,6 +860,7 @@ static int aml_dai_set_tdm_sysclk(struct snd_soc_dai *cpu_dai,
|
||||
{
|
||||
struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
unsigned int ratio = aml_mpll_mclk_ratio(freq);
|
||||
unsigned int mpll_freq = 0;
|
||||
|
||||
p_tdm->setting.sysclk = freq;
|
||||
|
||||
@@ -875,8 +873,20 @@ static int aml_dai_set_tdm_sysclk(struct snd_soc_dai *cpu_dai,
|
||||
ratio = 20;
|
||||
#endif
|
||||
|
||||
clk_set_rate(p_tdm->clk, freq * ratio);
|
||||
clk_set_rate(p_tdm->mclk, freq);
|
||||
mpll_freq = freq * ratio;
|
||||
if (mpll_freq != p_tdm->last_mpll_freq) {
|
||||
clk_set_rate(p_tdm->clk, mpll_freq);
|
||||
p_tdm->last_mpll_freq = mpll_freq;
|
||||
} else {
|
||||
pr_debug("%s(), mpll no change, keep clk\n", __func__);
|
||||
}
|
||||
|
||||
if (freq != p_tdm->last_mclk_freq) {
|
||||
clk_set_rate(p_tdm->mclk, freq);
|
||||
p_tdm->last_mclk_freq = freq;
|
||||
} else {
|
||||
pr_debug("%s(), mclk no change, keep clk\n", __func__);
|
||||
}
|
||||
|
||||
pr_debug("set mclk:%d, mpll:%d, get mclk:%lu, mpll:%lu\n",
|
||||
freq,
|
||||
@@ -1051,9 +1061,22 @@ static int aml_dai_tdm_remove(struct snd_soc_dai *cpu_dai)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aml_dai_tdm_mute_stream(struct snd_soc_dai *cpu_dai,
|
||||
int mute, int stream)
|
||||
{
|
||||
struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
pr_debug("tdm playback mute: %d\n", mute);
|
||||
aml_tdm_mute_playback(p_tdm->actrl, p_tdm->id, mute);
|
||||
} else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
pr_debug("tdm capture mute: %d\n", mute);
|
||||
aml_tdm_mute_capture(p_tdm->actrl, p_tdm->id, mute);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops aml_dai_tdm_ops = {
|
||||
.startup = aml_dai_tdm_startup,
|
||||
.shutdown = aml_dai_tdm_shutdown,
|
||||
.prepare = aml_dai_tdm_prepare,
|
||||
.trigger = aml_dai_tdm_trigger,
|
||||
.hw_params = aml_dai_tdm_hw_params,
|
||||
@@ -1063,6 +1086,7 @@ static struct snd_soc_dai_ops aml_dai_tdm_ops = {
|
||||
.set_bclk_ratio = aml_dai_set_bclk_ratio,
|
||||
.set_clkdiv = aml_dai_set_clkdiv,
|
||||
.set_tdm_slot = aml_dai_set_tdm_slot,
|
||||
.mute_stream = aml_dai_tdm_mute_stream,
|
||||
};
|
||||
|
||||
#define AML_DAI_TDM_RATES (SNDRV_PCM_RATE_8000_192000)
|
||||
|
||||
@@ -188,6 +188,72 @@ void aml_tdm_fifo_ctrl(
|
||||
|
||||
}
|
||||
|
||||
static void aml_clk_set_tdmout_by_id(
|
||||
struct aml_audio_controller *actrl,
|
||||
unsigned int tdm_index,
|
||||
unsigned int sclk_sel,
|
||||
unsigned int lrclk_sel,
|
||||
bool sclk_ws_inv,
|
||||
bool is_master,
|
||||
bool binv)
|
||||
{
|
||||
unsigned int val_sclk_ws_inv = 0;
|
||||
unsigned int reg = EE_AUDIO_CLK_TDMOUT_A_CTRL + tdm_index;
|
||||
|
||||
/* This is just a copy from previous setting. WHY??? */
|
||||
val_sclk_ws_inv = sclk_ws_inv && is_master;
|
||||
if (val_sclk_ws_inv)
|
||||
aml_audiobus_update_bits(actrl, reg,
|
||||
0x3<<30|1<<28|0xf<<24|0xf<<20,
|
||||
0x3<<30|val_sclk_ws_inv<<28|
|
||||
sclk_sel<<24|lrclk_sel<<20);
|
||||
else
|
||||
aml_audiobus_update_bits(actrl, reg,
|
||||
0x3<<30|1<<29|0xf<<24|0xf<<20,
|
||||
0x3<<30|binv<<29|
|
||||
sclk_sel<<24|lrclk_sel<<20);
|
||||
}
|
||||
|
||||
static void aml_clk_set_tdmin_by_id(
|
||||
struct aml_audio_controller *actrl,
|
||||
unsigned int tdm_index,
|
||||
unsigned int sclk_sel,
|
||||
unsigned int lrclk_sel)
|
||||
{
|
||||
unsigned int reg =
|
||||
EE_AUDIO_CLK_TDMIN_A_CTRL + tdm_index;
|
||||
aml_audiobus_update_bits(actrl,
|
||||
reg,
|
||||
0xff<<20,
|
||||
sclk_sel<<24|lrclk_sel<<20);
|
||||
}
|
||||
|
||||
static void aml_tdmout_invert_lrclk(
|
||||
struct aml_audio_controller *actrl,
|
||||
unsigned int tdm_index,
|
||||
bool finv)
|
||||
{
|
||||
unsigned int off_set =
|
||||
EE_AUDIO_TDMOUT_B_CTRL1 - EE_AUDIO_TDMOUT_A_CTRL1;
|
||||
unsigned int reg_out =
|
||||
EE_AUDIO_TDMOUT_A_CTRL1 + off_set * tdm_index;
|
||||
aml_audiobus_update_bits(actrl,
|
||||
reg_out, 0x1<<28, finv<<28);
|
||||
}
|
||||
|
||||
static void aml_tdmout_bclk_skew(
|
||||
struct aml_audio_controller *actrl,
|
||||
unsigned int tdm_index,
|
||||
unsigned int bclkout_skew)
|
||||
{
|
||||
unsigned int off_set =
|
||||
EE_AUDIO_TDMOUT_B_CTRL0 - EE_AUDIO_TDMOUT_A_CTRL0;
|
||||
unsigned int reg_out =
|
||||
EE_AUDIO_TDMOUT_A_CTRL0 + off_set * tdm_index;
|
||||
aml_audiobus_update_bits(actrl,
|
||||
reg_out, 0x1f<<15, bclkout_skew<<15);
|
||||
}
|
||||
|
||||
void aml_tdm_set_format(
|
||||
struct aml_audio_controller *actrl,
|
||||
struct pcm_setting *p_config,
|
||||
@@ -223,24 +289,7 @@ void aml_tdm_set_format(
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: clk tree
|
||||
reg_out = EE_AUDIO_CLK_TDMOUT_A_CTRL + id;
|
||||
reg_in = EE_AUDIO_CLK_TDMIN_A_CTRL + id;
|
||||
aml_audiobus_update_bits(actrl,
|
||||
reg_out,
|
||||
0xff<<20,
|
||||
valb<<24|valf<<20);
|
||||
aml_audiobus_update_bits(actrl,
|
||||
reg_in,
|
||||
0xff<<20,
|
||||
valb<<24|valf<<20);
|
||||
|
||||
if (p_config->sclk_ws_inv)
|
||||
aml_audiobus_update_bits(actrl,
|
||||
reg_out,
|
||||
1 << 28,
|
||||
0 << 28);
|
||||
aml_clk_set_tdmin_by_id(actrl, id, valb, valf);
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
@@ -355,27 +404,11 @@ void aml_tdm_set_format(
|
||||
|
||||
/* TDM out */
|
||||
if (playback_active) {
|
||||
|
||||
reg_out = EE_AUDIO_CLK_TDMOUT_A_CTRL + id;
|
||||
aml_audiobus_update_bits(actrl, reg_out,
|
||||
0x3<<30, 0x3<<30);
|
||||
|
||||
if (p_config->sclk_ws_inv && master_mode)
|
||||
aml_audiobus_update_bits(actrl, reg_out,
|
||||
0x1 << 28,
|
||||
0x1 << 28);
|
||||
else
|
||||
aml_audiobus_update_bits(actrl, reg_out,
|
||||
0x1<<29, binv<<29);
|
||||
|
||||
off_set = EE_AUDIO_TDMOUT_B_CTRL1 - EE_AUDIO_TDMOUT_A_CTRL1;
|
||||
reg_out = EE_AUDIO_TDMOUT_A_CTRL1 + off_set * id;
|
||||
aml_audiobus_update_bits(actrl, reg_out, 0x1<<28, finv<<28);
|
||||
|
||||
off_set = EE_AUDIO_TDMOUT_B_CTRL0 - EE_AUDIO_TDMOUT_A_CTRL0;
|
||||
reg_out = EE_AUDIO_TDMOUT_A_CTRL0 + off_set * id;
|
||||
aml_audiobus_update_bits(actrl, reg_out,
|
||||
0x1f<<15, bclkout_skew<<15);
|
||||
aml_clk_set_tdmout_by_id(actrl,
|
||||
id, valb, valf,
|
||||
p_config->sclk_ws_inv, master_mode, binv);
|
||||
aml_tdmout_invert_lrclk(actrl, id, finv);
|
||||
aml_tdmout_bclk_skew(actrl, id, bclkout_skew);
|
||||
}
|
||||
|
||||
/* TDM in */
|
||||
@@ -660,3 +693,44 @@ void i2s_to_hdmitx_ctrl(int tdm_index)
|
||||
| tdm_index << 4 /* Bclk_sel */
|
||||
);
|
||||
}
|
||||
|
||||
void aml_tdm_mute_playback(
|
||||
struct aml_audio_controller *actrl,
|
||||
int tdm_index,
|
||||
bool mute)
|
||||
{
|
||||
unsigned int offset, reg;
|
||||
unsigned int mute_mask = 0xffffffff;
|
||||
unsigned int mute_val = 0;
|
||||
int i = 0, lanes = 4;
|
||||
|
||||
if (mute)
|
||||
mute_val = 0xffffffff;
|
||||
|
||||
offset = EE_AUDIO_TDMOUT_B_MUTE0
|
||||
- EE_AUDIO_TDMOUT_A_MUTE0;
|
||||
reg = EE_AUDIO_TDMOUT_A_MUTE0 + offset * tdm_index;
|
||||
for (i = 0; i < lanes; i++)
|
||||
aml_audiobus_update_bits(actrl, reg + i, mute_mask, mute_val);
|
||||
}
|
||||
|
||||
void aml_tdm_mute_capture(
|
||||
struct aml_audio_controller *actrl,
|
||||
int tdm_index,
|
||||
bool mute)
|
||||
{
|
||||
unsigned int offset, reg;
|
||||
unsigned int mute_mask = 0xffffffff;
|
||||
unsigned int mute_val = 0;
|
||||
int i = 0, lanes = 4;
|
||||
|
||||
if (mute)
|
||||
mute_val = 0xffffffff;
|
||||
|
||||
offset = EE_AUDIO_TDMIN_B_MUTE0
|
||||
- EE_AUDIO_TDMIN_A_MUTE0;
|
||||
reg = EE_AUDIO_TDMIN_A_MUTE0 + offset * tdm_index;
|
||||
for (i = 0; i < lanes; i++)
|
||||
aml_audiobus_update_bits(actrl, reg + i, mute_mask, mute_val);
|
||||
}
|
||||
|
||||
|
||||
@@ -142,4 +142,12 @@ extern void aml_tdm_clk_pad_select(
|
||||
int tdm_index, int clk_sel);
|
||||
|
||||
extern void i2s_to_hdmitx_ctrl(int tdm_index);
|
||||
void aml_tdm_mute_playback(
|
||||
struct aml_audio_controller *actrl,
|
||||
int index,
|
||||
bool mute);
|
||||
void aml_tdm_mute_capture(
|
||||
struct aml_audio_controller *actrl,
|
||||
int tdm_index,
|
||||
bool mute);
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user