summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorRichard Leitner <richard.leitner@linux.dev>2025-12-09 23:44:42 +0100
committerHans Verkuil <hverkuil+cisco@kernel.org>2026-01-16 14:08:53 +0100
commitb95d8058a2786a8b42e32f383f8df45097675d00 (patch)
treeb952ae3f29c23687ca4cba65d393ff6a5e44a95f /drivers
parent84ec7597ae1afb9430c55044f318d1d40f8ecef0 (diff)
media: i2c: ov9282: implement try_ctrl for strobe_duration
As the granularity of the hardware supported values is lower than the control value, implement a try_ctrl() function for V4L2_CID_FLASH_DURATION. This function calculates the nearest possible µs strobe duration for the given value and returns it back to the caller. Signed-off-by: Richard Leitner <richard.leitner@linux.dev> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/i2c/ov9282.c53
1 files changed, 51 insertions, 2 deletions
diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c
index 92da28f248a4..3c74d65fe312 100644
--- a/drivers/media/i2c/ov9282.c
+++ b/drivers/media/i2c/ov9282.c
@@ -9,6 +9,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
+#include <linux/math.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -128,6 +129,8 @@
#define OV9282_REG_MIN 0x00
#define OV9282_REG_MAX 0xfffff
+#define OV9282_STROBE_SPAN_FACTOR 192
+
static const char * const ov9282_supply_names[] = {
"avdd", /* Analog power */
"dovdd", /* Digital I/O power */
@@ -691,7 +694,7 @@ static int ov9282_set_ctrl_flash_strobe_oe(struct ov9282 *ov9282, bool enable)
return ov9282_write_reg(ov9282, OV9282_REG_OUTPUT_ENABLE6, 1, current_val);
}
-static int ov9282_set_ctrl_flash_duration(struct ov9282 *ov9282, u32 value)
+static u32 ov9282_us_to_flash_duration(struct ov9282 *ov9282, u32 value)
{
/*
* Calculate "strobe_frame_span" increments from a given value (µs).
@@ -702,7 +705,26 @@ static int ov9282_set_ctrl_flash_duration(struct ov9282 *ov9282, u32 value)
* The formula below is interpolated from different modes/framerates
* and should work quite well for most settings.
*/
- u32 val = value * 192 / (ov9282->cur_mode->width + ov9282->hblank_ctrl->val);
+ u32 frame_width = ov9282->cur_mode->width + ov9282->hblank_ctrl->val;
+
+ return value * OV9282_STROBE_SPAN_FACTOR / frame_width;
+}
+
+static u32 ov9282_flash_duration_to_us(struct ov9282 *ov9282, u32 value)
+{
+ /*
+ * Calculate back to microseconds from "strobe_frame_span" increments.
+ * As the calculation in ov9282_us_to_flash_duration uses an integer
+ * divison round up here.
+ */
+ u32 frame_width = ov9282->cur_mode->width + ov9282->hblank_ctrl->val;
+
+ return DIV_ROUND_UP(value * frame_width, OV9282_STROBE_SPAN_FACTOR);
+}
+
+static int ov9282_set_ctrl_flash_duration(struct ov9282 *ov9282, u32 value)
+{
+ u32 val = ov9282_us_to_flash_duration(ov9282, value);
int ret;
ret = ov9282_write_reg(ov9282, OV9282_REG_STROBE_FRAME_SPAN, 1,
@@ -806,9 +828,36 @@ static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl)
return ret;
}
+static int ov9282_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov9282 *ov9282 =
+ container_of_const(ctrl->handler, struct ov9282, ctrl_handler);
+
+ if (ctrl->id == V4L2_CID_FLASH_DURATION) {
+ u32 us = ctrl->val;
+ u32 fd = ov9282_us_to_flash_duration(ov9282, us);
+
+ /* get nearest strobe_duration value */
+ u32 us0 = ov9282_flash_duration_to_us(ov9282, fd);
+ u32 us1 = ov9282_flash_duration_to_us(ov9282, fd + 1);
+
+ if (abs(us1 - us) < abs(us - us0))
+ ctrl->val = us1;
+ else
+ ctrl->val = us0;
+
+ if (us != ctrl->val)
+ dev_dbg(ov9282->dev, "using next valid strobe_duration %u instead of %u\n",
+ ctrl->val, us);
+ }
+
+ return 0;
+}
+
/* V4l2 subdevice control ops*/
static const struct v4l2_ctrl_ops ov9282_ctrl_ops = {
.s_ctrl = ov9282_set_ctrl,
+ .try_ctrl = ov9282_try_ctrl,
};
/**