summaryrefslogtreecommitdiff
authorSong 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)
commit0ccf4012679d41bbd5fe15083df28af0e4e189f1 (patch)
tree6ea91965b6bcc6fa0679bde9d27fc732f1fbbe59
parentbd011bb30410c2194da85b12440ff741ea42a697 (diff)
downloadcommon-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
Diffstat
-rw-r--r--sound/soc/amlogic/meson/audio_hw.c2
-rw-r--r--sound/soc/amlogic/meson/i2s.c158
-rw-r--r--sound/soc/amlogic/meson/i2s.h3
-rw-r--r--sound/soc/amlogic/meson/spdif_dai.c4
-rw-r--r--sound/soc/codecs/amlogic/cs42528.c21
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;