1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * AMD ALSA SoC Pink Sardine SoundWire DMA Driver |
4 | * |
5 | * Copyright 2023 Advanced Micro Devices, Inc. |
6 | */ |
7 | |
8 | #include <linux/err.h> |
9 | #include <linux/io.h> |
10 | #include <linux/module.h> |
11 | #include <linux/platform_device.h> |
12 | #include <sound/pcm_params.h> |
13 | #include <sound/soc.h> |
14 | #include <sound/soc-dai.h> |
15 | #include <linux/pm_runtime.h> |
16 | #include <linux/soundwire/sdw_amd.h> |
17 | #include "acp63.h" |
18 | |
19 | #define DRV_NAME "amd_ps_sdw_dma" |
20 | |
21 | static struct sdw_dma_ring_buf_reg sdw0_dma_ring_buf_reg[ACP63_SDW0_DMA_MAX_STREAMS] = { |
22 | {ACP_AUDIO0_TX_DMA_SIZE, ACP_AUDIO0_TX_FIFOADDR, ACP_AUDIO0_TX_FIFOSIZE, |
23 | ACP_AUDIO0_TX_RINGBUFSIZE, ACP_AUDIO0_TX_RINGBUFADDR, ACP_AUDIO0_TX_INTR_WATERMARK_SIZE, |
24 | ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH}, |
25 | {ACP_AUDIO1_TX_DMA_SIZE, ACP_AUDIO1_TX_FIFOADDR, ACP_AUDIO1_TX_FIFOSIZE, |
26 | ACP_AUDIO1_TX_RINGBUFSIZE, ACP_AUDIO1_TX_RINGBUFADDR, ACP_AUDIO1_TX_INTR_WATERMARK_SIZE, |
27 | ACP_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH}, |
28 | {ACP_AUDIO2_TX_DMA_SIZE, ACP_AUDIO2_TX_FIFOADDR, ACP_AUDIO2_TX_FIFOSIZE, |
29 | ACP_AUDIO2_TX_RINGBUFSIZE, ACP_AUDIO2_TX_RINGBUFADDR, ACP_AUDIO2_TX_INTR_WATERMARK_SIZE, |
30 | ACP_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH}, |
31 | {ACP_AUDIO0_RX_DMA_SIZE, ACP_AUDIO0_RX_FIFOADDR, ACP_AUDIO0_RX_FIFOSIZE, |
32 | ACP_AUDIO0_RX_RINGBUFSIZE, ACP_AUDIO0_RX_RINGBUFADDR, ACP_AUDIO0_RX_INTR_WATERMARK_SIZE, |
33 | ACP_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH}, |
34 | {ACP_AUDIO1_RX_DMA_SIZE, ACP_AUDIO1_RX_FIFOADDR, ACP_AUDIO1_RX_FIFOSIZE, |
35 | ACP_AUDIO1_RX_RINGBUFSIZE, ACP_AUDIO1_RX_RINGBUFADDR, ACP_AUDIO1_RX_INTR_WATERMARK_SIZE, |
36 | ACP_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH}, |
37 | {ACP_AUDIO2_RX_DMA_SIZE, ACP_AUDIO2_RX_FIFOADDR, ACP_AUDIO2_RX_FIFOSIZE, |
38 | ACP_AUDIO2_RX_RINGBUFSIZE, ACP_AUDIO2_RX_RINGBUFADDR, ACP_AUDIO2_RX_INTR_WATERMARK_SIZE, |
39 | ACP_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH} |
40 | }; |
41 | |
42 | /* |
43 | * SDW1 instance supports one TX stream and one RX stream. |
44 | * For TX/RX streams DMA registers programming for SDW1 instance, it uses ACP_P1_AUDIO1 register |
45 | * set as per hardware register documentation |
46 | */ |
47 | static struct sdw_dma_ring_buf_reg sdw1_dma_ring_buf_reg[ACP63_SDW1_DMA_MAX_STREAMS] = { |
48 | {ACP_P1_AUDIO1_TX_DMA_SIZE, ACP_P1_AUDIO1_TX_FIFOADDR, ACP_P1_AUDIO1_TX_FIFOSIZE, |
49 | ACP_P1_AUDIO1_TX_RINGBUFSIZE, ACP_P1_AUDIO1_TX_RINGBUFADDR, |
50 | ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE, |
51 | ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH}, |
52 | {ACP_P1_AUDIO1_RX_DMA_SIZE, ACP_P1_AUDIO1_RX_FIFOADDR, ACP_P1_AUDIO1_RX_FIFOSIZE, |
53 | ACP_P1_AUDIO1_RX_RINGBUFSIZE, ACP_P1_AUDIO1_RX_RINGBUFADDR, |
54 | ACP_P1_AUDIO1_RX_INTR_WATERMARK_SIZE, |
55 | ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH}, |
56 | }; |
57 | |
58 | static u32 sdw0_dma_enable_reg[ACP63_SDW0_DMA_MAX_STREAMS] = { |
59 | ACP_SW0_AUDIO0_TX_EN, |
60 | ACP_SW0_AUDIO1_TX_EN, |
61 | ACP_SW0_AUDIO2_TX_EN, |
62 | ACP_SW0_AUDIO0_RX_EN, |
63 | ACP_SW0_AUDIO1_RX_EN, |
64 | ACP_SW0_AUDIO2_RX_EN, |
65 | }; |
66 | |
67 | /* |
68 | * SDW1 instance supports one TX stream and one RX stream. |
69 | * For TX/RX streams DMA enable register programming for SDW1 instance, |
70 | * it uses ACP_SW1_AUDIO1_TX_EN and ACP_SW1_AUDIO1_RX_EN registers |
71 | * as per hardware register documentation. |
72 | */ |
73 | static u32 sdw1_dma_enable_reg[ACP63_SDW1_DMA_MAX_STREAMS] = { |
74 | ACP_SW1_AUDIO1_TX_EN, |
75 | ACP_SW1_AUDIO1_RX_EN, |
76 | }; |
77 | |
78 | static const struct snd_pcm_hardware acp63_sdw_hardware_playback = { |
79 | .info = SNDRV_PCM_INFO_INTERLEAVED | |
80 | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
81 | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | |
82 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, |
83 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | |
84 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, |
85 | .channels_min = 2, |
86 | .channels_max = 2, |
87 | .rates = SNDRV_PCM_RATE_48000, |
88 | .rate_min = 48000, |
89 | .rate_max = 48000, |
90 | .buffer_bytes_max = SDW_PLAYBACK_MAX_NUM_PERIODS * SDW_PLAYBACK_MAX_PERIOD_SIZE, |
91 | .period_bytes_min = SDW_PLAYBACK_MIN_PERIOD_SIZE, |
92 | .period_bytes_max = SDW_PLAYBACK_MAX_PERIOD_SIZE, |
93 | .periods_min = SDW_PLAYBACK_MIN_NUM_PERIODS, |
94 | .periods_max = SDW_PLAYBACK_MAX_NUM_PERIODS, |
95 | }; |
96 | |
97 | static const struct snd_pcm_hardware acp63_sdw_hardware_capture = { |
98 | .info = SNDRV_PCM_INFO_INTERLEAVED | |
99 | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
100 | SNDRV_PCM_INFO_MMAP | |
101 | SNDRV_PCM_INFO_MMAP_VALID | |
102 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, |
103 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | |
104 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, |
105 | .channels_min = 2, |
106 | .channels_max = 2, |
107 | .rates = SNDRV_PCM_RATE_48000, |
108 | .rate_min = 48000, |
109 | .rate_max = 48000, |
110 | .buffer_bytes_max = SDW_CAPTURE_MAX_NUM_PERIODS * SDW_CAPTURE_MAX_PERIOD_SIZE, |
111 | .period_bytes_min = SDW_CAPTURE_MIN_PERIOD_SIZE, |
112 | .period_bytes_max = SDW_CAPTURE_MAX_PERIOD_SIZE, |
113 | .periods_min = SDW_CAPTURE_MIN_NUM_PERIODS, |
114 | .periods_max = SDW_CAPTURE_MAX_NUM_PERIODS, |
115 | }; |
116 | |
117 | static void acp63_enable_disable_sdw_dma_interrupts(void __iomem *acp_base, bool enable) |
118 | { |
119 | u32 ext_intr_cntl, ext_intr_cntl1; |
120 | u32 irq_mask = ACP_SDW_DMA_IRQ_MASK; |
121 | u32 irq_mask1 = ACP_P1_SDW_DMA_IRQ_MASK; |
122 | |
123 | if (enable) { |
124 | ext_intr_cntl = readl(addr: acp_base + ACP_EXTERNAL_INTR_CNTL); |
125 | ext_intr_cntl |= irq_mask; |
126 | writel(val: ext_intr_cntl, addr: acp_base + ACP_EXTERNAL_INTR_CNTL); |
127 | ext_intr_cntl1 = readl(addr: acp_base + ACP_EXTERNAL_INTR_CNTL1); |
128 | ext_intr_cntl1 |= irq_mask1; |
129 | writel(val: ext_intr_cntl1, addr: acp_base + ACP_EXTERNAL_INTR_CNTL1); |
130 | } else { |
131 | ext_intr_cntl = readl(addr: acp_base + ACP_EXTERNAL_INTR_CNTL); |
132 | ext_intr_cntl &= ~irq_mask; |
133 | writel(val: ext_intr_cntl, addr: acp_base + ACP_EXTERNAL_INTR_CNTL); |
134 | ext_intr_cntl1 = readl(addr: acp_base + ACP_EXTERNAL_INTR_CNTL1); |
135 | ext_intr_cntl1 &= ~irq_mask1; |
136 | writel(val: ext_intr_cntl1, addr: acp_base + ACP_EXTERNAL_INTR_CNTL1); |
137 | } |
138 | } |
139 | |
140 | static void acp63_config_dma(struct acp_sdw_dma_stream *stream, void __iomem *acp_base, |
141 | u32 stream_id) |
142 | { |
143 | u16 page_idx; |
144 | u32 low, high, val; |
145 | u32 sdw_dma_pte_offset; |
146 | dma_addr_t addr; |
147 | |
148 | addr = stream->dma_addr; |
149 | sdw_dma_pte_offset = SDW_PTE_OFFSET(stream->instance); |
150 | val = sdw_dma_pte_offset + (stream_id * ACP_SDW_PTE_OFFSET); |
151 | |
152 | /* Group Enable */ |
153 | writel(ACP_SDW_SRAM_PTE_OFFSET | BIT(31), addr: acp_base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_2); |
154 | writel(PAGE_SIZE_4K_ENABLE, addr: acp_base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2); |
155 | for (page_idx = 0; page_idx < stream->num_pages; page_idx++) { |
156 | /* Load the low address of page int ACP SRAM through SRBM */ |
157 | low = lower_32_bits(addr); |
158 | high = upper_32_bits(addr); |
159 | |
160 | writel(val: low, addr: acp_base + ACP_SCRATCH_REG_0 + val); |
161 | high |= BIT(31); |
162 | writel(val: high, addr: acp_base + ACP_SCRATCH_REG_0 + val + 4); |
163 | val += 8; |
164 | addr += PAGE_SIZE; |
165 | } |
166 | writel(val: 0x1, addr: acp_base + ACPAXI2AXI_ATU_CTRL); |
167 | } |
168 | |
169 | static int acp63_configure_sdw_ringbuffer(void __iomem *acp_base, u32 stream_id, u32 size, |
170 | u32 manager_instance) |
171 | { |
172 | u32 reg_dma_size; |
173 | u32 reg_fifo_addr; |
174 | u32 reg_fifo_size; |
175 | u32 reg_ring_buf_size; |
176 | u32 reg_ring_buf_addr; |
177 | u32 sdw_fifo_addr; |
178 | u32 sdw_fifo_offset; |
179 | u32 sdw_ring_buf_addr; |
180 | u32 sdw_ring_buf_size; |
181 | u32 sdw_mem_window_offset; |
182 | |
183 | switch (manager_instance) { |
184 | case ACP_SDW0: |
185 | reg_dma_size = sdw0_dma_ring_buf_reg[stream_id].reg_dma_size; |
186 | reg_fifo_addr = sdw0_dma_ring_buf_reg[stream_id].reg_fifo_addr; |
187 | reg_fifo_size = sdw0_dma_ring_buf_reg[stream_id].reg_fifo_size; |
188 | reg_ring_buf_size = sdw0_dma_ring_buf_reg[stream_id].reg_ring_buf_size; |
189 | reg_ring_buf_addr = sdw0_dma_ring_buf_reg[stream_id].reg_ring_buf_addr; |
190 | break; |
191 | case ACP_SDW1: |
192 | reg_dma_size = sdw1_dma_ring_buf_reg[stream_id].reg_dma_size; |
193 | reg_fifo_addr = sdw1_dma_ring_buf_reg[stream_id].reg_fifo_addr; |
194 | reg_fifo_size = sdw1_dma_ring_buf_reg[stream_id].reg_fifo_size; |
195 | reg_ring_buf_size = sdw1_dma_ring_buf_reg[stream_id].reg_ring_buf_size; |
196 | reg_ring_buf_addr = sdw1_dma_ring_buf_reg[stream_id].reg_ring_buf_addr; |
197 | break; |
198 | default: |
199 | return -EINVAL; |
200 | } |
201 | sdw_fifo_offset = ACP_SDW_FIFO_OFFSET(manager_instance); |
202 | sdw_mem_window_offset = SDW_MEM_WINDOW_START(manager_instance); |
203 | sdw_fifo_addr = sdw_fifo_offset + (stream_id * SDW_FIFO_OFFSET); |
204 | sdw_ring_buf_addr = sdw_mem_window_offset + (stream_id * ACP_SDW_RING_BUFF_ADDR_OFFSET); |
205 | sdw_ring_buf_size = size; |
206 | writel(val: sdw_ring_buf_size, addr: acp_base + reg_ring_buf_size); |
207 | writel(val: sdw_ring_buf_addr, addr: acp_base + reg_ring_buf_addr); |
208 | writel(val: sdw_fifo_addr, addr: acp_base + reg_fifo_addr); |
209 | writel(SDW_DMA_SIZE, addr: acp_base + reg_dma_size); |
210 | writel(SDW_FIFO_SIZE, addr: acp_base + reg_fifo_size); |
211 | return 0; |
212 | } |
213 | |
214 | static int acp63_sdw_dma_open(struct snd_soc_component *component, |
215 | struct snd_pcm_substream *substream) |
216 | { |
217 | struct snd_pcm_runtime *runtime; |
218 | struct acp_sdw_dma_stream *stream; |
219 | struct snd_soc_dai *cpu_dai; |
220 | struct amd_sdw_manager *amd_manager; |
221 | struct snd_soc_pcm_runtime *prtd = substream->private_data; |
222 | int ret; |
223 | |
224 | runtime = substream->runtime; |
225 | cpu_dai = snd_soc_rtd_to_cpu(prtd, 0); |
226 | amd_manager = snd_soc_dai_get_drvdata(dai: cpu_dai); |
227 | stream = kzalloc(size: sizeof(*stream), GFP_KERNEL); |
228 | if (!stream) |
229 | return -ENOMEM; |
230 | |
231 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
232 | runtime->hw = acp63_sdw_hardware_playback; |
233 | else |
234 | runtime->hw = acp63_sdw_hardware_capture; |
235 | ret = snd_pcm_hw_constraint_integer(runtime, |
236 | SNDRV_PCM_HW_PARAM_PERIODS); |
237 | if (ret < 0) { |
238 | dev_err(component->dev, "set integer constraint failed\n" ); |
239 | kfree(objp: stream); |
240 | return ret; |
241 | } |
242 | |
243 | stream->stream_id = cpu_dai->id; |
244 | stream->instance = amd_manager->instance; |
245 | runtime->private_data = stream; |
246 | return ret; |
247 | } |
248 | |
249 | static int acp63_sdw_dma_hw_params(struct snd_soc_component *component, |
250 | struct snd_pcm_substream *substream, |
251 | struct snd_pcm_hw_params *params) |
252 | { |
253 | struct acp_sdw_dma_stream *stream; |
254 | struct sdw_dma_dev_data *sdw_data; |
255 | u32 period_bytes; |
256 | u32 water_mark_size_reg; |
257 | u32 irq_mask, ext_intr_ctrl; |
258 | u64 size; |
259 | u32 stream_id; |
260 | u32 acp_ext_intr_cntl_reg; |
261 | int ret; |
262 | |
263 | sdw_data = dev_get_drvdata(dev: component->dev); |
264 | stream = substream->runtime->private_data; |
265 | if (!stream) |
266 | return -EINVAL; |
267 | stream_id = stream->stream_id; |
268 | switch (stream->instance) { |
269 | case ACP_SDW0: |
270 | sdw_data->sdw0_dma_stream[stream_id] = substream; |
271 | water_mark_size_reg = sdw0_dma_ring_buf_reg[stream_id].water_mark_size_reg; |
272 | acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL; |
273 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
274 | irq_mask = BIT(SDW0_DMA_TX_IRQ_MASK(stream_id)); |
275 | else |
276 | irq_mask = BIT(SDW0_DMA_RX_IRQ_MASK(stream_id)); |
277 | break; |
278 | case ACP_SDW1: |
279 | sdw_data->sdw1_dma_stream[stream_id] = substream; |
280 | acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1; |
281 | water_mark_size_reg = sdw1_dma_ring_buf_reg[stream_id].water_mark_size_reg; |
282 | irq_mask = BIT(SDW1_DMA_IRQ_MASK(stream_id)); |
283 | break; |
284 | default: |
285 | return -EINVAL; |
286 | } |
287 | size = params_buffer_bytes(p: params); |
288 | period_bytes = params_period_bytes(p: params); |
289 | stream->dma_addr = substream->runtime->dma_addr; |
290 | stream->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); |
291 | acp63_config_dma(stream, acp_base: sdw_data->acp_base, stream_id); |
292 | ret = acp63_configure_sdw_ringbuffer(acp_base: sdw_data->acp_base, stream_id, size, |
293 | manager_instance: stream->instance); |
294 | if (ret) { |
295 | dev_err(component->dev, "Invalid DMA channel\n" ); |
296 | return -EINVAL; |
297 | } |
298 | ext_intr_ctrl = readl(addr: sdw_data->acp_base + acp_ext_intr_cntl_reg); |
299 | ext_intr_ctrl |= irq_mask; |
300 | writel(val: ext_intr_ctrl, addr: sdw_data->acp_base + acp_ext_intr_cntl_reg); |
301 | writel(val: period_bytes, addr: sdw_data->acp_base + water_mark_size_reg); |
302 | return 0; |
303 | } |
304 | |
305 | static u64 acp63_sdw_get_byte_count(struct acp_sdw_dma_stream *stream, void __iomem *acp_base) |
306 | { |
307 | union acp_sdw_dma_count byte_count; |
308 | u32 pos_low_reg, pos_high_reg; |
309 | |
310 | byte_count.bytescount = 0; |
311 | switch (stream->instance) { |
312 | case ACP_SDW0: |
313 | pos_low_reg = sdw0_dma_ring_buf_reg[stream->stream_id].pos_low_reg; |
314 | pos_high_reg = sdw0_dma_ring_buf_reg[stream->stream_id].pos_high_reg; |
315 | break; |
316 | case ACP_SDW1: |
317 | pos_low_reg = sdw1_dma_ring_buf_reg[stream->stream_id].pos_low_reg; |
318 | pos_high_reg = sdw1_dma_ring_buf_reg[stream->stream_id].pos_high_reg; |
319 | break; |
320 | default: |
321 | goto POINTER_RETURN_BYTES; |
322 | } |
323 | if (pos_low_reg) { |
324 | byte_count.bcount.high = readl(addr: acp_base + pos_high_reg); |
325 | byte_count.bcount.low = readl(addr: acp_base + pos_low_reg); |
326 | } |
327 | POINTER_RETURN_BYTES: |
328 | return byte_count.bytescount; |
329 | } |
330 | |
331 | static snd_pcm_uframes_t acp63_sdw_dma_pointer(struct snd_soc_component *comp, |
332 | struct snd_pcm_substream *substream) |
333 | { |
334 | struct sdw_dma_dev_data *sdw_data; |
335 | struct acp_sdw_dma_stream *stream; |
336 | u32 pos, buffersize; |
337 | u64 bytescount; |
338 | |
339 | sdw_data = dev_get_drvdata(dev: comp->dev); |
340 | stream = substream->runtime->private_data; |
341 | buffersize = frames_to_bytes(runtime: substream->runtime, |
342 | size: substream->runtime->buffer_size); |
343 | bytescount = acp63_sdw_get_byte_count(stream, acp_base: sdw_data->acp_base); |
344 | if (bytescount > stream->bytescount) |
345 | bytescount -= stream->bytescount; |
346 | pos = do_div(bytescount, buffersize); |
347 | return bytes_to_frames(runtime: substream->runtime, size: pos); |
348 | } |
349 | |
350 | static int acp63_sdw_dma_new(struct snd_soc_component *component, |
351 | struct snd_soc_pcm_runtime *rtd) |
352 | { |
353 | struct device *parent = component->dev->parent; |
354 | |
355 | snd_pcm_set_managed_buffer_all(pcm: rtd->pcm, SNDRV_DMA_TYPE_DEV, |
356 | data: parent, SDW_MIN_BUFFER, SDW_MAX_BUFFER); |
357 | return 0; |
358 | } |
359 | |
360 | static int acp63_sdw_dma_close(struct snd_soc_component *component, |
361 | struct snd_pcm_substream *substream) |
362 | { |
363 | struct sdw_dma_dev_data *sdw_data; |
364 | struct acp_sdw_dma_stream *stream; |
365 | |
366 | sdw_data = dev_get_drvdata(dev: component->dev); |
367 | stream = substream->runtime->private_data; |
368 | if (!stream) |
369 | return -EINVAL; |
370 | switch (stream->instance) { |
371 | case ACP_SDW0: |
372 | sdw_data->sdw0_dma_stream[stream->stream_id] = NULL; |
373 | break; |
374 | case ACP_SDW1: |
375 | sdw_data->sdw1_dma_stream[stream->stream_id] = NULL; |
376 | break; |
377 | default: |
378 | return -EINVAL; |
379 | } |
380 | kfree(objp: stream); |
381 | return 0; |
382 | } |
383 | |
384 | static int acp63_sdw_dma_enable(struct snd_pcm_substream *substream, |
385 | void __iomem *acp_base, bool sdw_dma_enable) |
386 | { |
387 | struct acp_sdw_dma_stream *stream; |
388 | u32 stream_id; |
389 | u32 sdw_dma_en_reg; |
390 | u32 sdw_dma_en_stat_reg; |
391 | u32 sdw_dma_stat; |
392 | u32 dma_enable; |
393 | |
394 | stream = substream->runtime->private_data; |
395 | stream_id = stream->stream_id; |
396 | switch (stream->instance) { |
397 | case ACP_SDW0: |
398 | sdw_dma_en_reg = sdw0_dma_enable_reg[stream_id]; |
399 | break; |
400 | case ACP_SDW1: |
401 | sdw_dma_en_reg = sdw1_dma_enable_reg[stream_id]; |
402 | break; |
403 | default: |
404 | return -EINVAL; |
405 | } |
406 | sdw_dma_en_stat_reg = sdw_dma_en_reg + 4; |
407 | dma_enable = sdw_dma_enable; |
408 | writel(val: dma_enable, addr: acp_base + sdw_dma_en_reg); |
409 | return readl_poll_timeout(acp_base + sdw_dma_en_stat_reg, sdw_dma_stat, |
410 | (sdw_dma_stat == dma_enable), ACP_DELAY_US, ACP_COUNTER); |
411 | } |
412 | |
413 | static int acp63_sdw_dma_trigger(struct snd_soc_component *comp, |
414 | struct snd_pcm_substream *substream, |
415 | int cmd) |
416 | { |
417 | struct sdw_dma_dev_data *sdw_data; |
418 | int ret; |
419 | |
420 | sdw_data = dev_get_drvdata(dev: comp->dev); |
421 | switch (cmd) { |
422 | case SNDRV_PCM_TRIGGER_START: |
423 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
424 | case SNDRV_PCM_TRIGGER_RESUME: |
425 | ret = acp63_sdw_dma_enable(substream, acp_base: sdw_data->acp_base, sdw_dma_enable: true); |
426 | break; |
427 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
428 | case SNDRV_PCM_TRIGGER_SUSPEND: |
429 | case SNDRV_PCM_TRIGGER_STOP: |
430 | ret = acp63_sdw_dma_enable(substream, acp_base: sdw_data->acp_base, sdw_dma_enable: false); |
431 | break; |
432 | default: |
433 | ret = -EINVAL; |
434 | } |
435 | if (ret) |
436 | dev_err(comp->dev, "trigger %d failed: %d" , cmd, ret); |
437 | return ret; |
438 | } |
439 | |
440 | static const struct snd_soc_component_driver acp63_sdw_component = { |
441 | .name = DRV_NAME, |
442 | .open = acp63_sdw_dma_open, |
443 | .close = acp63_sdw_dma_close, |
444 | .hw_params = acp63_sdw_dma_hw_params, |
445 | .trigger = acp63_sdw_dma_trigger, |
446 | .pointer = acp63_sdw_dma_pointer, |
447 | .pcm_construct = acp63_sdw_dma_new, |
448 | }; |
449 | |
450 | static int acp63_sdw_platform_probe(struct platform_device *pdev) |
451 | { |
452 | struct resource *res; |
453 | struct sdw_dma_dev_data *sdw_data; |
454 | struct acp63_dev_data *acp_data; |
455 | struct device *parent; |
456 | int status; |
457 | |
458 | parent = pdev->dev.parent; |
459 | acp_data = dev_get_drvdata(dev: parent); |
460 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
461 | if (!res) { |
462 | dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n" ); |
463 | return -ENODEV; |
464 | } |
465 | |
466 | sdw_data = devm_kzalloc(dev: &pdev->dev, size: sizeof(*sdw_data), GFP_KERNEL); |
467 | if (!sdw_data) |
468 | return -ENOMEM; |
469 | |
470 | sdw_data->acp_base = devm_ioremap(dev: &pdev->dev, offset: res->start, size: resource_size(res)); |
471 | if (!sdw_data->acp_base) |
472 | return -ENOMEM; |
473 | |
474 | sdw_data->acp_lock = &acp_data->acp_lock; |
475 | dev_set_drvdata(dev: &pdev->dev, data: sdw_data); |
476 | status = devm_snd_soc_register_component(dev: &pdev->dev, |
477 | component_driver: &acp63_sdw_component, |
478 | NULL, num_dai: 0); |
479 | if (status) { |
480 | dev_err(&pdev->dev, "Fail to register sdw dma component\n" ); |
481 | return status; |
482 | } |
483 | pm_runtime_set_autosuspend_delay(dev: &pdev->dev, ACP_SUSPEND_DELAY_MS); |
484 | pm_runtime_use_autosuspend(dev: &pdev->dev); |
485 | pm_runtime_mark_last_busy(dev: &pdev->dev); |
486 | pm_runtime_set_active(dev: &pdev->dev); |
487 | pm_runtime_enable(dev: &pdev->dev); |
488 | return 0; |
489 | } |
490 | |
491 | static void acp63_sdw_platform_remove(struct platform_device *pdev) |
492 | { |
493 | pm_runtime_disable(dev: &pdev->dev); |
494 | } |
495 | |
496 | static int acp_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data) |
497 | { |
498 | struct acp_sdw_dma_stream *stream; |
499 | struct snd_pcm_substream *substream; |
500 | struct snd_pcm_runtime *runtime; |
501 | u32 period_bytes, buf_size, water_mark_size_reg; |
502 | u32 stream_count; |
503 | int index, instance, ret; |
504 | |
505 | for (instance = 0; instance < AMD_SDW_MAX_MANAGERS; instance++) { |
506 | if (instance == ACP_SDW0) |
507 | stream_count = ACP63_SDW0_DMA_MAX_STREAMS; |
508 | else |
509 | stream_count = ACP63_SDW1_DMA_MAX_STREAMS; |
510 | |
511 | for (index = 0; index < stream_count; index++) { |
512 | if (instance == ACP_SDW0) { |
513 | substream = sdw_data->sdw0_dma_stream[index]; |
514 | water_mark_size_reg = |
515 | sdw0_dma_ring_buf_reg[index].water_mark_size_reg; |
516 | } else { |
517 | substream = sdw_data->sdw1_dma_stream[index]; |
518 | water_mark_size_reg = |
519 | sdw1_dma_ring_buf_reg[index].water_mark_size_reg; |
520 | } |
521 | |
522 | if (substream && substream->runtime) { |
523 | runtime = substream->runtime; |
524 | stream = runtime->private_data; |
525 | period_bytes = frames_to_bytes(runtime, size: runtime->period_size); |
526 | buf_size = frames_to_bytes(runtime, size: runtime->buffer_size); |
527 | acp63_config_dma(stream, acp_base: sdw_data->acp_base, stream_id: index); |
528 | ret = acp63_configure_sdw_ringbuffer(acp_base: sdw_data->acp_base, stream_id: index, |
529 | size: buf_size, manager_instance: instance); |
530 | if (ret) |
531 | return ret; |
532 | writel(val: period_bytes, addr: sdw_data->acp_base + water_mark_size_reg); |
533 | } |
534 | } |
535 | } |
536 | acp63_enable_disable_sdw_dma_interrupts(acp_base: sdw_data->acp_base, enable: true); |
537 | return 0; |
538 | } |
539 | |
540 | static int __maybe_unused acp63_sdw_pcm_resume(struct device *dev) |
541 | { |
542 | struct sdw_dma_dev_data *sdw_data; |
543 | |
544 | sdw_data = dev_get_drvdata(dev); |
545 | return acp_restore_sdw_dma_config(sdw_data); |
546 | } |
547 | |
548 | static const struct dev_pm_ops acp63_pm_ops = { |
549 | SET_SYSTEM_SLEEP_PM_OPS(NULL, acp63_sdw_pcm_resume) |
550 | }; |
551 | |
552 | static struct platform_driver acp63_sdw_dma_driver = { |
553 | .probe = acp63_sdw_platform_probe, |
554 | .remove_new = acp63_sdw_platform_remove, |
555 | .driver = { |
556 | .name = "amd_ps_sdw_dma" , |
557 | .pm = &acp63_pm_ops, |
558 | }, |
559 | }; |
560 | |
561 | module_platform_driver(acp63_sdw_dma_driver); |
562 | |
563 | MODULE_AUTHOR("Vijendar.Mukunda@amd.com" ); |
564 | MODULE_DESCRIPTION("AMD ACP6.3 PS SDW DMA Driver" ); |
565 | MODULE_LICENSE("GPL" ); |
566 | MODULE_ALIAS("platform:" DRV_NAME); |
567 | |