diff --git a/ac101.c b/ac101.c index 8411854..b2d7fb1 100644 --- a/ac101.c +++ b/ac101.c @@ -36,6 +36,15 @@ #include "ac101_regs.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*/ static bool speaker_double_used = 1; 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) { struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); + mutex_lock(&ac10x->dac_mutex); switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -276,49 +286,66 @@ static int ac101_headphone_event(struct snd_soc_codec* codec, int event) { } return 0; } + +static int ac101_sysclk_started(void) { + int reg_val; + + reg_val = ac101_read(static_ac10x->codec, SYSCLK_CTRL); + return (reg_val & (0x1<aifclk_mutex); + switch (event) { case SND_SOC_DAPM_PRE_PMU: if (ac10x->aif1_clken == 0){ - /*enable AIF1CLK*/ - ac101_update_bits(codec, SYSCLK_CTRL, (0x1<aif2_clken == 0) - ac101_update_bits(codec, SYSCLK_CTRL, (0x1<aif1_clken++; + /* I know it will degrades performance, but I have no choice */ + spin_lock_irqsave(&ac10x->lock, flags); - break; - case SND_SOC_DAPM_POST_PMD: - #if 0 - if (ac10x->aif1_clken > 0){ - ac10x->aif1_clken--; - if (ac10x->aif1_clken == 0){ - #else - { - if (ac10x->aif1_clken != 0) { - ac10x->aif1_clken = 0; - #endif - /*disable AIF1CLK*/ - ac101_update_bits(codec, SYSCLK_CTRL, (0x1<aif2_clken == 0) - ac101_update_bits(codec, SYSCLK_CTRL, (0x1<lock, flags); + + if (ret) { + AC101_DBG("%s() L%d start sysclk failed\n", __func__, __LINE__); + } else { + AC101_DBG("%s() L%d hw sysclk enable\n", __func__, __LINE__); + ac10x->aif1_clken++; } } 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<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; } @@ -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); late_enable_dac(codec, SND_SOC_DAPM_POST_PMD); + #if _MASTER_MULTI_CODEC != _MASTER_AC101 ac10x->aif1_clken = 1; ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD); + #endif } 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) { 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__, - substream->stream, codec_dai->playback_active); + AC101_DBG("%s,line:%d stream = %s, play: %d, capt: %d, active: %d\n", __func__, __LINE__, + snd_pcm_stream_str(substream), + codec_dai->playback_active, codec_dai->capture_active, + codec_dai->active); - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - reg_val = (ac101_read(codec, AIF_SR_CTRL) >> 12) & 0xF; - if (codec_dai->playback_active && dmic_used && reg_val == 0x4) { - ac101_update_bits(codec, AIF_SR_CTRL, (0xf<active) { + ac10x->aif1_clken = 1; + ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD); } } static int ac101_set_pll(struct snd_soc_dai *codec_dai, int pll_id, int source, 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; + int i, m, n_i, n_f; 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: return -EINVAL; } + /* 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)) { m = codec_pll_div[i].m; 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__); - /* Master mode, to clear cpu_dai fifos, disable output bclk & lrck */ - #if 0 - ac10x->aif1_clken = 1; - ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD); - #endif + if (_MASTER_MULTI_CODEC == _MASTER_AC101 && ac101_sysclk_started()) { + /* not configure hw_param twice, tell the caller it's started */ + return 1; + } /* get channels count & slot size */ 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); } + #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", params_rate(params), channels, aif1_slot_size); @@ -846,15 +877,39 @@ static int ac101_set_clock(int y_start_n_stop) { /* enable global clock */ ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_PRE_PMU); } else { - #if 0 + /* disable global clock */ static_ac10x->aif1_clken = 1; ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_POST_PMD); - #endif } return 0; } #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 static int ac101_set_dai_sysclk(struct snd_soc_dai *codec_dai, 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; } -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 = { //.startup = ac101_audio_startup, //.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); ac10x->dac_enable = 0; ac10x->aif1_clken = 0; - ac10x->aif2_clken = 0; mutex_init(&ac10x->dac_mutex); - mutex_init(&ac10x->aifclk_mutex); + spin_lock_init(&ac10x->lock); #if _MASTER_MULTI_CODEC == _MASTER_AC101 asoc_simple_card_register_set_clock(ac101_set_clock); diff --git a/ac108.c b/ac108.c index 31f4d4b..110946b 100644 --- a/ac108.c +++ b/ac108.c @@ -647,16 +647,23 @@ static int ac108_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_h int ret = 0; u8 v; - dev_dbg(dai->dev, "%s() stream=%d play:%d capt:%d +++\n", __func__, - substream->stream, dai->playback_active, dai->capture_active); + dev_dbg(dai->dev, "%s() stream=%s play:%d capt:%d +++\n", __func__, + snd_pcm_stream_str(substream), + dai->playback_active, dai->capture_active); if (ac10x->i2c101) { 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) { - // TODO: + if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE && dai->playback_active) + || (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai->capture_active)) { + /* not configure hw_param twice */ + /* return 0; */ } 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); - 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; } @@ -1005,6 +1013,16 @@ static int ac108_set_clock(int y_start_n_stop) { 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, struct snd_soc_dai *dai) { @@ -1013,8 +1031,17 @@ static int ac108_trigger(struct snd_pcm_substream *substream, int cmd, int ret = 0; u8 r; - dev_dbg(dai->dev, "%s() stream=%d cmd=%d\n", - __FUNCTION__, substream->stream, cmd); + dev_dbg(dai->dev, "%s() stream=%s cmd=%d\n", + __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) { case SNDRV_PCM_TRIGGER_START: @@ -1088,6 +1115,7 @@ static const struct snd_soc_dai_ops ac108_dai_ops = { /*ALSA PCM audio operations*/ .hw_params = ac108_hw_params, + .prepare = ac108_prepare, .trigger = ac108_trigger, .digital_mute = ac108_aif_mute, diff --git a/ac10x.h b/ac10x.h index 4253446..acf94bf 100644 --- a/ac10x.h +++ b/ac10x.h @@ -47,16 +47,15 @@ struct ac10x_priv { struct delayed_work dlywork; int tdm_chips_cnt; - /* memboer for ac101 .begin */ + /* member for ac101 .begin */ struct snd_soc_codec *codec; struct i2c_client *i2c101; struct regmap* regmap101; struct mutex dac_mutex; u8 dac_enable; - struct mutex aifclk_mutex; + spinlock_t lock; u8 aif1_clken; - u8 aif2_clken; struct work_struct codec_resume; 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, struct snd_pcm_hw_params *params, 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); /* codec driver specific */ diff --git a/simple-card.c b/simple-card.c index 915bc78..9ffc5c1 100644 --- a/simple-card.c +++ b/simple-card.c @@ -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; int ret = 0; - dev_dbg(rtd->card->dev, "%s() stream=%d cmd=%d play:%d, capt:%d\n", - __FUNCTION__, substream->stream, cmd, + dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d\n", + __FUNCTION__, snd_pcm_stream_str(substream), cmd, dai->playback_active, dai->capture_active); 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_SUSPEND: 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); break; default: