aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/usb/uvc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/usb/uvc')
-rw-r--r--drivers/media/usb/uvc/uvc_ctrl.c46
-rw-r--r--drivers/media/usb/uvc/uvc_debugfs.c20
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c71
-rw-r--r--drivers/media/usb/uvc/uvc_entity.c35
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c34
-rw-r--r--drivers/media/usb/uvc/uvc_video.c6
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h8
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;