1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Socionext UniPhier AIO Compress Audio driver. |
4 | // |
5 | // Copyright (c) 2017-2018 Socionext Inc. |
6 | |
7 | #include <linux/bitfield.h> |
8 | #include <linux/circ_buf.h> |
9 | #include <linux/dma-mapping.h> |
10 | #include <linux/errno.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/module.h> |
13 | #include <sound/core.h> |
14 | #include <sound/pcm.h> |
15 | #include <sound/soc.h> |
16 | |
17 | #include "aio.h" |
18 | |
19 | static int uniphier_aio_compr_prepare(struct snd_soc_component *component, |
20 | struct snd_compr_stream *cstream); |
21 | static int uniphier_aio_compr_hw_free(struct snd_soc_component *component, |
22 | struct snd_compr_stream *cstream); |
23 | |
24 | static int uniphier_aio_comprdma_new(struct snd_soc_pcm_runtime *rtd) |
25 | { |
26 | struct snd_compr *compr = rtd->compr; |
27 | struct device *dev = compr->card->dev; |
28 | struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0)); |
29 | struct uniphier_aio_sub *sub = &aio->sub[compr->direction]; |
30 | size_t size = AUD_RING_SIZE; |
31 | int dma_dir = DMA_FROM_DEVICE, ret; |
32 | |
33 | ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33)); |
34 | if (ret) |
35 | return ret; |
36 | |
37 | sub->compr_area = kzalloc(size, GFP_KERNEL); |
38 | if (!sub->compr_area) |
39 | return -ENOMEM; |
40 | |
41 | if (sub->swm->dir == PORT_DIR_OUTPUT) |
42 | dma_dir = DMA_TO_DEVICE; |
43 | |
44 | sub->compr_addr = dma_map_single(dev, sub->compr_area, size, dma_dir); |
45 | if (dma_mapping_error(dev, dma_addr: sub->compr_addr)) { |
46 | kfree(objp: sub->compr_area); |
47 | sub->compr_area = NULL; |
48 | |
49 | return -ENOMEM; |
50 | } |
51 | |
52 | sub->compr_bytes = size; |
53 | |
54 | return 0; |
55 | } |
56 | |
57 | static int uniphier_aio_comprdma_free(struct snd_soc_pcm_runtime *rtd) |
58 | { |
59 | struct snd_compr *compr = rtd->compr; |
60 | struct device *dev = compr->card->dev; |
61 | struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0)); |
62 | struct uniphier_aio_sub *sub = &aio->sub[compr->direction]; |
63 | int dma_dir = DMA_FROM_DEVICE; |
64 | |
65 | if (sub->swm->dir == PORT_DIR_OUTPUT) |
66 | dma_dir = DMA_TO_DEVICE; |
67 | |
68 | dma_unmap_single(dev, sub->compr_addr, sub->compr_bytes, dma_dir); |
69 | kfree(objp: sub->compr_area); |
70 | sub->compr_area = NULL; |
71 | |
72 | return 0; |
73 | } |
74 | |
75 | static int uniphier_aio_compr_open(struct snd_soc_component *component, |
76 | struct snd_compr_stream *cstream) |
77 | { |
78 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
79 | struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0)); |
80 | struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; |
81 | int ret; |
82 | |
83 | if (sub->cstream) |
84 | return -EBUSY; |
85 | |
86 | sub->cstream = cstream; |
87 | sub->pass_through = 1; |
88 | sub->use_mmap = false; |
89 | |
90 | ret = uniphier_aio_comprdma_new(rtd); |
91 | if (ret) |
92 | return ret; |
93 | |
94 | ret = aio_init(sub); |
95 | if (ret) |
96 | return ret; |
97 | |
98 | return 0; |
99 | } |
100 | |
101 | static int uniphier_aio_compr_free(struct snd_soc_component *component, |
102 | struct snd_compr_stream *cstream) |
103 | { |
104 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
105 | struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0)); |
106 | struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; |
107 | int ret; |
108 | |
109 | ret = uniphier_aio_compr_hw_free(component, cstream); |
110 | if (ret) |
111 | return ret; |
112 | ret = uniphier_aio_comprdma_free(rtd); |
113 | if (ret) |
114 | return ret; |
115 | |
116 | sub->cstream = NULL; |
117 | |
118 | return 0; |
119 | } |
120 | |
121 | static int uniphier_aio_compr_get_params(struct snd_soc_component *component, |
122 | struct snd_compr_stream *cstream, |
123 | struct snd_codec *params) |
124 | { |
125 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
126 | struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0)); |
127 | struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; |
128 | |
129 | *params = sub->cparams.codec; |
130 | |
131 | return 0; |
132 | } |
133 | |
134 | static int uniphier_aio_compr_set_params(struct snd_soc_component *component, |
135 | struct snd_compr_stream *cstream, |
136 | struct snd_compr_params *params) |
137 | { |
138 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
139 | struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0)); |
140 | struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; |
141 | struct device *dev = &aio->chip->pdev->dev; |
142 | |
143 | if (params->codec.id != SND_AUDIOCODEC_IEC61937) { |
144 | dev_err(dev, "Codec ID is not supported(%d)\n" , |
145 | params->codec.id); |
146 | return -EINVAL; |
147 | } |
148 | if (params->codec.profile != SND_AUDIOPROFILE_IEC61937_SPDIF) { |
149 | dev_err(dev, "Codec profile is not supported(%d)\n" , |
150 | params->codec.profile); |
151 | return -EINVAL; |
152 | } |
153 | |
154 | /* IEC frame type will be changed after received valid data */ |
155 | sub->iec_pc = IEC61937_PC_AAC; |
156 | |
157 | sub->cparams = *params; |
158 | sub->setting = 1; |
159 | |
160 | aio_port_reset(sub); |
161 | aio_src_reset(sub); |
162 | |
163 | return uniphier_aio_compr_prepare(component, cstream); |
164 | } |
165 | |
166 | static int uniphier_aio_compr_hw_free(struct snd_soc_component *component, |
167 | struct snd_compr_stream *cstream) |
168 | { |
169 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
170 | struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0)); |
171 | struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; |
172 | |
173 | sub->setting = 0; |
174 | |
175 | return 0; |
176 | } |
177 | |
178 | static int uniphier_aio_compr_prepare(struct snd_soc_component *component, |
179 | struct snd_compr_stream *cstream) |
180 | { |
181 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
182 | struct snd_compr_runtime *runtime = cstream->runtime; |
183 | struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0)); |
184 | struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; |
185 | int bytes = runtime->fragment_size; |
186 | unsigned long flags; |
187 | int ret; |
188 | |
189 | ret = aiodma_ch_set_param(sub); |
190 | if (ret) |
191 | return ret; |
192 | |
193 | spin_lock_irqsave(&sub->lock, flags); |
194 | ret = aiodma_rb_set_buffer(sub, start: sub->compr_addr, |
195 | end: sub->compr_addr + sub->compr_bytes, |
196 | period: bytes); |
197 | spin_unlock_irqrestore(lock: &sub->lock, flags); |
198 | if (ret) |
199 | return ret; |
200 | |
201 | ret = aio_port_set_param(sub, pass_through: sub->pass_through, params: &sub->params); |
202 | if (ret) |
203 | return ret; |
204 | ret = aio_oport_set_stream_type(sub, pc: sub->iec_pc); |
205 | if (ret) |
206 | return ret; |
207 | aio_port_set_enable(sub, enable: 1); |
208 | |
209 | ret = aio_if_set_param(sub, pass_through: sub->pass_through); |
210 | if (ret) |
211 | return ret; |
212 | |
213 | return 0; |
214 | } |
215 | |
216 | static int uniphier_aio_compr_trigger(struct snd_soc_component *component, |
217 | struct snd_compr_stream *cstream, |
218 | int cmd) |
219 | { |
220 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
221 | struct snd_compr_runtime *runtime = cstream->runtime; |
222 | struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0)); |
223 | struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; |
224 | struct device *dev = &aio->chip->pdev->dev; |
225 | int bytes = runtime->fragment_size, ret = 0; |
226 | unsigned long flags; |
227 | |
228 | spin_lock_irqsave(&sub->lock, flags); |
229 | switch (cmd) { |
230 | case SNDRV_PCM_TRIGGER_START: |
231 | aiodma_rb_sync(sub, start: sub->compr_addr, size: sub->compr_bytes, period: bytes); |
232 | aiodma_ch_set_enable(sub, enable: 1); |
233 | sub->running = 1; |
234 | |
235 | break; |
236 | case SNDRV_PCM_TRIGGER_STOP: |
237 | sub->running = 0; |
238 | aiodma_ch_set_enable(sub, enable: 0); |
239 | |
240 | break; |
241 | default: |
242 | dev_warn(dev, "Unknown trigger(%d)\n" , cmd); |
243 | ret = -EINVAL; |
244 | } |
245 | spin_unlock_irqrestore(lock: &sub->lock, flags); |
246 | |
247 | return ret; |
248 | } |
249 | |
250 | static int uniphier_aio_compr_pointer(struct snd_soc_component *component, |
251 | struct snd_compr_stream *cstream, |
252 | struct snd_compr_tstamp *tstamp) |
253 | { |
254 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
255 | struct snd_compr_runtime *runtime = cstream->runtime; |
256 | struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0)); |
257 | struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; |
258 | int bytes = runtime->fragment_size; |
259 | unsigned long flags; |
260 | u32 pos; |
261 | |
262 | spin_lock_irqsave(&sub->lock, flags); |
263 | |
264 | aiodma_rb_sync(sub, start: sub->compr_addr, size: sub->compr_bytes, period: bytes); |
265 | |
266 | if (sub->swm->dir == PORT_DIR_OUTPUT) { |
267 | pos = sub->rd_offs; |
268 | /* Size of AIO output format is double of IEC61937 */ |
269 | tstamp->copied_total = sub->rd_total / 2; |
270 | } else { |
271 | pos = sub->wr_offs; |
272 | tstamp->copied_total = sub->rd_total; |
273 | } |
274 | tstamp->byte_offset = pos; |
275 | |
276 | spin_unlock_irqrestore(lock: &sub->lock, flags); |
277 | |
278 | return 0; |
279 | } |
280 | |
281 | static int aio_compr_send_to_hw(struct uniphier_aio_sub *sub, |
282 | char __user *buf, size_t dstsize) |
283 | { |
284 | u32 __user *srcbuf = (u32 __user *)buf; |
285 | u32 *dstbuf = (u32 *)(sub->compr_area + sub->wr_offs); |
286 | int src = 0, dst = 0, ret; |
287 | u32 frm, frm_a, frm_b; |
288 | |
289 | while (dstsize > 0) { |
290 | ret = get_user(frm, srcbuf + src); |
291 | if (ret) |
292 | return ret; |
293 | src++; |
294 | |
295 | frm_a = frm & 0xffff; |
296 | frm_b = (frm >> 16) & 0xffff; |
297 | |
298 | if (frm == IEC61937_HEADER_SIGN) { |
299 | frm_a |= 0x01000000; |
300 | |
301 | /* Next data is Pc and Pd */ |
302 | sub->iec_header = true; |
303 | } else { |
304 | u16 pc = be16_to_cpu((__be16)frm_a); |
305 | |
306 | if (sub->iec_header && sub->iec_pc != pc) { |
307 | /* Force overwrite IEC frame type */ |
308 | sub->iec_pc = pc; |
309 | ret = aio_oport_set_stream_type(sub, pc); |
310 | if (ret) |
311 | return ret; |
312 | } |
313 | sub->iec_header = false; |
314 | } |
315 | dstbuf[dst++] = frm_a; |
316 | dstbuf[dst++] = frm_b; |
317 | |
318 | dstsize -= sizeof(u32) * 2; |
319 | } |
320 | |
321 | return 0; |
322 | } |
323 | |
324 | static int uniphier_aio_compr_copy(struct snd_soc_component *component, |
325 | struct snd_compr_stream *cstream, |
326 | char __user *buf, size_t count) |
327 | { |
328 | struct snd_soc_pcm_runtime *rtd = cstream->private_data; |
329 | struct snd_compr_runtime *runtime = cstream->runtime; |
330 | struct device *carddev = rtd->compr->card->dev; |
331 | struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0)); |
332 | struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; |
333 | size_t cnt = min_t(size_t, count, aio_rb_space_to_end(sub) / 2); |
334 | int bytes = runtime->fragment_size; |
335 | unsigned long flags; |
336 | size_t s; |
337 | int ret; |
338 | |
339 | if (cnt < sizeof(u32)) |
340 | return 0; |
341 | |
342 | if (sub->swm->dir == PORT_DIR_OUTPUT) { |
343 | dma_addr_t dmapos = sub->compr_addr + sub->wr_offs; |
344 | |
345 | /* Size of AIO output format is double of IEC61937 */ |
346 | s = cnt * 2; |
347 | |
348 | dma_sync_single_for_cpu(dev: carddev, addr: dmapos, size: s, dir: DMA_TO_DEVICE); |
349 | ret = aio_compr_send_to_hw(sub, buf, dstsize: s); |
350 | dma_sync_single_for_device(dev: carddev, addr: dmapos, size: s, dir: DMA_TO_DEVICE); |
351 | } else { |
352 | dma_addr_t dmapos = sub->compr_addr + sub->rd_offs; |
353 | |
354 | s = cnt; |
355 | |
356 | dma_sync_single_for_cpu(dev: carddev, addr: dmapos, size: s, dir: DMA_FROM_DEVICE); |
357 | ret = copy_to_user(to: buf, from: sub->compr_area + sub->rd_offs, n: s); |
358 | dma_sync_single_for_device(dev: carddev, addr: dmapos, size: s, dir: DMA_FROM_DEVICE); |
359 | } |
360 | if (ret) |
361 | return -EFAULT; |
362 | |
363 | spin_lock_irqsave(&sub->lock, flags); |
364 | |
365 | sub->threshold = 2 * bytes; |
366 | aiodma_rb_set_threshold(sub, size: sub->compr_bytes, th: 2 * bytes); |
367 | |
368 | if (sub->swm->dir == PORT_DIR_OUTPUT) { |
369 | sub->wr_offs += s; |
370 | if (sub->wr_offs >= sub->compr_bytes) |
371 | sub->wr_offs -= sub->compr_bytes; |
372 | } else { |
373 | sub->rd_offs += s; |
374 | if (sub->rd_offs >= sub->compr_bytes) |
375 | sub->rd_offs -= sub->compr_bytes; |
376 | } |
377 | aiodma_rb_sync(sub, start: sub->compr_addr, size: sub->compr_bytes, period: bytes); |
378 | |
379 | spin_unlock_irqrestore(lock: &sub->lock, flags); |
380 | |
381 | return cnt; |
382 | } |
383 | |
384 | static int uniphier_aio_compr_get_caps(struct snd_soc_component *component, |
385 | struct snd_compr_stream *cstream, |
386 | struct snd_compr_caps *caps) |
387 | { |
388 | caps->num_codecs = 1; |
389 | caps->min_fragment_size = AUD_MIN_FRAGMENT_SIZE; |
390 | caps->max_fragment_size = AUD_MAX_FRAGMENT_SIZE; |
391 | caps->min_fragments = AUD_MIN_FRAGMENT; |
392 | caps->max_fragments = AUD_MAX_FRAGMENT; |
393 | caps->codecs[0] = SND_AUDIOCODEC_IEC61937; |
394 | |
395 | return 0; |
396 | } |
397 | |
398 | static const struct snd_compr_codec_caps caps_iec = { |
399 | .num_descriptors = 1, |
400 | .descriptor[0].max_ch = 8, |
401 | .descriptor[0].num_sample_rates = 0, |
402 | .descriptor[0].num_bitrates = 0, |
403 | .descriptor[0].profiles = SND_AUDIOPROFILE_IEC61937_SPDIF, |
404 | .descriptor[0].modes = SND_AUDIOMODE_IEC_AC3 | |
405 | SND_AUDIOMODE_IEC_MPEG1 | |
406 | SND_AUDIOMODE_IEC_MP3 | |
407 | SND_AUDIOMODE_IEC_DTS, |
408 | .descriptor[0].formats = 0, |
409 | }; |
410 | |
411 | static int uniphier_aio_compr_get_codec_caps(struct snd_soc_component *component, |
412 | struct snd_compr_stream *stream, |
413 | struct snd_compr_codec_caps *codec) |
414 | { |
415 | if (codec->codec == SND_AUDIOCODEC_IEC61937) |
416 | *codec = caps_iec; |
417 | else |
418 | return -EINVAL; |
419 | |
420 | return 0; |
421 | } |
422 | |
423 | const struct snd_compress_ops uniphier_aio_compress_ops = { |
424 | .open = uniphier_aio_compr_open, |
425 | .free = uniphier_aio_compr_free, |
426 | .get_params = uniphier_aio_compr_get_params, |
427 | .set_params = uniphier_aio_compr_set_params, |
428 | .trigger = uniphier_aio_compr_trigger, |
429 | .pointer = uniphier_aio_compr_pointer, |
430 | .copy = uniphier_aio_compr_copy, |
431 | .get_caps = uniphier_aio_compr_get_caps, |
432 | .get_codec_caps = uniphier_aio_compr_get_codec_caps, |
433 | }; |
434 | |