diff --git a/README.md b/README.md index 0bd57c0..ff11d4b 100644 --- a/README.md +++ b/README.md @@ -225,7 +225,7 @@ aplay -D plughw:1,0 -r 16000 mono_to_play.wav ``` **Note: Limit for developer using 6-Mics Circular Array Kit(or 4-Mics Linear Array Kit) doing capture & playback the same time: 1. capture must be start first, or else the capture channels will possibly be disorder. -2. playback output channels must fill with 8 same channels data or 4 same stereo channels data, or else the speaker or headphone will output nothing possily.** +2. playback output channels must fill with 8 same channels data or 4 same stereo channels data, or else the speaker or headphone will output nothing possibly.** ### Coherence diff --git a/ac101.c b/ac101.c index 2f6d1d0..f4a6abd 100644 --- a/ac101.c +++ b/ac101.c @@ -290,21 +290,21 @@ static int ac101_sysclk_started(void) { return (reg_val & (0x1<lock, flags); + /* spin_lock move to simple_card_trigger */ + switch (event) { case SND_SOC_DAPM_PRE_PMU: if (ac10x->aif1_clken == 0){ - - /* enable aif1clk & sysclk */ ret = ac101_update_bits(codec, SYSCLK_CTRL, (0x1<lock, flags); 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); @@ -571,7 +570,7 @@ int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute) #if _MASTER_MULTI_CODEC != _MASTER_AC101 /* enable global clock */ ac10x->aif1_clken = 0; - ac101_aif1clk(codec, SND_SOC_DAPM_PRE_PMU); + ac101_aif1clk(codec, SND_SOC_DAPM_PRE_PMU, 0); ac101_aif_play(ac10x); #else schedule_delayed_work(&ac10x->dlywork, msecs_to_jiffies(50)); @@ -582,7 +581,7 @@ int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute) #endif if (ac10x->gpiod_spk_amp_gate) { - gpiod_set_value(ac10x->gpiod_spk_amp_gate, 1); + gpiod_set_value(ac10x->gpiod_spk_amp_gate, 0); } /* Disable Left & Right Speaker */ ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN), (0x0 << LSPK_EN) | (0x0 << RSPK_EN)); @@ -594,7 +593,7 @@ int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute) #if _MASTER_MULTI_CODEC != _MASTER_AC101 ac10x->aif1_clken = 1; - ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD); + ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0); #endif } return 0; @@ -612,9 +611,9 @@ void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai if (!codec_dai->active) { ac10x->aif1_clken = 1; - ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD); + ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0); } else { - ac101_aif1clk(codec, SND_SOC_DAPM_PRE_PMU); + ac101_aif1clk(codec, SND_SOC_DAPM_PRE_PMU, 0); } } @@ -776,7 +775,7 @@ int ac101_hw_params(struct snd_pcm_substream *substream, #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); + ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0); #endif AC101_DBG("rate: %d , channels: %d , samp_res: %d", @@ -892,11 +891,11 @@ int ac101_audio_startup(struct snd_pcm_substream *substream, static int ac101_set_clock(int y_start_n_stop) { if (y_start_n_stop) { /* enable global clock */ - ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_PRE_PMU); + ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_PRE_PMU, 1); } else { /* disable global clock */ 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, 0); } return 0; } @@ -905,6 +904,8 @@ static int ac101_set_clock(int y_start_n_stop) { int ac101_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { + struct snd_soc_codec *codec = dai->codec; + struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); int ret = 0; AC101_DBG("%s() stream=%s cmd=%d\n", @@ -916,6 +917,17 @@ int ac101_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + #if _MASTER_MULTI_CODEC == _MASTER_AC101 + if (ac10x->aif1_clken == 0){ + /* + * enable aif1clk, it' here due to reduce time between 'AC108 Sysclk Enable' and 'AC101 Sysclk Enable' + * Or else the two AC108 chips lost the sync. + */ + ret = 0; + ret = ret || ac101_update_bits(codec, MOD_CLK_ENA, (0x1<dac_enable = 0; ac10x->aif1_clken = 0; mutex_init(&ac10x->dac_mutex); - spin_lock_init(&ac10x->lock); #if _MASTER_MULTI_CODEC == _MASTER_AC101 - asoc_simple_card_register_set_clock(ac101_set_clock); + asoc_simple_card_register_set_clock(SNDRV_PCM_STREAM_PLAYBACK, ac101_set_clock); #endif set_configuration(ac10x->codec); @@ -1059,6 +1070,7 @@ int ac101_codec_probe(struct snd_soc_codec *codec) pr_err("[ac10x] Failed to register audio mode control, " "will continue without it.\n"); } + return 0; } @@ -1159,9 +1171,34 @@ static const struct regmap_config ac101_regmap = { .val_bits = 16, .reg_stride = 1, .max_register = 0xB5, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_FLAT, }; +/* Sync reg_cache from the hardware */ +int ac10x_fill_regcache(struct device* dev, struct regmap* map) { + int r, i, n; + int v; + + n = regmap_get_max_register(map); + for (i = 0; i < n; i++) { + regcache_cache_bypass(map, true); + r = regmap_read(map, i, &v); + if (r) { + dev_err(dev, "failed to read register %d\n", i); + continue; + } + regcache_cache_bypass(map, false); + + regcache_cache_only(map, true); + r = regmap_write(map, i, v); + regcache_cache_only(map, false); + } + regcache_cache_bypass(map, false); + regcache_cache_only(map, false); + + return 0; +} + int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct ac10x_priv *ac10x = i2c_get_clientdata(i2c); @@ -1179,6 +1216,8 @@ int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id) return ret; } + ac10x_fill_regcache(&i2c->dev, ac10x->regmap101); + /* Chip reset */ /* ret = regmap_write(ac10x->regmap101, CHIP_AUDIO_RST, 0); @@ -1192,7 +1231,7 @@ int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id) } if (v != AC101_CHIP_ID) { - dev_err(&i2c->dev, "chip is not AC101\n"); + dev_err(&i2c->dev, "chip is not AC101 (%X)\n", v); dev_err(&i2c->dev, "Expected %X\n", AC101_CHIP_ID); return -ENODEV; } diff --git a/ac108.c b/ac108.c index 9319595..070ee27 100644 --- a/ac108.c +++ b/ac108.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -37,9 +38,8 @@ /** * TODO: * 1, add PM API: ac108_suspend,ac108_resume - * 2, add set_pll ,set_clkdiv - * 3,0x65-0x6a - * 4,0x76-0x79 high 4bit + * 2,0x65-0x6a + * 3,0x76-0x79 high 4bit */ struct pll_div { unsigned int freq_in; @@ -794,6 +794,7 @@ static int ac108_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_h /*0x22: Module reset de-asserted*/ ac108_multi_write(MOD_RST_CTRL, 1 << I2S | 1 << ADC_DIGITAL | 1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG, ac10x); + dev_dbg(dai->dev, "%s() stream=%s ---\n", __func__, snd_pcm_stream_str(substream)); @@ -865,7 +866,7 @@ static int ac108_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { * 0x30:chip is slave mode, BCLK & LRCK input,enable SDO1_EN and * SDO2_EN, Transmitter Block Enable, Globe Enable */ - ac108_multi_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN | 0x03 << SDO1_EN | 0x0 << TXEN | 0x0 << GEN, + ac108_multi_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN | 0x03 << SDO1_EN | 0x1 << TXEN | 0x1 << GEN, 0x00 << LRCK_IOEN | 0x03 << SDO1_EN | 0x0 << TXEN | 0x0 << GEN, ac10x); break; default: @@ -982,31 +983,44 @@ static int ac108_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { * due to miss channels order in cpu_dai, we meed defer the clock starting. */ static int ac108_set_clock(int y_start_n_stop) { - unsigned long flags; u8 r; - dev_dbg(ac10x->codec->dev, "%s() L%d start:%d\n", __func__, __LINE__, y_start_n_stop); + dev_dbg(ac10x->codec->dev, "%s() L%d cmd:%d\n", __func__, __LINE__, y_start_n_stop); + + /* spin_lock move to simple_card_trigger */ + + if (y_start_n_stop) { + if (ac10x->sysclk_en == 0) { - spin_lock_irqsave(&ac10x->lock, flags); - if (y_start_n_stop) { /* enable lrck clock */ ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]); if (r & (0x01 << BCLK_IOEN)) { ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x03 << LRCK_IOEN, ac10x->i2cmap[_MASTER_INDEX]); } + /*0x10: PLL Common voltage enable, PLL enable */ + ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN, + 0x01 << PLL_EN | 0x01 << PLL_COM_EN, ac10x); /* enable global clock */ ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x1 << TXEN | 0x1 << GEN, ac10x); - } else { + + ac10x->sysclk_en = 1UL; + } + } else if (ac10x->sysclk_en != 0) { + /* disable global clock */ + ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x0 << TXEN | 0x0 << GEN, ac10x); + + /*0x10: PLL Common voltage disable, PLL disable */ + ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN, + 0x00 << PLL_EN | 0x00 << PLL_COM_EN, ac10x); + /* disable lrck clock if it's enabled */ ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]); if (r & (0x01 << LRCK_IOEN)) { - ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x1 << TXEN | 0x0 << GEN, ac10x); ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x01 << BCLK_IOEN, ac10x->i2cmap[_MASTER_INDEX]); - ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x1 << TXEN | 0x1 << GEN, ac10x); } + ac10x->sysclk_en = 0UL; } - spin_unlock_irqrestore(&ac10x->lock, flags); return 0; } @@ -1052,29 +1066,14 @@ static int ac108_trigger(struct snd_pcm_substream *substream, int cmd, ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]); if ((r & (0x01 << BCLK_IOEN)) && (r & (0x01 << LRCK_IOEN)) == 0) { /* disable global clock */ - ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x1 << TXEN | 0x0 << GEN, ac10x); + ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x0 << TXEN | 0x0 << GEN, ac10x); } - /*0x10: PLL Common voltage enable, PLL enable */ - ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN, - 0x01 << PLL_EN | 0x01 << PLL_COM_EN, ac10x); - - if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) { - /* enable global clock */ - ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x1 << TXEN | 0x1 << GEN, ac10x); - } /* delayed clock starting, move to simple_card_trigger() */ break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - /*0x10: PLL Common voltage disable, PLL disable */ - ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN, - 0x00 << PLL_EN | 0x00 << PLL_COM_EN, ac10x); - if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) { - /* disable global clock */ - ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x0 << TXEN | 0x0 << GEN, ac10x); - } break; default: ret = -EINVAL; @@ -1104,6 +1103,13 @@ void ac108_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + /*0x21: Module clock disable */ + ac108_multi_write(MOD_CLK_EN, 0x0, ac10x); + /*0x22: Module reset asserted */ + ac108_multi_write(MOD_RST_CTRL, 0x0, ac10x); + } + if (ac10x->i2c101) { ac101_aif_shutdown(substream, dai); } @@ -1204,7 +1210,10 @@ static int ac108_add_widgets(struct snd_soc_codec *codec) { return 0; } -static int ac108_probe(struct snd_soc_codec *codec) { +static int ac108_codec_probe(struct snd_soc_codec *codec) { + + spin_lock_init(&ac10x->lock); + ac10x->codec = codec; dev_set_drvdata(codec->dev, ac10x); ac108_add_widgets(codec); @@ -1212,6 +1221,7 @@ static int ac108_probe(struct snd_soc_codec *codec) { if (ac10x->i2c101) { ac101_codec_probe(codec); } + return 0; } @@ -1293,7 +1303,7 @@ int ac108_codec_resume(struct snd_soc_codec *codec) { } static struct snd_soc_codec_driver ac10x_soc_codec_driver = { - .probe = ac108_probe, + .probe = ac108_codec_probe, .remove = ac108_codec_remove, .suspend = ac108_codec_suspend, .resume = ac108_codec_resume, @@ -1371,7 +1381,7 @@ static const struct regmap_config ac108_regmap = { .val_bits = 8, .reg_stride = 1, .max_register = 0xDF, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_FLAT, }; static int ac108_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i2c_id) { struct device_node *np = i2c->dev.of_node; @@ -1392,6 +1402,7 @@ static int ac108_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i i2c_set_clientdata(i2c, ac10x); ret = ac101_probe(i2c, i2c_id); if (ret) { + ac10x->i2c101 = NULL; return ret; } goto __ret; @@ -1418,6 +1429,8 @@ static int ac108_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i return ret; } + ac10x_fill_regcache(&i2c->dev, ac10x->i2cmap[index]); + /* * Writing this register with 0x12 * will resets all register to their default state. @@ -1437,9 +1450,7 @@ __ret: /* It's time to bind codec to i2c[_MASTER_INDEX] when all i2c are ready */ if ((ac10x->codec_cnt != 0 && ac10x->tdm_chips_cnt < 2) || (ac10x->i2c[0] && ac10x->i2c[1] && ac10x->i2c101)) { - if (! ac10x->i2c101 || _MASTER_MULTI_CODEC == _MASTER_AC108) { - asoc_simple_card_register_set_clock(ac108_set_clock); - } + asoc_simple_card_register_set_clock(SNDRV_PCM_STREAM_CAPTURE, ac108_set_clock); /* no playback stream */ if (! ac10x->i2c101) { memset(&ac108_dai[_MASTER_INDEX]->playback, '\0', sizeof ac108_dai[_MASTER_INDEX]->playback); diff --git a/ac10x.h b/ac10x.h index 9d9ed0e..c450a50 100644 --- a/ac10x.h +++ b/ac10x.h @@ -46,6 +46,7 @@ struct ac10x_priv { unsigned char data_protocol; struct delayed_work dlywork; int tdm_chips_cnt; + int sysclk_en; /* member for ac101 .begin */ struct snd_soc_codec *codec; @@ -86,6 +87,8 @@ void ac101_shutdown(struct i2c_client *i2c); int ac101_remove(struct i2c_client *i2c); /* simple card export */ -int asoc_simple_card_register_set_clock(int (*set_clock)(int)); +int asoc_simple_card_register_set_clock(int stream, int (*set_clock)(int)); + +int ac10x_fill_regcache(struct device* dev, struct regmap* map); #endif//__AC10X_H__ diff --git a/simple-card.c b/simple-card.c index 188539b..b38fbfc 100644 --- a/simple-card.c +++ b/simple-card.c @@ -51,6 +51,7 @@ struct simple_card_data { struct asoc_simple_jack hp_jack; struct asoc_simple_jack mic_jack; struct snd_soc_dai_link *dai_link; + spinlock_t lock; }; #define simple_priv_to_dev(priv) ((priv)->snd_card.dev) @@ -203,10 +204,13 @@ err: return ret; } -static int (* _set_clock)(int y_start_n_stop); +#define _SET_CLOCK_CNT 2 +static int (* _set_clock[_SET_CLOCK_CNT])(int y_start_n_stop); -int asoc_simple_card_register_set_clock(int (*set_clock)(int)) { - _set_clock = set_clock; +int asoc_simple_card_register_set_clock(int stream, int (*set_clock)(int)) { + if (! _set_clock[stream]) { + _set_clock[stream] = set_clock; + } return 0; } EXPORT_SYMBOL(asoc_simple_card_register_set_clock); @@ -215,17 +219,23 @@ static int asoc_simple_card_trigger(struct snd_pcm_substream *substream, int cmd { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *dai = rtd->codec_dai; + struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + unsigned long flags; int ret = 0; 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); + /* I know it will degrades performance, but I have no choice */ + spin_lock_irqsave(&priv->lock, flags); + switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (_set_clock) _set_clock(1); + if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](1); + if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](1); break; case SNDRV_PCM_TRIGGER_STOP: @@ -235,12 +245,15 @@ static int asoc_simple_card_trigger(struct snd_pcm_substream *substream, int cmd if (dai->capture_active && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { break; } - if (_set_clock) _set_clock(0); + if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](0); + if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0); break; default: ret = -EINVAL; } + spin_unlock_irqrestore(&priv->lock, flags); + return ret; } @@ -605,9 +618,12 @@ static int asoc_simple_card_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(&priv->snd_card, priv); + spin_lock_init(&priv->lock); + ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); if (ret >= 0) return ret; + err: asoc_simple_card_clean_reference(&priv->snd_card);