xref: /openbmc/linux/sound/soc/amd/yc/acp6x-pdm-dma.c (revision 7bef0010)
17610174aSVijendar Mukunda // SPDX-License-Identifier: GPL-2.0+
27610174aSVijendar Mukunda /*
37610174aSVijendar Mukunda  * AMD ALSA SoC Yellow Carp PDM Driver
47610174aSVijendar Mukunda  *
57610174aSVijendar Mukunda  * Copyright 2021 Advanced Micro Devices, Inc.
67610174aSVijendar Mukunda  */
77610174aSVijendar Mukunda 
87610174aSVijendar Mukunda #include <linux/platform_device.h>
97610174aSVijendar Mukunda #include <linux/module.h>
107610174aSVijendar Mukunda #include <linux/err.h>
117610174aSVijendar Mukunda #include <linux/io.h>
127610174aSVijendar Mukunda #include <sound/pcm_params.h>
137610174aSVijendar Mukunda #include <sound/soc.h>
147610174aSVijendar Mukunda #include <sound/soc-dai.h>
154c2e711aSVijendar Mukunda #include <linux/pm_runtime.h>
167610174aSVijendar Mukunda 
177610174aSVijendar Mukunda #include "acp6x.h"
187610174aSVijendar Mukunda 
197610174aSVijendar Mukunda #define DRV_NAME "acp_yc_pdm_dma"
207610174aSVijendar Mukunda 
21ceb4fcc1SVijendar Mukunda static const struct snd_pcm_hardware acp6x_pdm_hardware_capture = {
22ceb4fcc1SVijendar Mukunda 	.info = SNDRV_PCM_INFO_INTERLEAVED |
23ceb4fcc1SVijendar Mukunda 		SNDRV_PCM_INFO_BLOCK_TRANSFER |
24ceb4fcc1SVijendar Mukunda 		SNDRV_PCM_INFO_MMAP |
25ceb4fcc1SVijendar Mukunda 		SNDRV_PCM_INFO_MMAP_VALID |
26ceb4fcc1SVijendar Mukunda 		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
27ceb4fcc1SVijendar Mukunda 	.formats = SNDRV_PCM_FMTBIT_S32_LE,
28ceb4fcc1SVijendar Mukunda 	.channels_min = 2,
29ceb4fcc1SVijendar Mukunda 	.channels_max = 2,
30ceb4fcc1SVijendar Mukunda 	.rates = SNDRV_PCM_RATE_48000,
31ceb4fcc1SVijendar Mukunda 	.rate_min = 48000,
32ceb4fcc1SVijendar Mukunda 	.rate_max = 48000,
33ceb4fcc1SVijendar Mukunda 	.buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
34ceb4fcc1SVijendar Mukunda 	.period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
35ceb4fcc1SVijendar Mukunda 	.period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
36ceb4fcc1SVijendar Mukunda 	.periods_min = CAPTURE_MIN_NUM_PERIODS,
37ceb4fcc1SVijendar Mukunda 	.periods_max = CAPTURE_MAX_NUM_PERIODS,
38ceb4fcc1SVijendar Mukunda };
39ceb4fcc1SVijendar Mukunda 
40ceb4fcc1SVijendar Mukunda static void acp6x_init_pdm_ring_buffer(u32 physical_addr, u32 buffer_size,
41ceb4fcc1SVijendar Mukunda 				       u32 watermark_size, void __iomem *acp_base)
42ceb4fcc1SVijendar Mukunda {
43ceb4fcc1SVijendar Mukunda 	acp6x_writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR);
44ceb4fcc1SVijendar Mukunda 	acp6x_writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE);
45ceb4fcc1SVijendar Mukunda 	acp6x_writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
46ceb4fcc1SVijendar Mukunda 	acp6x_writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL);
47ceb4fcc1SVijendar Mukunda }
48ceb4fcc1SVijendar Mukunda 
49ceb4fcc1SVijendar Mukunda static void acp6x_enable_pdm_clock(void __iomem *acp_base)
50ceb4fcc1SVijendar Mukunda {
51ceb4fcc1SVijendar Mukunda 	u32 pdm_clk_enable, pdm_ctrl;
52ceb4fcc1SVijendar Mukunda 
53ceb4fcc1SVijendar Mukunda 	pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
54ceb4fcc1SVijendar Mukunda 	pdm_ctrl = 0x00;
55ceb4fcc1SVijendar Mukunda 
56ceb4fcc1SVijendar Mukunda 	acp6x_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
57ceb4fcc1SVijendar Mukunda 	pdm_ctrl = acp6x_readl(acp_base + ACP_WOV_MISC_CTRL);
58ceb4fcc1SVijendar Mukunda 	pdm_ctrl |= ACP_WOV_MISC_CTRL_MASK;
59ceb4fcc1SVijendar Mukunda 	acp6x_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
60ceb4fcc1SVijendar Mukunda }
61ceb4fcc1SVijendar Mukunda 
62ceb4fcc1SVijendar Mukunda static void acp6x_enable_pdm_interrupts(void __iomem *acp_base)
63ceb4fcc1SVijendar Mukunda {
64ceb4fcc1SVijendar Mukunda 	u32 ext_int_ctrl;
65ceb4fcc1SVijendar Mukunda 
66ceb4fcc1SVijendar Mukunda 	ext_int_ctrl = acp6x_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
67ceb4fcc1SVijendar Mukunda 	ext_int_ctrl |= PDM_DMA_INTR_MASK;
68ceb4fcc1SVijendar Mukunda 	acp6x_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
69ceb4fcc1SVijendar Mukunda }
70ceb4fcc1SVijendar Mukunda 
71ceb4fcc1SVijendar Mukunda static void acp6x_disable_pdm_interrupts(void __iomem *acp_base)
72ceb4fcc1SVijendar Mukunda {
73ceb4fcc1SVijendar Mukunda 	u32 ext_int_ctrl;
74ceb4fcc1SVijendar Mukunda 
75ceb4fcc1SVijendar Mukunda 	ext_int_ctrl = acp6x_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
76ceb4fcc1SVijendar Mukunda 	ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
77ceb4fcc1SVijendar Mukunda 	acp6x_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
78ceb4fcc1SVijendar Mukunda }
79ceb4fcc1SVijendar Mukunda 
80ceb4fcc1SVijendar Mukunda static bool acp6x_check_pdm_dma_status(void __iomem *acp_base)
81ceb4fcc1SVijendar Mukunda {
82ceb4fcc1SVijendar Mukunda 	bool pdm_dma_status;
83ceb4fcc1SVijendar Mukunda 	u32 pdm_enable, pdm_dma_enable;
84ceb4fcc1SVijendar Mukunda 
85ceb4fcc1SVijendar Mukunda 	pdm_dma_status = false;
86ceb4fcc1SVijendar Mukunda 	pdm_enable = acp6x_readl(acp_base + ACP_WOV_PDM_ENABLE);
87ceb4fcc1SVijendar Mukunda 	pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
88ceb4fcc1SVijendar Mukunda 	if ((pdm_enable & ACP_PDM_ENABLE) && (pdm_dma_enable & ACP_PDM_DMA_EN_STATUS))
89ceb4fcc1SVijendar Mukunda 		pdm_dma_status = true;
90ceb4fcc1SVijendar Mukunda 
91ceb4fcc1SVijendar Mukunda 	return pdm_dma_status;
92ceb4fcc1SVijendar Mukunda }
93ceb4fcc1SVijendar Mukunda 
94ceb4fcc1SVijendar Mukunda static int acp6x_start_pdm_dma(void __iomem *acp_base)
95ceb4fcc1SVijendar Mukunda {
96ceb4fcc1SVijendar Mukunda 	u32 pdm_enable;
97ceb4fcc1SVijendar Mukunda 	u32 pdm_dma_enable;
98ceb4fcc1SVijendar Mukunda 	int timeout;
99ceb4fcc1SVijendar Mukunda 
100ceb4fcc1SVijendar Mukunda 	pdm_enable = 0x01;
101ceb4fcc1SVijendar Mukunda 	pdm_dma_enable  = 0x01;
102ceb4fcc1SVijendar Mukunda 
103ceb4fcc1SVijendar Mukunda 	acp6x_enable_pdm_clock(acp_base);
104ceb4fcc1SVijendar Mukunda 	acp6x_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
105ceb4fcc1SVijendar Mukunda 	acp6x_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
106ceb4fcc1SVijendar Mukunda 	timeout = 0;
107ceb4fcc1SVijendar Mukunda 	while (++timeout < ACP_COUNTER) {
108ceb4fcc1SVijendar Mukunda 		pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
109ceb4fcc1SVijendar Mukunda 		if ((pdm_dma_enable & 0x02) == ACP_PDM_DMA_EN_STATUS)
110ceb4fcc1SVijendar Mukunda 			return 0;
111ceb4fcc1SVijendar Mukunda 		udelay(DELAY_US);
112ceb4fcc1SVijendar Mukunda 	}
113ceb4fcc1SVijendar Mukunda 	return -ETIMEDOUT;
114ceb4fcc1SVijendar Mukunda }
115ceb4fcc1SVijendar Mukunda 
116ceb4fcc1SVijendar Mukunda static int acp6x_stop_pdm_dma(void __iomem *acp_base)
117ceb4fcc1SVijendar Mukunda {
118ceb4fcc1SVijendar Mukunda 	u32 pdm_enable, pdm_dma_enable;
119ceb4fcc1SVijendar Mukunda 	int timeout;
120ceb4fcc1SVijendar Mukunda 
121ceb4fcc1SVijendar Mukunda 	pdm_enable = 0x00;
122ceb4fcc1SVijendar Mukunda 	pdm_dma_enable  = 0x00;
123ceb4fcc1SVijendar Mukunda 
124ceb4fcc1SVijendar Mukunda 	pdm_enable = acp6x_readl(acp_base + ACP_WOV_PDM_ENABLE);
125ceb4fcc1SVijendar Mukunda 	pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
126ceb4fcc1SVijendar Mukunda 	if (pdm_dma_enable & 0x01) {
127ceb4fcc1SVijendar Mukunda 		pdm_dma_enable = 0x02;
128ceb4fcc1SVijendar Mukunda 		acp6x_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
129ceb4fcc1SVijendar Mukunda 		timeout = 0;
130ceb4fcc1SVijendar Mukunda 		while (++timeout < ACP_COUNTER) {
131ceb4fcc1SVijendar Mukunda 			pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
132ceb4fcc1SVijendar Mukunda 			if ((pdm_dma_enable & 0x02) == 0x00)
133ceb4fcc1SVijendar Mukunda 				break;
134ceb4fcc1SVijendar Mukunda 			udelay(DELAY_US);
135ceb4fcc1SVijendar Mukunda 		}
136ceb4fcc1SVijendar Mukunda 		if (timeout == ACP_COUNTER)
137ceb4fcc1SVijendar Mukunda 			return -ETIMEDOUT;
138ceb4fcc1SVijendar Mukunda 	}
139ceb4fcc1SVijendar Mukunda 	if (pdm_enable == ACP_PDM_ENABLE) {
140ceb4fcc1SVijendar Mukunda 		pdm_enable = ACP_PDM_DISABLE;
141ceb4fcc1SVijendar Mukunda 		acp6x_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
142ceb4fcc1SVijendar Mukunda 	}
143ceb4fcc1SVijendar Mukunda 	acp6x_writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
144ceb4fcc1SVijendar Mukunda 	return 0;
145ceb4fcc1SVijendar Mukunda }
146ceb4fcc1SVijendar Mukunda 
147ceb4fcc1SVijendar Mukunda static void acp6x_config_dma(struct pdm_stream_instance *rtd, int direction)
148ceb4fcc1SVijendar Mukunda {
149ceb4fcc1SVijendar Mukunda 	u16 page_idx;
150ceb4fcc1SVijendar Mukunda 	u32 low, high, val;
151ceb4fcc1SVijendar Mukunda 	dma_addr_t addr;
152ceb4fcc1SVijendar Mukunda 
153ceb4fcc1SVijendar Mukunda 	addr = rtd->dma_addr;
154ceb4fcc1SVijendar Mukunda 	val = PDM_PTE_OFFSET;
155ceb4fcc1SVijendar Mukunda 
156ceb4fcc1SVijendar Mukunda 	/* Group Enable */
157ceb4fcc1SVijendar Mukunda 	acp6x_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp6x_base +
158ceb4fcc1SVijendar Mukunda 		     ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
159ceb4fcc1SVijendar Mukunda 	acp6x_writel(PAGE_SIZE_4K_ENABLE, rtd->acp6x_base +
160ceb4fcc1SVijendar Mukunda 		     ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
161ceb4fcc1SVijendar Mukunda 	for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
162ceb4fcc1SVijendar Mukunda 		/* Load the low address of page int ACP SRAM through SRBM */
163ceb4fcc1SVijendar Mukunda 		low = lower_32_bits(addr);
164ceb4fcc1SVijendar Mukunda 		high = upper_32_bits(addr);
165ceb4fcc1SVijendar Mukunda 
166ceb4fcc1SVijendar Mukunda 		acp6x_writel(low, rtd->acp6x_base + ACP_SCRATCH_REG_0 + val);
167ceb4fcc1SVijendar Mukunda 		high |= BIT(31);
168ceb4fcc1SVijendar Mukunda 		acp6x_writel(high, rtd->acp6x_base + ACP_SCRATCH_REG_0 + val + 4);
169ceb4fcc1SVijendar Mukunda 		val += 8;
170ceb4fcc1SVijendar Mukunda 		addr += PAGE_SIZE;
171ceb4fcc1SVijendar Mukunda 	}
172ceb4fcc1SVijendar Mukunda }
173ceb4fcc1SVijendar Mukunda 
174ceb4fcc1SVijendar Mukunda static int acp6x_pdm_dma_open(struct snd_soc_component *component,
175ceb4fcc1SVijendar Mukunda 			      struct snd_pcm_substream *substream)
176ceb4fcc1SVijendar Mukunda {
177ceb4fcc1SVijendar Mukunda 	struct snd_pcm_runtime *runtime;
178ceb4fcc1SVijendar Mukunda 	struct pdm_dev_data *adata;
179ceb4fcc1SVijendar Mukunda 	struct pdm_stream_instance *pdm_data;
180ceb4fcc1SVijendar Mukunda 	int ret;
181ceb4fcc1SVijendar Mukunda 
182ceb4fcc1SVijendar Mukunda 	runtime = substream->runtime;
183ceb4fcc1SVijendar Mukunda 	adata = dev_get_drvdata(component->dev);
184ceb4fcc1SVijendar Mukunda 	pdm_data = kzalloc(sizeof(*pdm_data), GFP_KERNEL);
185ceb4fcc1SVijendar Mukunda 	if (!pdm_data)
186ceb4fcc1SVijendar Mukunda 		return -EINVAL;
187ceb4fcc1SVijendar Mukunda 
188ceb4fcc1SVijendar Mukunda 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
189ceb4fcc1SVijendar Mukunda 		runtime->hw = acp6x_pdm_hardware_capture;
190ceb4fcc1SVijendar Mukunda 
191ceb4fcc1SVijendar Mukunda 	ret = snd_pcm_hw_constraint_integer(runtime,
192ceb4fcc1SVijendar Mukunda 					    SNDRV_PCM_HW_PARAM_PERIODS);
193ceb4fcc1SVijendar Mukunda 	if (ret < 0) {
194ceb4fcc1SVijendar Mukunda 		dev_err(component->dev, "set integer constraint failed\n");
195ceb4fcc1SVijendar Mukunda 		kfree(pdm_data);
196ceb4fcc1SVijendar Mukunda 		return ret;
197ceb4fcc1SVijendar Mukunda 	}
198ceb4fcc1SVijendar Mukunda 
199ceb4fcc1SVijendar Mukunda 	acp6x_enable_pdm_interrupts(adata->acp6x_base);
200ceb4fcc1SVijendar Mukunda 
201ceb4fcc1SVijendar Mukunda 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
202ceb4fcc1SVijendar Mukunda 		adata->capture_stream = substream;
203ceb4fcc1SVijendar Mukunda 
204ceb4fcc1SVijendar Mukunda 	pdm_data->acp6x_base = adata->acp6x_base;
205ceb4fcc1SVijendar Mukunda 	runtime->private_data = pdm_data;
206ceb4fcc1SVijendar Mukunda 	return ret;
207ceb4fcc1SVijendar Mukunda }
208ceb4fcc1SVijendar Mukunda 
209ceb4fcc1SVijendar Mukunda static int acp6x_pdm_dma_hw_params(struct snd_soc_component *component,
210ceb4fcc1SVijendar Mukunda 				   struct snd_pcm_substream *substream,
211ceb4fcc1SVijendar Mukunda 				   struct snd_pcm_hw_params *params)
212ceb4fcc1SVijendar Mukunda {
213ceb4fcc1SVijendar Mukunda 	struct pdm_stream_instance *rtd;
214ceb4fcc1SVijendar Mukunda 	size_t size, period_bytes;
215ceb4fcc1SVijendar Mukunda 
216ceb4fcc1SVijendar Mukunda 	rtd = substream->runtime->private_data;
217ceb4fcc1SVijendar Mukunda 	if (!rtd)
218ceb4fcc1SVijendar Mukunda 		return -EINVAL;
219ceb4fcc1SVijendar Mukunda 	size = params_buffer_bytes(params);
220ceb4fcc1SVijendar Mukunda 	period_bytes = params_period_bytes(params);
221ceb4fcc1SVijendar Mukunda 	rtd->dma_addr = substream->runtime->dma_addr;
222ceb4fcc1SVijendar Mukunda 	rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
223ceb4fcc1SVijendar Mukunda 	acp6x_config_dma(rtd, substream->stream);
224ceb4fcc1SVijendar Mukunda 	acp6x_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, size,
225ceb4fcc1SVijendar Mukunda 				   period_bytes, rtd->acp6x_base);
226ceb4fcc1SVijendar Mukunda 	return 0;
227ceb4fcc1SVijendar Mukunda }
228ceb4fcc1SVijendar Mukunda 
229ceb4fcc1SVijendar Mukunda static u64 acp6x_pdm_get_byte_count(struct pdm_stream_instance *rtd,
230ceb4fcc1SVijendar Mukunda 				    int direction)
231ceb4fcc1SVijendar Mukunda {
232ceb4fcc1SVijendar Mukunda 	union acp_pdm_dma_count byte_count;
233ceb4fcc1SVijendar Mukunda 
234ceb4fcc1SVijendar Mukunda 	byte_count.bcount.high =
235ceb4fcc1SVijendar Mukunda 			acp6x_readl(rtd->acp6x_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
236ceb4fcc1SVijendar Mukunda 	byte_count.bcount.low =
237ceb4fcc1SVijendar Mukunda 			acp6x_readl(rtd->acp6x_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
238ceb4fcc1SVijendar Mukunda 	return byte_count.bytescount;
239ceb4fcc1SVijendar Mukunda }
240ceb4fcc1SVijendar Mukunda 
241ceb4fcc1SVijendar Mukunda static snd_pcm_uframes_t acp6x_pdm_dma_pointer(struct snd_soc_component *comp,
242ceb4fcc1SVijendar Mukunda 					       struct snd_pcm_substream *stream)
243ceb4fcc1SVijendar Mukunda {
244ceb4fcc1SVijendar Mukunda 	struct pdm_stream_instance *rtd;
245ceb4fcc1SVijendar Mukunda 	u32 pos, buffersize;
246ceb4fcc1SVijendar Mukunda 	u64 bytescount;
247ceb4fcc1SVijendar Mukunda 
248ceb4fcc1SVijendar Mukunda 	rtd = stream->runtime->private_data;
249ceb4fcc1SVijendar Mukunda 	buffersize = frames_to_bytes(stream->runtime,
250ceb4fcc1SVijendar Mukunda 				     stream->runtime->buffer_size);
251ceb4fcc1SVijendar Mukunda 	bytescount = acp6x_pdm_get_byte_count(rtd, stream->stream);
252ceb4fcc1SVijendar Mukunda 	if (bytescount > rtd->bytescount)
253ceb4fcc1SVijendar Mukunda 		bytescount -= rtd->bytescount;
254ceb4fcc1SVijendar Mukunda 	pos = do_div(bytescount, buffersize);
255ceb4fcc1SVijendar Mukunda 	return bytes_to_frames(stream->runtime, pos);
256ceb4fcc1SVijendar Mukunda }
257ceb4fcc1SVijendar Mukunda 
258ceb4fcc1SVijendar Mukunda static int acp6x_pdm_dma_new(struct snd_soc_component *component,
259ceb4fcc1SVijendar Mukunda 			     struct snd_soc_pcm_runtime *rtd)
260ceb4fcc1SVijendar Mukunda {
261ceb4fcc1SVijendar Mukunda 	struct device *parent = component->dev->parent;
262ceb4fcc1SVijendar Mukunda 
263ceb4fcc1SVijendar Mukunda 	snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
264ceb4fcc1SVijendar Mukunda 				       parent, MIN_BUFFER, MAX_BUFFER);
265ceb4fcc1SVijendar Mukunda 	return 0;
266ceb4fcc1SVijendar Mukunda }
267ceb4fcc1SVijendar Mukunda 
268ceb4fcc1SVijendar Mukunda static int acp6x_pdm_dma_close(struct snd_soc_component *component,
269ceb4fcc1SVijendar Mukunda 			       struct snd_pcm_substream *substream)
270ceb4fcc1SVijendar Mukunda {
271ceb4fcc1SVijendar Mukunda 	struct pdm_dev_data *adata = dev_get_drvdata(component->dev);
272ceb4fcc1SVijendar Mukunda 
273ceb4fcc1SVijendar Mukunda 	acp6x_disable_pdm_interrupts(adata->acp6x_base);
274ceb4fcc1SVijendar Mukunda 	adata->capture_stream = NULL;
275ceb4fcc1SVijendar Mukunda 	return 0;
276ceb4fcc1SVijendar Mukunda }
277ceb4fcc1SVijendar Mukunda 
278ceb4fcc1SVijendar Mukunda static int acp6x_pdm_dai_trigger(struct snd_pcm_substream *substream,
279ceb4fcc1SVijendar Mukunda 				 int cmd, struct snd_soc_dai *dai)
280ceb4fcc1SVijendar Mukunda {
281ceb4fcc1SVijendar Mukunda 	struct pdm_stream_instance *rtd;
282ceb4fcc1SVijendar Mukunda 	int ret;
283ceb4fcc1SVijendar Mukunda 	bool pdm_status;
284ceb4fcc1SVijendar Mukunda 	unsigned int ch_mask;
285ceb4fcc1SVijendar Mukunda 
286ceb4fcc1SVijendar Mukunda 	rtd = substream->runtime->private_data;
287ceb4fcc1SVijendar Mukunda 	ret = 0;
288ceb4fcc1SVijendar Mukunda 	switch (substream->runtime->channels) {
289ceb4fcc1SVijendar Mukunda 	case TWO_CH:
290ceb4fcc1SVijendar Mukunda 		ch_mask = 0x00;
291ceb4fcc1SVijendar Mukunda 		break;
292ceb4fcc1SVijendar Mukunda 	default:
293ceb4fcc1SVijendar Mukunda 		return -EINVAL;
294ceb4fcc1SVijendar Mukunda 	}
295ceb4fcc1SVijendar Mukunda 	switch (cmd) {
296ceb4fcc1SVijendar Mukunda 	case SNDRV_PCM_TRIGGER_START:
297ceb4fcc1SVijendar Mukunda 	case SNDRV_PCM_TRIGGER_RESUME:
298ceb4fcc1SVijendar Mukunda 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
299ceb4fcc1SVijendar Mukunda 		acp6x_writel(ch_mask, rtd->acp6x_base + ACP_WOV_PDM_NO_OF_CHANNELS);
300ceb4fcc1SVijendar Mukunda 		acp6x_writel(PDM_DECIMATION_FACTOR, rtd->acp6x_base +
301ceb4fcc1SVijendar Mukunda 			     ACP_WOV_PDM_DECIMATION_FACTOR);
302ceb4fcc1SVijendar Mukunda 		rtd->bytescount = acp6x_pdm_get_byte_count(rtd, substream->stream);
303ceb4fcc1SVijendar Mukunda 		pdm_status = acp6x_check_pdm_dma_status(rtd->acp6x_base);
304ceb4fcc1SVijendar Mukunda 		if (!pdm_status)
305ceb4fcc1SVijendar Mukunda 			ret = acp6x_start_pdm_dma(rtd->acp6x_base);
306ceb4fcc1SVijendar Mukunda 		break;
307ceb4fcc1SVijendar Mukunda 	case SNDRV_PCM_TRIGGER_STOP:
308ceb4fcc1SVijendar Mukunda 	case SNDRV_PCM_TRIGGER_SUSPEND:
309ceb4fcc1SVijendar Mukunda 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
310ceb4fcc1SVijendar Mukunda 		pdm_status = acp6x_check_pdm_dma_status(rtd->acp6x_base);
311ceb4fcc1SVijendar Mukunda 		if (pdm_status)
312ceb4fcc1SVijendar Mukunda 			ret = acp6x_stop_pdm_dma(rtd->acp6x_base);
313ceb4fcc1SVijendar Mukunda 		break;
314ceb4fcc1SVijendar Mukunda 	default:
315ceb4fcc1SVijendar Mukunda 		ret = -EINVAL;
316ceb4fcc1SVijendar Mukunda 		break;
317ceb4fcc1SVijendar Mukunda 	}
318ceb4fcc1SVijendar Mukunda 	return ret;
319ceb4fcc1SVijendar Mukunda }
320ceb4fcc1SVijendar Mukunda 
321*7bef0010SRikard Falkeborn static const struct snd_soc_dai_ops acp6x_pdm_dai_ops = {
322ceb4fcc1SVijendar Mukunda 	.trigger   = acp6x_pdm_dai_trigger,
323ceb4fcc1SVijendar Mukunda };
324ceb4fcc1SVijendar Mukunda 
3257610174aSVijendar Mukunda static struct snd_soc_dai_driver acp6x_pdm_dai_driver = {
3267610174aSVijendar Mukunda 	.capture = {
3277610174aSVijendar Mukunda 		.rates = SNDRV_PCM_RATE_48000,
3287610174aSVijendar Mukunda 		.formats = SNDRV_PCM_FMTBIT_S32_LE,
3297610174aSVijendar Mukunda 		.channels_min = 2,
3307610174aSVijendar Mukunda 		.channels_max = 2,
3317610174aSVijendar Mukunda 		.rate_min = 48000,
3327610174aSVijendar Mukunda 		.rate_max = 48000,
3337610174aSVijendar Mukunda 	},
334ceb4fcc1SVijendar Mukunda 	.ops = &acp6x_pdm_dai_ops,
3357610174aSVijendar Mukunda };
3367610174aSVijendar Mukunda 
3377610174aSVijendar Mukunda static const struct snd_soc_component_driver acp6x_pdm_component = {
3387610174aSVijendar Mukunda 	.name		= DRV_NAME,
339ceb4fcc1SVijendar Mukunda 	.open		= acp6x_pdm_dma_open,
340ceb4fcc1SVijendar Mukunda 	.close		= acp6x_pdm_dma_close,
341ceb4fcc1SVijendar Mukunda 	.hw_params	= acp6x_pdm_dma_hw_params,
342ceb4fcc1SVijendar Mukunda 	.pointer	= acp6x_pdm_dma_pointer,
343ceb4fcc1SVijendar Mukunda 	.pcm_construct	= acp6x_pdm_dma_new,
3447610174aSVijendar Mukunda };
3457610174aSVijendar Mukunda 
3467610174aSVijendar Mukunda static int acp6x_pdm_audio_probe(struct platform_device *pdev)
3477610174aSVijendar Mukunda {
3487610174aSVijendar Mukunda 	struct resource *res;
3497610174aSVijendar Mukunda 	struct pdm_dev_data *adata;
3507610174aSVijendar Mukunda 	int status;
3517610174aSVijendar Mukunda 
3527610174aSVijendar Mukunda 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
3537610174aSVijendar Mukunda 	if (!res) {
3547610174aSVijendar Mukunda 		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
3557610174aSVijendar Mukunda 		return -ENODEV;
3567610174aSVijendar Mukunda 	}
3577610174aSVijendar Mukunda 
3587610174aSVijendar Mukunda 	adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
3597610174aSVijendar Mukunda 	if (!adata)
3607610174aSVijendar Mukunda 		return -ENOMEM;
3617610174aSVijendar Mukunda 
3627610174aSVijendar Mukunda 	adata->acp6x_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
3637610174aSVijendar Mukunda 	if (!adata->acp6x_base)
3647610174aSVijendar Mukunda 		return -ENOMEM;
3657610174aSVijendar Mukunda 
3667610174aSVijendar Mukunda 	adata->capture_stream = NULL;
3677610174aSVijendar Mukunda 
3687610174aSVijendar Mukunda 	dev_set_drvdata(&pdev->dev, adata);
3697610174aSVijendar Mukunda 	status = devm_snd_soc_register_component(&pdev->dev,
3707610174aSVijendar Mukunda 						 &acp6x_pdm_component,
3717610174aSVijendar Mukunda 						 &acp6x_pdm_dai_driver, 1);
3727610174aSVijendar Mukunda 	if (status) {
3737610174aSVijendar Mukunda 		dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
3747610174aSVijendar Mukunda 
3757610174aSVijendar Mukunda 		return -ENODEV;
3767610174aSVijendar Mukunda 	}
3774c2e711aSVijendar Mukunda 	pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
3784c2e711aSVijendar Mukunda 	pm_runtime_use_autosuspend(&pdev->dev);
3794c2e711aSVijendar Mukunda 	pm_runtime_enable(&pdev->dev);
3804c2e711aSVijendar Mukunda 	pm_runtime_allow(&pdev->dev);
3817610174aSVijendar Mukunda 	return 0;
3827610174aSVijendar Mukunda }
3837610174aSVijendar Mukunda 
3844c2e711aSVijendar Mukunda static int acp6x_pdm_audio_remove(struct platform_device *pdev)
3854c2e711aSVijendar Mukunda {
3864c2e711aSVijendar Mukunda 	pm_runtime_disable(&pdev->dev);
3874c2e711aSVijendar Mukunda 	return 0;
3884c2e711aSVijendar Mukunda }
3894c2e711aSVijendar Mukunda 
3904c2e711aSVijendar Mukunda static int __maybe_unused acp6x_pdm_resume(struct device *dev)
3914c2e711aSVijendar Mukunda {
3924c2e711aSVijendar Mukunda 	struct pdm_dev_data *adata;
3934c2e711aSVijendar Mukunda 	struct snd_pcm_runtime *runtime;
3944c2e711aSVijendar Mukunda 	struct pdm_stream_instance *rtd;
3954c2e711aSVijendar Mukunda 	u32 period_bytes, buffer_len;
3964c2e711aSVijendar Mukunda 
3974c2e711aSVijendar Mukunda 	adata = dev_get_drvdata(dev);
3984c2e711aSVijendar Mukunda 	if (adata->capture_stream && adata->capture_stream->runtime) {
3994c2e711aSVijendar Mukunda 		runtime = adata->capture_stream->runtime;
4004c2e711aSVijendar Mukunda 		rtd = runtime->private_data;
4014c2e711aSVijendar Mukunda 		period_bytes = frames_to_bytes(runtime, runtime->period_size);
4024c2e711aSVijendar Mukunda 		buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
4034c2e711aSVijendar Mukunda 		acp6x_config_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
4044c2e711aSVijendar Mukunda 		acp6x_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, buffer_len,
4054c2e711aSVijendar Mukunda 					   period_bytes, adata->acp6x_base);
4064c2e711aSVijendar Mukunda 	}
4074c2e711aSVijendar Mukunda 	acp6x_enable_pdm_interrupts(adata->acp6x_base);
4084c2e711aSVijendar Mukunda 	return 0;
4094c2e711aSVijendar Mukunda }
4104c2e711aSVijendar Mukunda 
4114c2e711aSVijendar Mukunda static int __maybe_unused acp6x_pdm_suspend(struct device *dev)
4124c2e711aSVijendar Mukunda {
4134c2e711aSVijendar Mukunda 	struct pdm_dev_data *adata;
4144c2e711aSVijendar Mukunda 
4154c2e711aSVijendar Mukunda 	adata = dev_get_drvdata(dev);
4164c2e711aSVijendar Mukunda 	acp6x_disable_pdm_interrupts(adata->acp6x_base);
4174c2e711aSVijendar Mukunda 	return 0;
4184c2e711aSVijendar Mukunda }
4194c2e711aSVijendar Mukunda 
4204c2e711aSVijendar Mukunda static int __maybe_unused acp6x_pdm_runtime_resume(struct device *dev)
4214c2e711aSVijendar Mukunda {
4224c2e711aSVijendar Mukunda 	struct pdm_dev_data *adata;
4234c2e711aSVijendar Mukunda 
4244c2e711aSVijendar Mukunda 	adata = dev_get_drvdata(dev);
4254c2e711aSVijendar Mukunda 	acp6x_enable_pdm_interrupts(adata->acp6x_base);
4264c2e711aSVijendar Mukunda 	return 0;
4274c2e711aSVijendar Mukunda }
4284c2e711aSVijendar Mukunda 
4294c2e711aSVijendar Mukunda static const struct dev_pm_ops acp6x_pdm_pm_ops = {
4304c2e711aSVijendar Mukunda 	SET_RUNTIME_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_runtime_resume, NULL)
4314c2e711aSVijendar Mukunda 	SET_SYSTEM_SLEEP_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_resume)
4324c2e711aSVijendar Mukunda };
4334c2e711aSVijendar Mukunda 
4347610174aSVijendar Mukunda static struct platform_driver acp6x_pdm_dma_driver = {
4357610174aSVijendar Mukunda 	.probe = acp6x_pdm_audio_probe,
4364c2e711aSVijendar Mukunda 	.remove = acp6x_pdm_audio_remove,
4377610174aSVijendar Mukunda 	.driver = {
4387610174aSVijendar Mukunda 		.name = "acp_yc_pdm_dma",
4394c2e711aSVijendar Mukunda 		.pm = &acp6x_pdm_pm_ops,
4407610174aSVijendar Mukunda 	},
4417610174aSVijendar Mukunda };
4427610174aSVijendar Mukunda 
4437610174aSVijendar Mukunda module_platform_driver(acp6x_pdm_dma_driver);
4447610174aSVijendar Mukunda 
4457610174aSVijendar Mukunda MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
4467610174aSVijendar Mukunda MODULE_DESCRIPTION("AMD ACP6x YC PDM Driver");
4477610174aSVijendar Mukunda MODULE_LICENSE("GPL v2");
4487610174aSVijendar Mukunda MODULE_ALIAS("platform:" DRV_NAME);
449