diff mbox

mt9t031: preserve output format on VIDIOC_S_CROP

Message ID Pine.LNX.4.64.1004141605110.9388@axis700.grange (mailing list archive)
State RFC
Headers show

Commit Message

Guennadi Liakhovetski April 14, 2010, 2:23 p.m. UTC
None
diff mbox

Patch

diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c
index a9061bf..a604fa0 100644
--- a/drivers/media/video/mt9t031.c
+++ b/drivers/media/video/mt9t031.c
@@ -63,6 +63,8 @@ 
 struct mt9t031 {
 	struct v4l2_subdev subdev;
 	struct v4l2_rect rect;	/* Sensor window */
+	unsigned int out_width;
+	unsigned int out_height;
 	int model;	/* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */
 	u16 xskip;
 	u16 yskip;
@@ -284,6 +286,26 @@  static u16 mt9t031_skip(s32 *source, s32 target, s32 max)
 	return skip;
 }
 
+static u16 mt9t031_skip_out(s32 *source, s32 *target)
+{
+	unsigned int skip;
+
+	if (*source < *target + *target / 2) {
+		*target = *source;
+		return 1;
+	}
+
+	skip = (*source + *target / 2) / *target;
+	if (skip > 8) {
+		skip = 8;
+		*target = (*source + 4) / 8;
+	}
+	/* We try to preserve input, but with division we have to adjust it too */
+	*source = *target * skip;
+
+	return skip;
+}
+
 /* rect is the sensor rectangle, the caller guarantees parameter validity */
 static int mt9t031_set_params(struct i2c_client *client,
 			      struct v4l2_rect *rect, u16 xskip, u16 yskip)
@@ -393,6 +415,7 @@  static int mt9t031_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 	struct v4l2_rect rect = a->c;
 	struct i2c_client *client = sd->priv;
 	struct mt9t031 *mt9t031 = to_mt9t031(client);
+	u16 xskip, yskip;
 
 	rect.width = ALIGN(rect.width, 2);
 	rect.height = ALIGN(rect.height, 2);
@@ -403,7 +426,10 @@  static int mt9t031_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 	soc_camera_limit_side(&rect.top, &rect.height,
 		     MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT);
 
-	return mt9t031_set_params(client, &rect, mt9t031->xskip, mt9t031->yskip);
+	xskip = mt9t031_skip_out(&rect.width, &mt9t031->out_width);
+	yskip = mt9t031_skip_out(&rect.height, &mt9t031->out_height);
+
+	return mt9t031_set_params(client, &rect, xskip, yskip);
 }
 
 static int mt9t031_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
@@ -437,8 +463,8 @@  static int mt9t031_g_fmt(struct v4l2_subdev *sd,
 	struct i2c_client *client = sd->priv;
 	struct mt9t031 *mt9t031 = to_mt9t031(client);
 
-	mf->width	= mt9t031->rect.width / mt9t031->xskip;
-	mf->height	= mt9t031->rect.height / mt9t031->yskip;
+	mf->width	= mt9t031->out_width;
+	mf->height	= mt9t031->out_height;
 	mf->code	= V4L2_MBUS_FMT_SBGGR10_1X10;
 	mf->colorspace	= V4L2_COLORSPACE_SRGB;
 	mf->field	= V4L2_FIELD_NONE;
@@ -455,8 +481,9 @@  static int mt9t031_s_fmt(struct v4l2_subdev *sd,
 	struct v4l2_rect rect = mt9t031->rect;
 
 	/*
-	 * try_fmt has put width and height within limits.
-	 * S_FMT: use binning and skipping for scaling
+	 * try_fmt has put width and height within limits. Note: when converting
+	 * to a generic v4l2-subdev driver, try_fmt will have to be called
+	 * explicitly. S_FMT: use binning and skipping for scaling.
 	 */
 	xskip = mt9t031_skip(&rect.width, mf->width, MT9T031_MAX_WIDTH);
 	yskip = mt9t031_skip(&rect.height, mf->height, MT9T031_MAX_HEIGHT);
@@ -464,6 +491,9 @@  static int mt9t031_s_fmt(struct v4l2_subdev *sd,
 	mf->code	= V4L2_MBUS_FMT_SBGGR10_1X10;
 	mf->colorspace	= V4L2_COLORSPACE_SRGB;
 
+	mt9t031->out_width	= mf->width;
+	mt9t031->out_height	= mf->height;
+
 	/* mt9t031_set_params() doesn't change width and height */
 	return mt9t031_set_params(client, &rect, xskip, yskip);
 }
@@ -807,6 +837,9 @@  static int mt9t031_probe(struct i2c_client *client,
 	mt9t031->rect.width	= MT9T031_MAX_WIDTH;
 	mt9t031->rect.height	= MT9T031_MAX_HEIGHT;
 
+	mt9t031->out_width	= MT9T031_MAX_WIDTH;
+	mt9t031->out_height	= MT9T031_MAX_HEIGHT;
+
 	/*
 	 * Simulated autoexposure. If enabled, we calculate shutter width
 	 * ourselves in the driver based on vertical blanking and frame width