2 * ALSA PCM device for the
3 * ALSA interface to cobalt PCM capture streams
5 * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
8 * This program is free software; you may redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
12 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
16 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
17 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 #include <linux/init.h>
23 #include <linux/kernel.h>
24 #include <linux/vmalloc.h>
25 #include <linux/delay.h>
27 #include <media/v4l2-device.h>
29 #include <sound/core.h>
30 #include <sound/pcm.h>
32 #include "cobalt-driver.h"
33 #include "cobalt-alsa.h"
34 #include "cobalt-alsa-pcm.h"
36 static unsigned int pcm_debug
;
37 module_param(pcm_debug
, int, 0644);
38 MODULE_PARM_DESC(pcm_debug
, "enable debug messages for pcm");
40 #define dprintk(fmt, arg...) \
43 pr_info("cobalt-alsa-pcm %s: " fmt, __func__, ##arg); \
46 static struct snd_pcm_hardware snd_cobalt_hdmi_capture
= {
47 .info
= SNDRV_PCM_INFO_BLOCK_TRANSFER
|
49 SNDRV_PCM_INFO_INTERLEAVED
|
50 SNDRV_PCM_INFO_MMAP_VALID
,
52 .formats
= SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S32_LE
,
54 .rates
= SNDRV_PCM_RATE_48000
,
60 .buffer_bytes_max
= 4 * 240 * 8 * 4, /* 5 ms of data */
61 .period_bytes_min
= 1920, /* 1 sample = 8 * 4 bytes */
62 .period_bytes_max
= 240 * 8 * 4, /* 5 ms of 8 channel data */
67 static struct snd_pcm_hardware snd_cobalt_playback
= {
68 .info
= SNDRV_PCM_INFO_BLOCK_TRANSFER
|
70 SNDRV_PCM_INFO_INTERLEAVED
|
71 SNDRV_PCM_INFO_MMAP_VALID
,
73 .formats
= SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S32_LE
,
75 .rates
= SNDRV_PCM_RATE_48000
,
81 .buffer_bytes_max
= 4 * 240 * 8 * 4, /* 5 ms of data */
82 .period_bytes_min
= 1920, /* 1 sample = 8 * 4 bytes */
83 .period_bytes_max
= 240 * 8 * 4, /* 5 ms of 8 channel data */
88 static void sample_cpy(u8
*dst
, const u8
*src
, u32 len
, bool is_s32
)
90 static const unsigned map
[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
93 while (len
>= (is_s32
? 4 : 2)) {
94 unsigned offset
= map
[idx
] * 4;
95 u32 val
= src
[offset
+ 1] + (src
[offset
+ 2] << 8) +
96 (src
[offset
+ 3] << 16);
102 *dst
++ = (val
>> 8) & 0xff;
103 *dst
++ = (val
>> 16) & 0xff;
104 len
-= is_s32
? 4 : 2;
109 static void cobalt_alsa_announce_pcm_data(struct snd_cobalt_card
*cobsc
,
114 struct snd_pcm_substream
*substream
;
115 struct snd_pcm_runtime
*runtime
;
119 int length
= samples
;
120 int period_elapsed
= 0;
123 dprintk("cobalt alsa announce ptr=%p data=%p num_bytes=%zd\n", cobsc
,
126 substream
= cobsc
->capture_pcm_substream
;
127 if (substream
== NULL
) {
128 dprintk("substream was NULL\n");
132 runtime
= substream
->runtime
;
133 if (runtime
== NULL
) {
134 dprintk("runtime was NULL\n");
137 is_s32
= runtime
->format
== SNDRV_PCM_FORMAT_S32_LE
;
139 stride
= runtime
->frame_bits
>> 3;
141 dprintk("stride is zero\n");
146 dprintk("%s: length was zero\n", __func__
);
150 if (runtime
->dma_area
== NULL
) {
151 dprintk("dma area was NULL - ignoring\n");
155 oldptr
= cobsc
->hwptr_done_capture
;
156 if (oldptr
+ length
>= runtime
->buffer_size
) {
157 unsigned int cnt
= runtime
->buffer_size
- oldptr
;
160 for (i
= 0; i
< cnt
; i
++)
161 sample_cpy(runtime
->dma_area
+ (oldptr
+ i
) * stride
,
164 for (i
= cnt
; i
< length
; i
++)
165 sample_cpy(runtime
->dma_area
+ (i
- cnt
) * stride
,
166 pcm_data
+ i
* skip
, stride
, is_s32
);
170 for (i
= 0; i
< length
; i
++)
171 sample_cpy(runtime
->dma_area
+ (oldptr
+ i
) * stride
,
175 snd_pcm_stream_lock_irqsave(substream
, flags
);
177 cobsc
->hwptr_done_capture
+= length
;
178 if (cobsc
->hwptr_done_capture
>=
179 runtime
->buffer_size
)
180 cobsc
->hwptr_done_capture
-=
181 runtime
->buffer_size
;
183 cobsc
->capture_transfer_done
+= length
;
184 if (cobsc
->capture_transfer_done
>=
185 runtime
->period_size
) {
186 cobsc
->capture_transfer_done
-=
187 runtime
->period_size
;
191 snd_pcm_stream_unlock_irqrestore(substream
, flags
);
194 snd_pcm_period_elapsed(substream
);
197 static int alsa_fnc(struct vb2_buffer
*vb
, void *priv
)
199 struct cobalt_stream
*s
= priv
;
200 unsigned char *p
= vb2_plane_vaddr(vb
, 0);
205 for (i
= 0; i
< 8 * 4; i
++) {
208 pr_cont("%02x", p
[i
]);
212 cobalt_alsa_announce_pcm_data(s
->alsa
,
213 vb2_plane_vaddr(vb
, 0),
215 vb2_get_plane_payload(vb
, 0) / (8 * 4));
219 static int snd_cobalt_pcm_capture_open(struct snd_pcm_substream
*substream
)
221 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
222 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
223 struct cobalt_stream
*s
= cobsc
->s
;
225 runtime
->hw
= snd_cobalt_hdmi_capture
;
226 snd_pcm_hw_constraint_integer(runtime
, SNDRV_PCM_HW_PARAM_PERIODS
);
227 cobsc
->capture_pcm_substream
= substream
;
228 runtime
->private_data
= s
;
229 cobsc
->alsa_record_cnt
++;
230 if (cobsc
->alsa_record_cnt
== 1) {
233 rc
= vb2_thread_start(&s
->q
, alsa_fnc
, s
, s
->vdev
.name
);
235 cobsc
->alsa_record_cnt
--;
242 static int snd_cobalt_pcm_capture_close(struct snd_pcm_substream
*substream
)
244 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
245 struct cobalt_stream
*s
= cobsc
->s
;
247 cobsc
->alsa_record_cnt
--;
248 if (cobsc
->alsa_record_cnt
== 0)
249 vb2_thread_stop(&s
->q
);
253 static int snd_cobalt_pcm_ioctl(struct snd_pcm_substream
*substream
,
254 unsigned int cmd
, void *arg
)
256 return snd_pcm_lib_ioctl(substream
, cmd
, arg
);
260 static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream
*subs
,
263 struct snd_pcm_runtime
*runtime
= subs
->runtime
;
265 dprintk("Allocating vbuffer\n");
266 if (runtime
->dma_area
) {
267 if (runtime
->dma_bytes
> size
)
270 vfree(runtime
->dma_area
);
272 runtime
->dma_area
= vmalloc(size
);
273 if (!runtime
->dma_area
)
276 runtime
->dma_bytes
= size
;
281 static int snd_cobalt_pcm_hw_params(struct snd_pcm_substream
*substream
,
282 struct snd_pcm_hw_params
*params
)
284 dprintk("%s called\n", __func__
);
286 return snd_pcm_alloc_vmalloc_buffer(substream
,
287 params_buffer_bytes(params
));
290 static int snd_cobalt_pcm_hw_free(struct snd_pcm_substream
*substream
)
292 if (substream
->runtime
->dma_area
) {
293 dprintk("freeing pcm capture region\n");
294 vfree(substream
->runtime
->dma_area
);
295 substream
->runtime
->dma_area
= NULL
;
301 static int snd_cobalt_pcm_prepare(struct snd_pcm_substream
*substream
)
303 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
305 cobsc
->hwptr_done_capture
= 0;
306 cobsc
->capture_transfer_done
= 0;
311 static int snd_cobalt_pcm_trigger(struct snd_pcm_substream
*substream
, int cmd
)
314 case SNDRV_PCM_TRIGGER_START
:
315 case SNDRV_PCM_TRIGGER_STOP
:
324 snd_pcm_uframes_t
snd_cobalt_pcm_pointer(struct snd_pcm_substream
*substream
)
326 snd_pcm_uframes_t hwptr_done
;
327 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
329 hwptr_done
= cobsc
->hwptr_done_capture
;
334 static void pb_sample_cpy(u8
*dst
, const u8
*src
, u32 len
, bool is_s32
)
336 static const unsigned map
[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
339 while (len
>= (is_s32
? 4 : 2)) {
340 unsigned offset
= map
[idx
] * 4;
341 u8
*out
= dst
+ offset
;
352 len
-= is_s32
? 4 : 2;
357 static void cobalt_alsa_pb_pcm_data(struct snd_cobalt_card
*cobsc
,
362 struct snd_pcm_substream
*substream
;
363 struct snd_pcm_runtime
*runtime
;
370 dprintk("cobalt alsa pb ptr=%p data=%p samples=%zd\n", cobsc
,
373 substream
= cobsc
->playback_pcm_substream
;
374 if (substream
== NULL
) {
375 dprintk("substream was NULL\n");
379 runtime
= substream
->runtime
;
380 if (runtime
== NULL
) {
381 dprintk("runtime was NULL\n");
385 is_s32
= runtime
->format
== SNDRV_PCM_FORMAT_S32_LE
;
386 stride
= runtime
->frame_bits
>> 3;
388 dprintk("stride is zero\n");
393 dprintk("%s: samples was zero\n", __func__
);
397 if (runtime
->dma_area
== NULL
) {
398 dprintk("dma area was NULL - ignoring\n");
402 pos
= cobsc
->pb_pos
% cobsc
->pb_size
;
403 for (i
= 0; i
< cobsc
->pb_count
/ (8 * 4); i
++)
404 pb_sample_cpy(pcm_data
+ i
* skip
,
405 runtime
->dma_area
+ pos
+ i
* stride
,
407 snd_pcm_stream_lock_irqsave(substream
, flags
);
409 cobsc
->pb_pos
+= i
* stride
;
411 snd_pcm_stream_unlock_irqrestore(substream
, flags
);
412 if (cobsc
->pb_pos
% cobsc
->pb_count
== 0)
413 snd_pcm_period_elapsed(substream
);
416 static int alsa_pb_fnc(struct vb2_buffer
*vb
, void *priv
)
418 struct cobalt_stream
*s
= priv
;
420 if (s
->alsa
->alsa_pb_channel
)
421 cobalt_alsa_pb_pcm_data(s
->alsa
,
422 vb2_plane_vaddr(vb
, 0),
424 vb2_get_plane_payload(vb
, 0) / (8 * 4));
428 static int snd_cobalt_pcm_playback_open(struct snd_pcm_substream
*substream
)
430 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
431 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
432 struct cobalt_stream
*s
= cobsc
->s
;
434 runtime
->hw
= snd_cobalt_playback
;
435 snd_pcm_hw_constraint_integer(runtime
, SNDRV_PCM_HW_PARAM_PERIODS
);
436 cobsc
->playback_pcm_substream
= substream
;
437 runtime
->private_data
= s
;
438 cobsc
->alsa_playback_cnt
++;
439 if (cobsc
->alsa_playback_cnt
== 1) {
442 rc
= vb2_thread_start(&s
->q
, alsa_pb_fnc
, s
, s
->vdev
.name
);
444 cobsc
->alsa_playback_cnt
--;
452 static int snd_cobalt_pcm_playback_close(struct snd_pcm_substream
*substream
)
454 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
455 struct cobalt_stream
*s
= cobsc
->s
;
457 cobsc
->alsa_playback_cnt
--;
458 if (cobsc
->alsa_playback_cnt
== 0)
459 vb2_thread_stop(&s
->q
);
463 static int snd_cobalt_pcm_pb_prepare(struct snd_pcm_substream
*substream
)
465 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
467 cobsc
->pb_size
= snd_pcm_lib_buffer_bytes(substream
);
468 cobsc
->pb_count
= snd_pcm_lib_period_bytes(substream
);
474 static int snd_cobalt_pcm_pb_trigger(struct snd_pcm_substream
*substream
,
477 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
480 case SNDRV_PCM_TRIGGER_START
:
481 if (cobsc
->alsa_pb_channel
)
483 cobsc
->alsa_pb_channel
= true;
485 case SNDRV_PCM_TRIGGER_STOP
:
486 cobsc
->alsa_pb_channel
= false;
494 snd_pcm_uframes_t
snd_cobalt_pcm_pb_pointer(struct snd_pcm_substream
*substream
)
496 struct snd_cobalt_card
*cobsc
= snd_pcm_substream_chip(substream
);
501 return bytes_to_frames(substream
->runtime
, ptr
) %
502 substream
->runtime
->buffer_size
;
505 static struct page
*snd_pcm_get_vmalloc_page(struct snd_pcm_substream
*subs
,
506 unsigned long offset
)
508 void *pageptr
= subs
->runtime
->dma_area
+ offset
;
510 return vmalloc_to_page(pageptr
);
513 static const struct snd_pcm_ops snd_cobalt_pcm_capture_ops
= {
514 .open
= snd_cobalt_pcm_capture_open
,
515 .close
= snd_cobalt_pcm_capture_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_prepare
,
520 .trigger
= snd_cobalt_pcm_trigger
,
521 .pointer
= snd_cobalt_pcm_pointer
,
522 .page
= snd_pcm_get_vmalloc_page
,
525 static const struct snd_pcm_ops snd_cobalt_pcm_playback_ops
= {
526 .open
= snd_cobalt_pcm_playback_open
,
527 .close
= snd_cobalt_pcm_playback_close
,
528 .ioctl
= snd_cobalt_pcm_ioctl
,
529 .hw_params
= snd_cobalt_pcm_hw_params
,
530 .hw_free
= snd_cobalt_pcm_hw_free
,
531 .prepare
= snd_cobalt_pcm_pb_prepare
,
532 .trigger
= snd_cobalt_pcm_pb_trigger
,
533 .pointer
= snd_cobalt_pcm_pb_pointer
,
534 .page
= snd_pcm_get_vmalloc_page
,
537 int snd_cobalt_pcm_create(struct snd_cobalt_card
*cobsc
)
540 struct snd_card
*sc
= cobsc
->sc
;
541 struct cobalt_stream
*s
= cobsc
->s
;
542 struct cobalt
*cobalt
= s
->cobalt
;
545 s
->q
.gfp_flags
|= __GFP_ZERO
;
548 cobalt_s_bit_sysctrl(cobalt
,
549 COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s
->video_channel
),
552 cobalt_s_bit_sysctrl(cobalt
,
553 COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s
->video_channel
),
557 ret
= snd_pcm_new(sc
, "Cobalt PCM-In HDMI",
558 0, /* PCM device 0, the only one for this card */
559 0, /* 0 playback substreams */
560 1, /* 1 capture substream */
563 cobalt_err("snd_cobalt_pcm_create() failed for input with err %d\n",
568 snd_pcm_set_ops(sp
, SNDRV_PCM_STREAM_CAPTURE
,
569 &snd_cobalt_pcm_capture_ops
);
571 sp
->private_data
= cobsc
;
572 strlcpy(sp
->name
, "cobalt", sizeof(sp
->name
));
574 cobalt_s_bit_sysctrl(cobalt
,
575 COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT
, 0);
577 cobalt_s_bit_sysctrl(cobalt
,
578 COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT
, 1);
581 ret
= snd_pcm_new(sc
, "Cobalt PCM-Out HDMI",
582 0, /* PCM device 0, the only one for this card */
583 1, /* 0 playback substreams */
584 0, /* 1 capture substream */
587 cobalt_err("snd_cobalt_pcm_create() failed for output with err %d\n",
592 snd_pcm_set_ops(sp
, SNDRV_PCM_STREAM_PLAYBACK
,
593 &snd_cobalt_pcm_playback_ops
);
595 sp
->private_data
= cobsc
;
596 strlcpy(sp
->name
, "cobalt", sizeof(sp
->name
));