1 // SPDX-License-Identifier: GPL-2.0-only
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.
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...) \
31 pr_info("cobalt-alsa-pcm %s: " fmt, __func__, ##arg); \
34 static const struct snd_pcm_hardware snd_cobalt_hdmi_capture = {
35 .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
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,
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 */
55 static const struct snd_pcm_hardware snd_cobalt_playback = {
56 .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
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,
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 */
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 };
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);
90 *dst++ = (val >> 8) & 0xff;
91 *dst++ = (val >> 16) & 0xff;
92 len -= is_s32 ? 4 : 2;
97 static void cobalt_alsa_announce_pcm_data(struct snd_cobalt_card *cobsc,
102 struct snd_pcm_substream *substream;
103 struct snd_pcm_runtime *runtime;
107 int length = samples;
108 int period_elapsed = 0;
111 dprintk("cobalt alsa announce ptr=%p data=%p num_bytes=%zd\n", cobsc,
114 substream = cobsc->capture_pcm_substream;
115 if (substream == NULL) {
116 dprintk("substream was NULL\n");
120 runtime = substream->runtime;
121 if (runtime == NULL) {
122 dprintk("runtime was NULL\n");
125 is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE;
127 stride = runtime->frame_bits >> 3;
129 dprintk("stride is zero\n");
134 dprintk("%s: length was zero\n", __func__);
138 if (runtime->dma_area == NULL) {
139 dprintk("dma area was NULL - ignoring\n");
143 oldptr = cobsc->hwptr_done_capture;
144 if (oldptr + length >= runtime->buffer_size) {
145 unsigned int cnt = runtime->buffer_size - oldptr;
148 for (i = 0; i < cnt; i++)
149 sample_cpy(runtime->dma_area + (oldptr + i) * stride,
152 for (i = cnt; i < length; i++)
153 sample_cpy(runtime->dma_area + (i - cnt) * stride,
154 pcm_data + i * skip, stride, is_s32);
158 for (i = 0; i < length; i++)
159 sample_cpy(runtime->dma_area + (oldptr + i) * stride,
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;
179 snd_pcm_stream_unlock_irqrestore(substream, flags);
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);
193 for (i = 0; i < 8 * 4; i++) {
196 pr_cont("%02x", p[i]);
200 cobalt_alsa_announce_pcm_data(s->alsa,
201 vb2_plane_vaddr(vb, 0),
203 vb2_get_plane_payload(vb, 0) / (8 * 4));
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) {
221 rc = vb2_thread_start(&s->q, alsa_fnc, s, s->vdev.name);
223 cobsc->alsa_record_cnt--;
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);
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,
251 struct snd_pcm_runtime *runtime = subs->runtime;
253 dprintk("Allocating vbuffer\n");
254 if (runtime->dma_area) {
255 if (runtime->dma_bytes > size)
258 vfree(runtime->dma_area);
260 runtime->dma_area = vmalloc(size);
261 if (!runtime->dma_area)
264 runtime->dma_bytes = size;
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;
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;
299 static int snd_cobalt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
302 case SNDRV_PCM_TRIGGER_START:
303 case SNDRV_PCM_TRIGGER_STOP:
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;
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 };
327 while (len >= (is_s32 ? 4 : 2)) {
328 unsigned offset = map[idx] * 4;
329 u8 *out = dst + offset;
340 len -= is_s32 ? 4 : 2;
345 static void cobalt_alsa_pb_pcm_data(struct snd_cobalt_card *cobsc,
350 struct snd_pcm_substream *substream;
351 struct snd_pcm_runtime *runtime;
358 dprintk("cobalt alsa pb ptr=%p data=%p samples=%zd\n", cobsc,
361 substream = cobsc->playback_pcm_substream;
362 if (substream == NULL) {
363 dprintk("substream was NULL\n");
367 runtime = substream->runtime;
368 if (runtime == NULL) {
369 dprintk("runtime was NULL\n");
373 is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE;
374 stride = runtime->frame_bits >> 3;
376 dprintk("stride is zero\n");
381 dprintk("%s: samples was zero\n", __func__);
385 if (runtime->dma_area == NULL) {
386 dprintk("dma area was NULL - ignoring\n");
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,
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),
412 vb2_get_plane_payload(vb, 0) / (8 * 4));
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) {
430 rc = vb2_thread_start(&s->q, alsa_pb_fnc, s, s->vdev.name);
432 cobsc->alsa_playback_cnt--;
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);
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);
462 static int snd_cobalt_pcm_pb_trigger(struct snd_pcm_substream *substream,
465 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
468 case SNDRV_PCM_TRIGGER_START:
469 if (cobsc->alsa_pb_channel)
471 cobsc->alsa_pb_channel = true;
473 case SNDRV_PCM_TRIGGER_STOP:
474 cobsc->alsa_pb_channel = false;
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);
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)
528 struct snd_card *sc = cobsc->sc;
529 struct cobalt_stream *s = cobsc->s;
530 struct cobalt *cobalt = s->cobalt;
533 s->q.gfp_flags |= __GFP_ZERO;
536 cobalt_s_bit_sysctrl(cobalt,
537 COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel),
540 cobalt_s_bit_sysctrl(cobalt,
541 COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel),
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 */
551 cobalt_err("snd_cobalt_pcm_create() failed for input with err %d\n",
556 snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,
557 &snd_cobalt_pcm_capture_ops);
559 sp->private_data = cobsc;
560 strlcpy(sp->name, "cobalt", sizeof(sp->name));
562 cobalt_s_bit_sysctrl(cobalt,
563 COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 0);
565 cobalt_s_bit_sysctrl(cobalt,
566 COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 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 */
575 cobalt_err("snd_cobalt_pcm_create() failed for output with err %d\n",
580 snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_PLAYBACK,
581 &snd_cobalt_pcm_playback_ops);
583 sp->private_data = cobsc;
584 strlcpy(sp->name, "cobalt", sizeof(sp->name));