On Wed Sep 30 14:41:38 2026 +0800, Hermes Wu wrote:
> Store the negotiated media-bus format in the V4L2 subdev state instead
> of duplicating it in driver-private fields. This keeps ACTIVE and TRY
> formats under the framework's state management and keeps the active
> format synchronized with the configured DV timings.
> 
> Use the control handler's lock as the subdev state lock. Keep
> it6625_lock independent for MCU transactions.
> 
> Initialize every state from the current DV timings and default format
> index, use the core get_fmt implementation, and update the active format
> whenever the configured timings change.
> 
> Make ACTIVE set_fmt reject changes while streaming and commit the new
> format only after hardware programming succeeds. TRY changes remain
> state-only and do not program hardware.
> 
> Signed-off-by: Hermes Wu <[email protected]>
> Signed-off-by: Hans Verkuil <[email protected]>

Patch committed.

Thanks,
Hans Verkuil

 drivers/media/i2c/it6625.c | 234 +++++++++++++++++++++++++++------------------
 1 file changed, 142 insertions(+), 92 deletions(-)

---

diff --git a/drivers/media/i2c/it6625.c b/drivers/media/i2c/it6625.c
index 5f118d7fbb59..9900239b5474 100644
--- a/drivers/media/i2c/it6625.c
+++ b/drivers/media/i2c/it6625.c
@@ -255,7 +255,7 @@ struct it6625 {
        struct regmap *it6625_regmap;
        enum it6625_chip_type chip_type;
 
-       /* protects concurrent access to the chip's registers and state */
+       /* Serializes MCU transactions. */
        struct mutex it6625_lock;
        /* serializes the complete VIDIOC_S_EDID sequence against itself */
        struct mutex edid_lock;
@@ -291,8 +291,6 @@ struct it6625 {
        u8 csi_lanes;
        u8 port_num;
        enum v4l2_mbus_type bus_type;
-       u8 csi_format;
-       u32 mbus_fmt_code;
        /* number of EDID blocks currently loaded, protected by edid_lock */
        u8 edid_blocks;
 
@@ -921,10 +919,11 @@ static int it6625_v4l2_sd_ctrl_update(struct v4l2_subdev 
*sd)
        return it6625_s_ctrl_audio_present(sd);
 }
 
-static void it6625_enable_stream_locked(struct it6625 *it6625, bool enable)
+static int it6625_enable_stream_locked(struct it6625 *it6625, bool enable)
 {
        struct v4l2_subdev *sd = &it6625->sd;
        int val;
+       int err;
 
        lockdep_assert_held(&it6625->it6625_lock);
 
@@ -932,27 +931,34 @@ static void it6625_enable_stream_locked(struct it6625 
*it6625, bool enable)
                 __func__, enable ? "en" : "dis");
 
        val = enable ? B_MIPI_OUTPUT : 0;
-       it6625_set_bits(it6625, REG_MIPI_CONTROL, B_MIPI_OUTPUT, val);
-       it6625_update_config(it6625);
+       err = it6625_set_bits(it6625, REG_MIPI_CONTROL, B_MIPI_OUTPUT, val);
+       if (err < 0)
+               return err;
+
+       return it6625_update_config(it6625);
 }
 
-static void it6625_enable_stream(struct it6625 *it6625, bool enable)
+static int it6625_enable_stream(struct it6625 *it6625, bool enable)
 {
        guard(mutex)(&it6625->it6625_lock);
-       it6625_enable_stream_locked(it6625, enable);
+       return it6625_enable_stream_locked(it6625, enable);
 }
 
-static void it6625_set_mipi_config_locked(struct it6625 *it6625, u32 cfg_val)
+static int it6625_set_mipi_config_locked(struct it6625 *it6625, u32 cfg_val)
 {
        u8 mipi_data_type;
+       int err;
 
        lockdep_assert_held(&it6625->it6625_lock);
 
        dev_dbg(it6625->dev, "mipi_data_type = 0x%x", cfg_val);
 
        mipi_data_type = cfg_val & 0xFF;
-       it6625_write_byte(it6625, REG_MIPI_DATA_TYPE, mipi_data_type);
-       it6625_update_config(it6625);
+       err = it6625_write_byte(it6625, REG_MIPI_DATA_TYPE, mipi_data_type);
+       if (err < 0)
+               return err;
+
+       return it6625_update_config(it6625);
 }
 
 static inline unsigned int fps_from_bt_timings(const struct v4l2_bt_timings *t)
@@ -967,9 +973,20 @@ static inline unsigned int fps_from_bt_timings(const 
struct v4l2_bt_timings *t)
 
 static int it6625_initial_setup(struct it6625 *it6625)
 {
+       struct v4l2_subdev *sd = &it6625->sd;
+       struct v4l2_subdev_state *state;
+       struct v4l2_mbus_framefmt *fmt;
+       int idx;
        int val = 0;
        int err;
 
+       state = v4l2_subdev_lock_and_get_active_state(sd);
+       fmt = v4l2_subdev_state_get_format(state, 0);
+       idx = it6625_csi_mbus_code_idx(fmt->code);
+       if (idx < 0)
+               idx = 0;
+       v4l2_subdev_unlock_state(state);
+
        guard(mutex)(&it6625->it6625_lock);
 
        /*
@@ -998,7 +1015,8 @@ static int it6625_initial_setup(struct it6625 *it6625)
        if (err)
                return err;
 
-       err = it6625_write_byte(it6625, REG_MIPI_DATA_TYPE, it6625->csi_format);
+       err = it6625_write_byte(it6625, REG_MIPI_DATA_TYPE,
+                               it6625_formats[idx].csi_format);
        if (err)
                return err;
 
@@ -1166,10 +1184,32 @@ static void it6625_get_timings(struct it6625 *it6625,
        *timings = it6625->timings;
 }
 
+/*
+ * Project a DV-timings struct's width/height/field onto a pad format.
+ * Caller must hold it6625_lock and, separately, whichever subdev
+ * state fmt belongs to.
+ */
+static void it6625_fill_timings_format(const struct v4l2_dv_timings *timings,
+                                      struct v4l2_mbus_framefmt *fmt)
+{
+       fmt->width = timings->bt.width;
+       fmt->height = timings->bt.height;
+       fmt->field = timings->bt.interlaced == V4L2_DV_INTERLACED ?
+                    V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE;
+}
+
 static void it6625_clear_timings(struct it6625 *it6625)
 {
-       guard(mutex)(&it6625->it6625_lock);
-       memset(&it6625->timings, 0, sizeof(it6625->timings));
+       struct v4l2_subdev *sd = &it6625->sd;
+       struct v4l2_subdev_state *state = 
v4l2_subdev_lock_and_get_active_state(sd);
+       struct v4l2_mbus_framefmt *fmt = v4l2_subdev_state_get_format(state, 0);
+
+       scoped_guard(mutex, &it6625->it6625_lock) {
+               memset(&it6625->timings, 0, sizeof(it6625->timings));
+               it6625_fill_timings_format(&it6625->timings, fmt);
+       }
+
+       v4l2_subdev_unlock_state(state);
 }
 
 static void it6625_irq_hdmi_5v_change(struct it6625 *it6625)
@@ -1372,6 +1412,7 @@ static void it6625_polling_work(struct work_struct *work)
 static int it6625_log_status(struct v4l2_subdev *sd)
 {
        struct it6625 *it6625 = sd_to_6625(sd);
+       struct v4l2_subdev_state *state;
        struct v4l2_dv_timings timings, configured_timings;
        struct v4l2_bt_timings bt;
        u32 mbus_fmt_code;
@@ -1386,11 +1427,17 @@ static int it6625_log_status(struct v4l2_subdev *sd)
        v4l2_print_dv_timings(sd->name, "Configured format: ",
                              &configured_timings, true);
 
-       /* snapshot together so the reported pair was actually configured 
together */
+       /*
+        * VIDIOC_LOG_STATUS isn't core-locked, so take both locks
+        * ourselves; snapshot together so the reported pair was
+        * actually configured together.
+        */
+       state = v4l2_subdev_lock_and_get_active_state(sd);
        scoped_guard(mutex, &it6625->it6625_lock) {
-               mbus_fmt_code = it6625->mbus_fmt_code;
+               mbus_fmt_code = v4l2_subdev_state_get_format(state, 0)->code;
                bt = it6625->timings.bt;
        }
+       v4l2_subdev_unlock_state(state);
 
        v4l2_info(sd, "CSI format: %#x @ %uHz", mbus_fmt_code, 
fps_from_bt_timings(&bt));
 
@@ -1441,17 +1488,32 @@ static int
 it6625_update_timings_if_changed(struct it6625 *it6625,
                                 const struct v4l2_dv_timings *timings)
 {
-       guard(mutex)(&it6625->it6625_lock);
+       struct v4l2_subdev *sd = &it6625->sd;
+       struct v4l2_subdev_state *state;
+       struct v4l2_mbus_framefmt *fmt;
+       int ret;
 
-       if (v4l2_match_dv_timings(&it6625->timings, timings, 0, false))
-               return 0;
+       /* .s_dv_timings isn't core-locked, so take both locks ourselves */
+       state = v4l2_subdev_lock_and_get_active_state(sd);
+       fmt = v4l2_subdev_state_get_format(state, 0);
 
-       if (!v4l2_valid_dv_timings(timings, it6625_get_timings_cap(it6625), 
NULL, NULL))
-               return -ERANGE;
+       scoped_guard(mutex, &it6625->it6625_lock) {
+               if (v4l2_match_dv_timings(&it6625->timings, timings, 0, false)) 
{
+                       ret = 0;
+               } else if (!v4l2_valid_dv_timings(timings,
+                                                 
it6625_get_timings_cap(it6625),
+                                                 NULL, NULL)) {
+                       ret = -ERANGE;
+               } else {
+                       it6625->timings = *timings;
+                       it6625_fill_timings_format(&it6625->timings, fmt);
+                       ret = 1;
+               }
+       }
 
-       it6625->timings = *timings;
+       v4l2_subdev_unlock_state(state);
 
-       return 1;
+       return ret;
 }
 
 static int it6625_enum_dv_timings(struct v4l2_subdev *sd,
@@ -1483,8 +1545,7 @@ static int it6625_s_stream(struct v4l2_subdev *sd, int 
enable)
 {
        struct it6625 *it6625 = sd_to_6625(sd);
 
-       it6625_enable_stream(it6625, enable);
-       return 0;
+       return it6625_enable_stream(it6625, enable);
 }
 
 static int it6625_enum_mbus_code(struct v4l2_subdev *sd,
@@ -1612,85 +1673,59 @@ static inline u32 format_to_colorspace(u8 csi_format)
        }
 }
 
-static int it6625_get_fmt(struct v4l2_subdev *sd,
+static int it6625_set_fmt(struct v4l2_subdev *sd,
+                         const struct v4l2_subdev_client_info *ci,
                          struct v4l2_subdev_state *sd_state,
                          struct v4l2_subdev_format *format)
 {
        struct it6625 *it6625 = sd_to_6625(sd);
-       struct v4l2_dv_timings timings;
+       struct v4l2_mbus_framefmt *fmt;
+       u8 csi_format;
+       int idx;
+       int ret;
 
        if (format->pad != 0)
                return -EINVAL;
 
-       it6625_get_timings(it6625, &timings);
-       format->format.width = timings.bt.width;
-       format->format.height = timings.bt.height;
-       format->format.field = timings.bt.interlaced == V4L2_DV_INTERLACED ?
-                              V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE;
-
-       if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
-               struct v4l2_mbus_framefmt *fmt;
-
-               fmt = v4l2_subdev_state_get_format(sd_state, format->pad);
-               format->format.code = fmt->code;
-               format->format.colorspace = fmt->colorspace;
-       } else {
-               scoped_guard(mutex, &it6625->it6625_lock) {
-                       format->format.colorspace =
-                               format_to_colorspace(it6625->csi_format);
-                       format->format.code = it6625->mbus_fmt_code;
-               }
+       idx = it6625_csi_mbus_code_idx(format->format.code);
+       if (idx < 0) {
+               v4l2_dbg(1, debug, sd,
+                        "%s: unsupported format code 0x%x, falling back to 
default",
+                        __func__, format->format.code);
+               idx = 0;
        }
 
-       return 0;
-}
+       csi_format = it6625_formats[idx].csi_format;
 
-static int it6625_set_fmt(struct v4l2_subdev *sd,
-                         const struct v4l2_subdev_client_info *ci,
-                         struct v4l2_subdev_state *sd_state,
-                         struct v4l2_subdev_format *format)
-{
-       struct it6625 *it6625 = sd_to_6625(sd);
-       u32 mbus_fmt_code = format->format.code;
-       int ret;
+       /* fmt already carries width/height/field for this state; leave alone */
+       fmt = v4l2_subdev_state_get_format(sd_state, format->pad);
 
-       ret = it6625_get_fmt(sd, sd_state, format);
-       format->format.code = mbus_fmt_code;
+       if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+               if (v4l2_subdev_is_streaming(sd))
+                       return -EBUSY;
 
-       if (ret)
-               return ret;
+               guard(mutex)(&it6625->it6625_lock);
 
-       ret = it6625_csi_mbus_code_idx(mbus_fmt_code);
+               ret = it6625_enable_stream_locked(it6625, false);
+               if (ret)
+                       return ret;
 
-       if (ret < 0) {
-               v4l2_dbg(1, debug, sd,
-                        "%s: unsupported format code 0x%x, falling back to 
default",
-                        __func__, mbus_fmt_code);
-               ret = 0;
-               mbus_fmt_code = it6625_formats[ret].mbus_fmt_code;
-               format->format.code = mbus_fmt_code;
+               ret = it6625_set_mipi_config_locked(it6625, csi_format);
+               if (ret)
+                       return ret;
        }
 
-       if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
-               struct v4l2_mbus_framefmt *fmt;
+       /*
+        * TRY: commit unconditionally. ACTIVE: commit only after the
+        * hardware programming above actually succeeded.
+        */
+       fmt->code = it6625_formats[idx].mbus_fmt_code;
+       fmt->colorspace = format_to_colorspace(csi_format);
+       format->format = *fmt;
 
-               fmt = v4l2_subdev_state_get_format(sd_state, format->pad);
-               fmt->code = format->format.code;
-               fmt->colorspace = 
format_to_colorspace(it6625_formats[ret].csi_format);
-               format->format.colorspace = fmt->colorspace;
+       if (format->which == V4L2_SUBDEV_FORMAT_TRY)
                v4l2_dbg(1, debug, sd, "%s: try format code = 0x%x",
                         __func__, format->format.code);
-               return 0;
-       }
-
-       scoped_guard(mutex, &it6625->it6625_lock) {
-               it6625->csi_format = it6625_formats[ret].csi_format;
-               it6625->mbus_fmt_code = format->format.code;
-               it6625_enable_stream_locked(it6625, false);
-               it6625_set_mipi_config_locked(it6625, it6625->csi_format);
-       }
-
-       format->format.colorspace = 
format_to_colorspace(it6625_formats[ret].csi_format);
 
        return 0;
 }
@@ -1807,7 +1842,7 @@ static const struct v4l2_subdev_video_ops 
it6625_video_ops = {
 static const struct v4l2_subdev_pad_ops it6625_pad_ops = {
        .enum_mbus_code = it6625_enum_mbus_code,
        .set_fmt = it6625_set_fmt,
-       .get_fmt = it6625_get_fmt,
+       .get_fmt = v4l2_subdev_get_fmt,
        .get_edid = it6625_g_edid,
        .set_edid = it6625_s_edid,
        .enum_dv_timings = it6625_enum_dv_timings,
@@ -1827,8 +1862,12 @@ static const struct v4l2_subdev_ops it6625_ops = {
 static int it6625_init_state(struct v4l2_subdev *sd,
                             struct v4l2_subdev_state *sd_state)
 {
+       struct it6625 *it6625 = sd_to_6625(sd);
        struct v4l2_mbus_framefmt *fmt = v4l2_subdev_state_get_format(sd_state, 
0);
 
+       scoped_guard(mutex, &it6625->it6625_lock)
+               it6625_fill_timings_format(&it6625->timings, fmt);
+
        fmt->code = it6625_formats[0].mbus_fmt_code;
        fmt->colorspace = format_to_colorspace(it6625_formats[0].csi_format);
 
@@ -1869,6 +1908,7 @@ static int it6625_v4l2_init_controls(struct v4l2_subdev 
*sd)
                           it6625->csi_lanes == 3;
 
        v4l2_ctrl_handler_init(hdl, 4);
+
        it6625->ctrl_5v_detect =
                v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_DV_RX_POWER_PRESENT,
                                  0, 1, 0, 0);
@@ -2077,8 +2117,6 @@ static void it6625_init_data(struct it6625 *it6625)
        static struct v4l2_dv_timings default_timing =
                        V4L2_DV_BT_CEA_1920X1080P60;
 
-       it6625->csi_format = it6625_formats[0].csi_format;
-       it6625->mbus_fmt_code = it6625_formats[0].mbus_fmt_code;
        it6625->timings = default_timing;
        /* firmware ships with a verified 2-block default EDID in EDID RAM */
        it6625->edid_blocks = 2;
@@ -2270,9 +2308,16 @@ static int it6625_probe(struct i2c_client *client)
                goto err_clean_ctrl_handler;
        }
 
+       sd->state_lock = sd->ctrl_handler->lock;
+       err = v4l2_subdev_init_finalize(sd);
+       if (err) {
+               dev_err(it6625->dev, "%s %d err=%d", __func__, __LINE__, err);
+               goto err_clean_hdl;
+       }
+
        err = v4l2_ctrl_handler_setup(sd->ctrl_handler);
        if (err)
-               goto err_clean_hdl;
+               goto err_clean_state;
 
        it6625->cec_adap = cec_allocate_adapter(&it6625_cec_adap_ops,
                                                it6625, dev_name(it6625->dev),
@@ -2283,7 +2328,7 @@ static int it6625_probe(struct i2c_client *client)
        if (IS_ERR(it6625->cec_adap)) {
                err = PTR_ERR(it6625->cec_adap);
                dev_err(it6625->dev, "%s %d", __func__, __LINE__);
-               goto err_clean_hdl;
+               goto err_clean_state;
        }
 
        err = cec_register_adapter(it6625->cec_adap, &client->dev);
@@ -2291,7 +2336,7 @@ static int it6625_probe(struct i2c_client *client)
                dev_err(it6625->dev, "%s: failed to register the cec device", 
__func__);
                cec_delete_adapter(it6625->cec_adap);
                it6625->cec_adap = NULL;
-               goto err_clean_hdl;
+               goto err_clean_state;
        }
 
        it6625_debugfs_init(it6625, client);
@@ -2325,6 +2370,8 @@ err_clean_debugfs:
        v4l2_debugfs_if_free(it6625->infoframes);
        debugfs_remove_recursive(it6625->debugfs_dir);
        cec_unregister_adapter(it6625->cec_adap);
+err_clean_state:
+       v4l2_subdev_cleanup(sd);
 err_clean_hdl:
        media_entity_cleanup(&sd->entity);
 err_clean_ctrl_handler:
@@ -2362,12 +2409,15 @@ static void it6625_remove(struct i2c_client *client)
 
        debugfs_remove_recursive(it6625->debugfs_dir);
        cec_unregister_adapter(it6625->cec_adap);
+
+       v4l2_subdev_cleanup(sd);
+       media_entity_cleanup(&sd->entity);
+       v4l2_ctrl_handler_free(&it6625->hdl);
+
        mutex_destroy(&it6625->it6625_lock);
        mutex_destroy(&it6625->edid_lock);
        mutex_destroy(&it6625->if_read_lock);
        mutex_destroy(&it6625->if_state_lock);
-       media_entity_cleanup(&sd->entity);
-       v4l2_ctrl_handler_free(&it6625->hdl);
 }
 
 static const struct i2c_device_id it6625_id[] = {
_______________________________________________
linuxtv-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to