From: Niklas Söderlund niklas.soderlund+renesas@ragnatech.se
[ Upstream commit 46c1e7814d1c3310ef23c01ed1a582ef0c8ab1d2 ]
The .set_fmt callback should not write the new format directly do the device, it should only store it and have it applied by .s_stream.
The .s_stream callback already calls adv7180_set_field_mode() so it's safe to remove programming of the device and just store the format and have .s_stream apply it.
Signed-off-by: Niklas Söderlund niklas.soderlund+renesas@ragnatech.se Reviewed-by: Laurent Pinchart laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Hans Verkuil hverkuil+cisco@kernel.org Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
YES
- Fixes incorrect behavior: The change enforces the V4L2 subdev rule that .set_fmt should not program hardware but only update state, with hardware programming deferred to .s_stream. In the new code, .set_fmt (driver’s `adv7180_set_pad_format`) only stores the requested field and does not touch the device, eliminating unintended runtime side effects during format negotiation. See `drivers/media/i2c/adv7180.c:784-793` where it now only assigns `state->field` for ACTIVE formats and no longer toggles power or programs registers.
- Safe because .s_stream already applies the format: The .s_stream path powers the device off, configures it, then powers it on, and explicitly applies the field mode. `adv7180_s_stream` calls `init_device` when enabling, which calls `adv7180_set_field_mode`. See: - `drivers/media/i2c/adv7180.c:927-955` (.s_stream powers off, calls `init_device`, powers on), - `drivers/media/i2c/adv7180.c:844-859` (`init_device` calls `adv7180_program_std` then `adv7180_set_field_mode` at `drivers/media/i2c/adv7180.c:854`). This guarantees the stored `state->field` is applied at the correct time.
- Eliminates disruptive side effects: Previously, .set_fmt would power- cycle the decoder and program field mode immediately (via `adv7180_set_power(state, false)`, `adv7180_set_field_mode(state)`, `adv7180_set_power(state, true)`), which could: - Disrupt ongoing or prepared streaming sessions by unexpectedly toggling power during format negotiation. - Violate the expected V4L2 subdev semantics where format negotiation should be side-effect-free. The new behavior prevents these issues by staging only the state change and deferring hardware ops to .s_stream.
- Consistency and correctness: The stored field influences both negotiated format reporting and hardware setup when streaming starts: - Format reporting uses `state->field` to compute height and field (see `drivers/media/i2c/adv7180.c:700-709` and `759-761`). - When powering on with CSI-2, `adv7180_set_power` also considers `state->field` (e.g., sets CSI register 0x1D if progressive output is used), ensuring consistent device programming once the stream starts (see `drivers/media/i2c/adv7180.c:202-219`).
- Contained change, minimal risk: - Single-file, localized change to adv7180 driver. - No new features or ABI changes; aligns with established subdev patterns. - No architectural shifts; only removes premature device programming from .set_fmt. - The driver already centralizes device programming within the streaming lifecycle, so behavior remains correct while avoiding premature side effects.
- Stable suitability: - Addresses a real behavioral bug (programming hardware during .set_fmt) that can lead to video glitches or races during negotiation. - Small, targeted patch that reduces side effects and adheres to V4L2 best practices. - Low regression risk provided the target stable trees have the same pattern where .s_stream calls into `init_device` → `adv7180_set_field_mode` (as in this tree). In this codebase, that condition is satisfied.
Given these points, this is a good candidate for backporting to stable media trees.
drivers/media/i2c/adv7180.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-)
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 84600fa75ae8a..8100fe6b0f1d4 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -812,14 +812,7 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd, ret = adv7180_mbus_fmt(sd, &format->format);
if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - if (state->field != format->format.field) { - guard(mutex)(&state->mutex); - - state->field = format->format.field; - adv7180_set_power(state, false); - adv7180_set_field_mode(state); - adv7180_set_power(state, true); - } + state->field = format->format.field; } else { framefmt = v4l2_subdev_state_get_format(sd_state, 0); *framefmt = format->format;