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 | #include <linux/bitfield.h> |
24 | |
25 | #include "amd.h" |
26 | |
27 | #define DRV_NAME "acp_i2s_playcap" |
28 | #define I2S_MASTER_MODE_ENABLE 1 |
29 | #define LRCLK_DIV_FIELD GENMASK(10, 2) |
30 | #define BCLK_DIV_FIELD GENMASK(23, 11) |
31 | #define ACP63_LRCLK_DIV_FIELD GENMASK(12, 2) |
32 | #define ACP63_BCLK_DIV_FIELD GENMASK(23, 13) |
33 | |
34 | static inline void acp_set_i2s_clk(struct acp_dev_data *adata, int dai_id) |
35 | { |
36 | u32 i2s_clk_reg, val; |
37 | struct acp_chip_info *chip; |
38 | struct device *dev; |
39 | |
40 | dev = adata->dev; |
41 | chip = dev_get_platdata(dev); |
42 | switch (dai_id) { |
43 | case I2S_SP_INSTANCE: |
44 | i2s_clk_reg = ACP_I2STDM0_MSTRCLKGEN; |
45 | break; |
46 | case I2S_BT_INSTANCE: |
47 | i2s_clk_reg = ACP_I2STDM1_MSTRCLKGEN; |
48 | break; |
49 | case I2S_HS_INSTANCE: |
50 | i2s_clk_reg = ACP_I2STDM2_MSTRCLKGEN; |
51 | break; |
52 | default: |
53 | i2s_clk_reg = ACP_I2STDM0_MSTRCLKGEN; |
54 | break; |
55 | } |
56 | |
57 | val = I2S_MASTER_MODE_ENABLE; |
58 | if (adata->tdm_mode) |
59 | val |= BIT(1); |
60 | |
61 | switch (chip->acp_rev) { |
62 | case ACP63_DEV: |
63 | val |= FIELD_PREP(ACP63_LRCLK_DIV_FIELD, adata->lrclk_div); |
64 | val |= FIELD_PREP(ACP63_BCLK_DIV_FIELD, adata->bclk_div); |
65 | break; |
66 | default: |
67 | val |= FIELD_PREP(LRCLK_DIV_FIELD, adata->lrclk_div); |
68 | val |= FIELD_PREP(BCLK_DIV_FIELD, adata->bclk_div); |
69 | } |
70 | writel(val, addr: adata->acp_base + i2s_clk_reg); |
71 | } |
72 | |
73 | static int acp_i2s_set_fmt(struct snd_soc_dai *cpu_dai, |
74 | unsigned int fmt) |
75 | { |
76 | struct acp_dev_data *adata = snd_soc_dai_get_drvdata(dai: cpu_dai); |
77 | int mode; |
78 | |
79 | mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK; |
80 | switch (mode) { |
81 | case SND_SOC_DAIFMT_I2S: |
82 | adata->tdm_mode = TDM_DISABLE; |
83 | break; |
84 | case SND_SOC_DAIFMT_DSP_A: |
85 | adata->tdm_mode = TDM_ENABLE; |
86 | break; |
87 | default: |
88 | return -EINVAL; |
89 | } |
90 | return 0; |
91 | } |
92 | |
93 | static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mask, |
94 | int slots, int slot_width) |
95 | { |
96 | struct device *dev = dai->component->dev; |
97 | struct acp_dev_data *adata = snd_soc_dai_get_drvdata(dai); |
98 | struct acp_stream *stream; |
99 | int slot_len, no_of_slots; |
100 | |
101 | switch (slot_width) { |
102 | case SLOT_WIDTH_8: |
103 | slot_len = 8; |
104 | break; |
105 | case SLOT_WIDTH_16: |
106 | slot_len = 16; |
107 | break; |
108 | case SLOT_WIDTH_24: |
109 | slot_len = 24; |
110 | break; |
111 | case SLOT_WIDTH_32: |
112 | slot_len = 0; |
113 | break; |
114 | default: |
115 | dev_err(dev, "Unsupported bitdepth %d\n" , slot_width); |
116 | return -EINVAL; |
117 | } |
118 | |
119 | switch (slots) { |
120 | case 1 ... 7: |
121 | no_of_slots = slots; |
122 | break; |
123 | case 8: |
124 | no_of_slots = 0; |
125 | break; |
126 | default: |
127 | dev_err(dev, "Unsupported slots %d\n" , slots); |
128 | return -EINVAL; |
129 | } |
130 | |
131 | slots = no_of_slots; |
132 | |
133 | spin_lock_irq(lock: &adata->acp_lock); |
134 | list_for_each_entry(stream, &adata->stream_list, list) { |
135 | if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK) |
136 | adata->tdm_tx_fmt[stream->dai_id - 1] = |
137 | FRM_LEN | (slots << 15) | (slot_len << 18); |
138 | else if (rx_mask && stream->dir == SNDRV_PCM_STREAM_CAPTURE) |
139 | adata->tdm_rx_fmt[stream->dai_id - 1] = |
140 | FRM_LEN | (slots << 15) | (slot_len << 18); |
141 | } |
142 | spin_unlock_irq(lock: &adata->acp_lock); |
143 | return 0; |
144 | } |
145 | |
146 | static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, |
147 | struct snd_soc_dai *dai) |
148 | { |
149 | struct device *dev = dai->component->dev; |
150 | struct acp_dev_data *adata; |
151 | struct acp_resource *rsrc; |
152 | u32 val; |
153 | u32 xfer_resolution; |
154 | u32 reg_val, fmt_reg, tdm_fmt; |
155 | u32 lrclk_div_val, bclk_div_val; |
156 | |
157 | adata = snd_soc_dai_get_drvdata(dai); |
158 | rsrc = adata->rsrc; |
159 | |
160 | /* These values are as per Hardware Spec */ |
161 | switch (params_format(p: params)) { |
162 | case SNDRV_PCM_FORMAT_U8: |
163 | case SNDRV_PCM_FORMAT_S8: |
164 | xfer_resolution = 0x0; |
165 | break; |
166 | case SNDRV_PCM_FORMAT_S16_LE: |
167 | xfer_resolution = 0x02; |
168 | break; |
169 | case SNDRV_PCM_FORMAT_S24_LE: |
170 | xfer_resolution = 0x04; |
171 | break; |
172 | case SNDRV_PCM_FORMAT_S32_LE: |
173 | xfer_resolution = 0x05; |
174 | break; |
175 | default: |
176 | return -EINVAL; |
177 | } |
178 | |
179 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
180 | switch (dai->driver->id) { |
181 | case I2S_BT_INSTANCE: |
182 | reg_val = ACP_BTTDM_ITER; |
183 | fmt_reg = ACP_BTTDM_TXFRMT; |
184 | break; |
185 | case I2S_SP_INSTANCE: |
186 | reg_val = ACP_I2STDM_ITER; |
187 | fmt_reg = ACP_I2STDM_TXFRMT; |
188 | break; |
189 | case I2S_HS_INSTANCE: |
190 | reg_val = ACP_HSTDM_ITER; |
191 | fmt_reg = ACP_HSTDM_TXFRMT; |
192 | break; |
193 | default: |
194 | dev_err(dev, "Invalid dai id %x\n" , dai->driver->id); |
195 | return -EINVAL; |
196 | } |
197 | adata->xfer_tx_resolution[dai->driver->id - 1] = xfer_resolution; |
198 | } else { |
199 | switch (dai->driver->id) { |
200 | case I2S_BT_INSTANCE: |
201 | reg_val = ACP_BTTDM_IRER; |
202 | fmt_reg = ACP_BTTDM_RXFRMT; |
203 | break; |
204 | case I2S_SP_INSTANCE: |
205 | reg_val = ACP_I2STDM_IRER; |
206 | fmt_reg = ACP_I2STDM_RXFRMT; |
207 | break; |
208 | case I2S_HS_INSTANCE: |
209 | reg_val = ACP_HSTDM_IRER; |
210 | fmt_reg = ACP_HSTDM_RXFRMT; |
211 | break; |
212 | default: |
213 | dev_err(dev, "Invalid dai id %x\n" , dai->driver->id); |
214 | return -EINVAL; |
215 | } |
216 | adata->xfer_rx_resolution[dai->driver->id - 1] = xfer_resolution; |
217 | } |
218 | |
219 | val = readl(addr: adata->acp_base + reg_val); |
220 | val &= ~ACP3x_ITER_IRER_SAMP_LEN_MASK; |
221 | val = val | (xfer_resolution << 3); |
222 | writel(val, addr: adata->acp_base + reg_val); |
223 | |
224 | if (adata->tdm_mode) { |
225 | val = readl(addr: adata->acp_base + reg_val); |
226 | writel(val: val | BIT(1), addr: adata->acp_base + reg_val); |
227 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
228 | tdm_fmt = adata->tdm_tx_fmt[dai->driver->id - 1]; |
229 | else |
230 | tdm_fmt = adata->tdm_rx_fmt[dai->driver->id - 1]; |
231 | writel(val: tdm_fmt, addr: adata->acp_base + fmt_reg); |
232 | } |
233 | |
234 | if (rsrc->soc_mclk) { |
235 | switch (params_format(p: params)) { |
236 | case SNDRV_PCM_FORMAT_S16_LE: |
237 | switch (params_rate(p: params)) { |
238 | case 8000: |
239 | bclk_div_val = 768; |
240 | break; |
241 | case 16000: |
242 | bclk_div_val = 384; |
243 | break; |
244 | case 24000: |
245 | bclk_div_val = 256; |
246 | break; |
247 | case 32000: |
248 | bclk_div_val = 192; |
249 | break; |
250 | case 44100: |
251 | case 48000: |
252 | bclk_div_val = 128; |
253 | break; |
254 | case 88200: |
255 | case 96000: |
256 | bclk_div_val = 64; |
257 | break; |
258 | case 192000: |
259 | bclk_div_val = 32; |
260 | break; |
261 | default: |
262 | return -EINVAL; |
263 | } |
264 | lrclk_div_val = 32; |
265 | break; |
266 | case SNDRV_PCM_FORMAT_S32_LE: |
267 | switch (params_rate(p: params)) { |
268 | case 8000: |
269 | bclk_div_val = 384; |
270 | break; |
271 | case 16000: |
272 | bclk_div_val = 192; |
273 | break; |
274 | case 24000: |
275 | bclk_div_val = 128; |
276 | break; |
277 | case 32000: |
278 | bclk_div_val = 96; |
279 | break; |
280 | case 44100: |
281 | case 48000: |
282 | bclk_div_val = 64; |
283 | break; |
284 | case 88200: |
285 | case 96000: |
286 | bclk_div_val = 32; |
287 | break; |
288 | case 192000: |
289 | bclk_div_val = 16; |
290 | break; |
291 | default: |
292 | return -EINVAL; |
293 | } |
294 | lrclk_div_val = 64; |
295 | break; |
296 | default: |
297 | return -EINVAL; |
298 | } |
299 | adata->lrclk_div = lrclk_div_val; |
300 | adata->bclk_div = bclk_div_val; |
301 | } |
302 | return 0; |
303 | } |
304 | |
305 | static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) |
306 | { |
307 | struct acp_stream *stream = substream->runtime->private_data; |
308 | struct device *dev = dai->component->dev; |
309 | struct acp_dev_data *adata = dev_get_drvdata(dev); |
310 | struct acp_resource *rsrc = adata->rsrc; |
311 | u32 val, period_bytes, reg_val, ier_val, water_val, buf_size, buf_reg; |
312 | |
313 | period_bytes = frames_to_bytes(runtime: substream->runtime, size: substream->runtime->period_size); |
314 | buf_size = frames_to_bytes(runtime: substream->runtime, size: substream->runtime->buffer_size); |
315 | |
316 | switch (cmd) { |
317 | case SNDRV_PCM_TRIGGER_START: |
318 | case SNDRV_PCM_TRIGGER_RESUME: |
319 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
320 | stream->bytescount = acp_get_byte_count(adata, dai_id: stream->dai_id, direction: substream->stream); |
321 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
322 | switch (dai->driver->id) { |
323 | case I2S_BT_INSTANCE: |
324 | water_val = ACP_BT_TX_INTR_WATERMARK_SIZE; |
325 | reg_val = ACP_BTTDM_ITER; |
326 | ier_val = ACP_BTTDM_IER; |
327 | buf_reg = ACP_BT_TX_RINGBUFSIZE; |
328 | break; |
329 | case I2S_SP_INSTANCE: |
330 | water_val = ACP_I2S_TX_INTR_WATERMARK_SIZE; |
331 | reg_val = ACP_I2STDM_ITER; |
332 | ier_val = ACP_I2STDM_IER; |
333 | buf_reg = ACP_I2S_TX_RINGBUFSIZE; |
334 | break; |
335 | case I2S_HS_INSTANCE: |
336 | water_val = ACP_HS_TX_INTR_WATERMARK_SIZE; |
337 | reg_val = ACP_HSTDM_ITER; |
338 | ier_val = ACP_HSTDM_IER; |
339 | buf_reg = ACP_HS_TX_RINGBUFSIZE; |
340 | break; |
341 | default: |
342 | dev_err(dev, "Invalid dai id %x\n" , dai->driver->id); |
343 | return -EINVAL; |
344 | } |
345 | } else { |
346 | switch (dai->driver->id) { |
347 | case I2S_BT_INSTANCE: |
348 | water_val = ACP_BT_RX_INTR_WATERMARK_SIZE; |
349 | reg_val = ACP_BTTDM_IRER; |
350 | ier_val = ACP_BTTDM_IER; |
351 | buf_reg = ACP_BT_RX_RINGBUFSIZE; |
352 | break; |
353 | case I2S_SP_INSTANCE: |
354 | water_val = ACP_I2S_RX_INTR_WATERMARK_SIZE; |
355 | reg_val = ACP_I2STDM_IRER; |
356 | ier_val = ACP_I2STDM_IER; |
357 | buf_reg = ACP_I2S_RX_RINGBUFSIZE; |
358 | break; |
359 | case I2S_HS_INSTANCE: |
360 | water_val = ACP_HS_RX_INTR_WATERMARK_SIZE; |
361 | reg_val = ACP_HSTDM_IRER; |
362 | ier_val = ACP_HSTDM_IER; |
363 | buf_reg = ACP_HS_RX_RINGBUFSIZE; |
364 | break; |
365 | default: |
366 | dev_err(dev, "Invalid dai id %x\n" , dai->driver->id); |
367 | return -EINVAL; |
368 | } |
369 | } |
370 | writel(val: period_bytes, addr: adata->acp_base + water_val); |
371 | writel(val: buf_size, addr: adata->acp_base + buf_reg); |
372 | val = readl(addr: adata->acp_base + reg_val); |
373 | val = val | BIT(0); |
374 | writel(val, addr: adata->acp_base + reg_val); |
375 | writel(val: 1, addr: adata->acp_base + ier_val); |
376 | if (rsrc->soc_mclk) |
377 | acp_set_i2s_clk(adata, dai_id: dai->driver->id); |
378 | return 0; |
379 | case SNDRV_PCM_TRIGGER_STOP: |
380 | case SNDRV_PCM_TRIGGER_SUSPEND: |
381 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
382 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
383 | switch (dai->driver->id) { |
384 | case I2S_BT_INSTANCE: |
385 | reg_val = ACP_BTTDM_ITER; |
386 | break; |
387 | case I2S_SP_INSTANCE: |
388 | reg_val = ACP_I2STDM_ITER; |
389 | break; |
390 | case I2S_HS_INSTANCE: |
391 | reg_val = ACP_HSTDM_ITER; |
392 | break; |
393 | default: |
394 | dev_err(dev, "Invalid dai id %x\n" , dai->driver->id); |
395 | return -EINVAL; |
396 | } |
397 | |
398 | } else { |
399 | switch (dai->driver->id) { |
400 | case I2S_BT_INSTANCE: |
401 | reg_val = ACP_BTTDM_IRER; |
402 | break; |
403 | case I2S_SP_INSTANCE: |
404 | reg_val = ACP_I2STDM_IRER; |
405 | break; |
406 | case I2S_HS_INSTANCE: |
407 | reg_val = ACP_HSTDM_IRER; |
408 | break; |
409 | default: |
410 | dev_err(dev, "Invalid dai id %x\n" , dai->driver->id); |
411 | return -EINVAL; |
412 | } |
413 | } |
414 | val = readl(addr: adata->acp_base + reg_val); |
415 | val = val & ~BIT(0); |
416 | writel(val, addr: adata->acp_base + reg_val); |
417 | |
418 | if (!(readl(addr: adata->acp_base + ACP_BTTDM_ITER) & BIT(0)) && |
419 | !(readl(addr: adata->acp_base + ACP_BTTDM_IRER) & BIT(0))) |
420 | writel(val: 0, addr: adata->acp_base + ACP_BTTDM_IER); |
421 | if (!(readl(addr: adata->acp_base + ACP_I2STDM_ITER) & BIT(0)) && |
422 | !(readl(addr: adata->acp_base + ACP_I2STDM_IRER) & BIT(0))) |
423 | writel(val: 0, addr: adata->acp_base + ACP_I2STDM_IER); |
424 | if (!(readl(addr: adata->acp_base + ACP_HSTDM_ITER) & BIT(0)) && |
425 | !(readl(addr: adata->acp_base + ACP_HSTDM_IRER) & BIT(0))) |
426 | writel(val: 0, addr: adata->acp_base + ACP_HSTDM_IER); |
427 | return 0; |
428 | default: |
429 | return -EINVAL; |
430 | } |
431 | |
432 | return 0; |
433 | } |
434 | |
435 | static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) |
436 | { |
437 | struct device *dev = dai->component->dev; |
438 | struct acp_dev_data *adata = dev_get_drvdata(dev); |
439 | struct acp_resource *rsrc = adata->rsrc; |
440 | struct acp_stream *stream = substream->runtime->private_data; |
441 | u32 reg_dma_size = 0, reg_fifo_size = 0, reg_fifo_addr = 0; |
442 | u32 phy_addr = 0, acp_fifo_addr = 0, ext_int_ctrl; |
443 | unsigned int dir = substream->stream; |
444 | |
445 | switch (dai->driver->id) { |
446 | case I2S_SP_INSTANCE: |
447 | if (dir == SNDRV_PCM_STREAM_PLAYBACK) { |
448 | reg_dma_size = ACP_I2S_TX_DMA_SIZE; |
449 | acp_fifo_addr = rsrc->sram_pte_offset + |
450 | SP_PB_FIFO_ADDR_OFFSET; |
451 | reg_fifo_addr = ACP_I2S_TX_FIFOADDR; |
452 | reg_fifo_size = ACP_I2S_TX_FIFOSIZE; |
453 | |
454 | phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset; |
455 | writel(val: phy_addr, addr: adata->acp_base + ACP_I2S_TX_RINGBUFADDR); |
456 | } else { |
457 | reg_dma_size = ACP_I2S_RX_DMA_SIZE; |
458 | acp_fifo_addr = rsrc->sram_pte_offset + |
459 | SP_CAPT_FIFO_ADDR_OFFSET; |
460 | reg_fifo_addr = ACP_I2S_RX_FIFOADDR; |
461 | reg_fifo_size = ACP_I2S_RX_FIFOSIZE; |
462 | phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset; |
463 | writel(val: phy_addr, addr: adata->acp_base + ACP_I2S_RX_RINGBUFADDR); |
464 | } |
465 | break; |
466 | case I2S_BT_INSTANCE: |
467 | if (dir == SNDRV_PCM_STREAM_PLAYBACK) { |
468 | reg_dma_size = ACP_BT_TX_DMA_SIZE; |
469 | acp_fifo_addr = rsrc->sram_pte_offset + |
470 | BT_PB_FIFO_ADDR_OFFSET; |
471 | reg_fifo_addr = ACP_BT_TX_FIFOADDR; |
472 | reg_fifo_size = ACP_BT_TX_FIFOSIZE; |
473 | |
474 | phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset; |
475 | writel(val: phy_addr, addr: adata->acp_base + ACP_BT_TX_RINGBUFADDR); |
476 | } else { |
477 | reg_dma_size = ACP_BT_RX_DMA_SIZE; |
478 | acp_fifo_addr = rsrc->sram_pte_offset + |
479 | BT_CAPT_FIFO_ADDR_OFFSET; |
480 | reg_fifo_addr = ACP_BT_RX_FIFOADDR; |
481 | reg_fifo_size = ACP_BT_RX_FIFOSIZE; |
482 | |
483 | phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset; |
484 | writel(val: phy_addr, addr: adata->acp_base + ACP_BT_RX_RINGBUFADDR); |
485 | } |
486 | break; |
487 | case I2S_HS_INSTANCE: |
488 | if (dir == SNDRV_PCM_STREAM_PLAYBACK) { |
489 | reg_dma_size = ACP_HS_TX_DMA_SIZE; |
490 | acp_fifo_addr = rsrc->sram_pte_offset + |
491 | HS_PB_FIFO_ADDR_OFFSET; |
492 | reg_fifo_addr = ACP_HS_TX_FIFOADDR; |
493 | reg_fifo_size = ACP_HS_TX_FIFOSIZE; |
494 | |
495 | phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset; |
496 | writel(val: phy_addr, addr: adata->acp_base + ACP_HS_TX_RINGBUFADDR); |
497 | } else { |
498 | reg_dma_size = ACP_HS_RX_DMA_SIZE; |
499 | acp_fifo_addr = rsrc->sram_pte_offset + |
500 | HS_CAPT_FIFO_ADDR_OFFSET; |
501 | reg_fifo_addr = ACP_HS_RX_FIFOADDR; |
502 | reg_fifo_size = ACP_HS_RX_FIFOSIZE; |
503 | |
504 | phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset; |
505 | writel(val: phy_addr, addr: adata->acp_base + ACP_HS_RX_RINGBUFADDR); |
506 | } |
507 | break; |
508 | default: |
509 | dev_err(dev, "Invalid dai id %x\n" , dai->driver->id); |
510 | return -EINVAL; |
511 | } |
512 | |
513 | writel(DMA_SIZE, addr: adata->acp_base + reg_dma_size); |
514 | writel(val: acp_fifo_addr, addr: adata->acp_base + reg_fifo_addr); |
515 | writel(FIFO_SIZE, addr: adata->acp_base + reg_fifo_size); |
516 | |
517 | ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); |
518 | ext_int_ctrl |= BIT(I2S_RX_THRESHOLD(rsrc->offset)) | |
519 | BIT(BT_RX_THRESHOLD(rsrc->offset)) | |
520 | BIT(I2S_TX_THRESHOLD(rsrc->offset)) | |
521 | BIT(BT_TX_THRESHOLD(rsrc->offset)) | |
522 | BIT(HS_RX_THRESHOLD(rsrc->offset)) | |
523 | BIT(HS_TX_THRESHOLD(rsrc->offset)); |
524 | |
525 | writel(val: ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); |
526 | |
527 | return 0; |
528 | } |
529 | |
530 | static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) |
531 | { |
532 | struct acp_stream *stream = substream->runtime->private_data; |
533 | struct device *dev = dai->component->dev; |
534 | struct acp_dev_data *adata = dev_get_drvdata(dev); |
535 | struct acp_resource *rsrc = adata->rsrc; |
536 | unsigned int dir = substream->stream; |
537 | unsigned int irq_bit = 0; |
538 | |
539 | switch (dai->driver->id) { |
540 | case I2S_SP_INSTANCE: |
541 | if (dir == SNDRV_PCM_STREAM_PLAYBACK) { |
542 | irq_bit = BIT(I2S_TX_THRESHOLD(rsrc->offset)); |
543 | stream->pte_offset = ACP_SRAM_SP_PB_PTE_OFFSET; |
544 | stream->fifo_offset = SP_PB_FIFO_ADDR_OFFSET; |
545 | } else { |
546 | irq_bit = BIT(I2S_RX_THRESHOLD(rsrc->offset)); |
547 | stream->pte_offset = ACP_SRAM_SP_CP_PTE_OFFSET; |
548 | stream->fifo_offset = SP_CAPT_FIFO_ADDR_OFFSET; |
549 | } |
550 | break; |
551 | case I2S_BT_INSTANCE: |
552 | if (dir == SNDRV_PCM_STREAM_PLAYBACK) { |
553 | irq_bit = BIT(BT_TX_THRESHOLD(rsrc->offset)); |
554 | stream->pte_offset = ACP_SRAM_BT_PB_PTE_OFFSET; |
555 | stream->fifo_offset = BT_PB_FIFO_ADDR_OFFSET; |
556 | } else { |
557 | irq_bit = BIT(BT_RX_THRESHOLD(rsrc->offset)); |
558 | stream->pte_offset = ACP_SRAM_BT_CP_PTE_OFFSET; |
559 | stream->fifo_offset = BT_CAPT_FIFO_ADDR_OFFSET; |
560 | } |
561 | break; |
562 | case I2S_HS_INSTANCE: |
563 | if (dir == SNDRV_PCM_STREAM_PLAYBACK) { |
564 | irq_bit = BIT(HS_TX_THRESHOLD(rsrc->offset)); |
565 | stream->pte_offset = ACP_SRAM_HS_PB_PTE_OFFSET; |
566 | stream->fifo_offset = HS_PB_FIFO_ADDR_OFFSET; |
567 | } else { |
568 | irq_bit = BIT(HS_RX_THRESHOLD(rsrc->offset)); |
569 | stream->pte_offset = ACP_SRAM_HS_CP_PTE_OFFSET; |
570 | stream->fifo_offset = HS_CAPT_FIFO_ADDR_OFFSET; |
571 | } |
572 | break; |
573 | default: |
574 | dev_err(dev, "Invalid dai id %x\n" , dai->driver->id); |
575 | return -EINVAL; |
576 | } |
577 | |
578 | /* Save runtime dai configuration in stream */ |
579 | stream->id = dai->driver->id + dir; |
580 | stream->dai_id = dai->driver->id; |
581 | stream->irq_bit = irq_bit; |
582 | stream->dir = substream->stream; |
583 | |
584 | return 0; |
585 | } |
586 | |
587 | static int acp_i2s_probe(struct snd_soc_dai *dai) |
588 | { |
589 | struct device *dev = dai->component->dev; |
590 | struct acp_dev_data *adata = dev_get_drvdata(dev); |
591 | struct acp_resource *rsrc = adata->rsrc; |
592 | unsigned int val; |
593 | |
594 | if (!adata->acp_base) { |
595 | dev_err(dev, "I2S base is NULL\n" ); |
596 | return -EINVAL; |
597 | } |
598 | |
599 | val = readl(addr: adata->acp_base + rsrc->i2s_pin_cfg_offset); |
600 | if (val != rsrc->i2s_mode) { |
601 | dev_err(dev, "I2S Mode not supported val %x\n" , val); |
602 | return -EINVAL; |
603 | } |
604 | |
605 | return 0; |
606 | } |
607 | |
608 | const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { |
609 | .probe = acp_i2s_probe, |
610 | .startup = acp_i2s_startup, |
611 | .hw_params = acp_i2s_hwparams, |
612 | .prepare = acp_i2s_prepare, |
613 | .trigger = acp_i2s_trigger, |
614 | .set_fmt = acp_i2s_set_fmt, |
615 | .set_tdm_slot = acp_i2s_set_tdm_slot, |
616 | }; |
617 | EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, SND_SOC_ACP_COMMON); |
618 | |
619 | MODULE_LICENSE("Dual BSD/GPL" ); |
620 | MODULE_ALIAS(DRV_NAME); |
621 | |