Fix: 4MIC recording by portaudio & pyaudio
This commit is contained in:
parent
86329f0719
commit
8211f4c000
5 changed files with 131 additions and 62 deletions
|
@ -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:
|
**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.
|
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
|
### Coherence
|
||||||
|
|
||||||
|
|
81
ac101.c
81
ac101.c
|
@ -290,21 +290,21 @@ static int ac101_sysclk_started(void) {
|
||||||
return (reg_val & (0x1<<SYSCLK_ENA));
|
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, int quick) {
|
||||||
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;
|
int ret;
|
||||||
|
|
||||||
/* I know it will degrades performance, but I have no choice */
|
/* spin_lock move to simple_card_trigger */
|
||||||
spin_lock_irqsave(&ac10x->lock, flags);
|
|
||||||
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 & sysclk */
|
|
||||||
ret = ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<AIF1CLK_ENA), (0x1<<AIF1CLK_ENA));
|
ret = ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<AIF1CLK_ENA), (0x1<<AIF1CLK_ENA));
|
||||||
ret = ret || ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_AIF1), (0x1<<MOD_CLK_AIF1));
|
if(!quick || _MASTER_MULTI_CODEC != _MASTER_AC101) {
|
||||||
ret = ret || ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_AIF1), (0x1<<MOD_RESET_AIF1));
|
/* enable aif1clk & sysclk */
|
||||||
|
ret = ret || ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_AIF1), (0x1<<MOD_CLK_AIF1));
|
||||||
|
ret = ret || ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_AIF1), (0x1<<MOD_RESET_AIF1));
|
||||||
|
}
|
||||||
ret = ret || ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<SYSCLK_ENA), (0x1<<SYSCLK_ENA));
|
ret = ret || ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<SYSCLK_ENA), (0x1<<SYSCLK_ENA));
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -332,7 +332,6 @@ static int ac101_aif1clk(struct snd_soc_codec* codec, int event) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&ac10x->lock, flags);
|
|
||||||
|
|
||||||
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);
|
||||||
|
@ -571,7 +570,7 @@ int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute)
|
||||||
#if _MASTER_MULTI_CODEC != _MASTER_AC101
|
#if _MASTER_MULTI_CODEC != _MASTER_AC101
|
||||||
/* enable global clock */
|
/* enable global clock */
|
||||||
ac10x->aif1_clken = 0;
|
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);
|
ac101_aif_play(ac10x);
|
||||||
#else
|
#else
|
||||||
schedule_delayed_work(&ac10x->dlywork, msecs_to_jiffies(50));
|
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
|
#endif
|
||||||
|
|
||||||
if (ac10x->gpiod_spk_amp_gate) {
|
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 */
|
/* Disable Left & Right Speaker */
|
||||||
ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN), (0x0 << LSPK_EN) | (0x0 << RSPK_EN));
|
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
|
#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, 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -612,9 +611,9 @@ void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai
|
||||||
|
|
||||||
if (!codec_dai->active) {
|
if (!codec_dai->active) {
|
||||||
ac10x->aif1_clken = 1;
|
ac10x->aif1_clken = 1;
|
||||||
ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD);
|
ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0);
|
||||||
} else {
|
} 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
|
#if _MASTER_MULTI_CODEC == _MASTER_AC101
|
||||||
/* Master mode, to clear cpu_dai fifos, disable output bclk & lrck */
|
/* 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
|
#endif
|
||||||
|
|
||||||
AC101_DBG("rate: %d , channels: %d , samp_res: %d",
|
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) {
|
static int ac101_set_clock(int y_start_n_stop) {
|
||||||
if (y_start_n_stop) {
|
if (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, 1);
|
||||||
} else {
|
} else {
|
||||||
/* disable global clock */
|
/* 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, 0);
|
||||||
}
|
}
|
||||||
return 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,
|
int ac101_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||||
struct snd_soc_dai *dai)
|
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;
|
int ret = 0;
|
||||||
|
|
||||||
AC101_DBG("%s() stream=%s cmd=%d\n",
|
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_START:
|
||||||
case SNDRV_PCM_TRIGGER_RESUME:
|
case SNDRV_PCM_TRIGGER_RESUME:
|
||||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
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<<MOD_CLK_AIF1), (0x1<<MOD_CLK_AIF1));
|
||||||
|
ret = ret || ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_AIF1), (0x1<<MOD_RESET_AIF1));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_TRIGGER_STOP:
|
case SNDRV_PCM_TRIGGER_STOP:
|
||||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||||
|
@ -1035,10 +1047,9 @@ int ac101_codec_probe(struct snd_soc_codec *codec)
|
||||||
ac10x->dac_enable = 0;
|
ac10x->dac_enable = 0;
|
||||||
ac10x->aif1_clken = 0;
|
ac10x->aif1_clken = 0;
|
||||||
mutex_init(&ac10x->dac_mutex);
|
mutex_init(&ac10x->dac_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(SNDRV_PCM_STREAM_PLAYBACK, ac101_set_clock);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
set_configuration(ac10x->codec);
|
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, "
|
pr_err("[ac10x] Failed to register audio mode control, "
|
||||||
"will continue without it.\n");
|
"will continue without it.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1159,9 +1171,34 @@ static const struct regmap_config ac101_regmap = {
|
||||||
.val_bits = 16,
|
.val_bits = 16,
|
||||||
.reg_stride = 1,
|
.reg_stride = 1,
|
||||||
.max_register = 0xB5,
|
.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)
|
int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct ac10x_priv *ac10x = i2c_get_clientdata(i2c);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ac10x_fill_regcache(&i2c->dev, ac10x->regmap101);
|
||||||
|
|
||||||
/* Chip reset */
|
/* Chip reset */
|
||||||
/*
|
/*
|
||||||
ret = regmap_write(ac10x->regmap101, CHIP_AUDIO_RST, 0);
|
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) {
|
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);
|
dev_err(&i2c->dev, "Expected %X\n", AC101_CHIP_ID);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
79
ac108.c
79
ac108.c
|
@ -22,6 +22,7 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/pcm.h>
|
#include <sound/pcm.h>
|
||||||
#include <sound/pcm_params.h>
|
#include <sound/pcm_params.h>
|
||||||
|
@ -37,9 +38,8 @@
|
||||||
/**
|
/**
|
||||||
* TODO:
|
* TODO:
|
||||||
* 1, add PM API: ac108_suspend,ac108_resume
|
* 1, add PM API: ac108_suspend,ac108_resume
|
||||||
* 2, add set_pll ,set_clkdiv
|
* 2,0x65-0x6a
|
||||||
* 3,0x65-0x6a
|
* 3,0x76-0x79 high 4bit
|
||||||
* 4,0x76-0x79 high 4bit
|
|
||||||
*/
|
*/
|
||||||
struct pll_div {
|
struct pll_div {
|
||||||
unsigned int freq_in;
|
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<I2S, ADC digital, MIC offset Calibration, ADC analog>*/
|
/*0x22: Module reset de-asserted<I2S, ADC digital, MIC offset Calibration, ADC analog>*/
|
||||||
ac108_multi_write(MOD_RST_CTRL, 1 << I2S | 1 << ADC_DIGITAL | 1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG, ac10x);
|
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__,
|
dev_dbg(dai->dev, "%s() stream=%s ---\n", __func__,
|
||||||
snd_pcm_stream_str(substream));
|
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
|
* 0x30:chip is slave mode, BCLK & LRCK input,enable SDO1_EN and
|
||||||
* SDO2_EN, Transmitter Block Enable, Globe Enable
|
* 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);
|
0x00 << LRCK_IOEN | 0x03 << SDO1_EN | 0x0 << TXEN | 0x0 << GEN, ac10x);
|
||||||
break;
|
break;
|
||||||
default:
|
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.
|
* due to miss channels order in cpu_dai, we meed defer the clock starting.
|
||||||
*/
|
*/
|
||||||
static int ac108_set_clock(int y_start_n_stop) {
|
static int ac108_set_clock(int y_start_n_stop) {
|
||||||
unsigned long flags;
|
|
||||||
u8 r;
|
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 */
|
/* enable lrck clock */
|
||||||
ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]);
|
ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]);
|
||||||
if (r & (0x01 << BCLK_IOEN)) {
|
if (r & (0x01 << BCLK_IOEN)) {
|
||||||
ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x03 << LRCK_IOEN, ac10x->i2cmap[_MASTER_INDEX]);
|
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 */
|
/* enable global clock */
|
||||||
ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x1 << TXEN | 0x1 << GEN, ac10x);
|
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 */
|
/* disable lrck clock if it's enabled */
|
||||||
ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]);
|
ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]);
|
||||||
if (r & (0x01 << LRCK_IOEN)) {
|
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]);
|
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;
|
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]);
|
ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]);
|
||||||
if ((r & (0x01 << BCLK_IOEN)) && (r & (0x01 << LRCK_IOEN)) == 0) {
|
if ((r & (0x01 << BCLK_IOEN)) && (r & (0x01 << LRCK_IOEN)) == 0) {
|
||||||
/* disable global clock */
|
/* 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() */
|
/* delayed clock starting, move to simple_card_trigger() */
|
||||||
break;
|
break;
|
||||||
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:
|
||||||
/*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;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
@ -1104,6 +1103,13 @@ void ac108_aif_shutdown(struct snd_pcm_substream *substream,
|
||||||
struct snd_soc_codec *codec = dai->codec;
|
struct snd_soc_codec *codec = dai->codec;
|
||||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||||
|
|
||||||
|
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||||
|
/*0x21: Module clock disable <I2S, ADC digital, MIC offset Calibration, ADC analog>*/
|
||||||
|
ac108_multi_write(MOD_CLK_EN, 0x0, ac10x);
|
||||||
|
/*0x22: Module reset asserted <I2S, ADC digital, MIC offset Calibration, ADC analog>*/
|
||||||
|
ac108_multi_write(MOD_RST_CTRL, 0x0, ac10x);
|
||||||
|
}
|
||||||
|
|
||||||
if (ac10x->i2c101) {
|
if (ac10x->i2c101) {
|
||||||
ac101_aif_shutdown(substream, dai);
|
ac101_aif_shutdown(substream, dai);
|
||||||
}
|
}
|
||||||
|
@ -1204,7 +1210,10 @@ static int ac108_add_widgets(struct snd_soc_codec *codec) {
|
||||||
return 0;
|
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;
|
ac10x->codec = codec;
|
||||||
dev_set_drvdata(codec->dev, ac10x);
|
dev_set_drvdata(codec->dev, ac10x);
|
||||||
ac108_add_widgets(codec);
|
ac108_add_widgets(codec);
|
||||||
|
@ -1212,6 +1221,7 @@ static int ac108_probe(struct snd_soc_codec *codec) {
|
||||||
if (ac10x->i2c101) {
|
if (ac10x->i2c101) {
|
||||||
ac101_codec_probe(codec);
|
ac101_codec_probe(codec);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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 = {
|
static struct snd_soc_codec_driver ac10x_soc_codec_driver = {
|
||||||
.probe = ac108_probe,
|
.probe = ac108_codec_probe,
|
||||||
.remove = ac108_codec_remove,
|
.remove = ac108_codec_remove,
|
||||||
.suspend = ac108_codec_suspend,
|
.suspend = ac108_codec_suspend,
|
||||||
.resume = ac108_codec_resume,
|
.resume = ac108_codec_resume,
|
||||||
|
@ -1371,7 +1381,7 @@ static const struct regmap_config ac108_regmap = {
|
||||||
.val_bits = 8,
|
.val_bits = 8,
|
||||||
.reg_stride = 1,
|
.reg_stride = 1,
|
||||||
.max_register = 0xDF,
|
.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) {
|
static int ac108_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i2c_id) {
|
||||||
struct device_node *np = i2c->dev.of_node;
|
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);
|
i2c_set_clientdata(i2c, ac10x);
|
||||||
ret = ac101_probe(i2c, i2c_id);
|
ret = ac101_probe(i2c, i2c_id);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
ac10x->i2c101 = NULL;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
goto __ret;
|
goto __ret;
|
||||||
|
@ -1418,6 +1429,8 @@ static int ac108_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ac10x_fill_regcache(&i2c->dev, ac10x->i2cmap[index]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Writing this register with 0x12
|
* Writing this register with 0x12
|
||||||
* will resets all register to their default state.
|
* 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 */
|
/* 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)
|
if ((ac10x->codec_cnt != 0 && ac10x->tdm_chips_cnt < 2)
|
||||||
|| (ac10x->i2c[0] && ac10x->i2c[1] && ac10x->i2c101)) {
|
|| (ac10x->i2c[0] && ac10x->i2c[1] && ac10x->i2c101)) {
|
||||||
if (! ac10x->i2c101 || _MASTER_MULTI_CODEC == _MASTER_AC108) {
|
asoc_simple_card_register_set_clock(SNDRV_PCM_STREAM_CAPTURE, ac108_set_clock);
|
||||||
asoc_simple_card_register_set_clock(ac108_set_clock);
|
|
||||||
}
|
|
||||||
/* no playback stream */
|
/* no playback stream */
|
||||||
if (! ac10x->i2c101) {
|
if (! ac10x->i2c101) {
|
||||||
memset(&ac108_dai[_MASTER_INDEX]->playback, '\0', sizeof ac108_dai[_MASTER_INDEX]->playback);
|
memset(&ac108_dai[_MASTER_INDEX]->playback, '\0', sizeof ac108_dai[_MASTER_INDEX]->playback);
|
||||||
|
|
5
ac10x.h
5
ac10x.h
|
@ -46,6 +46,7 @@ struct ac10x_priv {
|
||||||
unsigned char data_protocol;
|
unsigned char data_protocol;
|
||||||
struct delayed_work dlywork;
|
struct delayed_work dlywork;
|
||||||
int tdm_chips_cnt;
|
int tdm_chips_cnt;
|
||||||
|
int sysclk_en;
|
||||||
|
|
||||||
/* member for ac101 .begin */
|
/* member for ac101 .begin */
|
||||||
struct snd_soc_codec *codec;
|
struct snd_soc_codec *codec;
|
||||||
|
@ -86,6 +87,8 @@ void ac101_shutdown(struct i2c_client *i2c);
|
||||||
int ac101_remove(struct i2c_client *i2c);
|
int ac101_remove(struct i2c_client *i2c);
|
||||||
|
|
||||||
/* simple card export */
|
/* 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__
|
#endif//__AC10X_H__
|
||||||
|
|
|
@ -51,6 +51,7 @@ struct simple_card_data {
|
||||||
struct asoc_simple_jack hp_jack;
|
struct asoc_simple_jack hp_jack;
|
||||||
struct asoc_simple_jack mic_jack;
|
struct asoc_simple_jack mic_jack;
|
||||||
struct snd_soc_dai_link *dai_link;
|
struct snd_soc_dai_link *dai_link;
|
||||||
|
spinlock_t lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define simple_priv_to_dev(priv) ((priv)->snd_card.dev)
|
#define simple_priv_to_dev(priv) ((priv)->snd_card.dev)
|
||||||
|
@ -203,10 +204,13 @@ err:
|
||||||
return ret;
|
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)) {
|
int asoc_simple_card_register_set_clock(int stream, int (*set_clock)(int)) {
|
||||||
_set_clock = set_clock;
|
if (! _set_clock[stream]) {
|
||||||
|
_set_clock[stream] = set_clock;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(asoc_simple_card_register_set_clock);
|
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_pcm_runtime *rtd = substream->private_data;
|
||||||
struct snd_soc_dai *dai = rtd->codec_dai;
|
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;
|
int ret = 0;
|
||||||
|
|
||||||
dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d\n",
|
dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d\n",
|
||||||
__FUNCTION__, snd_pcm_stream_str(substream), cmd,
|
__FUNCTION__, snd_pcm_stream_str(substream), cmd,
|
||||||
dai->playback_active, dai->capture_active);
|
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) {
|
switch (cmd) {
|
||||||
case SNDRV_PCM_TRIGGER_START:
|
case SNDRV_PCM_TRIGGER_START:
|
||||||
case SNDRV_PCM_TRIGGER_RESUME:
|
case SNDRV_PCM_TRIGGER_RESUME:
|
||||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
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;
|
break;
|
||||||
|
|
||||||
case SNDRV_PCM_TRIGGER_STOP:
|
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) {
|
if (dai->capture_active && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||||
break;
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&priv->lock, flags);
|
||||||
|
|
||||||
return ret;
|
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);
|
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);
|
ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
|
||||||
if (ret >= 0)
|
if (ret >= 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
asoc_simple_card_clean_reference(&priv->snd_card);
|
asoc_simple_card_clean_reference(&priv->snd_card);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue