Commit 954ee38d authored by Hans Verkuil's avatar Hans Verkuil

Merge tag 'tags/media-next-uvc-20240617-2' of...

Merge tag 'tags/media-next-uvc-20240617-2' of git://git.kernel.org/pub/scm/linux/kernel/git/pinchartl/linux.git into media_stage

uvcvideo fixes and improvements:

- add/fix quirks
- improve timestamp handling
- improve Power Line Frequency handling
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
parents 9ccbcd53 8c40efed
......@@ -459,6 +459,94 @@ static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping,
data[first+1] = min_t(int, abs(value), 0xff);
}
static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = {
.id = V4L2_CID_POWER_LINE_FREQUENCY,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
.size = 2,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_MENU,
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
.menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
V4L2_CID_POWER_LINE_FREQUENCY_50HZ),
};
static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = {
.id = V4L2_CID_POWER_LINE_FREQUENCY,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
.size = 2,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_MENU,
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
.menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
V4L2_CID_POWER_LINE_FREQUENCY_DISABLED),
};
static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc15 = {
.id = V4L2_CID_POWER_LINE_FREQUENCY,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
.size = 2,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_MENU,
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
.menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_AUTO,
V4L2_CID_POWER_LINE_FREQUENCY_DISABLED),
};
static const struct uvc_control_mapping *uvc_ctrl_filter_plf_mapping(
struct uvc_video_chain *chain, struct uvc_control *ctrl)
{
const struct uvc_control_mapping *out_mapping =
&uvc_ctrl_power_line_mapping_uvc11;
u8 *buf __free(kfree) = NULL;
u8 init_val;
int ret;
buf = kmalloc(sizeof(*buf), GFP_KERNEL);
if (!buf)
return NULL;
/* Save the current PLF value, so we can restore it. */
ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id,
chain->dev->intfnum, ctrl->info.selector,
buf, sizeof(*buf));
/* If we cannot read the control skip it. */
if (ret)
return NULL;
init_val = *buf;
/* If PLF value cannot be set to off, it is limited. */
*buf = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED;
ret = uvc_query_ctrl(chain->dev, UVC_SET_CUR, ctrl->entity->id,
chain->dev->intfnum, ctrl->info.selector,
buf, sizeof(*buf));
if (ret)
return &uvc_ctrl_power_line_mapping_limited;
/* UVC 1.1 does not define auto, we can exit. */
if (chain->dev->uvc_version < 0x150)
goto end;
/* Check if the device supports auto. */
*buf = V4L2_CID_POWER_LINE_FREQUENCY_AUTO;
ret = uvc_query_ctrl(chain->dev, UVC_SET_CUR, ctrl->entity->id,
chain->dev->intfnum, ctrl->info.selector,
buf, sizeof(*buf));
if (!ret)
out_mapping = &uvc_ctrl_power_line_mapping_uvc15;
end:
/* Restore initial value and add mapping. */
*buf = init_val;
uvc_query_ctrl(chain->dev, UVC_SET_CUR, ctrl->entity->id,
chain->dev->intfnum, ctrl->info.selector,
buf, sizeof(*buf));
return out_mapping;
}
static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
{
.id = V4L2_CID_BRIGHTNESS,
......@@ -748,52 +836,11 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
.data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
},
};
const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = {
.id = V4L2_CID_POWER_LINE_FREQUENCY,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
.size = 2,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_MENU,
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
.menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
V4L2_CID_POWER_LINE_FREQUENCY_50HZ),
};
const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = {
.id = V4L2_CID_POWER_LINE_FREQUENCY,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
.size = 2,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_MENU,
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
.menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
V4L2_CID_POWER_LINE_FREQUENCY_DISABLED),
};
static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc11[] = {
&uvc_ctrl_power_line_mapping_uvc11,
NULL, /* Sentinel */
};
static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc15 = {
.id = V4L2_CID_POWER_LINE_FREQUENCY,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
.size = 2,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_MENU,
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
.menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_AUTO,
V4L2_CID_POWER_LINE_FREQUENCY_DISABLED),
};
static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc15[] = {
&uvc_ctrl_power_line_mapping_uvc15,
NULL, /* Sentinel */
{
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
.filter_mapping = uvc_ctrl_filter_plf_mapping,
},
};
/* ------------------------------------------------------------------------
......@@ -2031,7 +2078,13 @@ static int uvc_ctrl_get_flags(struct uvc_device *dev,
else
ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id,
dev->intfnum, info->selector, data, 1);
if (!ret)
if (!ret) {
info->flags &= ~(UVC_CTRL_FLAG_GET_CUR |
UVC_CTRL_FLAG_SET_CUR |
UVC_CTRL_FLAG_AUTO_UPDATE |
UVC_CTRL_FLAG_ASYNCHRONOUS);
info->flags |= (data[0] & UVC_CONTROL_CAP_GET ?
UVC_CTRL_FLAG_GET_CUR : 0)
| (data[0] & UVC_CONTROL_CAP_SET ?
......@@ -2040,6 +2093,7 @@ static int uvc_ctrl_get_flags(struct uvc_device *dev,
UVC_CTRL_FLAG_AUTO_UPDATE : 0)
| (data[0] & UVC_CONTROL_CAP_ASYNCHRONOUS ?
UVC_CTRL_FLAG_ASYNCHRONOUS : 0);
}
kfree(data);
return ret;
......@@ -2591,7 +2645,6 @@ static void uvc_ctrl_prune_entity(struct uvc_device *dev,
static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
struct uvc_control *ctrl)
{
const struct uvc_control_mapping **mappings;
unsigned int i;
/*
......@@ -2623,46 +2676,16 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
if (!ctrl->initialized)
return;
/*
* First check if the device provides a custom mapping for this control,
* used to override standard mappings for non-conformant devices. Don't
* process standard mappings if a custom mapping is found. This
* mechanism doesn't support combining standard and custom mappings for
* a single control.
*/
if (chain->dev->info->mappings) {
bool custom = false;
for (i = 0; chain->dev->info->mappings[i]; ++i) {
const struct uvc_control_mapping *mapping =
chain->dev->info->mappings[i];
if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
ctrl->info.selector == mapping->selector) {
__uvc_ctrl_add_mapping(chain, ctrl, mapping);
custom = true;
}
}
if (custom)
return;
}
/* Process common mappings next. */
/* Process common mappings. */
for (i = 0; i < ARRAY_SIZE(uvc_ctrl_mappings); ++i) {
const struct uvc_control_mapping *mapping = &uvc_ctrl_mappings[i];
if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
ctrl->info.selector == mapping->selector)
__uvc_ctrl_add_mapping(chain, ctrl, mapping);
}
/* Finally process version-specific mappings. */
mappings = chain->dev->uvc_version < 0x0150
? uvc_ctrl_mappings_uvc11 : uvc_ctrl_mappings_uvc15;
for (i = 0; mappings[i]; ++i) {
const struct uvc_control_mapping *mapping = mappings[i];
/* Let the device provide a custom mapping. */
if (mapping->filter_mapping) {
mapping = mapping->filter_mapping(chain, ctrl);
if (!mapping)
continue;
}
if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
ctrl->info.selector == mapping->selector)
......
......@@ -687,16 +687,26 @@ static int uvc_parse_streaming(struct uvc_device *dev,
goto error;
}
size = nformats * sizeof(*format) + nframes * sizeof(*frame)
/*
* Allocate memory for the formats, the frames and the intervals,
* plus any required padding to guarantee that everything has the
* correct alignment.
*/
size = nformats * sizeof(*format);
size = ALIGN(size, __alignof__(*frame)) + nframes * sizeof(*frame);
size = ALIGN(size, __alignof__(*interval))
+ nintervals * sizeof(*interval);
format = kzalloc(size, GFP_KERNEL);
if (format == NULL) {
if (!format) {
ret = -ENOMEM;
goto error;
}
frame = (struct uvc_frame *)&format[nformats];
interval = (u32 *)&frame[nframes];
frame = (void *)format + nformats * sizeof(*format);
frame = PTR_ALIGN(frame, __alignof__(*frame));
interval = (void *)frame + nframes * sizeof(*frame);
interval = PTR_ALIGN(interval, __alignof__(*interval));
streaming->formats = format;
streaming->nformats = 0;
......@@ -2390,20 +2400,6 @@ MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
* Driver initialization and cleanup
*/
static const struct uvc_device_info uvc_ctrl_power_line_limited = {
.mappings = (const struct uvc_control_mapping *[]) {
&uvc_ctrl_power_line_mapping_limited,
NULL, /* Sentinel */
},
};
static const struct uvc_device_info uvc_ctrl_power_line_uvc11 = {
.mappings = (const struct uvc_control_mapping *[]) {
&uvc_ctrl_power_line_mapping_uvc11,
NULL, /* Sentinel */
},
};
static const struct uvc_device_info uvc_quirk_probe_minmax = {
.quirks = UVC_QUIRK_PROBE_MINMAX,
};
......@@ -2434,33 +2430,17 @@ static const struct uvc_device_info uvc_quirk_force_y8 = {
* though they are compliant.
*/
static const struct usb_device_id uvc_ids[] = {
/* Quanta USB2.0 HD UVC Webcam */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0408,
.idProduct = 0x3090,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
/* Quanta USB2.0 HD UVC Webcam */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0408,
.idProduct = 0x4030,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
/* Quanta USB2.0 HD UVC Webcam */
/* Quanta ACER HD User Facing */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0408,
.idProduct = 0x4034,
.idProduct = 0x4035,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = UVC_PC_PROTOCOL_15,
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
.driver_info = (kernel_ulong_t)&(const struct uvc_device_info){
.uvc_version = 0x010a,
} },
/* LogiLink Wireless Webcam */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
......@@ -2580,7 +2560,17 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT) },
.driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT
| UVC_QUIRK_INVALID_DEVICE_SOF) },
/* Logitech HD Pro Webcam C922 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x046d,
.idProduct = 0x085c,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_QUIRK(UVC_QUIRK_INVALID_DEVICE_SOF) },
/* Logitech Rally Bar Huddle */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
......@@ -2617,42 +2607,6 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTRICT_FRAME_RATE) },
/* Chicony EasyCamera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x04f2,
.idProduct = 0xb5eb,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
/* Chicony Electronics Co., Ltd Integrated Camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x04f2,
.idProduct = 0xb67c,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = UVC_PC_PROTOCOL_15,
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 },
/* Chicony EasyCamera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x04f2,
.idProduct = 0xb6ba,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
/* Chicony EasyCamera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x04f2,
.idProduct = 0xb746,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
/* Alcor Micro AU3820 (Future Boy PC USB Webcam) */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
......@@ -3037,15 +2991,6 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_QUIRK(UVC_QUIRK_FORCE_BPP) },
/* SunplusIT Inc HD Camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x2b7e,
.idProduct = 0xb752,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = UVC_PC_PROTOCOL_15,
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 },
/* Insta360 Link */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
......@@ -3055,51 +3000,6 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_QUIRK(UVC_QUIRK_DISABLE_AUTOSUSPEND) },
/* Lenovo Integrated Camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x30c9,
.idProduct = 0x0093,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = UVC_PC_PROTOCOL_15,
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 },
/* Sonix Technology USB 2.0 Camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x3277,
.idProduct = 0x0072,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
/* Shine-Optics Integrated Camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x3277,
.idProduct = 0x009e,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = UVC_PC_PROTOCOL_15,
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 },
/* Acer EasyCamera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x5986,
.idProduct = 0x1172,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
/* Acer EasyCamera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x5986,
.idProduct = 0x1180,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
/* Intel D410/ASR depth camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
......
......@@ -214,13 +214,13 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream,
* Compute a bandwidth estimation by multiplying the frame
* size by the number of video frames per second, divide the
* result by the number of USB frames (or micro-frames for
* high-speed devices) per second and add the UVC header size
* (assumed to be 12 bytes long).
* high- and super-speed devices) per second and add the UVC
* header size (assumed to be 12 bytes long).
*/
bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp;
bandwidth *= 10000000 / interval + 1;
bandwidth /= 1000;
if (stream->dev->udev->speed == USB_SPEED_HIGH)
if (stream->dev->udev->speed >= USB_SPEED_HIGH)
bandwidth /= 8;
bandwidth += 12;
......@@ -466,18 +466,49 @@ static inline ktime_t uvc_video_get_time(void)
return ktime_get_real();
}
static void uvc_video_clock_add_sample(struct uvc_clock *clock,
const struct uvc_clock_sample *sample)
{
unsigned long flags;
/*
* If we write new data on the position where we had the last
* overflow, remove the overflow pointer. There is no SOF overflow
* in the whole circular buffer.
*/
if (clock->head == clock->last_sof_overflow)
clock->last_sof_overflow = -1;
spin_lock_irqsave(&clock->lock, flags);
if (clock->count > 0 && clock->last_sof > sample->dev_sof) {
/*
* Remove data from the circular buffer that is older than the
* last SOF overflow. We only support one SOF overflow per
* circular buffer.
*/
if (clock->last_sof_overflow != -1)
clock->count = (clock->head - clock->last_sof_overflow
+ clock->size) % clock->size;
clock->last_sof_overflow = clock->head;
}
/* Add sample. */
clock->samples[clock->head] = *sample;
clock->head = (clock->head + 1) % clock->size;
clock->count = min(clock->count + 1, clock->size);
spin_unlock_irqrestore(&clock->lock, flags);
}
static void
uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf,
const u8 *data, int len)
{
struct uvc_clock_sample *sample;
struct uvc_clock_sample sample;
unsigned int header_size;
bool has_pts = false;
bool has_scr = false;
unsigned long flags;
ktime_t time;
u16 host_sof;
u16 dev_sof;
switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) {
case UVC_STREAM_PTS | UVC_STREAM_SCR:
......@@ -522,14 +553,51 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf,
* all the data packets of the same frame contains the same SOF. In that
* case only the first one will match the host_sof.
*/
dev_sof = get_unaligned_le16(&data[header_size - 2]);
if (dev_sof == stream->clock.last_sof)
sample.dev_sof = get_unaligned_le16(&data[header_size - 2]);
if (sample.dev_sof == stream->clock.last_sof)
return;
stream->clock.last_sof = dev_sof;
sample.dev_stc = get_unaligned_le32(&data[header_size - 6]);
host_sof = usb_get_current_frame_number(stream->dev->udev);
time = uvc_video_get_time();
/*
* STC (Source Time Clock) is the clock used by the camera. The UVC 1.5
* standard states that it "must be captured when the first video data
* of a video frame is put on the USB bus". This is generally understood
* as requiring devices to clear the payload header's SCR bit before
* the first packet containing video data.
*
* Most vendors follow that interpretation, but some (namely SunplusIT
* on some devices) always set the `UVC_STREAM_SCR` bit, fill the SCR
* field with 0's,and expect that the driver only processes the SCR if
* there is data in the packet.
*
* Ignore all the hardware timestamp information if we haven't received
* any data for this frame yet, the packet contains no data, and both
* STC and SOF are zero. This heuristics should be safe on compliant
* devices. This should be safe with compliant devices, as in the very
* unlikely case where a UVC 1.1 device would send timing information
* only before the first packet containing data, and both STC and SOF
* happen to be zero for a particular frame, we would only miss one
* clock sample from many and the clock recovery algorithm wouldn't
* suffer from this condition.
*/
if (buf && buf->bytesused == 0 && len == header_size &&
sample.dev_stc == 0 && sample.dev_sof == 0)
return;
sample.host_sof = usb_get_current_frame_number(stream->dev->udev);
/*
* On some devices, like the Logitech C922, the device SOF does not run
* at a stable rate of 1kHz. For those devices use the host SOF instead.
* In the tests performed so far, this improves the timestamp precision.
* This is probably explained by a small packet handling jitter from the
* host, but the exact reason hasn't been fully determined.
*/
if (stream->dev->quirks & UVC_QUIRK_INVALID_DEVICE_SOF)
sample.dev_sof = sample.host_sof;
sample.host_time = uvc_video_get_time();
/*
* The UVC specification allows device implementations that can't obtain
......@@ -552,46 +620,29 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf,
* the 8 LSBs of the delta are kept.
*/
if (stream->clock.sof_offset == (u16)-1) {
u16 delta_sof = (host_sof - dev_sof) & 255;
u16 delta_sof = (sample.host_sof - sample.dev_sof) & 255;
if (delta_sof >= 10)
stream->clock.sof_offset = delta_sof;
else
stream->clock.sof_offset = 0;
}
dev_sof = (dev_sof + stream->clock.sof_offset) & 2047;
spin_lock_irqsave(&stream->clock.lock, flags);
sample = &stream->clock.samples[stream->clock.head];
sample->dev_stc = get_unaligned_le32(&data[header_size - 6]);
sample->dev_sof = dev_sof;
sample->host_sof = host_sof;
sample->host_time = time;
/* Update the sliding window head and count. */
stream->clock.head = (stream->clock.head + 1) % stream->clock.size;
if (stream->clock.count < stream->clock.size)
stream->clock.count++;
spin_unlock_irqrestore(&stream->clock.lock, flags);
sample.dev_sof = (sample.dev_sof + stream->clock.sof_offset) & 2047;
uvc_video_clock_add_sample(&stream->clock, &sample);
stream->clock.last_sof = sample.dev_sof;
}
static void uvc_video_clock_reset(struct uvc_streaming *stream)
static void uvc_video_clock_reset(struct uvc_clock *clock)
{
struct uvc_clock *clock = &stream->clock;
clock->head = 0;
clock->count = 0;
clock->last_sof = -1;
clock->last_sof_overflow = -1;
clock->sof_offset = -1;
}
static int uvc_video_clock_init(struct uvc_streaming *stream)
static int uvc_video_clock_init(struct uvc_clock *clock)
{
struct uvc_clock *clock = &stream->clock;
spin_lock_init(&clock->lock);
clock->size = 32;
......@@ -600,15 +651,15 @@ static int uvc_video_clock_init(struct uvc_streaming *stream)
if (clock->samples == NULL)
return -ENOMEM;
uvc_video_clock_reset(stream);
uvc_video_clock_reset(clock);
return 0;
}
static void uvc_video_clock_cleanup(struct uvc_streaming *stream)
static void uvc_video_clock_cleanup(struct uvc_clock *clock)
{
kfree(stream->clock.samples);
stream->clock.samples = NULL;
kfree(clock->samples);
clock->samples = NULL;
}
/*
......@@ -709,11 +760,11 @@ void uvc_video_clock_update(struct uvc_streaming *stream,
unsigned long flags;
u64 timestamp;
u32 delta_stc;
u32 y1, y2;
u32 y1;
u32 x1, x2;
u32 mean;
u32 sof;
u64 y;
u64 y, y2;
if (!uvc_hw_timestamps_param)
return;
......@@ -728,11 +779,11 @@ void uvc_video_clock_update(struct uvc_streaming *stream,
spin_lock_irqsave(&clock->lock, flags);
if (clock->count < clock->size)
if (clock->count < 2)
goto done;
first = &clock->samples[clock->head];
last = &clock->samples[(clock->head - 1) % clock->size];
first = &clock->samples[(clock->head - clock->count + clock->size) % clock->size];
last = &clock->samples[(clock->head - 1 + clock->size) % clock->size];
/* First step, PTS to SOF conversion. */
delta_stc = buf->pts - (1UL << 31);
......@@ -746,6 +797,18 @@ void uvc_video_clock_update(struct uvc_streaming *stream,
if (y2 < y1)
y2 += 2048 << 16;
/*
* Have at least 1/4 of a second of timestamps before we
* try to do any calculation. Otherwise we do not have enough
* precision. This value was determined by running Android CTS
* on different devices.
*
* dev_sof runs at 1KHz, and we have a fixed point precision of
* 16 bits.
*/
if ((y2 - y1) < ((1000 / 4) << 16))
goto done;
y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2
- (u64)y2 * (u64)x1;
y = div_u64(y, x2 - x1);
......@@ -753,7 +816,7 @@ void uvc_video_clock_update(struct uvc_streaming *stream,
sof = y;
uvc_dbg(stream->dev, CLOCK,
"%s: PTS %u y %llu.%06llu SOF %u.%06llu (x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n",
"%s: PTS %u y %llu.%06llu SOF %u.%06llu (x1 %u x2 %u y1 %u y2 %llu SOF offset %u)\n",
stream->dev->name, buf->pts,
y >> 16, div_u64((y & 0xffff) * 1000000, 65536),
sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536),
......@@ -768,7 +831,7 @@ void uvc_video_clock_update(struct uvc_streaming *stream,
goto done;
y1 = NSEC_PER_SEC;
y2 = (u32)ktime_to_ns(ktime_sub(last->host_time, first->host_time)) + y1;
y2 = ktime_to_ns(ktime_sub(last->host_time, first->host_time)) + y1;
/*
* Interpolated and host SOF timestamps can wrap around at slightly
......@@ -789,7 +852,7 @@ void uvc_video_clock_update(struct uvc_streaming *stream,
timestamp = ktime_to_ns(first->host_time) + y - y1;
uvc_dbg(stream->dev, CLOCK,
"%s: SOF %u.%06llu y %llu ts %llu buf ts %llu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n",
"%s: SOF %u.%06llu y %llu ts %llu buf ts %llu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %llu)\n",
stream->dev->name,
sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536),
y, timestamp, vbuf->vb2_buf.timestamp,
......@@ -2071,7 +2134,7 @@ int uvc_video_resume(struct uvc_streaming *stream, int reset)
stream->frozen = 0;
uvc_video_clock_reset(stream);
uvc_video_clock_reset(&stream->clock);
if (!uvc_queue_streaming(&stream->queue))
return 0;
......@@ -2220,7 +2283,7 @@ int uvc_video_start_streaming(struct uvc_streaming *stream)
{
int ret;
ret = uvc_video_clock_init(stream);
ret = uvc_video_clock_init(&stream->clock);
if (ret < 0)
return ret;
......@@ -2238,7 +2301,7 @@ int uvc_video_start_streaming(struct uvc_streaming *stream)
error_video:
usb_set_interface(stream->dev->udev, stream->intfnum, 0);
error_commit:
uvc_video_clock_cleanup(stream);
uvc_video_clock_cleanup(&stream->clock);
return ret;
}
......@@ -2266,5 +2329,5 @@ void uvc_video_stop_streaming(struct uvc_streaming *stream)
usb_clear_halt(stream->dev->udev, pipe);
}
uvc_video_clock_cleanup(stream);
uvc_video_clock_cleanup(&stream->clock);
}
......@@ -75,6 +75,7 @@
#define UVC_QUIRK_WAKE_AUTOSUSPEND 0x00002000
#define UVC_QUIRK_NO_RESET_RESUME 0x00004000
#define UVC_QUIRK_DISABLE_AUTOSUSPEND 0x00008000
#define UVC_QUIRK_INVALID_DEVICE_SOF 0x00010000
/* Format flags */
#define UVC_FMT_FLAG_COMPRESSED 0x00000001
......@@ -86,7 +87,9 @@
struct gpio_desc;
struct sg_table;
struct uvc_control;
struct uvc_device;
struct uvc_video_chain;
/*
* TODO: Put the most frequently accessed fields at the beginning of
......@@ -125,6 +128,9 @@ struct uvc_control_mapping {
s32 master_manual;
u32 slave_ids[2];
const struct uvc_control_mapping *(*filter_mapping)
(struct uvc_video_chain *chain,
struct uvc_control *ctrl);
s32 (*get)(struct uvc_control_mapping *mapping, u8 query,
const u8 *data);
void (*set)(struct uvc_control_mapping *mapping, s32 value,
......@@ -500,6 +506,7 @@ struct uvc_streaming {
unsigned int head;
unsigned int count;
unsigned int size;
unsigned int last_sof_overflow;
u16 last_sof;
u16 sof_offset;
......@@ -524,7 +531,6 @@ struct uvc_device_info {
u32 quirks;
u32 meta_format;
u16 uvc_version;
const struct uvc_control_mapping **mappings;
};
struct uvc_status_streaming {
......@@ -750,8 +756,6 @@ int uvc_status_start(struct uvc_device *dev, gfp_t flags);
void uvc_status_stop(struct uvc_device *dev);
/* Controls */
extern const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited;
extern const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11;
extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops;
int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment