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:
parent
39f0932050
commit
b6e10ff6c2
5 changed files with 75 additions and 0 deletions
|
@ -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)
|
||||||
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 @@
|
||||||
#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)
|
||||||
|
|
||||||
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 @@ out_unregister:
|
||||||
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);
|
||||||
|
|
||||||
|
|
14
drivers/media/v4l2-core/v4l2-subdev-priv.h
Normal file
14
drivers/media/v4l2-core/v4l2-subdev-priv.h
Normal 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
|
|
@ -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 @@
|
||||||
#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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
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,
|
||||||
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;
|
||||||
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 {
|
||||||
* @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 {
|
||||||
* 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
|
||||||
|
|
Loading…
Add table
Reference in a new issue