diff mbox series

[07/14] ASoC: amd: add acp3x pdm driver dma ops

Message ID 20200505205327.642282-8-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 PDM driver DMA 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 | 199 +++++++++++++++++++++++++++
 sound/soc/amd/renoir/rn_acp3x.h      |  29 ++++
 2 files changed, 228 insertions(+)

Comments

Pierre-Louis Bossart May 5, 2020, 9:59 p.m. UTC | #1
On 5/5/20 3:53 PM, Alex Deucher wrote:
> From: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> 
> This patch adds PDM driver DMA 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 | 199 +++++++++++++++++++++++++++
>   sound/soc/amd/renoir/rn_acp3x.h      |  29 ++++
>   2 files changed, 228 insertions(+)
> 
> diff --git a/sound/soc/amd/renoir/acp3x-pdm-dma.c b/sound/soc/amd/renoir/acp3x-pdm-dma.c
> index 4ee47a85e37e..0b5dc49f42c3 100644
> --- a/sound/soc/amd/renoir/acp3x-pdm-dma.c
> +++ b/sound/soc/amd/renoir/acp3x-pdm-dma.c
> @@ -16,6 +16,25 @@
>   
>   #define DRV_NAME "acp_rn_pdm_dma"
>   
> +static const struct snd_pcm_hardware acp_pdm_hardware_capture = {
> +	.info = SNDRV_PCM_INFO_INTERLEAVED |
> +		SNDRV_PCM_INFO_BLOCK_TRANSFER |
> +		SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_MMAP |
> +		SNDRV_PCM_INFO_MMAP_VALID |
> +	    SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,

Can you actually resume from the same position? this seems odd when 
combined with INFO_BATCH which means the position is only precise at 
period boundaries.

> +	.formats = SNDRV_PCM_FMTBIT_S32_LE,
> +	.channels_min = 2,
> +	.channels_max = 2,
> +	.rates = SNDRV_PCM_RATE_48000,
> +	.rate_min = 48000,
> +	.rate_max = 48000,
> +	.buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
> +	.period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
> +	.period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
> +	.periods_min = CAPTURE_MIN_NUM_PERIODS,
> +	.periods_max = CAPTURE_MAX_NUM_PERIODS,
> +};
> +

[...]

> +static snd_pcm_uframes_t acp_pdm_dma_pointer(struct snd_soc_component *comp,
> +					     struct snd_pcm_substream *stream)
> +{
> +	struct pdm_stream_instance *rtd;
> +	u32 pos, buffersize;
> +	u64 bytescount;
> +
> +	rtd = stream->runtime->private_data;
> +	pos = 0;
> +	buffersize = 0;
> +	bytescount = 0;

these 3 inits seem unnecessary?
> +
> +	buffersize = frames_to_bytes(stream->runtime,
> +				     stream->runtime->buffer_size);
> +	bytescount = acp_pdm_get_byte_count(rtd, stream->stream);
> +	if (bytescount > rtd->bytescount)
> +		bytescount -= rtd->bytescount;
> +	pos = do_div(bytescount, buffersize);
> +	return bytes_to_frames(stream->runtime, pos);
> +}
> +
Vijendar Mukunda May 6, 2020, 5:30 p.m. UTC | #2
[AMD Official Use Only - Internal Distribution Only]



> -----Original Message-----
> From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
> Sent: Wednesday, May 6, 2020 3:29 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 07/14] ASoC: amd: add acp3x pdm driver dma ops
> 
> 
> 
> On 5/5/20 3:53 PM, Alex Deucher wrote:
> > From: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> >
> > This patch adds PDM driver DMA 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 | 199
> +++++++++++++++++++++++++++
> >   sound/soc/amd/renoir/rn_acp3x.h      |  29 ++++
> >   2 files changed, 228 insertions(+)
> >
> > diff --git a/sound/soc/amd/renoir/acp3x-pdm-dma.c
> b/sound/soc/amd/renoir/acp3x-pdm-dma.c
> > index 4ee47a85e37e..0b5dc49f42c3 100644
> > --- a/sound/soc/amd/renoir/acp3x-pdm-dma.c
> > +++ b/sound/soc/amd/renoir/acp3x-pdm-dma.c
> > @@ -16,6 +16,25 @@
> >
> >   #define DRV_NAME "acp_rn_pdm_dma"
> >
> > +static const struct snd_pcm_hardware acp_pdm_hardware_capture = {
> > +	.info = SNDRV_PCM_INFO_INTERLEAVED |
> > +		SNDRV_PCM_INFO_BLOCK_TRANSFER |
> > +		SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_MMAP |
> > +		SNDRV_PCM_INFO_MMAP_VALID |
> > +	    SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
> 
> Can you actually resume from the same position? this seems odd when
> combined with INFO_BATCH which means the position is only precise at
> period boundaries.
> 
We used similar flag in Raven APU acp dma driver well.
As per my understanding INFO_BATCH is more about providing period granularity when hw_ptr is queried.
But PDM driver DMA pointer callback returns precise hw_ptr when queried.
Correct me, if understanding is wrong.


 

> > +	.formats = SNDRV_PCM_FMTBIT_S32_LE,
> > +	.channels_min = 2,
> > +	.channels_max = 2,
> > +	.rates = SNDRV_PCM_RATE_48000,
> > +	.rate_min = 48000,
> > +	.rate_max = 48000,
> > +	.buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS *
> CAPTURE_MAX_PERIOD_SIZE,
> > +	.period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
> > +	.period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
> > +	.periods_min = CAPTURE_MIN_NUM_PERIODS,
> > +	.periods_max = CAPTURE_MAX_NUM_PERIODS,
> > +};
> > +
> 
> [...]
> 
> > +static snd_pcm_uframes_t acp_pdm_dma_pointer(struct
> snd_soc_component *comp,
> > +					     struct snd_pcm_substream
> *stream)
> > +{
> > +	struct pdm_stream_instance *rtd;
> > +	u32 pos, buffersize;
> > +	u64 bytescount;
> > +
> > +	rtd = stream->runtime->private_data;
> > +	pos = 0;
> > +	buffersize = 0;
> > +	bytescount = 0;
> 
> these 3 inits seem unnecessary?

Will remove it.
> > +
> > +	buffersize = frames_to_bytes(stream->runtime,
> > +				     stream->runtime->buffer_size);
> > +	bytescount = acp_pdm_get_byte_count(rtd, stream->stream);
> > +	if (bytescount > rtd->bytescount)
> > +		bytescount -= rtd->bytescount;
> > +	pos = do_div(bytescount, buffersize);
> > +	return bytes_to_frames(stream->runtime, pos);
> > +}
> > +
Pierre-Louis Bossart May 6, 2020, 6:02 p.m. UTC | #3
>>> +static const struct snd_pcm_hardware acp_pdm_hardware_capture = {
>>> +	.info = SNDRV_PCM_INFO_INTERLEAVED |
>>> +		SNDRV_PCM_INFO_BLOCK_TRANSFER |
>>> +		SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_MMAP |
>>> +		SNDRV_PCM_INFO_MMAP_VALID |
>>> +	    SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
>>
>> Can you actually resume from the same position? this seems odd when
>> combined with INFO_BATCH which means the position is only precise at
>> period boundaries.
>>
> We used similar flag in Raven APU acp dma driver well.

Takashi clarified during the early SOF days that if you cannot resume 
from the same position, then the ALSA core will attempt a prepare and 
restart. So if you don't support a restart from the same point, it's 
fine to remove this flag.

> As per my understanding INFO_BATCH is more about providing period granularity when hw_ptr is queried.
> But PDM driver DMA pointer callback returns precise hw_ptr when queried.
> Correct me, if understanding is wrong.

No, INFO_BATCH means the hw_ptr is only updated with a large 
granularity, possibly as large as a period. if you can support precise 
position you should not use this flag.
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 4ee47a85e37e..0b5dc49f42c3 100644
--- a/sound/soc/amd/renoir/acp3x-pdm-dma.c
+++ b/sound/soc/amd/renoir/acp3x-pdm-dma.c
@@ -16,6 +16,25 @@ 
 
 #define DRV_NAME "acp_rn_pdm_dma"
 
+static const struct snd_pcm_hardware acp_pdm_hardware_capture = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID |
+	    SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+	.formats = SNDRV_PCM_FMTBIT_S32_LE,
+	.channels_min = 2,
+	.channels_max = 2,
+	.rates = SNDRV_PCM_RATE_48000,
+	.rate_min = 48000,
+	.rate_max = 48000,
+	.buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+	.period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+	.period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+	.periods_min = CAPTURE_MIN_NUM_PERIODS,
+	.periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
 static irqreturn_t pdm_irq_handler(int irq, void *dev_id)
 {
 	struct pdm_dev_data *rn_pdm_data;
@@ -41,6 +60,180 @@  static irqreturn_t pdm_irq_handler(int irq, void *dev_id)
 		return IRQ_NONE;
 }
 
+static void init_pdm_ring_buffer(u32 physical_addr,
+				 u32 buffer_size,
+				 u32 watermark_size,
+				 void __iomem *acp_base)
+{
+	rn_writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR);
+	rn_writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE);
+	rn_writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+	rn_writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static void enable_pdm_interrupts(void __iomem *acp_base)
+{
+	u32 ext_int_ctrl;
+
+	ext_int_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+	ext_int_ctrl |= PDM_DMA_INTR_MASK;
+	rn_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static void disable_pdm_interrupts(void __iomem *acp_base)
+{
+	u32 ext_int_ctrl;
+
+	ext_int_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+	ext_int_ctrl |= ~PDM_DMA_INTR_MASK;
+	rn_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static void config_acp_dma(struct pdm_stream_instance *rtd, int direction)
+{
+	u16 page_idx;
+	u32 low, high, val;
+	dma_addr_t addr;
+
+	addr = rtd->dma_addr;
+	val = 0;
+
+	/* Group Enable */
+	rn_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp_base +
+		  ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+	rn_writel(PAGE_SIZE_4K_ENABLE, rtd->acp_base +
+		  ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+
+	for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
+		/* Load the low address of page int ACP SRAM through SRBM */
+		low = lower_32_bits(addr);
+		high = upper_32_bits(addr);
+
+		rn_writel(low, rtd->acp_base + ACP_SCRATCH_REG_0 + val);
+		high |= BIT(31);
+		rn_writel(high, rtd->acp_base + ACP_SCRATCH_REG_0 + val + 4);
+		val += 8;
+		addr += PAGE_SIZE;
+	}
+}
+
+static int acp_pdm_dma_open(struct snd_soc_component *component,
+			    struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime;
+	struct pdm_dev_data *adata;
+	struct pdm_stream_instance *pdm_data;
+	int ret;
+
+	runtime = substream->runtime;
+	adata = dev_get_drvdata(component->dev);
+	pdm_data = kzalloc(sizeof(*pdm_data), GFP_KERNEL);
+	if (!pdm_data)
+		return -EINVAL;
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		runtime->hw = acp_pdm_hardware_capture;
+
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0) {
+		dev_err(component->dev, "set integer constraint failed\n");
+		kfree(pdm_data);
+		return ret;
+	}
+
+	enable_pdm_interrupts(adata->acp_base);
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		adata->capture_stream = substream;
+
+	pdm_data->acp_base = adata->acp_base;
+	runtime->private_data = pdm_data;
+	return ret;
+}
+
+static int acp_pdm_dma_hw_params(struct snd_soc_component *component,
+				 struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params)
+{
+	struct pdm_stream_instance *rtd;
+	size_t size, period_bytes;
+
+	rtd = substream->runtime->private_data;
+	if (!rtd)
+		return -EINVAL;
+	size = params_buffer_bytes(params);
+	period_bytes = params_period_bytes(params);
+	rtd->dma_addr = substream->dma_buffer.addr;
+	rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+	config_acp_dma(rtd, substream->stream);
+	init_pdm_ring_buffer(MEM_WINDOW_START, size, period_bytes,
+			     rtd->acp_base);
+	return 0;
+}
+
+static u64 acp_pdm_get_byte_count(struct pdm_stream_instance *rtd,
+				  int direction)
+{
+	union acp_pdm_dma_count byte_count;
+
+	byte_count.bcount.high =
+			rn_readl(rtd->acp_base +
+				 ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
+	byte_count.bcount.low =
+			rn_readl(rtd->acp_base +
+				 ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
+	return byte_count.bytescount;
+}
+
+static snd_pcm_uframes_t acp_pdm_dma_pointer(struct snd_soc_component *comp,
+					     struct snd_pcm_substream *stream)
+{
+	struct pdm_stream_instance *rtd;
+	u32 pos, buffersize;
+	u64 bytescount;
+
+	rtd = stream->runtime->private_data;
+	pos = 0;
+	buffersize = 0;
+	bytescount = 0;
+
+	buffersize = frames_to_bytes(stream->runtime,
+				     stream->runtime->buffer_size);
+	bytescount = acp_pdm_get_byte_count(rtd, stream->stream);
+	if (bytescount > rtd->bytescount)
+		bytescount -= rtd->bytescount;
+	pos = do_div(bytescount, buffersize);
+	return bytes_to_frames(stream->runtime, pos);
+}
+
+static int acp_pdm_dma_new(struct snd_soc_component *component,
+			   struct snd_soc_pcm_runtime *rtd)
+{
+	struct device *parent = component->dev->parent;
+
+	snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+				       parent, MIN_BUFFER, MAX_BUFFER);
+	return 0;
+}
+
+static int acp_pdm_dma_mmap(struct snd_soc_component *component,
+			    struct snd_pcm_substream *substream,
+			    struct vm_area_struct *vma)
+{
+	return snd_pcm_lib_default_mmap(substream, vma);
+}
+
+static int acp_pdm_dma_close(struct snd_soc_component *component,
+			     struct snd_pcm_substream *substream)
+{
+	struct pdm_dev_data *adata = dev_get_drvdata(component->dev);
+
+	disable_pdm_interrupts(adata->acp_base);
+	adata->capture_stream = NULL;
+	return 0;
+}
+
 static struct snd_soc_dai_ops acp_pdm_dai_ops = {
 	.hw_params = NULL,
 	.trigger   = NULL,
@@ -61,6 +254,12 @@  static struct snd_soc_dai_driver acp_pdm_dai_driver = {
 
 static const struct snd_soc_component_driver acp_pdm_component = {
 	.name		= DRV_NAME,
+	.open		= acp_pdm_dma_open,
+	.close		= acp_pdm_dma_close,
+	.hw_params	= acp_pdm_dma_hw_params,
+	.pointer	= acp_pdm_dma_pointer,
+	.mmap		= acp_pdm_dma_mmap,
+	.pcm_construct	= acp_pdm_dma_new,
 };
 
 static int acp_pdm_audio_probe(struct platform_device *pdev)
diff --git a/sound/soc/amd/renoir/rn_acp3x.h b/sound/soc/amd/renoir/rn_acp3x.h
index 1ad8a7845fda..3536d24374f3 100644
--- a/sound/soc/amd/renoir/rn_acp3x.h
+++ b/sound/soc/amd/renoir/rn_acp3x.h
@@ -29,13 +29,42 @@ 
 #define ACP_ERROR_MASK 0x20000000
 #define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
 #define PDM_DMA_STAT 0x10
+#define PDM_DMA_INTR_MASK  0x10000
+#define ACP_ERROR_STAT 29
 
+#define ACP_SRAM_PTE_OFFSET	0x02050000
+#define PAGE_SIZE_4K_ENABLE     0x2
+#define MEM_WINDOW_START	0x4000000
+
+#define CAPTURE_MIN_NUM_PERIODS     4
+#define CAPTURE_MAX_NUM_PERIODS     4
+#define CAPTURE_MAX_PERIOD_SIZE     8192
+#define CAPTURE_MIN_PERIOD_SIZE     4096
+
+#define MAX_BUFFER (CAPTURE_MAX_PERIOD_SIZE * CAPTURE_MAX_NUM_PERIODS)
+#define MIN_BUFFER MAX_BUFFER
 struct pdm_dev_data {
 	u32 pdm_irq;
 	void __iomem *acp_base;
 	struct snd_pcm_substream *capture_stream;
 };
 
+struct pdm_stream_instance {
+	u16 num_pages;
+	u16 channels;
+	dma_addr_t dma_addr;
+	u64 bytescount;
+	void __iomem *acp_base;
+};
+
+union acp_pdm_dma_count {
+	struct {
+	u32 low;
+	u32 high;
+	} bcount;
+	u64 bytescount;
+};
+
 static inline u32 rn_readl(void __iomem *base_addr)
 {
 	return readl(base_addr - ACP_PHY_BASE_ADDRESS);