All of lore.kernel.org
 help / color / mirror / Atom feed
From: Pavel Machek <pavel@ucw.cz>
To: Merlijn Wajer <merlijn@wizzup.org>
Cc: "Arthur D." <spinal.by@gmail.com>,
	sakari.ailus@iki.fi, mchehab@kernel.org,
	linux-media@vger.kernel.org,
	kernel list <linux-kernel@vger.kernel.org>
Subject: [PATCH] et8ek8: Support for EXPOSURE_ABSOLUTE
Date: Mon, 9 Mar 2020 00:12:06 +0100	[thread overview]
Message-ID: <20200308231206.GC31247@amd> (raw)
In-Reply-To: <aa625ac3-dbdc-dacd-ba79-c6d71fbe782a@wizzup.org>

[-- Attachment #1: Type: text/plain, Size: 5870 bytes --]

To do a good job taking photos, userland needs to be able to query/set
exposure in absolute units. (As scene gets darker, exposure time
should be increased to cca 1/100 second -- based on scene type -- then
sensitivity should be stepped up to maximum reasonable value, only
then time should be increased again). This patch provides neccessary
support.

Signed-off-by: Pavel Machek <pavel@ucw.cz>

---

I have experimental version of sdlcam that can take photos, and this
is the most important missing piece.

diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c
index 256acf73d5ea..82d59691c788 100644
--- a/drivers/media/i2c/et8ek8/et8ek8_driver.c
+++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c
@@ -50,6 +50,8 @@ struct et8ek8_sensor {
 
 	struct v4l2_ctrl_handler ctrl_handler;
 	struct v4l2_ctrl *exposure;
+	struct v4l2_ctrl *exposure_abs;
+	u32 cur_exposure;
 	struct v4l2_ctrl *pixel_rate;
 	struct et8ek8_reglist *current_reglist;
 
@@ -546,6 +548,65 @@ static int et8ek8_reglist_import(struct i2c_client *client,
 	return 0;
 }
 
+typedef unsigned int fixpoint8; /* .8 fixed point format. */
+
+/*
+ * Return time of one row in microseconds
+ * If the sensor is not set to any mode, return zero.
+ */
+fixpoint8 et8ek8_get_row_time(struct et8ek8_sensor *sensor)
+{
+	unsigned int clock;	/* Pixel clock in Hz>>10 fixed point */
+	fixpoint8 rt;	/* Row time in .8 fixed point */
+
+	if (!sensor->current_reglist)
+		return 0;
+
+	clock = sensor->current_reglist->mode.pixel_clock;
+	clock = (clock + (1 << 9)) >> 10;
+	rt = sensor->current_reglist->mode.width * (1000000 >> 2);
+	rt = (rt + (clock >> 1)) / clock;
+
+	return rt;
+}
+
+/*
+ * Convert exposure time `us' to rows. Modify `us' to make it to
+ * correspond to the actual exposure time.
+ */
+static int et8ek8_exposure_us_to_rows(struct et8ek8_sensor *sensor, u32 *us)
+{
+	unsigned int rows;	/* Exposure value as written to HW (ie. rows) */
+	fixpoint8 rt;	/* Row time in .8 fixed point */
+
+	/* Assume that the maximum exposure time is at most ~8 s,
+	 * and the maximum width (with blanking) ~8000 pixels.
+	 * The formula here is in principle as simple as
+	 *    rows = exptime / 1e6 / width * pixel_clock
+	 * but to get accurate results while coping with value ranges,
+	 * have to do some fixed point math.
+	 */
+
+	rt = et8ek8_get_row_time(sensor);
+	rows = ((*us << 8) + (rt >> 1)) / rt;
+
+	if (rows > sensor->current_reglist->mode.max_exp)
+		rows = sensor->current_reglist->mode.max_exp;
+
+	/* Set the exposure time to the rounded value */
+	*us = (rt * rows + (1 << 7)) >> 8;
+
+	return rows;
+}
+
+/*
+ * Convert exposure time in rows to microseconds
+ */
+static int et8ek8_exposure_rows_to_us(struct et8ek8_sensor *sensor, int rows)
+{
+	return (et8ek8_get_row_time(sensor) * rows + (1 << 7)) >> 8;
+}
+
 /* Called to change the V4L2 gain control value. This function
  * rounds and clamps the given value and updates the V4L2 control value.
  * If power is on, also updates the sensor analog and digital gains.
@@ -637,18 +698,22 @@ static int et8ek8_set_ctrl(struct v4l2_ctrl *ctrl)
 {
 	struct et8ek8_sensor *sensor =
 		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
+	u32 val = ctrl->val;
 
 	switch (ctrl->id) {
 	case V4L2_CID_GAIN:
 		return et8ek8_set_gain(sensor, ctrl->val);
 
+	case V4L2_CID_EXPOSURE_ABSOLUTE:
+		val = et8ek8_exposure_us_to_rows(sensor, &val);
+		/* Fall through */
+
 	case V4L2_CID_EXPOSURE:
 	{
-		struct i2c_client *client =
-			v4l2_get_subdevdata(&sensor->subdev);
-
+		struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+		sensor->cur_exposure = val;
 		return et8ek8_i2c_write_reg(client, ET8EK8_REG_16BIT, 0x1243,
-					    ctrl->val);
+					    val);
 	}
 
 	case V4L2_CID_TEST_PATTERN:
@@ -662,8 +727,28 @@ static int et8ek8_set_ctrl(struct v4l2_ctrl *ctrl)
 	}
 }
 
+static int et8ek8_get_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct et8ek8_sensor *sensor =
+		container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler);
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE_ABSOLUTE:
+		ctrl->val = et8ek8_exposure_rows_to_us(sensor, sensor->cur_exposure);
+		return 0;
+
+	case V4L2_CID_EXPOSURE:
+		ctrl->val = sensor->cur_exposure;
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
 static const struct v4l2_ctrl_ops et8ek8_ctrl_ops = {
 	.s_ctrl = et8ek8_set_ctrl,
+	.g_volatile_ctrl = et8ek8_get_ctrl,
 };
 
 static const char * const et8ek8_test_pattern_menu[] = {
@@ -697,6 +782,13 @@ static int et8ek8_init_controls(struct et8ek8_sensor *sensor)
 			v4l2_ctrl_new_std(&sensor->ctrl_handler,
 					  &et8ek8_ctrl_ops, V4L2_CID_EXPOSURE,
 					  min, max, min, max);
+
+		min = et8ek8_exposure_rows_to_us(sensor, 1);
+		max = et8ek8_exposure_rows_to_us(sensor, max);
+
+		sensor->exposure_abs =
+			v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops,
+					  V4L2_CID_EXPOSURE_ABSOLUTE, min, max, min, max);
 	}
 
 	/* V4L2_CID_PIXEL_RATE */
@@ -738,8 +830,12 @@ static void et8ek8_update_controls(struct et8ek8_sensor *sensor)
 	 */
 	pixel_rate = ((mode->pixel_clock + (1 << S) - 1) >> S) + mode->width;
 	pixel_rate = mode->window_width * (pixel_rate - 1) / mode->width;
+	__v4l2_ctrl_modify_range(sensor->exposure, 1, max, 1, max);
+
+	min = et8ek8_exposure_rows_to_us(sensor, 1);
+	max = et8ek8_exposure_rows_to_us(sensor, max);
+	__v4l2_ctrl_modify_range(sensor->exposure_abs, min, max, min, max);
 
-	__v4l2_ctrl_modify_range(ctrl, min, max, min, max);
 	__v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate, pixel_rate << S);
 }
 


-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

           reply	other threads:[~2020-03-08 23:12 UTC|newest]

Thread overview: expand[flat|nested]  mbox.gz  Atom feed
 [parent not found: <aa625ac3-dbdc-dacd-ba79-c6d71fbe782a@wizzup.org>]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200308231206.GC31247@amd \
    --to=pavel@ucw.cz \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=mchehab@kernel.org \
    --cc=merlijn@wizzup.org \
    --cc=sakari.ailus@iki.fi \
    --cc=spinal.by@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.