Re: [PATCH RFC 5/5] media: imx219: Add status polling using .detect()

From: Dave Stevenson

Date: Thu Oct 01 2026 - 12:19:39 EST


Hi Mattij

On Thu, 1 Oct 2026 at 13:55, Mattijs Korpershoek
<mkorpershoek@xxxxxxxxxx> wrote:
>
> Userspace needs to be notified when a sensor connection status
> changes (e.g. disconnected at boot, then later reconnected) so it can
> react accordingly.
>
> Add periodic polling using a delayed work that calls .detect() every
> 2s and sends a KOBJ_CHANGE uevent with HOTPLUG=1 on status changes.
> This mirrors the approach used by DRM connectors in output_poll_execute().

AIUI DRM polls from within the framework (drm_probe_helper.c), not by
a workqueue in the individual drivers.

Admittedly V4L2 doesn't currently have a totally obvious place to
setup this, but it would be far less effort to have the polling
framework within the core code rather than driver.
Possibly initialised in __v4l2_async_register_subdev_sensor() based on
whether .detect is set, and cleaned up in
v4l2_async_unregister_subdev, with the workqueue calling .detect and
generating the udev event based on the return value? I think that's
feasible.

Dave

> Signed-off-by: Mattijs Korpershoek <mkorpershoek@xxxxxxxxxx>
> ---
> drivers/media/i2c/imx219.c | 40 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 40 insertions(+)
>
> diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c
> index e198d3fe99c6..76e578a8eab5 100644
> --- a/drivers/media/i2c/imx219.c
> +++ b/drivers/media/i2c/imx219.c
> @@ -20,6 +20,7 @@
> #include <linux/i2c.h>
> #include <linux/minmax.h>
> #include <linux/module.h>
> +#include <linux/workqueue.h>
> #include <linux/pm_runtime.h>
> #include <linux/regulator/consumer.h>
>
> @@ -374,6 +375,9 @@ struct imx219 {
>
> /* Two or Four lanes */
> u8 lanes;
> +
> + struct delayed_work detect_work;
> + enum v4l2_subdev_connected_status_whence detect_status;
> };
>
> static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd)
> @@ -1252,6 +1256,35 @@ static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219)
> return ret;
> }
>
> +#define IMX219_DETECT_INTERVAL_MS 2000
> +static void imx219_detect_work(struct work_struct *work)
> +{
> + struct imx219 *imx219 = container_of(work, struct imx219,
> + detect_work.work);
> + struct v4l2_subdev_connected_status status = {};
> +
> + /*
> + * All async notifiers should have been run before
> + * we can use sd.devnode
> + */
> + if (!imx219->sd.devnode)
> + goto reschedule_detect_work;
> +
> + imx219_detect(&imx219->sd, &status);
> +
> + if (status.status != imx219->detect_status) {
> + struct device *dev = &imx219->sd.devnode->dev;
> + char *envp[] = { "HOTPLUG=1", NULL };
> +
> + imx219->detect_status = status.status;
> + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
> + }
> +
> +reschedule_detect_work:
> + schedule_delayed_work(&imx219->detect_work,
> + msecs_to_jiffies(IMX219_DETECT_INTERVAL_MS));
> +}
> +
> static int imx219_probe(struct i2c_client *client)
> {
> struct device *dev = &client->dev;
> @@ -1334,6 +1367,11 @@ static int imx219_probe(struct i2c_client *client)
> pm_runtime_set_autosuspend_delay(dev, 1000);
> pm_runtime_use_autosuspend(dev);
>
> + imx219->detect_status = V4L2_SUBDEV_STATUS_UNKNOWN;
> + INIT_DELAYED_WORK(&imx219->detect_work, imx219_detect_work);
> + schedule_delayed_work(&imx219->detect_work,
> + msecs_to_jiffies(IMX219_DETECT_INTERVAL_MS));
> +
> return 0;
>
> error_subdev_cleanup:
> @@ -1355,6 +1393,8 @@ static void imx219_remove(struct i2c_client *client)
> struct v4l2_subdev *sd = i2c_get_clientdata(client);
> struct imx219 *imx219 = to_imx219(sd);
>
> + cancel_delayed_work_sync(&imx219->detect_work);
> +
> v4l2_async_unregister_subdev(sd);
> v4l2_subdev_cleanup(sd);
> media_entity_cleanup(&sd->entity);
>
> --
> 2.55.0
>