Re: [RFC PATCH 03/12] mt9m001: convert to the control framework.

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

 



On Wed, 12 Jan 2011, Hans Verkuil wrote:

> Signed-off-by: Hans Verkuil <hverkuil@xxxxxxxxx>
> ---
>  drivers/media/video/mt9m001.c |  210 +++++++++++++++--------------------------
>  1 files changed, 75 insertions(+), 135 deletions(-)
> 
> diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
> index f7fc88d..b9b6e33 100644
> --- a/drivers/media/video/mt9m001.c
> +++ b/drivers/media/video/mt9m001.c
> @@ -15,6 +15,7 @@
>  
>  #include <media/v4l2-subdev.h>
>  #include <media/v4l2-chip-ident.h>
> +#include <media/v4l2-ctrls.h>
>  #include <media/soc_camera.h>
>  
>  /*
> @@ -84,15 +85,18 @@ static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = {
>  
>  struct mt9m001 {
>  	struct v4l2_subdev subdev;
> +	struct v4l2_ctrl_handler hdl;
> +	struct {
> +		/* exposure/auto-exposure cluster */
> +		struct v4l2_ctrl *autoexposure;
> +		struct v4l2_ctrl *exposure;
> +	};

Hm, why an anonymous struct? Why not just put them directly at the top 
level?

>  	struct v4l2_rect rect;	/* Sensor window */
>  	const struct mt9m001_datafmt *fmt;
>  	const struct mt9m001_datafmt *fmts;
>  	int num_fmts;
>  	int model;	/* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */
> -	unsigned int gain;
> -	unsigned int exposure;
>  	unsigned short y_skip_top;	/* Lines to skip at the top */
> -	unsigned char autoexposure;
>  };
>  
>  static struct mt9m001 *to_mt9m001(const struct i2c_client *client)
> @@ -209,7 +213,6 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
>  	struct i2c_client *client = v4l2_get_subdevdata(sd);
>  	struct mt9m001 *mt9m001 = to_mt9m001(client);
>  	struct v4l2_rect rect = a->c;
> -	struct soc_camera_device *icd = client->dev.platform_data;
>  	int ret;
>  	const u16 hblank = 9, vblank = 25;
>  	unsigned int total_h;
> @@ -251,17 +254,18 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
>  	if (!ret)
>  		ret = reg_write(client, MT9M001_WINDOW_HEIGHT,
>  				rect.height + mt9m001->y_skip_top - 1);
> -	if (!ret && mt9m001->autoexposure) {
> +	v4l2_ctrl_lock(mt9m001->autoexposure);
> +	if (!ret && mt9m001->autoexposure->cur.val == V4L2_EXPOSURE_AUTO) {
>  		ret = reg_write(client, MT9M001_SHUTTER_WIDTH, total_h);
>  		if (!ret) {
> -			const struct v4l2_queryctrl *qctrl =
> -				soc_camera_find_qctrl(icd->ops,
> -						      V4L2_CID_EXPOSURE);
> -			mt9m001->exposure = (524 + (total_h - 1) *
> -				 (qctrl->maximum - qctrl->minimum)) /
> -				1048 + qctrl->minimum;
> +			struct v4l2_ctrl *ctrl = mt9m001->exposure;
> +
> +			ctrl->cur.val = (524 + (total_h - 1) *
> +				 (ctrl->maximum - ctrl->minimum)) /
> +				1048 + ctrl->minimum;
>  		}
>  	}
> +	v4l2_ctrl_unlock(mt9m001->autoexposure);
>  
>  	if (!ret)
>  		mt9m001->rect = rect;
> @@ -421,107 +425,36 @@ static int mt9m001_s_register(struct v4l2_subdev *sd,
>  }
>  #endif
>  
> -static const struct v4l2_queryctrl mt9m001_controls[] = {
> -	{
> -		.id		= V4L2_CID_VFLIP,
> -		.type		= V4L2_CTRL_TYPE_BOOLEAN,
> -		.name		= "Flip Vertically",
> -		.minimum	= 0,
> -		.maximum	= 1,
> -		.step		= 1,
> -		.default_value	= 0,
> -	}, {
> -		.id		= V4L2_CID_GAIN,
> -		.type		= V4L2_CTRL_TYPE_INTEGER,
> -		.name		= "Gain",
> -		.minimum	= 0,
> -		.maximum	= 127,
> -		.step		= 1,
> -		.default_value	= 64,
> -		.flags		= V4L2_CTRL_FLAG_SLIDER,
> -	}, {
> -		.id		= V4L2_CID_EXPOSURE,
> -		.type		= V4L2_CTRL_TYPE_INTEGER,
> -		.name		= "Exposure",
> -		.minimum	= 1,
> -		.maximum	= 255,
> -		.step		= 1,
> -		.default_value	= 255,
> -		.flags		= V4L2_CTRL_FLAG_SLIDER,
> -	}, {
> -		.id		= V4L2_CID_EXPOSURE_AUTO,
> -		.type		= V4L2_CTRL_TYPE_BOOLEAN,
> -		.name		= "Automatic Exposure",
> -		.minimum	= 0,
> -		.maximum	= 1,
> -		.step		= 1,
> -		.default_value	= 1,
> -	}
> -};
> -
>  static struct soc_camera_ops mt9m001_ops = {
>  	.set_bus_param		= mt9m001_set_bus_param,
>  	.query_bus_param	= mt9m001_query_bus_param,
> -	.controls		= mt9m001_controls,
> -	.num_controls		= ARRAY_SIZE(mt9m001_controls),
>  };
>  
> -static int mt9m001_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
> +static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl)
>  {
> +	struct v4l2_subdev *sd =
> +		&container_of(ctrl->handler, struct mt9m001, hdl)->subdev;
>  	struct i2c_client *client = v4l2_get_subdevdata(sd);
>  	struct mt9m001 *mt9m001 = to_mt9m001(client);

This looks a bit clumsy to me, sorry. Above you already have "struct 
mt9m001 *" (container_of(ctrl->handler, struct mt9m001, hdl)), but you 
only use it implicitly to get to sd, and then mt9m001 is calculated 
again...

> +	struct v4l2_ctrl *exp = mt9m001->exposure;
>  	int data;
>  
>  	switch (ctrl->id) {
>  	case V4L2_CID_VFLIP:
> -		data = reg_read(client, MT9M001_READ_OPTIONS2);
> -		if (data < 0)
> -			return -EIO;
> -		ctrl->value = !!(data & 0x8000);
> -		break;
> -	case V4L2_CID_EXPOSURE_AUTO:
> -		ctrl->value = mt9m001->autoexposure;
> -		break;
> -	case V4L2_CID_GAIN:
> -		ctrl->value = mt9m001->gain;
> -		break;
> -	case V4L2_CID_EXPOSURE:
> -		ctrl->value = mt9m001->exposure;
> -		break;
> -	}
> -	return 0;
> -}
> -
> -static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
> -{
> -	struct i2c_client *client = v4l2_get_subdevdata(sd);
> -	struct mt9m001 *mt9m001 = to_mt9m001(client);
> -	struct soc_camera_device *icd = client->dev.platform_data;
> -	const struct v4l2_queryctrl *qctrl;
> -	int data;
> -
> -	qctrl = soc_camera_find_qctrl(&mt9m001_ops, ctrl->id);
> -
> -	if (!qctrl)
> -		return -EINVAL;
> -
> -	switch (ctrl->id) {
> -	case V4L2_CID_VFLIP:
> -		if (ctrl->value)
> +		if (ctrl->val)
>  			data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000);
>  		else
>  			data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000);
>  		if (data < 0)
>  			return -EIO;
> -		break;
> +		return 0;
> +
>  	case V4L2_CID_GAIN:
> -		if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
> -			return -EINVAL;
>  		/* See Datasheet Table 7, Gain settings. */
> -		if (ctrl->value <= qctrl->default_value) {
> +		if (ctrl->val <= ctrl->default_value) {
>  			/* Pack it into 0..1 step 0.125, register values 0..8 */
> -			unsigned long range = qctrl->default_value - qctrl->minimum;
> -			data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range;
> +			unsigned long range = ctrl->default_value - ctrl->minimum;
> +			data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range;
>  
>  			dev_dbg(&client->dev, "Setting gain %d\n", data);
>  			data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
> @@ -530,8 +463,8 @@ static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
>  		} else {
>  			/* Pack it into 1.125..15 variable step, register values 9..67 */
>  			/* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
> -			unsigned long range = qctrl->maximum - qctrl->default_value - 1;
> -			unsigned long gain = ((ctrl->value - qctrl->default_value - 1) *
> +			unsigned long range = ctrl->maximum - ctrl->default_value - 1;
> +			unsigned long gain = ((ctrl->val - ctrl->default_value - 1) *
>  					       111 + range / 2) / range + 9;
>  
>  			if (gain <= 32)
> @@ -547,47 +480,36 @@ static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
>  			if (data < 0)
>  				return -EIO;
>  		}
> +		return 0;
>  
> -		/* Success */
> -		mt9m001->gain = ctrl->value;
> -		break;
> -	case V4L2_CID_EXPOSURE:
> -		/* mt9m001 has maximum == default */
> -		if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
> -			return -EINVAL;
> -		else {
> -			unsigned long range = qctrl->maximum - qctrl->minimum;
> -			unsigned long shutter = ((ctrl->value - qctrl->minimum) * 1048 +
> +	case V4L2_CID_EXPOSURE_AUTO:
> +		/* Force manual exposure if only the exposure was changed */
> +		if (!ctrl->has_new)
> +			ctrl->val = V4L2_EXPOSURE_MANUAL;
> +		if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
> +			unsigned long range = exp->maximum - exp->minimum;
> +			unsigned long shutter = ((exp->val - exp->minimum) * 1048 +
>  						 range / 2) / range + 1;
>  
>  			dev_dbg(&client->dev,
>  				"Setting shutter width from %d to %lu\n",
> -				reg_read(client, MT9M001_SHUTTER_WIDTH),
> -				shutter);
> +				reg_read(client, MT9M001_SHUTTER_WIDTH), shutter);
>  			if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0)
>  				return -EIO;
> -			mt9m001->exposure = ctrl->value;
> -			mt9m001->autoexposure = 0;
> -		}
> -		break;
> -	case V4L2_CID_EXPOSURE_AUTO:
> -		if (ctrl->value) {
> +		} else {
>  			const u16 vblank = 25;
>  			unsigned int total_h = mt9m001->rect.height +
>  				mt9m001->y_skip_top + vblank;
> -			if (reg_write(client, MT9M001_SHUTTER_WIDTH,
> -				      total_h) < 0)
> +
> +			if (reg_write(client, MT9M001_SHUTTER_WIDTH, total_h) < 0)
>  				return -EIO;
> -			qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE);
> -			mt9m001->exposure = (524 + (total_h - 1) *
> -				 (qctrl->maximum - qctrl->minimum)) /
> -				1048 + qctrl->minimum;
> -			mt9m001->autoexposure = 1;
> -		} else
> -			mt9m001->autoexposure = 0;
> -		break;
> +			exp->val = (524 + (total_h - 1) *
> +					(exp->maximum - exp->minimum)) / 1048 +
> +						exp->minimum;
> +		}
> +		return 0;
>  	}
> -	return 0;
> +	return -EINVAL;

It seems to me, that you've dropped V4L2_CID_EXPOSURE here, was it 
intentional? I won't verify this in detail now, because, if it wasn't 
intentional and you fix it in v2, I'll have to re-check it anyway. Or is 
it supposed to be handled by that V4L2_EXPOSURE_MANUAL? So, if the user 
issues a V4L2_CID_EXPOSURE, are you getting V4L2_CID_EXPOSURE_AUTO with 
val == V4L2_EXPOSURE_MANUAL instead? Weird...

>  }
>  
>  /*
> @@ -665,10 +587,7 @@ static int mt9m001_video_probe(struct soc_camera_device *icd,
>  		dev_err(&client->dev, "Failed to initialise the camera\n");
>  
>  	/* mt9m001_init() has reset the chip, returning registers to defaults */
> -	mt9m001->gain = 64;
> -	mt9m001->exposure = 255;
> -
> -	return ret;
> +	return v4l2_ctrl_handler_setup(&mt9m001->hdl);
>  }
>  
>  static void mt9m001_video_remove(struct soc_camera_device *icd)
> @@ -691,9 +610,11 @@ static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
>  	return 0;
>  }
>  
> +static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = {
> +	.s_ctrl = mt9m001_s_ctrl,
> +};
> +
>  static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = {
> -	.g_ctrl		= mt9m001_g_ctrl,
> -	.s_ctrl		= mt9m001_s_ctrl,
>  	.g_chip_ident	= mt9m001_g_chip_ident,
>  #ifdef CONFIG_VIDEO_ADV_DEBUG
>  	.g_register	= mt9m001_g_register,
> @@ -766,6 +687,28 @@ static int mt9m001_probe(struct i2c_client *client,
>  		return -ENOMEM;
>  
>  	v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops);
> +	v4l2_ctrl_handler_init(&mt9m001->hdl, 4);
> +	v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
> +			V4L2_CID_VFLIP, 0, 1, 1, 0);
> +	v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
> +			V4L2_CID_GAIN, 0, 127, 1, 64);
> +	mt9m001->exposure = v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
> +			V4L2_CID_EXPOSURE, 1, 255, 1, 255);
> +	/*
> +	 * Simulated autoexposure. If enabled, we calculate shutter width
> +	 * ourselves in the driver based on vertical blanking and frame width
> +	 */
> +	mt9m001->autoexposure = v4l2_ctrl_new_std_menu(&mt9m001->hdl,
> +			&mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
> +			V4L2_EXPOSURE_AUTO);
> +	mt9m001->subdev.ctrl_handler = &mt9m001->hdl;
> +	if (mt9m001->hdl.error) {
> +		int err = mt9m001->hdl.error;
> +
> +		kfree(mt9m001);
> +		return err;
> +	}
> +	v4l2_ctrl_cluster(2, &mt9m001->autoexposure);

Ooh, is this the reason for that anonymous struct?...

>  
>  	/* Second stage probe - when a capture adapter is there */
>  	icd->ops		= &mt9m001_ops;
> @@ -776,15 +719,10 @@ static int mt9m001_probe(struct i2c_client *client,
>  	mt9m001->rect.width	= MT9M001_MAX_WIDTH;
>  	mt9m001->rect.height	= MT9M001_MAX_HEIGHT;
>  
> -	/*
> -	 * Simulated autoexposure. If enabled, we calculate shutter width
> -	 * ourselves in the driver based on vertical blanking and frame width
> -	 */
> -	mt9m001->autoexposure = 1;
> -
>  	ret = mt9m001_video_probe(icd, client);
>  	if (ret) {
>  		icd->ops = NULL;
> +		v4l2_ctrl_handler_free(&mt9m001->hdl);
>  		kfree(mt9m001);
>  	}
>  
> @@ -796,6 +734,8 @@ static int mt9m001_remove(struct i2c_client *client)
>  	struct mt9m001 *mt9m001 = to_mt9m001(client);
>  	struct soc_camera_device *icd = client->dev.platform_data;
>  
> +	v4l2_device_unregister_subdev(&mt9m001->subdev);

hm, first, this is not really related, right? Secondly, are you sure this 
is needed? It is now double with soc_camera_remove(). I know, it is safe, 
but still, one of them looks superfluous to me.

> +	v4l2_ctrl_handler_free(&mt9m001->hdl);
>  	icd->ops = NULL;
>  	mt9m001_video_remove(icd);
>  	kfree(mt9m001);
> -- 
> 1.7.0.4
> 

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
--
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