Commit b6e10ff6 authored by Hans de Goede's avatar Hans de Goede

media: v4l2-core: Make the v4l2-core code enable/disable the privacy LED if present

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: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Acked-by: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Link: https://lore.kernel.org/r/20230127203729.10205-2-hdegoede@redhat.com
parent 39f09320
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
#include <media/v4l2-fwnode.h> #include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h> #include <media/v4l2-subdev.h>
#include "v4l2-subdev-priv.h"
static int v4l2_async_nf_call_bound(struct v4l2_async_notifier *n, static int v4l2_async_nf_call_bound(struct v4l2_async_notifier *n,
struct v4l2_subdev *subdev, struct v4l2_subdev *subdev,
struct v4l2_async_subdev *asd) struct v4l2_async_subdev *asd)
...@@ -822,6 +824,8 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) ...@@ -822,6 +824,8 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
if (!sd->async_list.next) if (!sd->async_list.next)
return; return;
v4l2_subdev_put_privacy_led(sd);
mutex_lock(&list_lock); mutex_lock(&list_lock);
__v4l2_async_nf_unregister(sd->subdev_notifier); __v4l2_async_nf_unregister(sd->subdev_notifier);
......
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
#include <media/v4l2-fwnode.h> #include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h> #include <media/v4l2-subdev.h>
#include "v4l2-subdev-priv.h"
static const struct v4l2_fwnode_bus_conv { static const struct v4l2_fwnode_bus_conv {
enum v4l2_fwnode_bus_type fwnode_bus_type; enum v4l2_fwnode_bus_type fwnode_bus_type;
enum v4l2_mbus_type mbus_type; enum v4l2_mbus_type mbus_type;
...@@ -1302,6 +1304,10 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd) ...@@ -1302,6 +1304,10 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
v4l2_async_nf_init(notifier); 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); ret = v4l2_async_nf_parse_fwnode_sensor(sd->dev, notifier);
if (ret < 0) if (ret < 0)
goto out_cleanup; goto out_cleanup;
...@@ -1322,6 +1328,7 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd) ...@@ -1322,6 +1328,7 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
v4l2_async_nf_unregister(notifier); v4l2_async_nf_unregister(notifier);
out_cleanup: out_cleanup:
v4l2_subdev_put_privacy_led(sd);
v4l2_async_nf_cleanup(notifier); v4l2_async_nf_cleanup(notifier);
kfree(notifier); kfree(notifier);
......
/* 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
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
*/ */
#include <linux/ioctl.h> #include <linux/ioctl.h>
#include <linux/leds.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -23,6 +24,8 @@ ...@@ -23,6 +24,8 @@
#include <media/v4l2-fh.h> #include <media/v4l2-fh.h>
#include <media/v4l2-event.h> #include <media/v4l2-event.h>
#include "v4l2-subdev-priv.h"
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) 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) ...@@ -322,6 +325,15 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable)
{ {
int ret; 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); ret = sd->ops->video->s_stream(sd, enable);
if (!enable && ret < 0) { if (!enable && ret < 0) {
...@@ -1090,6 +1102,7 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) ...@@ -1090,6 +1102,7 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
sd->grp_id = 0; sd->grp_id = 0;
sd->dev_priv = NULL; sd->dev_priv = NULL;
sd->host_priv = NULL; sd->host_priv = NULL;
sd->privacy_led = NULL;
#if defined(CONFIG_MEDIA_CONTROLLER) #if defined(CONFIG_MEDIA_CONTROLLER)
sd->entity.name = sd->name; sd->entity.name = sd->name;
sd->entity.obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV; sd->entity.obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV;
...@@ -1105,3 +1118,36 @@ void v4l2_subdev_notify_event(struct v4l2_subdev *sd, ...@@ -1105,3 +1118,36 @@ void v4l2_subdev_notify_event(struct v4l2_subdev *sd,
v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT, (void *)ev); v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT, (void *)ev);
} }
EXPORT_SYMBOL_GPL(v4l2_subdev_notify_event); 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);
...@@ -38,6 +38,7 @@ struct v4l2_subdev; ...@@ -38,6 +38,7 @@ struct v4l2_subdev;
struct v4l2_subdev_fh; struct v4l2_subdev_fh;
struct tuner_setup; struct tuner_setup;
struct v4l2_mbus_frame_desc; struct v4l2_mbus_frame_desc;
struct led_classdev;
/** /**
* struct v4l2_decode_vbi_line - used to decode_vbi_line * struct v4l2_decode_vbi_line - used to decode_vbi_line
...@@ -941,6 +942,7 @@ struct v4l2_subdev_platform_data { ...@@ -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 * @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 * driver. This is optional. If NULL, each state instance will get
* a lock of its own. * 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 * @active_state: Active state for the subdev (NULL for subdevs tracking the
* state internally). Initialized by calling * state internally). Initialized by calling
* v4l2_subdev_init_finalize(). * v4l2_subdev_init_finalize().
...@@ -982,6 +984,8 @@ struct v4l2_subdev { ...@@ -982,6 +984,8 @@ struct v4l2_subdev {
* appropriate functions. * appropriate functions.
*/ */
struct led_classdev *privacy_led;
/* /*
* TODO: active_state should most likely be changed from a pointer to an * 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 * embedded field. For the time being it's kept as a pointer to more
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment