author | Song Zhao <song.zhao@amlogic.com> | 2019-03-28 21:31:24 (GMT) |
---|---|---|
committer | Xiaoliang Wang <xiaoliang.wang@amlogic.com> | 2019-04-11 01:27:16 (GMT) |
commit | 0ccf4012679d41bbd5fe15083df28af0e4e189f1 (patch) | |
tree | 6ea91965b6bcc6fa0679bde9d27fc732f1fbbe59 | |
parent | bd011bb30410c2194da85b12440ff741ea42a697 (diff) | |
download | common-0ccf4012679d41bbd5fe15083df28af0e4e189f1.zip common-0ccf4012679d41bbd5fe15083df28af0e4e189f1.tar.gz common-0ccf4012679d41bbd5fe15083df28af0e4e189f1.tar.bz2 |
alsa: workaround for cs42528 noise issue
* Disable auto mute for cs42528
* Use HW timer for SPDIF input for a stable xrun detection
* For SPDIF input, when FIFO xrun happens, let application get the
error code ASAP, so the pipeline has enough data for fade out.
related about
b/118851574
b/119818288
b/120102523
Change-Id: If0b732fd191c9196fd44c23f14e2bba3fd64bd0b
-rw-r--r-- | sound/soc/amlogic/meson/audio_hw.c | 2 | ||||
-rw-r--r-- | sound/soc/amlogic/meson/i2s.c | 158 | ||||
-rw-r--r-- | sound/soc/amlogic/meson/i2s.h | 3 | ||||
-rw-r--r-- | sound/soc/amlogic/meson/spdif_dai.c | 4 | ||||
-rw-r--r-- | sound/soc/codecs/amlogic/cs42528.c | 21 |
5 files changed, 119 insertions, 69 deletions
diff --git a/sound/soc/amlogic/meson/audio_hw.c b/sound/soc/amlogic/meson/audio_hw.c index 49972f8..e568c2d 100644 --- a/sound/soc/amlogic/meson/audio_hw.c +++ b/sound/soc/amlogic/meson/audio_hw.c @@ -478,7 +478,7 @@ static void spdifin_reg_set(void) u32 period_96k = (period_data / 48 + 1) >> 1; /* 96k min period */ u32 period_192k = (period_data / 96) >> 1; /* 192k min period */ - pr_info("spdifin_reg_set: clk_rate=%d\n", clk_rate); + //pr_info("spdifin_reg_set: clk_rate=%d\n", clk_rate); aml_audin_write(AUDIN_SPDIF_MODE, (aml_audin_read(AUDIN_SPDIF_MODE) & 0x7fffc000) | diff --git a/sound/soc/amlogic/meson/i2s.c b/sound/soc/amlogic/meson/i2s.c index af1dcbd..a340f33ae 100644 --- a/sound/soc/amlogic/meson/i2s.c +++ b/sound/soc/amlogic/meson/i2s.c @@ -31,6 +31,7 @@ #include <linux/hrtimer.h> #include <linux/debugfs.h> #include <linux/major.h> +#include <linux/of_irq.h> #include <sound/core.h> #include <sound/pcm.h> @@ -52,13 +53,8 @@ * timer for i2s data update, hw timer, hrtimer, timer * once select only one way to update */ -/*#define USE_HW_TIMER*/ /*#define USE_HRTIMER*/ -#ifdef USE_HW_TIMER -#define XRUN_NUM 100 /*1ms*100=100ms timeout*/ -#else #define XRUN_NUM 10 /*10ms*10=100ms timeout*/ -#endif unsigned long aml_i2s_playback_start_addr; EXPORT_SYMBOL(aml_i2s_playback_start_addr); @@ -88,6 +84,8 @@ EXPORT_SYMBOL(aml_audio_hw_trigger); #ifndef USE_HRTIMER static void aml_i2s_timer_callback(unsigned long data); #endif +static int timer_irq_num; +static int snd_request_hw_timer(struct aml_runtime_data *prtd); /*--------------------------------------------------------------------------* * Hardware definition @@ -350,11 +348,31 @@ static int aml_i2s_prepare(struct snd_pcm_substream *substream) tmp_buf->cached_sample = 0; #endif + if (prtd->timer_init) + return 0; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && + s && s->device_type == AML_AUDIO_SPDIFIN) { + int ret = snd_request_hw_timer(prtd); + + if (ret < 0) + return ret; + } else { +#if defined(USE_HRTIMER) + hrtimer_init(&prtd->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + prtd->hrtimer.function = aml_i2s_hrtimer_callback; + pr_info("hrtimer inited..\n"); +#else + init_timer(&prtd->timer); + prtd->timer.function = &aml_i2s_timer_callback; + prtd->timer.data = (unsigned long)substream; +#endif + } + + prtd->timer_init = 1; return 0; } -#ifdef USE_HW_TIMER -int hw_timer_init; static irqreturn_t audio_isr_handler(int irq, void *data) { struct aml_runtime_data *prtd = data; @@ -365,34 +383,36 @@ static irqreturn_t audio_isr_handler(int irq, void *data) return IRQ_HANDLED; } -static int snd_free_hw_timer_irq(void *data) -{ - free_irq(INT_TIMER_D, data); - - return 0; -} - -static int snd_request_hw_timer(void *data) +static int snd_request_hw_timer(struct aml_runtime_data *prtd) { int ret = 0; - if (hw_timer_init == 0) { - aml_isa_write(ISA_TIMERD, TIMER_COUNT); - aml_isa_update_bits(ISA_TIMER_MUX, 3 << 6, - TIMERD_RESOLUTION << 6); - aml_isa_update_bits(ISA_TIMER_MUX, 1 << 15, TIMERD_MODE << 15); - aml_isa_update_bits(ISA_TIMER_MUX, 1 << 19, 1 << 19); - hw_timer_init = 1; - } - ret = request_irq(INT_TIMER_D, audio_isr_handler, - IRQF_SHARED, "timerd_irq", data); + aml_isa_write(ISA_TIMERD, TIMER_COUNT); + aml_isa_update_bits(ISA_TIMER_MUX, 3 << 6, + TIMERD_RESOLUTION << 6); + aml_isa_update_bits(ISA_TIMER_MUX, 1 << 15, TIMERD_MODE << 15); + aml_isa_update_bits(ISA_TIMER_MUX, 1 << 19, 1 << 19); + + if (timer_irq_num) { + ret = request_irq(timer_irq_num, audio_isr_handler, + IRQF_SHARED, "timerd_irq", prtd); if (ret < 0) { pr_err("audio hw interrupt register fail\n"); return -1; } + } + return 0; +} + +static int snd_free_hw_timer_irq(struct aml_runtime_data *prtd) +{ + if (prtd->timer_init) { + free_irq(timer_irq_num, prtd); + prtd->timer_init = 0; + } + return 0; } -#endif #ifdef USE_HRTIMER static void aml_i2s_hrtimer_set_rate(struct snd_pcm_substream *substream) @@ -459,7 +479,6 @@ static void start_timer(struct aml_runtime_data *prtd) spin_lock_irqsave(&prtd->timer_lock, flags); if (!prtd->active) { -#ifndef USE_HW_TIMER #ifdef USE_HRTIMER hrtimer_start(&prtd->hrtimer, prtd->wakeups_per_second, HRTIMER_MODE_REL); @@ -467,7 +486,6 @@ static void start_timer(struct aml_runtime_data *prtd) prtd->timer.expires = jiffies + 1; add_timer(&prtd->timer); #endif -#endif prtd->active = 1; prtd->xrun_num = 0; } @@ -475,19 +493,46 @@ static void start_timer(struct aml_runtime_data *prtd) } +static void start_hw_timer(struct aml_runtime_data *prtd) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&prtd->timer_lock, flags); + if (!prtd->active) { + aml_isa_update_bits(ISA_TIMER_MUX, 1 << 19, 1 << 19); + prtd->active = 1; + prtd->xrun_num = 0; + } + spin_unlock_irqrestore(&prtd->timer_lock, flags); + +} + + static void stop_timer(struct aml_runtime_data *prtd) { unsigned long flags = 0; spin_lock_irqsave(&prtd->timer_lock, flags); if (prtd->active) { -#ifndef USE_HW_TIMER #ifdef USE_HRTIMER hrtimer_cancel(&prtd->hrtimer); #else del_timer(&prtd->timer); #endif -#endif + prtd->active = 0; + prtd->xrun_num = 0; + } + spin_unlock_irqrestore(&prtd->timer_lock, flags); +} + + +static void stop_hw_timer(struct aml_runtime_data *prtd) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&prtd->timer_lock, flags); + if (prtd->active) { + aml_isa_update_bits(ISA_TIMER_MUX, 1 << 19, 0 << 19); prtd->active = 0; prtd->xrun_num = 0; } @@ -499,6 +544,7 @@ static int aml_i2s_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *rtd = substream->runtime; struct aml_runtime_data *prtd = rtd->private_data; + struct audio_stream *s = &prtd->s; int ret = 0; switch (cmd) { @@ -508,12 +554,18 @@ static int aml_i2s_trigger(struct snd_pcm_substream *substream, int cmd) #ifdef USE_HRTIMER aml_i2s_hrtimer_set_rate(substream); #endif - start_timer(prtd); + if (s->device_type == AML_AUDIO_SPDIFIN) + start_hw_timer(prtd); + else + start_timer(prtd); break; /* SNDRV_PCM_TRIGGER_START */ case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP: - stop_timer(prtd); + if (s->device_type == AML_AUDIO_SPDIFIN) + stop_hw_timer(prtd); + else + stop_timer(prtd); break; default: ret = -EINVAL; @@ -543,8 +595,12 @@ static snd_pcm_uframes_t aml_i2s_pointer(struct snd_pcm_substream *substream) ptr = audio_in_i2s_wr_ptr(); else if (s->device_type == AML_AUDIO_I2SIN2) ptr = audio_in_i2s2_wr_ptr(); - else + else { + if (prtd->xrun_num) + return SNDRV_PCM_POS_XRUN; ptr = audio_in_spdif_wr_ptr(); + } + addr = ptr - s->I2S_addr; #ifdef CONFIG_AMLOGIC_SND_SPLIT_MODE_MMAP return bytes_to_frames(runtime, addr); @@ -620,7 +676,11 @@ static void aml_i2s_timer_callback(unsigned long data) #endif prtd->xrun_num = 0; } else if (last_ptr == s->last_ptr) { - if (prtd->xrun_num++ > XRUN_NUM) { + if (s->device_type == AML_AUDIO_SPDIFIN) { + /* timeout ASAP */ + prtd->xrun_num++; + s->size = runtime->period_size; + } else if (prtd->xrun_num++ > XRUN_NUM) { prtd->xrun_num = 0; s->size = runtime->period_size; } @@ -644,9 +704,8 @@ static void aml_i2s_timer_callback(unsigned long data) } } -#ifndef USE_HW_TIMER - mod_timer(&prtd->timer, jiffies + 1); -#endif + if (s->device_type != AML_AUDIO_SPDIFIN) + mod_timer(&prtd->timer, jiffies + 1); spin_unlock_irqrestore(&prtd->timer_lock, flags); if (elapsed) @@ -704,22 +763,6 @@ static int aml_i2s_open(struct snd_pcm_substream *substream) spin_lock_init(&prtd->timer_lock); -#if defined(USE_HW_TIMER) - ret = snd_request_hw_timer(prtd); - if (ret < 0) { - dev_err(substream->pcm->card->dev, "request audio hw timer failed\n"); - goto out; - } -#elif defined(USE_HRTIMER) - hrtimer_init(&prtd->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - prtd->hrtimer.function = aml_i2s_hrtimer_callback; - pr_info("hrtimer inited..\n"); -#else - init_timer(&prtd->timer); - prtd->timer.function = &aml_i2s_timer_callback; - prtd->timer.data = (unsigned long)substream; -#endif - out: return ret; } @@ -732,10 +775,11 @@ static int aml_i2s_close(struct snd_pcm_substream *substream) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (s->device_type == AML_AUDIO_I2SOUT) aml_i2s_playback_running_flag = 0; + } else { + if (s->device_type == AML_AUDIO_SPDIFIN) + snd_free_hw_timer_irq(prtd); } -#ifdef USE_HW_TIMER - snd_free_hw_timer_irq(prtd); -#endif + kfree(prtd); prtd = NULL; @@ -1361,6 +1405,8 @@ struct snd_soc_platform_driver aml_soc_platform = { static int aml_soc_platform_probe(struct platform_device *pdev) { + timer_irq_num = irq_of_parse_and_map(pdev->dev.of_node, 0); + pr_info("timerD int: %d\n", timer_irq_num); return snd_soc_register_platform(&pdev->dev, &aml_soc_platform); } diff --git a/sound/soc/amlogic/meson/i2s.h b/sound/soc/amlogic/meson/i2s.h index ac8e725..7196228c 100644 --- a/sound/soc/amlogic/meson/i2s.h +++ b/sound/soc/amlogic/meson/i2s.h @@ -34,7 +34,7 @@ /* timerbase resolution: 00: 1us; 01: 10us; 10: 100us; 11: 1ms*/ #define TIMERD_RESOLUTION 0x1 /* timer count: 16bits*/ -#define TIMER_COUNT 100 +#define TIMER_COUNT 300 struct audio_stream { int stream_id; @@ -88,6 +88,7 @@ struct aml_runtime_data { void *buf; /* tmp buffer for playback or capture */ int active; unsigned int xrun_num; + int timer_init; /* hrtimer */ struct hrtimer hrtimer; diff --git a/sound/soc/amlogic/meson/spdif_dai.c b/sound/soc/amlogic/meson/spdif_dai.c index 58990a6..4fe7f6a 100644 --- a/sound/soc/amlogic/meson/spdif_dai.c +++ b/sound/soc/amlogic/meson/spdif_dai.c @@ -169,7 +169,7 @@ static int aml_dai_spdif_trigger(struct snd_pcm_substream *substream, int cmd, pr_info("aiu 958 playback enable\n"); audio_hw_958_enable(1); } else { - pr_info("spdif in capture enable\n"); + //pr_info("spdif in capture enable\n"); audio_in_spdif_enable(1); } break; @@ -180,7 +180,7 @@ static int aml_dai_spdif_trigger(struct snd_pcm_substream *substream, int cmd, pr_info("aiu 958 playback disable\n"); audio_hw_958_enable(0); } else { - pr_info("spdif in capture disable\n"); + //pr_info("spdif in capture disable\n"); audio_in_spdif_enable(0); } break; diff --git a/sound/soc/codecs/amlogic/cs42528.c b/sound/soc/codecs/amlogic/cs42528.c index cdfbf6c..a8c981f 100644 --- a/sound/soc/codecs/amlogic/cs42528.c +++ b/sound/soc/codecs/amlogic/cs42528.c @@ -30,7 +30,7 @@ struct reg_default cs42528_reg_defaults[] = { { 0x04, 0x40 }, /* Interface Formats */ { 0x05, 0x80 }, /* Misc Control */ { 0x06, 0x02 }, /* Clock Control */ - { 0x0D, 0x28 }, /* Volume Control */ + { 0x0D, 0x20 }, /* Volume Control */ { 0x0E, 0xff }, /* Channel Mute */ { 0x0F, 0x00 }, /* Volume Control A1 */ { 0x10, 0x00 }, /* Volume Control B1 */ @@ -60,7 +60,7 @@ static int cs42528_reg_table[CS42528_REG_COUNT][2] = { { 0x04, 0x40 }, /* Interface Formats */ { 0x05, 0x80 }, /* Misc Control */ { 0x06, 0x02 }, /* Clock Control */ - { 0x0D, 0x28 }, /* Volume Control */ + { 0x0D, 0x20 }, /* Volume Control */ { 0x0E, 0xff }, /* Channel Mute */ { 0x0F, 0x00 }, /* Volume Control A1 */ { 0x10, 0x00 }, /* Volume Control B1 */ @@ -96,6 +96,7 @@ struct cs42528_priv { #ifdef CONFIG_HAS_EARLYSUSPEND struct early_suspend early_suspend; #endif + int amp_muted; }; /* -127dB to 0dB with step of 0.5dB */ @@ -385,22 +386,24 @@ static int reset_cs42528(struct snd_soc_codec *codec) return 0; } -static int Enable_Amp(struct snd_soc_codec *codec, int enable) +static int Enable_Amp(struct snd_soc_codec *codec, int mute) { struct cs42528_priv *cs42528 = snd_soc_codec_get_drvdata(codec); struct cs42528_platform_data *pdata = cs42528->pdata; if (pdata->amp_pin > 0) { - if (enable == 1) { + if (mute && !cs42528->amp_muted) { + cs42528->amp_muted = 1; gpio_direction_output(pdata->amp_pin, GPIOF_OUT_INIT_LOW); - pr_info("%s %d set GPIO amp pin low!\n", - __func__, __LINE__); - } else { + //pr_info("%s %d set GPIO amp pin low!\n", + // __func__, __LINE__); + } else if (!mute && cs42528->amp_muted) { + cs42528->amp_muted = 0; gpio_direction_output(pdata->amp_pin, GPIOF_OUT_INIT_HIGH); - pr_info("%s %d set GPIO amp pin high!\n", - __func__, __LINE__); + //pr_info("%s %d set GPIO amp pin high!\n", + // __func__, __LINE__); } } return 0; |