blob: 24fbef8abdafe226e3b09e5dd1b31ceda15750c5 [file] [log] [blame]
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