Linux 4.19.133
[linux/fpc-iii.git] / drivers / media / pci / cobalt / cobalt-alsa-pcm.c
blobf6a7df13cd048a4ee0fa5015d7fbb953ff4d154a
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * ALSA PCM device for the
4 * ALSA interface to cobalt PCM capture streams
6 * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
7 * All rights reserved.
8 */
10 #include <linux/init.h>
11 #include <linux/kernel.h>
12 #include <linux/vmalloc.h>
13 #include <linux/delay.h>
15 #include <media/v4l2-device.h>
17 #include <sound/core.h>
18 #include <sound/pcm.h>
20 #include "cobalt-driver.h"
21 #include "cobalt-alsa.h"
22 #include "cobalt-alsa-pcm.h"
24 static unsigned int pcm_debug;
25 module_param(pcm_debug, int, 0644);
26 MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm");
28 #define dprintk(fmt, arg...) \
29 do { \
30 if (pcm_debug) \
31 pr_info("cobalt-alsa-pcm %s: " fmt, __func__, ##arg); \
32 } while (0)
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,
40 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
42 .rates = SNDRV_PCM_RATE_48000,
44 .rate_min = 48000,
45 .rate_max = 48000,
46 .channels_min = 1,
47 .channels_max = 8,
48 .buffer_bytes_max = 4 * 240 * 8 * 4, /* 5 ms of data */
49 .period_bytes_min = 1920, /* 1 sample = 8 * 4 bytes */
50 .period_bytes_max = 240 * 8 * 4, /* 5 ms of 8 channel data */
51 .periods_min = 1,
52 .periods_max = 4,
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,
61 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
63 .rates = SNDRV_PCM_RATE_48000,
65 .rate_min = 48000,
66 .rate_max = 48000,
67 .channels_min = 1,
68 .channels_max = 8,
69 .buffer_bytes_max = 4 * 240 * 8 * 4, /* 5 ms of data */
70 .period_bytes_min = 1920, /* 1 sample = 8 * 4 bytes */
71 .period_bytes_max = 240 * 8 * 4, /* 5 ms of 8 channel data */
72 .periods_min = 1,
73 .periods_max = 4,
76 static void sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32)
78 static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
79 unsigned idx = 0;
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);
86 if (is_s32) {
87 *dst++ = 0;
88 *dst++ = val & 0xff;
90 *dst++ = (val >> 8) & 0xff;
91 *dst++ = (val >> 16) & 0xff;
92 len -= is_s32 ? 4 : 2;
93 idx++;
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)
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;
111 dprintk("cobalt alsa announce ptr=%p data=%p num_bytes=%zd\n", cobsc,
112 pcm_data, samples);
114 substream = cobsc->capture_pcm_substream;
115 if (substream == NULL) {
116 dprintk("substream was NULL\n");
117 return;
120 runtime = substream->runtime;
121 if (runtime == NULL) {
122 dprintk("runtime was NULL\n");
123 return;
125 is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE;
127 stride = runtime->frame_bits >> 3;
128 if (stride == 0) {
129 dprintk("stride is zero\n");
130 return;
133 if (length == 0) {
134 dprintk("%s: length was zero\n", __func__);
135 return;
138 if (runtime->dma_area == NULL) {
139 dprintk("dma area was NULL - ignoring\n");
140 return;
143 oldptr = cobsc->hwptr_done_capture;
144 if (oldptr + length >= runtime->buffer_size) {
145 unsigned int cnt = runtime->buffer_size - oldptr;
146 unsigned i;
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;
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);
163 snd_pcm_stream_lock_irqsave(substream, flags);
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;
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;
179 snd_pcm_stream_unlock_irqrestore(substream, flags);
181 if (period_elapsed)
182 snd_pcm_period_elapsed(substream);
185 static int alsa_fnc(struct vb2_buffer *vb, void *priv)
187 struct cobalt_stream *s = priv;
188 unsigned char *p = vb2_plane_vaddr(vb, 0);
189 int i;
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]);
198 pr_cont("\n");
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;
207 static int snd_cobalt_pcm_capture_open(struct snd_pcm_substream *substream)
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;
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;
221 rc = vb2_thread_start(&s->q, alsa_fnc, s, s->vdev.name);
222 if (rc) {
223 cobsc->alsa_record_cnt--;
224 return rc;
227 return 0;
230 static int snd_cobalt_pcm_capture_close(struct snd_pcm_substream *substream)
232 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
233 struct cobalt_stream *s = cobsc->s;
235 cobsc->alsa_record_cnt--;
236 if (cobsc->alsa_record_cnt == 0)
237 vb2_thread_stop(&s->q);
238 return 0;
241 static int snd_cobalt_pcm_ioctl(struct snd_pcm_substream *substream,
242 unsigned int cmd, void *arg)
244 return snd_pcm_lib_ioctl(substream, cmd, arg);
248 static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
249 size_t size)
251 struct snd_pcm_runtime *runtime = subs->runtime;
253 dprintk("Allocating vbuffer\n");
254 if (runtime->dma_area) {
255 if (runtime->dma_bytes > size)
256 return 0;
258 vfree(runtime->dma_area);
260 runtime->dma_area = vmalloc(size);
261 if (!runtime->dma_area)
262 return -ENOMEM;
264 runtime->dma_bytes = size;
266 return 0;
269 static int snd_cobalt_pcm_hw_params(struct snd_pcm_substream *substream,
270 struct snd_pcm_hw_params *params)
272 dprintk("%s called\n", __func__);
274 return snd_pcm_alloc_vmalloc_buffer(substream,
275 params_buffer_bytes(params));
278 static int snd_cobalt_pcm_hw_free(struct snd_pcm_substream *substream)
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;
286 return 0;
289 static int snd_cobalt_pcm_prepare(struct snd_pcm_substream *substream)
291 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
293 cobsc->hwptr_done_capture = 0;
294 cobsc->capture_transfer_done = 0;
296 return 0;
299 static int snd_cobalt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
301 switch (cmd) {
302 case SNDRV_PCM_TRIGGER_START:
303 case SNDRV_PCM_TRIGGER_STOP:
304 return 0;
305 default:
306 return -EINVAL;
308 return 0;
311 static
312 snd_pcm_uframes_t snd_cobalt_pcm_pointer(struct snd_pcm_substream *substream)
314 snd_pcm_uframes_t hwptr_done;
315 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
317 hwptr_done = cobsc->hwptr_done_capture;
319 return hwptr_done;
322 static void pb_sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32)
324 static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
325 unsigned idx = 0;
327 while (len >= (is_s32 ? 4 : 2)) {
328 unsigned offset = map[idx] * 4;
329 u8 *out = dst + offset;
331 *out++ = 0;
332 if (is_s32) {
333 src++;
334 *out++ = *src++;
335 } else {
336 *out++ = 0;
338 *out++ = *src++;
339 *out = *src++;
340 len -= is_s32 ? 4 : 2;
341 idx++;
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)
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;
358 dprintk("cobalt alsa pb ptr=%p data=%p samples=%zd\n", cobsc,
359 pcm_data, samples);
361 substream = cobsc->playback_pcm_substream;
362 if (substream == NULL) {
363 dprintk("substream was NULL\n");
364 return;
367 runtime = substream->runtime;
368 if (runtime == NULL) {
369 dprintk("runtime was NULL\n");
370 return;
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;
380 if (samples == 0) {
381 dprintk("%s: samples was zero\n", __func__);
382 return;
385 if (runtime->dma_area == NULL) {
386 dprintk("dma area was NULL - ignoring\n");
387 return;
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);
397 cobsc->pb_pos += i * stride;
399 snd_pcm_stream_unlock_irqrestore(substream, flags);
400 if (cobsc->pb_pos % cobsc->pb_count == 0)
401 snd_pcm_period_elapsed(substream);
404 static int alsa_pb_fnc(struct vb2_buffer *vb, void *priv)
406 struct cobalt_stream *s = priv;
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;
416 static int snd_cobalt_pcm_playback_open(struct snd_pcm_substream *substream)
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;
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;
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;
437 return 0;
440 static int snd_cobalt_pcm_playback_close(struct snd_pcm_substream *substream)
442 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
443 struct cobalt_stream *s = cobsc->s;
445 cobsc->alsa_playback_cnt--;
446 if (cobsc->alsa_playback_cnt == 0)
447 vb2_thread_stop(&s->q);
448 return 0;
451 static int snd_cobalt_pcm_pb_prepare(struct snd_pcm_substream *substream)
453 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
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;
459 return 0;
462 static int snd_cobalt_pcm_pb_trigger(struct snd_pcm_substream *substream,
463 int cmd)
465 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
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;
481 static
482 snd_pcm_uframes_t snd_cobalt_pcm_pb_pointer(struct snd_pcm_substream *substream)
484 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
485 size_t ptr;
487 ptr = cobsc->pb_pos;
489 return bytes_to_frames(substream->runtime, ptr) %
490 substream->runtime->buffer_size;
493 static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
494 unsigned long offset)
496 void *pageptr = subs->runtime->dma_area + offset;
498 return vmalloc_to_page(pageptr);
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,
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,
525 int snd_cobalt_pcm_create(struct snd_cobalt_card *cobsc)
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;
533 s->q.gfp_flags |= __GFP_ZERO;
535 if (!s->is_output) {
536 cobalt_s_bit_sysctrl(cobalt,
537 COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel),
539 mdelay(2);
540 cobalt_s_bit_sysctrl(cobalt,
541 COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel),
543 mdelay(1);
545 ret = snd_pcm_new(sc, "Cobalt PCM-In HDMI",
546 0, /* PCM device 0, the only one for this card */
547 0, /* 0 playback substreams */
548 1, /* 1 capture substream */
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;
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 strlcpy(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);
569 ret = snd_pcm_new(sc, "Cobalt PCM-Out HDMI",
570 0, /* PCM device 0, the only one for this card */
571 1, /* 0 playback substreams */
572 0, /* 1 capture substream */
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;
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 strlcpy(sp->name, "cobalt", sizeof(sp->name));
587 return 0;
589 err_exit:
590 return ret;