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/delay.h>
14 #include <media/v4l2-device.h>
16 #include <sound/core.h>
17 #include <sound/pcm.h>
19 #include "cobalt-driver.h"
20 #include "cobalt-alsa.h"
21 #include "cobalt-alsa-pcm.h"
23 static unsigned int pcm_debug
;
24 module_param(pcm_debug
, int, 0644);
25 MODULE_PARM_DESC(pcm_debug
, "enable debug messages for pcm");
27 #define dprintk(fmt, arg...) \
30 pr_info("cobalt-alsa-pcm %s: " fmt, __func__, ##arg); \
33 static const struct snd_pcm_hardware snd_cobalt_hdmi_capture
= {
34 .info
= SNDRV_PCM_INFO_BLOCK_TRANSFER
|
36 SNDRV_PCM_INFO_INTERLEAVED
|
37 SNDRV_PCM_INFO_MMAP_VALID
,
39 .formats
= SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S32_LE
,
41 .rates
= SNDRV_PCM_RATE_48000
,
47 .buffer_bytes_max
= 4 * 240 * 8 * 4, /* 5 ms of data */
48 .period_bytes_min
= 1920, /* 1 sample = 8 * 4 bytes */
49 .period_bytes_max
= 240 * 8 * 4, /* 5 ms of 8 channel data */
54 static const struct snd_pcm_hardware snd_cobalt_playback
= {
55 .info
= SNDRV_PCM_INFO_BLOCK_TRANSFER
|
57 SNDRV_PCM_INFO_INTERLEAVED
|
58 SNDRV_PCM_INFO_MMAP_VALID
,
60 .formats
= SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S32_LE
,
62 .rates
= SNDRV_PCM_RATE_48000
,
68 .buffer_bytes_max
= 4 * 240 * 8 * 4, /* 5 ms of data */
69 .period_bytes_min
= 1920, /* 1 sample = 8 * 4 bytes */
70 .period_bytes_max
= 240 * 8 * 4, /* 5 ms of 8 channel data */
75 static void sample_cpy(u8
*dst
, const u8
*src
, u32 len
, bool is_s32
)
77 static const unsigned map
[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
80 while (len
>= (is_s32
? 4 : 2)) {
81 unsigned offset
= map
[idx
] * 4;
82 u32 val
= src
[offset
+ 1] + (src
[offset
+ 2] << 8) +
83 (src
[offset
+ 3] << 16);
89 *dst
++ = (val
>> 8) & 0xff;
90 *dst
++ = (val
>> 16) & 0xff;
91 len
-= is_s32
? 4 : 2;
96 static void cobalt_alsa_announce_pcm_data(struct snd_cobalt_card
*cobsc
,
101 struct snd_pcm_substream
*substream
;
102 struct snd_pcm_runtime
*runtime
;
106 int length
= samples
;
107 int period_elapsed
= 0;
110 dprintk("cobalt alsa announce ptr=%p data=%p num_bytes=%zd\n", cobsc
,
113 substream
= cobsc
->capture_pcm_substream
;
114 if (substream
== NULL
) {
115 dprintk("substream was NULL\n");
119 runtime
= substream
->runtime
;
120 if (runtime
== NULL
) {
121 dprintk("runtime was NULL\n");
124 is_s32
= runtime
->format
== SNDRV_PCM_FORMAT_S32_LE
;
126 stride
= runtime
->frame_bits
>> 3;
128 dprintk("stride is zero\n");
133 dprintk("%s: length was zero\n", __func__
);
137 if (runtime
->dma_area
== NULL
) {
138 dprintk("dma area was NULL - ignoring\n");
142 oldptr
= cobsc
->hwptr_done_capture
;
143 if (oldptr
+ length
>= runtime
->buffer_size
) {
144 unsigned int cnt
= runtime
->buffer_size
- oldptr
;
147 for (i
= 0; i
< cnt
; i
++)
148 sample_cpy(runtime
->dma_area
+ (oldptr
+ i
) * stride
,
151 for (i
= cnt
; i
< length
; i
++)
152 sample_cpy(runtime
->dma_area
+ (i
- cnt
) * stride
,
153 pcm_data
+ i
* skip
, stride
, is_s32
);
157 for (i
= 0; i
< length
; i
++)
158 sample_cpy(runtime
->dma_area
+ (oldptr
+ i
) * stride
,
162 snd_pcm_stream_lock_irqsave(substream
, flags
);
164 cobsc
->hwptr_done_capture
+= length
;
165 if (cobsc
->hwptr_done_capture
>=
166 runtime
->buffer_size
)
167 cobsc
->hwptr_done_capture
-=
168 runtime
->buffer_size
;
170 cobsc
->capture_transfer_done
+= length
;
171 if (cobsc
->capture_transfer_done
>=
172 runtime
->period_size
) {
173 cobsc
->capture_transfer_done
-=
174 runtime
->period_size
;
178 snd_pcm_stream_unlock_irqrestore(substream
, flags
);
181 snd_pcm_period_elapsed(substream
);
184 static int alsa_fnc(struct vb2_buffer
*vb
, void *priv
)
186 struct cobalt_stream
*s
= priv
;
187 unsigned char *p
= vb2_plane_vaddr(vb
, 0);
192 for (i
= 0; i
< 8 * 4; i
++) {
195 pr_cont("%02x", p
[i
]);
199 cobalt_alsa_announce_pcm_data(s
->alsa
,
200 vb2_plane_vaddr(vb
, 0),
202 vb2_get_plane_payload(vb
, 0) / (8 * 4));
206 static int snd_cobalt_pcm_capture_open(struct snd_pcm_substream
*substream
)
208 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
209 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
210 struct cobalt_stream
*s
= cobsc
->s
;
212 runtime
->hw
= snd_cobalt_hdmi_capture
;
213 snd_pcm_hw_constraint_integer(runtime
, SNDRV_PCM_HW_PARAM_PERIODS
);
214 cobsc
->capture_pcm_substream
= substream
;
215 runtime
->private_data
= s
;
216 cobsc
->alsa_record_cnt
++;
217 if (cobsc
->alsa_record_cnt
== 1) {
220 rc
= vb2_thread_start(&s
->q
, alsa_fnc
, s
, s
->vdev
.name
);
222 cobsc
->alsa_record_cnt
--;
229 static int snd_cobalt_pcm_capture_close(struct snd_pcm_substream
*substream
)
231 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
232 struct cobalt_stream
*s
= cobsc
->s
;
234 cobsc
->alsa_record_cnt
--;
235 if (cobsc
->alsa_record_cnt
== 0)
236 vb2_thread_stop(&s
->q
);
240 static int snd_cobalt_pcm_prepare(struct snd_pcm_substream
*substream
)
242 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
244 cobsc
->hwptr_done_capture
= 0;
245 cobsc
->capture_transfer_done
= 0;
250 static int snd_cobalt_pcm_trigger(struct snd_pcm_substream
*substream
, int cmd
)
253 case SNDRV_PCM_TRIGGER_START
:
254 case SNDRV_PCM_TRIGGER_STOP
:
263 snd_pcm_uframes_t
snd_cobalt_pcm_pointer(struct snd_pcm_substream
*substream
)
265 snd_pcm_uframes_t hwptr_done
;
266 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
268 hwptr_done
= cobsc
->hwptr_done_capture
;
273 static void pb_sample_cpy(u8
*dst
, const u8
*src
, u32 len
, bool is_s32
)
275 static const unsigned map
[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
278 while (len
>= (is_s32
? 4 : 2)) {
279 unsigned offset
= map
[idx
] * 4;
280 u8
*out
= dst
+ offset
;
291 len
-= is_s32
? 4 : 2;
296 static void cobalt_alsa_pb_pcm_data(struct snd_cobalt_card
*cobsc
,
301 struct snd_pcm_substream
*substream
;
302 struct snd_pcm_runtime
*runtime
;
309 dprintk("cobalt alsa pb ptr=%p data=%p samples=%zd\n", cobsc
,
312 substream
= cobsc
->playback_pcm_substream
;
313 if (substream
== NULL
) {
314 dprintk("substream was NULL\n");
318 runtime
= substream
->runtime
;
319 if (runtime
== NULL
) {
320 dprintk("runtime was NULL\n");
324 is_s32
= runtime
->format
== SNDRV_PCM_FORMAT_S32_LE
;
325 stride
= runtime
->frame_bits
>> 3;
327 dprintk("stride is zero\n");
332 dprintk("%s: samples was zero\n", __func__
);
336 if (runtime
->dma_area
== NULL
) {
337 dprintk("dma area was NULL - ignoring\n");
341 pos
= cobsc
->pb_pos
% cobsc
->pb_size
;
342 for (i
= 0; i
< cobsc
->pb_count
/ (8 * 4); i
++)
343 pb_sample_cpy(pcm_data
+ i
* skip
,
344 runtime
->dma_area
+ pos
+ i
* stride
,
346 snd_pcm_stream_lock_irqsave(substream
, flags
);
348 cobsc
->pb_pos
+= i
* stride
;
350 snd_pcm_stream_unlock_irqrestore(substream
, flags
);
351 if (cobsc
->pb_pos
% cobsc
->pb_count
== 0)
352 snd_pcm_period_elapsed(substream
);
355 static int alsa_pb_fnc(struct vb2_buffer
*vb
, void *priv
)
357 struct cobalt_stream
*s
= priv
;
359 if (s
->alsa
->alsa_pb_channel
)
360 cobalt_alsa_pb_pcm_data(s
->alsa
,
361 vb2_plane_vaddr(vb
, 0),
363 vb2_get_plane_payload(vb
, 0) / (8 * 4));
367 static int snd_cobalt_pcm_playback_open(struct snd_pcm_substream
*substream
)
369 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
370 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
371 struct cobalt_stream
*s
= cobsc
->s
;
373 runtime
->hw
= snd_cobalt_playback
;
374 snd_pcm_hw_constraint_integer(runtime
, SNDRV_PCM_HW_PARAM_PERIODS
);
375 cobsc
->playback_pcm_substream
= substream
;
376 runtime
->private_data
= s
;
377 cobsc
->alsa_playback_cnt
++;
378 if (cobsc
->alsa_playback_cnt
== 1) {
381 rc
= vb2_thread_start(&s
->q
, alsa_pb_fnc
, s
, s
->vdev
.name
);
383 cobsc
->alsa_playback_cnt
--;
391 static int snd_cobalt_pcm_playback_close(struct snd_pcm_substream
*substream
)
393 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
394 struct cobalt_stream
*s
= cobsc
->s
;
396 cobsc
->alsa_playback_cnt
--;
397 if (cobsc
->alsa_playback_cnt
== 0)
398 vb2_thread_stop(&s
->q
);
402 static int snd_cobalt_pcm_pb_prepare(struct snd_pcm_substream
*substream
)
404 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
406 cobsc
->pb_size
= snd_pcm_lib_buffer_bytes(substream
);
407 cobsc
->pb_count
= snd_pcm_lib_period_bytes(substream
);
413 static int snd_cobalt_pcm_pb_trigger(struct snd_pcm_substream
*substream
,
416 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
419 case SNDRV_PCM_TRIGGER_START
:
420 if (cobsc
->alsa_pb_channel
)
422 cobsc
->alsa_pb_channel
= true;
424 case SNDRV_PCM_TRIGGER_STOP
:
425 cobsc
->alsa_pb_channel
= false;
433 snd_pcm_uframes_t
snd_cobalt_pcm_pb_pointer(struct snd_pcm_substream
*substream
)
435 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
440 return bytes_to_frames(substream
->runtime
, ptr
) %
441 substream
->runtime
->buffer_size
;
444 static const struct snd_pcm_ops snd_cobalt_pcm_capture_ops
= {
445 .open
= snd_cobalt_pcm_capture_open
,
446 .close
= snd_cobalt_pcm_capture_close
,
447 .prepare
= snd_cobalt_pcm_prepare
,
448 .trigger
= snd_cobalt_pcm_trigger
,
449 .pointer
= snd_cobalt_pcm_pointer
,
452 static const struct snd_pcm_ops snd_cobalt_pcm_playback_ops
= {
453 .open
= snd_cobalt_pcm_playback_open
,
454 .close
= snd_cobalt_pcm_playback_close
,
455 .prepare
= snd_cobalt_pcm_pb_prepare
,
456 .trigger
= snd_cobalt_pcm_pb_trigger
,
457 .pointer
= snd_cobalt_pcm_pb_pointer
,
460 int snd_cobalt_pcm_create(struct snd_cobalt_card
*cobsc
)
463 struct snd_card
*sc
= cobsc
->sc
;
464 struct cobalt_stream
*s
= cobsc
->s
;
465 struct cobalt
*cobalt
= s
->cobalt
;
468 s
->q
.gfp_flags
|= __GFP_ZERO
;
471 cobalt_s_bit_sysctrl(cobalt
,
472 COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s
->video_channel
),
475 cobalt_s_bit_sysctrl(cobalt
,
476 COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s
->video_channel
),
480 ret
= snd_pcm_new(sc
, "Cobalt PCM-In HDMI",
481 0, /* PCM device 0, the only one for this card */
482 0, /* 0 playback substreams */
483 1, /* 1 capture substream */
486 cobalt_err("snd_cobalt_pcm_create() failed for input with err %d\n",
491 snd_pcm_set_ops(sp
, SNDRV_PCM_STREAM_CAPTURE
,
492 &snd_cobalt_pcm_capture_ops
);
493 snd_pcm_set_managed_buffer_all(sp
, SNDRV_DMA_TYPE_VMALLOC
,
496 sp
->private_data
= cobsc
;
497 strscpy(sp
->name
, "cobalt", sizeof(sp
->name
));
499 cobalt_s_bit_sysctrl(cobalt
,
500 COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT
, 0);
502 cobalt_s_bit_sysctrl(cobalt
,
503 COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT
, 1);
506 ret
= snd_pcm_new(sc
, "Cobalt PCM-Out HDMI",
507 0, /* PCM device 0, the only one for this card */
508 1, /* 0 playback substreams */
509 0, /* 1 capture substream */
512 cobalt_err("snd_cobalt_pcm_create() failed for output with err %d\n",
517 snd_pcm_set_ops(sp
, SNDRV_PCM_STREAM_PLAYBACK
,
518 &snd_cobalt_pcm_playback_ops
);
519 snd_pcm_set_managed_buffer_all(sp
, SNDRV_DMA_TYPE_VMALLOC
,
522 sp
->private_data
= cobsc
;
523 strscpy(sp
->name
, "cobalt", sizeof(sp
->name
));