Fix: i2c access fail & channels miss order

This commit is contained in:
Peter.Yang 2018-03-24 10:01:33 +00:00
parent 22f495491a
commit d3d95d0296
4 changed files with 151 additions and 87 deletions

179
ac101.c
View file

@ -36,6 +36,15 @@
#include "ac101_regs.h" #include "ac101_regs.h"
#include "ac10x.h" #include "ac10x.h"
/*
* *** To sync channels ***
* 1. disable clock in codec hw_params()
* 2. clear fifo in bcm2835 hw_params()
* 3. clear fifo in bcm2385 prepare()
* 4. enable RX in bcm2835 trigger()
* 5. enable clock in machine trigger()
*/
/*Default initialize configuration*/ /*Default initialize configuration*/
static bool speaker_double_used = 1; static bool speaker_double_used = 1;
static int double_speaker_val = 0x1B; static int double_speaker_val = 0x1B;
@ -219,6 +228,7 @@ void set_configuration(struct snd_soc_codec *codec)
static int late_enable_dac(struct snd_soc_codec* codec, int event) { static int late_enable_dac(struct snd_soc_codec* codec, int event) {
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
mutex_lock(&ac10x->dac_mutex); mutex_lock(&ac10x->dac_mutex);
switch (event) { switch (event) {
case SND_SOC_DAPM_PRE_PMU: case SND_SOC_DAPM_PRE_PMU:
@ -276,49 +286,66 @@ static int ac101_headphone_event(struct snd_soc_codec* codec, int event) {
} }
return 0; return 0;
} }
static int ac101_sysclk_started(void) {
int reg_val;
reg_val = ac101_read(static_ac10x->codec, SYSCLK_CTRL);
return (reg_val & (0x1<<SYSCLK_ENA));
}
static int ac101_aif1clk(struct snd_soc_codec* codec, int event) { static int ac101_aif1clk(struct snd_soc_codec* codec, int event) {
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
unsigned long flags;
int ret;
AC101_DBG("%s() L%d event=%d pre_up/%d post_down/%d\n", __func__, __LINE__, AC101_DBG("%s() L%d event=%d pre_up/%d post_down/%d\n", __func__, __LINE__,
event, SND_SOC_DAPM_PRE_PMU, SND_SOC_DAPM_POST_PMD); event, SND_SOC_DAPM_PRE_PMU, SND_SOC_DAPM_POST_PMD);
mutex_lock(&ac10x->aifclk_mutex);
switch (event) { switch (event) {
case SND_SOC_DAPM_PRE_PMU: case SND_SOC_DAPM_PRE_PMU:
if (ac10x->aif1_clken == 0){ if (ac10x->aif1_clken == 0){
/*enable AIF1CLK*/ /* I know it will degrades performance, but I have no choice */
ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<AIF1CLK_ENA), (0x1<<AIF1CLK_ENA)); spin_lock_irqsave(&ac10x->lock, flags);
ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_AIF1), (0x1<<MOD_CLK_AIF1));
ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_AIF1), (0x1<<MOD_RESET_AIF1));
/*enable systemclk*/
if (ac10x->aif2_clken == 0)
ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<SYSCLK_ENA), (0x1<<SYSCLK_ENA));
}
ac10x->aif1_clken++;
break; /* enable aif1clk & sysclk */
case SND_SOC_DAPM_POST_PMD: ret = ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<AIF1CLK_ENA), (0x1<<AIF1CLK_ENA));
#if 0 ret = ret || ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_AIF1), (0x1<<MOD_CLK_AIF1));
if (ac10x->aif1_clken > 0){ ret = ret || ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_AIF1), (0x1<<MOD_RESET_AIF1));
ac10x->aif1_clken--; ret = ret || ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<SYSCLK_ENA), (0x1<<SYSCLK_ENA));
if (ac10x->aif1_clken == 0){ spin_unlock_irqrestore(&ac10x->lock, flags);
#else
{ if (ret) {
if (ac10x->aif1_clken != 0) { AC101_DBG("%s() L%d start sysclk failed\n", __func__, __LINE__);
ac10x->aif1_clken = 0; } else {
#endif AC101_DBG("%s() L%d hw sysclk enable\n", __func__, __LINE__);
/*disable AIF1CLK*/ ac10x->aif1_clken++;
ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<AIF1CLK_ENA), (0x0<<AIF1CLK_ENA));
ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_AIF1), (0x0<<MOD_CLK_AIF1));
ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_AIF1), (0x0<<MOD_RESET_AIF1));
/*DISABLE systemclk*/
if (ac10x->aif2_clken == 0)
ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<SYSCLK_ENA), (0x0<<SYSCLK_ENA));
} }
} }
break; break;
case SND_SOC_DAPM_POST_PMD:
if (ac10x->aif1_clken != 0) {
/* I know it will degrades performance, but I have no choice */
spin_lock_irqsave(&ac10x->lock, flags);
/* disable aif1clk & sysclk */
ret = ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<AIF1CLK_ENA),(0x0<<AIF1CLK_ENA));
ret = ret || ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_AIF1), (0x0<<MOD_CLK_AIF1));
ret = ret || ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_AIF1), (0x0<<MOD_RESET_AIF1));
ret = ret || ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<SYSCLK_ENA), (0x0<<SYSCLK_ENA));
spin_unlock_irqrestore(&ac10x->lock, flags);
if (ret) {
AC101_DBG("%s() L%d stop sysclk failed\n", __func__, __LINE__);
} else {
AC101_DBG("%s() L%d hw sysclk disable\n", __func__, __LINE__);
ac10x->aif1_clken = 0;
}
break;
}
} }
mutex_unlock(&ac10x->aifclk_mutex);
return 0; return 0;
} }
@ -555,8 +582,10 @@ int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute)
ac101_headphone_event(codec, SND_SOC_DAPM_PRE_PMD); ac101_headphone_event(codec, SND_SOC_DAPM_PRE_PMD);
late_enable_dac(codec, SND_SOC_DAPM_POST_PMD); late_enable_dac(codec, SND_SOC_DAPM_POST_PMD);
#if _MASTER_MULTI_CODEC != _MASTER_AC101
ac10x->aif1_clken = 1; ac10x->aif1_clken = 1;
ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD); ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD);
#endif
} }
return 0; return 0;
} }
@ -564,27 +593,24 @@ int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute)
void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai) void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai)
{ {
struct snd_soc_codec *codec = codec_dai->codec; struct snd_soc_codec *codec = codec_dai->codec;
int reg_val; struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
AC101_DBG("%s,line:%d stream = %d, play-active = %d\n", __func__, __LINE__, AC101_DBG("%s,line:%d stream = %s, play: %d, capt: %d, active: %d\n", __func__, __LINE__,
substream->stream, codec_dai->playback_active); snd_pcm_stream_str(substream),
codec_dai->playback_active, codec_dai->capture_active,
codec_dai->active);
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { if (!codec_dai->active) {
reg_val = (ac101_read(codec, AIF_SR_CTRL) >> 12) & 0xF; ac10x->aif1_clken = 1;
if (codec_dai->playback_active && dmic_used && reg_val == 0x4) { ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD);
ac101_update_bits(codec, AIF_SR_CTRL, (0xf<<AIF1_FS), (0x7<<AIF1_FS));
}
} }
} }
static int ac101_set_pll(struct snd_soc_dai *codec_dai, int pll_id, int source, static int ac101_set_pll(struct snd_soc_dai *codec_dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out) unsigned int freq_in, unsigned int freq_out)
{ {
int i = 0;
int m = 0;
int n_i = 0;
int n_f = 0;
struct snd_soc_codec *codec = codec_dai->codec; struct snd_soc_codec *codec = codec_dai->codec;
int i, m, n_i, n_f;
AC101_DBG("%s, line:%d, pll_id:%d\n", __func__, __LINE__, pll_id); AC101_DBG("%s, line:%d, pll_id:%d\n", __func__, __LINE__, pll_id);
@ -617,8 +643,9 @@ static int ac101_set_pll(struct snd_soc_dai *codec_dai, int pll_id, int source,
default: default:
return -EINVAL; return -EINVAL;
} }
/* freq_out = freq_in * n/(m*(2k+1)) , k=1,N=N_i+N_f */ /* freq_out = freq_in * n/(m*(2k+1)) , k=1,N=N_i+N_f */
for (i = 0; i < ARRAY_SIZE(codec_pll_div); i++) { for (i = m = n_i = n_f = 0; i < ARRAY_SIZE(codec_pll_div); i++) {
if ((codec_pll_div[i].pll_in == freq_in) && (codec_pll_div[i].pll_out == freq_out)) { if ((codec_pll_div[i].pll_in == freq_in) && (codec_pll_div[i].pll_out == freq_out)) {
m = codec_pll_div[i].m; m = codec_pll_div[i].m;
n_i = codec_pll_div[i].n_i; n_i = codec_pll_div[i].n_i;
@ -656,11 +683,10 @@ int ac101_hw_params(struct snd_pcm_substream *substream,
AC101_DBG("%s() L%d +++\n", __func__, __LINE__); AC101_DBG("%s() L%d +++\n", __func__, __LINE__);
/* Master mode, to clear cpu_dai fifos, disable output bclk & lrck */ if (_MASTER_MULTI_CODEC == _MASTER_AC101 && ac101_sysclk_started()) {
#if 0 /* not configure hw_param twice, tell the caller it's started */
ac10x->aif1_clken = 1; return 1;
ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD); }
#endif
/* get channels count & slot size */ /* get channels count & slot size */
channels = params_channels(params); channels = params_channels(params);
@ -731,6 +757,11 @@ int ac101_hw_params(struct snd_pcm_substream *substream,
ac101_set_pll(codec_dai, AC101_BCLK1, 0, aif1_lrck_div * params_rate(params), freq_out); ac101_set_pll(codec_dai, AC101_BCLK1, 0, aif1_lrck_div * params_rate(params), freq_out);
} }
#if _MASTER_MULTI_CODEC == _MASTER_AC101
/* Master mode, to clear cpu_dai fifos, disable output bclk & lrck */
ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD);
#endif
AC101_DBG("rate: %d , channels: %d , samp_res: %d", AC101_DBG("rate: %d , channels: %d , samp_res: %d",
params_rate(params), channels, aif1_slot_size); params_rate(params), channels, aif1_slot_size);
@ -846,15 +877,39 @@ static int ac101_set_clock(int y_start_n_stop) {
/* enable global clock */ /* enable global clock */
ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_PRE_PMU); ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_PRE_PMU);
} else { } else {
#if 0 /* disable global clock */
static_ac10x->aif1_clken = 1; static_ac10x->aif1_clken = 1;
ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_POST_PMD); ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_POST_PMD);
#endif
} }
return 0; return 0;
} }
#endif #endif
int ac101_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
int ret = 0;
AC101_DBG("%s() stream=%s cmd=%d\n",
__FUNCTION__,
snd_pcm_stream_str(substream),
cmd);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
break;
default:
ret = -EINVAL;
}
return ret;
}
#if 0 #if 0
static int ac101_set_dai_sysclk(struct snd_soc_dai *codec_dai, static int ac101_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir) int clk_id, unsigned int freq, int dir)
@ -870,29 +925,6 @@ static int ac101_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return 0; return 0;
} }
static int ac101_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
int ret = 0;
AC101_DBG("%s() stream=%d cmd=%d\n",
__FUNCTION__, substream->stream, cmd);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
break;
default:
ret = -EINVAL;
}
return ret;
}
static const struct snd_soc_dai_ops ac101_aif1_dai_ops = { static const struct snd_soc_dai_ops ac101_aif1_dai_ops = {
//.startup = ac101_audio_startup, //.startup = ac101_audio_startup,
//.shutdown = ac101_aif_shutdown, //.shutdown = ac101_aif_shutdown,
@ -984,9 +1016,8 @@ int ac101_codec_probe(struct snd_soc_codec *codec)
INIT_WORK(&ac10x->codec_resume, codec_resume_work); INIT_WORK(&ac10x->codec_resume, codec_resume_work);
ac10x->dac_enable = 0; ac10x->dac_enable = 0;
ac10x->aif1_clken = 0; ac10x->aif1_clken = 0;
ac10x->aif2_clken = 0;
mutex_init(&ac10x->dac_mutex); mutex_init(&ac10x->dac_mutex);
mutex_init(&ac10x->aifclk_mutex); spin_lock_init(&ac10x->lock);
#if _MASTER_MULTI_CODEC == _MASTER_AC101 #if _MASTER_MULTI_CODEC == _MASTER_AC101
asoc_simple_card_register_set_clock(ac101_set_clock); asoc_simple_card_register_set_clock(ac101_set_clock);

44
ac108.c
View file

@ -647,16 +647,23 @@ static int ac108_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_h
int ret = 0; int ret = 0;
u8 v; u8 v;
dev_dbg(dai->dev, "%s() stream=%d play:%d capt:%d +++\n", __func__, dev_dbg(dai->dev, "%s() stream=%s play:%d capt:%d +++\n", __func__,
substream->stream, dai->playback_active, dai->capture_active); snd_pcm_stream_str(substream),
dai->playback_active, dai->capture_active);
if (ac10x->i2c101) { if (ac10x->i2c101) {
ret = ac101_hw_params(substream, params, dai); ret = ac101_hw_params(substream, params, dai);
if (ret > 0) {
dev_dbg(dai->dev, "%s() L%d returned\n", __func__, __LINE__);
/* not configure hw_param twice */
return 0;
}
} }
/* nothing should be done when it isn't capturing stream. */ if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE && dai->playback_active)
if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) { || (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai->capture_active)) {
// TODO: /* not configure hw_param twice */
/* return 0; */
} }
channels = params_channels(params); channels = params_channels(params);
@ -785,7 +792,8 @@ static int ac108_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_h
*/ */
ac108_multi_chips_slots(ac10x, channels); ac108_multi_chips_slots(ac10x, channels);
dev_dbg(dai->dev, "%s() stream=%d ---\n", __func__, substream->stream); dev_dbg(dai->dev, "%s() stream=%s ---\n", __func__,
snd_pcm_stream_str(substream));
return 0; return 0;
} }
@ -1005,6 +1013,16 @@ static int ac108_set_clock(int y_start_n_stop) {
return 0; return 0;
} }
static int ac108_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
dev_dbg(dai->dev, "%s() stream=%s\n",
__func__,
snd_pcm_stream_str(substream));
return 0;
}
static int ac108_trigger(struct snd_pcm_substream *substream, int cmd, static int ac108_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
@ -1013,8 +1031,17 @@ static int ac108_trigger(struct snd_pcm_substream *substream, int cmd,
int ret = 0; int ret = 0;
u8 r; u8 r;
dev_dbg(dai->dev, "%s() stream=%d cmd=%d\n", dev_dbg(dai->dev, "%s() stream=%s cmd=%d\n",
__FUNCTION__, substream->stream, cmd); __FUNCTION__,
snd_pcm_stream_str(substream),
cmd);
if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) {
ac101_trigger(substream, cmd, dai);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
return 0;
}
}
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
@ -1088,6 +1115,7 @@ static const struct snd_soc_dai_ops ac108_dai_ops = {
/*ALSA PCM audio operations*/ /*ALSA PCM audio operations*/
.hw_params = ac108_hw_params, .hw_params = ac108_hw_params,
.prepare = ac108_prepare,
.trigger = ac108_trigger, .trigger = ac108_trigger,
.digital_mute = ac108_aif_mute, .digital_mute = ac108_aif_mute,

View file

@ -47,16 +47,15 @@ struct ac10x_priv {
struct delayed_work dlywork; struct delayed_work dlywork;
int tdm_chips_cnt; int tdm_chips_cnt;
/* memboer for ac101 .begin */ /* member for ac101 .begin */
struct snd_soc_codec *codec; struct snd_soc_codec *codec;
struct i2c_client *i2c101; struct i2c_client *i2c101;
struct regmap* regmap101; struct regmap* regmap101;
struct mutex dac_mutex; struct mutex dac_mutex;
u8 dac_enable; u8 dac_enable;
struct mutex aifclk_mutex; spinlock_t lock;
u8 aif1_clken; u8 aif1_clken;
u8 aif2_clken;
struct work_struct codec_resume; struct work_struct codec_resume;
struct delayed_work dlywork101; struct delayed_work dlywork101;
@ -71,6 +70,8 @@ int ac101_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt);
int ac101_hw_params(struct snd_pcm_substream *substream, int ac101_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *params,
struct snd_soc_dai *codec_dai); struct snd_soc_dai *codec_dai);
int ac101_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai);
int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute); int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute);
/* codec driver specific */ /* codec driver specific */

View file

@ -216,8 +216,8 @@ static int asoc_simple_card_trigger(struct snd_pcm_substream *substream, int cmd
struct snd_soc_dai *dai = rtd->codec_dai; struct snd_soc_dai *dai = rtd->codec_dai;
int ret = 0; int ret = 0;
dev_dbg(rtd->card->dev, "%s() stream=%d cmd=%d play:%d, capt:%d\n", dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d\n",
__FUNCTION__, substream->stream, cmd, __FUNCTION__, snd_pcm_stream_str(substream), cmd,
dai->playback_active, dai->capture_active); dai->playback_active, dai->capture_active);
switch (cmd) { switch (cmd) {
@ -230,6 +230,10 @@ static int asoc_simple_card_trigger(struct snd_pcm_substream *substream, int cmd
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
/* capture channel resync, if overrun */
if (dai->capture_active && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
break;
}
if (_set_clock) _set_clock(0); if (_set_clock) _set_clock(0);
break; break;
default: default: