diff mbox series

[08/14] ASoC: amd: add ACP PDM DMA driver dai ops

Message ID 20200505205327.642282-9-alexander.deucher@amd.com (mailing list archive)
State New, archived
Headers show
Series Add Renoir ACP driver | expand

Commit Message

Alex Deucher May 5, 2020, 8:53 p.m. UTC
From: Vijendar Mukunda <Vijendar.Mukunda@amd.com>

This patch adds ACP3x PDM DMA driver DAI operations.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
---
 sound/soc/amd/renoir/acp3x-pdm-dma.c | 147 ++++++++++++++++++++++++++-
 sound/soc/amd/renoir/rn_acp3x.h      |   7 ++
 2 files changed, 152 insertions(+), 2 deletions(-)

Comments

Pierre-Louis Bossart May 5, 2020, 9:55 p.m. UTC | #1
> +static int start_pdm_dma(void __iomem *acp_base)
> +{
> +	u32 pdm_enable;
> +	u32 pdm_dma_enable;
> +	int timeout;
> +
> +	pdm_enable = 0x01;
> +	pdm_dma_enable  = 0x01;
> +
> +	enable_pdm_clock(acp_base);
> +	rn_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
> +	rn_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
> +	pdm_dma_enable = 0x00;
> +	timeout = 0;
> +	while (++timeout < 20000) {
> +		pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
> +		if ((pdm_dma_enable & 0x02) == ACP_PDM_DMA_EN_STATUS)
> +			return 0;
> +		udelay(5);
> +	}

maybe use human-readable defines for timeout and delay?
e.g.

#define TIMEOUT_MS 100
#define DELAY_US 5

> +	return -ETIMEDOUT;
> +}
> +
> +static int stop_pdm_dma(void __iomem *acp_base)
> +{
> +	u32 pdm_enable, pdm_dma_enable, pdm_fifo_flush;
> +	int timeout;
> +
> +	pdm_enable = 0x00;
> +	pdm_dma_enable  = 0x00;
> +	pdm_fifo_flush = 0x00;
> +
> +	pdm_enable = rn_readl(acp_base + ACP_WOV_PDM_ENABLE);
> +	pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
> +	if (pdm_dma_enable & 0x01) {
> +		pdm_dma_enable = 0x02;
> +		rn_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
> +		pdm_dma_enable = 0x00;
> +		timeout = 0;
> +		while (++timeout < 20000) {
> +			pdm_dma_enable = rn_readl(acp_base +
> +						  ACP_WOV_PDM_DMA_ENABLE);
> +			if ((pdm_dma_enable & 0x02) == 0x00)
> +				return 0;
> +			udelay(5);
> +		}
> +		if (timeout == 20000)

if this test needed, it'll be always true, no?

> +			return -ETIMEDOUT;
> +	}
> +	if (pdm_enable == ACP_PDM_ENABLE) {
> +		pdm_enable = ACP_PDM_DISABLE;
> +		rn_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
> +	}
> +	rn_writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
> +	return 0;
> +}
> +

> +static int acp_pdm_dai_hw_params(struct snd_pcm_substream *substream,
> +				 struct snd_pcm_hw_params *params,
> +				 struct snd_soc_dai *dai)
> +{
> +	struct pdm_stream_instance *rtd;
> +	unsigned int ch_mask;
> +
> +	rtd = substream->runtime->private_data;
> +	switch (params_channels(params)) {
> +	case TWO_CH:
> +	default:
> +		ch_mask = 0x00;
> +		break;
> +	}

the switch doesn't appear very useful at the moment?

> +	config_pdm_stream_params(ch_mask, rtd->acp_base);
> +	return 0;
> +}
> +
Vijendar Mukunda May 6, 2020, 5:12 p.m. UTC | #2
> -----Original Message-----
> From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
> Sent: Wednesday, May 6, 2020 3:25 AM
> To: Alex Deucher <alexdeucher@gmail.com>; alsa-devel@alsa-project.org;
> broonie@kernel.org; Mukunda, Vijendar <Vijendar.Mukunda@amd.com>;
> tiwai@suse.de
> Cc: Deucher, Alexander <Alexander.Deucher@amd.com>
> Subject: Re: [PATCH 08/14] ASoC: amd: add ACP PDM DMA driver dai ops
> 
> 
> 
> > +static int start_pdm_dma(void __iomem *acp_base)
> > +{
> > +	u32 pdm_enable;
> > +	u32 pdm_dma_enable;
> > +	int timeout;
> > +
> > +	pdm_enable = 0x01;
> > +	pdm_dma_enable  = 0x01;
> > +
> > +	enable_pdm_clock(acp_base);
> > +	rn_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
> > +	rn_writel(pdm_dma_enable, acp_base +
> ACP_WOV_PDM_DMA_ENABLE);
> > +	pdm_dma_enable = 0x00;
> > +	timeout = 0;
> > +	while (++timeout < 20000) {
> > +		pdm_dma_enable = rn_readl(acp_base +
> ACP_WOV_PDM_DMA_ENABLE);
> > +		if ((pdm_dma_enable & 0x02) ==
> ACP_PDM_DMA_EN_STATUS)
> > +			return 0;
> > +		udelay(5);
> > +	}
> 
> maybe use human-readable defines for timeout and delay?
> e.g.
> 
> #define TIMEOUT_MS 100
> #define DELAY_US 5

Will fix it.
> 
> > +	return -ETIMEDOUT;
> > +}
> > +
> > +static int stop_pdm_dma(void __iomem *acp_base)
> > +{
> > +	u32 pdm_enable, pdm_dma_enable, pdm_fifo_flush;
> > +	int timeout;
> > +
> > +	pdm_enable = 0x00;
> > +	pdm_dma_enable  = 0x00;
> > +	pdm_fifo_flush = 0x00;
> > +
> > +	pdm_enable = rn_readl(acp_base + ACP_WOV_PDM_ENABLE);
> > +	pdm_dma_enable = rn_readl(acp_base +
> ACP_WOV_PDM_DMA_ENABLE);
> > +	if (pdm_dma_enable & 0x01) {
> > +		pdm_dma_enable = 0x02;
> > +		rn_writel(pdm_dma_enable, acp_base +
> ACP_WOV_PDM_DMA_ENABLE);
> > +		pdm_dma_enable = 0x00;
> > +		timeout = 0;
> > +		while (++timeout < 20000) {
> > +			pdm_dma_enable = rn_readl(acp_base +
> > +
> ACP_WOV_PDM_DMA_ENABLE);
> > +			if ((pdm_dma_enable & 0x02) == 0x00)
> > +				return 0;
> > +			udelay(5);
> > +		}
> > +		if (timeout == 20000)
> 
> if this test needed, it'll be always true, no?

Sorry its my bad. It shouldn't return 0 when ACP DMA transfer is stopped.
It should break the while loop then it should continue to do register programming for stopping the PDM decoder and flushing the FIFO..
timeout check only to verify whether is there any timeout occurred for stopping the PDM DMA transfer or not.

Will modify the logic and  share the updated patch.
> 
> > +			return -ETIMEDOUT;
> > +	}
> > +	if (pdm_enable == ACP_PDM_ENABLE) {
> > +		pdm_enable = ACP_PDM_DISABLE;
> > +		rn_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
> > +	}
> > +	rn_writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
> > +	return 0;
> > +}
> > +
> 
> > +static int acp_pdm_dai_hw_params(struct snd_pcm_substream *substream,
> > +				 struct snd_pcm_hw_params *params,
> > +				 struct snd_soc_dai *dai)
> > +{
> > +	struct pdm_stream_instance *rtd;
> > +	unsigned int ch_mask;
> > +
> > +	rtd = substream->runtime->private_data;
> > +	switch (params_channels(params)) {
> > +	case TWO_CH:
> > +	default:
> > +		ch_mask = 0x00;
> > +		break;
> > +	}
> 
> the switch doesn't appear very useful at the moment?

We kept it for future reference to extend support for more than 2 channels.
> 
> > +	config_pdm_stream_params(ch_mask, rtd->acp_base);
> > +	return 0;
> > +}
> > +
diff mbox series

Patch

diff --git a/sound/soc/amd/renoir/acp3x-pdm-dma.c b/sound/soc/amd/renoir/acp3x-pdm-dma.c
index 0b5dc49f42c3..6e8d5b85bce4 100644
--- a/sound/soc/amd/renoir/acp3x-pdm-dma.c
+++ b/sound/soc/amd/renoir/acp3x-pdm-dma.c
@@ -71,6 +71,27 @@  static void init_pdm_ring_buffer(u32 physical_addr,
 	rn_writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL);
 }
 
+static void config_pdm_stream_params(unsigned int ch_mask,
+				     void __iomem *acp_base)
+{
+	rn_writel(ch_mask, acp_base + ACP_WOV_PDM_NO_OF_CHANNELS);
+	rn_writel(PDM_DECIMATION_FACTOR, acp_base +
+		  ACP_WOV_PDM_DECIMATION_FACTOR);
+}
+
+static void enable_pdm_clock(void __iomem *acp_base)
+{
+	u32 pdm_clk_enable, pdm_ctrl;
+
+	pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
+	pdm_ctrl = 0x00;
+
+	rn_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
+	pdm_ctrl = rn_readl(acp_base + ACP_WOV_MISC_CTRL);
+	pdm_ctrl |= ACP_WOV_MISC_CTRL_MASK;
+	rn_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
+}
+
 static void enable_pdm_interrupts(void __iomem *acp_base)
 {
 	u32 ext_int_ctrl;
@@ -89,6 +110,77 @@  static void disable_pdm_interrupts(void __iomem *acp_base)
 	rn_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
 }
 
+static bool check_pdm_dma_status(void __iomem *acp_base)
+{
+	bool pdm_dma_status;
+	u32 pdm_enable, pdm_dma_enable;
+
+	pdm_dma_status = false;
+	pdm_enable = rn_readl(acp_base + ACP_WOV_PDM_ENABLE);
+	pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+	if ((pdm_enable & ACP_PDM_ENABLE) && (pdm_dma_enable &
+	     ACP_PDM_DMA_EN_STATUS))
+		pdm_dma_status = true;
+	return pdm_dma_status;
+}
+
+static int start_pdm_dma(void __iomem *acp_base)
+{
+	u32 pdm_enable;
+	u32 pdm_dma_enable;
+	int timeout;
+
+	pdm_enable = 0x01;
+	pdm_dma_enable  = 0x01;
+
+	enable_pdm_clock(acp_base);
+	rn_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+	rn_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+	pdm_dma_enable = 0x00;
+	timeout = 0;
+	while (++timeout < 20000) {
+		pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+		if ((pdm_dma_enable & 0x02) == ACP_PDM_DMA_EN_STATUS)
+			return 0;
+		udelay(5);
+	}
+	return -ETIMEDOUT;
+}
+
+static int stop_pdm_dma(void __iomem *acp_base)
+{
+	u32 pdm_enable, pdm_dma_enable, pdm_fifo_flush;
+	int timeout;
+
+	pdm_enable = 0x00;
+	pdm_dma_enable  = 0x00;
+	pdm_fifo_flush = 0x00;
+
+	pdm_enable = rn_readl(acp_base + ACP_WOV_PDM_ENABLE);
+	pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+	if (pdm_dma_enable & 0x01) {
+		pdm_dma_enable = 0x02;
+		rn_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+		pdm_dma_enable = 0x00;
+		timeout = 0;
+		while (++timeout < 20000) {
+			pdm_dma_enable = rn_readl(acp_base +
+						  ACP_WOV_PDM_DMA_ENABLE);
+			if ((pdm_dma_enable & 0x02) == 0x00)
+				return 0;
+			udelay(5);
+		}
+		if (timeout == 20000)
+			return -ETIMEDOUT;
+	}
+	if (pdm_enable == ACP_PDM_ENABLE) {
+		pdm_enable = ACP_PDM_DISABLE;
+		rn_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+	}
+	rn_writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
+	return 0;
+}
+
 static void config_acp_dma(struct pdm_stream_instance *rtd, int direction)
 {
 	u16 page_idx;
@@ -234,9 +326,60 @@  static int acp_pdm_dma_close(struct snd_soc_component *component,
 	return 0;
 }
 
+static int acp_pdm_dai_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params,
+				 struct snd_soc_dai *dai)
+{
+	struct pdm_stream_instance *rtd;
+	unsigned int ch_mask;
+
+	rtd = substream->runtime->private_data;
+	switch (params_channels(params)) {
+	case TWO_CH:
+	default:
+		ch_mask = 0x00;
+		break;
+	}
+	config_pdm_stream_params(ch_mask, rtd->acp_base);
+	return 0;
+}
+
+static int acp_pdm_dai_trigger(struct snd_pcm_substream *substream,
+			       int cmd, struct snd_soc_dai *dai)
+{
+	struct pdm_stream_instance *rtd;
+	int ret;
+	bool pdm_status;
+
+	rtd = substream->runtime->private_data;
+	ret = 0;
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		rtd->bytescount = acp_pdm_get_byte_count(rtd,
+							 substream->stream);
+		pdm_status = check_pdm_dma_status(rtd->acp_base);
+		if (!pdm_status)
+			ret = start_pdm_dma(rtd->acp_base);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		pdm_status = check_pdm_dma_status(rtd->acp_base);
+		if (pdm_status)
+			ret = stop_pdm_dma(rtd->acp_base);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
 static struct snd_soc_dai_ops acp_pdm_dai_ops = {
-	.hw_params = NULL,
-	.trigger   = NULL,
+	.hw_params = acp_pdm_dai_hw_params,
+	.trigger   = acp_pdm_dai_trigger,
 };
 
 static struct snd_soc_dai_driver acp_pdm_dai_driver = {
diff --git a/sound/soc/amd/renoir/rn_acp3x.h b/sound/soc/amd/renoir/rn_acp3x.h
index 3536d24374f3..f63e232565af 100644
--- a/sound/soc/amd/renoir/rn_acp3x.h
+++ b/sound/soc/amd/renoir/rn_acp3x.h
@@ -31,6 +31,13 @@ 
 #define PDM_DMA_STAT 0x10
 #define PDM_DMA_INTR_MASK  0x10000
 #define ACP_ERROR_STAT 29
+#define PDM_DECIMATION_FACTOR 0x2
+#define ACP_PDM_CLK_FREQ_MASK 0x07
+#define ACP_WOV_MISC_CTRL_MASK 0x10
+#define ACP_PDM_ENABLE 0x01
+#define ACP_PDM_DISABLE 0x00
+#define ACP_PDM_DMA_EN_STATUS 0x02
+#define TWO_CH 0x02
 
 #define ACP_SRAM_PTE_OFFSET	0x02050000
 #define PAGE_SIZE_4K_ENABLE     0x2