| From 4bd8b09e665b585b567e17731a0d1c4f3c61a2a2 Mon Sep 17 00:00:00 2001 |
| From: Akshu Agrawal <akshu.agrawal@amd.corp-partner.google.com> |
| Date: Fri, 20 Nov 2020 22:09:22 +0530 |
| Subject: [PATCH] CHROMIUM: ASoC: AMD: Poll and clear interrupt status |
| |
| It is observed that when simultaneously thershold value is reached |
| for more than 2 streams, then the hw does not fire the interrupt. |
| To work around this we poll for this special condition and when hit |
| clear the interrupt and signal period completion. |
| |
| BUG=b:170595019, b:168281719 |
| TEST=Speaker/DMIC test |
| |
| Signed-off-by: Akshu Agrawal <akshu.agrawal@amd.corp-partner.google.com> |
| Change-Id: Icd70c6d4f0e98d6b330e9812027ea3cb99cec653 |
| Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/2569315 |
| Reviewed-by: Yu-Hsuan Hsu <yuhsuan@chromium.org> |
| Commit-Queue: Yu-Hsuan Hsu <yuhsuan@chromium.org> |
| Tested-by: Yu-Hsuan Hsu <yuhsuan@chromium.org> |
| --- |
| sound/soc/amd/raven/acp3x-pcm-dma.c | 76 +++++++++++++++++++++++++++++ |
| sound/soc/amd/raven/acp3x.h | 2 + |
| 2 files changed, 78 insertions(+) |
| |
| diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c |
| index 8148b0d22e88..8718e01ad608 100644 |
| --- a/sound/soc/amd/raven/acp3x-pcm-dma.c |
| +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c |
| @@ -4,11 +4,14 @@ |
| // |
| //Copyright 2016 Advanced Micro Devices, Inc. |
| |
| +#include <linux/delay.h> |
| #include <linux/platform_device.h> |
| #include <linux/module.h> |
| #include <linux/err.h> |
| #include <linux/io.h> |
| #include <linux/pm_runtime.h> |
| +#include <linux/sysfs.h> |
| +#include <linux/workqueue.h> |
| #include <sound/pcm_params.h> |
| #include <sound/soc.h> |
| #include <sound/soc-dai.h> |
| @@ -70,6 +73,7 @@ static irqreturn_t i2s_irq_handler(int irq, void *dev_id) |
| play_flag = 0; |
| cap_flag = 0; |
| val = rv_readl(rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT); |
| + |
| if ((val & BIT(BT_TX_THRESHOLD)) && rv_i2s_data->play_stream) { |
| rv_writel(BIT(BT_TX_THRESHOLD), rv_i2s_data->acp3x_base + |
| mmACP_EXTERNAL_INTR_STAT); |
| @@ -235,12 +239,70 @@ static int acp3x_dma_open(struct snd_soc_component *component, |
| return ret; |
| } |
| |
| + if (!adata->play_stream && !adata->capture_stream && |
| + !adata->i2ssp_play_stream && !adata->i2ssp_capture_stream) { |
| + rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); |
| + schedule_work(&adata->work); |
| + } |
| + |
| i2s_data->acp3x_base = adata->acp3x_base; |
| runtime->private_data = i2s_data; |
| return ret; |
| } |
| |
| |
| +static void acp_intr_work(struct work_struct *work) |
| +{ |
| + u32 val; |
| + struct i2s_dev_data *adata = |
| + container_of(work, struct i2s_dev_data, work); |
| + |
| + /* sleep and check status bits */ |
| + for (;;) { |
| + val = rv_readl(adata->acp3x_base + mmACP_EXTERNAL_INTR_STAT); |
| + if ((val & BIT(BT_TX_THRESHOLD)) && |
| + (val & BIT(BT_RX_THRESHOLD))) { |
| + rv_writel(BIT(BT_TX_THRESHOLD), adata->acp3x_base + |
| + mmACP_EXTERNAL_INTR_STAT); |
| + snd_pcm_period_elapsed(adata->play_stream); |
| + rv_writel(BIT(BT_RX_THRESHOLD), adata->acp3x_base + |
| + mmACP_EXTERNAL_INTR_STAT); |
| + snd_pcm_period_elapsed(adata->capture_stream); |
| + } |
| + if ((val & BIT(I2S_TX_THRESHOLD)) && |
| + (val & BIT(BT_RX_THRESHOLD))) { |
| + rv_writel(BIT(I2S_TX_THRESHOLD), adata->acp3x_base + |
| + mmACP_EXTERNAL_INTR_STAT); |
| + snd_pcm_period_elapsed(adata->i2ssp_play_stream); |
| + rv_writel(BIT(BT_RX_THRESHOLD), adata->acp3x_base + |
| + mmACP_EXTERNAL_INTR_STAT); |
| + snd_pcm_period_elapsed(adata->capture_stream); |
| + } |
| + if ((val & BIT(BT_TX_THRESHOLD)) && |
| + (val & BIT(I2S_RX_THRESHOLD))) { |
| + rv_writel(BIT(BT_TX_THRESHOLD), adata->acp3x_base + |
| + mmACP_EXTERNAL_INTR_STAT); |
| + snd_pcm_period_elapsed(adata->play_stream); |
| + rv_writel(BIT(I2S_RX_THRESHOLD), adata->acp3x_base + |
| + mmACP_EXTERNAL_INTR_STAT); |
| + snd_pcm_period_elapsed(adata->i2ssp_capture_stream); |
| + } |
| + if ((val & BIT(I2S_TX_THRESHOLD)) && |
| + (val & BIT(I2S_RX_THRESHOLD))) { |
| + rv_writel(BIT(I2S_TX_THRESHOLD), adata->acp3x_base + |
| + mmACP_EXTERNAL_INTR_STAT); |
| + snd_pcm_period_elapsed(adata->i2ssp_play_stream); |
| + rv_writel(BIT(I2S_RX_THRESHOLD), adata->acp3x_base + |
| + mmACP_EXTERNAL_INTR_STAT); |
| + snd_pcm_period_elapsed(adata->i2ssp_capture_stream); |
| + } |
| + val = rv_readl(adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); |
| + if (!val) |
| + break; |
| + usleep_range(adata->sleep_us, adata->sleep_us + 10); |
| + } |
| +} |
| + |
| static int acp3x_dma_hw_params(struct snd_soc_component *component, |
| struct snd_pcm_substream *substream, |
| struct snd_pcm_hw_params *params) |
| @@ -289,6 +351,9 @@ static int acp3x_dma_hw_params(struct snd_soc_component *component, |
| rtd->dma_addr = substream->dma_buffer.addr; |
| rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); |
| config_acp3x_dma(rtd, substream->stream); |
| + adata->sleep_us = 1000000 / params_rate(params) * |
| + params_period_size(params); |
| + |
| return 0; |
| } |
| |
| @@ -361,6 +426,15 @@ static int acp3x_dma_close(struct snd_soc_component *component, |
| } |
| } |
| |
| + /* Disable ACP irq, when the current stream is being closed and |
| + * another stream is also not active. |
| + */ |
| + if (!adata->play_stream && !adata->capture_stream && |
| + !adata->i2ssp_play_stream && !adata->i2ssp_capture_stream) { |
| + rv_writel(0, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); |
| + cancel_work_sync(&adata->work); |
| + } |
| + |
| return 0; |
| } |
| |
| @@ -425,6 +499,8 @@ static int acp3x_audio_probe(struct platform_device *pdev) |
| return -ENODEV; |
| } |
| |
| + INIT_WORK(&adata->work, acp_intr_work); |
| + |
| pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); |
| pm_runtime_use_autosuspend(&pdev->dev); |
| pm_runtime_enable(&pdev->dev); |
| diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h |
| index c3f0c8b7545d..1b7f659cb0c3 100644 |
| --- a/sound/soc/amd/raven/acp3x.h |
| +++ b/sound/soc/amd/raven/acp3x.h |
| @@ -91,11 +91,13 @@ struct i2s_dev_data { |
| u16 i2s_instance; |
| u32 tdm_fmt; |
| u32 substream_type; |
| + u32 sleep_us; |
| void __iomem *acp3x_base; |
| struct snd_pcm_substream *play_stream; |
| struct snd_pcm_substream *capture_stream; |
| struct snd_pcm_substream *i2ssp_play_stream; |
| struct snd_pcm_substream *i2ssp_capture_stream; |
| + struct work_struct work; |
| }; |
| |
| struct i2s_stream_instance { |
| -- |
| 2.17.1 |
| |