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: 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
This commit is contained in:
Hans de Goede 2023-01-27 21:37:25 +01:00
parent 39f0932050
commit b6e10ff6c2
5 changed files with 75 additions and 0 deletions

View 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);

View 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);

View File

@ -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

View 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);

View 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