From patchwork Mon May 6 09:33:47 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrzej Hajda X-Patchwork-Id: 18303 Received: from mail.tu-berlin.de ([130.149.7.33]) by www.linuxtv.org with esmtp (Exim 4.72) (envelope-from ) id 1UZHof-0002an-F3; Mon, 06 May 2013 11:34:41 +0200 X-tubIT-Incoming-IP: 209.132.180.67 Received: from vger.kernel.org ([209.132.180.67]) by mail.tu-berlin.de (exim-4.72/mailfrontend-8) with esmtp id 1UZHoc-0007nC-lq; Mon, 06 May 2013 11:34:41 +0200 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753330Ab3EFJeh (ORCPT + 1 other); Mon, 6 May 2013 05:34:37 -0400 Received: from mailout2.w1.samsung.com ([210.118.77.12]:8662 "EHLO mailout2.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752835Ab3EFJeg (ORCPT ); Mon, 6 May 2013 05:34:36 -0400 Received: from eucpsbgm2.samsung.com (unknown [203.254.199.245]) by mailout2.w1.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0MMD00DADEBKQY80@mailout2.w1.samsung.com>; Mon, 06 May 2013 10:34:34 +0100 (BST) X-AuditID: cbfec7f5-b7fa36d000007f20-de-5187792a8fd8 Received: from eusync2.samsung.com ( [203.254.199.212]) by eucpsbgm2.samsung.com (EUCPMTA) with SMTP id E6.09.32544.A2977815; Mon, 06 May 2013 10:34:34 +0100 (BST) Received: from AMDC1061.digital.local ([106.116.147.88]) by eusync2.samsung.com (Oracle Communications Messaging Server 7u4-23.01 (7.0.4.23.0) 64bit (built Aug 10 2011)) with ESMTPA id <0MMD00DHYEL47T90@eusync2.samsung.com>; Mon, 06 May 2013 10:34:33 +0100 (BST) From: Andrzej Hajda To: linux-media@vger.kernel.org, linux-leds@vger.kernel.org, devicetree-discuss@lists.ozlabs.org Cc: Laurent Pinchart , Sylwester Nawrocki , Sakari Ailus , Kyungmin Park , hj210.choi@samsung.com, sw0312.kim@samsung.com, Bryan Wu , Richard Purdie , Andrzej Hajda Subject: [PATCH RFC 1/2] v4l2-leddev: added LED class support for V4L2 flash subdevices Date: Mon, 06 May 2013 11:33:47 +0200 Message-id: <1367832828-30771-2-git-send-email-a.hajda@samsung.com> X-Mailer: git-send-email 1.7.10.4 In-reply-to: <1367832828-30771-1-git-send-email-a.hajda@samsung.com> References: <1367832828-30771-1-git-send-email-a.hajda@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFprJLMWRmVeSWpSXmKPExsVy+t/xK7pale2BBi1bmCxurTvHanF050Qm iwOzH7JaPN54jdnibNMbdovOiUvYLba+Wcdo0bNhK6vF7l1PWS0Ov2lntTizfyWbxYzJL9kc eDx2zrrL7jG7Yyarx+GvC1k8zs9YyOixZ/4PVo++LasYPT5vkgtgj+KySUnNySxLLdK3S+DK 2Pb6AXvBybCKa6efsTQwfnTrYuTkkBAwkZj0cjU7hC0mceHeerYuRi4OIYGljBIb9l1jhHD6 mCSm/ehhAqliE9CU+Lv5JhuILSKQJHHmew9YB7PAFSaJdw8+gI0SFoiUmNnQygJiswioSqw9 0MkMYvMKOEt82rqdDWKdokT3swlANgcHp4CLxK8f1SBhIaCSCwv2sU5g5F3AyLCKUTS1NLmg OCk910ivODG3uDQvXS85P3cTIyQ4v+5gXHrM6hCjAAejEg+vxKO2QCHWxLLiytxDjBIczEoi vD57gUK8KYmVValF+fFFpTmpxYcYmTg4pRoY5Q5dNzto+vqfb8jlZZWVO/+vWJoSeGHSRr/5 kqe27TUq3OX46OiCafVsln6Xluae2H30L8eMtZYRquKTD5j8eGwStfnXx52O19e4nJ07r2mq yr/YJN/71w/alIkzf9zx7Z50/uyJBlVLVF6dWda146tk+TWbaU3p15O/yfyV1oi7zbtRKiLt brgSS3FGoqEWc1FxIgDbz8YrLAIAAA== Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-PMX-Version: 6.0.0.2142326, Antispam-Engine: 2.7.2.2107409, Antispam-Data: 2013.5.6.92422 X-PMX-Spam: Gauge=IIIIIIIII, Probability=9%, Report=' HTML_NO_HTTP 0.1, MULTIPLE_RCPTS 0.1, HTML_00_10 0.05, BODY_SIZE_10000_PLUS 0, __ANY_URI 0, __HAS_FROM 0, __HAS_HTML 0, __HAS_MSGID 0, __HAS_X_MAILER 0, __HAS_X_MAILING_LIST 0, __IN_REP_TO 0, __MIME_TEXT_ONLY 0, __MULTIPLE_RCPTS_CC_X2 0, __SANE_MSGID 0, __SUBJ_ALPHA_END 0, __TO_MALFORMED_2 0, __TO_NO_NAME 0, __URI_NO_MAILTO 0, __URI_NO_PATH 0, __URI_NO_WWW 0, __URI_NS ' This patch adds helper functions for exposing V4L2 flash subdevice as LED class device. For such device there will be created appropriate entry in /class/leds/ directory with standard LED class attributes and attributes corresponding to V4L2 flash controls exposed by the subdevice. Signed-off-by: Andrzej Hajda Signed-off-by: Kyungmin Park --- 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 + * + * 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 +#include +#include +#include +#include + +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 "); +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 + * + * 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 +#include +#include + +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 */