1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Loongson ALSA SoC Platform (DMA) driver |
4 | // |
5 | // Copyright (C) 2023 Loongson Technology Corporation Limited |
6 | // Author: Yingkun Meng <mengyingkun@loongson.cn> |
7 | // |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/io-64-nonatomic-lo-hi.h> |
11 | #include <linux/delay.h> |
12 | #include <linux/pm_runtime.h> |
13 | #include <linux/dma-mapping.h> |
14 | #include <sound/soc.h> |
15 | #include <sound/pcm.h> |
16 | #include <sound/pcm_params.h> |
17 | #include "loongson_i2s.h" |
18 | |
19 | /* DMA dma_order Register */ |
20 | #define DMA_ORDER_STOP (1 << 4) /* DMA stop */ |
21 | #define DMA_ORDER_START (1 << 3) /* DMA start */ |
22 | #define DMA_ORDER_ASK_VALID (1 << 2) /* DMA ask valid flag */ |
23 | #define DMA_ORDER_AXI_UNCO (1 << 1) /* Uncache access */ |
24 | #define DMA_ORDER_ADDR_64 (1 << 0) /* 64bits address support */ |
25 | |
26 | #define DMA_ORDER_ASK_MASK (~0x1fUL) /* Ask addr mask */ |
27 | #define DMA_ORDER_CTRL_MASK (0x0fUL) /* Control mask */ |
28 | |
29 | /* |
30 | * DMA registers descriptor. |
31 | */ |
32 | struct loongson_dma_desc { |
33 | u32 order; /* Next descriptor address register */ |
34 | u32 saddr; /* Source address register */ |
35 | u32 daddr; /* Device address register */ |
36 | u32 length; /* Total length register */ |
37 | u32 step_length; /* Memory stride register */ |
38 | u32 step_times; /* Repeat time register */ |
39 | u32 cmd; /* Command register */ |
40 | u32 stats; /* Status register */ |
41 | u32 order_hi; /* Next descriptor high address register */ |
42 | u32 saddr_hi; /* High source address register */ |
43 | u32 res[6]; /* Reserved */ |
44 | } __packed; |
45 | |
46 | struct loongson_runtime_data { |
47 | struct loongson_dma_data *dma_data; |
48 | |
49 | struct loongson_dma_desc *dma_desc_arr; |
50 | dma_addr_t dma_desc_arr_phy; |
51 | int dma_desc_arr_size; |
52 | |
53 | struct loongson_dma_desc *dma_pos_desc; |
54 | dma_addr_t dma_pos_desc_phy; |
55 | }; |
56 | |
57 | static const struct snd_pcm_hardware ls_pcm_hardware = { |
58 | .info = SNDRV_PCM_INFO_MMAP | |
59 | SNDRV_PCM_INFO_INTERLEAVED | |
60 | SNDRV_PCM_INFO_MMAP_VALID | |
61 | SNDRV_PCM_INFO_RESUME | |
62 | SNDRV_PCM_INFO_PAUSE, |
63 | .formats = (SNDRV_PCM_FMTBIT_S8 | |
64 | SNDRV_PCM_FMTBIT_S16_LE | |
65 | SNDRV_PCM_FMTBIT_S20_3LE | |
66 | SNDRV_PCM_FMTBIT_S24_LE), |
67 | .period_bytes_min = 128, |
68 | .period_bytes_max = 128 * 1024, |
69 | .periods_min = 1, |
70 | .periods_max = PAGE_SIZE / sizeof(struct loongson_dma_desc), |
71 | .buffer_bytes_max = 1024 * 1024, |
72 | }; |
73 | |
74 | static struct |
75 | loongson_dma_desc *dma_desc_save(struct loongson_runtime_data *prtd) |
76 | { |
77 | void __iomem *order_reg = prtd->dma_data->order_addr; |
78 | u64 val; |
79 | |
80 | val = (u64)prtd->dma_pos_desc_phy & DMA_ORDER_ASK_MASK; |
81 | val |= (readq(addr: order_reg) & DMA_ORDER_CTRL_MASK); |
82 | val |= DMA_ORDER_ASK_VALID; |
83 | writeq(val, addr: order_reg); |
84 | |
85 | while (readl(addr: order_reg) & DMA_ORDER_ASK_VALID) |
86 | udelay(2); |
87 | |
88 | return prtd->dma_pos_desc; |
89 | } |
90 | |
91 | static int loongson_pcm_trigger(struct snd_soc_component *component, |
92 | struct snd_pcm_substream *substream, int cmd) |
93 | { |
94 | struct loongson_runtime_data *prtd = substream->runtime->private_data; |
95 | struct device *dev = substream->pcm->card->dev; |
96 | void __iomem *order_reg = prtd->dma_data->order_addr; |
97 | u64 val; |
98 | int ret = 0; |
99 | |
100 | switch (cmd) { |
101 | case SNDRV_PCM_TRIGGER_START: |
102 | case SNDRV_PCM_TRIGGER_RESUME: |
103 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
104 | val = prtd->dma_pos_desc_phy & DMA_ORDER_ASK_MASK; |
105 | if (dev->coherent_dma_mask == DMA_BIT_MASK(64)) |
106 | val |= DMA_ORDER_ADDR_64; |
107 | else |
108 | val &= ~DMA_ORDER_ADDR_64; |
109 | val |= (readq(addr: order_reg) & DMA_ORDER_CTRL_MASK); |
110 | val |= DMA_ORDER_START; |
111 | writeq(val, addr: order_reg); |
112 | |
113 | while ((readl(addr: order_reg) & DMA_ORDER_START)) |
114 | udelay(2); |
115 | break; |
116 | case SNDRV_PCM_TRIGGER_STOP: |
117 | case SNDRV_PCM_TRIGGER_SUSPEND: |
118 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
119 | dma_desc_save(prtd); |
120 | |
121 | /* dma stop */ |
122 | val = readq(addr: order_reg) | DMA_ORDER_STOP; |
123 | writeq(val, addr: order_reg); |
124 | udelay(1000); |
125 | |
126 | break; |
127 | default: |
128 | dev_err(dev, "Invalid pcm trigger operation\n" ); |
129 | return -EINVAL; |
130 | } |
131 | |
132 | return ret; |
133 | } |
134 | |
135 | static int loongson_pcm_hw_params(struct snd_soc_component *component, |
136 | struct snd_pcm_substream *substream, |
137 | struct snd_pcm_hw_params *params) |
138 | { |
139 | struct snd_pcm_runtime *runtime = substream->runtime; |
140 | struct device *dev = substream->pcm->card->dev; |
141 | struct loongson_runtime_data *prtd = runtime->private_data; |
142 | size_t buf_len = params_buffer_bytes(p: params); |
143 | size_t period_len = params_period_bytes(p: params); |
144 | dma_addr_t order_addr, mem_addr; |
145 | struct loongson_dma_desc *desc; |
146 | u32 num_periods; |
147 | int i; |
148 | |
149 | if (buf_len % period_len) { |
150 | dev_err(dev, "buf len not multiply of period len\n" ); |
151 | return -EINVAL; |
152 | } |
153 | |
154 | num_periods = buf_len / period_len; |
155 | if (!num_periods || num_periods > prtd->dma_desc_arr_size) { |
156 | dev_err(dev, "dma data too small or too big\n" ); |
157 | return -EINVAL; |
158 | } |
159 | |
160 | snd_pcm_set_runtime_buffer(substream, bufp: &substream->dma_buffer); |
161 | runtime->dma_bytes = buf_len; |
162 | |
163 | /* initialize dma descriptor array */ |
164 | mem_addr = runtime->dma_addr; |
165 | order_addr = prtd->dma_desc_arr_phy; |
166 | for (i = 0; i < num_periods; i++) { |
167 | desc = &prtd->dma_desc_arr[i]; |
168 | |
169 | /* next descriptor physical address */ |
170 | order_addr += sizeof(*desc); |
171 | desc->order = lower_32_bits(order_addr | BIT(0)); |
172 | desc->order_hi = upper_32_bits(order_addr); |
173 | |
174 | desc->saddr = lower_32_bits(mem_addr); |
175 | desc->saddr_hi = upper_32_bits(mem_addr); |
176 | desc->daddr = prtd->dma_data->dev_addr; |
177 | |
178 | desc->cmd = BIT(0); |
179 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
180 | desc->cmd |= BIT(12); |
181 | |
182 | desc->length = period_len >> 2; |
183 | desc->step_length = 0; |
184 | desc->step_times = 1; |
185 | |
186 | mem_addr += period_len; |
187 | } |
188 | desc = &prtd->dma_desc_arr[num_periods - 1]; |
189 | desc->order = lower_32_bits(prtd->dma_desc_arr_phy | BIT(0)); |
190 | desc->order_hi = upper_32_bits(prtd->dma_desc_arr_phy); |
191 | |
192 | /* init position descriptor */ |
193 | *prtd->dma_pos_desc = *prtd->dma_desc_arr; |
194 | |
195 | return 0; |
196 | } |
197 | |
198 | static snd_pcm_uframes_t |
199 | loongson_pcm_pointer(struct snd_soc_component *component, |
200 | struct snd_pcm_substream *substream) |
201 | { |
202 | struct snd_pcm_runtime *runtime = substream->runtime; |
203 | struct loongson_runtime_data *prtd = runtime->private_data; |
204 | struct loongson_dma_desc *desc; |
205 | snd_pcm_uframes_t x; |
206 | u64 addr; |
207 | |
208 | desc = dma_desc_save(prtd); |
209 | addr = ((u64)desc->saddr_hi << 32) | desc->saddr; |
210 | |
211 | x = bytes_to_frames(runtime, size: addr - runtime->dma_addr); |
212 | if (x == runtime->buffer_size) |
213 | x = 0; |
214 | return x; |
215 | } |
216 | |
217 | static irqreturn_t loongson_pcm_dma_irq(int irq, void *devid) |
218 | { |
219 | struct snd_pcm_substream *substream = devid; |
220 | |
221 | snd_pcm_period_elapsed(substream); |
222 | return IRQ_HANDLED; |
223 | } |
224 | |
225 | static int loongson_pcm_open(struct snd_soc_component *component, |
226 | struct snd_pcm_substream *substream) |
227 | { |
228 | struct snd_pcm_runtime *runtime = substream->runtime; |
229 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
230 | struct snd_card *card = substream->pcm->card; |
231 | struct loongson_runtime_data *prtd; |
232 | struct loongson_dma_data *dma_data; |
233 | int ret; |
234 | |
235 | /* |
236 | * For mysterious reasons (and despite what the manual says) |
237 | * playback samples are lost if the DMA count is not a multiple |
238 | * of the DMA burst size. Let's add a rule to enforce that. |
239 | */ |
240 | snd_pcm_hw_constraint_step(runtime, cond: 0, |
241 | SNDRV_PCM_HW_PARAM_PERIOD_BYTES, step: 128); |
242 | snd_pcm_hw_constraint_step(runtime, cond: 0, |
243 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, step: 128); |
244 | snd_pcm_hw_constraint_integer(runtime: substream->runtime, |
245 | SNDRV_PCM_HW_PARAM_PERIODS); |
246 | snd_soc_set_runtime_hwparams(substream, hw: &ls_pcm_hardware); |
247 | |
248 | prtd = kzalloc(size: sizeof(*prtd), GFP_KERNEL); |
249 | if (!prtd) |
250 | return -ENOMEM; |
251 | |
252 | prtd->dma_desc_arr = dma_alloc_coherent(dev: card->dev, PAGE_SIZE, |
253 | dma_handle: &prtd->dma_desc_arr_phy, |
254 | GFP_KERNEL); |
255 | if (!prtd->dma_desc_arr) { |
256 | ret = -ENOMEM; |
257 | goto desc_err; |
258 | } |
259 | prtd->dma_desc_arr_size = PAGE_SIZE / sizeof(*prtd->dma_desc_arr); |
260 | |
261 | prtd->dma_pos_desc = dma_alloc_coherent(dev: card->dev, |
262 | size: sizeof(*prtd->dma_pos_desc), |
263 | dma_handle: &prtd->dma_pos_desc_phy, |
264 | GFP_KERNEL); |
265 | if (!prtd->dma_pos_desc) { |
266 | ret = -ENOMEM; |
267 | goto pos_err; |
268 | } |
269 | |
270 | dma_data = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream); |
271 | prtd->dma_data = dma_data; |
272 | |
273 | substream->runtime->private_data = prtd; |
274 | |
275 | return 0; |
276 | pos_err: |
277 | dma_free_coherent(dev: card->dev, PAGE_SIZE, cpu_addr: prtd->dma_desc_arr, |
278 | dma_handle: prtd->dma_desc_arr_phy); |
279 | desc_err: |
280 | kfree(objp: prtd); |
281 | |
282 | return ret; |
283 | } |
284 | |
285 | static int loongson_pcm_close(struct snd_soc_component *component, |
286 | struct snd_pcm_substream *substream) |
287 | { |
288 | struct snd_card *card = substream->pcm->card; |
289 | struct loongson_runtime_data *prtd = substream->runtime->private_data; |
290 | |
291 | dma_free_coherent(dev: card->dev, PAGE_SIZE, cpu_addr: prtd->dma_desc_arr, |
292 | dma_handle: prtd->dma_desc_arr_phy); |
293 | |
294 | dma_free_coherent(dev: card->dev, size: sizeof(*prtd->dma_pos_desc), |
295 | cpu_addr: prtd->dma_pos_desc, dma_handle: prtd->dma_pos_desc_phy); |
296 | |
297 | kfree(objp: prtd); |
298 | return 0; |
299 | } |
300 | |
301 | static int loongson_pcm_mmap(struct snd_soc_component *component, |
302 | struct snd_pcm_substream *substream, |
303 | struct vm_area_struct *vma) |
304 | { |
305 | return remap_pfn_range(vma, addr: vma->vm_start, |
306 | pfn: substream->dma_buffer.addr >> PAGE_SHIFT, |
307 | size: vma->vm_end - vma->vm_start, vma->vm_page_prot); |
308 | } |
309 | |
310 | static int loongson_pcm_new(struct snd_soc_component *component, |
311 | struct snd_soc_pcm_runtime *rtd) |
312 | { |
313 | struct snd_card *card = rtd->card->snd_card; |
314 | struct snd_pcm_substream *substream; |
315 | struct loongson_dma_data *dma_data; |
316 | unsigned int i; |
317 | int ret; |
318 | |
319 | for_each_pcm_streams(i) { |
320 | substream = rtd->pcm->streams[i].substream; |
321 | if (!substream) |
322 | continue; |
323 | |
324 | dma_data = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), |
325 | substream); |
326 | ret = devm_request_irq(dev: card->dev, irq: dma_data->irq, |
327 | handler: loongson_pcm_dma_irq, |
328 | IRQF_TRIGGER_HIGH, LS_I2S_DRVNAME, |
329 | dev_id: substream); |
330 | if (ret < 0) { |
331 | dev_err(card->dev, "request irq for DMA failed\n" ); |
332 | return ret; |
333 | } |
334 | } |
335 | |
336 | return snd_pcm_set_fixed_buffer_all(pcm: rtd->pcm, SNDRV_DMA_TYPE_DEV, |
337 | data: card->dev, |
338 | size: ls_pcm_hardware.buffer_bytes_max); |
339 | } |
340 | |
341 | const struct snd_soc_component_driver loongson_i2s_component = { |
342 | .name = LS_I2S_DRVNAME, |
343 | .open = loongson_pcm_open, |
344 | .close = loongson_pcm_close, |
345 | .hw_params = loongson_pcm_hw_params, |
346 | .trigger = loongson_pcm_trigger, |
347 | .pointer = loongson_pcm_pointer, |
348 | .mmap = loongson_pcm_mmap, |
349 | .pcm_construct = loongson_pcm_new, |
350 | }; |
351 | |