This patch adds helper functions for exposing V4L2 flash
subdevice as LED class device. For such device there will be
created appropriate entry in <sysfs>/class/leds/ directory with
standard LED class attributes and attributes corresponding
to V4L2 flash controls exposed by the subdevice.

Signed-off-by: Andrzej Hajda <a.ha...@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.p...@samsung.com>
---
 drivers/media/v4l2-core/Kconfig       |    7 +
 drivers/media/v4l2-core/Makefile      |    1 +
 drivers/media/v4l2-core/v4l2-leddev.c |  269 +++++++++++++++++++++++++++++++++
 include/media/v4l2-leddev.h           |   49 ++++++
 4 files changed, 326 insertions(+)
 create mode 100644 drivers/media/v4l2-core/v4l2-leddev.c
 create mode 100644 include/media/v4l2-leddev.h

diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index 8c05565..c32b7f2 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -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
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index aa50c46..2906c7c 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -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
diff --git a/drivers/media/v4l2-core/v4l2-leddev.c 
b/drivers/media/v4l2-core/v4l2-leddev.c
new file mode 100644
index 0000000..f41885e
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-leddev.c
@@ -0,0 +1,269 @@
+/*
+ * V4L2 API for exposing flash subdevs as LED class devices
+ *
+ * Copyright (C) 2013, Samsung Electronics Co., Ltd.
+ * Andrzej Hajda <a.ha...@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.ha...@samsung.com>");
+MODULE_DESCRIPTION("V4L2 API for exposing flash subdevs as LED class devices");
+MODULE_LICENSE("GPL");
diff --git a/include/media/v4l2-leddev.h b/include/media/v4l2-leddev.h
new file mode 100644
index 0000000..384b71f
--- /dev/null
+++ b/include/media/v4l2-leddev.h
@@ -0,0 +1,49 @@
+/*
+ * V4L2 API for exposing flash subdevs as led class devices
+ *
+ * Copyright (C) 2013, Samsung Electronics Co., Ltd.
+ * Andrzej Hajda <a.ha...@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 */
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to