From 3c371e3b35ba862b9c71824be372a8f04b896cef Mon Sep 17 00:00:00 2001 From: "Amarnath Kumar, Deepak" Date: Tue, 15 Sep 2026 15:55:25 +0300 Subject: [PATCH 1/8] media: i2c: imx471: add settle delay before entering streaming mode Insert a usleep_range(30000, 50000) settle window between the final mode-configuration register write and the MODE_SELECT=STREAMING write in imx471_start_streaming(). On platforms with a discrete MIPI retimer between the sensor and the IPU CSI2 receiver, the retimer's PLL/CDR needs time to complete HS lock after the sensor is powered back on (e.g. after a privacy-shutter reopen). Without this margin, the very first HS burst can arrive before the retimer has locked, causing the CSI2 receiver to flag short-packet errors on frame 0 of the new streaming session, which can cascade into sync-frame-drop / poll-timeout symptoms downstream. WIP: posting for early feedback, not ready for review yet. Signed-off-by: Amarnath Kumar, Deepak --- drivers/media/i2c/imx471.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c index 75c8893a239a..2b7358a09836 100644 --- a/drivers/media/i2c/imx471.c +++ b/drivers/media/i2c/imx471.c @@ -767,6 +767,13 @@ static int imx471_start_streaming(struct imx471 *imx471) if (ret) return ret; + /* + * Settle window before entering streaming mode so an external MIPI + * retimer (if present) has time to complete HS-lock, avoiding + * short-packet corruption on the first frame after reopen. + */ + usleep_range(30000, 50000); + return imx471_write_reg(imx471, IMX471_REG_MODE_SELECT, 1, IMX471_MODE_STREAMING); } From 18bd590e17706762fae6f3059c93fa1ec463a75a Mon Sep 17 00:00:00 2001 From: "Amarnath Kumar, Deepak" Date: Tue, 15 Sep 2026 17:05:16 +0300 Subject: [PATCH 2/8] media: i2c: imx471: make retimer settle delay configurable Replace the fixed usleep_range(30000, 50000) settle delay with a per-platform configurable value (imx471->settle_delay_us, default 40ms +/-10ms), overridable via the optional firmware property "intel,retimer-settle-delay-us". This mirrors the pattern used by the Windows sensor driver, which defines a per-module default delay that can be overridden via a registry key (OS_ReadRegistry_V2(..., "IsSupportCameraMask"/delay overrides, ...)). Linux lacks the same per-DSDT-module-name string identification Windows uses, so this exposes the equivalent knob as a standard fwnode/ACPI property instead, so specific boards (e.g. platforms with a discrete MIPI retimer) can tune the value without a code change. Signed-off-by: Amarnath Kumar, Deepak --- drivers/media/i2c/imx471.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c index 2b7358a09836..9d828b4200fa 100644 --- a/drivers/media/i2c/imx471.c +++ b/drivers/media/i2c/imx471.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -157,6 +158,9 @@ struct imx471 { /* True if the device has been identified */ bool identified; + + /* Pre-streaming settle delay (us) for external MIPI retimer HS-lock */ + u32 settle_delay_us; }; static const struct imx471_reg imx471_global_regs[] = { @@ -772,7 +776,7 @@ static int imx471_start_streaming(struct imx471 *imx471) * retimer (if present) has time to complete HS-lock, avoiding * short-packet corruption on the first frame after reopen. */ - usleep_range(30000, 50000); + usleep_range(imx471->settle_delay_us, imx471->settle_delay_us + 20000); return imx471_write_reg(imx471, IMX471_REG_MODE_SELECT, 1, IMX471_MODE_STREAMING); @@ -982,6 +986,16 @@ static int imx471_get_pm_resources(struct device *dev) return dev_err_probe(dev, PTR_ERR(imx471->img_clk), "failed to get imaging clock\n"); + /* + * Default settle delay before entering streaming mode, sized for an + * external MIPI retimer's HS-lock time. Board/platform can override + * via the "intel,retimer-settle-delay-us" firmware property, mirroring + * the per-module registry override used by the Windows sensor driver. + */ + imx471->settle_delay_us = 40000; + fwnode_property_read_u32(dev_fwnode(dev), "intel,retimer-settle-delay-us", + &imx471->settle_delay_us); + return 0; } From 0e253fd7f1eefd638bab3491070b186182b04b27 Mon Sep 17 00:00:00 2001 From: "Amarnath Kumar, Deepak" Date: Tue, 15 Sep 2026 17:30:09 +0300 Subject: [PATCH 3/8] media: i2c: imx471: fix reset/power sequencing on power on/off - imx471_power_off(): assert hardware reset (XCLR low) and wait 1-2ms while the imaging clock is still active, before disabling the clock and regulator. Previously the clock was cut before reset was asserted, risking undefined internal sensor state on next power-up. - imx471_power_on(): assert reset immediately, then enable regulator/clock, hold reset low for >= 20ms while supplies and MCLK stabilize, then release reset and wait 30ms for the sensor's internal PLL and I2C core to stabilize before any register access. Previously reset was never actively asserted during power-up and had no hold time, matching reference driver patterns (ov01a10.c, ov02c10.c, ov5675.c) that all assert reset for >= 2-20ms during power-up and enforce a post-reset settle delay. This is a separate, complementary fix to the retimer settle delay in imx471_start_streaming(): this one ensures the sensor's own internal state (frame counters, PLL, I2C core) is reliably reset on every power-on, independent of any external MIPI retimer. Signed-off-by: Amarnath Kumar, Deepak --- drivers/media/i2c/imx471.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c index 9d828b4200fa..477350cb369f 100644 --- a/drivers/media/i2c/imx471.c +++ b/drivers/media/i2c/imx471.c @@ -695,8 +695,11 @@ static int imx471_power_off(struct device *dev) dev_info(dev, "imx471 power off"); - clk_disable_unprepare(imx471->img_clk); + /* Assert hardware reset (XCLR Low) while clock is still active */ gpiod_set_value_cansleep(imx471->reset_gpio, 1); + usleep_range(1000, 2000); + + clk_disable_unprepare(imx471->img_clk); if (imx471->avdd) regulator_disable(imx471->avdd); @@ -711,6 +714,9 @@ static int imx471_power_on(struct device *dev) dev_info(dev, "start to power on"); + /* Ensure hardware reset is held active LOW during power ramp */ + gpiod_set_value_cansleep(imx471->reset_gpio, 1); + if (imx471->avdd) { ret = regulator_enable(imx471->avdd); if (ret < 0) { @@ -725,9 +731,14 @@ static int imx471_power_on(struct device *dev) return ret; } + /* Hold reset low for >= 20ms after power supplies and MCLK stabilize */ + msleep(20); + + /* Release hardware reset (XCLR High) */ gpiod_set_value_cansleep(imx471->reset_gpio, 0); - usleep_range(10000, 15000); + /* Allow Sony IMX471 internal PLL and I2C core to stabilize */ + msleep(30); return 0; } From 6d7a35f35a9cdb189fae989ce98c0f31454a0597 Mon Sep 17 00:00:00 2001 From: "Amarnath Kumar, Deepak" Date: Tue, 15 Sep 2026 17:33:44 +0300 Subject: [PATCH 4/8] media: i2c: imx471: add software reset + standby before streaming setup Issue IMX471_REG_SW_RESET (0x0103=1) and place the sensor in standby (MODE_SELECT=STANDBY) at the start of imx471_start_streaming(), before identify_module()/register configuration, to reset frame counters and digital logic on every stream start. Uses a 10-15ms settle window after the software reset write (matching ov2740.c and imx258.c's ~12ms), NOT the shorter 1-2ms initially proposed: a too-short settle here was previously observed to cause MIPI runt packets on frame 0 and FLL/VMAX timing-register corruption (66 FPS overrun) when register writes landed while the digital core was still mid-reset. Signed-off-by: Amarnath Kumar, Deepak --- drivers/media/i2c/imx471.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c index 477350cb369f..4397320e7819 100644 --- a/drivers/media/i2c/imx471.c +++ b/drivers/media/i2c/imx471.c @@ -23,6 +23,9 @@ #define IMX471_MODE_STANDBY 0x00 #define IMX471_MODE_STREAMING 0x01 +/* Software reset */ +#define IMX471_REG_SW_RESET 0x0103 + /* Chip ID */ #define IMX471_REG_CHIP_ID 0x0016 #define IMX471_CHIP_ID 0x0471 @@ -752,6 +755,18 @@ static int imx471_start_streaming(struct imx471 *imx471) dev_info(&client->dev, "Start streaming\n"); + /* Software reset, then hold in standby before applying settings */ + ret = imx471_write_reg(imx471, IMX471_REG_SW_RESET, 1, 1); + if (ret) + return ret; + /* 10-15ms settle window required after SW reset before register access */ + usleep_range(10000, 15000); + + ret = imx471_write_reg(imx471, IMX471_REG_MODE_SELECT, 1, + IMX471_MODE_STANDBY); + if (ret) + return ret; + ret = imx471_identify_module(imx471); if (ret) return ret; From 07184774d9e431cc25d7a3cde3861989cbfe2f9d Mon Sep 17 00:00:00 2001 From: "Amarnath Kumar, Deepak" Date: Tue, 29 Sep 2026 15:17:15 +0300 Subject: [PATCH 5/8] media: i2c: imx471: increase default retimer settle delay to 75ms Increase the default pre-streaming settle delay from 40ms to 75ms. Hardware testing on Lenovo platforms with discrete MIPI retimers shows that 40ms was borderline for the retimer's PLL/CDR to complete High-Speed (HS) lock on stream reopen (privacy shutter un-shutter / switchToNormal), leading to occasional CSI-2 short packet discard errors (0x00000020) and truncated frame dimensions (error group 3 code 18). Increasing the default delay to 75ms (+20ms margin) completely eliminates the 0x20 short packet discard error on stream restart. Signed-off-by: Amarnath Kumar, Deepak --- drivers/media/i2c/imx471.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c index 4397320e7819..f7cfcd1fd22d 100644 --- a/drivers/media/i2c/imx471.c +++ b/drivers/media/i2c/imx471.c @@ -1018,7 +1018,7 @@ static int imx471_get_pm_resources(struct device *dev) * via the "intel,retimer-settle-delay-us" firmware property, mirroring * the per-module registry override used by the Windows sensor driver. */ - imx471->settle_delay_us = 40000; + imx471->settle_delay_us = 75000; fwnode_property_read_u32(dev_fwnode(dev), "intel,retimer-settle-delay-us", &imx471->settle_delay_us); From 2802bc6600647171fac6bc216b655bcc13c9a977 Mon Sep 17 00:00:00 2001 From: "Amarnath Kumar, Deepak" Date: Tue, 29 Sep 2026 15:35:22 +0300 Subject: [PATCH 6/8] media: i2c: imx471: add grouped parameter hold for exposure and timing controls Use IMX471_REG_PARAM_HOLD (0x0104) to group parameter updates during imx471_set_ctrl() for analogue gain, digital gain, exposure, and vertical blanking (FLL). Writing exposure and timing registers while the sensor is actively streaming without group hold can cause registers to be latched mid-raster, leading to frame timing mismatches and truncated frame dimensions on stream startup. Asserting parameter hold ensures that gain, exposure, and vertical blanking updates are latched atomically during the next vertical blanking period. Signed-off-by: Amarnath Kumar, Deepak --- drivers/media/i2c/imx471.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c index f7cfcd1fd22d..81651bc70714 100644 --- a/drivers/media/i2c/imx471.c +++ b/drivers/media/i2c/imx471.c @@ -26,6 +26,9 @@ /* Software reset */ #define IMX471_REG_SW_RESET 0x0103 +/* Grouped parameter hold */ +#define IMX471_REG_PARAM_HOLD 0x0104 + /* Chip ID */ #define IMX471_REG_CHIP_ID 0x0016 #define IMX471_CHIP_ID 0x0471 @@ -496,22 +499,30 @@ static int imx471_set_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: + imx471_write_reg(imx471, IMX471_REG_PARAM_HOLD, 1, 1); /* Analog gain = 1024/(1024 - ctrl->val) times */ ret = imx471_write_reg(imx471, IMX471_REG_ANALOG_GAIN, 2, ctrl->val); + imx471_write_reg(imx471, IMX471_REG_PARAM_HOLD, 1, 0); break; case V4L2_CID_DIGITAL_GAIN: + imx471_write_reg(imx471, IMX471_REG_PARAM_HOLD, 1, 1); ret = imx471_write_reg(imx471, IMX471_REG_DIG_GAIN_GLOBAL, 2, ctrl->val); + imx471_write_reg(imx471, IMX471_REG_PARAM_HOLD, 1, 0); break; case V4L2_CID_EXPOSURE: + imx471_write_reg(imx471, IMX471_REG_PARAM_HOLD, 1, 1); ret = imx471_write_reg(imx471, IMX471_REG_EXPOSURE, 2, ctrl->val); + imx471_write_reg(imx471, IMX471_REG_PARAM_HOLD, 1, 0); break; case V4L2_CID_VBLANK: + imx471_write_reg(imx471, IMX471_REG_PARAM_HOLD, 1, 1); /* Update FLL that meets expected vertical blanking */ ret = imx471_write_reg(imx471, IMX471_REG_FLL, 2, imx471->cur_mode->height + ctrl->val); + imx471_write_reg(imx471, IMX471_REG_PARAM_HOLD, 1, 0); break; case V4L2_CID_TEST_PATTERN: ret = imx471_write_reg(imx471, IMX471_REG_TEST_PATTERN, From 11cf4e7cdefa66bf019b51505524a4deb387e2ac Mon Sep 17 00:00:00 2001 From: "Amarnath Kumar, Deepak" Date: Wed, 30 Sep 2026 07:29:05 +0300 Subject: [PATCH 7/8] media: i2c: imx471: clamp exposure to FLL margin and align timing registers - Synchronize FLL and exposure clamping: Sony sensors require coarse integration time to not exceed (FLL - 18). During dynamic control updates (e.g. after privacy shutter un-shuttering), if FLL is reduced while exposure remains high, the sensor timing logic stalls. Update exposure within the same parameter hold when FLL is modified to ensure exposure <= FLL - 18 at all times. - Align mode_1928x1088_regs defaults with 30fps supported_modes: set FLL to 1308 (0x051C) and exposure to 1270 (0x04F6) instead of 5040/5022 lines. - Add manual T_CLK-POST configuration (0x4802=0x20, 0x4820=0x01, 0x4821=0xCC, 460ns for 400 Mbps) required by the Synopsys CSI-2 host to prevent clock lane premature cutoff, matching the reference Windows driver. Signed-off-by: Amarnath Kumar, Deepak --- drivers/media/i2c/imx471.c | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c index 81651bc70714..9045da471e8a 100644 --- a/drivers/media/i2c/imx471.c +++ b/drivers/media/i2c/imx471.c @@ -240,8 +240,8 @@ static const struct imx471_reg mode_1928x1088_regs[] = { {0x0114, 0x03}, {0x0342, 0x0a}, {0x0343, 0x00}, - {0x0340, 0x13}, - {0x0341, 0xb0}, + {0x0340, 0x05}, + {0x0341, 0x1c}, {0x0344, 0x00}, {0x0345, 0x00}, {0x0346, 0x01}, @@ -281,8 +281,8 @@ static const struct imx471_reg mode_1928x1088_regs[] = { {0x030e, 0x00}, {0x030f, 0x53}, {0x0310, 0x01}, - {0x0202, 0x13}, - {0x0203, 0x9e}, + {0x0202, 0x04}, + {0x0203, 0xf6}, {0x0204, 0x00}, {0x0205, 0x00}, {0x020e, 0x01}, @@ -292,6 +292,10 @@ static const struct imx471_reg mode_1928x1088_regs[] = { {0x3ffe, 0x00}, {0x3fff, 0x8a}, {0x5f0a, 0xb6}, + /* Manual Tclk-post for Synopsys CSI host (460ns for 400 Mbps) */ + {0x4802, 0x20}, + {0x4820, 0x01}, + {0x4821, 0xcc}, }; static const char * const imx471_test_pattern_menu[] = { @@ -511,19 +515,34 @@ static int imx471_set_ctrl(struct v4l2_ctrl *ctrl) ctrl->val); imx471_write_reg(imx471, IMX471_REG_PARAM_HOLD, 1, 0); break; - case V4L2_CID_EXPOSURE: + case V4L2_CID_EXPOSURE: { + u32 fll = imx471->cur_mode->height + imx471->vblank->val; + u32 exp_val = ctrl->val; + + if (fll > 18 && exp_val > fll - 18) + exp_val = fll - 18; + imx471_write_reg(imx471, IMX471_REG_PARAM_HOLD, 1, 1); ret = imx471_write_reg(imx471, IMX471_REG_EXPOSURE, 2, - ctrl->val); + exp_val); imx471_write_reg(imx471, IMX471_REG_PARAM_HOLD, 1, 0); break; - case V4L2_CID_VBLANK: + } + case V4L2_CID_VBLANK: { + u32 fll = imx471->cur_mode->height + ctrl->val; + u32 cur_exp = imx471->exposure->val; + imx471_write_reg(imx471, IMX471_REG_PARAM_HOLD, 1, 1); /* Update FLL that meets expected vertical blanking */ - ret = imx471_write_reg(imx471, IMX471_REG_FLL, 2, - imx471->cur_mode->height + ctrl->val); + ret = imx471_write_reg(imx471, IMX471_REG_FLL, 2, fll); + if (fll > 18 && cur_exp > fll - 18) { + cur_exp = fll - 18; + imx471_write_reg(imx471, IMX471_REG_EXPOSURE, 2, + cur_exp); + } imx471_write_reg(imx471, IMX471_REG_PARAM_HOLD, 1, 0); break; + } case V4L2_CID_TEST_PATTERN: ret = imx471_write_reg(imx471, IMX471_REG_TEST_PATTERN, 2, ctrl->val); From d11e7ede4fc3bf260efcaa26aca6447b1425fc23 Mon Sep 17 00:00:00 2001 From: "Amarnath Kumar, Deepak" Date: Wed, 30 Sep 2026 11:33:42 +0300 Subject: [PATCH 8/8] media: i2c: imx471: wrap initial control setup with parameter hold Hold IMX471_REG_PARAM_HOLD (0x0104) while applying initial customized controls via __v4l2_ctrl_handler_setup() in imx471_start_streaming(). This ensures that exposure, gain, and vertical blanking (FLL) are latched atomically before entering streaming mode, preventing in-flight frame truncation or packet length mismatch errors on stream startup. Signed-off-by: Amarnath Kumar, Deepak --- drivers/media/i2c/imx471.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c index 9045da471e8a..ea36c30bdbf9 100644 --- a/drivers/media/i2c/imx471.c +++ b/drivers/media/i2c/imx471.c @@ -822,8 +822,10 @@ static int imx471_start_streaming(struct imx471 *imx471) if (ret) return ret; - /* Apply customized values from user */ - ret = __v4l2_ctrl_handler_setup(imx471->sd.ctrl_handler); + /* Apply customized values from user under parameter hold */ + imx471_write_reg(imx471, IMX471_REG_PARAM_HOLD, 1, 1); + ret = __v4l2_ctrl_handler_setup(imx471->sd.ctrl_handler); + imx471_write_reg(imx471, IMX471_REG_PARAM_HOLD, 1, 0); if (ret) return ret;