GNU Linux-libre 5.19-rc6-gnu
[releases.git] / sound / soc / amd / acp / acp-i2s.c
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2 //
3 // This file is provided under a dual BSD/GPLv2 license. When using or
4 // redistributing this file, you may do so under either license.
5 //
6 // Copyright(c) 2021 Advanced Micro Devices, Inc.
7 //
8 // Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
9 //
10
11 /*
12  * Generic Hardware interface for ACP Audio I2S controller
13  */
14
15 #include <linux/platform_device.h>
16 #include <linux/module.h>
17 #include <linux/err.h>
18 #include <linux/io.h>
19 #include <sound/pcm_params.h>
20 #include <sound/soc.h>
21 #include <sound/soc-dai.h>
22 #include <linux/dma-mapping.h>
23
24 #include "amd.h"
25
26 #define DRV_NAME "acp_i2s_playcap"
27
28 static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params,
29                             struct snd_soc_dai *dai)
30 {
31         struct device *dev = dai->component->dev;
32         struct acp_dev_data *adata;
33         u32 val;
34         u32 xfer_resolution;
35         u32 reg_val;
36
37         adata = snd_soc_dai_get_drvdata(dai);
38
39         /* These values are as per Hardware Spec */
40         switch (params_format(params)) {
41         case SNDRV_PCM_FORMAT_U8:
42         case SNDRV_PCM_FORMAT_S8:
43                 xfer_resolution = 0x0;
44                 break;
45         case SNDRV_PCM_FORMAT_S16_LE:
46                 xfer_resolution = 0x02;
47                 break;
48         case SNDRV_PCM_FORMAT_S24_LE:
49                 xfer_resolution = 0x04;
50                 break;
51         case SNDRV_PCM_FORMAT_S32_LE:
52                 xfer_resolution = 0x05;
53                 break;
54         default:
55                 return -EINVAL;
56         }
57
58         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
59                 switch (dai->driver->id) {
60                 case I2S_BT_INSTANCE:
61                         reg_val = ACP_BTTDM_ITER;
62                         break;
63                 case I2S_SP_INSTANCE:
64                         reg_val = ACP_I2STDM_ITER;
65                         break;
66                 default:
67                         dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
68                         return -EINVAL;
69                 }
70         } else {
71                 switch (dai->driver->id) {
72                 case I2S_BT_INSTANCE:
73                         reg_val = ACP_BTTDM_IRER;
74                         break;
75                 case I2S_SP_INSTANCE:
76                         reg_val = ACP_I2STDM_IRER;
77                         break;
78                 default:
79                         dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
80                         return -EINVAL;
81                 }
82         }
83
84         val = readl(adata->acp_base + reg_val);
85         val &= ~ACP3x_ITER_IRER_SAMP_LEN_MASK;
86         val = val | (xfer_resolution  << 3);
87         writel(val, adata->acp_base + reg_val);
88
89         return 0;
90 }
91
92 static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
93 {
94         struct acp_stream *stream = substream->runtime->private_data;
95         struct device *dev = dai->component->dev;
96         struct acp_dev_data *adata = dev_get_drvdata(dev);
97         u32 val, period_bytes, reg_val, ier_val, water_val, buf_size, buf_reg;
98
99         period_bytes = frames_to_bytes(substream->runtime, substream->runtime->period_size);
100         buf_size = frames_to_bytes(substream->runtime, substream->runtime->buffer_size);
101
102         switch (cmd) {
103         case SNDRV_PCM_TRIGGER_START:
104         case SNDRV_PCM_TRIGGER_RESUME:
105         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
106                 stream->bytescount = acp_get_byte_count(adata, stream->dai_id, substream->stream);
107                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
108                         switch (dai->driver->id) {
109                         case I2S_BT_INSTANCE:
110                                 water_val = ACP_BT_TX_INTR_WATERMARK_SIZE;
111                                 reg_val = ACP_BTTDM_ITER;
112                                 ier_val = ACP_BTTDM_IER;
113                                 buf_reg = ACP_BT_TX_RINGBUFSIZE;
114                                 break;
115                         case I2S_SP_INSTANCE:
116                                 water_val = ACP_I2S_TX_INTR_WATERMARK_SIZE;
117                                 reg_val = ACP_I2STDM_ITER;
118                                 ier_val = ACP_I2STDM_IER;
119                                 buf_reg = ACP_I2S_TX_RINGBUFSIZE;
120                                 break;
121                         default:
122                                 dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
123                                 return -EINVAL;
124                         }
125                 } else {
126                         switch (dai->driver->id) {
127                         case I2S_BT_INSTANCE:
128                                 water_val = ACP_BT_RX_INTR_WATERMARK_SIZE;
129                                 reg_val = ACP_BTTDM_IRER;
130                                 ier_val = ACP_BTTDM_IER;
131                                 buf_reg = ACP_BT_RX_RINGBUFSIZE;
132                                 break;
133                         case I2S_SP_INSTANCE:
134                                 water_val = ACP_I2S_RX_INTR_WATERMARK_SIZE;
135                                 reg_val = ACP_I2STDM_IRER;
136                                 ier_val = ACP_I2STDM_IER;
137                                 buf_reg = ACP_I2S_RX_RINGBUFSIZE;
138                                 break;
139                         default:
140                                 dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
141                                 return -EINVAL;
142                         }
143                 }
144                 writel(period_bytes, adata->acp_base + water_val);
145                 writel(buf_size, adata->acp_base + buf_reg);
146                 val = readl(adata->acp_base + reg_val);
147                 val = val | BIT(0);
148                 writel(val, adata->acp_base + reg_val);
149                 writel(1, adata->acp_base + ier_val);
150                 return 0;
151         case SNDRV_PCM_TRIGGER_STOP:
152         case SNDRV_PCM_TRIGGER_SUSPEND:
153         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
154                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
155                         switch (dai->driver->id) {
156                         case I2S_BT_INSTANCE:
157                                 reg_val = ACP_BTTDM_ITER;
158                                 break;
159                         case I2S_SP_INSTANCE:
160                                 reg_val = ACP_I2STDM_ITER;
161                                 break;
162                         default:
163                                 dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
164                                 return -EINVAL;
165                         }
166
167                 } else {
168                         switch (dai->driver->id) {
169                         case I2S_BT_INSTANCE:
170                                 reg_val = ACP_BTTDM_IRER;
171                                 break;
172                         case I2S_SP_INSTANCE:
173                                 reg_val = ACP_I2STDM_IRER;
174                                 break;
175                         default:
176                                 dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
177                                 return -EINVAL;
178                         }
179                 }
180                 val = readl(adata->acp_base + reg_val);
181                 val = val & ~BIT(0);
182                 writel(val, adata->acp_base + reg_val);
183
184                 if (!(readl(adata->acp_base + ACP_BTTDM_ITER) & BIT(0)) &&
185                     !(readl(adata->acp_base + ACP_BTTDM_IRER) & BIT(0)))
186                         writel(0, adata->acp_base + ACP_BTTDM_IER);
187                 if (!(readl(adata->acp_base + ACP_I2STDM_ITER) & BIT(0)) &&
188                     !(readl(adata->acp_base + ACP_I2STDM_IRER) & BIT(0)))
189                         writel(0, adata->acp_base + ACP_I2STDM_IER);
190                 return 0;
191         default:
192                 return -EINVAL;
193         }
194
195         return 0;
196 }
197
198 static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
199 {
200         struct device *dev = dai->component->dev;
201         struct acp_dev_data *adata = dev_get_drvdata(dev);
202         struct acp_stream *stream = substream->runtime->private_data;
203         u32 reg_dma_size = 0, reg_fifo_size = 0, reg_fifo_addr = 0;
204         u32 phy_addr = 0, acp_fifo_addr = 0, ext_int_ctrl;
205         unsigned int dir = substream->stream;
206
207         switch (dai->driver->id) {
208         case I2S_SP_INSTANCE:
209                 if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
210                         reg_dma_size = ACP_I2S_TX_DMA_SIZE;
211                         acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
212                                                 SP_PB_FIFO_ADDR_OFFSET;
213                         reg_fifo_addr = ACP_I2S_TX_FIFOADDR;
214                         reg_fifo_size = ACP_I2S_TX_FIFOSIZE;
215
216                         phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset;
217                         writel(phy_addr, adata->acp_base + ACP_I2S_TX_RINGBUFADDR);
218                 } else {
219                         reg_dma_size = ACP_I2S_RX_DMA_SIZE;
220                         acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
221                                                 SP_CAPT_FIFO_ADDR_OFFSET;
222                         reg_fifo_addr = ACP_I2S_RX_FIFOADDR;
223                         reg_fifo_size = ACP_I2S_RX_FIFOSIZE;
224                         phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset;
225                         writel(phy_addr, adata->acp_base + ACP_I2S_RX_RINGBUFADDR);
226                 }
227                 break;
228         case I2S_BT_INSTANCE:
229                 if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
230                         reg_dma_size = ACP_BT_TX_DMA_SIZE;
231                         acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
232                                                 BT_PB_FIFO_ADDR_OFFSET;
233                         reg_fifo_addr = ACP_BT_TX_FIFOADDR;
234                         reg_fifo_size = ACP_BT_TX_FIFOSIZE;
235
236                         phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
237                         writel(phy_addr, adata->acp_base + ACP_BT_TX_RINGBUFADDR);
238                 } else {
239                         reg_dma_size = ACP_BT_RX_DMA_SIZE;
240                         acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
241                                                 BT_CAPT_FIFO_ADDR_OFFSET;
242                         reg_fifo_addr = ACP_BT_RX_FIFOADDR;
243                         reg_fifo_size = ACP_BT_RX_FIFOSIZE;
244
245                         phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
246                         writel(phy_addr, adata->acp_base + ACP_BT_RX_RINGBUFADDR);
247                 }
248                 break;
249         default:
250                 dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
251                 return -EINVAL;
252         }
253
254         writel(DMA_SIZE, adata->acp_base + reg_dma_size);
255         writel(acp_fifo_addr, adata->acp_base + reg_fifo_addr);
256         writel(FIFO_SIZE, adata->acp_base + reg_fifo_size);
257
258         ext_int_ctrl = readl(adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
259         ext_int_ctrl |= BIT(I2S_RX_THRESHOLD) | BIT(BT_RX_THRESHOLD)
260                         | BIT(I2S_TX_THRESHOLD) | BIT(BT_TX_THRESHOLD);
261
262         writel(ext_int_ctrl, adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
263
264         return 0;
265 }
266
267 static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
268 {
269         struct acp_stream *stream = substream->runtime->private_data;
270         struct device *dev = dai->component->dev;
271         unsigned int dir = substream->stream;
272         unsigned int irq_bit = 0;
273
274         switch (dai->driver->id) {
275         case I2S_SP_INSTANCE:
276                 if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
277                         irq_bit = BIT(I2S_TX_THRESHOLD);
278                         stream->pte_offset = ACP_SRAM_SP_PB_PTE_OFFSET;
279                         stream->fifo_offset = SP_PB_FIFO_ADDR_OFFSET;
280                 } else {
281                         irq_bit = BIT(I2S_RX_THRESHOLD);
282                         stream->pte_offset = ACP_SRAM_SP_CP_PTE_OFFSET;
283                         stream->fifo_offset = SP_CAPT_FIFO_ADDR_OFFSET;
284                 }
285                 break;
286         case I2S_BT_INSTANCE:
287                 if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
288                         irq_bit = BIT(BT_TX_THRESHOLD);
289                         stream->pte_offset = ACP_SRAM_BT_PB_PTE_OFFSET;
290                         stream->fifo_offset = BT_PB_FIFO_ADDR_OFFSET;
291                 } else {
292                         irq_bit = BIT(BT_RX_THRESHOLD);
293                         stream->pte_offset = ACP_SRAM_BT_CP_PTE_OFFSET;
294                         stream->fifo_offset = BT_CAPT_FIFO_ADDR_OFFSET;
295                 }
296                 break;
297         default:
298                 dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
299                 return -EINVAL;
300         }
301
302         /* Save runtime dai configuration in stream */
303         stream->id = dai->driver->id + dir;
304         stream->dai_id = dai->driver->id;
305         stream->irq_bit = irq_bit;
306
307         return 0;
308 }
309
310 const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = {
311         .startup = acp_i2s_startup,
312         .hw_params = acp_i2s_hwparams,
313         .prepare = acp_i2s_prepare,
314         .trigger = acp_i2s_trigger,
315 };
316 EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, SND_SOC_ACP_COMMON);
317
318 int asoc_acp_i2s_probe(struct snd_soc_dai *dai)
319 {
320         struct device *dev = dai->component->dev;
321         struct acp_dev_data *adata = dev_get_drvdata(dev);
322         unsigned int val;
323
324         if (!adata->acp_base) {
325                 dev_err(dev, "I2S base is NULL\n");
326                 return -EINVAL;
327         }
328
329         val = readl(adata->acp_base + ACP_I2S_PIN_CONFIG);
330         if (val != I2S_MODE) {
331                 dev_err(dev, "I2S Mode not supported val %x\n", val);
332                 return -EINVAL;
333         }
334
335         return 0;
336 }
337 EXPORT_SYMBOL_NS_GPL(asoc_acp_i2s_probe, SND_SOC_ACP_COMMON);
338
339 MODULE_LICENSE("Dual BSD/GPL");
340 MODULE_ALIAS(DRV_NAME);