Re: [PATCH v2 1/2] v4l: Add new alpha component control

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Monday 28 November 2011 14:02:49 Sylwester Nawrocki wrote:
> On 11/28/2011 01:39 PM, Hans Verkuil wrote:
> > On Monday 28 November 2011 13:13:32 Sylwester Nawrocki wrote:
> >> On 11/28/2011 12:38 PM, Hans Verkuil wrote:
> >>> On Friday 25 November 2011 16:39:31 Sylwester Nawrocki wrote:
> >>>> This control is intended for the video capture or memory-to-memory
> >>>> devices that are capable of setting up a per-pixel alpha component to
> >>>> some arbitrary value. The V4L2_CID_ALPHA_COMPONENT control allows to
> >>>> set the alpha component for all pixels to a value in range from 0 to
> >>>> 255.
> >>>> 
> >>>> Signed-off-by: Sylwester Nawrocki <s.nawrocki@xxxxxxxxxxx>
> >>>> Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx>
> >>>> ---
> >>>> 
> >>>>  Documentation/DocBook/media/v4l/compat.xml         |   11 ++++++++
> >>>>  Documentation/DocBook/media/v4l/controls.xml       |   25
> >>>> 
> >>>> +++++++++++++++---- .../DocBook/media/v4l/pixfmt-packed-rgb.xml       
> >>>> |
> >>>> 
> >>>>  7 ++++-
> >>>>  drivers/media/video/v4l2-ctrls.c                   |    7 +++++
> >>>>  include/linux/videodev2.h                          |    6 ++--
> >>>>  5 files changed, 45 insertions(+), 11 deletions(-)
> >> 
> >> ...
> >> 
> >>>>  	/* MPEG controls */
> >>>>  	/* Keep the order of the 'case's the same as in videodev2.h! */
> >>>> 
> >>>> @@ -714,6 +715,12 @@ void v4l2_ctrl_fill(u32 id, const char **name,
> >>>> enum v4l2_ctrl_type *type, /* Max is calculated as RGB888 that is
> >>>> 2^24 */
> >>>> 
> >>>>  		*max = 0xFFFFFF;
> >>>>  		break;
> >>>> 
> >>>> +	case V4L2_CID_ALPHA_COMPONENT:
> >>>> +		*type = V4L2_CTRL_TYPE_INTEGER;
> >>>> +		*step = 1;
> >>>> +		*min = 0;
> >>>> +		*max = 0xff;
> >>>> +		break;
> >>> 
> >>> Hmm. Do we really want to fix the max value to 0xff? The bits assigned
> >>> to the alpha component will vary between 1 (V4L2_PIX_FMT_RGB555X), 4
> >>> (V4L2_PIX_FMT_RGB444) or 8 (V4L2_PIX_FMT_RGB32). It wouldn't surprise
> >>> me to see larger sizes as well in the future (e.g. 16 bits).
> >>> 
> >>> I think the max value should be the largest alpha value the hardware
> >>> can support. The application has to set it to the right value that
> >>> corresponds to the currently chosen pixel format. The driver just
> >>> copies the first N bits into the alpha value where N depends on the
> >>> pixel format.
> >>> 
> >>> what do you think?
> >> 
> >> Yes, ideally the maximum value of the alpha control should be changing
> >> depending on the set colour format.
> >> Currently the maximum value of the control equals maximum alpha value
> >> for the fourcc of maximum colour depth (V4L2_PIX_FMT_RGB32).
> >> 
> >> What I found missing was a method for changing the control range
> >> dynamically, without deleting and re-initializing the control handler.
> >> If we reinitalize whole control handler the previously set control
> >> values are lost.
> > 
> > You can just change the maximum field of struct v4l2_ctrl on the fly like
> > this:
> > diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
> > struct v4l2_ctrl *my_ctrl;
> > 
> > v4l2_ctrl_lock(my_ctrl);
> > my_ctrl->maximum = 10;
> > if (my_ctrl->cur.val > my_ctrl->maximum)
> > 
> > 	my_ctrl->cur.val = my_ctrl->maximum;
> > 
> > v4l2_ctrl_unlock(my_ctrl);
> > 
> > Although this might warrant a v4l2_ctrl_update_range() function that does
> > this for you. Because after a change like this a V4L2_EVENT_CTRL should
> > also be sent.
> > 
> > In any case, this functionality isn't hard to add. Just let me know if
> > you need it and I can make a patch for this.
> 
> Yes, it would be great if you could prepare a patch for
> v4l2_ctrl_update_range(). Then I could use it in the next iteration of the
> patches, instead of hacking at the driver. IIRC it's not the first time we
> needed changing the control range dynamically.

Here is a patch that updates the range. It also sends a control event telling
any listener that the range has changed. Tested with vivi and a modified
v4l2-ctl.

The only thing missing is a DocBook entry for that new event flag and perhaps
some more documentation in places.

Let me know how this works for you, and if it is really needed, then I can
add it to the control framework.

Regards,

	Hans

index 0f415da..d7ca646 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -913,8 +913,7 @@ static int new_to_user(struct v4l2_ext_control *c,
 }
 
 /* Copy the new value to the current value. */
-static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
-						bool update_inactive)
+static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags)
 {
 	bool changed = false;
 
@@ -938,8 +937,8 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
 		ctrl->cur.val = ctrl->val;
 		break;
 	}
-	if (update_inactive) {
-		/* Note: update_inactive can only be true for auto clusters. */
+	if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) {
+		/* Note: CH_FLAGS is only set for auto clusters. */
 		ctrl->flags &=
 			~(V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_VOLATILE);
 		if (!is_cur_manual(ctrl->cluster[0])) {
@@ -949,14 +948,13 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
 		}
 		fh = NULL;
 	}
-	if (changed || update_inactive) {
+	if (changed || ch_flags) {
 		/* If a control was changed that was not one of the controls
 		   modified by the application, then send the event to all. */
 		if (!ctrl->is_new)
 			fh = NULL;
 		send_event(fh, ctrl,
-			(changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) |
-			(update_inactive ? V4L2_EVENT_CTRL_CH_FLAGS : 0));
+			(changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) | ch_flags);
 	}
 }
 
@@ -1290,6 +1288,40 @@ unlock:
 	return 0;
 }
 
+/* Control range checking */
+static int check_range(enum v4l2_ctrl_type type,
+		s32 min, s32 max, u32 step, s32 def)
+{
+	switch (type) {
+	case V4L2_CTRL_TYPE_BOOLEAN:
+		if (step != 1 || max > 1 || min < 0)
+			return -ERANGE;
+		/* fall through */
+	case V4L2_CTRL_TYPE_INTEGER:
+		if (step <= 0 || min > max || def < min || def > max)
+			return -ERANGE;
+		return 0;
+	case V4L2_CTRL_TYPE_BITMASK:
+		if (step || min || !max || (def & ~max))
+			return -ERANGE;
+		return 0;
+	case V4L2_CTRL_TYPE_MENU:
+		if (min > max || def < min || def > max)
+			return -ERANGE;
+		/* Note: step == menu_skip_mask for menu controls.
+		   So here we check if the default value is masked out. */
+		if (step && ((1 << def) & step))
+			return -EINVAL;
+		return 0;
+	case V4L2_CTRL_TYPE_STRING:
+		if (min > max || min < 0 || step < 1 || def)
+			return -ERANGE;
+		return 0;
+	default:
+		return 0;
+	}
+}
+
 /* Add a new control */
 static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 			const struct v4l2_ctrl_ops *ops,
@@ -1299,32 +1331,20 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 {
 	struct v4l2_ctrl *ctrl;
 	unsigned sz_extra = 0;
+	int err;
 
 	if (hdl->error)
 		return NULL;
 
 	/* Sanity checks */
 	if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE ||
-	    (type == V4L2_CTRL_TYPE_INTEGER && step == 0) ||
-	    (type == V4L2_CTRL_TYPE_BITMASK && max == 0) ||
-	    (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
-	    (type == V4L2_CTRL_TYPE_STRING && max == 0)) {
-		handler_set_err(hdl, -ERANGE);
-		return NULL;
-	}
-	if (type != V4L2_CTRL_TYPE_BITMASK && max < min) {
+	    (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL)) {
 		handler_set_err(hdl, -ERANGE);
 		return NULL;
 	}
-	if ((type == V4L2_CTRL_TYPE_INTEGER ||
-	     type == V4L2_CTRL_TYPE_MENU ||
-	     type == V4L2_CTRL_TYPE_BOOLEAN) &&
-	    (def < min || def > max)) {
-		handler_set_err(hdl, -ERANGE);
-		return NULL;
-	}
-	if (type == V4L2_CTRL_TYPE_BITMASK && ((def & ~max) || min || step)) {
-		handler_set_err(hdl, -ERANGE);
+	err = check_range(type, min, max, step, def);
+	if (err) {
+		handler_set_err(hdl, err);
 		return NULL;
 	}
 
@@ -2062,7 +2082,7 @@ EXPORT_SYMBOL(v4l2_ctrl_g_ctrl);
    copied to the current value on a set.
    Must be called with ctrl->handler->lock held. */
 static int try_or_set_cluster(struct v4l2_fh *fh,
-			      struct v4l2_ctrl *master, bool set)
+			      struct v4l2_ctrl *master, bool set, u32 ch_flags)
 {
 	bool update_flag;
 	int ret;
@@ -2100,7 +2120,8 @@ static int try_or_set_cluster(struct v4l2_fh *fh,
 	/* If OK, then make the new values permanent. */
 	update_flag = is_cur_manual(master) != is_new_manual(master);
 	for (i = 0; i < master->ncontrols; i++)
-		new_to_cur(fh, master->cluster[i], update_flag && i > 0);
+		new_to_cur(fh, master->cluster[i], ch_flags |
+			((update_flag && i > 0) ? V4L2_EVENT_CTRL_CH_FLAGS : 0));
 	return 0;
 }
 
@@ -2226,7 +2247,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
 		} while (!ret && idx);
 
 		if (!ret)
-			ret = try_or_set_cluster(fh, master, set);
+			ret = try_or_set_cluster(fh, master, set, 0);
 
 		/* Copy the new values back to userspace. */
 		if (!ret) {
@@ -2271,18 +2292,12 @@ int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs
 EXPORT_SYMBOL(v4l2_subdev_s_ext_ctrls);
 
 /* Helper function for VIDIOC_S_CTRL compatibility */
-static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, s32 *val)
+static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
+		    s32 val, u32 ch_flags)
 {
 	struct v4l2_ctrl *master = ctrl->cluster[0];
-	int ret;
 	int i;
 
-	ret = validate_new_int(ctrl, val);
-	if (ret)
-		return ret;
-
-	v4l2_ctrl_lock(ctrl);
-
 	/* Reset the 'is_new' flags of the cluster */
 	for (i = 0; i < master->ncontrols; i++)
 		if (master->cluster[i])
@@ -2292,13 +2307,25 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, s32 *val)
 	   manual mode we have to update the current volatile values since
 	   those will become the initial manual values after such a switch. */
 	if (master->is_auto && master->has_volatiles && ctrl == master &&
-	    !is_cur_manual(master) && *val == master->manual_mode_value)
+	    !is_cur_manual(master) && val == master->manual_mode_value)
 		update_from_auto_cluster(master);
-	ctrl->val = *val;
+	ctrl->val = val;
 	ctrl->is_new = 1;
-	ret = try_or_set_cluster(fh, master, true);
-	*val = ctrl->cur.val;
-	v4l2_ctrl_unlock(ctrl);
+	return try_or_set_cluster(fh, master, true, ch_flags);
+}
+
+/* Helper function for VIDIOC_S_CTRL compatibility */
+static int set_ctrl_lock(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, s32 *val)
+{
+	int ret = validate_new_int(ctrl, val);
+
+	if (!ret) {
+		v4l2_ctrl_lock(ctrl);
+		ret = set_ctrl(fh, ctrl, *val, 0);
+		if (!ret)
+			*val = ctrl->cur.val;
+		v4l2_ctrl_unlock(ctrl);
+	}
 	return ret;
 }
 
@@ -2313,7 +2340,7 @@ int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
 	if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
 		return -EACCES;
 
-	return set_ctrl(fh, ctrl, &control->value);
+	return set_ctrl_lock(fh, ctrl, &control->value);
 }
 EXPORT_SYMBOL(v4l2_s_ctrl);
 
@@ -2327,10 +2354,44 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
 {
 	/* It's a driver bug if this happens. */
 	WARN_ON(!type_is_int(ctrl));
-	return set_ctrl(NULL, ctrl, &val);
+	return set_ctrl_lock(NULL, ctrl, &val);
 }
 EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
 
+int v4l2_ctrl_update_range(struct v4l2_ctrl *ctrl,
+			s32 min, s32 max, u32 step, s32 def)
+{
+	int ret = check_range(ctrl->type, min, max, step, def);
+	s32 val;
+
+	switch (ctrl->type) {
+	case V4L2_CTRL_TYPE_INTEGER:
+	case V4L2_CTRL_TYPE_BOOLEAN:
+	case V4L2_CTRL_TYPE_MENU:
+	case V4L2_CTRL_TYPE_BITMASK:
+		if (ret)
+			return ret;
+		break;
+	default:
+		return -EINVAL;
+	}
+	v4l2_ctrl_lock(ctrl);
+	ctrl->minimum = min;
+	ctrl->maximum = max;
+	ctrl->step = step;
+	ctrl->default_value = def;
+	val = ctrl->cur.val;
+	if (validate_new_int(ctrl, &val))
+		val = def;
+	if (val != ctrl->cur.val)
+		ret = set_ctrl(NULL, ctrl, val, V4L2_EVENT_CTRL_CH_RANGE);
+	else
+		send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_RANGE);
+	v4l2_ctrl_unlock(ctrl);
+	return ret;
+}
+EXPORT_SYMBOL(v4l2_ctrl_update_range);
+
 void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl,
 				struct v4l2_subscribed_event *sev)
 {
@@ -2339,7 +2400,7 @@ void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl,
 	if (ctrl->type != V4L2_CTRL_TYPE_CTRL_CLASS &&
 	    (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL)) {
 		struct v4l2_event ev;
-		u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
+		u32 changes = V4L2_EVENT_CTRL_CH_FLAGS | V4L2_EVENT_CTRL_CH_RANGE;
 
 		if (!(ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY))
 			changes |= V4L2_EVENT_CTRL_CH_VALUE;
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 7d754fb..fd89106 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -190,6 +190,7 @@ struct vivi_dev {
 	unsigned 		   ms;
 	unsigned long              jiffies;
 	unsigned		   button_pressed;
+	bool			   toggle_range;
 
 	int			   mv_count;	/* Controls bars movement */
 
@@ -545,6 +546,17 @@ static void vivi_thread_tick(struct vivi_dev *dev)
 	vivi_fillbuff(dev, buf);
 	dprintk(dev, 1, "filled buffer %p\n", buf);
 
+	if (dev->toggle_range) {
+		static bool toggle;
+
+		dev->toggle_range = false;
+		if (toggle)
+			v4l2_ctrl_update_range(dev->contrast, 0, 255, 1, 16);
+		else
+			v4l2_ctrl_update_range(dev->contrast, 128, 255, 2, 150);
+		toggle = !toggle;
+	}
+
 	vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
 	dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
 }
@@ -1034,8 +1046,10 @@ static int vivi_s_ctrl(struct v4l2_ctrl *ctrl)
 {
 	struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
 
-	if (ctrl == dev->button)
+	if (ctrl == dev->button) {
 		dev->button_pressed = 30;
+		dev->toggle_range = true;
+	}
 	return 0;
 }
 
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 4b752d5..22e632a 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -2063,6 +2063,7 @@ struct v4l2_event_vsync {
 /* Payload for V4L2_EVENT_CTRL */
 #define V4L2_EVENT_CTRL_CH_VALUE		(1 << 0)
 #define V4L2_EVENT_CTRL_CH_FLAGS		(1 << 1)
+#define V4L2_EVENT_CTRL_CH_RANGE		(1 << 2)
 
 struct v4l2_event_ctrl {
 	__u32 changes;
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index eeb3df6..69ea0d5 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -445,6 +445,23 @@ void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active);
   */
 void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed);
 
+/** v4l2_ctrl_update_range() - Update the range of a control.
+  * @ctrl:	The control to update.
+  * @min:	The control's minimum value.
+  * @max:	The control's maximum value.
+  * @step:	The control's step value
+  * @def: 	The control's default value.
+  *
+  * Update the range of a control on the fly. This works for control types
+  * INTEGER, BOOLEAN, MENU and BITMASK. For menu controls the @step value
+  * is interpreted as a menu_skip_mask.
+  *
+  * An error is returned if one of the range arguments is invalid for this
+  * control type.
+  */
+int v4l2_ctrl_update_range(struct v4l2_ctrl *ctrl,
+			s32 min, s32 max, u32 step, s32 def);
+
 /** v4l2_ctrl_lock() - Helper function to lock the handler
   * associated with the control.
   * @ctrl:	The control to lock.
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux