xref: /linux/sound/soc/amd/vangogh/acp5x-pcm-dma.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
177f61444SVijendar Mukunda // SPDX-License-Identifier: GPL-2.0+
277f61444SVijendar Mukunda //
377f61444SVijendar Mukunda // AMD ALSA SoC PCM Driver
477f61444SVijendar Mukunda //
577f61444SVijendar Mukunda // Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
677f61444SVijendar Mukunda 
777f61444SVijendar Mukunda #include <linux/platform_device.h>
877f61444SVijendar Mukunda #include <linux/module.h>
977f61444SVijendar Mukunda #include <linux/err.h>
1077f61444SVijendar Mukunda #include <linux/io.h>
11361414dcSVijendar Mukunda #include <linux/pm_runtime.h>
1277f61444SVijendar Mukunda #include <sound/pcm.h>
1377f61444SVijendar Mukunda #include <sound/pcm_params.h>
1477f61444SVijendar Mukunda #include <sound/soc.h>
1577f61444SVijendar Mukunda #include <sound/soc-dai.h>
1677f61444SVijendar Mukunda 
1777f61444SVijendar Mukunda #include "acp5x.h"
1877f61444SVijendar Mukunda 
1977f61444SVijendar Mukunda #define DRV_NAME "acp5x_i2s_dma"
2077f61444SVijendar Mukunda 
21cab396d8SVijendar Mukunda static const struct snd_pcm_hardware acp5x_pcm_hardware_playback = {
22cab396d8SVijendar Mukunda 	.info = SNDRV_PCM_INFO_INTERLEAVED |
23cab396d8SVijendar Mukunda 		SNDRV_PCM_INFO_BLOCK_TRANSFER |
24cab396d8SVijendar Mukunda 		SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
25cab396d8SVijendar Mukunda 		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
26cab396d8SVijendar Mukunda 	.formats = SNDRV_PCM_FMTBIT_S16_LE |  SNDRV_PCM_FMTBIT_S8 |
27cab396d8SVijendar Mukunda 		   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
28cab396d8SVijendar Mukunda 	.channels_min = 2,
29cab396d8SVijendar Mukunda 	.channels_max = 2,
30cab396d8SVijendar Mukunda 	.rates = SNDRV_PCM_RATE_8000_96000,
31cab396d8SVijendar Mukunda 	.rate_min = 8000,
32cab396d8SVijendar Mukunda 	.rate_max = 96000,
33cab396d8SVijendar Mukunda 	.buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
34cab396d8SVijendar Mukunda 	.period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
35cab396d8SVijendar Mukunda 	.period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
36cab396d8SVijendar Mukunda 	.periods_min = PLAYBACK_MIN_NUM_PERIODS,
37cab396d8SVijendar Mukunda 	.periods_max = PLAYBACK_MAX_NUM_PERIODS,
38cab396d8SVijendar Mukunda };
39cab396d8SVijendar Mukunda 
40cab396d8SVijendar Mukunda static const struct snd_pcm_hardware acp5x_pcm_hardware_capture = {
41cab396d8SVijendar Mukunda 	.info = SNDRV_PCM_INFO_INTERLEAVED |
42cab396d8SVijendar Mukunda 		SNDRV_PCM_INFO_BLOCK_TRANSFER |
43cab396d8SVijendar Mukunda 		SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
44cab396d8SVijendar Mukunda 		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
45cab396d8SVijendar Mukunda 	.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
46cab396d8SVijendar Mukunda 		   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
47cab396d8SVijendar Mukunda 	.channels_min = 2,
48cab396d8SVijendar Mukunda 	.channels_max = 2,
49cab396d8SVijendar Mukunda 	.rates = SNDRV_PCM_RATE_8000_96000,
50cab396d8SVijendar Mukunda 	.rate_min = 8000,
51cab396d8SVijendar Mukunda 	.rate_max = 96000,
52cab396d8SVijendar Mukunda 	.buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
53cab396d8SVijendar Mukunda 	.period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
54cab396d8SVijendar Mukunda 	.period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
55cab396d8SVijendar Mukunda 	.periods_min = CAPTURE_MIN_NUM_PERIODS,
56cab396d8SVijendar Mukunda 	.periods_max = CAPTURE_MAX_NUM_PERIODS,
5777f61444SVijendar Mukunda };
5877f61444SVijendar Mukunda 
i2s_irq_handler(int irq,void * dev_id)59fc2c8067SVijendar Mukunda static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
60fc2c8067SVijendar Mukunda {
61fc2c8067SVijendar Mukunda 	struct i2s_dev_data *vg_i2s_data;
62fc2c8067SVijendar Mukunda 	u16 irq_flag;
63fc2c8067SVijendar Mukunda 	u32 val;
64fc2c8067SVijendar Mukunda 
65fc2c8067SVijendar Mukunda 	vg_i2s_data = dev_id;
66fc2c8067SVijendar Mukunda 	if (!vg_i2s_data)
67fc2c8067SVijendar Mukunda 		return IRQ_NONE;
68fc2c8067SVijendar Mukunda 
69fc2c8067SVijendar Mukunda 	irq_flag = 0;
70fc2c8067SVijendar Mukunda 	val = acp_readl(vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);
71fc2c8067SVijendar Mukunda 	if ((val & BIT(HS_TX_THRESHOLD)) && vg_i2s_data->play_stream) {
72fc2c8067SVijendar Mukunda 		acp_writel(BIT(HS_TX_THRESHOLD), vg_i2s_data->acp5x_base +
73fc2c8067SVijendar Mukunda 			   ACP_EXTERNAL_INTR_STAT);
74fc2c8067SVijendar Mukunda 		snd_pcm_period_elapsed(vg_i2s_data->play_stream);
75fc2c8067SVijendar Mukunda 		irq_flag = 1;
76fc2c8067SVijendar Mukunda 	}
77fc2c8067SVijendar Mukunda 	if ((val & BIT(I2S_TX_THRESHOLD)) && vg_i2s_data->i2ssp_play_stream) {
78fc2c8067SVijendar Mukunda 		acp_writel(BIT(I2S_TX_THRESHOLD),
79fc2c8067SVijendar Mukunda 			   vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);
80fc2c8067SVijendar Mukunda 		snd_pcm_period_elapsed(vg_i2s_data->i2ssp_play_stream);
81fc2c8067SVijendar Mukunda 		irq_flag = 1;
82fc2c8067SVijendar Mukunda 	}
83fc2c8067SVijendar Mukunda 
84fc2c8067SVijendar Mukunda 	if ((val & BIT(HS_RX_THRESHOLD)) && vg_i2s_data->capture_stream) {
85fc2c8067SVijendar Mukunda 		acp_writel(BIT(HS_RX_THRESHOLD), vg_i2s_data->acp5x_base +
86fc2c8067SVijendar Mukunda 			   ACP_EXTERNAL_INTR_STAT);
87fc2c8067SVijendar Mukunda 		snd_pcm_period_elapsed(vg_i2s_data->capture_stream);
88fc2c8067SVijendar Mukunda 		irq_flag = 1;
89fc2c8067SVijendar Mukunda 	}
90fc2c8067SVijendar Mukunda 	if ((val & BIT(I2S_RX_THRESHOLD)) && vg_i2s_data->i2ssp_capture_stream) {
91fc2c8067SVijendar Mukunda 		acp_writel(BIT(I2S_RX_THRESHOLD),
92fc2c8067SVijendar Mukunda 			   vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);
93fc2c8067SVijendar Mukunda 		snd_pcm_period_elapsed(vg_i2s_data->i2ssp_capture_stream);
94fc2c8067SVijendar Mukunda 		irq_flag = 1;
95fc2c8067SVijendar Mukunda 	}
96fc2c8067SVijendar Mukunda 
97fc2c8067SVijendar Mukunda 	if (irq_flag)
98fc2c8067SVijendar Mukunda 		return IRQ_HANDLED;
99fc2c8067SVijendar Mukunda 	else
100fc2c8067SVijendar Mukunda 		return IRQ_NONE;
101fc2c8067SVijendar Mukunda }
102fc2c8067SVijendar Mukunda 
config_acp5x_dma(struct i2s_stream_instance * rtd,int direction)103cab396d8SVijendar Mukunda static void config_acp5x_dma(struct i2s_stream_instance *rtd, int direction)
104cab396d8SVijendar Mukunda {
105cab396d8SVijendar Mukunda 	u16 page_idx;
106cab396d8SVijendar Mukunda 	u32 low, high, val, acp_fifo_addr, reg_fifo_addr;
107cab396d8SVijendar Mukunda 	u32 reg_dma_size, reg_fifo_size;
108cab396d8SVijendar Mukunda 	dma_addr_t addr;
109cab396d8SVijendar Mukunda 
110cab396d8SVijendar Mukunda 	addr = rtd->dma_addr;
111cab396d8SVijendar Mukunda 	if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
112cab396d8SVijendar Mukunda 		switch (rtd->i2s_instance) {
113cab396d8SVijendar Mukunda 		case I2S_HS_INSTANCE:
114cab396d8SVijendar Mukunda 			val = ACP_SRAM_HS_PB_PTE_OFFSET;
115cab396d8SVijendar Mukunda 			break;
116cab396d8SVijendar Mukunda 		case I2S_SP_INSTANCE:
117cab396d8SVijendar Mukunda 		default:
118cab396d8SVijendar Mukunda 			val = ACP_SRAM_SP_PB_PTE_OFFSET;
119cab396d8SVijendar Mukunda 		}
120cab396d8SVijendar Mukunda 	} else {
121cab396d8SVijendar Mukunda 		switch (rtd->i2s_instance) {
122cab396d8SVijendar Mukunda 		case I2S_HS_INSTANCE:
123cab396d8SVijendar Mukunda 			val = ACP_SRAM_HS_CP_PTE_OFFSET;
124cab396d8SVijendar Mukunda 			break;
125cab396d8SVijendar Mukunda 		case I2S_SP_INSTANCE:
126cab396d8SVijendar Mukunda 		default:
127cab396d8SVijendar Mukunda 			val = ACP_SRAM_SP_CP_PTE_OFFSET;
128cab396d8SVijendar Mukunda 		}
129cab396d8SVijendar Mukunda 	}
130cab396d8SVijendar Mukunda 	/* Group Enable */
131cab396d8SVijendar Mukunda 	acp_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp5x_base +
132cab396d8SVijendar Mukunda 		   ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
133cab396d8SVijendar Mukunda 	acp_writel(PAGE_SIZE_4K_ENABLE, rtd->acp5x_base +
134cab396d8SVijendar Mukunda 		   ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
135cab396d8SVijendar Mukunda 
136cab396d8SVijendar Mukunda 	for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
137cab396d8SVijendar Mukunda 		/* Load the low address of page int ACP SRAM through SRBM */
138cab396d8SVijendar Mukunda 		low = lower_32_bits(addr);
139cab396d8SVijendar Mukunda 		high = upper_32_bits(addr);
140cab396d8SVijendar Mukunda 
141cab396d8SVijendar Mukunda 		acp_writel(low, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val);
142cab396d8SVijendar Mukunda 		high |= BIT(31);
143cab396d8SVijendar Mukunda 		acp_writel(high, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val + 4);
144cab396d8SVijendar Mukunda 		/* Move to next physically contiguous page */
145cab396d8SVijendar Mukunda 		val += 8;
146cab396d8SVijendar Mukunda 		addr += PAGE_SIZE;
147cab396d8SVijendar Mukunda 	}
148cab396d8SVijendar Mukunda 
149cab396d8SVijendar Mukunda 	if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
150cab396d8SVijendar Mukunda 		switch (rtd->i2s_instance) {
151cab396d8SVijendar Mukunda 		case I2S_HS_INSTANCE:
152cab396d8SVijendar Mukunda 			reg_dma_size = ACP_HS_TX_DMA_SIZE;
153cab396d8SVijendar Mukunda 			acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
154cab396d8SVijendar Mukunda 					HS_PB_FIFO_ADDR_OFFSET;
155cab396d8SVijendar Mukunda 			reg_fifo_addr = ACP_HS_TX_FIFOADDR;
156cab396d8SVijendar Mukunda 			reg_fifo_size = ACP_HS_TX_FIFOSIZE;
157cab396d8SVijendar Mukunda 			acp_writel(I2S_HS_TX_MEM_WINDOW_START,
158cab396d8SVijendar Mukunda 				   rtd->acp5x_base + ACP_HS_TX_RINGBUFADDR);
159cab396d8SVijendar Mukunda 			break;
160cab396d8SVijendar Mukunda 
161cab396d8SVijendar Mukunda 		case I2S_SP_INSTANCE:
162cab396d8SVijendar Mukunda 		default:
163cab396d8SVijendar Mukunda 			reg_dma_size = ACP_I2S_TX_DMA_SIZE;
164cab396d8SVijendar Mukunda 			acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
165cab396d8SVijendar Mukunda 					SP_PB_FIFO_ADDR_OFFSET;
166cab396d8SVijendar Mukunda 			reg_fifo_addr =	ACP_I2S_TX_FIFOADDR;
167cab396d8SVijendar Mukunda 			reg_fifo_size = ACP_I2S_TX_FIFOSIZE;
168cab396d8SVijendar Mukunda 			acp_writel(I2S_SP_TX_MEM_WINDOW_START,
169cab396d8SVijendar Mukunda 				   rtd->acp5x_base + ACP_I2S_TX_RINGBUFADDR);
170cab396d8SVijendar Mukunda 		}
171cab396d8SVijendar Mukunda 	} else {
172cab396d8SVijendar Mukunda 		switch (rtd->i2s_instance) {
173cab396d8SVijendar Mukunda 		case I2S_HS_INSTANCE:
174cab396d8SVijendar Mukunda 			reg_dma_size = ACP_HS_RX_DMA_SIZE;
175cab396d8SVijendar Mukunda 			acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
176cab396d8SVijendar Mukunda 					HS_CAPT_FIFO_ADDR_OFFSET;
177cab396d8SVijendar Mukunda 			reg_fifo_addr = ACP_HS_RX_FIFOADDR;
178cab396d8SVijendar Mukunda 			reg_fifo_size = ACP_HS_RX_FIFOSIZE;
179cab396d8SVijendar Mukunda 			acp_writel(I2S_HS_RX_MEM_WINDOW_START,
180cab396d8SVijendar Mukunda 				   rtd->acp5x_base + ACP_HS_RX_RINGBUFADDR);
181cab396d8SVijendar Mukunda 			break;
182cab396d8SVijendar Mukunda 
183cab396d8SVijendar Mukunda 		case I2S_SP_INSTANCE:
184cab396d8SVijendar Mukunda 		default:
185cab396d8SVijendar Mukunda 			reg_dma_size = ACP_I2S_RX_DMA_SIZE;
186cab396d8SVijendar Mukunda 			acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
187cab396d8SVijendar Mukunda 					SP_CAPT_FIFO_ADDR_OFFSET;
188cab396d8SVijendar Mukunda 			reg_fifo_addr = ACP_I2S_RX_FIFOADDR;
189cab396d8SVijendar Mukunda 			reg_fifo_size = ACP_I2S_RX_FIFOSIZE;
190cab396d8SVijendar Mukunda 			acp_writel(I2S_SP_RX_MEM_WINDOW_START,
191cab396d8SVijendar Mukunda 				   rtd->acp5x_base + ACP_I2S_RX_RINGBUFADDR);
192cab396d8SVijendar Mukunda 		}
193cab396d8SVijendar Mukunda 	}
194cab396d8SVijendar Mukunda 	acp_writel(DMA_SIZE, rtd->acp5x_base + reg_dma_size);
195cab396d8SVijendar Mukunda 	acp_writel(acp_fifo_addr, rtd->acp5x_base + reg_fifo_addr);
196cab396d8SVijendar Mukunda 	acp_writel(FIFO_SIZE, rtd->acp5x_base + reg_fifo_size);
197cab396d8SVijendar Mukunda 	acp_writel(BIT(I2S_RX_THRESHOLD) | BIT(HS_RX_THRESHOLD)
198cab396d8SVijendar Mukunda 		   | BIT(I2S_TX_THRESHOLD) | BIT(HS_TX_THRESHOLD),
199cab396d8SVijendar Mukunda 		   rtd->acp5x_base + ACP_EXTERNAL_INTR_CNTL);
200cab396d8SVijendar Mukunda }
201cab396d8SVijendar Mukunda 
acp5x_dma_open(struct snd_soc_component * component,struct snd_pcm_substream * substream)202cab396d8SVijendar Mukunda static int acp5x_dma_open(struct snd_soc_component *component,
203cab396d8SVijendar Mukunda 			  struct snd_pcm_substream *substream)
204cab396d8SVijendar Mukunda {
205cab396d8SVijendar Mukunda 	struct snd_pcm_runtime *runtime;
206cab396d8SVijendar Mukunda 	struct snd_soc_pcm_runtime *prtd;
207cab396d8SVijendar Mukunda 	struct i2s_dev_data *adata;
208cab396d8SVijendar Mukunda 	struct i2s_stream_instance *i2s_data;
209cab396d8SVijendar Mukunda 	int ret;
210cab396d8SVijendar Mukunda 
211cab396d8SVijendar Mukunda 	runtime = substream->runtime;
212d4f23dcdSKuninori Morimoto 	prtd = snd_soc_substream_to_rtd(substream);
213cab396d8SVijendar Mukunda 	component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
214cab396d8SVijendar Mukunda 	adata = dev_get_drvdata(component->dev);
215cab396d8SVijendar Mukunda 
216cab396d8SVijendar Mukunda 	i2s_data = kzalloc(sizeof(*i2s_data), GFP_KERNEL);
217cab396d8SVijendar Mukunda 	if (!i2s_data)
218cab396d8SVijendar Mukunda 		return -ENOMEM;
219cab396d8SVijendar Mukunda 
220cab396d8SVijendar Mukunda 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
221cab396d8SVijendar Mukunda 		runtime->hw = acp5x_pcm_hardware_playback;
222cab396d8SVijendar Mukunda 	else
223cab396d8SVijendar Mukunda 		runtime->hw = acp5x_pcm_hardware_capture;
224cab396d8SVijendar Mukunda 
225cab396d8SVijendar Mukunda 	ret = snd_pcm_hw_constraint_integer(runtime,
226cab396d8SVijendar Mukunda 					    SNDRV_PCM_HW_PARAM_PERIODS);
227cab396d8SVijendar Mukunda 	if (ret < 0) {
228cab396d8SVijendar Mukunda 		dev_err(component->dev, "set integer constraint failed\n");
229cab396d8SVijendar Mukunda 		kfree(i2s_data);
230cab396d8SVijendar Mukunda 		return ret;
231cab396d8SVijendar Mukunda 	}
232cab396d8SVijendar Mukunda 	i2s_data->acp5x_base = adata->acp5x_base;
233cab396d8SVijendar Mukunda 	runtime->private_data = i2s_data;
234cab396d8SVijendar Mukunda 	return ret;
235cab396d8SVijendar Mukunda }
236cab396d8SVijendar Mukunda 
acp5x_dma_hw_params(struct snd_soc_component * component,struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)237cab396d8SVijendar Mukunda static int acp5x_dma_hw_params(struct snd_soc_component *component,
238cab396d8SVijendar Mukunda 			       struct snd_pcm_substream *substream,
239cab396d8SVijendar Mukunda 			       struct snd_pcm_hw_params *params)
240cab396d8SVijendar Mukunda {
241cab396d8SVijendar Mukunda 	struct i2s_stream_instance *rtd;
242cab396d8SVijendar Mukunda 	struct snd_soc_pcm_runtime *prtd;
243cab396d8SVijendar Mukunda 	struct snd_soc_card *card;
244cab396d8SVijendar Mukunda 	struct acp5x_platform_info *pinfo;
245cab396d8SVijendar Mukunda 	struct i2s_dev_data *adata;
246cab396d8SVijendar Mukunda 	u64 size;
247cab396d8SVijendar Mukunda 
248d4f23dcdSKuninori Morimoto 	prtd = snd_soc_substream_to_rtd(substream);
249cab396d8SVijendar Mukunda 	card = prtd->card;
250cab396d8SVijendar Mukunda 	pinfo = snd_soc_card_get_drvdata(card);
251cab396d8SVijendar Mukunda 	adata = dev_get_drvdata(component->dev);
252cab396d8SVijendar Mukunda 	rtd = substream->runtime->private_data;
253cab396d8SVijendar Mukunda 
254cab396d8SVijendar Mukunda 	if (!rtd)
255cab396d8SVijendar Mukunda 		return -EINVAL;
256cab396d8SVijendar Mukunda 
257cab396d8SVijendar Mukunda 	if (pinfo) {
258cab396d8SVijendar Mukunda 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
259cab396d8SVijendar Mukunda 			rtd->i2s_instance = pinfo->play_i2s_instance;
260cab396d8SVijendar Mukunda 			switch (rtd->i2s_instance) {
261cab396d8SVijendar Mukunda 			case I2S_HS_INSTANCE:
262cab396d8SVijendar Mukunda 				adata->play_stream = substream;
263cab396d8SVijendar Mukunda 				break;
264cab396d8SVijendar Mukunda 			case I2S_SP_INSTANCE:
265cab396d8SVijendar Mukunda 			default:
266cab396d8SVijendar Mukunda 				adata->i2ssp_play_stream = substream;
267cab396d8SVijendar Mukunda 			}
268cab396d8SVijendar Mukunda 		} else {
269cab396d8SVijendar Mukunda 			rtd->i2s_instance = pinfo->cap_i2s_instance;
270cab396d8SVijendar Mukunda 			switch (rtd->i2s_instance) {
271cab396d8SVijendar Mukunda 			case I2S_HS_INSTANCE:
272cab396d8SVijendar Mukunda 				adata->capture_stream = substream;
273cab396d8SVijendar Mukunda 				break;
274cab396d8SVijendar Mukunda 			case I2S_SP_INSTANCE:
275cab396d8SVijendar Mukunda 			default:
276cab396d8SVijendar Mukunda 				adata->i2ssp_capture_stream = substream;
277cab396d8SVijendar Mukunda 			}
278cab396d8SVijendar Mukunda 		}
279cab396d8SVijendar Mukunda 	} else {
280cab396d8SVijendar Mukunda 		dev_err(component->dev, "pinfo failed\n");
281cab396d8SVijendar Mukunda 		return -EINVAL;
282cab396d8SVijendar Mukunda 	}
283cab396d8SVijendar Mukunda 	size = params_buffer_bytes(params);
28454e1bf9fSMeng Tang 	rtd->dma_addr = substream->runtime->dma_addr;
285cab396d8SVijendar Mukunda 	rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
286cab396d8SVijendar Mukunda 	config_acp5x_dma(rtd, substream->stream);
287cab396d8SVijendar Mukunda 	return 0;
288cab396d8SVijendar Mukunda }
289cab396d8SVijendar Mukunda 
acp5x_dma_pointer(struct snd_soc_component * component,struct snd_pcm_substream * substream)290cab396d8SVijendar Mukunda static snd_pcm_uframes_t acp5x_dma_pointer(struct snd_soc_component *component,
291cab396d8SVijendar Mukunda 					   struct snd_pcm_substream *substream)
292cab396d8SVijendar Mukunda {
293cab396d8SVijendar Mukunda 	struct i2s_stream_instance *rtd;
294cab396d8SVijendar Mukunda 	u32 pos;
295cab396d8SVijendar Mukunda 	u32 buffersize;
296cab396d8SVijendar Mukunda 	u64 bytescount;
297cab396d8SVijendar Mukunda 
298cab396d8SVijendar Mukunda 	rtd = substream->runtime->private_data;
299cab396d8SVijendar Mukunda 	buffersize = frames_to_bytes(substream->runtime,
300cab396d8SVijendar Mukunda 				     substream->runtime->buffer_size);
301cab396d8SVijendar Mukunda 	bytescount = acp_get_byte_count(rtd, substream->stream);
302cab396d8SVijendar Mukunda 	if (bytescount > rtd->bytescount)
303cab396d8SVijendar Mukunda 		bytescount -= rtd->bytescount;
304cab396d8SVijendar Mukunda 	pos = do_div(bytescount, buffersize);
305cab396d8SVijendar Mukunda 	return bytes_to_frames(substream->runtime, pos);
306cab396d8SVijendar Mukunda }
307cab396d8SVijendar Mukunda 
acp5x_dma_new(struct snd_soc_component * component,struct snd_soc_pcm_runtime * rtd)308cab396d8SVijendar Mukunda static int acp5x_dma_new(struct snd_soc_component *component,
309cab396d8SVijendar Mukunda 			 struct snd_soc_pcm_runtime *rtd)
310cab396d8SVijendar Mukunda {
311cab396d8SVijendar Mukunda 	struct device *parent = component->dev->parent;
312cab396d8SVijendar Mukunda 
313cab396d8SVijendar Mukunda 	snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
314cab396d8SVijendar Mukunda 				       parent, MIN_BUFFER, MAX_BUFFER);
315cab396d8SVijendar Mukunda 	return 0;
316cab396d8SVijendar Mukunda }
317cab396d8SVijendar Mukunda 
acp5x_dma_close(struct snd_soc_component * component,struct snd_pcm_substream * substream)318cab396d8SVijendar Mukunda static int acp5x_dma_close(struct snd_soc_component *component,
319cab396d8SVijendar Mukunda 			   struct snd_pcm_substream *substream)
320cab396d8SVijendar Mukunda {
321cab396d8SVijendar Mukunda 	struct snd_soc_pcm_runtime *prtd;
322cab396d8SVijendar Mukunda 	struct i2s_dev_data *adata;
323cab396d8SVijendar Mukunda 	struct i2s_stream_instance *ins;
324cab396d8SVijendar Mukunda 
325d4f23dcdSKuninori Morimoto 	prtd = snd_soc_substream_to_rtd(substream);
326cab396d8SVijendar Mukunda 	component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
327cab396d8SVijendar Mukunda 	adata = dev_get_drvdata(component->dev);
328cab396d8SVijendar Mukunda 	ins = substream->runtime->private_data;
329cab396d8SVijendar Mukunda 	if (!ins)
330cab396d8SVijendar Mukunda 		return -EINVAL;
331cab396d8SVijendar Mukunda 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
332cab396d8SVijendar Mukunda 		switch (ins->i2s_instance) {
333cab396d8SVijendar Mukunda 		case I2S_HS_INSTANCE:
334cab396d8SVijendar Mukunda 			adata->play_stream = NULL;
335cab396d8SVijendar Mukunda 			break;
336cab396d8SVijendar Mukunda 		case I2S_SP_INSTANCE:
337cab396d8SVijendar Mukunda 		default:
338cab396d8SVijendar Mukunda 			adata->i2ssp_play_stream = NULL;
339cab396d8SVijendar Mukunda 		}
340cab396d8SVijendar Mukunda 	} else {
341cab396d8SVijendar Mukunda 		switch (ins->i2s_instance) {
342cab396d8SVijendar Mukunda 		case I2S_HS_INSTANCE:
343cab396d8SVijendar Mukunda 			adata->capture_stream = NULL;
344cab396d8SVijendar Mukunda 			break;
345cab396d8SVijendar Mukunda 		case I2S_SP_INSTANCE:
346cab396d8SVijendar Mukunda 		default:
347cab396d8SVijendar Mukunda 			adata->i2ssp_capture_stream = NULL;
348cab396d8SVijendar Mukunda 		}
349cab396d8SVijendar Mukunda 	}
350cab396d8SVijendar Mukunda 	kfree(ins);
351cab396d8SVijendar Mukunda 	return 0;
352cab396d8SVijendar Mukunda }
353cab396d8SVijendar Mukunda 
354cab396d8SVijendar Mukunda static const struct snd_soc_component_driver acp5x_i2s_component = {
355cab396d8SVijendar Mukunda 	.name		= DRV_NAME,
356cab396d8SVijendar Mukunda 	.open		= acp5x_dma_open,
357cab396d8SVijendar Mukunda 	.close		= acp5x_dma_close,
358cab396d8SVijendar Mukunda 	.hw_params	= acp5x_dma_hw_params,
359cab396d8SVijendar Mukunda 	.pointer	= acp5x_dma_pointer,
360cab396d8SVijendar Mukunda 	.pcm_construct	= acp5x_dma_new,
361cab396d8SVijendar Mukunda };
362cab396d8SVijendar Mukunda 
acp5x_audio_probe(struct platform_device * pdev)36377f61444SVijendar Mukunda static int acp5x_audio_probe(struct platform_device *pdev)
36477f61444SVijendar Mukunda {
36577f61444SVijendar Mukunda 	struct resource *res;
36677f61444SVijendar Mukunda 	struct i2s_dev_data *adata;
367fc2c8067SVijendar Mukunda 	unsigned int irqflags;
36877f61444SVijendar Mukunda 	int status;
36977f61444SVijendar Mukunda 
37077f61444SVijendar Mukunda 	if (!pdev->dev.platform_data) {
37177f61444SVijendar Mukunda 		dev_err(&pdev->dev, "platform_data not retrieved\n");
37277f61444SVijendar Mukunda 		return -ENODEV;
37377f61444SVijendar Mukunda 	}
37477f61444SVijendar Mukunda 	irqflags = *((unsigned int *)(pdev->dev.platform_data));
37577f61444SVijendar Mukunda 
37677f61444SVijendar Mukunda 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
37777f61444SVijendar Mukunda 	if (!res) {
37877f61444SVijendar Mukunda 		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
37977f61444SVijendar Mukunda 		return -ENODEV;
38077f61444SVijendar Mukunda 	}
38177f61444SVijendar Mukunda 
38277f61444SVijendar Mukunda 	adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
38377f61444SVijendar Mukunda 	if (!adata)
38477f61444SVijendar Mukunda 		return -ENOMEM;
38577f61444SVijendar Mukunda 
38677f61444SVijendar Mukunda 	adata->acp5x_base = devm_ioremap(&pdev->dev, res->start,
38777f61444SVijendar Mukunda 					 resource_size(res));
38877f61444SVijendar Mukunda 	if (!adata->acp5x_base)
38977f61444SVijendar Mukunda 		return -ENOMEM;
390fc2c8067SVijendar Mukunda 
391468f2529SDan Carpenter 	status = platform_get_irq(pdev, 0);
392468f2529SDan Carpenter 	if (status < 0)
393468f2529SDan Carpenter 		return status;
394468f2529SDan Carpenter 	adata->i2s_irq = status;
395fc2c8067SVijendar Mukunda 
39677f61444SVijendar Mukunda 	dev_set_drvdata(&pdev->dev, adata);
39777f61444SVijendar Mukunda 	status = devm_snd_soc_register_component(&pdev->dev,
39877f61444SVijendar Mukunda 						 &acp5x_i2s_component,
39977f61444SVijendar Mukunda 						 NULL, 0);
400fc2c8067SVijendar Mukunda 	if (status) {
40177f61444SVijendar Mukunda 		dev_err(&pdev->dev, "Fail to register acp i2s component\n");
402fc2c8067SVijendar Mukunda 		return status;
403fc2c8067SVijendar Mukunda 	}
404fc2c8067SVijendar Mukunda 	status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler,
405fc2c8067SVijendar Mukunda 				  irqflags, "ACP5x_I2S_IRQ", adata);
406361414dcSVijendar Mukunda 	if (status) {
407fc2c8067SVijendar Mukunda 		dev_err(&pdev->dev, "ACP5x I2S IRQ request failed\n");
40877f61444SVijendar Mukunda 		return status;
40977f61444SVijendar Mukunda 	}
410361414dcSVijendar Mukunda 	pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
411361414dcSVijendar Mukunda 	pm_runtime_use_autosuspend(&pdev->dev);
41215475631SArun Gopal Kondaveeti 	pm_runtime_mark_last_busy(&pdev->dev);
41315475631SArun Gopal Kondaveeti 	pm_runtime_set_active(&pdev->dev);
414361414dcSVijendar Mukunda 	pm_runtime_enable(&pdev->dev);
415361414dcSVijendar Mukunda 	return 0;
416361414dcSVijendar Mukunda }
417361414dcSVijendar Mukunda 
acp5x_audio_remove(struct platform_device * pdev)4188564d4f2SUwe Kleine-König static void acp5x_audio_remove(struct platform_device *pdev)
419361414dcSVijendar Mukunda {
420361414dcSVijendar Mukunda 	pm_runtime_disable(&pdev->dev);
421361414dcSVijendar Mukunda }
422361414dcSVijendar Mukunda 
acp5x_pcm_resume(struct device * dev)423361414dcSVijendar Mukunda static int __maybe_unused acp5x_pcm_resume(struct device *dev)
424361414dcSVijendar Mukunda {
425361414dcSVijendar Mukunda 	struct i2s_dev_data *adata;
42683b71361SVijendar Mukunda 	struct i2s_stream_instance *rtd;
42783b71361SVijendar Mukunda 	u32 val;
428361414dcSVijendar Mukunda 
429361414dcSVijendar Mukunda 	adata = dev_get_drvdata(dev);
430361414dcSVijendar Mukunda 
431361414dcSVijendar Mukunda 	if (adata->play_stream && adata->play_stream->runtime) {
43283b71361SVijendar Mukunda 		rtd = adata->play_stream->runtime->private_data;
433361414dcSVijendar Mukunda 		config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK);
43483b71361SVijendar Mukunda 		acp_writel((rtd->xfer_resolution  << 3), rtd->acp5x_base + ACP_HSTDM_ITER);
43583b71361SVijendar Mukunda 		if (adata->tdm_mode == TDM_ENABLE) {
43683b71361SVijendar Mukunda 			acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_TXFRMT);
43783b71361SVijendar Mukunda 			val = acp_readl(adata->acp5x_base + ACP_HSTDM_ITER);
43883b71361SVijendar Mukunda 			acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_ITER);
439361414dcSVijendar Mukunda 		}
44083b71361SVijendar Mukunda 	}
44183b71361SVijendar Mukunda 	if (adata->i2ssp_play_stream && adata->i2ssp_play_stream->runtime) {
44283b71361SVijendar Mukunda 		rtd = adata->i2ssp_play_stream->runtime->private_data;
44383b71361SVijendar Mukunda 		config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK);
44483b71361SVijendar Mukunda 		acp_writel((rtd->xfer_resolution  << 3), rtd->acp5x_base + ACP_I2STDM_ITER);
44583b71361SVijendar Mukunda 		if (adata->tdm_mode == TDM_ENABLE) {
44683b71361SVijendar Mukunda 			acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_TXFRMT);
44783b71361SVijendar Mukunda 			val = acp_readl(adata->acp5x_base + ACP_I2STDM_ITER);
44883b71361SVijendar Mukunda 			acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_ITER);
44983b71361SVijendar Mukunda 		}
450361414dcSVijendar Mukunda 	}
451361414dcSVijendar Mukunda 
452361414dcSVijendar Mukunda 	if (adata->capture_stream && adata->capture_stream->runtime) {
45383b71361SVijendar Mukunda 		rtd = adata->capture_stream->runtime->private_data;
454361414dcSVijendar Mukunda 		config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
45583b71361SVijendar Mukunda 		acp_writel((rtd->xfer_resolution  << 3), rtd->acp5x_base + ACP_HSTDM_IRER);
456361414dcSVijendar Mukunda 		if (adata->tdm_mode == TDM_ENABLE) {
45783b71361SVijendar Mukunda 			acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_RXFRMT);
45883b71361SVijendar Mukunda 			val = acp_readl(adata->acp5x_base + ACP_HSTDM_IRER);
45983b71361SVijendar Mukunda 			acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_IRER);
46083b71361SVijendar Mukunda 		}
46183b71361SVijendar Mukunda 	}
46283b71361SVijendar Mukunda 	if (adata->i2ssp_capture_stream && adata->i2ssp_capture_stream->runtime) {
46383b71361SVijendar Mukunda 		rtd = adata->i2ssp_capture_stream->runtime->private_data;
46483b71361SVijendar Mukunda 		config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
46583b71361SVijendar Mukunda 		acp_writel((rtd->xfer_resolution  << 3), rtd->acp5x_base + ACP_I2STDM_IRER);
46683b71361SVijendar Mukunda 		if (adata->tdm_mode == TDM_ENABLE) {
46783b71361SVijendar Mukunda 			acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_RXFRMT);
46883b71361SVijendar Mukunda 			val = acp_readl(adata->acp5x_base + ACP_I2STDM_IRER);
46983b71361SVijendar Mukunda 			acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_IRER);
47083b71361SVijendar Mukunda 		}
471361414dcSVijendar Mukunda 	}
472361414dcSVijendar Mukunda 	acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
473361414dcSVijendar Mukunda 	return 0;
474361414dcSVijendar Mukunda }
475361414dcSVijendar Mukunda 
acp5x_pcm_suspend(struct device * dev)476361414dcSVijendar Mukunda static int __maybe_unused acp5x_pcm_suspend(struct device *dev)
477361414dcSVijendar Mukunda {
478361414dcSVijendar Mukunda 	struct i2s_dev_data *adata;
479361414dcSVijendar Mukunda 
480361414dcSVijendar Mukunda 	adata = dev_get_drvdata(dev);
481361414dcSVijendar Mukunda 	acp_writel(0, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
482361414dcSVijendar Mukunda 	return 0;
483361414dcSVijendar Mukunda }
484361414dcSVijendar Mukunda 
acp5x_pcm_runtime_resume(struct device * dev)485361414dcSVijendar Mukunda static int __maybe_unused acp5x_pcm_runtime_resume(struct device *dev)
486361414dcSVijendar Mukunda {
487361414dcSVijendar Mukunda 	struct i2s_dev_data *adata;
488361414dcSVijendar Mukunda 
489361414dcSVijendar Mukunda 	adata = dev_get_drvdata(dev);
490361414dcSVijendar Mukunda 	acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
491361414dcSVijendar Mukunda 	return 0;
492361414dcSVijendar Mukunda }
493361414dcSVijendar Mukunda 
494361414dcSVijendar Mukunda static const struct dev_pm_ops acp5x_pm_ops = {
495361414dcSVijendar Mukunda 	SET_RUNTIME_PM_OPS(acp5x_pcm_suspend,
496361414dcSVijendar Mukunda 			   acp5x_pcm_runtime_resume, NULL)
497361414dcSVijendar Mukunda 	SET_SYSTEM_SLEEP_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_resume)
498361414dcSVijendar Mukunda };
49977f61444SVijendar Mukunda 
50077f61444SVijendar Mukunda static struct platform_driver acp5x_dma_driver = {
50177f61444SVijendar Mukunda 	.probe = acp5x_audio_probe,
502*130af75bSUwe Kleine-König 	.remove = acp5x_audio_remove,
50377f61444SVijendar Mukunda 	.driver = {
50477f61444SVijendar Mukunda 		.name = "acp5x_i2s_dma",
505361414dcSVijendar Mukunda 		.pm = &acp5x_pm_ops,
50677f61444SVijendar Mukunda 	},
50777f61444SVijendar Mukunda };
50877f61444SVijendar Mukunda 
50977f61444SVijendar Mukunda module_platform_driver(acp5x_dma_driver);
51077f61444SVijendar Mukunda 
51177f61444SVijendar Mukunda MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
51277f61444SVijendar Mukunda MODULE_DESCRIPTION("AMD ACP 5.x PCM Driver");
51377f61444SVijendar Mukunda MODULE_LICENSE("GPL v2");
51477f61444SVijendar Mukunda MODULE_ALIAS("platform:" DRV_NAME);
515