diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2011-09-24 09:46:55 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-12-11 08:23:30 -0500 |
commit | 66847ef013cc4ed3ae519360e7e4cbf531465ae8 (patch) | |
tree | 79651abe38091a5841e16ccbe0850834a1a9c66b /drivers/media/video/uvc | |
parent | 25738cbd72db53ca1c326bf94915d41086cb4297 (diff) |
[media] uvcvideo: Add UVC timestamps support
UVC devices transmit a device timestamp along with video frames. Convert
the timestamp to a host timestamp and use it to fill the V4L2 buffer
timestamp field.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/uvc')
-rw-r--r-- | drivers/media/video/uvc/uvc_queue.c | 12 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_video.c | 330 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvcvideo.h | 25 |
3 files changed, 367 insertions, 0 deletions
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index 268be579aa72..518f77d3a4d8 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c | |||
@@ -104,10 +104,22 @@ static void uvc_buffer_queue(struct vb2_buffer *vb) | |||
104 | spin_unlock_irqrestore(&queue->irqlock, flags); | 104 | spin_unlock_irqrestore(&queue->irqlock, flags); |
105 | } | 105 | } |
106 | 106 | ||
107 | static int uvc_buffer_finish(struct vb2_buffer *vb) | ||
108 | { | ||
109 | struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); | ||
110 | struct uvc_streaming *stream = | ||
111 | container_of(queue, struct uvc_streaming, queue); | ||
112 | struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf); | ||
113 | |||
114 | uvc_video_clock_update(stream, &vb->v4l2_buf, buf); | ||
115 | return 0; | ||
116 | } | ||
117 | |||
107 | static struct vb2_ops uvc_queue_qops = { | 118 | static struct vb2_ops uvc_queue_qops = { |
108 | .queue_setup = uvc_queue_setup, | 119 | .queue_setup = uvc_queue_setup, |
109 | .buf_prepare = uvc_buffer_prepare, | 120 | .buf_prepare = uvc_buffer_prepare, |
110 | .buf_queue = uvc_buffer_queue, | 121 | .buf_queue = uvc_buffer_queue, |
122 | .buf_finish = uvc_buffer_finish, | ||
111 | }; | 123 | }; |
112 | 124 | ||
113 | void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, | 125 | void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, |
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index 513ba30f8d57..c7e69b8f81c9 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c | |||
@@ -357,6 +357,329 @@ static int uvc_commit_video(struct uvc_streaming *stream, | |||
357 | return uvc_set_video_ctrl(stream, probe, 0); | 357 | return uvc_set_video_ctrl(stream, probe, 0); |
358 | } | 358 | } |
359 | 359 | ||
360 | /* ----------------------------------------------------------------------------- | ||
361 | * Clocks and timestamps | ||
362 | */ | ||
363 | |||
364 | static void | ||
365 | uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, | ||
366 | const __u8 *data, int len) | ||
367 | { | ||
368 | struct uvc_clock_sample *sample; | ||
369 | unsigned int header_size; | ||
370 | bool has_pts = false; | ||
371 | bool has_scr = false; | ||
372 | unsigned long flags; | ||
373 | struct timespec ts; | ||
374 | u16 host_sof; | ||
375 | u16 dev_sof; | ||
376 | |||
377 | switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) { | ||
378 | case UVC_STREAM_PTS | UVC_STREAM_SCR: | ||
379 | header_size = 12; | ||
380 | has_pts = true; | ||
381 | has_scr = true; | ||
382 | break; | ||
383 | case UVC_STREAM_PTS: | ||
384 | header_size = 6; | ||
385 | has_pts = true; | ||
386 | break; | ||
387 | case UVC_STREAM_SCR: | ||
388 | header_size = 8; | ||
389 | has_scr = true; | ||
390 | break; | ||
391 | default: | ||
392 | header_size = 2; | ||
393 | break; | ||
394 | } | ||
395 | |||
396 | /* Check for invalid headers. */ | ||
397 | if (len < header_size) | ||
398 | return; | ||
399 | |||
400 | /* Extract the timestamps: | ||
401 | * | ||
402 | * - store the frame PTS in the buffer structure | ||
403 | * - if the SCR field is present, retrieve the host SOF counter and | ||
404 | * kernel timestamps and store them with the SCR STC and SOF fields | ||
405 | * in the ring buffer | ||
406 | */ | ||
407 | if (has_pts && buf != NULL) | ||
408 | buf->pts = get_unaligned_le32(&data[2]); | ||
409 | |||
410 | if (!has_scr) | ||
411 | return; | ||
412 | |||
413 | /* To limit the amount of data, drop SCRs with an SOF identical to the | ||
414 | * previous one. | ||
415 | */ | ||
416 | dev_sof = get_unaligned_le16(&data[header_size - 2]); | ||
417 | if (dev_sof == stream->clock.last_sof) | ||
418 | return; | ||
419 | |||
420 | stream->clock.last_sof = dev_sof; | ||
421 | |||
422 | host_sof = usb_get_current_frame_number(stream->dev->udev); | ||
423 | ktime_get_ts(&ts); | ||
424 | |||
425 | /* The UVC specification allows device implementations that can't obtain | ||
426 | * the USB frame number to keep their own frame counters as long as they | ||
427 | * match the size and frequency of the frame number associated with USB | ||
428 | * SOF tokens. The SOF values sent by such devices differ from the USB | ||
429 | * SOF tokens by a fixed offset that needs to be estimated and accounted | ||
430 | * for to make timestamp recovery as accurate as possible. | ||
431 | * | ||
432 | * The offset is estimated the first time a device SOF value is received | ||
433 | * as the difference between the host and device SOF values. As the two | ||
434 | * SOF values can differ slightly due to transmission delays, consider | ||
435 | * that the offset is null if the difference is not higher than 10 ms | ||
436 | * (negative differences can not happen and are thus considered as an | ||
437 | * offset). The video commit control wDelay field should be used to | ||
438 | * compute a dynamic threshold instead of using a fixed 10 ms value, but | ||
439 | * devices don't report reliable wDelay values. | ||
440 | * | ||
441 | * See uvc_video_clock_host_sof() for an explanation regarding why only | ||
442 | * the 8 LSBs of the delta are kept. | ||
443 | */ | ||
444 | if (stream->clock.sof_offset == (u16)-1) { | ||
445 | u16 delta_sof = (host_sof - dev_sof) & 255; | ||
446 | if (delta_sof >= 10) | ||
447 | stream->clock.sof_offset = delta_sof; | ||
448 | else | ||
449 | stream->clock.sof_offset = 0; | ||
450 | } | ||
451 | |||
452 | dev_sof = (dev_sof + stream->clock.sof_offset) & 2047; | ||
453 | |||
454 | spin_lock_irqsave(&stream->clock.lock, flags); | ||
455 | |||
456 | sample = &stream->clock.samples[stream->clock.head]; | ||
457 | sample->dev_stc = get_unaligned_le32(&data[header_size - 6]); | ||
458 | sample->dev_sof = dev_sof; | ||
459 | sample->host_sof = host_sof; | ||
460 | sample->host_ts = ts; | ||
461 | |||
462 | /* Update the sliding window head and count. */ | ||
463 | stream->clock.head = (stream->clock.head + 1) % stream->clock.size; | ||
464 | |||
465 | if (stream->clock.count < stream->clock.size) | ||
466 | stream->clock.count++; | ||
467 | |||
468 | spin_unlock_irqrestore(&stream->clock.lock, flags); | ||
469 | } | ||
470 | |||
471 | static int uvc_video_clock_init(struct uvc_streaming *stream) | ||
472 | { | ||
473 | struct uvc_clock *clock = &stream->clock; | ||
474 | |||
475 | spin_lock_init(&clock->lock); | ||
476 | clock->head = 0; | ||
477 | clock->count = 0; | ||
478 | clock->size = 32; | ||
479 | clock->last_sof = -1; | ||
480 | clock->sof_offset = -1; | ||
481 | |||
482 | clock->samples = kmalloc(clock->size * sizeof(*clock->samples), | ||
483 | GFP_KERNEL); | ||
484 | if (clock->samples == NULL) | ||
485 | return -ENOMEM; | ||
486 | |||
487 | return 0; | ||
488 | } | ||
489 | |||
490 | static void uvc_video_clock_cleanup(struct uvc_streaming *stream) | ||
491 | { | ||
492 | kfree(stream->clock.samples); | ||
493 | stream->clock.samples = NULL; | ||
494 | } | ||
495 | |||
496 | /* | ||
497 | * uvc_video_clock_host_sof - Return the host SOF value for a clock sample | ||
498 | * | ||
499 | * Host SOF counters reported by usb_get_current_frame_number() usually don't | ||
500 | * cover the whole 11-bits SOF range (0-2047) but are limited to the HCI frame | ||
501 | * schedule window. They can be limited to 8, 9 or 10 bits depending on the host | ||
502 | * controller and its configuration. | ||
503 | * | ||
504 | * We thus need to recover the SOF value corresponding to the host frame number. | ||
505 | * As the device and host frame numbers are sampled in a short interval, the | ||
506 | * difference between their values should be equal to a small delta plus an | ||
507 | * integer multiple of 256 caused by the host frame number limited precision. | ||
508 | * | ||
509 | * To obtain the recovered host SOF value, compute the small delta by masking | ||
510 | * the high bits of the host frame counter and device SOF difference and add it | ||
511 | * to the device SOF value. | ||
512 | */ | ||
513 | static u16 uvc_video_clock_host_sof(const struct uvc_clock_sample *sample) | ||
514 | { | ||
515 | /* The delta value can be negative. */ | ||
516 | s8 delta_sof; | ||
517 | |||
518 | delta_sof = (sample->host_sof - sample->dev_sof) & 255; | ||
519 | |||
520 | return (sample->dev_sof + delta_sof) & 2047; | ||
521 | } | ||
522 | |||
523 | /* | ||
524 | * uvc_video_clock_update - Update the buffer timestamp | ||
525 | * | ||
526 | * This function converts the buffer PTS timestamp to the host clock domain by | ||
527 | * going through the USB SOF clock domain and stores the result in the V4L2 | ||
528 | * buffer timestamp field. | ||
529 | * | ||
530 | * The relationship between the device clock and the host clock isn't known. | ||
531 | * However, the device and the host share the common USB SOF clock which can be | ||
532 | * used to recover that relationship. | ||
533 | * | ||
534 | * The relationship between the device clock and the USB SOF clock is considered | ||
535 | * to be linear over the clock samples sliding window and is given by | ||
536 | * | ||
537 | * SOF = m * PTS + p | ||
538 | * | ||
539 | * Several methods to compute the slope (m) and intercept (p) can be used. As | ||
540 | * the clock drift should be small compared to the sliding window size, we | ||
541 | * assume that the line that goes through the points at both ends of the window | ||
542 | * is a good approximation. Naming those points P1 and P2, we get | ||
543 | * | ||
544 | * SOF = (SOF2 - SOF1) / (STC2 - STC1) * PTS | ||
545 | * + (SOF1 * STC2 - SOF2 * STC1) / (STC2 - STC1) | ||
546 | * | ||
547 | * or | ||
548 | * | ||
549 | * SOF = ((SOF2 - SOF1) * PTS + SOF1 * STC2 - SOF2 * STC1) / (STC2 - STC1) (1) | ||
550 | * | ||
551 | * to avoid loosing precision in the division. Similarly, the host timestamp is | ||
552 | * computed with | ||
553 | * | ||
554 | * TS = ((TS2 - TS1) * PTS + TS1 * SOF2 - TS2 * SOF1) / (SOF2 - SOF1) (2) | ||
555 | * | ||
556 | * SOF values are coded on 11 bits by USB. We extend their precision with 16 | ||
557 | * decimal bits, leading to a 11.16 coding. | ||
558 | * | ||
559 | * TODO: To avoid surprises with device clock values, PTS/STC timestamps should | ||
560 | * be normalized using the nominal device clock frequency reported through the | ||
561 | * UVC descriptors. | ||
562 | * | ||
563 | * Both the PTS/STC and SOF counters roll over, after a fixed but device | ||
564 | * specific amount of time for PTS/STC and after 2048ms for SOF. As long as the | ||
565 | * sliding window size is smaller than the rollover period, differences computed | ||
566 | * on unsigned integers will produce the correct result. However, the p term in | ||
567 | * the linear relations will be miscomputed. | ||
568 | * | ||
569 | * To fix the issue, we subtract a constant from the PTS and STC values to bring | ||
570 | * PTS to half the 32 bit STC range. The sliding window STC values then fit into | ||
571 | * the 32 bit range without any rollover. | ||
572 | * | ||
573 | * Similarly, we add 2048 to the device SOF values to make sure that the SOF | ||
574 | * computed by (1) will never be smaller than 0. This offset is then compensated | ||
575 | * by adding 2048 to the SOF values used in (2). However, this doesn't prevent | ||
576 | * rollovers between (1) and (2): the SOF value computed by (1) can be slightly | ||
577 | * lower than 4096, and the host SOF counters can have rolled over to 2048. This | ||
578 | * case is handled by subtracting 2048 from the SOF value if it exceeds the host | ||
579 | * SOF value at the end of the sliding window. | ||
580 | * | ||
581 | * Finally we subtract a constant from the host timestamps to bring the first | ||
582 | * timestamp of the sliding window to 1s. | ||
583 | */ | ||
584 | void uvc_video_clock_update(struct uvc_streaming *stream, | ||
585 | struct v4l2_buffer *v4l2_buf, | ||
586 | struct uvc_buffer *buf) | ||
587 | { | ||
588 | struct uvc_clock *clock = &stream->clock; | ||
589 | struct uvc_clock_sample *first; | ||
590 | struct uvc_clock_sample *last; | ||
591 | unsigned long flags; | ||
592 | struct timespec ts; | ||
593 | u32 delta_stc; | ||
594 | u32 y1, y2; | ||
595 | u32 x1, x2; | ||
596 | u32 mean; | ||
597 | u32 sof; | ||
598 | u32 div; | ||
599 | u32 rem; | ||
600 | u64 y; | ||
601 | |||
602 | spin_lock_irqsave(&clock->lock, flags); | ||
603 | |||
604 | if (clock->count < clock->size) | ||
605 | goto done; | ||
606 | |||
607 | first = &clock->samples[clock->head]; | ||
608 | last = &clock->samples[(clock->head - 1) % clock->size]; | ||
609 | |||
610 | /* First step, PTS to SOF conversion. */ | ||
611 | delta_stc = buf->pts - (1UL << 31); | ||
612 | x1 = first->dev_stc - delta_stc; | ||
613 | x2 = last->dev_stc - delta_stc; | ||
614 | y1 = (first->dev_sof + 2048) << 16; | ||
615 | y2 = (last->dev_sof + 2048) << 16; | ||
616 | |||
617 | if (y2 < y1) | ||
618 | y2 += 2048 << 16; | ||
619 | |||
620 | y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2 | ||
621 | - (u64)y2 * (u64)x1; | ||
622 | y = div_u64(y, x2 - x1); | ||
623 | |||
624 | sof = y; | ||
625 | |||
626 | uvc_trace(UVC_TRACE_CLOCK, "%s: PTS %u y %llu.%06llu SOF %u.%06llu " | ||
627 | "(x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n", | ||
628 | stream->dev->name, buf->pts, | ||
629 | y >> 16, div_u64((y & 0xffff) * 1000000, 65536), | ||
630 | sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536), | ||
631 | x1, x2, y1, y2, clock->sof_offset); | ||
632 | |||
633 | /* Second step, SOF to host clock conversion. */ | ||
634 | ts = timespec_sub(last->host_ts, first->host_ts); | ||
635 | x1 = (uvc_video_clock_host_sof(first) + 2048) << 16; | ||
636 | x2 = (uvc_video_clock_host_sof(last) + 2048) << 16; | ||
637 | y1 = NSEC_PER_SEC; | ||
638 | y2 = (ts.tv_sec + 1) * NSEC_PER_SEC + ts.tv_nsec; | ||
639 | |||
640 | if (x2 < x1) | ||
641 | x2 += 2048 << 16; | ||
642 | |||
643 | /* Interpolated and host SOF timestamps can wrap around at slightly | ||
644 | * different times. Handle this by adding or removing 2048 to or from | ||
645 | * the computed SOF value to keep it close to the SOF samples mean | ||
646 | * value. | ||
647 | */ | ||
648 | mean = (x1 + x2) / 2; | ||
649 | if (mean - (1024 << 16) > sof) | ||
650 | sof += 2048 << 16; | ||
651 | else if (sof > mean + (1024 << 16)) | ||
652 | sof -= 2048 << 16; | ||
653 | |||
654 | y = (u64)(y2 - y1) * (u64)sof + (u64)y1 * (u64)x2 | ||
655 | - (u64)y2 * (u64)x1; | ||
656 | y = div_u64(y, x2 - x1); | ||
657 | |||
658 | div = div_u64_rem(y, NSEC_PER_SEC, &rem); | ||
659 | ts.tv_sec = first->host_ts.tv_sec - 1 + div; | ||
660 | ts.tv_nsec = first->host_ts.tv_nsec + rem; | ||
661 | if (ts.tv_nsec >= NSEC_PER_SEC) { | ||
662 | ts.tv_sec++; | ||
663 | ts.tv_nsec -= NSEC_PER_SEC; | ||
664 | } | ||
665 | |||
666 | uvc_trace(UVC_TRACE_CLOCK, "%s: SOF %u.%06llu y %llu ts %lu.%06lu " | ||
667 | "buf ts %lu.%06lu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n", | ||
668 | stream->dev->name, | ||
669 | sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536), | ||
670 | y, ts.tv_sec, ts.tv_nsec / NSEC_PER_USEC, | ||
671 | v4l2_buf->timestamp.tv_sec, v4l2_buf->timestamp.tv_usec, | ||
672 | x1, first->host_sof, first->dev_sof, | ||
673 | x2, last->host_sof, last->dev_sof, y1, y2); | ||
674 | |||
675 | /* Update the V4L2 buffer. */ | ||
676 | v4l2_buf->timestamp.tv_sec = ts.tv_sec; | ||
677 | v4l2_buf->timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC; | ||
678 | |||
679 | done: | ||
680 | spin_unlock_irqrestore(&stream->clock.lock, flags); | ||
681 | } | ||
682 | |||
360 | /* ------------------------------------------------------------------------ | 683 | /* ------------------------------------------------------------------------ |
361 | * Stream statistics | 684 | * Stream statistics |
362 | */ | 685 | */ |
@@ -637,6 +960,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, | |||
637 | uvc_video_stats_update(stream); | 960 | uvc_video_stats_update(stream); |
638 | } | 961 | } |
639 | 962 | ||
963 | uvc_video_clock_decode(stream, buf, data, len); | ||
640 | uvc_video_stats_decode(stream, data, len); | 964 | uvc_video_stats_decode(stream, data, len); |
641 | 965 | ||
642 | /* Store the payload FID bit and return immediately when the buffer is | 966 | /* Store the payload FID bit and return immediately when the buffer is |
@@ -1096,6 +1420,8 @@ static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers) | |||
1096 | 1420 | ||
1097 | if (free_buffers) | 1421 | if (free_buffers) |
1098 | uvc_free_urb_buffers(stream); | 1422 | uvc_free_urb_buffers(stream); |
1423 | |||
1424 | uvc_video_clock_cleanup(stream); | ||
1099 | } | 1425 | } |
1100 | 1426 | ||
1101 | /* | 1427 | /* |
@@ -1225,6 +1551,10 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) | |||
1225 | 1551 | ||
1226 | uvc_video_stats_start(stream); | 1552 | uvc_video_stats_start(stream); |
1227 | 1553 | ||
1554 | ret = uvc_video_clock_init(stream); | ||
1555 | if (ret < 0) | ||
1556 | return ret; | ||
1557 | |||
1228 | if (intf->num_altsetting > 1) { | 1558 | if (intf->num_altsetting > 1) { |
1229 | struct usb_host_endpoint *best_ep = NULL; | 1559 | struct usb_host_endpoint *best_ep = NULL; |
1230 | unsigned int best_psize = 3 * 1024; | 1560 | unsigned int best_psize = 3 * 1024; |
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index e4d4b6d02024..e9c19f53e4a1 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h | |||
@@ -329,6 +329,8 @@ struct uvc_buffer { | |||
329 | void *mem; | 329 | void *mem; |
330 | unsigned int length; | 330 | unsigned int length; |
331 | unsigned int bytesused; | 331 | unsigned int bytesused; |
332 | |||
333 | u32 pts; | ||
332 | }; | 334 | }; |
333 | 335 | ||
334 | #define UVC_QUEUE_DISCONNECTED (1 << 0) | 336 | #define UVC_QUEUE_DISCONNECTED (1 << 0) |
@@ -455,6 +457,25 @@ struct uvc_streaming { | |||
455 | struct uvc_stats_frame frame; | 457 | struct uvc_stats_frame frame; |
456 | struct uvc_stats_stream stream; | 458 | struct uvc_stats_stream stream; |
457 | } stats; | 459 | } stats; |
460 | |||
461 | /* Timestamps support. */ | ||
462 | struct uvc_clock { | ||
463 | struct uvc_clock_sample { | ||
464 | u32 dev_stc; | ||
465 | u16 dev_sof; | ||
466 | struct timespec host_ts; | ||
467 | u16 host_sof; | ||
468 | } *samples; | ||
469 | |||
470 | unsigned int head; | ||
471 | unsigned int count; | ||
472 | unsigned int size; | ||
473 | |||
474 | u16 last_sof; | ||
475 | u16 sof_offset; | ||
476 | |||
477 | spinlock_t lock; | ||
478 | } clock; | ||
458 | }; | 479 | }; |
459 | 480 | ||
460 | enum uvc_device_state { | 481 | enum uvc_device_state { |
@@ -527,6 +548,7 @@ struct uvc_driver { | |||
527 | #define UVC_TRACE_STATUS (1 << 9) | 548 | #define UVC_TRACE_STATUS (1 << 9) |
528 | #define UVC_TRACE_VIDEO (1 << 10) | 549 | #define UVC_TRACE_VIDEO (1 << 10) |
529 | #define UVC_TRACE_STATS (1 << 11) | 550 | #define UVC_TRACE_STATS (1 << 11) |
551 | #define UVC_TRACE_CLOCK (1 << 12) | ||
530 | 552 | ||
531 | #define UVC_WARN_MINMAX 0 | 553 | #define UVC_WARN_MINMAX 0 |
532 | #define UVC_WARN_PROBE_DEF 1 | 554 | #define UVC_WARN_PROBE_DEF 1 |
@@ -607,6 +629,9 @@ extern int uvc_probe_video(struct uvc_streaming *stream, | |||
607 | struct uvc_streaming_control *probe); | 629 | struct uvc_streaming_control *probe); |
608 | extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, | 630 | extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, |
609 | __u8 intfnum, __u8 cs, void *data, __u16 size); | 631 | __u8 intfnum, __u8 cs, void *data, __u16 size); |
632 | void uvc_video_clock_update(struct uvc_streaming *stream, | ||
633 | struct v4l2_buffer *v4l2_buf, | ||
634 | struct uvc_buffer *buf); | ||
610 | 635 | ||
611 | /* Status */ | 636 | /* Status */ |
612 | extern int uvc_status_init(struct uvc_device *dev); | 637 | extern int uvc_status_init(struct uvc_device *dev); |