media: v4l2-core: Make the v4l2-core code enable/disable the privacy LED if present
authorHans de Goede <hdegoede@redhat.com>
Fri, 27 Jan 2023 20:37:25 +0000 (21:37 +0100)
committerHans de Goede <hdegoede@redhat.com>
Fri, 3 Feb 2023 09:22:34 +0000 (10:22 +0100)
Make v4l2_async_register_subdev_sensor() try to get a privacy LED
associated with the sensor and extend the call_s_stream() wrapper to
enable/disable the privacy LED if found.

This makes the core handle privacy LED control, rather then having to
duplicate this code in all the sensor drivers.

Suggested-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Link: https://lore.kernel.org/r/20230127203729.10205-2-hdegoede@redhat.com
drivers/media/v4l2-core/v4l2-async.c
drivers/media/v4l2-core/v4l2-fwnode.c
drivers/media/v4l2-core/v4l2-subdev-priv.h [new file with mode: 0644]
drivers/media/v4l2-core/v4l2-subdev.c
include/media/v4l2-subdev.h

index 2f1b718..d7e9ffc 100644 (file)
@@ -24,6 +24,8 @@
 #include <media/v4l2-fwnode.h>
 #include <media/v4l2-subdev.h>
 
+#include "v4l2-subdev-priv.h"
+
 static int v4l2_async_nf_call_bound(struct v4l2_async_notifier *n,
                                    struct v4l2_subdev *subdev,
                                    struct v4l2_async_subdev *asd)
@@ -822,6 +824,8 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
        if (!sd->async_list.next)
                return;
 
+       v4l2_subdev_put_privacy_led(sd);
+
        mutex_lock(&list_lock);
 
        __v4l2_async_nf_unregister(sd->subdev_notifier);
index 3d9533c..049c2f2 100644 (file)
@@ -28,6 +28,8 @@
 #include <media/v4l2-fwnode.h>
 #include <media/v4l2-subdev.h>
 
+#include "v4l2-subdev-priv.h"
+
 static const struct v4l2_fwnode_bus_conv {
        enum v4l2_fwnode_bus_type fwnode_bus_type;
        enum v4l2_mbus_type mbus_type;
@@ -1302,6 +1304,10 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
 
        v4l2_async_nf_init(notifier);
 
+       ret = v4l2_subdev_get_privacy_led(sd);
+       if (ret < 0)
+               goto out_cleanup;
+
        ret = v4l2_async_nf_parse_fwnode_sensor(sd->dev, notifier);
        if (ret < 0)
                goto out_cleanup;
@@ -1322,6 +1328,7 @@ out_unregister:
        v4l2_async_nf_unregister(notifier);
 
 out_cleanup:
+       v4l2_subdev_put_privacy_led(sd);
        v4l2_async_nf_cleanup(notifier);
        kfree(notifier);
 
diff --git a/drivers/media/v4l2-core/v4l2-subdev-priv.h b/drivers/media/v4l2-core/v4l2-subdev-priv.h
new file mode 100644 (file)
index 0000000..52391d6
--- /dev/null
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * V4L2 sub-device pivate header.
+ *
+ * Copyright (C) 2023 Hans de Goede <hdegoede@redhat.com>
+ */
+
+#ifndef _V4L2_SUBDEV_PRIV_H_
+#define _V4L2_SUBDEV_PRIV_H_
+
+int v4l2_subdev_get_privacy_led(struct v4l2_subdev *sd);
+void v4l2_subdev_put_privacy_led(struct v4l2_subdev *sd);
+
+#endif
index 4988a25..6630fb3 100644 (file)
@@ -9,6 +9,7 @@
  */
 
 #include <linux/ioctl.h>
+#include <linux/leds.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -23,6 +24,8 @@
 #include <media/v4l2-fh.h>
 #include <media/v4l2-event.h>
 
+#include "v4l2-subdev-priv.h"
+
 #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
 static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd)
 {
@@ -322,6 +325,15 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable)
 {
        int ret;
 
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+       if (!IS_ERR_OR_NULL(sd->privacy_led)) {
+               if (enable)
+                       led_set_brightness(sd->privacy_led,
+                                          sd->privacy_led->max_brightness);
+               else
+                       led_set_brightness(sd->privacy_led, 0);
+       }
+#endif
        ret = sd->ops->video->s_stream(sd, enable);
 
        if (!enable && ret < 0) {
@@ -1090,6 +1102,7 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
        sd->grp_id = 0;
        sd->dev_priv = NULL;
        sd->host_priv = NULL;
+       sd->privacy_led = NULL;
 #if defined(CONFIG_MEDIA_CONTROLLER)
        sd->entity.name = sd->name;
        sd->entity.obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV;
@@ -1105,3 +1118,36 @@ void v4l2_subdev_notify_event(struct v4l2_subdev *sd,
        v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT, (void *)ev);
 }
 EXPORT_SYMBOL_GPL(v4l2_subdev_notify_event);
+
+int v4l2_subdev_get_privacy_led(struct v4l2_subdev *sd)
+{
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+       sd->privacy_led = led_get(sd->dev, "privacy-led");
+       if (IS_ERR(sd->privacy_led) && PTR_ERR(sd->privacy_led) != -ENOENT)
+               return dev_err_probe(sd->dev, PTR_ERR(sd->privacy_led),
+                                    "getting privacy LED\n");
+
+       if (!IS_ERR_OR_NULL(sd->privacy_led)) {
+               mutex_lock(&sd->privacy_led->led_access);
+               led_sysfs_disable(sd->privacy_led);
+               led_trigger_remove(sd->privacy_led);
+               led_set_brightness(sd->privacy_led, 0);
+               mutex_unlock(&sd->privacy_led->led_access);
+       }
+#endif
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_get_privacy_led);
+
+void v4l2_subdev_put_privacy_led(struct v4l2_subdev *sd)
+{
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+       if (!IS_ERR_OR_NULL(sd->privacy_led)) {
+               mutex_lock(&sd->privacy_led->led_access);
+               led_sysfs_enable(sd->privacy_led);
+               mutex_unlock(&sd->privacy_led->led_access);
+               led_put(sd->privacy_led);
+       }
+#endif
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_put_privacy_led);
index b15fa99..f3fe9b6 100644 (file)
@@ -38,6 +38,7 @@ struct v4l2_subdev;
 struct v4l2_subdev_fh;
 struct tuner_setup;
 struct v4l2_mbus_frame_desc;
+struct led_classdev;
 
 /**
  * struct v4l2_decode_vbi_line - used to decode_vbi_line
@@ -941,6 +942,7 @@ struct v4l2_subdev_platform_data {
  * @state_lock: A pointer to a lock used for all the subdev's states, set by the
  *             driver. This is optional. If NULL, each state instance will get
  *             a lock of its own.
+ * @privacy_led: Optional pointer to a LED classdev for the privacy LED for sensors.
  * @active_state: Active state for the subdev (NULL for subdevs tracking the
  *               state internally). Initialized by calling
  *               v4l2_subdev_init_finalize().
@@ -982,6 +984,8 @@ struct v4l2_subdev {
         * appropriate functions.
         */
 
+       struct led_classdev *privacy_led;
+
        /*
         * TODO: active_state should most likely be changed from a pointer to an
         * embedded field. For the time being it's kept as a pointer to more