diff mbox series

media: uvcvideo: initial UVC 1.5 region of interest support

Message ID 20210201033441.112092-1-senozhatsky@chromium.org (mailing list archive)
State New, archived
Headers show
Series media: uvcvideo: initial UVC 1.5 region of interest support | expand

Commit Message

Sergey Senozhatsky Feb. 1, 2021, 3:34 a.m. UTC
This patch implements parts of UVC 1.5 Region of Interest (ROI)
control, using the uvcvideo selection API.

There are several things to mention here.

First, UVC 1.5 defines CT_DIGITAL_WINDOW_CONTROL; and ROI rectangle
coordinates "must be within the current Digital Window as specified
by the CT_WINDOW control."  (4.2.2.1.20 Digital Region of Interest
(ROI) Control.) This is not entirely clear if we need to implement
CT_DIGITAL_WINDOW_CONTROL. ROI is naturally limited by:
a) ROI GET_MIN and GET_MAX rectangles
b) current image crop
c) stream->cur_frame->wWidth/stream->cur_frame->wHeight

Second, ROI control is a compound data type:
  Control Selector     CT_REGION_OF_INTEREST_CONTROL
  Mandatory Requests   SET_CUR, GET_CUR, GET_MIN, GET_MAX, GET_DEF
  wLength 10
  Offset   Field            Size
  0        wROI_Top         2
  2        wROI_Left        2
  4        wROI_Bottom      2
  6        wROI_Right       2
  8        bmAutoControls   2       (Bitmap)

While uvc_control_mapping can handle only s32 data at the moment:
->get() returns s32 value, ->set() accepts s32 value; while v4l2_ctrl
maximum/minimum/default_value can hold only s64 values.

Therefore ROI control handling is split into two parts:
a) bmAutoControls is handled via uvc_control_mapping as V4L2_CTRL_TYPE_MENU
b) ROI rectangle (SET_CUR, GET_CUR, GET_DEF) handling is implemented
   by the means of selection API.

Third, the patch adds new selection targets in order to handle ROI
control requests, but it doesn't implement all of the requests.
Namely, SEL_TGT_BOUNDS for ROI implements GET_MAX (that is maximal
ROI rectangle area). GET_MIN is not implemented ( as of now) since
it's not very clear if user space would need such information.

Signed-off-by: Sergey Senozhatsky <senozhatsky@chromium.org>
---
 drivers/media/usb/uvc/uvc_ctrl.c   |  32 +++++++
 drivers/media/usb/uvc/uvc_v4l2.c   | 143 ++++++++++++++++++++++++++++-
 include/uapi/linux/usb/video.h     |   1 +
 include/uapi/linux/v4l2-common.h   |  10 ++
 include/uapi/linux/v4l2-controls.h |   1 +
 5 files changed, 184 insertions(+), 3 deletions(-)

Comments

Laurent Pinchart Feb. 1, 2021, 8:31 p.m. UTC | #1
Hi Sergey,

Thank you for the patch.

On Mon, Feb 01, 2021 at 12:34:41PM +0900, Sergey Senozhatsky wrote:
> This patch implements parts of UVC 1.5 Region of Interest (ROI)
> control, using the uvcvideo selection API.
> 
> There are several things to mention here.
> 
> First, UVC 1.5 defines CT_DIGITAL_WINDOW_CONTROL; and ROI rectangle
> coordinates "must be within the current Digital Window as specified
> by the CT_WINDOW control."  (4.2.2.1.20 Digital Region of Interest
> (ROI) Control.) This is not entirely clear if we need to implement
> CT_DIGITAL_WINDOW_CONTROL. ROI is naturally limited by:
> a) ROI GET_MIN and GET_MAX rectangles
> b) current image crop
> c) stream->cur_frame->wWidth/stream->cur_frame->wHeight
> 
> Second, ROI control is a compound data type:
>   Control Selector     CT_REGION_OF_INTEREST_CONTROL
>   Mandatory Requests   SET_CUR, GET_CUR, GET_MIN, GET_MAX, GET_DEF
>   wLength 10
>   Offset   Field            Size
>   0        wROI_Top         2
>   2        wROI_Left        2
>   4        wROI_Bottom      2
>   6        wROI_Right       2
>   8        bmAutoControls   2       (Bitmap)
> 
> While uvc_control_mapping can handle only s32 data at the moment:
> ->get() returns s32 value, ->set() accepts s32 value; while v4l2_ctrl
> maximum/minimum/default_value can hold only s64 values.
> 
> Therefore ROI control handling is split into two parts:
> a) bmAutoControls is handled via uvc_control_mapping as V4L2_CTRL_TYPE_MENU
> b) ROI rectangle (SET_CUR, GET_CUR, GET_DEF) handling is implemented
>    by the means of selection API.
> 
> Third, the patch adds new selection targets in order to handle ROI
> control requests, but it doesn't implement all of the requests.
> Namely, SEL_TGT_BOUNDS for ROI implements GET_MAX (that is maximal
> ROI rectangle area). GET_MIN is not implemented ( as of now) since
> it's not very clear if user space would need such information.
> 
> Signed-off-by: Sergey Senozhatsky <senozhatsky@chromium.org>
> ---
>  drivers/media/usb/uvc/uvc_ctrl.c   |  32 +++++++
>  drivers/media/usb/uvc/uvc_v4l2.c   | 143 ++++++++++++++++++++++++++++-
>  include/uapi/linux/usb/video.h     |   1 +
>  include/uapi/linux/v4l2-common.h   |  10 ++
>  include/uapi/linux/v4l2-controls.h |   1 +

Before commenting on the uvcvideo-specific implementation, I'd like to
discuss the API. For this we need documentation for the new selection
targets. While doing so, could you split this patch in three:

- The new selection rectangles in the V4L2 core (and documentation)
- The new V4L2 control (and its documentation)
- The implementation of the feature in the uvcvideo driver

The first and second patches could be squashed if the two API elements
are tightly coupled.

Please note that the documentation should explain what the selection
rectangles apply to, whether they're only valid on video nodes, or on
subdevs as well, and how they related to other API elements.

>  5 files changed, 184 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index b3dde98499f4..4e55a0922f15 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -355,6 +355,15 @@ static const struct uvc_control_info uvc_ctrls[] = {
>  		.flags		= UVC_CTRL_FLAG_GET_CUR
>  				| UVC_CTRL_FLAG_AUTO_UPDATE,
>  	},
> +	{
> +		.entity		= UVC_GUID_UVC_CAMERA,
> +		.selector	= UVC_CT_REGION_OF_INTEREST_CONTROL,
> +		.index		= 21,
> +		.size		= 10,
> +		.flags		= UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
> +				| UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
> +				| UVC_CTRL_FLAG_GET_DEF
> +	},
>  };
>  
>  static const struct uvc_menu_info power_line_frequency_controls[] = {
> @@ -370,6 +379,17 @@ static const struct uvc_menu_info exposure_auto_controls[] = {
>  	{ 8, "Aperture Priority Mode" },
>  };
>  
> +static struct uvc_menu_info roi_auto_controls[] = {
> +	{ 0, "Auto Exposure" },
> +	{ 1, "Auto Iris" },
> +	{ 2, "Auto White Balance" },
> +	{ 3, "Auto Focus" },
> +	{ 4, "Auto Face Detect" },
> +	{ 5, "Auto Detect and Track" },
> +	{ 6, "Image Stabilization" },
> +	{ 7, "Higher Quality" },
> +};
> +
>  static s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping,
>  	u8 query, const u8 *data)
>  {
> @@ -753,6 +773,18 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
>  		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
>  	},
> +	{
> +		.id		= V4L2_CID_REGION_OF_INTEREST_CONTROLS,
> +		.name		= "Region of Interest (controls)",
> +		.entity		= UVC_GUID_UVC_CAMERA,
> +		.selector	= UVC_CT_REGION_OF_INTEREST_CONTROL,
> +		.size		= 16,
> +		.offset		= 64,
> +		.v4l2_type	= V4L2_CTRL_TYPE_MENU,
> +		.data_type	= UVC_CTRL_DATA_TYPE_BITMASK,
> +		.menu_info	= roi_auto_controls,
> +		.menu_count	= ARRAY_SIZE(roi_auto_controls),
> +	},
>  };
>  
>  /* ------------------------------------------------------------------------
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index 252136cc885c..71b4577196e5 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -1139,14 +1139,60 @@ static int uvc_ioctl_querymenu(struct file *file, void *fh,
>  	return uvc_query_v4l2_menu(chain, qm);
>  }
>  
> -static int uvc_ioctl_g_selection(struct file *file, void *fh,
> -				 struct v4l2_selection *sel)
> +/* UVC 1.5 ROI rectangle is half the size of v4l2_rect */
> +struct uvc_roi_rect {
> +	__u16			top;
> +	__u16			left;
> +	__u16			bottom;
> +	__u16			right;
> +};
> +
> +static int uvc_ioctl_g_roi_target(struct file *file, void *fh,
> +				  struct v4l2_selection *sel)
>  {
>  	struct uvc_fh *handle = fh;
>  	struct uvc_streaming *stream = handle->stream;
> +	struct uvc_roi_rect *roi;
> +	u8 query;
> +	int ret;
>  
> -	if (sel->type != stream->type)
> +	switch (sel->target) {
> +	case V4L2_SEL_TGT_ROI_DEFAULT:
> +		query = UVC_GET_DEF;
> +		break;
> +	case V4L2_SEL_TGT_ROI_CURRENT:
> +		query = UVC_GET_CUR;
> +		break;
> +	case V4L2_SEL_TGT_ROI_BOUNDS:
> +		query = UVC_GET_MAX;
> +		break;
> +	default:
>  		return -EINVAL;
> +	}
> +
> +	roi = kzalloc(sizeof(struct uvc_roi_rect), GFP_KERNEL);
> +	if (!roi)
> +		return -ENOMEM;
> +
> +	ret = uvc_query_ctrl(stream->dev, query, 1, stream->dev->intfnum,
> +			     UVC_CT_REGION_OF_INTEREST_CONTROL, roi,
> +			     sizeof(struct uvc_roi_rect));
> +	if (!ret) {
> +		sel->r.left	= roi->left;
> +		sel->r.top	= roi->top;
> +		sel->r.width	= roi->right;
> +		sel->r.height	= roi->bottom;
> +	}
> +
> +	kfree(roi);
> +	return ret;
> +}
> +
> +static int uvc_ioctl_g_sel_target(struct file *file, void *fh,
> +				  struct v4l2_selection *sel)
> +{
> +	struct uvc_fh *handle = fh;
> +	struct uvc_streaming *stream = handle->stream;
>  
>  	switch (sel->target) {
>  	case V4L2_SEL_TGT_CROP_DEFAULT:
> @@ -1173,6 +1219,96 @@ static int uvc_ioctl_g_selection(struct file *file, void *fh,
>  	return 0;
>  }
>  
> +static int uvc_ioctl_g_selection(struct file *file, void *fh,
> +				 struct v4l2_selection *sel)
> +{
> +	struct uvc_fh *handle = fh;
> +	struct uvc_streaming *stream = handle->stream;
> +
> +	if (sel->type != stream->type)
> +		return -EINVAL;
> +
> +	switch (sel->target) {
> +	case V4L2_SEL_TGT_CROP_DEFAULT:
> +	case V4L2_SEL_TGT_CROP_BOUNDS:
> +	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
> +	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
> +		return uvc_ioctl_g_sel_target(file, fh, sel);
> +	case V4L2_SEL_TGT_ROI_CURRENT:
> +	case V4L2_SEL_TGT_ROI_DEFAULT:
> +	case V4L2_SEL_TGT_ROI_BOUNDS:
> +		return uvc_ioctl_g_roi_target(file, fh, sel);
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static bool validate_roi_bounds(struct uvc_streaming *stream,
> +				struct v4l2_selection *sel)
> +{
> +	bool ok = true;
> +
> +	if (sel->r.left > USHRT_MAX || sel->r.top > USHRT_MAX ||
> +	    sel->r.width > USHRT_MAX || sel->r.height > USHRT_MAX)
> +		return false;
> +
> +	/* perhaps also can test against ROI GET_MAX */
> +
> +	mutex_lock(&stream->mutex);
> +	if ((u16)sel->r.width > stream->cur_frame->wWidth)
> +		ok = false;
> +	if ((u16)sel->r.height > stream->cur_frame->wHeight)
> +		ok = false;
> +	mutex_unlock(&stream->mutex);
> +
> +	return ok;
> +}
> +
> +static int uvc_ioctl_s_roi(struct file *file, void *fh,
> +			   struct v4l2_selection *sel)
> +{
> +	struct uvc_fh *handle = fh;
> +	struct uvc_streaming *stream = handle->stream;
> +	struct uvc_roi_rect *roi;
> +	int ret;
> +
> +	if (!validate_roi_bounds(stream, sel))
> +		return -E2BIG;
> +
> +	roi = kzalloc(sizeof(struct uvc_roi_rect), GFP_KERNEL);
> +	if (!roi)
> +		return -ENOMEM;
> +
> +	roi->left	= sel->r.left;
> +	roi->top	= sel->r.top;
> +	roi->right	= sel->r.width;
> +	roi->bottom	= sel->r.height;
> +
> +	ret = uvc_query_ctrl(stream->dev, UVC_SET_CUR, 1, stream->dev->intfnum,
> +			     UVC_CT_REGION_OF_INTEREST_CONTROL, roi,
> +			     sizeof(struct uvc_roi_rect));
> +
> +	kfree(roi);
> +	return ret;
> +}
> +
> +static int uvc_ioctl_s_selection(struct file *file, void *fh,
> +				 struct v4l2_selection *sel)
> +{
> +	struct uvc_fh *handle = fh;
> +	struct uvc_streaming *stream = handle->stream;
> +
> +	if (sel->type != stream->type)
> +		return -EINVAL;
> +
> +	switch (sel->target) {
> +	case V4L2_SEL_TGT_ROI:
> +		return uvc_ioctl_s_roi(file, fh, sel);
> +	}
> +
> +	return -EINVAL;
> +}
> +
>  static int uvc_ioctl_g_parm(struct file *file, void *fh,
>  			    struct v4l2_streamparm *parm)
>  {
> @@ -1533,6 +1669,7 @@ const struct v4l2_ioctl_ops uvc_ioctl_ops = {
>  	.vidioc_try_ext_ctrls = uvc_ioctl_try_ext_ctrls,
>  	.vidioc_querymenu = uvc_ioctl_querymenu,
>  	.vidioc_g_selection = uvc_ioctl_g_selection,
> +	.vidioc_s_selection = uvc_ioctl_s_selection,
>  	.vidioc_g_parm = uvc_ioctl_g_parm,
>  	.vidioc_s_parm = uvc_ioctl_s_parm,
>  	.vidioc_enum_framesizes = uvc_ioctl_enum_framesizes,
> diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h
> index d854cb19c42c..c87624962896 100644
> --- a/include/uapi/linux/usb/video.h
> +++ b/include/uapi/linux/usb/video.h
> @@ -104,6 +104,7 @@
>  #define UVC_CT_ROLL_ABSOLUTE_CONTROL			0x0f
>  #define UVC_CT_ROLL_RELATIVE_CONTROL			0x10
>  #define UVC_CT_PRIVACY_CONTROL				0x11
> +#define UVC_CT_REGION_OF_INTEREST_CONTROL		0x14
>  
>  /* A.9.5. Processing Unit Control Selectors */
>  #define UVC_PU_CONTROL_UNDEFINED			0x00
> diff --git a/include/uapi/linux/v4l2-common.h b/include/uapi/linux/v4l2-common.h
> index 7d21c1634b4d..60d5228f9113 100644
> --- a/include/uapi/linux/v4l2-common.h
> +++ b/include/uapi/linux/v4l2-common.h
> @@ -70,6 +70,16 @@
>  #define V4L2_SEL_TGT_CROP_BOUNDS	0x0002
>  /* Native frame size */
>  #define V4L2_SEL_TGT_NATIVE_SIZE	0x0003
> +
> +/* Current Region of Interest area */
> +#define V4L2_SEL_TGT_ROI_CURRENT	0x0004
> +/* Default Region of Interest area */
> +#define V4L2_SEL_TGT_ROI_DEFAULT	0x0005
> +/* Region of Interest bounds */
> +#define V4L2_SEL_TGT_ROI_BOUNDS	0x0006
> +/* Set Region of Interest area */
> +#define V4L2_SEL_TGT_ROI		0x0007
> +
>  /* Current composing area */
>  #define V4L2_SEL_TGT_COMPOSE		0x0100
>  /* Default composing area */
> diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
> index 039c0d7add1b..cbc1635046a0 100644
> --- a/include/uapi/linux/v4l2-controls.h
> +++ b/include/uapi/linux/v4l2-controls.h
> @@ -976,6 +976,7 @@ enum v4l2_auto_focus_range {
>  
>  #define V4L2_CID_PAN_SPEED			(V4L2_CID_CAMERA_CLASS_BASE+32)
>  #define V4L2_CID_TILT_SPEED			(V4L2_CID_CAMERA_CLASS_BASE+33)
> +#define V4L2_CID_REGION_OF_INTEREST_CONTROLS	(V4L2_CID_CAMERA_CLASS_BASE+34)
>  
>  #define V4L2_CID_CAMERA_ORIENTATION		(V4L2_CID_CAMERA_CLASS_BASE+34)
>  #define V4L2_CAMERA_ORIENTATION_FRONT		0
diff mbox series

Patch

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index b3dde98499f4..4e55a0922f15 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -355,6 +355,15 @@  static const struct uvc_control_info uvc_ctrls[] = {
 		.flags		= UVC_CTRL_FLAG_GET_CUR
 				| UVC_CTRL_FLAG_AUTO_UPDATE,
 	},
+	{
+		.entity		= UVC_GUID_UVC_CAMERA,
+		.selector	= UVC_CT_REGION_OF_INTEREST_CONTROL,
+		.index		= 21,
+		.size		= 10,
+		.flags		= UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
+				| UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
+				| UVC_CTRL_FLAG_GET_DEF
+	},
 };
 
 static const struct uvc_menu_info power_line_frequency_controls[] = {
@@ -370,6 +379,17 @@  static const struct uvc_menu_info exposure_auto_controls[] = {
 	{ 8, "Aperture Priority Mode" },
 };
 
+static struct uvc_menu_info roi_auto_controls[] = {
+	{ 0, "Auto Exposure" },
+	{ 1, "Auto Iris" },
+	{ 2, "Auto White Balance" },
+	{ 3, "Auto Focus" },
+	{ 4, "Auto Face Detect" },
+	{ 5, "Auto Detect and Track" },
+	{ 6, "Image Stabilization" },
+	{ 7, "Higher Quality" },
+};
+
 static s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping,
 	u8 query, const u8 *data)
 {
@@ -753,6 +773,18 @@  static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
 		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
 	},
+	{
+		.id		= V4L2_CID_REGION_OF_INTEREST_CONTROLS,
+		.name		= "Region of Interest (controls)",
+		.entity		= UVC_GUID_UVC_CAMERA,
+		.selector	= UVC_CT_REGION_OF_INTEREST_CONTROL,
+		.size		= 16,
+		.offset		= 64,
+		.v4l2_type	= V4L2_CTRL_TYPE_MENU,
+		.data_type	= UVC_CTRL_DATA_TYPE_BITMASK,
+		.menu_info	= roi_auto_controls,
+		.menu_count	= ARRAY_SIZE(roi_auto_controls),
+	},
 };
 
 /* ------------------------------------------------------------------------
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 252136cc885c..71b4577196e5 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1139,14 +1139,60 @@  static int uvc_ioctl_querymenu(struct file *file, void *fh,
 	return uvc_query_v4l2_menu(chain, qm);
 }
 
-static int uvc_ioctl_g_selection(struct file *file, void *fh,
-				 struct v4l2_selection *sel)
+/* UVC 1.5 ROI rectangle is half the size of v4l2_rect */
+struct uvc_roi_rect {
+	__u16			top;
+	__u16			left;
+	__u16			bottom;
+	__u16			right;
+};
+
+static int uvc_ioctl_g_roi_target(struct file *file, void *fh,
+				  struct v4l2_selection *sel)
 {
 	struct uvc_fh *handle = fh;
 	struct uvc_streaming *stream = handle->stream;
+	struct uvc_roi_rect *roi;
+	u8 query;
+	int ret;
 
-	if (sel->type != stream->type)
+	switch (sel->target) {
+	case V4L2_SEL_TGT_ROI_DEFAULT:
+		query = UVC_GET_DEF;
+		break;
+	case V4L2_SEL_TGT_ROI_CURRENT:
+		query = UVC_GET_CUR;
+		break;
+	case V4L2_SEL_TGT_ROI_BOUNDS:
+		query = UVC_GET_MAX;
+		break;
+	default:
 		return -EINVAL;
+	}
+
+	roi = kzalloc(sizeof(struct uvc_roi_rect), GFP_KERNEL);
+	if (!roi)
+		return -ENOMEM;
+
+	ret = uvc_query_ctrl(stream->dev, query, 1, stream->dev->intfnum,
+			     UVC_CT_REGION_OF_INTEREST_CONTROL, roi,
+			     sizeof(struct uvc_roi_rect));
+	if (!ret) {
+		sel->r.left	= roi->left;
+		sel->r.top	= roi->top;
+		sel->r.width	= roi->right;
+		sel->r.height	= roi->bottom;
+	}
+
+	kfree(roi);
+	return ret;
+}
+
+static int uvc_ioctl_g_sel_target(struct file *file, void *fh,
+				  struct v4l2_selection *sel)
+{
+	struct uvc_fh *handle = fh;
+	struct uvc_streaming *stream = handle->stream;
 
 	switch (sel->target) {
 	case V4L2_SEL_TGT_CROP_DEFAULT:
@@ -1173,6 +1219,96 @@  static int uvc_ioctl_g_selection(struct file *file, void *fh,
 	return 0;
 }
 
+static int uvc_ioctl_g_selection(struct file *file, void *fh,
+				 struct v4l2_selection *sel)
+{
+	struct uvc_fh *handle = fh;
+	struct uvc_streaming *stream = handle->stream;
+
+	if (sel->type != stream->type)
+		return -EINVAL;
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+		return uvc_ioctl_g_sel_target(file, fh, sel);
+	case V4L2_SEL_TGT_ROI_CURRENT:
+	case V4L2_SEL_TGT_ROI_DEFAULT:
+	case V4L2_SEL_TGT_ROI_BOUNDS:
+		return uvc_ioctl_g_roi_target(file, fh, sel);
+	}
+
+	return -EINVAL;
+}
+
+static bool validate_roi_bounds(struct uvc_streaming *stream,
+				struct v4l2_selection *sel)
+{
+	bool ok = true;
+
+	if (sel->r.left > USHRT_MAX || sel->r.top > USHRT_MAX ||
+	    sel->r.width > USHRT_MAX || sel->r.height > USHRT_MAX)
+		return false;
+
+	/* perhaps also can test against ROI GET_MAX */
+
+	mutex_lock(&stream->mutex);
+	if ((u16)sel->r.width > stream->cur_frame->wWidth)
+		ok = false;
+	if ((u16)sel->r.height > stream->cur_frame->wHeight)
+		ok = false;
+	mutex_unlock(&stream->mutex);
+
+	return ok;
+}
+
+static int uvc_ioctl_s_roi(struct file *file, void *fh,
+			   struct v4l2_selection *sel)
+{
+	struct uvc_fh *handle = fh;
+	struct uvc_streaming *stream = handle->stream;
+	struct uvc_roi_rect *roi;
+	int ret;
+
+	if (!validate_roi_bounds(stream, sel))
+		return -E2BIG;
+
+	roi = kzalloc(sizeof(struct uvc_roi_rect), GFP_KERNEL);
+	if (!roi)
+		return -ENOMEM;
+
+	roi->left	= sel->r.left;
+	roi->top	= sel->r.top;
+	roi->right	= sel->r.width;
+	roi->bottom	= sel->r.height;
+
+	ret = uvc_query_ctrl(stream->dev, UVC_SET_CUR, 1, stream->dev->intfnum,
+			     UVC_CT_REGION_OF_INTEREST_CONTROL, roi,
+			     sizeof(struct uvc_roi_rect));
+
+	kfree(roi);
+	return ret;
+}
+
+static int uvc_ioctl_s_selection(struct file *file, void *fh,
+				 struct v4l2_selection *sel)
+{
+	struct uvc_fh *handle = fh;
+	struct uvc_streaming *stream = handle->stream;
+
+	if (sel->type != stream->type)
+		return -EINVAL;
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_ROI:
+		return uvc_ioctl_s_roi(file, fh, sel);
+	}
+
+	return -EINVAL;
+}
+
 static int uvc_ioctl_g_parm(struct file *file, void *fh,
 			    struct v4l2_streamparm *parm)
 {
@@ -1533,6 +1669,7 @@  const struct v4l2_ioctl_ops uvc_ioctl_ops = {
 	.vidioc_try_ext_ctrls = uvc_ioctl_try_ext_ctrls,
 	.vidioc_querymenu = uvc_ioctl_querymenu,
 	.vidioc_g_selection = uvc_ioctl_g_selection,
+	.vidioc_s_selection = uvc_ioctl_s_selection,
 	.vidioc_g_parm = uvc_ioctl_g_parm,
 	.vidioc_s_parm = uvc_ioctl_s_parm,
 	.vidioc_enum_framesizes = uvc_ioctl_enum_framesizes,
diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h
index d854cb19c42c..c87624962896 100644
--- a/include/uapi/linux/usb/video.h
+++ b/include/uapi/linux/usb/video.h
@@ -104,6 +104,7 @@ 
 #define UVC_CT_ROLL_ABSOLUTE_CONTROL			0x0f
 #define UVC_CT_ROLL_RELATIVE_CONTROL			0x10
 #define UVC_CT_PRIVACY_CONTROL				0x11
+#define UVC_CT_REGION_OF_INTEREST_CONTROL		0x14
 
 /* A.9.5. Processing Unit Control Selectors */
 #define UVC_PU_CONTROL_UNDEFINED			0x00
diff --git a/include/uapi/linux/v4l2-common.h b/include/uapi/linux/v4l2-common.h
index 7d21c1634b4d..60d5228f9113 100644
--- a/include/uapi/linux/v4l2-common.h
+++ b/include/uapi/linux/v4l2-common.h
@@ -70,6 +70,16 @@ 
 #define V4L2_SEL_TGT_CROP_BOUNDS	0x0002
 /* Native frame size */
 #define V4L2_SEL_TGT_NATIVE_SIZE	0x0003
+
+/* Current Region of Interest area */
+#define V4L2_SEL_TGT_ROI_CURRENT	0x0004
+/* Default Region of Interest area */
+#define V4L2_SEL_TGT_ROI_DEFAULT	0x0005
+/* Region of Interest bounds */
+#define V4L2_SEL_TGT_ROI_BOUNDS	0x0006
+/* Set Region of Interest area */
+#define V4L2_SEL_TGT_ROI		0x0007
+
 /* Current composing area */
 #define V4L2_SEL_TGT_COMPOSE		0x0100
 /* Default composing area */
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 039c0d7add1b..cbc1635046a0 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -976,6 +976,7 @@  enum v4l2_auto_focus_range {
 
 #define V4L2_CID_PAN_SPEED			(V4L2_CID_CAMERA_CLASS_BASE+32)
 #define V4L2_CID_TILT_SPEED			(V4L2_CID_CAMERA_CLASS_BASE+33)
+#define V4L2_CID_REGION_OF_INTEREST_CONTROLS	(V4L2_CID_CAMERA_CLASS_BASE+34)
 
 #define V4L2_CID_CAMERA_ORIENTATION		(V4L2_CID_CAMERA_CLASS_BASE+34)
 #define V4L2_CAMERA_ORIENTATION_FRONT		0