From 11fcd7554ae39e66019b030f73cadb97cabe1a6f Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Mon, 20 Apr 2026 16:37:00 +0000 Subject: [PATCH 1/2] media: i2c: imx500: Keep the device active while network firmware is loaded Hold a pm_runtime reference for the lifetime of imx500->fw_network. The reference is taken in imx500_set_ctrl() before kernel_read_file_from_fd() and released in imx500_clear_fw_network() on vfree. This prevents the autosuspend from racing the read (kernel oops in __memcpy) and from silently dropping the rpk before stream-on. Signed-off-by: Naushir Patuck --- drivers/media/i2c/imx500.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/imx500.c b/drivers/media/i2c/imx500.c index c5a0e1d1491bb6..5226160bb0b6bf 100644 --- a/drivers/media/i2c/imx500.c +++ b/drivers/media/i2c/imx500.c @@ -1924,9 +1924,17 @@ static int imx500_clear_weights(struct imx500 *imx500) static void imx500_clear_fw_network(struct imx500 *imx500) { + struct i2c_client *client = v4l2_get_subdevdata(&imx500->sd); + /* Remove any previous firmware blob. */ - if (imx500->fw_network) + if (imx500->fw_network) { vfree(imx500->fw_network); + /* + * Release the PM ref taken when the fw was loaded in + * imx500_set_ctrl(). + */ + pm_runtime_put_noidle(&client->dev); + } imx500->fw_network = NULL; imx500->network_written = false; @@ -2138,6 +2146,13 @@ static int imx500_set_ctrl(struct v4l2_ctrl *ctrl) } imx500_clear_fw_network(imx500); + + /* + * Take a pm_runtime reference before the read. This keeps + * the device active as long as a NW firmware has been loaded. + */ + pm_runtime_resume_and_get(&client->dev); + ret = kernel_read_file_from_fd(ctrl->val, 0, (void **)&imx500->fw_network, INT_MAX, &imx500->fw_network_size, @@ -2151,7 +2166,8 @@ static int imx500_set_ctrl(struct v4l2_ctrl *ctrl) if (ret < 0) { dev_err(&client->dev, "%s failed to read fw image: %d\n", __func__, ret); - imx500_clear_fw_network(imx500); + /* Release the ref we took above. */ + pm_runtime_put_noidle(&client->dev); return ret; } if (ret != imx500->fw_network_size) { From 0ea340d93bd8aa7bbb42df1ae120ee7a7abebcc6 Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Tue, 21 Apr 2026 10:10:00 +0000 Subject: [PATCH 2/2] media: i2c: imx500: release rpk on subdev close The pm_runtime reference is held for as long as imx500->fw_network is populated. This means the cached rpk (plus the pm reference that blocks autosuspend) is present in the device state past userspace closing the subdev fd, until the next VIDIOC_S_CTRL cleared or replaced it. Add a .close internal op that calls imx500_clear_fw_network() when the last userspace handle on the subdev is released. Signed-off-by: Naushir Patuck --- drivers/media/i2c/imx500.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/media/i2c/imx500.c b/drivers/media/i2c/imx500.c index 5226160bb0b6bf..5fc593340e9588 100644 --- a/drivers/media/i2c/imx500.c +++ b/drivers/media/i2c/imx500.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -2331,6 +2332,26 @@ static const struct v4l2_ctrl_ops imx500_ctrl_ops = { .s_ctrl = imx500_set_ctrl, }; +static int imx500_subdev_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct imx500 *imx500 = to_imx500(sd); + /* + * Release the cached rpk when the last userspace handle on the subdev + * is closed. This also drops the pm_runtime reference ta taken in the + * NETWORK_FW_FD branch of imx500_set_ctrl(), so the device can + * autosuspend between sessions. + */ + if (v4l2_fh_is_singular(&fh->vfh)) + imx500_clear_fw_network(imx500); + + return 0; +} + +static const struct v4l2_subdev_internal_ops imx500_internal_ops = { + .close = imx500_subdev_close, +}; + static const struct v4l2_ctrl_config imx500_notify_gains_ctrl = { .ops = &imx500_ctrl_ops, .id = V4L2_CID_NOTIFY_GAINS, @@ -3632,6 +3653,7 @@ static int imx500_probe(struct i2c_client *client) goto error_power_off; /* Initialize subdev */ + imx500->sd.internal_ops = &imx500_internal_ops; imx500->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; imx500->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;