diff options
Diffstat (limited to 'drivers/media/usb/uvc')
-rw-r--r-- | drivers/media/usb/uvc/uvc_ctrl.c | 46 | ||||
-rw-r--r-- | drivers/media/usb/uvc/uvc_debugfs.c | 20 | ||||
-rw-r--r-- | drivers/media/usb/uvc/uvc_driver.c | 71 | ||||
-rw-r--r-- | drivers/media/usb/uvc/uvc_entity.c | 35 | ||||
-rw-r--r-- | drivers/media/usb/uvc/uvc_v4l2.c | 34 | ||||
-rw-r--r-- | drivers/media/usb/uvc/uvc_video.c | 6 | ||||
-rw-r--r-- | drivers/media/usb/uvc/uvcvideo.h | 8 |
7 files changed, 173 insertions, 47 deletions
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index e399b9fad757..f479d8971dfb 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -773,12 +773,16 @@ static s32 uvc_get_le_value(struct uvc_control_mapping *mapping, offset &= 7; mask = ((1LL << bits) - 1) << offset; - for (; bits > 0; data++) { + while (1) { u8 byte = *data & mask; value |= offset > 0 ? (byte >> offset) : (byte << (-offset)); bits -= 8 - (offset > 0 ? offset : 0); + if (bits <= 0) + break; + offset -= 8; mask = (1 << bits) - 1; + data++; } /* Sign-extend the value if needed. */ @@ -1844,30 +1848,35 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, { struct uvc_entity *entity; struct uvc_control *ctrl; - unsigned int i, found = 0; + unsigned int i; + bool found; u32 reqflags; u16 size; u8 *data = NULL; int ret; /* Find the extension unit. */ + found = false; list_for_each_entry(entity, &chain->entities, chain) { if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT && - entity->id == xqry->unit) + entity->id == xqry->unit) { + found = true; break; + } } - if (entity->id != xqry->unit) { + if (!found) { uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n", xqry->unit); return -ENOENT; } /* Find the control and perform delayed initialization if needed. */ + found = false; for (i = 0; i < entity->ncontrols; ++i) { ctrl = &entity->controls[i]; if (ctrl->index == xqry->selector - 1) { - found = 1; + found = true; break; } } @@ -2011,25 +2020,14 @@ int uvc_ctrl_restore_values(struct uvc_device *dev) static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl, const struct uvc_control_info *info) { - int ret = 0; - ctrl->info = *info; INIT_LIST_HEAD(&ctrl->info.mappings); /* Allocate an array to save control values (cur, def, max, etc.) */ ctrl->uvc_data = kzalloc(ctrl->info.size * UVC_CTRL_DATA_LAST + 1, GFP_KERNEL); - if (ctrl->uvc_data == NULL) { - ret = -ENOMEM; - goto done; - } - - /* - * Retrieve control flags from the device. Ignore errors and work with - * default flag values from the uvc_ctrl array when the device doesn't - * properly implement GET_INFO on standard controls. - */ - uvc_ctrl_get_flags(dev, ctrl, &ctrl->info); + if (!ctrl->uvc_data) + return -ENOMEM; ctrl->initialized = 1; @@ -2037,10 +2035,7 @@ static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl, "entity %u\n", ctrl->info.entity, ctrl->info.selector, dev->udev->devpath, ctrl->entity->id); -done: - if (ret < 0) - kfree(ctrl->uvc_data); - return ret; + return 0; } /* @@ -2253,6 +2248,13 @@ static void uvc_ctrl_init_ctrl(struct uvc_device *dev, struct uvc_control *ctrl) if (uvc_entity_match_guid(ctrl->entity, info->entity) && ctrl->index == info->index) { uvc_ctrl_add_info(dev, ctrl, info); + /* + * Retrieve control flags from the device. Ignore errors + * and work with default flag values from the uvc_ctrl + * array when the device doesn't properly implement + * GET_INFO on standard controls. + */ + uvc_ctrl_get_flags(dev, ctrl, &ctrl->info); break; } } diff --git a/drivers/media/usb/uvc/uvc_debugfs.c b/drivers/media/usb/uvc/uvc_debugfs.c index 2b8af4b54117..1a1258d4ffca 100644 --- a/drivers/media/usb/uvc/uvc_debugfs.c +++ b/drivers/media/usb/uvc/uvc_debugfs.c @@ -73,7 +73,6 @@ static struct dentry *uvc_debugfs_root_dir; void uvc_debugfs_init_stream(struct uvc_streaming *stream) { struct usb_device *udev = stream->dev->udev; - struct dentry *dent; char dir_name[33]; if (uvc_debugfs_root_dir == NULL) @@ -82,22 +81,11 @@ void uvc_debugfs_init_stream(struct uvc_streaming *stream) snprintf(dir_name, sizeof(dir_name), "%u-%u-%u", udev->bus->busnum, udev->devnum, stream->intfnum); - dent = debugfs_create_dir(dir_name, uvc_debugfs_root_dir); - if (IS_ERR_OR_NULL(dent)) { - uvc_printk(KERN_INFO, "Unable to create debugfs %s " - "directory.\n", dir_name); - return; - } - - stream->debugfs_dir = dent; + stream->debugfs_dir = debugfs_create_dir(dir_name, + uvc_debugfs_root_dir); - dent = debugfs_create_file("stats", 0444, stream->debugfs_dir, - stream, &uvc_debugfs_stats_fops); - if (IS_ERR_OR_NULL(dent)) { - uvc_printk(KERN_INFO, "Unable to create debugfs stats file.\n"); - uvc_debugfs_cleanup_stream(stream); - return; - } + debugfs_create_file("stats", 0444, stream->debugfs_dir, stream, + &uvc_debugfs_stats_fops); } void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 431d86e1c94b..ddb9eaa11be7 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -214,6 +214,11 @@ static struct uvc_format_desc uvc_fmts[] = { .guid = UVC_GUID_FORMAT_CNF4, .fcc = V4L2_PIX_FMT_CNF4, }, + { + .name = "HEVC", + .guid = UVC_GUID_FORMAT_HEVC, + .fcc = V4L2_PIX_FMT_HEVC, + }, }; /* ------------------------------------------------------------------------ @@ -248,10 +253,10 @@ static struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16]) return NULL; } -static u32 uvc_colorspace(const u8 primaries) +static enum v4l2_colorspace uvc_colorspace(const u8 primaries) { - static const u8 colorprimaries[] = { - 0, + static const enum v4l2_colorspace colorprimaries[] = { + V4L2_COLORSPACE_DEFAULT, /* Unspecified */ V4L2_COLORSPACE_SRGB, V4L2_COLORSPACE_470_SYSTEM_M, V4L2_COLORSPACE_470_SYSTEM_BG, @@ -262,7 +267,61 @@ static u32 uvc_colorspace(const u8 primaries) if (primaries < ARRAY_SIZE(colorprimaries)) return colorprimaries[primaries]; - return 0; + return V4L2_COLORSPACE_DEFAULT; /* Reserved */ +} + +static enum v4l2_xfer_func uvc_xfer_func(const u8 transfer_characteristics) +{ + /* + * V4L2 does not currently have definitions for all possible values of + * UVC transfer characteristics. If v4l2_xfer_func is extended with new + * values, the mapping below should be updated. + * + * Substitutions are taken from the mapping given for + * V4L2_XFER_FUNC_DEFAULT documented in videodev2.h. + */ + static const enum v4l2_xfer_func xfer_funcs[] = { + V4L2_XFER_FUNC_DEFAULT, /* Unspecified */ + V4L2_XFER_FUNC_709, + V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 M */ + V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 B, G */ + V4L2_XFER_FUNC_709, /* Substitution for SMPTE 170M */ + V4L2_XFER_FUNC_SMPTE240M, + V4L2_XFER_FUNC_NONE, + V4L2_XFER_FUNC_SRGB, + }; + + if (transfer_characteristics < ARRAY_SIZE(xfer_funcs)) + return xfer_funcs[transfer_characteristics]; + + return V4L2_XFER_FUNC_DEFAULT; /* Reserved */ +} + +static enum v4l2_ycbcr_encoding uvc_ycbcr_enc(const u8 matrix_coefficients) +{ + /* + * V4L2 does not currently have definitions for all possible values of + * UVC matrix coefficients. If v4l2_ycbcr_encoding is extended with new + * values, the mapping below should be updated. + * + * Substitutions are taken from the mapping given for + * V4L2_YCBCR_ENC_DEFAULT documented in videodev2.h. + * + * FCC is assumed to be close enough to 601. + */ + static const enum v4l2_ycbcr_encoding ycbcr_encs[] = { + V4L2_YCBCR_ENC_DEFAULT, /* Unspecified */ + V4L2_YCBCR_ENC_709, + V4L2_YCBCR_ENC_601, /* Substitution for FCC */ + V4L2_YCBCR_ENC_601, /* Substitution for BT.470-2 B, G */ + V4L2_YCBCR_ENC_601, + V4L2_YCBCR_ENC_SMPTE240M, + }; + + if (matrix_coefficients < ARRAY_SIZE(ycbcr_encs)) + return ycbcr_encs[matrix_coefficients]; + + return V4L2_YCBCR_ENC_DEFAULT; /* Reserved */ } /* Simplify a fraction using a simple continued fraction decomposition. The @@ -284,7 +343,7 @@ void uvc_simplify_fraction(u32 *numerator, u32 *denominator, return; /* Convert the fraction to a simple continued fraction. See - * http://mathforum.org/dr.math/faq/faq.fractions.html + * https://mathforum.org/dr.math/faq/faq.fractions.html * Stop if the current term is bigger than or equal to the given * threshold. */ @@ -704,6 +763,8 @@ static int uvc_parse_format(struct uvc_device *dev, } format->colorspace = uvc_colorspace(buffer[3]); + format->xfer_func = uvc_xfer_func(buffer[4]); + format->ycbcr_enc = uvc_ycbcr_enc(buffer[5]); buflen -= buffer[0]; buffer += buffer[0]; diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c index b4499cddeffe..ca3a9c2eec27 100644 --- a/drivers/media/usb/uvc/uvc_entity.c +++ b/drivers/media/usb/uvc/uvc_entity.c @@ -73,10 +73,45 @@ static int uvc_mc_init_entity(struct uvc_video_chain *chain, int ret; if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) { + u32 function; + v4l2_subdev_init(&entity->subdev, &uvc_subdev_ops); strscpy(entity->subdev.name, entity->name, sizeof(entity->subdev.name)); + switch (UVC_ENTITY_TYPE(entity)) { + case UVC_VC_SELECTOR_UNIT: + function = MEDIA_ENT_F_VID_MUX; + break; + case UVC_VC_PROCESSING_UNIT: + case UVC_VC_EXTENSION_UNIT: + /* For lack of a better option. */ + function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; + break; + case UVC_COMPOSITE_CONNECTOR: + case UVC_COMPONENT_CONNECTOR: + function = MEDIA_ENT_F_CONN_COMPOSITE; + break; + case UVC_SVIDEO_CONNECTOR: + function = MEDIA_ENT_F_CONN_SVIDEO; + break; + case UVC_ITT_CAMERA: + function = MEDIA_ENT_F_CAM_SENSOR; + break; + case UVC_TT_VENDOR_SPECIFIC: + case UVC_ITT_VENDOR_SPECIFIC: + case UVC_ITT_MEDIA_TRANSPORT_INPUT: + case UVC_OTT_VENDOR_SPECIFIC: + case UVC_OTT_DISPLAY: + case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: + case UVC_EXTERNAL_VENDOR_SPECIFIC: + default: + function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; + break; + } + + entity->subdev.entity.function = function; + ret = media_entity_pads_init(&entity->subdev.entity, entity->num_pads, entity->pads); diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 0335e69b70ab..fa06bfa174ad 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -247,12 +247,44 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, if (ret < 0) goto done; + /* After the probe, update fmt with the values returned from + * negotiation with the device. + */ + for (i = 0; i < stream->nformats; ++i) { + if (probe->bFormatIndex == stream->format[i].index) { + format = &stream->format[i]; + break; + } + } + + if (i == stream->nformats) { + uvc_trace(UVC_TRACE_FORMAT, "Unknown bFormatIndex %u\n", + probe->bFormatIndex); + return -EINVAL; + } + + for (i = 0; i < format->nframes; ++i) { + if (probe->bFrameIndex == format->frame[i].bFrameIndex) { + frame = &format->frame[i]; + break; + } + } + + if (i == format->nframes) { + uvc_trace(UVC_TRACE_FORMAT, "Unknown bFrameIndex %u\n", + probe->bFrameIndex); + return -EINVAL; + } + fmt->fmt.pix.width = frame->wWidth; fmt->fmt.pix.height = frame->wHeight; fmt->fmt.pix.field = V4L2_FIELD_NONE; fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame); fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize; + fmt->fmt.pix.pixelformat = format->fcc; fmt->fmt.pix.colorspace = format->colorspace; + fmt->fmt.pix.xfer_func = format->xfer_func; + fmt->fmt.pix.ycbcr_enc = format->ycbcr_enc; if (uvc_format != NULL) *uvc_format = format; @@ -289,6 +321,8 @@ static int uvc_v4l2_get_format(struct uvc_streaming *stream, fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame); fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize; fmt->fmt.pix.colorspace = format->colorspace; + fmt->fmt.pix.xfer_func = format->xfer_func; + fmt->fmt.pix.ycbcr_enc = format->ycbcr_enc; done: mutex_unlock(&stream->mutex); diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index a65d5353a441..a6a441d92b94 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -622,7 +622,7 @@ static u16 uvc_video_clock_host_sof(const struct uvc_clock_sample *sample) * to avoid losing precision in the division. Similarly, the host timestamp is * computed with * - * TS = ((TS2 - TS1) * PTS + TS1 * SOF2 - TS2 * SOF1) / (SOF2 - SOF1) (2) + * TS = ((TS2 - TS1) * SOF + TS1 * SOF2 - TS2 * SOF1) / (SOF2 - SOF1) (2) * * SOF values are coded on 11 bits by USB. We extend their precision with 16 * decimal bits, leading to a 11.16 coding. @@ -1509,11 +1509,11 @@ static void uvc_video_complete(struct urb *urb) default: uvc_printk(KERN_WARNING, "Non-zero status (%d) in video " "completion handler.\n", urb->status); - /* fall through */ + fallthrough; case -ENOENT: /* usb_poison_urb() called. */ if (stream->frozen) return; - /* fall through */ + fallthrough; case -ECONNRESET: /* usb_unlink_urb() called. */ case -ESHUTDOWN: /* The endpoint is being disabled. */ uvc_queue_cancel(queue, urb->status == -ESHUTDOWN); diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 6ab972c643e3..a3dfacf069c4 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -165,6 +165,10 @@ {0x32, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_HEVC \ + { 'H', 'E', 'V', 'C', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} + /* ------------------------------------------------------------------------ * Driver specific constants. @@ -370,7 +374,9 @@ struct uvc_format { u8 type; u8 index; u8 bpp; - u8 colorspace; + enum v4l2_colorspace colorspace; + enum v4l2_xfer_func xfer_func; + enum v4l2_ycbcr_encoding ycbcr_enc; u32 fcc; u32 flags; |