@@ -35,6 +35,13 @@ config V4L2_MEM2MEM_DEV
tristate
depends on VIDEOBUF2_CORE
+# Used by drivers that need v4l2-leddev.ko
+config V4L2_LEDDEV
+ tristate "LED support for V4L2 flash subdevices"
+ depends on VIDEO_V4L2 && LEDS_CLASS
+ ---help---
+ This option enables LED class support for V4L2 flash devices.
+
# Used by drivers that need Videobuf modules
config VIDEOBUF_GEN
tristate
@@ -20,6 +20,7 @@ obj-$(CONFIG_VIDEO_V4L2) += v4l2-common.o
obj-$(CONFIG_VIDEO_TUNER) += tuner.o
obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
+obj-$(CONFIG_V4L2_LEDDEV) += v4l2-leddev.o
obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
new file mode 100644
@@ -0,0 +1,269 @@
+/*
+ * V4L2 API for exposing flash subdevs as LED class devices
+ *
+ * Copyright (C) 2013, Samsung Electronics Co., Ltd.
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/sysfs.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-leddev.h>
+
+struct v4l2_leddev_attr {
+ u32 ctrl_id;
+ u8 name[32];
+ struct device_attribute dattr;
+};
+
+struct v4l2_leddev_attr *to_v4l2_leddev_attr(struct device_attribute *da)
+{
+ return container_of(da, struct v4l2_leddev_attr, dattr);
+}
+
+struct v4l2_leddev *to_v4l2_leddev(struct led_classdev *cdev)
+{
+ return container_of(cdev, struct v4l2_leddev, cdev);
+}
+
+static void v4l2_leddev_brightness_set(struct led_classdev *cdev,
+ enum led_brightness value)
+{
+ struct v4l2_ext_control ctrls[2] = {
+ {
+ .id = V4L2_CID_FLASH_LED_MODE,
+ .value = value ? V4L2_FLASH_LED_MODE_TORCH
+ : V4L2_FLASH_LED_MODE_NONE,
+ },
+ {
+ .id = V4L2_CID_FLASH_TORCH_INTENSITY,
+ .value = value
+ }
+
+ };
+ struct v4l2_ext_controls ext_ctrls = {
+ .ctrl_class = V4L2_CTRL_CLASS_FLASH,
+ .controls = ctrls,
+ .count = value ? 2 : 1,
+ };
+ struct v4l2_leddev *ld = to_v4l2_leddev(cdev);
+
+ v4l2_subdev_s_ext_ctrls(ld->sd, &ext_ctrls);
+}
+
+static enum led_brightness v4l2_leddev_brightness_get(struct led_classdev *cdev)
+{
+ struct v4l2_ext_control ctrls[2] = {
+ {
+ .id = V4L2_CID_FLASH_LED_MODE,
+ },
+ {
+ .id = V4L2_CID_FLASH_TORCH_INTENSITY,
+ },
+
+ };
+ struct v4l2_ext_controls ext_ctrls = {
+ .ctrl_class = V4L2_CTRL_CLASS_FLASH,
+ .controls = ctrls,
+ .count = 2,
+ };
+ struct v4l2_leddev *ld = to_v4l2_leddev(cdev);
+ int ret;
+
+ ret = v4l2_subdev_g_ext_ctrls(ld->sd, &ext_ctrls);
+
+ if (ret || ctrls[0].value != V4L2_FLASH_LED_MODE_TORCH)
+ return 0;
+
+ return ctrls[1].value;
+}
+
+static ssize_t v4l2_leddev_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *cdev = dev_get_drvdata(dev);
+ struct v4l2_leddev *ld = to_v4l2_leddev(cdev);
+ struct v4l2_leddev_attr *ldattr = to_v4l2_leddev_attr(attr);
+ struct v4l2_ext_control ctrl = {
+ .id = ldattr->ctrl_id,
+ };
+ struct v4l2_ext_controls ext_ctrls = {
+ .ctrl_class = V4L2_CTRL_CLASS_FLASH,
+ .controls = &ctrl,
+ .count = 1,
+ };
+ int ret;
+
+ ret = v4l2_subdev_g_ext_ctrls(ld->sd, &ext_ctrls);
+ if (ret)
+ return 0;
+
+ return sprintf(buf, "%d\n", ctrl.value);
+}
+
+static ssize_t v4l2_leddev_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct led_classdev *cdev = dev_get_drvdata(dev);
+ struct v4l2_leddev *ld = to_v4l2_leddev(cdev);
+ struct v4l2_leddev_attr *ldattr = to_v4l2_leddev_attr(attr);
+ struct v4l2_ext_control ctrl = {
+ .id = ldattr->ctrl_id,
+ };
+ struct v4l2_ext_controls ext_ctrls = {
+ .ctrl_class = V4L2_CTRL_CLASS_FLASH,
+ .controls = &ctrl,
+ .count = 1,
+ };
+ int ret;
+
+ ret = kstrtos32(buf, 0, &ctrl.value);
+ if (!ret)
+ ret = v4l2_subdev_s_ext_ctrls(ld->sd, &ext_ctrls);
+
+ return ret ? 0 : size;
+}
+
+static int v4l2_leddev_ctrls_count(struct v4l2_leddev *ld)
+{
+ struct v4l2_queryctrl qc = {
+ .id = V4L2_CID_FLASH_CLASS | V4L2_CTRL_FLAG_NEXT_CTRL,
+ };
+ int n = 0;
+
+ while (v4l2_queryctrl(ld->sd->ctrl_handler, &qc) == 0) {
+ if (V4L2_CTRL_ID2CLASS (qc.id) != V4L2_CTRL_CLASS_FLASH)
+ break;
+ ++n;
+ qc.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
+ }
+ return n;
+}
+
+static int v4l2_leddev_create_attrs(struct v4l2_leddev *ld)
+{
+ struct v4l2_queryctrl qc = {
+ .id = V4L2_CID_FLASH_CLASS | V4L2_CTRL_FLAG_NEXT_CTRL,
+ };
+ int i = 0;
+ int ret;
+
+ ld->attr_count = v4l2_leddev_ctrls_count(ld);
+ ld->attrs = kzalloc(sizeof(*ld->attrs) * ld->attr_count, GFP_KERNEL);
+ if (!ld->attrs)
+ return -ENOMEM;
+
+ while (v4l2_queryctrl(ld->sd->ctrl_handler, &qc) == 0) {
+ struct v4l2_leddev_attr *attr = &ld->attrs[i];
+ if (V4L2_CTRL_ID2CLASS (qc.id) != V4L2_CTRL_CLASS_FLASH)
+ break;
+
+ attr->ctrl_id = qc.id;
+ strncpy(attr->name, qc.name, sizeof(attr->name));
+ attr->dattr.attr.name = attr->name;
+ if (!(qc.flags & V4L2_CTRL_FLAG_READ_ONLY)) {
+ attr->dattr.store = v4l2_leddev_store;
+ attr->dattr.attr.mode |= 0220;
+ }
+ if (!(qc.flags & V4L2_CTRL_FLAG_WRITE_ONLY)) {
+ attr->dattr.show = v4l2_leddev_show;
+ attr->dattr.attr.mode |= 0444;
+ }
+ v4l2_info(ld->sd, "creating attr %s(%d/%d)\n", attr->name, i,
+ ld->attr_count);
+ ret = device_create_file(ld->cdev.dev, &attr->dattr);
+ if (!ret) {
+ if (++i == ld->attr_count)
+ break;
+ } else {
+ v4l2_err(ld->sd, "error creating attr %s (%d)\n",
+ attr->name, ret);
+ }
+
+ qc.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
+ }
+ ld->attr_count = i;
+ return 0;
+}
+
+static void v4l2_leddev_remove_attrs(struct v4l2_leddev *ld)
+{
+ int i;
+
+ for (i = ld->attr_count - 1; i >= 0; --i)
+ device_remove_file(ld->cdev.dev, &ld->attrs[i].dattr);
+
+ kfree(ld->attrs);
+ ld->attrs = NULL;
+ ld->attr_count = 0;
+}
+
+static int v4l2_leddev_get_max_brightness(struct v4l2_leddev *ld)
+{
+ struct v4l2_queryctrl qc = {
+ .id = V4L2_CID_FLASH_TORCH_INTENSITY,
+ };
+ int ret;
+
+ ret = v4l2_queryctrl(ld->sd->ctrl_handler, &qc);
+ if (ret) {
+ v4l2_err(ld->sd, "cannot query torch intensity (%d)\n", ret);
+ return 0;
+ }
+ return qc.maximum;
+}
+
+int v4l2_leddev_register(struct device *dev, struct v4l2_leddev *ld)
+{
+ int ret;
+
+ if (!ld->sd) {
+ dev_err(dev, "cannot register leddev without subdev provided\n");
+ return -EINVAL;
+ }
+ if (!ld->cdev.name)
+ ld->cdev.name = ld->sd->name;
+ if (!ld->cdev.max_brightness)
+ ld->cdev.max_brightness = v4l2_leddev_get_max_brightness(ld);
+ if (!ld->cdev.brightness_set)
+ ld->cdev.brightness_set = v4l2_leddev_brightness_set;
+ if (!ld->cdev.brightness_get)
+ ld->cdev.brightness_get = v4l2_leddev_brightness_get;
+
+ ret = led_classdev_register(dev, &ld->cdev);
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_leddev_create_attrs(ld);
+ if (ret < 0)
+ led_classdev_unregister(&ld->cdev);
+
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_leddev_register);
+
+void v4l2_leddev_unregister(struct v4l2_leddev *ld)
+{
+ v4l2_leddev_remove_attrs(ld);
+ led_classdev_unregister(&ld->cdev);
+}
+EXPORT_SYMBOL(v4l2_leddev_unregister);
+
+MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
+MODULE_DESCRIPTION("V4L2 API for exposing flash subdevs as LED class devices");
+MODULE_LICENSE("GPL");
new file mode 100644
@@ -0,0 +1,49 @@
+/*
+ * V4L2 API for exposing flash subdevs as led class devices
+ *
+ * Copyright (C) 2013, Samsung Electronics Co., Ltd.
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _V4L2_LEDDEV_H
+#define _V4L2_LEDDEV_H
+
+#include <linux/device.h>
+#include <linux/leds.h>
+#include <media/v4l2-subdev.h>
+
+struct v4l2_leddev_attr;
+
+struct v4l2_leddev {
+ struct v4l2_subdev *sd;
+ struct led_classdev cdev;
+ int attr_count;
+ struct v4l2_leddev_attr *attrs;
+};
+
+#ifdef CONFIG_V4L2_LEDDEV
+
+int v4l2_leddev_register(struct device *dev, struct v4l2_leddev *ld);
+void v4l2_leddev_unregister(struct v4l2_leddev *ld);
+
+#else
+
+#define v4l2_leddev_register(dev, ld) (0)
+#define v4l2_leddev_unregister(ld) (void)
+
+#endif
+
+#endif /* _V4L2_LEDDEV_H */