[KS workshop follow-up] multiple sensor contexts

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

 



Hi all

At the V4L/DVB workshop in Prague a couple of weeks ago possible merits of 
supporting multiple camera sensor contexts have been discussed. Such 
contexts are often promoted by camera manufacturers as a hardware 
optimization to support fast switching to the snapshot mode. Such a switch 
is often accompanied by a change of the frame format. Typically, a smaller 
frame is used for the preview mode and a larger frame is used for photo 
shooting. Those sensors provide 2 (or more) sets of frame size and data 
format registers and a single command to switch between them. The 
decision, whether or not to support these multiple camera contexts has 
been postponed until some measurements become available, how much time 
such a "fast switching" implementation would save us.

I took the mt9m111 driver, that supports mt9m111, mt9m131, and mt9m112 
camera sensors from Aptina. They do indeed implement two contexts, 
however, the driver first had to be somewhat reorganised to make use of 
them. I pushed my (highly!) experimental tree to

git://linuxtv.org/gliakhovetski/v4l-dvb.git staging-3.3

with the addition of the below debugging diff, that pre-programs a fixed 
format into the second context registers and switches to it, once a 
matching S_FMT is called. On the i.MX31 based pcm037 board, that I've got, 
this sensor is attached to the I2C bus #2, running at 20kHz. The explicit 
programming of the new format parameters measures to take around 27ms, 
which is also about what we win, when using the second context.

As for interpretation: firstly 20kHz is not much, I expect many other set 
ups to run much faster. But even if we accept, that on some hardware > 
20kHz doesn't work and we really lose 27ms when not using multiple 
register contexts, is it a lot? Thinking about my personal photographing 
experiences with cameras and camera-phones, I don't think, I'd notice a 
27ms latency;-) I don't think anything below 200ms really makes a 
difference and, I think, the major contributor to the snapshot latency is 
the need to synchronise on a frame, and, possibly skip or shoot several 
frames, instead of just one.

So, my conclusion would be: when working with "sane" camera sensors, i.e., 
those, where you don't have to reprogram 100s of registers from some magic 
tables to configure a different frame format (;-)), supporting several 
register contexts doesn't bring a huge advantage in terms of snapshot 
latency. OTOH, it can well happen, that at some point we anyway will have 
to support those multiple register contexts for some other reason.

Opinions?

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c
index 45739a1..23ffcd9 100644
--- a/drivers/media/video/mt9m111.c
+++ b/drivers/media/video/mt9m111.c
@@ -13,6 +13,7 @@
 #include <linux/log2.h>
 #include <linux/gpio.h>
 #include <linux/delay.h>
+#include <linux/time.h>
 #include <linux/v4l2-mediabus.h>
 
 #include <media/soc_camera.h>
@@ -309,11 +310,10 @@ static int mt9m111_reg_mask(struct i2c_client *client, const u16 reg,
 	return ret;
 }
 
-static int mt9m111_set_context(struct mt9m111 *mt9m111,
-			       struct mt9m111_context *ctx)
+static int mt9m111_set_context(struct mt9m111 *mt9m111)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
-	return reg_write(CONTEXT_CONTROL, ctx->control);
+	return reg_write(CONTEXT_CONTROL, mt9m111->ctx->control);
 }
 
 static int mt9m111_setup_rect_ctx(struct mt9m111 *mt9m111,
@@ -349,10 +349,7 @@ static int mt9m111_setup_geometry(struct mt9m111 *mt9m111, struct v4l2_rect *rec
 	if (code != V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) {
 		/* IFP in use, down-scaling possible */
 		if (!ret)
-			ret = mt9m111_setup_rect_ctx(mt9m111, &context_b,
-						     rect, width, height);
-		if (!ret)
-			ret = mt9m111_setup_rect_ctx(mt9m111, &context_a,
+			ret = mt9m111_setup_rect_ctx(mt9m111, mt9m111->ctx,
 						     rect, width, height);
 	}
 
@@ -523,11 +520,8 @@ static int mt9m111_set_pixfmt(struct mt9m111 *mt9m111,
 		return -EINVAL;
 	}
 
-	ret = mt9m111_reg_mask(client, context_a.output_fmt_ctrl2,
+	ret = mt9m111_reg_mask(client, mt9m111->ctx->output_fmt_ctrl2,
 			       data_outfmt2, mask_outfmt2);
-	if (!ret)
-		ret = mt9m111_reg_mask(client, context_b.output_fmt_ctrl2,
-				       data_outfmt2, mask_outfmt2);
 
 	return ret;
 }
@@ -576,27 +570,53 @@ static int mt9m111_try_fmt(struct v4l2_subdev *sd,
 	return 0;
 }
 
+#define SNAPSHOT_WIDTH 640
+#define SNAPSHOT_HEIGHT 480
+#define SNAPSHOT_CODE V4L2_MBUS_FMT_SBGGR8_1X8
+
 static int mt9m111_s_fmt(struct v4l2_subdev *sd,
 			 struct v4l2_mbus_framefmt *mf)
 {
 	const struct mt9m111_datafmt *fmt;
 	struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
 	struct v4l2_rect *rect = &mt9m111->rect;
+	struct timespec ts_1, ts_2;
 	int ret;
 
+	/* FIXME: testing only: an arbitrary policy: != VGA use context A */
+	if (mf->width != SNAPSHOT_WIDTH || mf->height != SNAPSHOT_HEIGHT ||
+	    mf->code != SNAPSHOT_CODE)
+		mt9m111->ctx = &context_a;
+	else
+		mt9m111->ctx = &context_b;
+
 	mt9m111_try_fmt(sd, mf);
 	fmt = mt9m111_find_datafmt(mt9m111, mf->code);
 	/* try_fmt() guarantees fmt != NULL && fmt->code == mf->code */
 
-	ret = mt9m111_setup_geometry(mt9m111, rect, mf->width, mf->height, mf->code);
-	if (!ret)
-		ret = mt9m111_set_pixfmt(mt9m111, mf->code);
+	ret = mt9m111_set_context(mt9m111);
+	ktime_get_ts(&ts_1);
+	if (!mt9m111->power_count || mt9m111->ctx == &context_a) {
+		if (!ret)
+			ret = mt9m111_setup_geometry(mt9m111, rect,
+					mf->width, mf->height, mf->code);
+		if (!ret)
+			ret = mt9m111_set_pixfmt(mt9m111, mf->code);
+	}
+	ktime_get_ts(&ts_2);
 	if (!ret) {
 		mt9m111->width	= mf->width;
 		mt9m111->height	= mf->height;
 		mt9m111->fmt	= fmt;
 	}
 
+	if (ts_2.tv_nsec < ts_1.tv_nsec) {
+		ts_2.tv_nsec += 1000000000;
+		ts_2.tv_sec -= 1;
+	}
+
+	pr_info("Context switch took %lu.%09lus\n", ts_2.tv_sec - ts_1.tv_sec, ts_2.tv_nsec - ts_1.tv_nsec);
+
 	return ret;
 }
 
@@ -762,7 +782,7 @@ static int mt9m111_suspend(struct mt9m111 *mt9m111)
 
 static void mt9m111_restore_state(struct mt9m111 *mt9m111)
 {
-	mt9m111_set_context(mt9m111, mt9m111->ctx);
+	mt9m111_set_context(mt9m111);
 	mt9m111_set_pixfmt(mt9m111, mt9m111->fmt->code);
 	mt9m111_setup_geometry(mt9m111, &mt9m111->rect,
 			mt9m111->width, mt9m111->height, mt9m111->fmt->code);
@@ -780,23 +800,6 @@ static int mt9m111_resume(struct mt9m111 *mt9m111)
 	return ret;
 }
 
-static int mt9m111_init(struct mt9m111 *mt9m111)
-{
-	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
-	int ret;
-
-	/* Default HIGHPOWER context */
-	mt9m111->ctx = &context_b;
-	ret = mt9m111_enable(mt9m111);
-	if (!ret)
-		ret = mt9m111_reset(mt9m111);
-	if (!ret)
-		ret = mt9m111_set_context(mt9m111, mt9m111->ctx);
-	if (ret)
-		dev_err(&client->dev, "mt9m111 init failed: %d\n", ret);
-	return ret;
-}
-
 /*
  * Interface active, can use i2c. If it fails, it can indeed mean, that
  * this wasn't our capture interface, so, we wait for the right one
@@ -805,7 +808,6 @@ static int mt9m111_video_probe(struct i2c_client *client)
 {
 	struct mt9m111 *mt9m111 = to_mt9m111(client);
 	s32 data;
-	int ret;
 
 	data = reg_read(CHIP_VERSION);
 
@@ -826,9 +828,8 @@ static int mt9m111_video_probe(struct i2c_client *client)
 		return -ENODEV;
 	}
 
-	ret = mt9m111_init(mt9m111);
-	if (ret)
-		return ret;
+	mt9m111->ctx = &context_b;
+
 	return v4l2_ctrl_handler_setup(&mt9m111->hdl);
 }
 
@@ -836,33 +837,45 @@ static int mt9m111_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	int ret = 0;
+	int ret;
 
 	mutex_lock(&mt9m111->power_lock);
 
-	/*
-	 * If the power count is modified from 0 to != 0 or from != 0 to 0,
-	 * update the power state.
-	 */
-	if (mt9m111->power_count == !on) {
-		if (on) {
+	if (on) {
+		if (!mt9m111->power_count) {
+			struct v4l2_mbus_framefmt mf_snapshot = {
+				.width	= SNAPSHOT_WIDTH,
+				.height	= SNAPSHOT_HEIGHT,
+				.code	= SNAPSHOT_CODE,
+			};
+
+			/* Pre-program a fixed snapshot format */
+			mt9m111_s_fmt(&mt9m111->subdev, &mf_snapshot);
+
+			/* Default preview context */
+			mt9m111->ctx = &context_a;
+
 			ret = mt9m111_resume(mt9m111);
-			if (ret) {
+			if (ret < 0)
 				dev_err(&client->dev,
 					"Failed to resume the sensor: %d\n", ret);
-				goto out;
-			}
 		} else {
-			mt9m111_suspend(mt9m111);
+			ret = 0;
 		}
+		if (!ret)
+			mt9m111->power_count++;
+	} else {
+		if (WARN_ON(!mt9m111->power_count)) {
+			ret = -EINVAL;
+			goto out;
+		}
+		ret = mt9m111_suspend(mt9m111);
+		mt9m111->power_count--;
 	}
 
-	/* Update the power count. */
-	mt9m111->power_count += on ? 1 : -1;
-	WARN_ON(mt9m111->power_count < 0);
-
 out:
 	mutex_unlock(&mt9m111->power_lock);
+
 	return ret;
 }
 
--
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