From: Saikiran <bjsaikiran@gmail.com>
To: linux-media@vger.kernel.org
Cc: linux-arm-msm@vger.kernel.org, rfoss@kernel.org,
todor.too@gmail.com, bryan.odonoghue@linaro.org, bod@kernel.org,
vladimir.zapolskiy@linaro.org, hansg@kernel.org,
sakari.ailus@linux.intel.com, mchehab@kernel.org,
Saikiran <bjsaikiran@gmail.com>
Subject: [PATCH] media: i2c: ov02c10: Enforce cool-down period to prevent brownout
Date: Sat, 24 Jan 2026 12:47:51 +0530 [thread overview]
Message-ID: <20260124071751.5885-4-bjsaikiran@gmail.com> (raw)
In-Reply-To: <20260124071751.5885-1-bjsaikiran@gmail.com>
The OV02C10 sensor is susceptible to brownout/latch-up states when
power-cycled rapidly (e.g., within 50-100ms). This often occurs during
userspace interactions like browser WebRTC permissions checks, where
the device is opened, closed, and reopened in quick succession.
When this happens, the regulator discharge is incomplete, and the
sensor fails to perform a clean Power-On Reset (POR). The internal
microcontroller locks up, resulting in I2C timeouts ("failed to set
mode") and necessitating a full system reboot to recover the camera.
To prevent this, implement a mandatory cool-down period. The driver
now tracks the timestamp of the last power-off. If a power-on attempt
occurs within 3 seconds of the last power-off, the driver sleeps for
the remaining duration to ensure physical power rails have fully
discharged and the sensor has completely reset before voltage is
re-applied.
Additionally, standard Power-On-Reset logic is refined:
1. Ensure MCLK is disabled BEFORE regulators during power-off to
prevent phantom power injection.
2. Assert the reset line (hold low) throughout the regulator ramp-up
phase to prevent indeterminate states.
Testing Results (10 rapid cycles each):
1. 900ms minimum gap: Failed (brownout/timeout errors)
2. 1500ms minimum gap: Failed (intermittent failures)
3. 2000ms minimum gap: Reliable (0 failures in 50+ test cycles)
4. 3000ms minimum gap: Reliable (excessive, 2s is sufficient)
The 3-second check window with 2-second minimum enforcement provides
the optimal balance between reliability and responsiveness.
Signed-off-by: Saikiran <bjsaikiran@gmail.com>
---
drivers/media/i2c/ov02c10.c | 63 +++++++++++++++++++++++++++++++------
1 file changed, 54 insertions(+), 9 deletions(-)
diff --git a/drivers/media/i2c/ov02c10.c b/drivers/media/i2c/ov02c10.c
index db191dccff75..7e9454e8540c 100644
--- a/drivers/media/i2c/ov02c10.c
+++ b/drivers/media/i2c/ov02c10.c
@@ -389,6 +389,9 @@ struct ov02c10 {
/* MIPI lane info */
u32 link_freq_index;
u8 mipi_lanes;
+
+ /* Power cycling rate limit */
+ ktime_t last_power_off;
};
static inline struct ov02c10 *to_ov02c10(struct v4l2_subdev *subdev)
@@ -616,6 +619,13 @@ static int ov02c10_enable_streams(struct v4l2_subdev *sd,
if (ret)
goto out;
+ /*
+ * Delay before streaming:
+ * Give the sensor time to process all the register writes and internal
+ * calibration before we assert the STREAM_ON bit.
+ */
+ usleep_range(2000, 2500);
+
ret = cci_write(ov02c10->regmap, OV02C10_REG_STREAM_CONTROL, 1, NULL);
out:
if (ret)
@@ -670,12 +680,25 @@ static int ov02c10_power_off(struct device *dev)
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct ov02c10 *ov02c10 = to_ov02c10(sd);
+ /* 1. Assert Reset */
gpiod_set_value_cansleep(ov02c10->reset, 1);
+ /* 2. Disable Clock (Stop sensor state machine) */
+ clk_disable_unprepare(ov02c10->img_clk);
+ usleep_range(1000, 1500);
+
+ /* 3. Disable Power */
regulator_bulk_disable(ARRAY_SIZE(ov02c10_supply_names),
ov02c10->supplies);
- clk_disable_unprepare(ov02c10->img_clk);
+ /*
+ * 4. Discharge Wait
+ * Wait for regulators to fully discharge before returning.
+ * This delay ensures clean power cycling.
+ */
+ usleep_range(50000, 55000);
+
+ ov02c10->last_power_off = ktime_get();
return 0;
}
@@ -685,26 +708,48 @@ static int ov02c10_power_on(struct device *dev)
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct ov02c10 *ov02c10 = to_ov02c10(sd);
int ret;
+ s64 delta_us;
- ret = clk_prepare_enable(ov02c10->img_clk);
- if (ret < 0) {
- dev_err(dev, "failed to enable imaging clock: %d", ret);
- return ret;
+ /*
+ * Mandatory Cool-Down:
+ * If the camera was powered off within the last 3 seconds, ensure at least
+ * 2 seconds have elapsed to allow full regulator discharge and sensor reset.
+ * This prevents brownouts during rapid open/close/open sequences.
+ */
+ delta_us = ktime_us_delta(ktime_get(), ov02c10->last_power_off);
+ if (delta_us < 3000000) {
+ dev_dbg(dev, "Enforcing %lld us cool-down period\n", 2000000 - delta_us);
+ fsleep(2000000 - delta_us);
}
+ /*
+ * Standard Power-Up Sequence:
+ * 1. Enable Regulators
+ * 2. Enable Clock
+ * 3. Release Reset (with ample boot time)
+ */
+
ret = regulator_bulk_enable(ARRAY_SIZE(ov02c10_supply_names),
ov02c10->supplies);
if (ret < 0) {
dev_err(dev, "failed to enable regulators: %d", ret);
- clk_disable_unprepare(ov02c10->img_clk);
return ret;
}
+ ret = clk_prepare_enable(ov02c10->img_clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable imaging clock: %d", ret);
+ regulator_bulk_disable(ARRAY_SIZE(ov02c10_supply_names),
+ ov02c10->supplies);
+ return ret;
+ }
+
+ /* Wait for power/clock to stabilize */
+ usleep_range(5000, 5500);
+
if (ov02c10->reset) {
- /* Assert reset for at least 2ms on back to back off-on */
- usleep_range(5000, 5500);
gpiod_set_value_cansleep(ov02c10->reset, 0);
- usleep_range(20000, 21000);
+ usleep_range(80000, 85000);
}
return 0;
--
2.51.0
next prev parent reply other threads:[~2026-01-24 7:18 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-24 7:17 [PATCH 0/3] Fix OV02C10 camera stability on Snapdragon X Elite Saikiran
2026-01-24 7:17 ` [PATCH] media: qcom: camss: Fix pipeline lock leak in stop_streaming Saikiran
2026-01-25 12:23 ` Bryan O'Donoghue
2026-01-24 7:17 ` [PATCH] media: i2c: ov02c10: Check for errors in disable_streams Saikiran
2026-01-25 12:26 ` Bryan O'Donoghue
2026-01-26 10:18 ` Hans de Goede
2026-01-24 7:17 ` Saikiran [this message]
2026-01-25 13:21 ` [PATCH] media: i2c: ov02c10: Enforce cool-down period to prevent brownout Bryan O'Donoghue
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=20260124071751.5885-4-bjsaikiran@gmail.com \
--to=bjsaikiran@gmail.com \
--cc=bod@kernel.org \
--cc=bryan.odonoghue@linaro.org \
--cc=hansg@kernel.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=mchehab@kernel.org \
--cc=rfoss@kernel.org \
--cc=sakari.ailus@linux.intel.com \
--cc=todor.too@gmail.com \
--cc=vladimir.zapolskiy@linaro.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox