This source file includes following definitions.
- sample_cpy
- cobalt_alsa_announce_pcm_data
- alsa_fnc
- snd_cobalt_pcm_capture_open
- snd_cobalt_pcm_capture_close
- snd_cobalt_pcm_ioctl
- snd_pcm_alloc_vmalloc_buffer
- snd_cobalt_pcm_hw_params
- snd_cobalt_pcm_hw_free
- snd_cobalt_pcm_prepare
- snd_cobalt_pcm_trigger
- snd_cobalt_pcm_pointer
- pb_sample_cpy
- cobalt_alsa_pb_pcm_data
- alsa_pb_fnc
- snd_cobalt_pcm_playback_open
- snd_cobalt_pcm_playback_close
- snd_cobalt_pcm_pb_prepare
- snd_cobalt_pcm_pb_trigger
- snd_cobalt_pcm_pb_pointer
- snd_pcm_get_vmalloc_page
- snd_cobalt_pcm_create
1
2
3
4
5
6
7
8
9
10 #include <linux/init.h>
11 #include <linux/kernel.h>
12 #include <linux/vmalloc.h>
13 #include <linux/delay.h>
14
15 #include <media/v4l2-device.h>
16
17 #include <sound/core.h>
18 #include <sound/pcm.h>
19
20 #include "cobalt-driver.h"
21 #include "cobalt-alsa.h"
22 #include "cobalt-alsa-pcm.h"
23
24 static unsigned int pcm_debug;
25 module_param(pcm_debug, int, 0644);
26 MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm");
27
28 #define dprintk(fmt, arg...) \
29 do { \
30 if (pcm_debug) \
31 pr_info("cobalt-alsa-pcm %s: " fmt, __func__, ##arg); \
32 } while (0)
33
34 static const struct snd_pcm_hardware snd_cobalt_hdmi_capture = {
35 .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
36 SNDRV_PCM_INFO_MMAP |
37 SNDRV_PCM_INFO_INTERLEAVED |
38 SNDRV_PCM_INFO_MMAP_VALID,
39
40 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
41
42 .rates = SNDRV_PCM_RATE_48000,
43
44 .rate_min = 48000,
45 .rate_max = 48000,
46 .channels_min = 1,
47 .channels_max = 8,
48 .buffer_bytes_max = 4 * 240 * 8 * 4,
49 .period_bytes_min = 1920,
50 .period_bytes_max = 240 * 8 * 4,
51 .periods_min = 1,
52 .periods_max = 4,
53 };
54
55 static const struct snd_pcm_hardware snd_cobalt_playback = {
56 .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
57 SNDRV_PCM_INFO_MMAP |
58 SNDRV_PCM_INFO_INTERLEAVED |
59 SNDRV_PCM_INFO_MMAP_VALID,
60
61 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
62
63 .rates = SNDRV_PCM_RATE_48000,
64
65 .rate_min = 48000,
66 .rate_max = 48000,
67 .channels_min = 1,
68 .channels_max = 8,
69 .buffer_bytes_max = 4 * 240 * 8 * 4,
70 .period_bytes_min = 1920,
71 .period_bytes_max = 240 * 8 * 4,
72 .periods_min = 1,
73 .periods_max = 4,
74 };
75
76 static void sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32)
77 {
78 static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
79 unsigned idx = 0;
80
81 while (len >= (is_s32 ? 4 : 2)) {
82 unsigned offset = map[idx] * 4;
83 u32 val = src[offset + 1] + (src[offset + 2] << 8) +
84 (src[offset + 3] << 16);
85
86 if (is_s32) {
87 *dst++ = 0;
88 *dst++ = val & 0xff;
89 }
90 *dst++ = (val >> 8) & 0xff;
91 *dst++ = (val >> 16) & 0xff;
92 len -= is_s32 ? 4 : 2;
93 idx++;
94 }
95 }
96
97 static void cobalt_alsa_announce_pcm_data(struct snd_cobalt_card *cobsc,
98 u8 *pcm_data,
99 size_t skip,
100 size_t samples)
101 {
102 struct snd_pcm_substream *substream;
103 struct snd_pcm_runtime *runtime;
104 unsigned long flags;
105 unsigned int oldptr;
106 unsigned int stride;
107 int length = samples;
108 int period_elapsed = 0;
109 bool is_s32;
110
111 dprintk("cobalt alsa announce ptr=%p data=%p num_bytes=%zd\n", cobsc,
112 pcm_data, samples);
113
114 substream = cobsc->capture_pcm_substream;
115 if (substream == NULL) {
116 dprintk("substream was NULL\n");
117 return;
118 }
119
120 runtime = substream->runtime;
121 if (runtime == NULL) {
122 dprintk("runtime was NULL\n");
123 return;
124 }
125 is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE;
126
127 stride = runtime->frame_bits >> 3;
128 if (stride == 0) {
129 dprintk("stride is zero\n");
130 return;
131 }
132
133 if (length == 0) {
134 dprintk("%s: length was zero\n", __func__);
135 return;
136 }
137
138 if (runtime->dma_area == NULL) {
139 dprintk("dma area was NULL - ignoring\n");
140 return;
141 }
142
143 oldptr = cobsc->hwptr_done_capture;
144 if (oldptr + length >= runtime->buffer_size) {
145 unsigned int cnt = runtime->buffer_size - oldptr;
146 unsigned i;
147
148 for (i = 0; i < cnt; i++)
149 sample_cpy(runtime->dma_area + (oldptr + i) * stride,
150 pcm_data + i * skip,
151 stride, is_s32);
152 for (i = cnt; i < length; i++)
153 sample_cpy(runtime->dma_area + (i - cnt) * stride,
154 pcm_data + i * skip, stride, is_s32);
155 } else {
156 unsigned i;
157
158 for (i = 0; i < length; i++)
159 sample_cpy(runtime->dma_area + (oldptr + i) * stride,
160 pcm_data + i * skip,
161 stride, is_s32);
162 }
163 snd_pcm_stream_lock_irqsave(substream, flags);
164
165 cobsc->hwptr_done_capture += length;
166 if (cobsc->hwptr_done_capture >=
167 runtime->buffer_size)
168 cobsc->hwptr_done_capture -=
169 runtime->buffer_size;
170
171 cobsc->capture_transfer_done += length;
172 if (cobsc->capture_transfer_done >=
173 runtime->period_size) {
174 cobsc->capture_transfer_done -=
175 runtime->period_size;
176 period_elapsed = 1;
177 }
178
179 snd_pcm_stream_unlock_irqrestore(substream, flags);
180
181 if (period_elapsed)
182 snd_pcm_period_elapsed(substream);
183 }
184
185 static int alsa_fnc(struct vb2_buffer *vb, void *priv)
186 {
187 struct cobalt_stream *s = priv;
188 unsigned char *p = vb2_plane_vaddr(vb, 0);
189 int i;
190
191 if (pcm_debug) {
192 pr_info("alsa: ");
193 for (i = 0; i < 8 * 4; i++) {
194 if (!(i & 3))
195 pr_cont(" ");
196 pr_cont("%02x", p[i]);
197 }
198 pr_cont("\n");
199 }
200 cobalt_alsa_announce_pcm_data(s->alsa,
201 vb2_plane_vaddr(vb, 0),
202 8 * 4,
203 vb2_get_plane_payload(vb, 0) / (8 * 4));
204 return 0;
205 }
206
207 static int snd_cobalt_pcm_capture_open(struct snd_pcm_substream *substream)
208 {
209 struct snd_pcm_runtime *runtime = substream->runtime;
210 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
211 struct cobalt_stream *s = cobsc->s;
212
213 runtime->hw = snd_cobalt_hdmi_capture;
214 snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
215 cobsc->capture_pcm_substream = substream;
216 runtime->private_data = s;
217 cobsc->alsa_record_cnt++;
218 if (cobsc->alsa_record_cnt == 1) {
219 int rc;
220
221 rc = vb2_thread_start(&s->q, alsa_fnc, s, s->vdev.name);
222 if (rc) {
223 cobsc->alsa_record_cnt--;
224 return rc;
225 }
226 }
227 return 0;
228 }
229
230 static int snd_cobalt_pcm_capture_close(struct snd_pcm_substream *substream)
231 {
232 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
233 struct cobalt_stream *s = cobsc->s;
234
235 cobsc->alsa_record_cnt--;
236 if (cobsc->alsa_record_cnt == 0)
237 vb2_thread_stop(&s->q);
238 return 0;
239 }
240
241 static int snd_cobalt_pcm_ioctl(struct snd_pcm_substream *substream,
242 unsigned int cmd, void *arg)
243 {
244 return snd_pcm_lib_ioctl(substream, cmd, arg);
245 }
246
247
248 static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
249 size_t size)
250 {
251 struct snd_pcm_runtime *runtime = subs->runtime;
252
253 dprintk("Allocating vbuffer\n");
254 if (runtime->dma_area) {
255 if (runtime->dma_bytes > size)
256 return 0;
257
258 vfree(runtime->dma_area);
259 }
260 runtime->dma_area = vmalloc(size);
261 if (!runtime->dma_area)
262 return -ENOMEM;
263
264 runtime->dma_bytes = size;
265
266 return 0;
267 }
268
269 static int snd_cobalt_pcm_hw_params(struct snd_pcm_substream *substream,
270 struct snd_pcm_hw_params *params)
271 {
272 dprintk("%s called\n", __func__);
273
274 return snd_pcm_alloc_vmalloc_buffer(substream,
275 params_buffer_bytes(params));
276 }
277
278 static int snd_cobalt_pcm_hw_free(struct snd_pcm_substream *substream)
279 {
280 if (substream->runtime->dma_area) {
281 dprintk("freeing pcm capture region\n");
282 vfree(substream->runtime->dma_area);
283 substream->runtime->dma_area = NULL;
284 }
285
286 return 0;
287 }
288
289 static int snd_cobalt_pcm_prepare(struct snd_pcm_substream *substream)
290 {
291 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
292
293 cobsc->hwptr_done_capture = 0;
294 cobsc->capture_transfer_done = 0;
295
296 return 0;
297 }
298
299 static int snd_cobalt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
300 {
301 switch (cmd) {
302 case SNDRV_PCM_TRIGGER_START:
303 case SNDRV_PCM_TRIGGER_STOP:
304 return 0;
305 default:
306 return -EINVAL;
307 }
308 return 0;
309 }
310
311 static
312 snd_pcm_uframes_t snd_cobalt_pcm_pointer(struct snd_pcm_substream *substream)
313 {
314 snd_pcm_uframes_t hwptr_done;
315 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
316
317 hwptr_done = cobsc->hwptr_done_capture;
318
319 return hwptr_done;
320 }
321
322 static void pb_sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32)
323 {
324 static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
325 unsigned idx = 0;
326
327 while (len >= (is_s32 ? 4 : 2)) {
328 unsigned offset = map[idx] * 4;
329 u8 *out = dst + offset;
330
331 *out++ = 0;
332 if (is_s32) {
333 src++;
334 *out++ = *src++;
335 } else {
336 *out++ = 0;
337 }
338 *out++ = *src++;
339 *out = *src++;
340 len -= is_s32 ? 4 : 2;
341 idx++;
342 }
343 }
344
345 static void cobalt_alsa_pb_pcm_data(struct snd_cobalt_card *cobsc,
346 u8 *pcm_data,
347 size_t skip,
348 size_t samples)
349 {
350 struct snd_pcm_substream *substream;
351 struct snd_pcm_runtime *runtime;
352 unsigned long flags;
353 unsigned int pos;
354 unsigned int stride;
355 bool is_s32;
356 unsigned i;
357
358 dprintk("cobalt alsa pb ptr=%p data=%p samples=%zd\n", cobsc,
359 pcm_data, samples);
360
361 substream = cobsc->playback_pcm_substream;
362 if (substream == NULL) {
363 dprintk("substream was NULL\n");
364 return;
365 }
366
367 runtime = substream->runtime;
368 if (runtime == NULL) {
369 dprintk("runtime was NULL\n");
370 return;
371 }
372
373 is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE;
374 stride = runtime->frame_bits >> 3;
375 if (stride == 0) {
376 dprintk("stride is zero\n");
377 return;
378 }
379
380 if (samples == 0) {
381 dprintk("%s: samples was zero\n", __func__);
382 return;
383 }
384
385 if (runtime->dma_area == NULL) {
386 dprintk("dma area was NULL - ignoring\n");
387 return;
388 }
389
390 pos = cobsc->pb_pos % cobsc->pb_size;
391 for (i = 0; i < cobsc->pb_count / (8 * 4); i++)
392 pb_sample_cpy(pcm_data + i * skip,
393 runtime->dma_area + pos + i * stride,
394 stride, is_s32);
395 snd_pcm_stream_lock_irqsave(substream, flags);
396
397 cobsc->pb_pos += i * stride;
398
399 snd_pcm_stream_unlock_irqrestore(substream, flags);
400 if (cobsc->pb_pos % cobsc->pb_count == 0)
401 snd_pcm_period_elapsed(substream);
402 }
403
404 static int alsa_pb_fnc(struct vb2_buffer *vb, void *priv)
405 {
406 struct cobalt_stream *s = priv;
407
408 if (s->alsa->alsa_pb_channel)
409 cobalt_alsa_pb_pcm_data(s->alsa,
410 vb2_plane_vaddr(vb, 0),
411 8 * 4,
412 vb2_get_plane_payload(vb, 0) / (8 * 4));
413 return 0;
414 }
415
416 static int snd_cobalt_pcm_playback_open(struct snd_pcm_substream *substream)
417 {
418 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
419 struct snd_pcm_runtime *runtime = substream->runtime;
420 struct cobalt_stream *s = cobsc->s;
421
422 runtime->hw = snd_cobalt_playback;
423 snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
424 cobsc->playback_pcm_substream = substream;
425 runtime->private_data = s;
426 cobsc->alsa_playback_cnt++;
427 if (cobsc->alsa_playback_cnt == 1) {
428 int rc;
429
430 rc = vb2_thread_start(&s->q, alsa_pb_fnc, s, s->vdev.name);
431 if (rc) {
432 cobsc->alsa_playback_cnt--;
433 return rc;
434 }
435 }
436
437 return 0;
438 }
439
440 static int snd_cobalt_pcm_playback_close(struct snd_pcm_substream *substream)
441 {
442 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
443 struct cobalt_stream *s = cobsc->s;
444
445 cobsc->alsa_playback_cnt--;
446 if (cobsc->alsa_playback_cnt == 0)
447 vb2_thread_stop(&s->q);
448 return 0;
449 }
450
451 static int snd_cobalt_pcm_pb_prepare(struct snd_pcm_substream *substream)
452 {
453 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
454
455 cobsc->pb_size = snd_pcm_lib_buffer_bytes(substream);
456 cobsc->pb_count = snd_pcm_lib_period_bytes(substream);
457 cobsc->pb_pos = 0;
458
459 return 0;
460 }
461
462 static int snd_cobalt_pcm_pb_trigger(struct snd_pcm_substream *substream,
463 int cmd)
464 {
465 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
466
467 switch (cmd) {
468 case SNDRV_PCM_TRIGGER_START:
469 if (cobsc->alsa_pb_channel)
470 return -EBUSY;
471 cobsc->alsa_pb_channel = true;
472 return 0;
473 case SNDRV_PCM_TRIGGER_STOP:
474 cobsc->alsa_pb_channel = false;
475 return 0;
476 default:
477 return -EINVAL;
478 }
479 }
480
481 static
482 snd_pcm_uframes_t snd_cobalt_pcm_pb_pointer(struct snd_pcm_substream *substream)
483 {
484 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
485 size_t ptr;
486
487 ptr = cobsc->pb_pos;
488
489 return bytes_to_frames(substream->runtime, ptr) %
490 substream->runtime->buffer_size;
491 }
492
493 static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
494 unsigned long offset)
495 {
496 void *pageptr = subs->runtime->dma_area + offset;
497
498 return vmalloc_to_page(pageptr);
499 }
500
501 static const struct snd_pcm_ops snd_cobalt_pcm_capture_ops = {
502 .open = snd_cobalt_pcm_capture_open,
503 .close = snd_cobalt_pcm_capture_close,
504 .ioctl = snd_cobalt_pcm_ioctl,
505 .hw_params = snd_cobalt_pcm_hw_params,
506 .hw_free = snd_cobalt_pcm_hw_free,
507 .prepare = snd_cobalt_pcm_prepare,
508 .trigger = snd_cobalt_pcm_trigger,
509 .pointer = snd_cobalt_pcm_pointer,
510 .page = snd_pcm_get_vmalloc_page,
511 };
512
513 static const struct snd_pcm_ops snd_cobalt_pcm_playback_ops = {
514 .open = snd_cobalt_pcm_playback_open,
515 .close = snd_cobalt_pcm_playback_close,
516 .ioctl = snd_cobalt_pcm_ioctl,
517 .hw_params = snd_cobalt_pcm_hw_params,
518 .hw_free = snd_cobalt_pcm_hw_free,
519 .prepare = snd_cobalt_pcm_pb_prepare,
520 .trigger = snd_cobalt_pcm_pb_trigger,
521 .pointer = snd_cobalt_pcm_pb_pointer,
522 .page = snd_pcm_get_vmalloc_page,
523 };
524
525 int snd_cobalt_pcm_create(struct snd_cobalt_card *cobsc)
526 {
527 struct snd_pcm *sp;
528 struct snd_card *sc = cobsc->sc;
529 struct cobalt_stream *s = cobsc->s;
530 struct cobalt *cobalt = s->cobalt;
531 int ret;
532
533 s->q.gfp_flags |= __GFP_ZERO;
534
535 if (!s->is_output) {
536 cobalt_s_bit_sysctrl(cobalt,
537 COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel),
538 0);
539 mdelay(2);
540 cobalt_s_bit_sysctrl(cobalt,
541 COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel),
542 1);
543 mdelay(1);
544
545 ret = snd_pcm_new(sc, "Cobalt PCM-In HDMI",
546 0,
547 0,
548 1,
549 &sp);
550 if (ret) {
551 cobalt_err("snd_cobalt_pcm_create() failed for input with err %d\n",
552 ret);
553 goto err_exit;
554 }
555
556 snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,
557 &snd_cobalt_pcm_capture_ops);
558 sp->info_flags = 0;
559 sp->private_data = cobsc;
560 strscpy(sp->name, "cobalt", sizeof(sp->name));
561 } else {
562 cobalt_s_bit_sysctrl(cobalt,
563 COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 0);
564 mdelay(2);
565 cobalt_s_bit_sysctrl(cobalt,
566 COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 1);
567 mdelay(1);
568
569 ret = snd_pcm_new(sc, "Cobalt PCM-Out HDMI",
570 0,
571 1,
572 0,
573 &sp);
574 if (ret) {
575 cobalt_err("snd_cobalt_pcm_create() failed for output with err %d\n",
576 ret);
577 goto err_exit;
578 }
579
580 snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_PLAYBACK,
581 &snd_cobalt_pcm_playback_ops);
582 sp->info_flags = 0;
583 sp->private_data = cobsc;
584 strscpy(sp->name, "cobalt", sizeof(sp->name));
585 }
586
587 return 0;
588
589 err_exit:
590 return ret;
591 }