@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
uvcvideo-objs := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \
- uvc_status.o uvc_isight.o uvc_debugfs.o uvc_metadata.o
+ uvc_status.o uvc_isight.o uvc_debugfs.o uvc_metadata.o \
+ uvc_gpio.o
ifeq ($(CONFIG_MEDIA_CONTROLLER),y)
uvcvideo-objs += uvc_entity.o
endif
@@ -8,7 +8,6 @@
#include <linux/atomic.h>
#include <linux/bits.h>
-#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
@@ -775,9 +774,9 @@ static const u8 uvc_media_transport_input_guid[16] =
UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT;
static const u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING;
-static struct uvc_entity *uvc_alloc_new_entity(struct uvc_device *dev, u16 type,
- u16 id, unsigned int num_pads,
- unsigned int extra_size)
+struct uvc_entity *uvc_alloc_new_entity(struct uvc_device *dev, u16 type,
+ u16 id, unsigned int num_pads,
+ unsigned int extra_size)
{
struct uvc_entity *entity;
unsigned int num_inputs;
@@ -1240,118 +1239,6 @@ static int uvc_parse_control(struct uvc_device *dev)
return 0;
}
-/* -----------------------------------------------------------------------------
- * Privacy GPIO
- */
-
-static void uvc_gpio_event(struct uvc_device *dev)
-{
- struct uvc_entity *unit = dev->gpio_unit;
- struct uvc_video_chain *chain;
- u8 new_val;
-
- if (!unit)
- return;
-
- new_val = gpiod_get_value_cansleep(unit->gpio.gpio_privacy);
-
- /* GPIO entities are always on the first chain. */
- chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
- uvc_ctrl_status_event(chain, unit->controls, &new_val);
-}
-
-static int uvc_gpio_get_cur(struct uvc_device *dev, struct uvc_entity *entity,
- u8 cs, void *data, u16 size)
-{
- if (cs != UVC_CT_PRIVACY_CONTROL || size < 1)
- return -EINVAL;
-
- *(u8 *)data = gpiod_get_value_cansleep(entity->gpio.gpio_privacy);
-
- return 0;
-}
-
-static int uvc_gpio_get_info(struct uvc_device *dev, struct uvc_entity *entity,
- u8 cs, u8 *caps)
-{
- if (cs != UVC_CT_PRIVACY_CONTROL)
- return -EINVAL;
-
- *caps = UVC_CONTROL_CAP_GET | UVC_CONTROL_CAP_AUTOUPDATE;
- return 0;
-}
-
-static irqreturn_t uvc_gpio_irq(int irq, void *data)
-{
- struct uvc_device *dev = data;
-
- uvc_gpio_event(dev);
- return IRQ_HANDLED;
-}
-
-static int uvc_gpio_parse(struct uvc_device *dev)
-{
- struct uvc_entity *unit;
- struct gpio_desc *gpio_privacy;
- int irq;
-
- gpio_privacy = devm_gpiod_get_optional(&dev->intf->dev, "privacy",
- GPIOD_IN);
- if (IS_ERR_OR_NULL(gpio_privacy))
- return PTR_ERR_OR_ZERO(gpio_privacy);
-
- irq = gpiod_to_irq(gpio_privacy);
- if (irq < 0)
- return dev_err_probe(&dev->intf->dev, irq,
- "No IRQ for privacy GPIO\n");
-
- unit = uvc_alloc_new_entity(dev, UVC_EXT_GPIO_UNIT,
- UVC_EXT_GPIO_UNIT_ID, 0, 1);
- if (IS_ERR(unit))
- return PTR_ERR(unit);
-
- unit->gpio.gpio_privacy = gpio_privacy;
- unit->gpio.irq = irq;
- unit->gpio.bControlSize = 1;
- unit->gpio.bmControls = (u8 *)unit + sizeof(*unit);
- unit->gpio.bmControls[0] = 1;
- unit->get_cur = uvc_gpio_get_cur;
- unit->get_info = uvc_gpio_get_info;
- strscpy(unit->name, "GPIO", sizeof(unit->name));
-
- list_add_tail(&unit->list, &dev->entities);
-
- dev->gpio_unit = unit;
-
- return 0;
-}
-
-static int uvc_gpio_init_irq(struct uvc_device *dev)
-{
- struct uvc_entity *unit = dev->gpio_unit;
- int ret;
-
- if (!unit || unit->gpio.irq < 0)
- return 0;
-
- ret = request_threaded_irq(unit->gpio.irq, NULL, uvc_gpio_irq,
- IRQF_ONESHOT | IRQF_TRIGGER_FALLING |
- IRQF_TRIGGER_RISING,
- "uvc_privacy_gpio", dev);
-
- unit->gpio.initialized = !ret;
-
- return ret;
-}
-
-static void uvc_gpio_deinit(struct uvc_device *dev)
-{
- if (!dev->gpio_unit || !dev->gpio_unit->gpio.initialized)
- return;
-
- free_irq(dev->gpio_unit->gpio.irq, dev);
-}
-
/* ------------------------------------------------------------------------
* UVC device scan
*/
new file mode 100644
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * uvc_gpio.c -- USB Video Class driver
+ *
+ * Copyright 2024 Google LLC
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio/consumer.h>
+#include "uvcvideo.h"
+
+static void uvc_gpio_event(struct uvc_device *dev)
+{
+ struct uvc_entity *unit = dev->gpio_unit;
+ struct uvc_video_chain *chain;
+ u8 new_val;
+
+ if (!unit)
+ return;
+
+ new_val = gpiod_get_value_cansleep(unit->gpio.gpio_privacy);
+
+ /* GPIO entities are always on the first chain. */
+ chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
+ uvc_ctrl_status_event(chain, unit->controls, &new_val);
+}
+
+static int uvc_gpio_get_cur(struct uvc_device *dev, struct uvc_entity *entity,
+ u8 cs, void *data, u16 size)
+{
+ if (cs != UVC_CT_PRIVACY_CONTROL || size < 1)
+ return -EINVAL;
+
+ *(u8 *)data = gpiod_get_value_cansleep(entity->gpio.gpio_privacy);
+
+ return 0;
+}
+
+static int uvc_gpio_get_info(struct uvc_device *dev, struct uvc_entity *entity,
+ u8 cs, u8 *caps)
+{
+ if (cs != UVC_CT_PRIVACY_CONTROL)
+ return -EINVAL;
+
+ *caps = UVC_CONTROL_CAP_GET | UVC_CONTROL_CAP_AUTOUPDATE;
+ return 0;
+}
+
+static irqreturn_t uvc_gpio_irq(int irq, void *data)
+{
+ struct uvc_device *dev = data;
+
+ uvc_gpio_event(dev);
+ return IRQ_HANDLED;
+}
+
+int uvc_gpio_parse(struct uvc_device *dev)
+{
+ struct uvc_entity *unit;
+ struct gpio_desc *gpio_privacy;
+ int irq;
+
+ gpio_privacy = devm_gpiod_get_optional(&dev->intf->dev, "privacy",
+ GPIOD_IN);
+ if (IS_ERR_OR_NULL(gpio_privacy))
+ return PTR_ERR_OR_ZERO(gpio_privacy);
+
+ irq = gpiod_to_irq(gpio_privacy);
+ if (irq < 0)
+ return dev_err_probe(&dev->udev->dev, irq,
+ "No IRQ for privacy GPIO\n");
+
+ unit = uvc_alloc_new_entity(dev, UVC_EXT_GPIO_UNIT,
+ UVC_EXT_GPIO_UNIT_ID, 0, 1);
+ if (IS_ERR(unit))
+ return PTR_ERR(unit);
+
+ unit->gpio.gpio_privacy = gpio_privacy;
+ unit->gpio.irq = irq;
+ unit->gpio.bControlSize = 1;
+ unit->gpio.bmControls = (u8 *)unit + sizeof(*unit);
+ unit->gpio.bmControls[0] = 1;
+ unit->get_cur = uvc_gpio_get_cur;
+ unit->get_info = uvc_gpio_get_info;
+ strscpy(unit->name, "GPIO", sizeof(unit->name));
+
+ list_add_tail(&unit->list, &dev->entities);
+
+ dev->gpio_unit = unit;
+
+ return 0;
+}
+
+int uvc_gpio_init_irq(struct uvc_device *dev)
+{
+ struct uvc_entity *unit = dev->gpio_unit;
+ int ret;
+
+ if (!unit || unit->gpio.irq < 0)
+ return 0;
+
+ ret = request_threaded_irq(unit->gpio.irq, NULL, uvc_gpio_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_FALLING |
+ IRQF_TRIGGER_RISING,
+ "uvc_privacy_gpio", dev);
+
+ unit->gpio.initialized = !ret;
+
+ return ret;
+}
+
+void uvc_gpio_deinit(struct uvc_device *dev)
+{
+ if (!dev->gpio_unit || !dev->gpio_unit->gpio.initialized)
+ return;
+
+ free_irq(dev->gpio_unit->gpio.irq, dev);
+}
@@ -673,6 +673,9 @@ do { \
extern struct uvc_driver uvc_driver;
struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id);
+struct uvc_entity *uvc_alloc_new_entity(struct uvc_device *dev, u16 type,
+ u16 id, unsigned int num_pads,
+ unsigned int extra_size);
/* Video buffers queue management. */
int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
@@ -817,4 +820,9 @@ void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream);
size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf,
size_t size);
+/* gpio */
+int uvc_gpio_parse(struct uvc_device *dev);
+int uvc_gpio_init_irq(struct uvc_device *dev);
+void uvc_gpio_deinit(struct uvc_device *dev);
+
#endif
This is just a refactor patch, no new functionality is added. Signed-off-by: Ricardo Ribalda <ribalda@chromium.org> --- drivers/media/usb/uvc/Makefile | 3 +- drivers/media/usb/uvc/uvc_driver.c | 119 +------------------------------------ drivers/media/usb/uvc/uvc_gpio.c | 118 ++++++++++++++++++++++++++++++++++++ drivers/media/usb/uvc/uvcvideo.h | 8 +++ 4 files changed, 131 insertions(+), 117 deletions(-)