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:
Shuai Li
2019-01-13 23:03:48 +08:00
committed by Jianxin Pan
parent 7c027df92d
commit 87344d4079
5 changed files with 180 additions and 74 deletions

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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