This patch set is a follow-up of the LED / flash API integration
series [1].
========================
Changes since version 7:
========================
- removed explicit support for indicator leds from
LED Flash class - indicator leds will be registered
as a separate LED Flash class devices
- added flash_sync_strobe sysfs attribute and related
V4L2_CID_FLASH_SYNC_STROBE control
- changed the way of matching V4L2 Flash sub-devices
in a media device, which entailed modification in
v4l2-async driver
- modified max77693 DT bindings documentation
- applied various fixes an cleanups
========================
Changes since version 6:
========================
- removed addition of public LED subsystem API for setting
torch brightness in favour of internal API for
synchronous and asynchronous led brightness level setting
- fixed possible race condition upon creating LED Flash class
related sysfs attributes
========================
Changes since version 5:
========================
- removed flash manager framework - its implementation needs
further thorough discussion.
- removed external strobe facilities from the LED Flash Class
and provided external_strobe_set op in v4l2-flash. LED subsystem
should be strobe provider agnostic.
Thanks,
Jacek Anaszewski
[1] https://lkml.org/lkml/2014/7/11/914
Jacek Anaszewski (14):
leds: Add LED Flash class extension to the LED subsystem
Documentation: leds: Add description of LED Flash class extension
Documentation: leds: Add description of v4l2-flash sub-device
v4l2-async: change custom.match callback argument type
v4l2-ctrls: Add V4L2_CID_FLASH_SYNC_STROBE control
media: Add registration helpers for V4L2 flash sub-devices
exynos4-is: Add support for v4l2-flash subdevs
DT: Add documentation for exynos4-is 'flashes' property
mfd: max77693: adjust max77693_led_platform_data
leds: Add support for max77693 mfd flash cell
DT: Add documentation for the mfd Maxim max77693
leds: Add driver for AAT1290 current regulator
of: Add Skyworks Solutions, Inc. vendor prefix
DT: Add documentation for the Skyworks AAT1290
Documentation/DocBook/media/v4l/controls.xml | 9 +
.../devicetree/bindings/leds/leds-aat1290.txt | 17 +
.../devicetree/bindings/media/samsung-fimc.txt | 7 +
Documentation/devicetree/bindings/mfd/max77693.txt | 74 ++
.../devicetree/bindings/vendor-prefixes.txt | 1 +
Documentation/leds/leds-class-flash.txt | 61 ++
drivers/leds/Kconfig | 27 +
drivers/leds/Makefile | 3 +
drivers/leds/led-class-flash.c | 446 ++++++++
drivers/leds/led-class.c | 4 +
drivers/leds/leds-aat1290.c | 472 ++++++++
drivers/leds/leds-max77693.c | 1152 ++++++++++++++++++++
drivers/media/platform/exynos4-is/media-dev.c | 65 +-
drivers/media/platform/exynos4-is/media-dev.h | 13 +-
drivers/media/v4l2-core/Kconfig | 11 +
drivers/media/v4l2-core/Makefile | 2 +
drivers/media/v4l2-core/v4l2-async.c | 16 +-
drivers/media/v4l2-core/v4l2-ctrls.c | 2 +
drivers/media/v4l2-core/v4l2-flash.c | 516 +++++++++
include/linux/led-class-flash.h | 198 ++++
include/linux/leds.h | 3 +
include/linux/mfd/max77693.h | 3 +-
include/media/v4l2-async.h | 2 +-
include/media/v4l2-flash.h | 138 +++
include/uapi/linux/v4l2-controls.h | 1 +
25 files changed, 3230 insertions(+), 13 deletions(-)
create mode 100644 Documentation/devicetree/bindings/leds/leds-aat1290.txt
create mode 100644 Documentation/leds/leds-class-flash.txt
create mode 100644 drivers/leds/led-class-flash.c
create mode 100644 drivers/leds/leds-aat1290.c
create mode 100644 drivers/leds/leds-max77693.c
create mode 100644 drivers/media/v4l2-core/v4l2-flash.c
create mode 100644 include/linux/led-class-flash.h
create mode 100644 include/media/v4l2-flash.h
--
1.7.9.5
The documentation being added contains overall description of the
LED Flash Class and the related sysfs attributes.
Signed-off-by: Jacek Anaszewski <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Bryan Wu <[email protected]>
Cc: Richard Purdie <[email protected]>
---
Documentation/leds/leds-class-flash.txt | 48 +++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
create mode 100644 Documentation/leds/leds-class-flash.txt
diff --git a/Documentation/leds/leds-class-flash.txt b/Documentation/leds/leds-class-flash.txt
new file mode 100644
index 0000000..d68565c
--- /dev/null
+++ b/Documentation/leds/leds-class-flash.txt
@@ -0,0 +1,48 @@
+
+Flash LED handling under Linux
+==============================
+
+Some LED devices support two modes - torch and flash. The modes are
+supported by the LED class (see Documentation/leds/leds-class.txt)
+and LED Flash class respectively.
+
+In order to enable support for flash LEDs CONFIG_LEDS_CLASS_FLASH symbol
+must be defined in the kernel config. A flash LED driver must register
+in the LED subsystem with led_classdev_flash_register to gain flash
+capabilities.
+
+Following sysfs attributes are exposed for controlling flash led devices:
+
+ - flash_brightness - flash LED brightness in microamperes (RW)
+ - max_flash_brightness - maximum available flash LED brightness (RO)
+ - indicator_brightness - privacy LED brightness in microamperes (RW)
+ - max_indicator_brightness - maximum privacy LED brightness in
+ microamperes (RO)
+ - flash_timeout - flash strobe duration in microseconds (RW)
+ - max_flash_timeout - maximum available flash strobe duration (RO)
+ - flash_strobe - flash strobe state (RW)
+ - flash_sync_strobe - one flash device can control more than one
+ sub-led; when this atrribute is set to 1
+ the flash led will be strobed synchronously
+ with the other ones controlled by the same
+ device (RW)
+ - flash_fault - bitmask of flash faults that may have occurred,
+ possible flags are:
+ * 0x01 - flash controller voltage to the flash LED has exceeded
+ the limit specific to the flash controller
+ * 0x02 - the flash strobe was still on when the timeout set by
+ the user has expired; not all flash controllers may
+ set this in all such conditions
+ * 0x04 - the flash controller has overheated
+ * 0x08 - the short circuit protection of the flash controller
+ has been triggered
+ * 0x10 - current in the LED power supply has exceeded the limit
+ specific to the flash controller
+ * 0x40 - flash controller voltage to the flash LED has been
+ below the minimum limit specific to the flash
+ * 0x80 - the input voltage of the flash controller is below
+ the limit under which strobing the flash at full
+ current will not be possible. The condition persists
+ until this flag is no longer set
+ * 0x100 - the temperature of the LED has exceeded its allowed
+ upper limit
--
1.7.9.5
It is useful to have an access to the async sub-device
being matched, not only to the related struct device.
Change match callback argument from struct device
to struct v4l2_subdev. It will allow e.g. for matching
a sub-device by its "name" property.
Signed-off-by: Jacek Anaszewski <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Guennadi Liakhovetski <[email protected]>
Cc: Laurent Pinchart <[email protected]>
Cc: Hans Verkuil <[email protected]>
---
drivers/media/v4l2-core/v4l2-async.c | 16 ++++++++--------
include/media/v4l2-async.h | 2 +-
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 85a6a34..8140992 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -22,10 +22,10 @@
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
-static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd)
+static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
{
#if IS_ENABLED(CONFIG_I2C)
- struct i2c_client *client = i2c_verify_client(dev);
+ struct i2c_client *client = i2c_verify_client(sd->dev);
return client &&
asd->match.i2c.adapter_id == client->adapter->nr &&
asd->match.i2c.address == client->addr;
@@ -34,14 +34,14 @@ static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd)
#endif
}
-static bool match_devname(struct device *dev, struct v4l2_async_subdev *asd)
+static bool match_devname(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
{
- return !strcmp(asd->match.device_name.name, dev_name(dev));
+ return !strcmp(asd->match.device_name.name, dev_name(sd->dev));
}
-static bool match_of(struct device *dev, struct v4l2_async_subdev *asd)
+static bool match_of(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
{
- return dev->of_node == asd->match.of.node;
+ return sd->dev->of_node == asd->match.of.node;
}
static LIST_HEAD(subdev_list);
@@ -52,7 +52,7 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *
struct v4l2_subdev *sd)
{
struct v4l2_async_subdev *asd;
- bool (*match)(struct device *, struct v4l2_async_subdev *);
+ bool (*match)(struct v4l2_subdev *, struct v4l2_async_subdev *);
list_for_each_entry(asd, ¬ifier->waiting, list) {
/* bus_type has been verified valid before */
@@ -79,7 +79,7 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *
}
/* match cannot be NULL here */
- if (match(sd->dev, asd))
+ if (match(sd, asd))
return asd;
}
diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
index 7683569..1c0b586 100644
--- a/include/media/v4l2-async.h
+++ b/include/media/v4l2-async.h
@@ -51,7 +51,7 @@ struct v4l2_async_subdev {
unsigned short address;
} i2c;
struct {
- bool (*match)(struct device *,
+ bool (*match)(struct v4l2_subdev *,
struct v4l2_async_subdev *);
void *priv;
} custom;
--
1.7.9.5
Add V4L2_CID_FLASH_SYNC_STROBE control for determining
whether a flash device strobe has to be synchronized
with other flash leds controller by the same device.
Signed-off-by: Jacek Anaszewski <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Sakari Ailus <[email protected]>
Cc: Hans Verkuil <[email protected]>
---
Documentation/DocBook/media/v4l/controls.xml | 9 +++++++++
drivers/media/v4l2-core/v4l2-ctrls.c | 2 ++
include/uapi/linux/v4l2-controls.h | 1 +
3 files changed, 12 insertions(+)
diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml
index e013e4b..35e9ea3 100644
--- a/Documentation/DocBook/media/v4l/controls.xml
+++ b/Documentation/DocBook/media/v4l/controls.xml
@@ -4563,6 +4563,15 @@ interface and may change in the future.</para>
after strobe during which another strobe will not be
possible. This is a read-only control.</entry>
</row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_FLASH_SYNC_STROBE</constant></entry>
+ <entry>boolean</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Synchronized strobe: whether the flash
+ should be strobed synchronously with the other ones controlled
+ by the same device</entry>
+ </row>
<row><entry></entry></row>
</tbody>
</tgroup>
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 45c5b47..a7cca8c 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -846,6 +846,7 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_FLASH_FAULT: return "Faults";
case V4L2_CID_FLASH_CHARGE: return "Charge";
case V4L2_CID_FLASH_READY: return "Ready to Strobe";
+ case V4L2_CID_FLASH_SYNC_STROBE: return "Synchronize Strobe";
/* JPEG encoder controls */
/* Keep the order of the 'case's the same as in v4l2-controls.h! */
@@ -949,6 +950,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_FLASH_STROBE_STATUS:
case V4L2_CID_FLASH_CHARGE:
case V4L2_CID_FLASH_READY:
+ case V4L2_CID_FLASH_SYNC_STROBE:
case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:
case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 661f119..5bce13d 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -833,6 +833,7 @@ enum v4l2_flash_strobe_source {
#define V4L2_CID_FLASH_CHARGE (V4L2_CID_FLASH_CLASS_BASE + 11)
#define V4L2_CID_FLASH_READY (V4L2_CID_FLASH_CLASS_BASE + 12)
+#define V4L2_CID_FLASH_SYNC_STROBE (V4L2_CID_FLASH_CLASS_BASE + 13)
/* JPEG-class control IDs */
--
1.7.9.5
This patch adds helper functions for registering/unregistering
LED Flash class devices as V4L2 sub-devices. The functions should
be called from the LED subsystem device driver. In case the
support for V4L2 Flash sub-devices is disabled in the kernel
config the functions' empty versions will be used.
Signed-off-by: Jacek Anaszewski <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Sakari Ailus <[email protected]>
Cc: Hans Verkuil <[email protected]>
---
drivers/media/v4l2-core/Kconfig | 11 +
drivers/media/v4l2-core/Makefile | 2 +
drivers/media/v4l2-core/v4l2-flash.c | 516 ++++++++++++++++++++++++++++++++++
include/media/v4l2-flash.h | 138 +++++++++
4 files changed, 667 insertions(+)
create mode 100644 drivers/media/v4l2-core/v4l2-flash.c
create mode 100644 include/media/v4l2-flash.h
diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index ba7e21a..f034f1a 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -44,6 +44,17 @@ config V4L2_MEM2MEM_DEV
tristate
depends on VIDEOBUF2_CORE
+# Used by LED subsystem flash drivers
+config V4L2_FLASH_LED_CLASS
+ tristate "Enable support for Flash sub-devices"
+ depends on VIDEO_V4L2_SUBDEV_API
+ depends on LEDS_CLASS_FLASH
+ ---help---
+ Say Y here to enable support for Flash sub-devices, which allow
+ to control LED class devices with use of V4L2 Flash controls.
+
+ When in doubt, say N.
+
# 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 63d29f2..44e858c 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -22,6 +22,8 @@ obj-$(CONFIG_VIDEO_TUNER) += tuner.o
obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
+obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash.o
+
obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
diff --git a/drivers/media/v4l2-core/v4l2-flash.c b/drivers/media/v4l2-core/v4l2-flash.c
new file mode 100644
index 0000000..f5075b0
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-flash.c
@@ -0,0 +1,516 @@
+/*
+ * V4L2 Flash LED sub-device registration helpers.
+ *
+ * Copyright (C) 2014 Samsung Electronics Co., Ltd
+ * Author: Jacek Anaszewski <[email protected]>
+ *
+ * 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."
+ */
+
+#include <linux/led-class-flash.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <media/v4l2-flash.h>
+
+#define has_flash_op(v4l2_flash, op) \
+ (v4l2_flash && v4l2_flash->ops->op)
+
+#define call_flash_op(v4l2_flash, op, args...) \
+ (has_flash_op(v4l2_flash, op) ? \
+ v4l2_flash->ops->op(args) : \
+ -EINVAL)
+
+static inline enum led_brightness v4l2_flash_intensity_to_led_brightness(
+ struct v4l2_ctrl *ctrl,
+ s32 intensity)
+{
+ s64 __intensity = intensity - ctrl->minimum;
+
+ do_div(__intensity, ctrl->step);
+
+ return __intensity + 1;
+}
+
+static inline s32 v4l2_flash_led_brightness_to_intensity(
+ struct v4l2_ctrl *ctrl,
+ enum led_brightness brightness)
+{
+ return ((brightness - 1) * ctrl->step) + ctrl->minimum;
+}
+
+static int v4l2_flash_g_volatile_ctrl(struct v4l2_ctrl *c)
+{
+ struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
+ struct led_classdev_flash *flash = v4l2_flash->flash;
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
+ bool is_strobing;
+ int ret;
+
+ switch (c->id) {
+ case V4L2_CID_FLASH_TORCH_INTENSITY:
+ /*
+ * Update torch brightness only if in TORCH_MODE.
+ * In other modes torch led is turned off, which
+ * would spuriously inform the user space that
+ * V4L2_CID_FLASH_TORCH_INTENSITY control setting
+ * has changed.
+ */
+ if (ctrls[LED_MODE]->val == V4L2_FLASH_LED_MODE_TORCH) {
+ ret = led_update_brightness(led_cdev);
+ if (ret < 0)
+ return ret;
+ c->val = v4l2_flash_led_brightness_to_intensity(
+ ctrls[TORCH_INTENSITY],
+ led_cdev->brightness);
+ }
+ return 0;
+ case V4L2_CID_FLASH_INDICATOR_INTENSITY:
+ ret = led_update_brightness(led_cdev);
+ if (ret < 0)
+ return ret;
+ c->val = v4l2_flash_led_brightness_to_intensity(
+ ctrls[INDICATOR_INTENSITY],
+ led_cdev->brightness);
+ return 0;
+ case V4L2_CID_FLASH_INTENSITY:
+ ret = led_update_flash_brightness(flash);
+ if (ret < 0)
+ return ret;
+ /* no conversion is needed */
+ c->val = flash->brightness.val;
+ return 0;
+ case V4L2_CID_FLASH_STROBE_STATUS:
+ ret = led_get_flash_strobe(flash, &is_strobing);
+ if (ret < 0)
+ return ret;
+ c->val = is_strobing;
+ return 0;
+ case V4L2_CID_FLASH_FAULT:
+ /* led faults map directly to V4L2 flash faults */
+ return led_get_flash_fault(flash, &c->val);
+ case V4L2_CID_FLASH_SYNC_STROBE:
+ c->val = flash->sync_strobe;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c)
+{
+ struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
+ struct led_classdev_flash *flash = v4l2_flash->flash;
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
+ enum led_brightness brightness;
+ bool external_strobe;
+ int ret = 0;
+
+ switch (c->id) {
+ case V4L2_CID_FLASH_LED_MODE:
+ switch (c->val) {
+ case V4L2_FLASH_LED_MODE_NONE:
+ led_set_brightness(led_cdev, LED_OFF);
+ return led_set_flash_strobe(flash, false);
+ case V4L2_FLASH_LED_MODE_FLASH:
+ /* Turn the torch LED off */
+ led_set_brightness(led_cdev, LED_OFF);
+ external_strobe = (ctrls[STROBE_SOURCE]->val ==
+ V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
+
+ if (has_flash_op(v4l2_flash, external_strobe_set))
+ ret = call_flash_op(v4l2_flash,
+ external_strobe_set, v4l2_flash,
+ external_strobe);
+ return ret;
+ case V4L2_FLASH_LED_MODE_TORCH:
+ /* Stop flash strobing */
+ ret = led_set_flash_strobe(flash, false);
+ if (ret < 0)
+ return ret;
+
+ brightness =
+ v4l2_flash_intensity_to_led_brightness(
+ ctrls[TORCH_INTENSITY],
+ ctrls[TORCH_INTENSITY]->val);
+ led_set_brightness(led_cdev, brightness);
+ return 0;
+ }
+ break;
+ case V4L2_CID_FLASH_STROBE_SOURCE:
+ external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
+
+ return call_flash_op(v4l2_flash, external_strobe_set,
+ v4l2_flash, external_strobe);
+ case V4L2_CID_FLASH_STROBE:
+ if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH ||
+ ctrls[STROBE_SOURCE]->val !=
+ V4L2_FLASH_STROBE_SOURCE_SOFTWARE)
+ return -EINVAL;
+ return led_set_flash_strobe(flash, true);
+ case V4L2_CID_FLASH_STROBE_STOP:
+ if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH ||
+ ctrls[STROBE_SOURCE]->val !=
+ V4L2_FLASH_STROBE_SOURCE_SOFTWARE)
+ return -EINVAL;
+ return led_set_flash_strobe(flash, false);
+ case V4L2_CID_FLASH_TIMEOUT:
+ /* no conversion is needed */
+ return led_set_flash_timeout(flash, c->val);
+ case V4L2_CID_FLASH_INTENSITY:
+ /* no conversion is needed */
+ return led_set_flash_brightness(flash, c->val);
+ case V4L2_CID_FLASH_INDICATOR_INTENSITY:
+ brightness = v4l2_flash_intensity_to_led_brightness(
+ ctrls[INDICATOR_INTENSITY],
+ c->val);
+ led_set_brightness(led_cdev, brightness);
+ return 0;
+ case V4L2_CID_FLASH_TORCH_INTENSITY:
+ /*
+ * If not in MODE_TORCH don't call led-class brightness_set
+ * op, as it would result in turning the torch led on.
+ * Instead the value is cached only and will be written
+ * to the device upon transition to MODE_TORCH.
+ */
+ if (ctrls[LED_MODE]->val == V4L2_FLASH_LED_MODE_TORCH) {
+ brightness =
+ v4l2_flash_intensity_to_led_brightness(
+ ctrls[TORCH_INTENSITY],
+ c->val);
+ led_set_brightness(led_cdev, brightness);
+ }
+ return 0;
+ case V4L2_CID_FLASH_SYNC_STROBE:
+ flash->sync_strobe = c->val;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops v4l2_flash_ctrl_ops = {
+ .g_volatile_ctrl = v4l2_flash_g_volatile_ctrl,
+ .s_ctrl = v4l2_flash_s_ctrl,
+};
+
+static void fill_ctrl_init_data(struct v4l2_flash *v4l2_flash,
+ struct v4l2_flash_ctrl_config *flash_cfg,
+ struct v4l2_flash_ctrl_data *ctrl_init_data)
+{
+ struct led_classdev_flash *flash = v4l2_flash->flash;
+ const struct led_flash_ops *flash_ops = flash->ops;
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ struct v4l2_ctrl_config *ctrl_cfg;
+ u32 mask;
+ s64 max;
+
+ /* Init FLASH_LED_MODE ctrl data */
+ mask = 1 << V4L2_FLASH_LED_MODE_NONE |
+ 1 << V4L2_FLASH_LED_MODE_TORCH;
+ if (led_cdev->flags & LED_DEV_CAP_FLASH)
+ mask |= 1 << V4L2_FLASH_LED_MODE_FLASH;
+
+ ctrl_init_data[LED_MODE].supported = true;
+ ctrl_cfg = &ctrl_init_data[LED_MODE].config;
+ ctrl_cfg->id = V4L2_CID_FLASH_LED_MODE;
+ ctrl_cfg->max = V4L2_FLASH_LED_MODE_TORCH;
+ ctrl_cfg->menu_skip_mask = ~mask;
+ ctrl_cfg->def = V4L2_FLASH_LED_MODE_NONE;
+ ctrl_cfg->flags = 0;
+
+ /* Init TORCH_INTENSITY ctrl data */
+ ctrl_init_data[TORCH_INTENSITY].supported = true;
+ ctrl_init_data[TORCH_INTENSITY].config = flash_cfg->torch_intensity;
+ ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config;
+ ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY;
+ ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
+
+ if (!(led_cdev->flags & LED_DEV_CAP_FLASH))
+ return;
+
+ /* Init FLASH_STROBE_SOURCE ctrl data */
+ mask = 1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
+ if (flash_cfg->has_external_strobe) {
+ mask |= 1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
+ max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
+ } else {
+ max = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
+ }
+
+ ctrl_init_data[STROBE_SOURCE].supported = true;
+ ctrl_cfg = &ctrl_init_data[STROBE_SOURCE].config;
+ ctrl_cfg->id = V4L2_CID_FLASH_STROBE_SOURCE;
+ ctrl_cfg->max = max;
+ ctrl_cfg->menu_skip_mask = ~mask;
+ ctrl_cfg->def = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
+
+ /* Init FLASH_STROBE ctrl data */
+ ctrl_init_data[FLASH_STROBE].supported = true;
+ ctrl_cfg = &ctrl_init_data[FLASH_STROBE].config;
+ ctrl_cfg->id = V4L2_CID_FLASH_STROBE;
+
+ /* Init STROBE_STOP ctrl data */
+ ctrl_init_data[STROBE_STOP].supported = true;
+ ctrl_cfg = &ctrl_init_data[STROBE_STOP].config;
+ ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STOP;
+
+ /* Init STROBE_STATUS ctrl data */
+ if (flash_ops->strobe_get) {
+ ctrl_init_data[STROBE_STATUS].supported = true;
+ ctrl_cfg = &ctrl_init_data[STROBE_STATUS].config;
+ ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STATUS;
+ ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
+ V4L2_CTRL_FLAG_READ_ONLY;
+ }
+
+ /* Init FLASH_TIMEOUT ctrl data */
+ if (flash_ops->timeout_set) {
+ ctrl_init_data[FLASH_TIMEOUT].supported = true;
+ ctrl_init_data[FLASH_TIMEOUT].config = flash_cfg->flash_timeout;
+ ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config;
+ ctrl_cfg->id = V4L2_CID_FLASH_TIMEOUT;
+ ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
+ }
+
+ /* Init FLASH_INTENSITY ctrl data */
+ if (flash_ops->flash_brightness_set) {
+ ctrl_init_data[FLASH_INTENSITY].supported = true;
+ ctrl_init_data[FLASH_INTENSITY].config =
+ flash_cfg->flash_intensity;
+ ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config;
+ ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY;
+ ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
+ }
+
+ /* Init INDICATOR_INTENSITY ctrl data */
+ if (flash_cfg->has_indicator) {
+ ctrl_init_data[INDICATOR_INTENSITY].supported = true;
+ ctrl_init_data[INDICATOR_INTENSITY].config =
+ flash_cfg->indicator_intensity;
+ ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config;
+ ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY;
+ ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
+ }
+
+ /* Init FLASH_FAULT ctrl data */
+ if (flash_cfg->flash_faults) {
+ ctrl_init_data[FLASH_FAULT].supported = true;
+ ctrl_cfg = &ctrl_init_data[FLASH_FAULT].config;
+ ctrl_cfg->id = V4L2_CID_FLASH_FAULT;
+ ctrl_cfg->max = flash_cfg->flash_faults;
+ ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
+ V4L2_CTRL_FLAG_READ_ONLY;
+ }
+
+ if (led_cdev->flags & LED_DEV_CAP_COMPOUND) {
+ ctrl_init_data[SYNC_STROBE].supported = true;
+ ctrl_cfg = &ctrl_init_data[SYNC_STROBE].config;
+ ctrl_cfg->id = V4L2_CID_FLASH_SYNC_STROBE;
+ ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
+ }
+}
+
+static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash,
+ struct v4l2_flash_ctrl_config *flash_cfg)
+
+{
+ struct v4l2_flash_ctrl_data *ctrl_init_data;
+ struct v4l2_ctrl *ctrl;
+ struct v4l2_ctrl_config *ctrl_cfg;
+ int i, ret, num_ctrls = 0;
+
+ /* allocate memory dynamically so as not to exceed stack frame size */
+ ctrl_init_data = kcalloc(NUM_FLASH_CTRLS, sizeof(*ctrl_init_data),
+ GFP_KERNEL);
+ if (!ctrl_init_data)
+ return -ENOMEM;
+
+ memset(ctrl_init_data, 0, sizeof(*ctrl_init_data));
+
+ fill_ctrl_init_data(v4l2_flash, flash_cfg, ctrl_init_data);
+
+ for (i = 0; i < NUM_FLASH_CTRLS; ++i)
+ if (ctrl_init_data[i].supported)
+ ++num_ctrls;
+
+ v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls);
+
+ for (i = 0; i < NUM_FLASH_CTRLS; ++i) {
+ ctrl_cfg = &ctrl_init_data[i].config;
+ if (!ctrl_init_data[i].supported)
+ continue;
+
+ if (ctrl_cfg->id == V4L2_CID_FLASH_LED_MODE ||
+ ctrl_cfg->id == V4L2_CID_FLASH_STROBE_SOURCE)
+ ctrl = v4l2_ctrl_new_std_menu(&v4l2_flash->hdl,
+ &v4l2_flash_ctrl_ops,
+ ctrl_cfg->id,
+ ctrl_cfg->max,
+ ctrl_cfg->menu_skip_mask,
+ ctrl_cfg->def);
+ else
+ ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl,
+ &v4l2_flash_ctrl_ops,
+ ctrl_cfg->id,
+ ctrl_cfg->min,
+ ctrl_cfg->max,
+ ctrl_cfg->step,
+ ctrl_cfg->def);
+
+ if (ctrl)
+ ctrl->flags |= ctrl_cfg->flags;
+
+ if (i <= STROBE_SOURCE)
+ v4l2_flash->ctrls[i] = ctrl;
+ }
+
+ kfree(ctrl_init_data);
+
+ if (v4l2_flash->hdl.error) {
+ ret = v4l2_flash->hdl.error;
+ goto error_free_handler;
+ }
+
+ v4l2_ctrl_handler_setup(&v4l2_flash->hdl);
+
+ v4l2_flash->sd.ctrl_handler = &v4l2_flash->hdl;
+
+ return 0;
+
+error_free_handler:
+ v4l2_ctrl_handler_free(&v4l2_flash->hdl);
+ return ret;
+}
+
+/*
+ * V4L2 subdev internal operations
+ */
+
+static int v4l2_flash_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd);
+ struct led_classdev_flash *flash = v4l2_flash->flash;
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ int ret = 0;
+
+ mutex_lock(&led_cdev->led_access);
+
+ if (!v4l2_fh_is_singular(&fh->vfh)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ led_sysfs_disable(led_cdev);
+
+unlock:
+ mutex_unlock(&led_cdev->led_access);
+ return ret;
+}
+
+static int v4l2_flash_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd);
+ struct led_classdev_flash *flash = v4l2_flash->flash;
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ int ret = 0;
+
+ mutex_lock(&led_cdev->led_access);
+
+ if (has_flash_op(v4l2_flash, external_strobe_set))
+ ret = call_flash_op(v4l2_flash, external_strobe_set,
+ v4l2_flash, false);
+ led_sysfs_enable(led_cdev);
+
+ mutex_unlock(&led_cdev->led_access);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_internal_ops v4l2_flash_subdev_internal_ops = {
+ .open = v4l2_flash_open,
+ .close = v4l2_flash_close,
+};
+
+static const struct v4l2_subdev_core_ops v4l2_flash_core_ops = {
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+};
+
+static const struct v4l2_subdev_ops v4l2_flash_subdev_ops = {
+ .core = &v4l2_flash_core_ops,
+};
+
+struct v4l2_flash *v4l2_flash_init(struct led_classdev_flash *flash,
+ const struct v4l2_flash_ops *ops,
+ struct v4l2_flash_ctrl_config *config)
+{
+ struct v4l2_flash *v4l2_flash;
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ if (!flash || !ops || !config)
+ return ERR_PTR(-EINVAL);
+
+ v4l2_flash = kzalloc(sizeof(*v4l2_flash), GFP_KERNEL);
+ if (!v4l2_flash)
+ return ERR_PTR(-ENOMEM);
+
+ sd = &v4l2_flash->sd;
+ v4l2_flash->flash = flash;
+ v4l2_flash->ops = ops;
+ sd->dev = led_cdev->dev->parent;
+ v4l2_subdev_init(sd, &v4l2_flash_subdev_ops);
+ sd->internal_ops = &v4l2_flash_subdev_internal_ops;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(sd->name, sizeof(sd->name), led_cdev->name);
+
+ ret = v4l2_flash_init_controls(v4l2_flash, config);
+ if (ret < 0)
+ goto err_init_controls;
+
+ ret = media_entity_init(&sd->entity, 0, NULL, 0);
+ if (ret < 0)
+ goto err_init_entity;
+
+ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
+
+ ret = v4l2_async_register_subdev(sd);
+ if (ret < 0)
+ goto err_init_entity;
+
+ return v4l2_flash;
+
+err_init_entity:
+ media_entity_cleanup(&sd->entity);
+err_init_controls:
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ kfree(v4l2_flash);
+
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(v4l2_flash_init);
+
+void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
+{
+ struct v4l2_subdev *sd = &v4l2_flash->sd;
+
+ if (!v4l2_flash)
+ return;
+
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ kfree(v4l2_flash);
+}
+EXPORT_SYMBOL_GPL(v4l2_flash_release);
+
+MODULE_AUTHOR("Jacek Anaszewski <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("V4L2 Flash sub-device helpers");
diff --git a/include/media/v4l2-flash.h b/include/media/v4l2-flash.h
new file mode 100644
index 0000000..1e18df5
--- /dev/null
+++ b/include/media/v4l2-flash.h
@@ -0,0 +1,138 @@
+/*
+ * V4L2 Flash LED sub-device registration helpers.
+ *
+ * Copyright (C) 2014 Samsung Electronics Co., Ltd
+ * Author: Jacek Anaszewski <[email protected]>
+ *
+ * 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."
+ */
+
+#ifndef _V4L2_FLASH_H
+#define _V4L2_FLASH_H
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+
+struct led_classdev_flash;
+struct led_classdev;
+struct v4l2_flash;
+enum led_brightness;
+
+enum ctrl_init_data_id {
+ LED_MODE,
+ TORCH_INTENSITY,
+ STROBE_SOURCE,
+ FLASH_STROBE,
+ STROBE_STOP,
+ STROBE_STATUS,
+ FLASH_TIMEOUT,
+ FLASH_INTENSITY,
+ INDICATOR_INTENSITY,
+ FLASH_FAULT,
+ SYNC_STROBE,
+ NUM_FLASH_CTRLS,
+};
+
+/*
+ * struct v4l2_flash_ctrl_data - flash control initialization data -
+ * filled basing on the features declared
+ * by the LED Flash class driver
+ * @config: initialization data for a control
+ * @supported: indicates whether a control is supported
+ * by the LED Flash class driver
+ */
+struct v4l2_flash_ctrl_data {
+ struct v4l2_ctrl_config config;
+ bool supported;
+};
+
+struct v4l2_flash_ops {
+ /* setup strobing the flash by hardware pin state assertion */
+ int (*external_strobe_set)(struct v4l2_flash *v4l2_flash,
+ bool enable);
+};
+
+/**
+ * struct v4l2_flash_ctrl_config - V4L2 Flash controls initialization data
+ * @torch_intensity: V4L2_CID_FLASH_TORCH_INTENSITY constraints
+ * @flash_intensity: V4L2_CID_FLASH_INTENSITY constraints
+ * @indicator_intensity: V4L2_CID_FLASH_INDICATOR_INTENSITY constraints
+ * @flash_timeout: V4L2_CID_FLASH_TIMEOUT constraints
+ * @flash_fault: possible flash faults
+ * @has_external_strobe: external strobe capability
+ */
+struct v4l2_flash_ctrl_config {
+ struct v4l2_ctrl_config torch_intensity;
+ struct v4l2_ctrl_config flash_intensity;
+ struct v4l2_ctrl_config indicator_intensity;
+ struct v4l2_ctrl_config flash_timeout;
+ u32 flash_faults;
+ bool has_external_strobe:1;
+ bool has_indicator:1;
+};
+
+/**
+ * struct v4l2_flash - Flash sub-device context
+ * @flash: LED Flash Class device controlled by this sub-device
+ * @ops: V4L2 specific flash ops
+ * @sd: V4L2 sub-device
+ * @hdl: flash controls handler
+ * @ctrls: array of pointers to controls, whose values define
+ the sub-device state
+ */
+struct v4l2_flash {
+ struct led_classdev_flash *flash;
+ const struct v4l2_flash_ops *ops;
+
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *ctrls[STROBE_SOURCE + 1];
+};
+
+static inline struct v4l2_flash *v4l2_subdev_to_v4l2_flash(
+ struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct v4l2_flash, sd);
+}
+
+static inline struct v4l2_flash *v4l2_ctrl_to_v4l2_flash(struct v4l2_ctrl *c)
+{
+ return container_of(c->handler, struct v4l2_flash, hdl);
+}
+
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+/**
+ * v4l2_flash_init - initialize V4L2 flash led sub-device
+ * @led_fdev: the LED Flash Class device to wrap
+ * @config: initialization data for V4L2 Flash controls
+ * @flash_ops: V4L2 Flash device ops
+ *
+ * Create V4L2 subdev wrapping given LED subsystem device.
+
+ * Returns: A valid pointer, or, when an error occurs, the return
+ * value is encoded using ERR_PTR(). Use IS_ERR() to check and
+ * PTR_ERR() to obtain the numeric return value.
+ */
+struct v4l2_flash *v4l2_flash_init(struct led_classdev_flash *led_fdev,
+ const struct v4l2_flash_ops *ops,
+ struct v4l2_flash_ctrl_config *config);
+
+/**
+ * v4l2_flash_release - release V4L2 Flash sub-device
+ * @flash: the V4L2 Flash device to release
+ *
+ * Release V4L2 flash led subdev.
+ */
+void v4l2_flash_release(struct v4l2_flash *v4l2_flash);
+
+#else
+#define v4l2_flash_init(led_cdev, ops, config) (NULL)
+#define v4l2_flash_release(v4l2_flash)
+#endif /* CONFIG_V4L2_FLASH_LED_CLASS */
+
+#endif /* _V4L2_FLASH_H */
--
1.7.9.5
This patch adds a description of 'flashes' property
to the samsung-fimc.txt.
Signed-off-by: Jacek Anaszewski <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Sylwester Nawrocki <[email protected]>
Cc: Rob Herring <[email protected]>
Cc: Pawel Moll <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: Ian Campbell <[email protected]>
Cc: Kumar Gala <[email protected]>
Cc: <[email protected]>
---
.../devicetree/bindings/media/samsung-fimc.txt | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
index 922d6f8..4b7ed03 100644
--- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
+++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
@@ -40,6 +40,12 @@ should be inactive. For the "active-a" state the camera port A must be activated
and the port B deactivated and for the state "active-b" it should be the other
way around.
+Optional properties:
+
+- flashes - array of strings with flash led names; the name has to
+ be same with the related led label
+ (see Documentation/devicetree/bindings/leds/common.txt)
+
The 'camera' node must include at least one 'fimc' child node.
@@ -166,6 +172,7 @@ Example:
clock-output-names = "cam_a_clkout", "cam_b_clkout";
pinctrl-names = "default";
pinctrl-0 = <&cam_port_a_clk_active>;
+ flashes = "max77693-flash1", "max77693-flash2";
status = "okay";
#address-cells = <1>;
#size-cells = <1>;
--
1.7.9.5
Add "label" array for Device Tree strings with
the name of a LED device and make flash_timeout
a two element array, for caching the sub-led
related flash timeout.
Signed-off-by: Jacek Anaszewski <[email protected]>
Signed-off-by: Andrzej Hajda <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Lee Jones <[email protected]>
Cc: SangYoung Son <[email protected]>
Cc: Samuel Ortiz <[email protected]>
---
include/linux/mfd/max77693.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/include/linux/mfd/max77693.h b/include/linux/mfd/max77693.h
index f0b6585..30fa19ea 100644
--- a/include/linux/mfd/max77693.h
+++ b/include/linux/mfd/max77693.h
@@ -88,14 +88,15 @@ enum max77693_led_boost_mode {
};
struct max77693_led_platform_data {
+ const char *label[2];
u32 fleds[2];
u32 iout_torch[2];
u32 iout_flash[2];
u32 trigger[2];
u32 trigger_type[2];
+ u32 flash_timeout[2];
u32 num_leds;
u32 boost_mode;
- u32 flash_timeout;
u32 boost_vout;
u32 low_vsys;
};
--
1.7.9.5
This patch adds led-flash support to Maxim max77693 chipset.
A device can be exposed to user space through LED subsystem
sysfs interface or through V4L2 sub-device when the support
for V4L2 Flash sub-devices is enabled. Device supports up to
two leds which can work in flash and torch mode. The leds can
be triggered externally or by software.
Signed-off-by: Jacek Anaszewski <[email protected]>
Signed-off-by: Andrzej Hajda <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Lee Jones <[email protected]>
Cc: Bryan Wu <[email protected]>
Cc: Richard Purdie <[email protected]>
Cc: SangYoung Son <[email protected]>
Cc: Samuel Ortiz <[email protected]>
---
drivers/leds/Kconfig | 10 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-max77693.c | 1152 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1163 insertions(+)
create mode 100644 drivers/leds/leds-max77693.c
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index fa8021e..2e66d55 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -463,6 +463,16 @@ config LEDS_TCA6507
LED driver chips accessed via the I2C bus.
Driver support brightness control and hardware-assisted blinking.
+config LEDS_MAX77693
+ tristate "LED support for MAX77693 Flash"
+ depends on LEDS_CLASS_FLASH
+ depends on MFD_MAX77693
+ depends on OF
+ help
+ This option enables support for the flash part of the MAX77693
+ multifunction device. It has build in control for two leds in flash
+ and torch mode.
+
config LEDS_MAX8997
tristate "LED support for MAX8997 PMIC"
depends on LEDS_CLASS && MFD_MAX8997
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index cbba921..57ca62b 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o
obj-$(CONFIG_LEDS_NS2) += leds-ns2.o
obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o
obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o
+obj-$(CONFIG_LEDS_MAX77693) += leds-max77693.o
obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c
new file mode 100644
index 0000000..2e96fd9
--- /dev/null
+++ b/drivers/leds/leds-max77693.c
@@ -0,0 +1,1152 @@
+/*
+ * LED Flash class driver for the flash cell of max77693 mfd.
+ *
+ * Copyright (C) 2014, Samsung Electronics Co., Ltd.
+ *
+ * Authors: Jacek Anaszewski <[email protected]>
+ * Andrzej Hajda <[email protected]>
+ *
+ * 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.
+ */
+
+#include <asm/div64.h>
+#include <linux/led-class-flash.h>
+#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-private.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-flash.h>
+
+#define MODE_OFF 0
+#define MODE_FLASH1 (1 << 0)
+#define MODE_FLASH2 (1 << 1)
+#define MODE_TORCH1 (1 << 2)
+#define MODE_TORCH2 (1 << 3)
+#define MODE_FLASH_EXTERNAL1 (1 << 4)
+#define MODE_FLASH_EXTERNAL2 (1 << 5)
+
+#define MODE_FLASH (MODE_FLASH1 | MODE_FLASH2 | \
+ MODE_FLASH_EXTERNAL1 | MODE_FLASH_EXTERNAL2)
+
+#define FLED1_IOUT (1 << 0)
+#define FLED2_IOUT (1 << 1)
+
+enum {
+ FLED1,
+ FLED2
+};
+
+enum {
+ FLASH,
+ TORCH
+};
+
+struct max77693_sub_led {
+ struct led_classdev_flash ldev;
+ struct work_struct work_brightness_set;
+ struct v4l2_flash *v4l2_flash;
+
+ unsigned int torch_brightness;
+ unsigned int flash_timeout;
+};
+
+struct max77693_led {
+ struct regmap *regmap;
+ struct platform_device *pdev;
+ struct max77693_led_platform_data *pdata;
+ struct mutex lock;
+
+ struct max77693_sub_led sub_leds[2];
+
+ unsigned int current_flash_timeout;
+ unsigned int mode_flags;
+ u8 torch_iout_reg;
+ bool iout_joint;
+ int strobing_sub_led_id;
+};
+
+struct max77693_led_settings {
+ struct led_flash_setting torch_brightness;
+ struct led_flash_setting flash_brightness;
+ struct led_flash_setting flash_timeout;
+};
+
+static u8 max77693_led_iout_to_reg(u32 ua)
+{
+ if (ua < FLASH_IOUT_MIN)
+ ua = FLASH_IOUT_MIN;
+ return (ua - FLASH_IOUT_MIN) / FLASH_IOUT_STEP;
+}
+
+static u8 max77693_flash_timeout_to_reg(u32 us)
+{
+ return (us - FLASH_TIMEOUT_MIN) / FLASH_TIMEOUT_STEP;
+}
+
+static inline struct max77693_led *ldev1_to_led(
+ struct led_classdev_flash *ldev)
+{
+ struct max77693_sub_led *sub_led = container_of(ldev,
+ struct max77693_sub_led,
+ ldev);
+ return container_of(sub_led, struct max77693_led, sub_leds[0]);
+}
+
+static inline struct max77693_led *ldev2_to_led(
+ struct led_classdev_flash *ldev)
+{
+ struct max77693_sub_led *sub_led = container_of(ldev,
+ struct max77693_sub_led,
+ ldev);
+ return container_of(sub_led, struct max77693_led, sub_leds[1]);
+}
+
+static u8 max77693_led_vsys_to_reg(u32 mv)
+{
+ return ((mv - MAX_FLASH1_VSYS_MIN) / MAX_FLASH1_VSYS_STEP) << 2;
+}
+
+static u8 max77693_led_vout_to_reg(u32 mv)
+{
+ return (mv - FLASH_VOUT_MIN) / FLASH_VOUT_STEP + FLASH_VOUT_RMIN;
+}
+
+/* split composite current @i into two @iout according to @imax weights */
+static void max77693_calc_iout(u32 iout[2], u32 i, u32 imax[2])
+{
+ u64 t = i;
+
+ t *= imax[1];
+ do_div(t, imax[0] + imax[1]);
+
+ iout[1] = (u32)t / FLASH_IOUT_STEP * FLASH_IOUT_STEP;
+ iout[0] = i - iout[1];
+}
+
+static int max77693_set_mode(struct max77693_led *led, unsigned int mode)
+{
+ struct max77693_led_platform_data *p = led->pdata;
+ struct regmap *rmap = led->regmap;
+ int ret, v = 0;
+
+ if (mode & MODE_TORCH1) {
+ if (p->trigger[FLED1] & MAX77693_LED_TRIG_SOFT)
+ v |= FLASH_EN_ON << TORCH_EN_SHIFT(1);
+ }
+
+ if (mode & MODE_TORCH2) {
+ if (p->trigger[FLED2] & MAX77693_LED_TRIG_SOFT)
+ v |= FLASH_EN_ON << TORCH_EN_SHIFT(2);
+ }
+
+ if (mode & MODE_FLASH1) {
+ if (p->trigger[FLED1] & MAX77693_LED_TRIG_SOFT)
+ v |= FLASH_EN_ON << FLASH_EN_SHIFT(1);
+ } else if (mode & MODE_FLASH_EXTERNAL1) {
+ if (p->trigger[FLED1] & MAX77693_LED_TRIG_EXT)
+ v |= FLASH_EN_FLASH << FLASH_EN_SHIFT(2);
+ /*
+ * Enable hw triggering also for torch mode, as some camera
+ * sensors use torch led to fathom ambient light conditions
+ * before strobing the flash.
+ */
+ if (p->trigger[FLED1] & MAX77693_LED_TRIG_EXT)
+ v |= FLASH_EN_TORCH << TORCH_EN_SHIFT(1);
+ }
+
+ if (mode & MODE_FLASH2) {
+ if (p->trigger[FLED2] & MAX77693_LED_TRIG_SOFT)
+ v |= FLASH_EN_ON << FLASH_EN_SHIFT(2);
+ } else if (mode & MODE_FLASH_EXTERNAL2) {
+ if (p->trigger[FLED2] & MAX77693_LED_TRIG_EXT)
+ v |= FLASH_EN_FLASH << FLASH_EN_SHIFT(2);
+ /*
+ * Enable hw triggering also for torch mode, as some camera
+ * sensors use torch led to fathom ambient light conditions
+ * before strobing the flash.
+ */
+ if (p->trigger[FLED2] & MAX77693_LED_TRIG_EXT)
+ v |= FLASH_EN_TORCH << TORCH_EN_SHIFT(2);
+ }
+
+ /* Reset the register only prior setting flash modes */
+ if (mode & ~(MODE_TORCH1 | MODE_TORCH2)) {
+ ret = regmap_write(rmap, MAX77693_LED_REG_FLASH_EN, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return regmap_write(rmap, MAX77693_LED_REG_FLASH_EN, v);
+}
+
+static void max77693_set_sync_strobe(struct max77693_led *led,
+ unsigned int *mode)
+{
+ struct max77693_sub_led *sub_leds = led->sub_leds;
+ struct led_classdev_flash *flash;
+ unsigned int m = *mode;
+
+ /*
+ * If there are two leds then check if the other one
+ * wants to be strobed simultaneously.
+ */
+ if (!led->iout_joint) {
+ if (m & (MODE_FLASH1 | MODE_FLASH_EXTERNAL1)) {
+ flash = &sub_leds[FLED2].ldev;
+ if (flash->sync_strobe)
+ m |= m << 1;
+ } else if (m & (MODE_FLASH2 | MODE_FLASH_EXTERNAL2)) {
+ flash = &sub_leds[FLED1].ldev;
+ if (flash->sync_strobe)
+ m |= m >> 1;
+ }
+ }
+
+ *mode = m;
+}
+
+static int max77693_add_mode(struct max77693_led *led, unsigned int mode)
+{
+ int ret;
+
+ /* Span the mode on FLED2 for joint iouts case */
+ if (led->iout_joint)
+ mode |= (mode << 1);
+
+ /*
+ * Torch mode once enabled remains active until turned off,
+ * and thus no action is required in this case.
+ */
+ if ((mode & MODE_TORCH1) &&
+ (led->mode_flags & MODE_TORCH1))
+ return 0;
+ if ((mode & MODE_TORCH2) &&
+ (led->mode_flags & MODE_TORCH2))
+ return 0;
+
+ /* Span a flash mode on the other led if it is to be synchronized */
+ max77693_set_sync_strobe(led, &mode);
+
+ /*
+ * FLASH_EXTERNAL mode activates FLASHEN and TORCHEN pins
+ * in the device. The related register settings interfere
+ * with SW triggerred modes, thus clear them to ensure proper
+ * device configuration.
+ */
+ if (mode & MODE_FLASH_EXTERNAL1)
+ led->mode_flags &= (~MODE_TORCH1 & ~MODE_FLASH1);
+ if (mode & MODE_FLASH_EXTERNAL2)
+ led->mode_flags &= (~MODE_TORCH2 & ~MODE_FLASH2);
+
+ led->mode_flags |= mode;
+
+ ret = max77693_set_mode(led, led->mode_flags);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Clear flash mode flag after setting the mode to avoid
+ * spurious flash strobing on every subsequent torch mode
+ * setting.
+ */
+ if (mode & MODE_FLASH)
+ led->mode_flags &= ~mode;
+
+ return ret;
+}
+
+static int max77693_clear_mode(struct max77693_led *led, unsigned int mode)
+{
+ int ret;
+
+ if (led->iout_joint)
+ /* Clear mode also on FLED2 for joint iouts case */
+ mode |= (mode << 1);
+ else
+ /*
+ * Clear a flash mode on the other led
+ * if it is to be synchronized.
+ */
+ max77693_set_sync_strobe(led, &mode);
+
+ led->mode_flags &= ~mode;
+
+ ret = max77693_set_mode(led, led->mode_flags);
+
+ return ret;
+}
+
+static int max77693_set_torch_current(struct max77693_led *led,
+ int led_id, u32 micro_amp)
+{
+ struct max77693_led_platform_data *p = led->pdata;
+ struct regmap *rmap = led->regmap;
+ u32 iout[2], iout_max[2];
+ u8 iout1_reg = 0, iout2_reg = 0;
+
+ iout_max[FLED1] = p->iout_torch[FLED1];
+ iout_max[FLED2] = p->iout_torch[FLED2];
+
+ if (led_id == FLED1) {
+ /*
+ * Preclude splitting current to FLED2 if we
+ * are driving two separate leds.
+ */
+ if (!led->iout_joint)
+ iout_max[FLED2] = 0;
+ max77693_calc_iout(iout, micro_amp, iout_max);
+ } else if (led_id == FLED2) {
+ iout_max[FLED1] = 0;
+ max77693_calc_iout(iout, micro_amp, iout_max);
+ }
+
+ if (led_id == FLED1 || led->iout_joint) {
+ iout1_reg = max77693_led_iout_to_reg(iout[FLED1]);
+ led->torch_iout_reg &= 0xf0;
+ }
+ if (led_id == FLED2 || led->iout_joint) {
+ iout2_reg = max77693_led_iout_to_reg(iout[FLED2]);
+ led->torch_iout_reg &= 0x0f;
+ }
+
+ led->torch_iout_reg |= ((iout1_reg << TORCH_IOUT1_SHIFT) |
+ (iout2_reg << TORCH_IOUT2_SHIFT));
+
+ return regmap_write(rmap, MAX77693_LED_REG_ITORCH,
+ led->torch_iout_reg);
+}
+
+static int max77693_set_flash_current(struct max77693_led *led,
+ int led_id,
+ u32 micro_amp)
+{
+ struct max77693_led_platform_data *p = led->pdata;
+ struct regmap *rmap = led->regmap;
+ u32 iout[2], iout_max[2];
+ u8 iout1_reg, iout2_reg;
+ int ret = -EINVAL;
+
+ iout_max[FLED1] = p->iout_flash[FLED1];
+ iout_max[FLED2] = p->iout_flash[FLED2];
+
+ if (led_id == FLED1) {
+ /*
+ * Preclude splitting current to FLED2 if we
+ * are driving two separate leds.
+ */
+ if (!led->iout_joint)
+ iout_max[FLED2] = 0;
+ max77693_calc_iout(iout, micro_amp, iout_max);
+ } else if (led_id == FLED2) {
+ iout_max[FLED1] = 0;
+ max77693_calc_iout(iout, micro_amp, iout_max);
+ }
+
+ if (led_id == FLED1 || led->iout_joint) {
+ iout1_reg = max77693_led_iout_to_reg(iout[FLED1]);
+ ret = regmap_write(rmap, MAX77693_LED_REG_IFLASH1,
+ iout1_reg);
+ if (ret < 0)
+ return ret;
+ }
+ if (led_id == FLED2 || led->iout_joint) {
+ iout2_reg = max77693_led_iout_to_reg(iout[FLED2]);
+ ret = regmap_write(rmap, MAX77693_LED_REG_IFLASH2,
+ iout2_reg);
+ }
+
+ return ret;
+}
+
+static int max77693_set_timeout(struct max77693_led *led, u32 timeout)
+{
+ struct max77693_led_platform_data *p = led->pdata;
+ struct regmap *rmap = led->regmap;
+ u8 v;
+ int ret;
+
+ v = max77693_flash_timeout_to_reg(timeout);
+
+ if (p->trigger_type[FLASH] == MAX77693_LED_TRIG_TYPE_LEVEL)
+ v |= FLASH_TMR_LEVEL;
+
+ ret = regmap_write(rmap, MAX77693_LED_REG_FLASH_TIMER, v);
+ if (ret < 0)
+ return ret;
+
+ led->current_flash_timeout = timeout;
+
+ return 0;
+}
+
+static int max77693_strobe_status_get(struct max77693_led *led, bool *state)
+{
+ struct regmap *rmap = led->regmap;
+ unsigned int v;
+ int ret;
+
+ ret = regmap_read(rmap, MAX77693_LED_REG_FLASH_STATUS, &v);
+ if (ret < 0)
+ return ret;
+
+ *state = v & FLASH_STATUS_FLASH_ON;
+
+ return ret;
+}
+
+static int max77693_int_flag_get(struct max77693_led *led, unsigned int *v)
+{
+ struct regmap *rmap = led->regmap;
+
+ return regmap_read(rmap, MAX77693_LED_REG_FLASH_INT, v);
+}
+
+static int max77693_setup(struct max77693_led *led)
+{
+ struct max77693_led_platform_data *p = led->pdata;
+ struct regmap *rmap = led->regmap;
+ int i, first_led, last_led, ret;
+ u32 max_flash_curr[2];
+ u8 v;
+
+ /*
+ * Initialize only flash current. Torch current doesn't
+ * require initialization as ITORCH register is written with
+ * new value each time brightness_set op is called.
+ */
+ if (led->iout_joint) {
+ first_led = FLED1;
+ last_led = FLED1;
+ max_flash_curr[FLED1] = p->iout_flash[FLED1] +
+ p->iout_flash[FLED2];
+ } else {
+ first_led = p->fleds[FLED1] ? FLED1 : FLED2;
+ last_led = p->num_leds == 2 ? FLED2 : first_led;
+ max_flash_curr[FLED1] = p->iout_flash[FLED1];
+ max_flash_curr[FLED2] = p->iout_flash[FLED2];
+ }
+
+ for (i = first_led; i <= last_led; ++i) {
+ ret = max77693_set_flash_current(led, i,
+ max_flash_curr[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ v = TORCH_TMR_NO_TIMER | MAX77693_LED_TRIG_TYPE_LEVEL;
+ ret = regmap_write(rmap, MAX77693_LED_REG_ITORCHTIMER, v);
+ if (ret < 0)
+ return ret;
+
+ /* initially set FLED1 timeout */
+ ret = max77693_set_timeout(led, p->flash_timeout[FLED1]);
+ if (ret < 0)
+ return ret;
+
+ if (p->low_vsys > 0)
+ v = max77693_led_vsys_to_reg(p->low_vsys) |
+ MAX_FLASH1_MAX_FL_EN;
+ else
+ v = 0;
+
+ ret = regmap_write(rmap, MAX77693_LED_REG_MAX_FLASH1, v);
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(rmap, MAX77693_LED_REG_MAX_FLASH2, 0);
+ if (ret < 0)
+ return ret;
+
+ if (p->boost_mode == MAX77693_LED_BOOST_FIXED)
+ v = FLASH_BOOST_FIXED;
+ else
+ v = p->boost_mode | p->boost_mode << 1;
+ if (p->fleds[FLED1] && p->fleds[FLED2])
+ v |= FLASH_BOOST_LEDNUM_2;
+ ret = regmap_write(rmap, MAX77693_LED_REG_VOUT_CNTL, v);
+ if (ret < 0)
+ return ret;
+
+ v = max77693_led_vout_to_reg(p->boost_vout);
+ ret = regmap_write(rmap, MAX77693_LED_REG_VOUT_FLASH1, v);
+ if (ret < 0)
+ return ret;
+
+ return max77693_set_mode(led, MODE_OFF);
+}
+
+static int max77693_led_brightness_set(struct max77693_led *led,
+ int led_id, enum led_brightness value)
+{
+ int ret;
+
+ mutex_lock(&led->lock);
+
+ if (value == 0) {
+ ret = max77693_clear_mode(led, MODE_TORCH1 << led_id);
+ if (ret < 0)
+ dev_dbg(&led->pdev->dev,
+ "Failed to clear torch mode (%d)\n",
+ ret);
+ goto unlock;
+ }
+
+ ret = max77693_set_torch_current(led, led_id, value * TORCH_IOUT_STEP);
+ if (ret < 0) {
+ dev_dbg(&led->pdev->dev,
+ "Failed to set torch current (%d)\n",
+ ret);
+ goto unlock;
+ }
+
+ ret = max77693_add_mode(led, MODE_TORCH1 << led_id);
+ if (ret < 0)
+ dev_dbg(&led->pdev->dev,
+ "Failed to set torch mode (%d)\n",
+ ret);
+unlock:
+ mutex_unlock(&led->lock);
+ return ret;
+}
+
+#define MAX77693_LED_BRIGHTNESS_SET_WORK(ID) \
+static void max77693_led##ID##_brightness_set_work( \
+ struct work_struct *work) \
+{ \
+ struct max77693_sub_led *sub_led = \
+ container_of(work, struct max77693_sub_led, \
+ work_brightness_set); \
+ struct max77693_led *led = container_of(sub_led, \
+ struct max77693_led, \
+ sub_leds[FLED##ID]); \
+ struct max77693_sub_led *sub_leds = led->sub_leds; \
+ \
+ max77693_led_brightness_set(led, FLED##ID, \
+ sub_leds[FLED##ID].torch_brightness); \
+}
+
+/* LED subsystem callbacks */
+
+#define MAX77693_LED_TORCH_BRIGHTNESS_SET(ID) \
+static int max77693_led##ID##_torch_brightness_set( \
+ struct led_classdev *led_cdev, \
+ enum led_brightness value) \
+{ \
+ struct led_classdev_flash *flash = lcdev_to_flash(led_cdev); \
+ struct max77693_led *led = ldev##ID##_to_led(flash); \
+ \
+ return max77693_led_brightness_set(led, FLED##ID, value); \
+}
+
+#define MAX77693_LED_BRIGHTNESS_SET(ID) \
+static void max77693_led##ID##_brightness_set( \
+ struct led_classdev *led_cdev, \
+ enum led_brightness value) \
+{ \
+ struct led_classdev_flash *flash = lcdev_to_flash(led_cdev); \
+ struct max77693_led *led = ldev##ID##_to_led(flash); \
+ struct max77693_sub_led *sub_leds = led->sub_leds; \
+ \
+ sub_leds[FLED##ID].torch_brightness = value; \
+ schedule_work(&sub_leds[FLED##ID].work_brightness_set); \
+}
+
+#define MAX77693_LED_FLASH_BRIGHTNESS_SET(ID) \
+static int max77693_led##ID##_flash_brightness_set( \
+ struct led_classdev_flash *flash, \
+ u32 brightness) \
+{ \
+ struct max77693_led *led = ldev##ID##_to_led(flash); \
+ int ret; \
+ \
+ mutex_lock(&led->lock); \
+ ret = max77693_set_flash_current(led, FLED##ID, brightness); \
+ mutex_unlock(&led->lock); \
+ \
+ return ret; \
+}
+
+#define MAX77693_LED_FLASH_STROBE_SET(ID) \
+static int max77693_led##ID##_flash_strobe_set( \
+ struct led_classdev_flash *flash, \
+ bool state) \
+{ \
+ struct max77693_led *led = ldev##ID##_to_led(flash); \
+ struct max77693_sub_led *sub_leds = led->sub_leds; \
+ int ret; \
+ \
+ mutex_lock(&led->lock); \
+ \
+ if (!state) { \
+ ret = max77693_clear_mode(led, MODE_FLASH##ID); \
+ goto unlock; \
+ } \
+ \
+ if (sub_leds[FLED##ID].flash_timeout != \
+ led->current_flash_timeout) { \
+ ret = max77693_set_timeout(led, \
+ sub_leds[FLED##ID].flash_timeout); \
+ if (ret < 0) \
+ goto unlock; \
+ } \
+ \
+ led->strobing_sub_led_id = ID; \
+ \
+ ret = max77693_add_mode(led, MODE_FLASH##ID); \
+ \
+unlock: \
+ mutex_unlock(&led->lock); \
+ return ret; \
+}
+
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+#define MAX77693_LED_FLASH_EXTERNAL_STROBE_SET(ID) \
+static int max77693_led##ID##_external_strobe_set( \
+ struct v4l2_flash *v4l2_flash, \
+ bool enable) \
+{ \
+ struct max77693_led *led = \
+ ldev##ID##_to_led(v4l2_flash->flash); \
+ int ret; \
+ \
+ mutex_lock(&led->lock); \
+ \
+ if (enable) \
+ ret = max77693_add_mode(led, MODE_FLASH_EXTERNAL##ID); \
+ else \
+ ret = max77693_clear_mode(led, \
+ MODE_FLASH_EXTERNAL##ID); \
+ \
+ mutex_unlock(&led->lock); \
+ \
+ return ret; \
+}
+#else
+#define MAX77693_LED_FLASH_EXTERNAL_STROBE_SET(ID)
+#endif
+
+#define MAX77693_LED_FLASH_FAULT_GET(ID) \
+static int max77693_led##ID##_flash_fault_get( \
+ struct led_classdev_flash *flash, \
+ u32 *fault) \
+{ \
+ struct max77693_led *led = ldev##ID##_to_led(flash); \
+ unsigned int v; \
+ int ret; \
+ \
+ ret = max77693_int_flag_get(led, &v); \
+ if (ret < 0) \
+ return ret; \
+ \
+ *fault = 0; \
+ \
+ if (v & FLASH_INT_FLED##ID##_OPEN) \
+ *fault |= LED_FAULT_OVER_VOLTAGE; \
+ if (v & FLASH_INT_FLED##ID##_SHORT) \
+ *fault |= LED_FAULT_SHORT_CIRCUIT; \
+ if (v & FLASH_INT_OVER_CURRENT) \
+ *fault |= LED_FAULT_OVER_CURRENT; \
+ \
+ return 0; \
+}
+
+#define MAX77693_LED_FLASH_STROBE_GET(ID) \
+static int max77693_led##ID##_flash_strobe_get( \
+ struct led_classdev_flash *flash, \
+ bool *state) \
+{ \
+ struct max77693_led *led = ldev##ID##_to_led(flash); \
+ int ret; \
+ \
+ if (!state) \
+ return -EINVAL; \
+ \
+ mutex_lock(&led->lock); \
+ \
+ ret = max77693_strobe_status_get(led, state); \
+ \
+ *state = !!(*state && led->strobing_sub_led_id == ID); \
+ \
+ mutex_unlock(&led->lock); \
+ \
+ return ret; \
+}
+
+#define MAX77693_LED_FLASH_TIMEOUT_SET(ID) \
+static int max77693_led##ID##_flash_timeout_set( \
+ struct led_classdev_flash *flash, \
+ u32 timeout) \
+{ \
+ struct max77693_led *led = ldev##ID##_to_led(flash); \
+ struct max77693_sub_led *sub_leds = led->sub_leds; \
+ \
+ mutex_lock(&led->lock); \
+ sub_leds[FLED##ID].flash_timeout = timeout; \
+ mutex_unlock(&led->lock); \
+ \
+ return 0; \
+}
+
+MAX77693_LED_BRIGHTNESS_SET(1)
+MAX77693_LED_BRIGHTNESS_SET_WORK(1)
+MAX77693_LED_TORCH_BRIGHTNESS_SET(1)
+MAX77693_LED_FLASH_BRIGHTNESS_SET(1)
+MAX77693_LED_FLASH_STROBE_SET(1)
+MAX77693_LED_FLASH_STROBE_GET(1)
+MAX77693_LED_FLASH_EXTERNAL_STROBE_SET(1)
+MAX77693_LED_FLASH_TIMEOUT_SET(1)
+MAX77693_LED_FLASH_FAULT_GET(1)
+
+MAX77693_LED_BRIGHTNESS_SET(2)
+MAX77693_LED_BRIGHTNESS_SET_WORK(2)
+MAX77693_LED_TORCH_BRIGHTNESS_SET(2)
+MAX77693_LED_FLASH_BRIGHTNESS_SET(2)
+MAX77693_LED_FLASH_STROBE_SET(2)
+MAX77693_LED_FLASH_STROBE_GET(2)
+MAX77693_LED_FLASH_EXTERNAL_STROBE_SET(2)
+MAX77693_LED_FLASH_TIMEOUT_SET(2)
+MAX77693_LED_FLASH_FAULT_GET(2)
+
+static int max77693_led_parse_dt(struct max77693_led *led,
+ struct device_node *node)
+{
+ struct max77693_led_platform_data *p = led->pdata;
+ struct device *dev = &led->pdev->dev;
+ struct device_node *child_node;
+ u32 fled_id;
+ int ret;
+
+ of_property_read_u32_array(node, "maxim,fleds", p->fleds, 2);
+ of_property_read_u32_array(node, "maxim,trigger", p->trigger, 2);
+ of_property_read_u32_array(node, "maxim,trigger-type", p->trigger_type,
+ 2);
+ of_property_read_u32(node, "maxim,boost-mode", &p->boost_mode);
+ of_property_read_u32(node, "maxim,boost-vout", &p->boost_vout);
+ of_property_read_u32(node, "maxim,vsys-min", &p->low_vsys);
+
+ for_each_available_child_of_node(node, child_node) {
+ ret = of_property_read_u32(child_node, "maxim,fled_id",
+ &fled_id);
+ if (ret < 0) {
+ dev_err(dev, "Error reading \"fled_id\" DT property\n");
+ return ret;
+ }
+
+ fled_id = clamp_val(fled_id, 1, 2);
+ --fled_id;
+
+ ret = of_property_read_string(child_node, "label",
+ (const char **) &p->label[fled_id]);
+ if (ret < 0) {
+ dev_err(dev, "Error reading \"label\" DT property\n");
+ return ret;
+ }
+
+ of_property_read_u32(child_node, "max-microamp",
+ &p->iout_torch[fled_id]);
+ of_property_read_u32(child_node, "flash-max-microamp",
+ &p->iout_flash[fled_id]);
+ of_property_read_u32(child_node, "flash-timeout-microsec",
+ &p->flash_timeout[fled_id]);
+ if (++p->num_leds == 2)
+ break;
+ }
+
+ return 0;
+}
+
+static void clamp_align(u32 *v, u32 min, u32 max, u32 step)
+{
+ *v = clamp_val(*v, min, max);
+ if (step > 1)
+ *v = (*v - min) / step * step + min;
+}
+
+static void max77693_led_validate_platform_data(
+ struct max77693_led_platform_data *p)
+{
+ u32 max;
+ int i;
+
+ p->boost_mode = clamp_val(p->boost_mode, MAX77693_LED_BOOST_NONE,
+ MAX77693_LED_BOOST_FIXED);
+
+ for (i = 0; i < ARRAY_SIZE(p->fleds); ++i)
+ p->fleds[i] = clamp_val(p->fleds[i], 0, 1);
+
+ /* Ensure fleds configuration is sane */
+ if (!p->fleds[FLED1] && !p->fleds[FLED2]) {
+ p->fleds[FLED1] = p->fleds[FLED2] = 1;
+ p->num_leds = 1;
+ }
+
+ /* Ensure num_leds is consistent with fleds configuration */
+ if ((!p->fleds[FLED1] || !p->fleds[FLED2]) && p->num_leds == 2)
+ p->num_leds = 1;
+
+ /*
+ * boost must be enabled if current outputs
+ * are connected to separate leds.
+ */
+ if ((p->num_leds == 2 || (p->fleds[FLED1] && p->fleds[FLED2])) &&
+ p->boost_mode == MAX77693_LED_BOOST_NONE)
+ p->boost_mode = MAX77693_LED_BOOST_FIXED;
+
+ max = p->boost_mode ? FLASH_IOUT_MAX_2LEDS : FLASH_IOUT_MAX_1LED;
+
+ if (p->fleds[FLED1]) {
+ clamp_align(&p->iout_torch[FLED1], TORCH_IOUT_MIN,
+ TORCH_IOUT_MAX, TORCH_IOUT_STEP);
+ clamp_align(&p->iout_flash[FLED1], FLASH_IOUT_MIN, max,
+ FLASH_IOUT_STEP);
+ } else {
+ p->iout_torch[FLED1] = p->iout_flash[FLED1] = 0;
+ }
+ if (p->fleds[FLED2]) {
+ clamp_align(&p->iout_torch[FLED2], TORCH_IOUT_MIN,
+ TORCH_IOUT_MAX, TORCH_IOUT_STEP);
+ clamp_align(&p->iout_flash[FLED2], FLASH_IOUT_MIN, max,
+ FLASH_IOUT_STEP);
+ } else {
+ p->iout_torch[FLED2] = p->iout_flash[FLED2] = 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(p->trigger); ++i)
+ p->trigger[i] = clamp_val(p->trigger[i], 0, 7);
+ for (i = 0; i < ARRAY_SIZE(p->trigger_type); ++i)
+ p->trigger_type[i] = clamp_val(p->trigger_type[i],
+ MAX77693_LED_TRIG_TYPE_EDGE,
+ MAX77693_LED_TRIG_TYPE_LEVEL);
+
+ for (i = 0; i < ARRAY_SIZE(p->flash_timeout); ++i)
+ clamp_align(&p->flash_timeout[i], FLASH_TIMEOUT_MIN,
+ FLASH_TIMEOUT_MAX, FLASH_TIMEOUT_STEP);
+
+ clamp_align(&p->boost_vout, FLASH_VOUT_MIN, FLASH_VOUT_MAX,
+ FLASH_VOUT_STEP);
+
+ if (p->low_vsys)
+ clamp_align(&p->low_vsys, MAX_FLASH1_VSYS_MIN,
+ MAX_FLASH1_VSYS_MAX, MAX_FLASH1_VSYS_STEP);
+}
+
+static int max77693_led_get_platform_data(struct max77693_led *led)
+{
+ struct device *dev = &led->pdev->dev;
+ int ret;
+
+ if (!dev->of_node)
+ return -EINVAL;
+
+ led->pdata = devm_kzalloc(dev, sizeof(*led->pdata), GFP_KERNEL);
+ if (!led->pdata)
+ return -ENOMEM;
+
+ ret = max77693_led_parse_dt(led, dev->of_node);
+ if (ret < 0)
+ return ret;
+
+ max77693_led_validate_platform_data(led->pdata);
+
+ return 0;
+}
+
+#define MAX77693_LED_INIT_FLASH_OPS(ID) \
+static const struct led_flash_ops flash_ops##ID = { \
+ \
+ .flash_brightness_set = max77693_led##ID##_flash_brightness_set, \
+ .strobe_set = max77693_led##ID##_flash_strobe_set, \
+ .strobe_get = max77693_led##ID##_flash_strobe_get, \
+ .timeout_set = max77693_led##ID##_flash_timeout_set, \
+ .fault_get = max77693_led##ID##_flash_fault_get, \
+}
+
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+#define MAX77693_LED_V4L2_FLASH_OPS(ID) \
+static const struct v4l2_flash_ops v4l2_flash##ID##_ops = { \
+ .external_strobe_set = max77693_led##ID##_external_strobe_set, \
+}
+
+#define MAX77693_LED_GET_V4L2_FLASH_OPS(ID) \
+static inline const struct v4l2_flash_ops *get_v4l2_flash##ID##_ops(void) \
+{ \
+ return &v4l2_flash##ID##_ops; \
+}
+#else
+#define MAX77693_LED_V4L2_FLASH_OPS(ID)
+
+#define MAX77693_LED_GET_V4L2_FLASH_OPS(ID) \
+static inline const struct v4l2_flash_ops *get_v4l2_flash##ID##_ops(void) \
+{ \
+ return NULL; \
+}
+#endif
+
+MAX77693_LED_INIT_FLASH_OPS(1);
+MAX77693_LED_INIT_FLASH_OPS(2);
+
+MAX77693_LED_V4L2_FLASH_OPS(1);
+MAX77693_LED_V4L2_FLASH_OPS(2);
+MAX77693_LED_GET_V4L2_FLASH_OPS(1);
+MAX77693_LED_GET_V4L2_FLASH_OPS(2);
+
+static void max77693_init_flash_settings(struct max77693_led *led,
+ struct max77693_led_settings *s,
+ int led_id)
+{
+ struct max77693_led_platform_data *p = led->pdata;
+ struct led_flash_setting *setting;
+
+ /* Init torch intensity setting */
+ setting = &s->torch_brightness;
+ setting->min = led->iout_joint ? TORCH_IOUT_MIN * 2 :
+ TORCH_IOUT_MIN;
+ setting->max = led->iout_joint ?
+ p->iout_torch[FLED1] + p->iout_torch[FLED2] :
+ p->iout_torch[led_id];
+ setting->step = TORCH_IOUT_STEP;
+ setting->val = setting->max;
+
+ /* Init flash intensity setting */
+ setting = &s->flash_brightness;
+ setting->min = led->iout_joint ? FLASH_IOUT_MIN * 2 :
+ FLASH_IOUT_MIN;
+ setting->max = led->iout_joint ?
+ p->iout_flash[FLED1] + p->iout_flash[FLED2] :
+ p->iout_flash[led_id];
+ setting->step = FLASH_IOUT_STEP;
+ setting->val = setting->max;
+
+ /* Init flash timeout setting */
+ setting = &s->flash_timeout;
+ setting->min = FLASH_TIMEOUT_MIN;
+ setting->max = p->flash_timeout[led_id];
+ setting->step = FLASH_TIMEOUT_STEP;
+ setting->val = setting->max;
+}
+
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+static void max77693_init_v4l2_ctrl_config(struct max77693_led_settings *s,
+ struct max77693_led_platform_data *p,
+ struct v4l2_flash_ctrl_config *config,
+ int led_id)
+{
+ struct led_flash_setting *setting;
+ struct v4l2_ctrl_config *c;
+
+ c = &config->torch_intensity;
+ setting = &s->torch_brightness;
+ c->min = setting->min;
+ c->max = setting->max;
+ c->step = setting->step;
+ c->def = setting->val;
+
+ c = &config->flash_intensity;
+ setting = &s->flash_brightness;
+ c->min = setting->min;
+ c->max = setting->max;
+ c->step = setting->step;
+ c->def = setting->val;
+
+ c = &config->flash_timeout;
+ setting = &s->flash_timeout;
+ c->min = setting->min;
+ c->max = setting->max;
+ c->step = setting->step;
+ c->def = setting->val;
+
+ /* Init flash faults config */
+ config->flash_faults = V4L2_FLASH_FAULT_OVER_VOLTAGE |
+ V4L2_FLASH_FAULT_SHORT_CIRCUIT |
+ V4L2_FLASH_FAULT_OVER_CURRENT;
+
+ config->has_external_strobe =
+ !!(p->trigger[led_id] & MAX77693_LED_TRIG_FLASH);
+}
+#else
+#define max77693_init_v4l2_ctrl_config(s, p, config, led_id)
+#endif
+
+static int max77693_register_led(struct max77693_led *led, int id)
+{
+ struct platform_device *pdev = led->pdev;
+ struct led_classdev_flash *flash;
+ struct led_classdev *led_cdev;
+ struct max77693_sub_led *sub_leds = led->sub_leds;
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+ struct v4l2_flash_ctrl_config v4l2_flash_config;
+#endif
+ const struct v4l2_flash_ops *v4l2_flash_ops = NULL;
+ struct max77693_led_settings settings;
+ int ret;
+
+ flash = &sub_leds[id].ldev;
+
+ /* Init flash settings */
+ max77693_init_flash_settings(led, &settings, id);
+ /* Init V4L2 Flash controls basing on initialized settings */
+ max77693_init_v4l2_ctrl_config(&settings, led->pdata,
+ &v4l2_flash_config, id);
+
+ /* Init led class */
+ led_cdev = &flash->led_cdev;
+
+ if (id == FLED1) {
+ led_cdev->name = led->pdata->label[FLED1];
+ led_cdev->brightness_set = max77693_led1_brightness_set;
+ led_cdev->brightness_set_sync =
+ max77693_led1_torch_brightness_set;
+ INIT_WORK(&sub_leds[id].work_brightness_set,
+ max77693_led1_brightness_set_work);
+ flash->ops = &flash_ops1;
+ v4l2_flash_ops = get_v4l2_flash1_ops();
+
+ } else {
+ led_cdev->name = led->pdata->label[FLED2];
+ led_cdev->brightness_set = max77693_led2_brightness_set;
+ led_cdev->brightness_set_sync =
+ max77693_led2_torch_brightness_set;
+ INIT_WORK(&sub_leds[id].work_brightness_set,
+ max77693_led2_brightness_set_work);
+ flash->ops = &flash_ops2;
+ v4l2_flash_ops = get_v4l2_flash2_ops();
+ }
+
+ led_cdev->max_brightness = settings.torch_brightness.val /
+ TORCH_IOUT_STEP;
+ led_cdev->flags |= LED_DEV_CAP_FLASH;
+ if (led->pdata->num_leds == 2)
+ led_cdev->flags |= LED_DEV_CAP_COMPOUND;
+
+ flash->brightness = settings.flash_brightness;
+ flash->timeout = settings.flash_timeout;
+ sub_leds[id].flash_timeout = flash->timeout.val;
+
+ /* Register in the LED subsystem. */
+ ret = led_classdev_flash_register(&pdev->dev, flash);
+ if (ret < 0)
+ return ret;
+
+ sub_leds[id].v4l2_flash =
+ v4l2_flash_init(flash,
+ v4l2_flash_ops,
+ &v4l2_flash_config);
+
+ if (IS_ERR(sub_leds[id].v4l2_flash)) {
+ ret = PTR_ERR(sub_leds[id].v4l2_flash);
+ goto err_v4l2_flash_init;
+ }
+
+ return 0;
+
+err_v4l2_flash_init:
+ led_classdev_flash_unregister(flash);
+
+ return ret;
+}
+
+static int max77693_led_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct max77693_dev *iodev = dev_get_drvdata(dev->parent);
+ struct max77693_led *led;
+ struct max77693_led_platform_data *p;
+ struct max77693_sub_led *sub_leds;
+ int ret;
+
+ led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->pdev = pdev;
+ led->regmap = iodev->regmap;
+ sub_leds = led->sub_leds;
+ platform_set_drvdata(pdev, led);
+ ret = max77693_led_get_platform_data(led);
+ if (ret < 0)
+ return -EINVAL;
+
+ p = led->pdata;
+ mutex_init(&led->lock);
+
+ if (p->num_leds == 1 && p->fleds[FLED1] && p->fleds[FLED2])
+ led->iout_joint = true;
+
+ ret = max77693_setup(led);
+ if (ret < 0)
+ goto err_setup;
+
+ if (led->iout_joint || p->fleds[FLED1]) {
+ ret = max77693_register_led(led, FLED1);
+ if (ret < 0)
+ goto err_setup;
+ }
+
+ if (!led->iout_joint && p->fleds[FLED2]) {
+ ret = max77693_register_led(led, FLED2);
+ if (ret < 0)
+ goto err_register_led2;
+ }
+
+ return 0;
+
+err_register_led2:
+ if (!p->fleds[FLED1])
+ goto err_setup;
+ v4l2_flash_release(sub_leds[FLED1].v4l2_flash);
+ led_classdev_flash_unregister(&sub_leds[FLED1].ldev);
+err_setup:
+ mutex_destroy(&led->lock);
+
+ return ret;
+}
+
+static int max77693_led_remove(struct platform_device *pdev)
+{
+ struct max77693_led *led = platform_get_drvdata(pdev);
+ struct max77693_led_platform_data *p = led->pdata;
+ struct max77693_sub_led *sub_leds = led->sub_leds;
+
+ if (led->iout_joint || p->fleds[FLED1]) {
+ v4l2_flash_release(sub_leds[FLED1].v4l2_flash);
+ led_classdev_flash_unregister(&sub_leds[FLED1].ldev);
+ cancel_work_sync(&sub_leds[FLED1].work_brightness_set);
+ }
+
+ if (!led->iout_joint && p->fleds[FLED2]) {
+ v4l2_flash_release(sub_leds[FLED2].v4l2_flash);
+ led_classdev_flash_unregister(&sub_leds[FLED2].ldev);
+ cancel_work_sync(&sub_leds[FLED2].work_brightness_set);
+ }
+
+ mutex_destroy(&led->lock);
+
+ return 0;
+}
+
+static struct of_device_id max77693_led_dt_match[] = {
+ {.compatible = "maxim,max77693-flash"},
+ {},
+};
+
+static struct platform_driver max77693_led_driver = {
+ .probe = max77693_led_probe,
+ .remove = max77693_led_remove,
+ .driver = {
+ .name = "max77693-flash",
+ .owner = THIS_MODULE,
+ .of_match_table = max77693_led_dt_match,
+ },
+};
+
+module_platform_driver(max77693_led_driver);
+
+MODULE_AUTHOR("Jacek Anaszewski <[email protected]>");
+MODULE_AUTHOR("Andrzej Hajda <[email protected]>");
+MODULE_DESCRIPTION("Maxim MAX77693 led flash driver");
+MODULE_LICENSE("GPL");
--
1.7.9.5
This patch adds device tree binding documentation for
the flash cell of the Maxim max77693 multifunctional device.
Signed-off-by: Jacek Anaszewski <[email protected]>
Signed-off-by: Andrzej Hajda <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Lee Jones <[email protected]>
Cc: SangYoung Son <[email protected]>
Cc: Samuel Ortiz <[email protected]>
Cc: Bryan Wu <[email protected]>
Cc: Richard Purdie <[email protected]>
Cc: Rob Herring <[email protected]>
Cc: Pawel Moll <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: Ian Campbell <[email protected]>
Cc: Kumar Gala <[email protected]>
Cc: <[email protected]>
---
Documentation/devicetree/bindings/mfd/max77693.txt | 74 ++++++++++++++++++++
1 file changed, 74 insertions(+)
diff --git a/Documentation/devicetree/bindings/mfd/max77693.txt b/Documentation/devicetree/bindings/mfd/max77693.txt
index 01e9f30..50a8dad 100644
--- a/Documentation/devicetree/bindings/mfd/max77693.txt
+++ b/Documentation/devicetree/bindings/mfd/max77693.txt
@@ -41,6 +41,62 @@ Optional properties:
To get more informations, please refer to documentaion.
[*] refer Documentation/devicetree/bindings/pwm/pwm.txt
+- led-flash : the LED submodule device node
+
+There are two led outputs available - fled1 and fled2. Each of them can
+control a separate led or they can be connected together to double
+the maximum current for a single connected led. One led is represented
+by one child node.
+
+Required properties:
+- compatible : must be "maxim,max77693-flash"
+
+Optional properties:
+- maxim,fleds : array of current outputs in order: fled1, fled2
+ Note: both current outputs can be connected to a single led
+ Possible values:
+ 0 - the output is left disconnected,
+ 1 - a diode is connected to the output.
+- maxim,trigger-type : Array of trigger types in order: flash, torch
+ Possible trigger types:
+ 0 - Rising edge of the signal triggers the flash/torch,
+ 1 - Signal level controls duration of the flash/torch.
+- maxim,trigger : Array of flags indicating which trigger can activate given led
+ in order: fled1, fled2
+ Possible flag values (can be combined):
+ 1 - FLASH pin of the chip,
+ 2 - TORCH pin of the chip,
+ 4 - software via I2C command.
+- maxim,boost-mode :
+ In boost mode the device can produce up to 1.2A of total current
+ on both outputs. The maximum current on each output is reduced
+ to 625mA then. If there are two child led nodes defined then boost
+ is enabled by default.
+ Possible values:
+ 0 - no boost,
+ 1 - adaptive mode,
+ 2 - fixed mode.
+- maxim,boost-vout : Output voltage of the boost module in millivolts.
+- maxim,vsys-min : Low input voltage level in millivolts. Flash is not fired
+ if chip estimates that system voltage could drop below this level due
+ to flash power consumption.
+
+A child node must be defined per sub-led.
+
+Required properties of the LED child node:
+- label : see Documentation/devicetree/bindings/leds/common.txt
+- maxim,fled_id : identifier of the fled output the led is connected to:
+ 1 - FLED1,
+ 2 - FLED2.
+
+Optional properties of the LED child node:
+- max-microamp : see Documentation/devicetree/bindings/leds/common.txt
+ Range: 15625 - 250000
+- flash-max-microamp : see Documentation/devicetree/bindings/leds/common.txt
+ Range: 15625 - 1000000
+- flash-timeout-microsec : see Documentation/devicetree/bindings/leds/common.txt
+ Range: 62500 - 1000000
+
Example:
max77693@66 {
compatible = "maxim,max77693";
@@ -73,4 +129,22 @@ Example:
pwms = <&pwm 0 40000 0>;
pwm-names = "haptic";
};
+
+ led_flash: led-flash {
+ compatible = "maxim,max77693-flash";
+ maxim,fleds = <1 0>;
+ maxim,trigger = <7 0>;
+ maxim,trigger-type = <0 1>;
+ maxim,boost-mode = <0>;
+ maxim,boost-vout = <5000>;
+ maxim,vsys-min = <2400>;
+
+ camera-flash {
+ maxim,fled_id = <1>
+ label = "max77693-flash";
+ max-microamp = <250000>;
+ flash-max-microamp = <1000000>;
+ flash-timeout-microsec = <1000000>;
+ }
+ };
};
--
1.7.9.5
Use "skyworks" as the vendor prefix for the
Skyworks Solutions, Inc.
Signed-off-by: Jacek Anaszewski <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Rob Herring <[email protected]>
Cc: Pawel Moll <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: Ian Campbell <[email protected]>
Cc: Kumar Gala <[email protected]>
Cc: <[email protected]>
---
.../devicetree/bindings/vendor-prefixes.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index c177cd7..3006825 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -137,6 +137,7 @@ ricoh Ricoh Co. Ltd.
rockchip Fuzhou Rockchip Electronics Co., Ltd
samsung Samsung Semiconductor
sandisk Sandisk Corporation
+skyworks Skyworks Solutions, Inc.
sbs Smart Battery System
schindler Schindler
seagate Seagate Technology PLC
--
1.7.9.5
This patch adds device tree binding documentation for
1.5A Step-Up Current Regulator for Flash LEDs.
Signed-off-by: Jacek Anaszewski <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Bryan Wu <[email protected]>
Cc: Richard Purdie <[email protected]>
Cc: Rob Herring <[email protected]>
Cc: Pawel Moll <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: Ian Campbell <[email protected]>
Cc: Kumar Gala <[email protected]>
Cc: <[email protected]>
---
.../devicetree/bindings/leds/leds-aat1290.txt | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/leds-aat1290.txt
diff --git a/Documentation/devicetree/bindings/leds/leds-aat1290.txt b/Documentation/devicetree/bindings/leds/leds-aat1290.txt
new file mode 100644
index 0000000..8f001dd
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-aat1290.txt
@@ -0,0 +1,17 @@
+* Skyworks Solutions, Inc. AAT1290 Current Regulator for Flash LEDs
+
+Required properties:
+
+- compatible : should be "skyworks,aat1290"
+- gpios : two gpio pins in order FLEN, EN/SET
+- flash-timeout-microsec : maximum flash timeout in microseconds -
+ it can be calculated using following formula:
+ T = 8.82 * 10^9 * Ct
+
+Example:
+
+flash_led: flash-led {
+ compatible = "skyworks,aat1290";
+ gpios = <&gpj1 1 0>, <&gpj1 2 0>;
+ flash-timeout-microsec = <1940000>;
+}
--
1.7.9.5
This patch adds a driver for the 1.5A Step-Up
Current Regulator for Flash LEDs. The device is
programmed through a Skyworks proprietary AS2Cwire
serial digital interface.
Signed-off-by: Jacek Anaszewski <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Bryan Wu <[email protected]>
Cc: Richard Purdie <[email protected]>
---
drivers/leds/Kconfig | 7 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-aat1290.c | 472 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 480 insertions(+)
create mode 100644 drivers/leds/leds-aat1290.c
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 2e66d55..ec4b78c 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -39,6 +39,13 @@ config LEDS_88PM860X
This option enables support for on-chip LED drivers found on Marvell
Semiconductor 88PM8606 PMIC.
+config LEDS_AAT1290
+ tristate "LED support for the AAT1290"
+ depends on LEDS_CLASS_FLASH
+ depends on OF
+ help
+ This option enables support for the LEDs on the AAT1290.
+
config LEDS_LM3530
tristate "LCD Backlight driver for LM3530"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 57ca62b..b802251 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
# LED Platform Drivers
obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o
+obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o
obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o
diff --git a/drivers/leds/leds-aat1290.c b/drivers/leds/leds-aat1290.c
new file mode 100644
index 0000000..2f8bd99
--- /dev/null
+++ b/drivers/leds/leds-aat1290.c
@@ -0,0 +1,472 @@
+/*
+ * LED Flash class driver for the AAT1290
+ * 1.5A Step-Up Current Regulator for Flash LEDs
+ *
+ * Copyright (C) 2014, Samsung Electronics Co., Ltd.
+ * Author: Jacek Anaszewski <[email protected]>
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/led-class-flash.h>
+#include <linux/leds.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <media/v4l2-flash.h>
+#include <linux/workqueue.h>
+
+#define AAT1290_MOVIE_MODE_CURRENT_ADDR 17
+#define AAT1290_FLASH_SAFETY_TIMER_ADDR 18
+#define AAT1290_MOVIE_MODE_CONFIG_ADDR 19
+#define AAT1290_MM_CURRENT_RATIO_ADDR 20
+#define AAT1290_LATCH_TIME_US 500
+#define AAT1290_EN_SET_TICK_TIME_US 1
+#define AAT1290_MOVIE_MODE_OFF 1
+#define AAT1290_MOVIE_MODE_ON 3
+#define AAT1290_MAX_MM_CURR_PERCENT_0 16
+#define AAT1290_MAX_MM_CURR_PERCENT_100 1
+#define AAT1290_FLASH_TM_NUM_LEVELS 16
+
+#define AAT1290_MM_TO_FL_1_92 1
+#define AAT1290_MM_TO_FL_3_7 2
+#define AAT1290_MM_TO_FL_5_5 3
+#define AAT1290_MM_TO_FL_7_3 4
+#define AAT1290_MM_TO_FL_9 5
+#define AAT1290_MM_TO_FL_10_7 6
+#define AAT1290_MM_TO_FL_12_4 7
+#define AAT1290_MM_TO_FL_14 8
+#define AAT1290_MM_TO_FL_15_9 9
+#define AAT1290_MM_TO_FL_17_5 10
+#define AAT1290_MM_TO_FL_19_1 11
+#define AAT1290_MM_TO_FL_20_8 12
+#define AAT1290_MM_TO_FL_22_4 13
+#define AAT1290_MM_TO_FL_24 14
+#define AAT1290_MM_TO_FL_25_6 15
+#define AAT1290_MM_TO_FL_OFF 16
+
+struct aat1290_led_settings {
+ struct led_flash_setting torch_brightness;
+ struct led_flash_setting flash_brightness;
+ struct led_flash_setting flash_timeout;
+};
+
+struct aat1290_led {
+ struct platform_device *pdev;
+ struct mutex lock;
+
+ struct led_classdev_flash ldev;
+ struct v4l2_flash *v4l2_flash;
+
+ int flen_gpio;
+ int en_set_gpio;
+
+ u32 max_flash_tm;
+ bool movie_mode;
+
+ char *label;
+ unsigned int torch_brightness;
+ unsigned int flash_timeout;
+ struct work_struct work_brightness_set;
+};
+
+static struct aat1290_led *ldev_to_led(struct led_classdev_flash *ldev)
+{
+ return container_of(ldev, struct aat1290_led, ldev);
+}
+
+static void aat1290_as2cwire_write(struct aat1290_led *led, int addr, int value)
+{
+ int i;
+
+ gpio_set_value(led->flen_gpio, 0);
+ gpio_set_value(led->en_set_gpio, 0);
+
+ udelay(10);
+
+ /* write address */
+ for (i = 0; i < addr; ++i) {
+ udelay(AAT1290_EN_SET_TICK_TIME_US);
+ gpio_set_value(led->en_set_gpio, 0);
+ udelay(AAT1290_EN_SET_TICK_TIME_US);
+ gpio_set_value(led->en_set_gpio, 1);
+ }
+
+ udelay(AAT1290_LATCH_TIME_US);
+
+ /* write data */
+ for (i = 0; i < value; ++i) {
+ udelay(AAT1290_EN_SET_TICK_TIME_US);
+ gpio_set_value(led->en_set_gpio, 0);
+ udelay(AAT1290_EN_SET_TICK_TIME_US);
+ gpio_set_value(led->en_set_gpio, 1);
+ }
+
+ udelay(AAT1290_LATCH_TIME_US);
+}
+
+static void aat1290_set_flash_safety_timer(struct aat1290_led *led,
+ unsigned int micro_sec)
+{
+ struct led_classdev_flash *flash = &led->ldev;
+ struct led_flash_setting *flash_tm = &flash->timeout;
+ int flash_tm_reg = AAT1290_FLASH_TM_NUM_LEVELS -
+ (micro_sec / flash_tm->step) + 1;
+
+ aat1290_as2cwire_write(led, AAT1290_FLASH_SAFETY_TIMER_ADDR,
+ flash_tm_reg);
+}
+
+static void aat1290_brightness_set(struct aat1290_led *led,
+ enum led_brightness brightness)
+{
+ mutex_lock(&led->lock);
+
+ if (brightness == 0) {
+ gpio_set_value(led->flen_gpio, 0);
+ gpio_set_value(led->en_set_gpio, 0);
+ goto unlock;
+ }
+
+ if (!led->movie_mode) {
+ aat1290_as2cwire_write(led, AAT1290_MM_CURRENT_RATIO_ADDR,
+ AAT1290_MM_TO_FL_1_92);
+ led->movie_mode = true;
+ }
+
+ aat1290_as2cwire_write(led, AAT1290_MOVIE_MODE_CURRENT_ADDR,
+ AAT1290_MAX_MM_CURR_PERCENT_0 - brightness);
+ aat1290_as2cwire_write(led, AAT1290_MOVIE_MODE_CONFIG_ADDR,
+ AAT1290_MOVIE_MODE_ON);
+unlock:
+ mutex_unlock(&led->lock);
+}
+
+/* LED subsystem callbacks */
+
+static void aat1290_brightness_set_work(struct work_struct *work)
+{
+ struct aat1290_led *led =
+ container_of(work, struct aat1290_led, work_brightness_set);
+
+ aat1290_brightness_set(led, led->torch_brightness);
+}
+
+static void aat1290_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+ struct aat1290_led *led = ldev_to_led(flash);
+
+ led->torch_brightness = brightness;
+ schedule_work(&led->work_brightness_set);
+}
+
+static int aat1290_torch_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+ struct aat1290_led *led = ldev_to_led(flash);
+
+ aat1290_brightness_set(led, brightness);
+
+ return 0;
+}
+
+static int aat1290_led_flash_strobe_set(struct led_classdev_flash *flash,
+ bool state)
+
+{
+ struct aat1290_led *led = ldev_to_led(flash);
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ struct led_flash_setting *timeout = &flash->timeout;
+
+ mutex_lock(&led->lock);
+
+ if (state == 0) {
+ gpio_set_value(led->flen_gpio, 0);
+ gpio_set_value(led->en_set_gpio, 0);
+ goto unlock;
+ }
+
+ aat1290_set_flash_safety_timer(led, timeout->val);
+
+ /*
+ * To reenter movie mode after a flash event the part
+ * must be cycled off and back on to reset the movie
+ * mode and reprogrammed via the AS2Cwire. Therefore
+ * the brightness value needs to be updated here to
+ * reflect the actual state.
+ */
+ led_cdev->brightness = 0;
+ led->movie_mode = false;
+
+ gpio_set_value(led->flen_gpio, 1);
+
+unlock:
+ mutex_unlock(&led->lock);
+
+ return 0;
+}
+
+static int aat1290_led_flash_timeout_set(struct led_classdev_flash *flash,
+ u32 timeout)
+{
+ /*
+ * Don't do anything - flash timeout is cached in the led-class-flash
+ * core and will be applied in the strobe_set op, as writing the
+ * safety timer register spuriously turns the torch mode on.
+ */
+
+ return 0;
+}
+
+static int aat1290_led_parse_dt(struct aat1290_led *led,
+ struct device *dev)
+{
+ int ret;
+ char *pname = "label";
+
+ ret = of_property_read_string(dev->of_node, pname,
+ (const char **) &led->label);
+ if (ret < 0) {
+ dev_err(dev, "Error reading %s Device Tree property (%d)\n",
+ pname, ret);
+ return ret;
+ }
+
+ pname = "flash-timeout-microsec";
+
+ ret = of_property_read_u32(dev->of_node, pname, &led->max_flash_tm);
+ if (ret) {
+ dev_err(dev, "Error reading %s Device Tree property (%d)\n",
+ pname, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void aat1290_init_flash_settings(struct aat1290_led *led,
+ struct aat1290_led_settings *s)
+{
+ struct led_flash_setting *setting;
+
+ /* Init flash intensity setting */
+ setting = &s->torch_brightness;
+ /*
+ * Torch current is adjustable in logarithmic fashion and thus
+ * it is not possible to define fixed step in microamperes.
+ * Instead led brightness levels are used to make possible
+ * setting all the supported levels from V4L2 Flash sub-device.
+ */
+ setting->min = 1;
+ setting->max = AAT1290_MAX_MM_CURR_PERCENT_0 -
+ AAT1290_MAX_MM_CURR_PERCENT_100;
+ setting->step = 1;
+ setting->val = setting->max;
+
+ /* Init flash timeout setting */
+ setting = &s->flash_timeout;
+ setting->min = led->max_flash_tm / AAT1290_FLASH_TM_NUM_LEVELS;
+ setting->max = setting->min * AAT1290_FLASH_TM_NUM_LEVELS;
+ setting->step = setting->min;
+ setting->val = setting->max;
+}
+
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+static void aat1290_init_v4l2_ctrl_config(struct aat1290_led_settings *s,
+ struct v4l2_flash_ctrl_config *config)
+{
+ struct led_flash_setting *setting;
+ struct v4l2_ctrl_config *c;
+
+ c = &config->torch_intensity;
+ setting = &s->torch_brightness;
+ c->min = setting->min;
+ c->max = setting->max;
+ c->step = setting->step;
+ c->def = setting->val;
+
+ c = &config->flash_timeout;
+ setting = &s->flash_timeout;
+ c->min = setting->min;
+ c->max = setting->max;
+ c->step = setting->step;
+ c->def = setting->val;
+
+ config->has_external_strobe = false;
+}
+#else
+#define aat1290_init_v4l2_ctrl_config(s, config)
+#endif
+
+static const struct led_flash_ops flash_ops = {
+ .strobe_set = aat1290_led_flash_strobe_set,
+ .timeout_set = aat1290_led_flash_timeout_set,
+};
+
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+static const struct v4l2_flash_ops v4l2_flash_ops = {
+ .external_strobe_set = NULL,
+};
+
+static const struct v4l2_flash_ops *get_v4l2_flash_ops(void)
+{
+ return &v4l2_flash_ops;
+}
+#else
+#define get_v4l2_flash_ops() (NULL)
+#endif
+
+static int aat1290_led_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *dev_node = pdev->dev.of_node;
+ struct aat1290_led *led;
+ struct led_classdev *led_cdev;
+ struct led_classdev_flash *flash;
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+ struct v4l2_flash_ctrl_config v4l2_flash_config;
+#endif
+ struct aat1290_led_settings settings;
+ int flen_gpio, enset_gpio, ret;
+
+ led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->pdev = pdev;
+ platform_set_drvdata(pdev, led);
+
+ if (!dev_node)
+ return -ENXIO;
+
+ flen_gpio = of_get_gpio(dev_node, 0);
+ if (gpio_is_valid(flen_gpio)) {
+ ret = gpio_request_one(flen_gpio, GPIOF_DIR_OUT,
+ "aat1290_flen");
+ if (ret < 0) {
+ dev_err(dev,
+ "failed to request GPIO %d, error %d\n",
+ flen_gpio, ret);
+ goto error_gpio_flen;
+ }
+ }
+ led->flen_gpio = flen_gpio;
+
+ enset_gpio = of_get_gpio(dev_node, 1);
+ if (gpio_is_valid(enset_gpio)) {
+ ret = gpio_request_one(enset_gpio, GPIOF_DIR_OUT,
+ "aat1290_en_set");
+ if (ret < 0) {
+ dev_err(dev,
+ "failed to request GPIO %d, error %d\n",
+ enset_gpio, ret);
+ goto error_gpio_en_set;
+ }
+ }
+ led->en_set_gpio = enset_gpio;
+
+ ret = aat1290_led_parse_dt(led, &pdev->dev);
+ if (ret < 0)
+ goto error_gpio_en_set;
+
+ mutex_init(&led->lock);
+
+ flash = &led->ldev;
+
+ /* Init flash settings */
+ aat1290_init_flash_settings(led, &settings);
+
+ flash->timeout = settings.flash_timeout;
+
+ /* Init V4L2 Flash controls basing on initialized settings */
+ aat1290_init_v4l2_ctrl_config(&settings, &v4l2_flash_config);
+
+ /* Init led class */
+ led_cdev = &flash->led_cdev;
+ led_cdev->name = led->label;
+ led_cdev->brightness_set = aat1290_led_brightness_set;
+ led_cdev->brightness_set_sync = aat1290_torch_brightness_set;
+ led_cdev->max_brightness = settings.torch_brightness.max;
+ led_cdev->flags |= LED_DEV_CAP_FLASH;
+
+ INIT_WORK(&led->work_brightness_set, aat1290_brightness_set_work);
+
+ flash->ops = &flash_ops;
+
+ /* Register in the LED subsystem. */
+ ret = led_classdev_flash_register(&pdev->dev, flash);
+ if (ret < 0)
+ goto error_gpio_en_set;
+
+ /* Create V4L2 Flash subdev. */
+ led->v4l2_flash = v4l2_flash_init(flash, get_v4l2_flash_ops(),
+ &v4l2_flash_config);
+ if (IS_ERR(led->v4l2_flash)) {
+ ret = PTR_ERR(led->v4l2_flash);
+ goto error_v4l2_flash_init;
+ }
+
+ return ret;
+
+error_v4l2_flash_init:
+ led_classdev_flash_unregister(flash);
+error_gpio_en_set:
+ if (gpio_is_valid(enset_gpio))
+ gpio_free(enset_gpio);
+error_gpio_flen:
+ if (gpio_is_valid(flen_gpio))
+ gpio_free(flen_gpio);
+ mutex_destroy(&led->lock);
+
+ return ret;
+}
+
+static int aat1290_led_remove(struct platform_device *pdev)
+{
+ struct aat1290_led *led = platform_get_drvdata(pdev);
+
+ v4l2_flash_release(led->v4l2_flash);
+ led_classdev_flash_unregister(&led->ldev);
+ cancel_work_sync(&led->work_brightness_set);
+
+ if (gpio_is_valid(led->en_set_gpio))
+ gpio_free(led->en_set_gpio);
+ if (gpio_is_valid(led->flen_gpio))
+ gpio_free(led->flen_gpio);
+
+ mutex_destroy(&led->lock);
+
+ return 0;
+}
+
+static struct of_device_id aat1290_led_dt_match[] = {
+ {.compatible = "skyworks,aat1290"},
+ {},
+};
+
+static struct platform_driver aat1290_led_driver = {
+ .probe = aat1290_led_probe,
+ .remove = aat1290_led_remove,
+ .driver = {
+ .name = "aat1290-led",
+ .owner = THIS_MODULE,
+ .of_match_table = aat1290_led_dt_match,
+ },
+};
+
+module_platform_driver(aat1290_led_driver);
+
+MODULE_AUTHOR("Jacek Anaszewski <[email protected]>");
+MODULE_DESCRIPTION("Skyworks Current Regulator for Flash LEDs");
+MODULE_LICENSE("GPL");
--
1.7.9.5
This patch adds suppport for external v4l2-flash devices.
The support includes parsing "flashes" Device Tree property
and asynchronous subdevice registration.
Signed-off-by: Jacek Anaszewski <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Sylwester Nawrocki <[email protected]>
---
drivers/media/platform/exynos4-is/media-dev.c | 65 ++++++++++++++++++++++++-
drivers/media/platform/exynos4-is/media-dev.h | 13 ++++-
2 files changed, 75 insertions(+), 3 deletions(-)
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index f315ef9..8807730 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -451,6 +451,47 @@ rpm_put:
return ret;
}
+static bool match_subdev_name(struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ return !strcmp(sd->name, (char *) asd->match.custom.priv);
+}
+
+static int fimc_md_register_flash_entities(struct fimc_md *fmd)
+{
+ struct device_node *parent = fmd->pdev->dev.of_node;
+ const char *flash_names[FIMC_MAX_FLASHES];
+ int ret, i;
+
+ ret = of_property_count_strings(parent, "flashes");
+ if (ret < 0) {
+ /* the property may not exist - this is valid state */
+ if (ret == -EINVAL)
+ return 0;
+ v4l2_err(&fmd->v4l2_dev, "Failed to retrieve flashes\n");
+ return -EINVAL;
+ }
+
+ fmd->num_flashes = ret;
+
+ if (fmd->num_flashes > FIMC_MAX_FLASHES) {
+ v4l2_err(&fmd->v4l2_dev, "Too many flash leds declared.\n");
+ return -EINVAL;
+ }
+
+ of_property_read_string_array(parent, "flashes", flash_names,
+ fmd->num_flashes);
+
+ for (i = 0; i < fmd->num_flashes; ++i) {
+ fmd->flash[i].asd.match_type = V4L2_ASYNC_MATCH_CUSTOM;
+ fmd->flash[i].asd.match.custom.match = match_subdev_name;
+ fmd->flash[i].asd.match.custom.priv = (void *) flash_names[i];
+ fmd->async_subdevs[fmd->num_sensors + i] = &fmd->flash[i].asd;
+ }
+
+ return 0;
+}
+
static int __of_get_csis_id(struct device_node *np)
{
u32 reg = 0;
@@ -1275,6 +1316,18 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
struct fimc_sensor_info *si = NULL;
int i;
+ /* Register flash subdev if detected any */
+ for (i = 0; i < ARRAY_SIZE(fmd->flash); i++) {
+ if (!fmd->flash[i].asd.match.custom.priv)
+ continue;
+ if (!strcmp(fmd->flash[i].asd.match.custom.priv,
+ subdev->name)) {
+ fmd->flash[i].subdev = subdev;
+ fmd->num_flashes++;
+ return 0;
+ }
+ }
+
/* Find platform data for this sensor subdev */
for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node)
@@ -1385,6 +1438,12 @@ static int fimc_md_probe(struct platform_device *pdev)
goto err_m_ent;
}
+ ret = fimc_md_register_flash_entities(fmd);
+ if (ret < 0) {
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+ goto err_m_ent;
+ }
+
mutex_unlock(&fmd->media_dev.graph_mutex);
ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
@@ -1401,12 +1460,14 @@ static int fimc_md_probe(struct platform_device *pdev)
goto err_attr;
}
- if (fmd->num_sensors > 0) {
+ if (fmd->num_sensors > 0 || fmd->num_flashes > 0) {
fmd->subdev_notifier.subdevs = fmd->async_subdevs;
- fmd->subdev_notifier.num_subdevs = fmd->num_sensors;
+ fmd->subdev_notifier.num_subdevs = fmd->num_sensors +
+ fmd->num_flashes;
fmd->subdev_notifier.bound = subdev_notifier_bound;
fmd->subdev_notifier.complete = subdev_notifier_complete;
fmd->num_sensors = 0;
+ fmd->num_flashes = 0;
ret = v4l2_async_notifier_register(&fmd->v4l2_dev,
&fmd->subdev_notifier);
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
index 0321454..feff9c8 100644
--- a/drivers/media/platform/exynos4-is/media-dev.h
+++ b/drivers/media/platform/exynos4-is/media-dev.h
@@ -34,6 +34,8 @@
#define FIMC_MAX_SENSORS 4
#define FIMC_MAX_CAMCLKS 2
+#define FIMC_MAX_FLASHES 2
+#define FIMC_MAX_ASYNC_SUBDEVS (FIMC_MAX_SENSORS + FIMC_MAX_FLASHES)
#define DEFAULT_SENSOR_CLK_FREQ 24000000U
/* LCD/ISP Writeback clocks (PIXELASYNCMx) */
@@ -93,6 +95,11 @@ struct fimc_sensor_info {
struct fimc_dev *host;
};
+struct fimc_flash_info {
+ struct v4l2_subdev *subdev;
+ struct v4l2_async_subdev asd;
+};
+
struct cam_clk {
struct clk_hw hw;
struct fimc_md *fmd;
@@ -104,6 +111,8 @@ struct cam_clk {
* @csis: MIPI CSIS subdevs data
* @sensor: array of registered sensor subdevs
* @num_sensors: actual number of registered sensors
+ * @flash: array of registered flash subdevs
+ * @num_flashes: actual number of registered flashes
* @camclk: external sensor clock information
* @fimc: array of registered fimc devices
* @fimc_is: fimc-is data structure
@@ -123,6 +132,8 @@ struct fimc_md {
struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
int num_sensors;
+ struct fimc_flash_info flash[FIMC_MAX_FLASHES];
+ int num_flashes;
struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
struct clk *wbclk[FIMC_MAX_WBCLKS];
struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS];
@@ -149,7 +160,7 @@ struct fimc_md {
} clk_provider;
struct v4l2_async_notifier subdev_notifier;
- struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS];
+ struct v4l2_async_subdev *async_subdevs[FIMC_MAX_ASYNC_SUBDEVS];
bool user_subdev_api;
spinlock_t slock;
--
1.7.9.5
This patch extends LED Flash class documention by
the description of interactions with v4l2-flash sub-device.
Signed-off-by: Jacek Anaszewski <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Bryan Wu <[email protected]>
Cc: Richard Purdie <[email protected]>
---
Documentation/leds/leds-class-flash.txt | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/Documentation/leds/leds-class-flash.txt b/Documentation/leds/leds-class-flash.txt
index d68565c..1a611ec 100644
--- a/Documentation/leds/leds-class-flash.txt
+++ b/Documentation/leds/leds-class-flash.txt
@@ -46,3 +46,16 @@ Following sysfs attributes are exposed for controlling flash led devices:
until this flag is no longer set
* 0x100 - the temperature of the LED has exceeded its allowed
upper limit
+
+A LED subsystem driver can be controlled also from the level of VideoForLinux2
+subsystem. In order to enable this CONFIG_V4L2_FLASH_LED_CLASS symbol has to
+be defined in the kernel config. The driver must call the v4l2_flash_init
+function to get registered in the V4L2 subsystem. On remove the
+v4l2_flash_release function has to be called (see <media/v4l2-flash.h>).
+
+After proper initialization a V4L2 Flash sub-device is created. The sub-device
+exposes a number of V4L2 controls, which allow for controlling a LED Flash class
+device with use of its internal kernel API.
+Opening the V4L2 Flash sub-device makes the LED subsystem sysfs interface
+unavailable. The interface is re-enabled after the V4L2 Flash sub-device
+is closed.
--
1.7.9.5
Some LED devices support two operation modes - torch and flash.
This patch provides support for flash LED devices in the LED subsystem
by introducing new sysfs attributes and kernel internal interface.
The attributes being introduced are: flash_brightness, flash_strobe,
flash_timeout, max_flash_timeout, max_flash_brightness, flash_fault
and flash_sync_strobe. All the flash related features are placed
in a separate module. Torch mode is supported by the LED class
interface.
The modifications aim to be compatible with V4L2 framework requirements
related to the flash devices management. The design assumes that V4L2
sub-device can take of the LED class device control and communicate
with it through the kernel internal interface. When V4L2 Flash sub-device
file is opened, the LED class device sysfs interface is made
unavailable.
Signed-off-by: Jacek Anaszewski <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Bryan Wu <[email protected]>
Cc: Richard Purdie <[email protected]>
---
drivers/leds/Kconfig | 10 +
drivers/leds/Makefile | 1 +
drivers/leds/led-class-flash.c | 446 +++++++++++++++++++++++++++++++++++++++
drivers/leds/led-class.c | 4 +
include/linux/led-class-flash.h | 198 +++++++++++++++++
include/linux/leds.h | 3 +
6 files changed, 662 insertions(+)
create mode 100644 drivers/leds/led-class-flash.c
create mode 100644 include/linux/led-class-flash.h
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index b3c0d8a..fa8021e 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -19,6 +19,16 @@ config LEDS_CLASS
This option enables the led sysfs class in /sys/class/leds. You'll
need this to do anything useful with LEDs. If unsure, say N.
+config LEDS_CLASS_FLASH
+ tristate "LED Flash Class Support"
+ depends on LEDS_CLASS
+ help
+ This option enables the flash led sysfs class in /sys/class/leds.
+ It wrapps LED Class and adds flash LEDs specific sysfs attributes
+ and kernel internal API to it. You'll need this to provide support
+ for the flash related features of a LED device. It can be built
+ as a module.
+
comment "LED drivers"
config LEDS_88PM860X
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 1c65a19..cbba921 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -2,6 +2,7 @@
# LED Core
obj-$(CONFIG_NEW_LEDS) += led-core.o
obj-$(CONFIG_LEDS_CLASS) += led-class.o
+obj-$(CONFIG_LEDS_CLASS_FLASH) += led-class-flash.o
obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
# LED Platform Drivers
diff --git a/drivers/leds/led-class-flash.c b/drivers/leds/led-class-flash.c
new file mode 100644
index 0000000..219b414
--- /dev/null
+++ b/drivers/leds/led-class-flash.c
@@ -0,0 +1,446 @@
+/*
+ * LED Flash class interface
+ *
+ * Copyright (C) 2014 Samsung Electronics Co., Ltd.
+ * Author: Jacek Anaszewski <[email protected]>
+ *
+ * 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.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/led-class-flash.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "leds.h"
+
+#define has_flash_op(flash, op) \
+ (flash && flash->ops->op)
+
+#define call_flash_op(flash, op, args...) \
+ ((has_flash_op(flash, op)) ? \
+ (flash->ops->op(flash, args)) : \
+ -EINVAL)
+
+static ssize_t flash_brightness_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+ unsigned long state;
+ ssize_t ret;
+
+ mutex_lock(&led_cdev->led_access);
+
+ if (led_sysfs_is_disabled(led_cdev)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ ret = kstrtoul(buf, 10, &state);
+ if (ret)
+ goto unlock;
+
+ ret = led_set_flash_brightness(flash, state);
+ if (ret < 0)
+ goto unlock;
+
+ ret = size;
+unlock:
+ mutex_unlock(&led_cdev->led_access);
+ return ret;
+}
+
+static ssize_t flash_brightness_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+
+ /* no lock needed for this */
+ led_update_flash_brightness(flash);
+
+ return sprintf(buf, "%u\n", flash->brightness.val);
+}
+static DEVICE_ATTR_RW(flash_brightness);
+
+static ssize_t max_flash_brightness_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+
+ return sprintf(buf, "%u\n", flash->brightness.max);
+}
+static DEVICE_ATTR_RO(max_flash_brightness);
+
+static ssize_t flash_strobe_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+ unsigned long state;
+ ssize_t ret = -EINVAL;
+
+ mutex_lock(&led_cdev->led_access);
+
+ if (led_sysfs_is_disabled(led_cdev)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ ret = kstrtoul(buf, 10, &state);
+ if (ret)
+ goto unlock;
+
+ if (state < 0 || state > 1) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ret = led_set_flash_strobe(flash, state);
+ if (ret < 0)
+ goto unlock;
+ ret = size;
+unlock:
+ mutex_unlock(&led_cdev->led_access);
+ return ret;
+}
+
+static ssize_t flash_strobe_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+ bool state;
+ int ret;
+
+ /* no lock needed for this */
+ ret = led_get_flash_strobe(flash, &state);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%u\n", state);
+}
+static DEVICE_ATTR_RW(flash_strobe);
+
+static ssize_t flash_timeout_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+ unsigned long flash_timeout;
+ ssize_t ret;
+
+ mutex_lock(&led_cdev->led_access);
+
+ if (led_sysfs_is_disabled(led_cdev)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ ret = kstrtoul(buf, 10, &flash_timeout);
+ if (ret)
+ goto unlock;
+
+ ret = led_set_flash_timeout(flash, flash_timeout);
+ if (ret < 0)
+ goto unlock;
+
+ ret = size;
+unlock:
+ mutex_unlock(&led_cdev->led_access);
+ return ret;
+}
+
+static ssize_t flash_timeout_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+
+ return sprintf(buf, "%u\n", flash->timeout.val);
+}
+static DEVICE_ATTR_RW(flash_timeout);
+
+static ssize_t max_flash_timeout_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+
+ return sprintf(buf, "%u\n", flash->timeout.max);
+}
+static DEVICE_ATTR_RO(max_flash_timeout);
+
+static ssize_t flash_fault_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+ u32 fault;
+ int ret;
+
+ ret = led_get_flash_fault(flash, &fault);
+ if (ret < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "0x%8.8x\n", fault);
+}
+static DEVICE_ATTR_RO(flash_fault);
+
+static ssize_t flash_sync_strobe_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+ unsigned long sync_strobe;
+ ssize_t ret;
+
+ mutex_lock(&led_cdev->led_access);
+
+ if (led_sysfs_is_disabled(led_cdev)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ ret = kstrtoul(buf, 10, &sync_strobe);
+ if (ret)
+ goto unlock;
+
+ flash->sync_strobe = sync_strobe;
+
+ ret = size;
+unlock:
+ mutex_unlock(&led_cdev->led_access);
+ return ret;
+}
+
+static ssize_t flash_sync_strobe_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+
+ return sprintf(buf, "%u\n", flash->sync_strobe);
+}
+static DEVICE_ATTR_RW(flash_sync_strobe);
+
+static struct attribute *led_flash_strobe_attrs[] = {
+ &dev_attr_flash_strobe.attr,
+ NULL,
+};
+
+static struct attribute *led_flash_timeout_attrs[] = {
+ &dev_attr_flash_timeout.attr,
+ &dev_attr_max_flash_timeout.attr,
+ NULL,
+};
+
+static struct attribute *led_flash_brightness_attrs[] = {
+ &dev_attr_flash_brightness.attr,
+ &dev_attr_max_flash_brightness.attr,
+ NULL,
+};
+
+static struct attribute *led_flash_fault_attrs[] = {
+ &dev_attr_flash_fault.attr,
+ NULL,
+};
+
+static struct attribute *led_flash_sync_strobe_attrs[] = {
+ &dev_attr_flash_sync_strobe.attr,
+ NULL,
+};
+
+static const struct attribute_group led_flash_strobe_group = {
+ .attrs = led_flash_strobe_attrs,
+};
+
+static const struct attribute_group led_flash_timeout_group = {
+ .attrs = led_flash_timeout_attrs,
+};
+
+static const struct attribute_group led_flash_brightness_group = {
+ .attrs = led_flash_brightness_attrs,
+};
+
+static const struct attribute_group led_flash_fault_group = {
+ .attrs = led_flash_fault_attrs,
+};
+
+static const struct attribute_group led_flash_sync_strobe_group = {
+ .attrs = led_flash_sync_strobe_attrs,
+};
+
+static const struct attribute_group *flash_groups[] = {
+ &led_flash_strobe_group,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static void led_flash_resume(struct led_classdev *led_cdev)
+{
+ struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+
+ call_flash_op(flash, flash_brightness_set, flash->brightness.val);
+ call_flash_op(flash, timeout_set, flash->timeout.val);
+}
+
+static void led_flash_init_sysfs_groups(struct led_classdev_flash *flash)
+{
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ const struct led_flash_ops *ops = flash->ops;
+ int num_sysfs_groups = 1;
+
+ if (ops->flash_brightness_set)
+ flash_groups[num_sysfs_groups++] = &led_flash_brightness_group;
+
+ if (ops->timeout_set)
+ flash_groups[num_sysfs_groups++] = &led_flash_timeout_group;
+
+ if (ops->fault_get)
+ flash_groups[num_sysfs_groups++] = &led_flash_fault_group;
+
+ if (led_cdev->flags & LED_DEV_CAP_COMPOUND)
+ flash_groups[num_sysfs_groups++] = &led_flash_sync_strobe_group;
+
+ led_cdev->groups = flash_groups;
+}
+
+int led_classdev_flash_register(struct device *parent,
+ struct led_classdev_flash *flash)
+{
+ struct led_classdev *led_cdev;
+ const struct led_flash_ops *ops;
+ int ret;
+
+ if (!flash)
+ return -EINVAL;
+
+ led_cdev = &flash->led_cdev;
+
+ if (led_cdev->flags & LED_DEV_CAP_FLASH) {
+ if (!led_cdev->brightness_set_sync)
+ return -EINVAL;
+
+ ops = flash->ops;
+ if (!ops || !ops->strobe_set)
+ return -EINVAL;
+
+ led_cdev->flash_resume = led_flash_resume;
+
+ /* Select the sysfs attributes to be created for the device */
+ led_flash_init_sysfs_groups(flash);
+ }
+
+ /* Register led class device */
+ ret = led_classdev_register(parent, led_cdev);
+ if (ret < 0)
+ return ret;
+
+ /* Setting a torch brightness needs to have immediate effect */
+ led_cdev->flags &= ~SET_BRIGHTNESS_ASYNC;
+ led_cdev->flags |= SET_BRIGHTNESS_SYNC;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(led_classdev_flash_register);
+
+void led_classdev_flash_unregister(struct led_classdev_flash *flash)
+{
+ if (!flash)
+ return;
+
+ led_classdev_unregister(&flash->led_cdev);
+}
+EXPORT_SYMBOL_GPL(led_classdev_flash_unregister);
+
+int led_set_flash_strobe(struct led_classdev_flash *flash, bool state)
+{
+ return call_flash_op(flash, strobe_set, state);
+}
+EXPORT_SYMBOL_GPL(led_set_flash_strobe);
+
+int led_get_flash_strobe(struct led_classdev_flash *flash, bool *state)
+{
+ return call_flash_op(flash, strobe_get, state);
+}
+EXPORT_SYMBOL_GPL(led_get_flash_strobe);
+
+static void led_clamp_align(struct led_flash_setting *s)
+{
+ u32 v, offset;
+
+ v = s->val + s->step / 2;
+ v = clamp(v, s->min, s->max);
+ offset = v - s->min;
+ offset = s->step * (offset / s->step);
+ s->val = s->min + offset;
+}
+
+int led_set_flash_timeout(struct led_classdev_flash *flash, u32 timeout)
+{
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ struct led_flash_setting *s = &flash->timeout;
+
+ s->val = timeout;
+ led_clamp_align(s);
+
+ if (!(led_cdev->flags & LED_SUSPENDED))
+ return call_flash_op(flash, timeout_set, s->val);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(led_set_flash_timeout);
+
+int led_get_flash_fault(struct led_classdev_flash *flash, u32 *fault)
+{
+ return call_flash_op(flash, fault_get, fault);
+}
+EXPORT_SYMBOL_GPL(led_get_flash_fault);
+
+int led_set_flash_brightness(struct led_classdev_flash *flash,
+ u32 brightness)
+{
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ struct led_flash_setting *s = &flash->brightness;
+
+ s->val = brightness;
+ led_clamp_align(s);
+
+ if (!(led_cdev->flags & LED_SUSPENDED))
+ return call_flash_op(flash, flash_brightness_set, s->val);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(led_set_flash_brightness);
+
+int led_update_flash_brightness(struct led_classdev_flash *flash)
+{
+ struct led_flash_setting *s = &flash->brightness;
+ u32 brightness;
+
+ if (has_flash_op(flash, flash_brightness_get)) {
+ int ret = call_flash_op(flash, flash_brightness_get,
+ &brightness);
+ if (ret < 0)
+ return ret;
+
+ s->val = brightness;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(led_update_flash_brightness);
+
+MODULE_AUTHOR("Jacek Anaszewski <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LED Flash class Interface");
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index dbeebac..02564c5 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -179,6 +179,10 @@ EXPORT_SYMBOL_GPL(led_classdev_suspend);
void led_classdev_resume(struct led_classdev *led_cdev)
{
led_cdev->brightness_set(led_cdev, led_cdev->brightness);
+
+ if (led_cdev->flash_resume)
+ led_cdev->flash_resume(led_cdev);
+
led_cdev->flags &= ~LED_SUSPENDED;
}
EXPORT_SYMBOL_GPL(led_classdev_resume);
diff --git a/include/linux/led-class-flash.h b/include/linux/led-class-flash.h
new file mode 100644
index 0000000..5188d9fd
--- /dev/null
+++ b/include/linux/led-class-flash.h
@@ -0,0 +1,198 @@
+/*
+ * LED Flash class interface
+ *
+ * Copyright (C) 2014 Samsung Electronics Co., Ltd.
+ * Author: Jacek Anaszewski <[email protected]>
+ *
+ * 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.
+ *
+ */
+#ifndef __LINUX_FLASH_LEDS_H_INCLUDED
+#define __LINUX_FLASH_LEDS_H_INCLUDED
+
+#include <linux/leds.h>
+#include <uapi/linux/v4l2-controls.h>
+
+struct device_node;
+struct led_classdev_flash;
+
+/*
+ * Supported led fault bits - must be kept in synch
+ * with V4L2_FLASH_FAULT bits.
+ */
+#define LED_FAULT_OVER_VOLTAGE V4L2_FLASH_FAULT_OVER_VOLTAGE
+#define LED_FAULT_TIMEOUT V4L2_FLASH_FAULT_TIMEOUT
+#define LED_FAULT_OVER_TEMPERATURE V4L2_FLASH_FAULT_OVER_TEMPERATURE
+#define LED_FAULT_SHORT_CIRCUIT V4L2_FLASH_FAULT_SHORT_CIRCUIT
+#define LED_FAULT_OVER_CURRENT V4L2_FLASH_FAULT_OVER_CURRENT
+#define LED_FAULT_INDICATOR V4L2_FLASH_FAULT_INDICATOR
+#define LED_FAULT_UNDER_VOLTAGE V4L2_FLASH_FAULT_UNDER_VOLTAGE
+#define LED_FAULT_INPUT_VOLTAGE V4L2_FLASH_FAULT_INPUT_VOLTAGE
+#define LED_FAULT_LED_OVER_TEMPERATURE V4L2_FLASH_OVER_TEMPERATURE
+
+struct led_flash_ops {
+ /* set flash brightness */
+ int (*flash_brightness_set)(struct led_classdev_flash *flash,
+ u32 brightness);
+ /* get flash brightness */
+ int (*flash_brightness_get)(struct led_classdev_flash *flash,
+ u32 *brightness);
+ /* set flash strobe state */
+ int (*strobe_set)(struct led_classdev_flash *flash, bool state);
+ /* get flash strobe state */
+ int (*strobe_get)(struct led_classdev_flash *flash, bool *state);
+ /* set flash timeout */
+ int (*timeout_set)(struct led_classdev_flash *flash, u32 timeout);
+ /* get the flash LED fault */
+ int (*fault_get)(struct led_classdev_flash *flash, u32 *fault);
+};
+
+/*
+ * Current value of a flash setting along
+ * with its constraints.
+ */
+struct led_flash_setting {
+ /* maximum allowed value */
+ u32 min;
+ /* maximum allowed value */
+ u32 max;
+ /* step value */
+ u32 step;
+ /* current value */
+ u32 val;
+};
+
+/*
+ * Aggregated flash settings - designed for ease
+ * of passing initialization data to the clients
+ * wrapping a LED Flash class device.
+ */
+struct led_flash_config {
+ struct led_flash_setting torch_brightness;
+ struct led_flash_setting flash_brightness;
+ struct led_flash_setting flash_timeout;
+ u32 flash_faults;
+};
+
+struct led_classdev_flash {
+ /* led class device */
+ struct led_classdev led_cdev;
+
+ /* flash led specific ops */
+ const struct led_flash_ops *ops;
+
+ /* flash brightness value in microamperes along with its constraints */
+ struct led_flash_setting brightness;
+
+ /* flash timeout value in microseconds along with its constraints */
+ struct led_flash_setting timeout;
+
+ /*
+ * Indicates whether the flash sub-led should strobe
+ * upon strobe activation on any of the remaining sub-leds.
+ */
+ bool sync_strobe:1;
+};
+
+static inline struct led_classdev_flash *lcdev_to_flash(
+ struct led_classdev *lcdev)
+{
+ return container_of(lcdev, struct led_classdev_flash, led_cdev);
+}
+
+/**
+ * led_classdev_flash_register - register a new object of led_classdev class
+ * with support for flash LEDs
+ * @parent: the flash LED to register
+ * @flash: the led_classdev_flash structure for this device
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+int led_classdev_flash_register(struct device *parent,
+ struct led_classdev_flash *flash);
+
+/**
+ * led_classdev_flash_unregister - unregisters an object of led_classdev class
+ * with support for flash LEDs
+ * @flash: the flash LED to unregister
+ *
+ * Unregister a previously registered via led_classdev_flash_register object
+ */
+void led_classdev_flash_unregister(struct led_classdev_flash *flash);
+
+/**
+ * led_set_flash_strobe - setup flash strobe
+ * @flash: the flash LED to set strobe on
+ * @state: 1 - strobe flash, 0 - stop flash strobe
+ *
+ * Strobe the flash LED.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+extern int led_set_flash_strobe(struct led_classdev_flash *flash,
+ bool state);
+
+/**
+ * led_get_flash_strobe - get flash strobe status
+ * @flash: the flash LED to query
+ * @state: 1 - flash is strobing, 0 - flash is off
+ *
+ * Check whether the flash is strobing at the moment.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+extern int led_get_flash_strobe(struct led_classdev_flash *flash,
+ bool *state);
+
+/**
+ * led_set_flash_brightness - set flash LED brightness
+ * @flash: the flash LED to set
+ * @brightness: the brightness to set it to
+ *
+ * Set a flash LED's brightness.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+extern int led_set_flash_brightness(struct led_classdev_flash *flash,
+ u32 brightness);
+
+/**
+ * led_update_flash_brightness - update flash LED brightness
+ * @flash: the flash LED to query
+ *
+ * Get a flash LED's current brightness and update led_flash->brightness
+ * member with the obtained value.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+extern int led_update_flash_brightness(struct led_classdev_flash *flash);
+
+/**
+ * led_set_flash_timeout - set flash LED timeout
+ * @flash: the flash LED to set
+ * @timeout: the flash timeout to set it to
+ *
+ * Set the flash strobe duration. The duration set by the driver
+ * is returned in the timeout argument and may differ from the
+ * one that was originally passed.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+extern int led_set_flash_timeout(struct led_classdev_flash *flash,
+ u32 timeout);
+
+/**
+ * led_get_flash_fault - get the flash LED fault
+ * @flash: the flash LED to query
+ * @fault: bitmask containing flash faults
+ *
+ * Get the flash LED fault.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+extern int led_get_flash_fault(struct led_classdev_flash *flash,
+ u32 *fault);
+
+#endif /* __LINUX_FLASH_LEDS_H_INCLUDED */
diff --git a/include/linux/leds.h b/include/linux/leds.h
index cfceef3..c359f35 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -46,6 +46,8 @@ struct led_classdev {
#define LED_SYSFS_DISABLE (1 << 20)
#define SET_BRIGHTNESS_ASYNC (1 << 21)
#define SET_BRIGHTNESS_SYNC (1 << 22)
+#define LED_DEV_CAP_FLASH (1 << 23)
+#define LED_DEV_CAP_COMPOUND (1 << 24)
/* Set LED brightness level */
/* Must not sleep, use a workqueue if needed */
@@ -81,6 +83,7 @@ struct led_classdev {
unsigned long blink_delay_on, blink_delay_off;
struct timer_list blink_timer;
int blink_brightness;
+ void (*flash_resume)(struct led_classdev *led_cdev);
struct work_struct set_brightness_work;
int delayed_set_value;
--
1.7.9.5
On Fri, Nov 28, 2014 at 09:18:00AM +0000, Jacek Anaszewski wrote:
> This patch adds a description of 'flashes' property
> to the samsung-fimc.txt.
>
> Signed-off-by: Jacek Anaszewski <[email protected]>
> Acked-by: Kyungmin Park <[email protected]>
> Cc: Sylwester Nawrocki <[email protected]>
> Cc: Rob Herring <[email protected]>
> Cc: Pawel Moll <[email protected]>
> Cc: Mark Rutland <[email protected]>
> Cc: Ian Campbell <[email protected]>
> Cc: Kumar Gala <[email protected]>
> Cc: <[email protected]>
> ---
> .../devicetree/bindings/media/samsung-fimc.txt | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
> index 922d6f8..4b7ed03 100644
> --- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
> +++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
> @@ -40,6 +40,12 @@ should be inactive. For the "active-a" state the camera port A must be activated
> and the port B deactivated and for the state "active-b" it should be the other
> way around.
>
> +Optional properties:
> +
> +- flashes - array of strings with flash led names; the name has to
> + be same with the related led label
> + (see Documentation/devicetree/bindings/leds/common.txt)
> +
Why is this not an array of phandles to the LED nodes? That's much
better than strings.
Also, I only seem to have recevied the documentation patches and none of
the code -- in future when posting RFC DT patches, please Cc for the
code too as it's useful context.
Mark.
> The 'camera' node must include at least one 'fimc' child node.
>
>
> @@ -166,6 +172,7 @@ Example:
> clock-output-names = "cam_a_clkout", "cam_b_clkout";
> pinctrl-names = "default";
> pinctrl-0 = <&cam_port_a_clk_active>;
> + flashes = "max77693-flash1", "max77693-flash2";
> status = "okay";
> #address-cells = <1>;
> #size-cells = <1>;
> --
> 1.7.9.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
On 11/28/2014 12:14 PM, Mark Rutland wrote:
> On Fri, Nov 28, 2014 at 09:18:00AM +0000, Jacek Anaszewski wrote:
>> This patch adds a description of 'flashes' property
>> to the samsung-fimc.txt.
>>
>> Signed-off-by: Jacek Anaszewski <[email protected]>
>> Acked-by: Kyungmin Park <[email protected]>
>> Cc: Sylwester Nawrocki <[email protected]>
>> Cc: Rob Herring <[email protected]>
>> Cc: Pawel Moll <[email protected]>
>> Cc: Mark Rutland <[email protected]>
>> Cc: Ian Campbell <[email protected]>
>> Cc: Kumar Gala <[email protected]>
>> Cc: <[email protected]>
>> ---
>> .../devicetree/bindings/media/samsung-fimc.txt | 7 +++++++
>> 1 file changed, 7 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
>> index 922d6f8..4b7ed03 100644
>> --- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
>> +++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
>> @@ -40,6 +40,12 @@ should be inactive. For the "active-a" state the camera port A must be activated
>> and the port B deactivated and for the state "active-b" it should be the other
>> way around.
>>
>> +Optional properties:
>> +
>> +- flashes - array of strings with flash led names; the name has to
>> + be same with the related led label
>> + (see Documentation/devicetree/bindings/leds/common.txt)
>> +
>
> Why is this not an array of phandles to the LED nodes? That's much
> better than strings.
This is because a single flash led device can control many sub-leds,
which are represented by child nodes in the Device Tree.
Every sub-led is registered as a separate LED Flash class device
in the LED subsystem, but in fact they share the same struct device
and thus have access only to the parent's phandle. The LED Flash
class devices are wrapped by V4L2 sub-devices and register
asynchronously within a media device. Since the v4l2_subdev structure
has a 'name' field, it is convenient to initialize it with
parsed 'label' property of a child led node and match the
sub-devices in the media device basing on it.
> Also, I only seem to have recevied the documentation patches and none of
> the code -- in future when posting RFC DT patches, please Cc for the
> code too as it's useful context.
Of course, I'll keep it in mind.
Regards,
Jacek
On Fri, Nov 28, 2014 at 12:09:14PM +0000, Jacek Anaszewski wrote:
> On 11/28/2014 12:14 PM, Mark Rutland wrote:
> > On Fri, Nov 28, 2014 at 09:18:00AM +0000, Jacek Anaszewski wrote:
> >> This patch adds a description of 'flashes' property
> >> to the samsung-fimc.txt.
> >>
> >> Signed-off-by: Jacek Anaszewski <[email protected]>
> >> Acked-by: Kyungmin Park <[email protected]>
> >> Cc: Sylwester Nawrocki <[email protected]>
> >> Cc: Rob Herring <[email protected]>
> >> Cc: Pawel Moll <[email protected]>
> >> Cc: Mark Rutland <[email protected]>
> >> Cc: Ian Campbell <[email protected]>
> >> Cc: Kumar Gala <[email protected]>
> >> Cc: <[email protected]>
> >> ---
> >> .../devicetree/bindings/media/samsung-fimc.txt | 7 +++++++
> >> 1 file changed, 7 insertions(+)
> >>
> >> diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
> >> index 922d6f8..4b7ed03 100644
> >> --- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
> >> +++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
> >> @@ -40,6 +40,12 @@ should be inactive. For the "active-a" state the camera port A must be activated
> >> and the port B deactivated and for the state "active-b" it should be the other
> >> way around.
> >>
> >> +Optional properties:
> >> +
> >> +- flashes - array of strings with flash led names; the name has to
> >> + be same with the related led label
> >> + (see Documentation/devicetree/bindings/leds/common.txt)
> >> +
> >
> > Why is this not an array of phandles to the LED nodes? That's much
> > better than strings.
>
> This is because a single flash led device can control many sub-leds,
> which are represented by child nodes in the Device Tree.
> Every sub-led is registered as a separate LED Flash class device
> in the LED subsystem, but in fact they share the same struct device
> and thus have access only to the parent's phandle.
But that's a Linux infrastrcture issue, no? You don't have to use the
node from the struct device to find the relevant phandle.
> The LED Flash
> class devices are wrapped by V4L2 sub-devices and register
> asynchronously within a media device. Since the v4l2_subdev structure
> has a 'name' field, it is convenient to initialize it with
> parsed 'label' property of a child led node and match the
> sub-devices in the media device basing on it.
While that might be convenient, I don't think it's fantastic to use that
to describe the relationship, as this leaks Linux internals (e.g. I can
refer to a name that doesn't exist in the DT but happens to be what
Linux used, and it would work). Also, are the labels guaranteed to be
globally unique?
Using phandles is much better for the binding. I appreciate that this
may require more code, but IMO it's worth that for the safety and
uniformity given by the use of phandles for referring to nodes.
> > Also, I only seem to have recevied the documentation patches and none of
> > the code -- in future when posting RFC DT patches, please Cc for the
> > code too as it's useful context.
>
> Of course, I'll keep it in mind.
Thanks!
Mark.
On 11/28/2014 01:30 PM, Mark Rutland wrote:
> On Fri, Nov 28, 2014 at 12:09:14PM +0000, Jacek Anaszewski wrote:
>> On 11/28/2014 12:14 PM, Mark Rutland wrote:
>>> On Fri, Nov 28, 2014 at 09:18:00AM +0000, Jacek Anaszewski wrote:
>>>> This patch adds a description of 'flashes' property
>>>> to the samsung-fimc.txt.
>>>>
>>>> Signed-off-by: Jacek Anaszewski <[email protected]>
>>>> Acked-by: Kyungmin Park <[email protected]>
>>>> Cc: Sylwester Nawrocki <[email protected]>
>>>> Cc: Rob Herring <[email protected]>
>>>> Cc: Pawel Moll <[email protected]>
>>>> Cc: Mark Rutland <[email protected]>
>>>> Cc: Ian Campbell <[email protected]>
>>>> Cc: Kumar Gala <[email protected]>
>>>> Cc: <[email protected]>
>>>> ---
>>>> .../devicetree/bindings/media/samsung-fimc.txt | 7 +++++++
>>>> 1 file changed, 7 insertions(+)
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
>>>> index 922d6f8..4b7ed03 100644
>>>> --- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
>>>> +++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
>>>> @@ -40,6 +40,12 @@ should be inactive. For the "active-a" state the camera port A must be activated
>>>> and the port B deactivated and for the state "active-b" it should be the other
>>>> way around.
>>>>
>>>> +Optional properties:
>>>> +
>>>> +- flashes - array of strings with flash led names; the name has to
>>>> + be same with the related led label
>>>> + (see Documentation/devicetree/bindings/leds/common.txt)
>>>> +
>>>
>>> Why is this not an array of phandles to the LED nodes? That's much
>>> better than strings.
>>
>> This is because a single flash led device can control many sub-leds,
>> which are represented by child nodes in the Device Tree.
>> Every sub-led is registered as a separate LED Flash class device
>> in the LED subsystem, but in fact they share the same struct device
>> and thus have access only to the parent's phandle.
>
> But that's a Linux infrastrcture issue, no? You don't have to use the
> node from the struct device to find the relevant phandle.
Right.
>> The LED Flash
>> class devices are wrapped by V4L2 sub-devices and register
>> asynchronously within a media device. Since the v4l2_subdev structure
>> has a 'name' field, it is convenient to initialize it with
>> parsed 'label' property of a child led node and match the
>> sub-devices in the media device basing on it.
>
> While that might be convenient, I don't think it's fantastic to use that
> to describe the relationship, as this leaks Linux internals (e.g. I can
> refer to a name that doesn't exist in the DT but happens to be what
> Linux used, and it would work). Also, are the labels guaranteed to be
> globally unique?
The labels are used for initializing class device name and kernel
doesn't allow to initialize two devices with same names.
This implies that labels are guaranteed to be globally unique.
Unless I am missing something.
Regards,
Jacek
On Fri, Nov 28, 2014 at 02:11:04PM +0000, Jacek Anaszewski wrote:
> On 11/28/2014 01:30 PM, Mark Rutland wrote:
> > On Fri, Nov 28, 2014 at 12:09:14PM +0000, Jacek Anaszewski wrote:
> >> On 11/28/2014 12:14 PM, Mark Rutland wrote:
> >>> On Fri, Nov 28, 2014 at 09:18:00AM +0000, Jacek Anaszewski wrote:
> >>>> This patch adds a description of 'flashes' property
> >>>> to the samsung-fimc.txt.
> >>>>
> >>>> Signed-off-by: Jacek Anaszewski <[email protected]>
> >>>> Acked-by: Kyungmin Park <[email protected]>
> >>>> Cc: Sylwester Nawrocki <[email protected]>
> >>>> Cc: Rob Herring <[email protected]>
> >>>> Cc: Pawel Moll <[email protected]>
> >>>> Cc: Mark Rutland <[email protected]>
> >>>> Cc: Ian Campbell <[email protected]>
> >>>> Cc: Kumar Gala <[email protected]>
> >>>> Cc: <[email protected]>
> >>>> ---
> >>>> .../devicetree/bindings/media/samsung-fimc.txt | 7 +++++++
> >>>> 1 file changed, 7 insertions(+)
> >>>>
> >>>> diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
> >>>> index 922d6f8..4b7ed03 100644
> >>>> --- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
> >>>> +++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
> >>>> @@ -40,6 +40,12 @@ should be inactive. For the "active-a" state the camera port A must be activated
> >>>> and the port B deactivated and for the state "active-b" it should be the other
> >>>> way around.
> >>>>
> >>>> +Optional properties:
> >>>> +
> >>>> +- flashes - array of strings with flash led names; the name has to
> >>>> + be same with the related led label
> >>>> + (see Documentation/devicetree/bindings/leds/common.txt)
> >>>> +
> >>>
> >>> Why is this not an array of phandles to the LED nodes? That's much
> >>> better than strings.
> >>
> >> This is because a single flash led device can control many sub-leds,
> >> which are represented by child nodes in the Device Tree.
> >> Every sub-led is registered as a separate LED Flash class device
> >> in the LED subsystem, but in fact they share the same struct device
> >> and thus have access only to the parent's phandle.
> >
> > But that's a Linux infrastrcture issue, no? You don't have to use the
> > node from the struct device to find the relevant phandle.
>
> Right.
>
> >> The LED Flash
> >> class devices are wrapped by V4L2 sub-devices and register
> >> asynchronously within a media device. Since the v4l2_subdev structure
> >> has a 'name' field, it is convenient to initialize it with
> >> parsed 'label' property of a child led node and match the
> >> sub-devices in the media device basing on it.
> >
> > While that might be convenient, I don't think it's fantastic to use that
> > to describe the relationship, as this leaks Linux internals (e.g. I can
> > refer to a name that doesn't exist in the DT but happens to be what
> > Linux used, and it would work). Also, are the labels guaranteed to be
> > globally unique?
>
> The labels are used for initializing class device name and kernel
> doesn't allow to initialize two devices with same names.
> This implies that labels are guaranteed to be globally unique.
On Linux, yes, but that's an implementation detail, not a property of
the binding·
Mark.
Hi!
> +Flash LED handling under Linux
> +==============================
> +
> +Some LED devices support two modes - torch and flash. The modes are
> +supported by the LED class (see Documentation/leds/leds-class.txt)
> +and LED Flash class respectively.
> +
> +In order to enable support for flash LEDs CONFIG_LEDS_CLASS_FLASH symbol
> +must be defined in the kernel config. A flash LED driver must register
> +in the LED subsystem with led_classdev_flash_register to gain flash
> +capabilities.
> +
> +Following sysfs attributes are exposed for controlling flash led devices:
> +
> + - flash_brightness - flash LED brightness in microamperes (RW)
> + - max_flash_brightness - maximum available flash LED brightness (RO)
> + - indicator_brightness - privacy LED brightness in microamperes (RW)
> + - max_indicator_brightness - maximum privacy LED brightness in
> + microamperes (RO)
> + - flash_timeout - flash strobe duration in microseconds (RW)
> + - max_flash_timeout - maximum available flash strobe duration (RO)
> + - flash_strobe - flash strobe state (RW)
> + - flash_sync_strobe - one flash device can control more than one
> + sub-led; when this atrribute is set to 1
> + the flash led will be strobed synchronously
> + with the other ones controlled by the same
> + device (RW)
This is not really clear. Does flash_timeout or flash_brightness need
to be set, first?
Do we really want to have separate indicator brightnesses in uA?
Should we maybe reuse existing "brightness" parameter for torch and
indication, maybe adding single (RO) indicator_brightness attribute?
> + - flash_fault - bitmask of flash faults that may have occurred,
> + possible flags are:
> + * 0x01 - flash controller voltage to the flash LED has exceeded
> + the limit specific to the flash controller
> + * 0x02 - the flash strobe was still on when the timeout set by
> + the user has expired; not all flash controllers may
> + set this in all such conditions
> + * 0x04 - the flash controller has overheated
> + * 0x08 - the short circuit protection of the flash controller
> + has been triggered
> + * 0x10 - current in the LED power supply has exceeded the limit
> + specific to the flash controller
> + * 0x40 - flash controller voltage to the flash LED has been
> + below the minimum limit specific to the flash
> + * 0x80 - the input voltage of the flash controller is below
> + the limit under which strobing the flash at full
> + current will not be possible. The condition persists
> + until this flag is no longer set
> + * 0x100 - the temperature of the LED has exceeded its allowed
> + upper limit
How are faults cleared? Should it be list of strings, instead of
bitmask? We may want to add new fault modes in future...
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
On Fri 2014-11-28 10:17:56, Jacek Anaszewski wrote:
> It is useful to have an access to the async sub-device
> being matched, not only to the related struct device.
> Change match callback argument from struct device
> to struct v4l2_subdev. It will allow e.g. for matching
> a sub-device by its "name" property.
>
> Signed-off-by: Jacek Anaszewski <[email protected]>
> Acked-by: Kyungmin Park <[email protected]>
> Cc: Guennadi Liakhovetski <[email protected]>
> Cc: Laurent Pinchart <[email protected]>
> Cc: Hans Verkuil <[email protected]>
Acked-by: Pavel Machek <[email protected]>
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
On Fri 2014-11-28 10:17:59, Jacek Anaszewski wrote:
> This patch adds suppport for external v4l2-flash devices.
> The support includes parsing "flashes" Device Tree property
> and asynchronous subdevice registration.
>
> Signed-off-by: Jacek Anaszewski <[email protected]>
> Acked-by: Kyungmin Park <[email protected]>
> Cc: Sylwester Nawrocki <[email protected]>
Acked-by: Pavel Machek <[email protected]>
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Hi Jacek,
Thank you for the patch.
On Friday 28 November 2014 10:17:56 Jacek Anaszewski wrote:
> It is useful to have an access to the async sub-device
> being matched, not only to the related struct device.
> Change match callback argument from struct device
> to struct v4l2_subdev. It will allow e.g. for matching
> a sub-device by its "name" property.
In principle I agree. However, we will need to reimplement v4l2-async based on
the component (drivers/base/component.c) framework at some point. As the
component framework is based on struct device, will it still be possible to
match on subdev name in that case ? If not, we might need to try to find
another approach to the issue.
> Signed-off-by: Jacek Anaszewski <[email protected]>
> Acked-by: Kyungmin Park <[email protected]>
> Cc: Guennadi Liakhovetski <[email protected]>
> Cc: Laurent Pinchart <[email protected]>
> Cc: Hans Verkuil <[email protected]>
> ---
> drivers/media/v4l2-core/v4l2-async.c | 16 ++++++++--------
> include/media/v4l2-async.h | 2 +-
> 2 files changed, 9 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/media/v4l2-core/v4l2-async.c
> b/drivers/media/v4l2-core/v4l2-async.c index 85a6a34..8140992 100644
> --- a/drivers/media/v4l2-core/v4l2-async.c
> +++ b/drivers/media/v4l2-core/v4l2-async.c
> @@ -22,10 +22,10 @@
> #include <media/v4l2-device.h>
> #include <media/v4l2-subdev.h>
>
> -static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd)
> +static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev
> *asd) {
> #if IS_ENABLED(CONFIG_I2C)
> - struct i2c_client *client = i2c_verify_client(dev);
> + struct i2c_client *client = i2c_verify_client(sd->dev);
> return client &&
> asd->match.i2c.adapter_id == client->adapter->nr &&
> asd->match.i2c.address == client->addr;
> @@ -34,14 +34,14 @@ static bool match_i2c(struct device *dev, struct
> v4l2_async_subdev *asd) #endif
> }
>
> -static bool match_devname(struct device *dev, struct v4l2_async_subdev
> *asd) +static bool match_devname(struct v4l2_subdev *sd, struct
> v4l2_async_subdev *asd) {
> - return !strcmp(asd->match.device_name.name, dev_name(dev));
> + return !strcmp(asd->match.device_name.name, dev_name(sd->dev));
> }
>
> -static bool match_of(struct device *dev, struct v4l2_async_subdev *asd)
> +static bool match_of(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
> {
> - return dev->of_node == asd->match.of.node;
> + return sd->dev->of_node == asd->match.of.node;
> }
>
> static LIST_HEAD(subdev_list);
> @@ -52,7 +52,7 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct
> v4l2_async_notifier * struct v4l2_subdev *sd)
> {
> struct v4l2_async_subdev *asd;
> - bool (*match)(struct device *, struct v4l2_async_subdev *);
> + bool (*match)(struct v4l2_subdev *, struct v4l2_async_subdev *);
>
> list_for_each_entry(asd, ¬ifier->waiting, list) {
> /* bus_type has been verified valid before */
> @@ -79,7 +79,7 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct
> v4l2_async_notifier * }
>
> /* match cannot be NULL here */
> - if (match(sd->dev, asd))
> + if (match(sd, asd))
> return asd;
> }
>
> diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
> index 7683569..1c0b586 100644
> --- a/include/media/v4l2-async.h
> +++ b/include/media/v4l2-async.h
> @@ -51,7 +51,7 @@ struct v4l2_async_subdev {
> unsigned short address;
> } i2c;
> struct {
> - bool (*match)(struct device *,
> + bool (*match)(struct v4l2_subdev *,
> struct v4l2_async_subdev *);
> void *priv;
> } custom;
--
Regards,
Laurent Pinchart
Hi!
> @@ -0,0 +1,472 @@
> +/*
> + * LED Flash class driver for the AAT1290
> + * 1.5A Step-Up Current Regulator for Flash LEDs
> + *
> + * Copyright (C) 2014, Samsung Electronics Co., Ltd.
> + * Author: Jacek Anaszewski <[email protected]>
> + *
> + * 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.
> + */
> +
> +#define AAT1290_MM_TO_FL_1_92 1
> +#define AAT1290_MM_TO_FL_3_7 2
> +#define AAT1290_MM_TO_FL_5_5 3
> +#define AAT1290_MM_TO_FL_7_3 4
> +#define AAT1290_MM_TO_FL_9 5
> +#define AAT1290_MM_TO_FL_10_7 6
> +#define AAT1290_MM_TO_FL_12_4 7
> +#define AAT1290_MM_TO_FL_14 8
> +#define AAT1290_MM_TO_FL_15_9 9
> +#define AAT1290_MM_TO_FL_17_5 10
> +#define AAT1290_MM_TO_FL_19_1 11
> +#define AAT1290_MM_TO_FL_20_8 12
> +#define AAT1290_MM_TO_FL_22_4 13
> +#define AAT1290_MM_TO_FL_24 14
> +#define AAT1290_MM_TO_FL_25_6 15
> +#define AAT1290_MM_TO_FL_OFF 16
Only one of these defines is unused.
> +static struct of_device_id aat1290_led_dt_match[] = {
> + {.compatible = "skyworks,aat1290"},
spaces after { and before } ?
Otherwise looks ok,
Signed-off-by: Pavel Machek <[email protected]>
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Hi!
> diff --git a/Documentation/devicetree/bindings/mfd/max77693.txt b/Documentation/devicetree/bindings/mfd/max77693.txt
> index 01e9f30..50a8dad 100644
> --- a/Documentation/devicetree/bindings/mfd/max77693.txt
> +++ b/Documentation/devicetree/bindings/mfd/max77693.txt
> @@ -41,6 +41,62 @@ Optional properties:
> To get more informations, please refer to documentaion.
> [*] refer Documentation/devicetree/bindings/pwm/pwm.txt
>
> +- led-flash : the LED submodule device node
> +
> +There are two led outputs available - fled1 and fled2. Each of them can
> +control a separate led or they can be connected together to double
> +the maximum current for a single connected led. One led is represented
> +by one child node.
> +
> +Required properties:
> +- compatible : must be "maxim,max77693-flash"
> +
> +Optional properties:
> +- maxim,fleds : array of current outputs in order: fled1, fled2
> + Note: both current outputs can be connected to a single led
> + Possible values:
> + 0 - the output is left disconnected,
> + 1 - a diode is connected to the output.
Is this one needed? Just ommit child note if it is not there.
> +- maxim,trigger-type : Array of trigger types in order: flash, torch
> + Possible trigger types:
> + 0 - Rising edge of the signal triggers the flash/torch,
> + 1 - Signal level controls duration of the flash/torch.
> +- maxim,trigger : Array of flags indicating which trigger can activate given led
> + in order: fled1, fled2
> + Possible flag values (can be combined):
> + 1 - FLASH pin of the chip,
> + 2 - TORCH pin of the chip,
> + 4 - software via I2C command.
Is it good idea to have bitfields like this?
Make these required properties of the subnode?
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
On Fri, Nov 28, 2014 at 10:17:55AM +0100, Jacek Anaszewski wrote:
> This patch extends LED Flash class documention by
> the description of interactions with v4l2-flash sub-device.
>
> Signed-off-by: Jacek Anaszewski <[email protected]>
> Acked-by: Kyungmin Park <[email protected]>
> Cc: Bryan Wu <[email protected]>
> Cc: Richard Purdie <[email protected]>
Acked-by: Sakari Ailus <[email protected]>
--
Sakari Ailus
e-mail: [email protected] XMPP: [email protected]
Hi Laurent,
On 11/29/2014 05:38 PM, Laurent Pinchart wrote:
> Hi Jacek,
>
> Thank you for the patch.
>
> On Friday 28 November 2014 10:17:56 Jacek Anaszewski wrote:
>> It is useful to have an access to the async sub-device
>> being matched, not only to the related struct device.
>> Change match callback argument from struct device
>> to struct v4l2_subdev. It will allow e.g. for matching
>> a sub-device by its "name" property.
>
> In principle I agree. However, we will need to reimplement v4l2-async based on
> the component (drivers/base/component.c) framework at some point. As the
> component framework is based on struct device, will it still be possible to
> match on subdev name in that case ? If not, we might need to try to find
> another approach to the issue.
There were reservations raised [1] concerning the way of matching
by name, as the labels are not guaranteed to be globally unique across
Device Tree. I admit, this issue has to be solved in a different way.
Especially in view of prospective transition to using
drivers/base/component.c
I propose to add a new structure:
struct v4l2_asd_match{
bool (*match)(struct v4l2_async_subdev *, void *);
void *priv;
}
and a function:
v4l2_async_register_subdev_with_match(struct v4l2_subdev *sd, struct
v4l2_asd_match*).
This way we could pass DT sub-node related to a sub-led in a priv
field of v4l2_asd_match upon registration.
This is similar approach as in case of drivers/base/component.c.
Best Regards,
Jacek Anaszewski
[1] http://www.spinics.net/lists/linux-leds/msg02532.html
On Fri, 28 Nov 2014, Jacek Anaszewski wrote:
> This patch adds device tree binding documentation for
> the flash cell of the Maxim max77693 multifunctional device.
>
> Signed-off-by: Jacek Anaszewski <[email protected]>
> Signed-off-by: Andrzej Hajda <[email protected]>
> Acked-by: Kyungmin Park <[email protected]>
> Cc: Lee Jones <[email protected]>
> Cc: SangYoung Son <[email protected]>
> Cc: Samuel Ortiz <[email protected]>
> Cc: Bryan Wu <[email protected]>
> Cc: Richard Purdie <[email protected]>
> Cc: Rob Herring <[email protected]>
> Cc: Pawel Moll <[email protected]>
> Cc: Mark Rutland <[email protected]>
> Cc: Ian Campbell <[email protected]>
> Cc: Kumar Gala <[email protected]>
> Cc: <[email protected]>
> ---
> Documentation/devicetree/bindings/mfd/max77693.txt | 74 ++++++++++++++++++++
> 1 file changed, 74 insertions(+)
This definitely requires a DT Ack.
> diff --git a/Documentation/devicetree/bindings/mfd/max77693.txt b/Documentation/devicetree/bindings/mfd/max77693.txt
> index 01e9f30..50a8dad 100644
> --- a/Documentation/devicetree/bindings/mfd/max77693.txt
> +++ b/Documentation/devicetree/bindings/mfd/max77693.txt
> @@ -41,6 +41,62 @@ Optional properties:
> To get more informations, please refer to documentaion.
> [*] refer Documentation/devicetree/bindings/pwm/pwm.txt
>
> +- led-flash : the LED submodule device node
> +
> +There are two led outputs available - fled1 and fled2. Each of them can
> +control a separate led or they can be connected together to double
> +the maximum current for a single connected led. One led is represented
> +by one child node.
> +
> +Required properties:
> +- compatible : must be "maxim,max77693-flash"
I'm not sure this compatible string is suitable. It looks like
NOR/NAND Flash to me. Perhaps 'fled', or just 'led' would be better.
> +Optional properties:
> +- maxim,fleds : array of current outputs in order: fled1, fled2
Nit: Sentences start with an uppercase character.
This is true for all other occurrences.
> + Note: both current outputs can be connected to a single led
> + Possible values:
> + 0 - the output is left disconnected,
> + 1 - a diode is connected to the output.
> +- maxim,trigger-type : Array of trigger types in order: flash, torch
> + Possible trigger types:
> + 0 - Rising edge of the signal triggers the flash/torch,
> + 1 - Signal level controls duration of the flash/torch.
> +- maxim,trigger : Array of flags indicating which trigger can activate given led
> + in order: fled1, fled2
> + Possible flag values (can be combined):
> + 1 - FLASH pin of the chip,
> + 2 - TORCH pin of the chip,
> + 4 - software via I2C command.
> +- maxim,boost-mode :
> + In boost mode the device can produce up to 1.2A of total current
> + on both outputs. The maximum current on each output is reduced
> + to 625mA then. If there are two child led nodes defined then boost
> + is enabled by default.
> + Possible values:
> + 0 - no boost,
> + 1 - adaptive mode,
> + 2 - fixed mode.
> +- maxim,boost-vout : Output voltage of the boost module in millivolts.
> +- maxim,vsys-min : Low input voltage level in millivolts. Flash is not fired
> + if chip estimates that system voltage could drop below this level due
> + to flash power consumption.
> +
> +A child node must be defined per sub-led.
> +
> +Required properties of the LED child node:
> +- label : see Documentation/devicetree/bindings/leds/common.txt
> +- maxim,fled_id : identifier of the fled output the led is connected to:
> + 1 - FLED1,
> + 2 - FLED2.
Better to define all of these random numbers in include/dt-bindings.
> +Optional properties of the LED child node:
> +- max-microamp : see Documentation/devicetree/bindings/leds/common.txt
> + Range: 15625 - 250000
> +- flash-max-microamp : see Documentation/devicetree/bindings/leds/common.txt
> + Range: 15625 - 1000000
> +- flash-timeout-microsec : see Documentation/devicetree/bindings/leds/common.txt
> + Range: 62500 - 1000000
> +
> Example:
> max77693@66 {
> compatible = "maxim,max77693";
> @@ -73,4 +129,22 @@ Example:
> pwms = <&pwm 0 40000 0>;
> pwm-names = "haptic";
> };
> +
> + led_flash: led-flash {
Should both be underscore. I believe the second portion here should
be more generic "led" for instance.
> + compatible = "maxim,max77693-flash";
> + maxim,fleds = <1 0>;
> + maxim,trigger = <7 0>;
> + maxim,trigger-type = <0 1>;
> + maxim,boost-mode = <0>;
> + maxim,boost-vout = <5000>;
> + maxim,vsys-min = <2400>;
These will all have to be signed off by a DT maintainer.
> + camera-flash {
> + maxim,fled_id = <1>
> + label = "max77693-flash";
> + max-microamp = <250000>;
> + flash-max-microamp = <1000000>;
> + flash-timeout-microsec = <1000000>;
> + }
Missing ';'
You should probably test your example code.
> + };
> };
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
On Fri, 28 Nov 2014, Jacek Anaszewski wrote:
> Add "label" array for Device Tree strings with
> the name of a LED device and make flash_timeout
> a two element array, for caching the sub-led
> related flash timeout.
<------------------------------------------------------------------------->
Please use all of the 75 char buffer.
> Signed-off-by: Jacek Anaszewski <[email protected]>
> Signed-off-by: Andrzej Hajda <[email protected]>
> Acked-by: Kyungmin Park <[email protected]>
> Cc: Lee Jones <[email protected]>
> Cc: SangYoung Son <[email protected]>
> Cc: Samuel Ortiz <[email protected]>
> ---
> include/linux/mfd/max77693.h | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/include/linux/mfd/max77693.h b/include/linux/mfd/max77693.h
> index f0b6585..30fa19ea 100644
> --- a/include/linux/mfd/max77693.h
> +++ b/include/linux/mfd/max77693.h
> @@ -88,14 +88,15 @@ enum max77693_led_boost_mode {
> };
>
> struct max77693_led_platform_data {
> + const char *label[2];
> u32 fleds[2];
> u32 iout_torch[2];
> u32 iout_flash[2];
> u32 trigger[2];
> u32 trigger_type[2];
> + u32 flash_timeout[2];
> u32 num_leds;
> u32 boost_mode;
> - u32 flash_timeout;
> u32 boost_vout;
> u32 low_vsys;
> };
I'm guessing this will effect the other patches in the set?
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
Hi Pavel,
Thanks for a review.
On 11/29/2014 01:58 PM, Pavel Machek wrote:
> Hi!
>
>> +Flash LED handling under Linux
>> +==============================
>> +
>> +Some LED devices support two modes - torch and flash. The modes are
>> +supported by the LED class (see Documentation/leds/leds-class.txt)
>> +and LED Flash class respectively.
>> +
>> +In order to enable support for flash LEDs CONFIG_LEDS_CLASS_FLASH symbol
>> +must be defined in the kernel config. A flash LED driver must register
>> +in the LED subsystem with led_classdev_flash_register to gain flash
>> +capabilities.
>> +
>> +Following sysfs attributes are exposed for controlling flash led devices:
>> +
>> + - flash_brightness - flash LED brightness in microamperes (RW)
>> + - max_flash_brightness - maximum available flash LED brightness (RO)
>> + - indicator_brightness - privacy LED brightness in microamperes (RW)
>> + - max_indicator_brightness - maximum privacy LED brightness in
>> + microamperes (RO)
>> + - flash_timeout - flash strobe duration in microseconds (RW)
>> + - max_flash_timeout - maximum available flash strobe duration (RO)
>> + - flash_strobe - flash strobe state (RW)
>> + - flash_sync_strobe - one flash device can control more than one
>> + sub-led; when this atrribute is set to 1
>> + the flash led will be strobed synchronously
>> + with the other ones controlled by the same
>> + device (RW)
>
> This is not really clear. Does flash_timeout or flash_brightness need
> to be set, first?
I would go for inheriting the settings from the led that is strobed
explicitly. Limits regarding current, the ones from device tree node,
would have to however be preserved in my opinion.
A consensus is needed here.
> Do we really want to have separate indicator brightnesses in uA?
> Should we maybe reuse existing "brightness" parameter for torch and
> indication, maybe adding single (RO) indicator_brightness attribute?
I forgot to remove the indicator related positions. It has been
definitely removed from the LED subsystem related patches.
>> + - flash_fault - bitmask of flash faults that may have occurred,
>> + possible flags are:
>> + * 0x01 - flash controller voltage to the flash LED has exceeded
>> + the limit specific to the flash controller
>> + * 0x02 - the flash strobe was still on when the timeout set by
>> + the user has expired; not all flash controllers may
>> + set this in all such conditions
>> + * 0x04 - the flash controller has overheated
>> + * 0x08 - the short circuit protection of the flash controller
>> + has been triggered
>> + * 0x10 - current in the LED power supply has exceeded the limit
>> + specific to the flash controller
>> + * 0x40 - flash controller voltage to the flash LED has been
>> + below the minimum limit specific to the flash
>> + * 0x80 - the input voltage of the flash controller is below
>> + the limit under which strobing the flash at full
>> + current will not be possible. The condition persists
>> + until this flag is no longer set
>> + * 0x100 - the temperature of the LED has exceeded its allowed
>> + upper limit
>
> How are faults cleared? Should it be list of strings, instead of
> bitmask? We may want to add new fault modes in future...
Faults are cleared by reading the attribute. I will add this note.
There can be more than one fault at a time. I think that the bitmask
is a flexible solution. I don't see any troubles related to adding
new fault modes in the future, do you?
Best Regards,
Jacek Anaszewski
Hi Pavel,
Thanks for a review.
On 11/29/2014 08:05 PM, Pavel Machek wrote:
> Hi!
>
>
>> @@ -0,0 +1,472 @@
>> +/*
>> + * LED Flash class driver for the AAT1290
>> + * 1.5A Step-Up Current Regulator for Flash LEDs
>> + *
>> + * Copyright (C) 2014, Samsung Electronics Co., Ltd.
>> + * Author: Jacek Anaszewski <[email protected]>
>> + *
>> + * 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.
>> + */
>> +
>
>> +#define AAT1290_MM_TO_FL_1_92 1
>> +#define AAT1290_MM_TO_FL_3_7 2
>> +#define AAT1290_MM_TO_FL_5_5 3
>> +#define AAT1290_MM_TO_FL_7_3 4
>> +#define AAT1290_MM_TO_FL_9 5
>> +#define AAT1290_MM_TO_FL_10_7 6
>> +#define AAT1290_MM_TO_FL_12_4 7
>> +#define AAT1290_MM_TO_FL_14 8
>> +#define AAT1290_MM_TO_FL_15_9 9
>> +#define AAT1290_MM_TO_FL_17_5 10
>> +#define AAT1290_MM_TO_FL_19_1 11
>> +#define AAT1290_MM_TO_FL_20_8 12
>> +#define AAT1290_MM_TO_FL_22_4 13
>> +#define AAT1290_MM_TO_FL_24 14
>> +#define AAT1290_MM_TO_FL_25_6 15
>> +#define AAT1290_MM_TO_FL_OFF 16
>
> Only one of these defines is unused.
The only cost of these macros are lines of code and I think
it is worth of having all the possible settings listed in one place.
>
>> +static struct of_device_id aat1290_led_dt_match[] = {
>
>> + {.compatible = "skyworks,aat1290"},
>
> spaces after { and before } ?
I see both styles in kernel, but ok, I will add spaces.
Regards,
Jacek
Hi Lee,
Thanks for the review.
On 12/01/2014 12:34 PM, Lee Jones wrote:
> On Fri, 28 Nov 2014, Jacek Anaszewski wrote:
>
>> Add "label" array for Device Tree strings with
>> the name of a LED device and make flash_timeout
>> a two element array, for caching the sub-led
>> related flash timeout.
>
> <------------------------------------------------------------------------->
>
> Please use all of the 75 char buffer.
OK.
>> Signed-off-by: Jacek Anaszewski <[email protected]>
>> Signed-off-by: Andrzej Hajda <[email protected]>
>> Acked-by: Kyungmin Park <[email protected]>
>> Cc: Lee Jones <[email protected]>
>> Cc: SangYoung Son <[email protected]>
>> Cc: Samuel Ortiz <[email protected]>
>> ---
>> include/linux/mfd/max77693.h | 3 ++-
>> 1 file changed, 2 insertions(+), 1 deletion(-)
>>
>> diff --git a/include/linux/mfd/max77693.h b/include/linux/mfd/max77693.h
>> index f0b6585..30fa19ea 100644
>> --- a/include/linux/mfd/max77693.h
>> +++ b/include/linux/mfd/max77693.h
>> @@ -88,14 +88,15 @@ enum max77693_led_boost_mode {
>> };
>>
>> struct max77693_led_platform_data {
>> + const char *label[2];
>> u32 fleds[2];
>> u32 iout_torch[2];
>> u32 iout_flash[2];
>> u32 trigger[2];
>> u32 trigger_type[2];
>> + u32 flash_timeout[2];
>> u32 num_leds;
>> u32 boost_mode;
>> - u32 flash_timeout;
>> u32 boost_vout;
>> u32 low_vsys;
>> };
>
> I'm guessing this will effect the other patches in the set?
>
max77692 flash driver depends on it and it has to be
in synch with the related DT bindings patch.
Best Regards,
Jacek Anaszewski
On Mon, 01 Dec 2014, Jacek Anaszewski wrote:
> On 12/01/2014 12:34 PM, Lee Jones wrote:
> >On Fri, 28 Nov 2014, Jacek Anaszewski wrote:
> >
> >I'm guessing this will effect the other patches in the set?
> >
>
> max77692 flash driver depends on it and it has to be
> in synch with the related DT bindings patch.
Very well. Providing you address the commitlog issues:
Acked-by: Lee Jones <[email protected]>
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
Hi Pavel,
Thanks for the review.
On 11/29/2014 08:26 PM, Pavel Machek wrote:
> Hi!
>
>> diff --git a/Documentation/devicetree/bindings/mfd/max77693.txt b/Documentation/devicetree/bindings/mfd/max77693.txt
>> index 01e9f30..50a8dad 100644
>> --- a/Documentation/devicetree/bindings/mfd/max77693.txt
>> +++ b/Documentation/devicetree/bindings/mfd/max77693.txt
>> @@ -41,6 +41,62 @@ Optional properties:
>> To get more informations, please refer to documentaion.
>> [*] refer Documentation/devicetree/bindings/pwm/pwm.txt
>>
>> +- led-flash : the LED submodule device node
>> +
>> +There are two led outputs available - fled1 and fled2. Each of them can
>> +control a separate led or they can be connected together to double
>> +the maximum current for a single connected led. One led is represented
>> +by one child node.
>> +
>> +Required properties:
>> +- compatible : must be "maxim,max77693-flash"
>> +
>> +Optional properties:
>> +- maxim,fleds : array of current outputs in order: fled1, fled2
>> + Note: both current outputs can be connected to a single led
>> + Possible values:
>> + 0 - the output is left disconnected,
>> + 1 - a diode is connected to the output.
>
> Is this one needed? Just ommit child note if it is not there.
It is needed because you can have one led connected two both
outputs. This allows to describe such a design.
>> +- maxim,trigger-type : Array of trigger types in order: flash, torch
>> + Possible trigger types:
>> + 0 - Rising edge of the signal triggers the flash/torch,
>> + 1 - Signal level controls duration of the flash/torch.
>> +- maxim,trigger : Array of flags indicating which trigger can activate given led
>> + in order: fled1, fled2
>> + Possible flag values (can be combined):
>> + 1 - FLASH pin of the chip,
>> + 2 - TORCH pin of the chip,
>> + 4 - software via I2C command.
>
> Is it good idea to have bitfields like this?
>
> Make these required properties of the subnode?
This is related to a single property: trigger. I think that splitting
it to three properties would make unnecessary noise in the
binding.
Best Regards,
Jacek Anaszewski
Hi!
> >Is this one needed? Just ommit child note if it is not there.
>
> It is needed because you can have one led connected two both
> outputs. This allows to describe such a design.
Ok.
> >>+- maxim,trigger-type : Array of trigger types in order: flash, torch
> >>+ Possible trigger types:
> >>+ 0 - Rising edge of the signal triggers the flash/torch,
> >>+ 1 - Signal level controls duration of the flash/torch.
> >>+- maxim,trigger : Array of flags indicating which trigger can activate given led
> >>+ in order: fled1, fled2
> >>+ Possible flag values (can be combined):
> >>+ 1 - FLASH pin of the chip,
> >>+ 2 - TORCH pin of the chip,
> >>+ 4 - software via I2C command.
> >
> >Is it good idea to have bitfields like this?
> >
> >Make these required properties of the subnode?
>
> This is related to a single property: trigger. I think that splitting
> it to three properties would make unnecessary noise in the
> binding.
Well, maybe it is not that much noise, and you'll have useful names
(not a bitfield).
Should these properties move to the LED subnode?
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Hi!
> >How are faults cleared? Should it be list of strings, instead of
> >bitmask? We may want to add new fault modes in future...
>
> Faults are cleared by reading the attribute. I will add this note.
> There can be more than one fault at a time. I think that the bitmask
> is a flexible solution. I don't see any troubles related to adding
> new fault modes in the future, do you?
I do not think that "read attribute to clear" is good idea. Normally,
you'd want the error attribute world-readable, but you don't want
non-root users to clear the errors.
I am not sure if bitmask is good solution. I'd return space-separated
strings like "overtemp". That way, there's good chance that other LED
drivers would be able to use similar interface...
Best regards,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Hi Pavel,
On 12/01/2014 02:02 PM, Pavel Machek wrote:
> Hi!
>
>>> Is this one needed? Just ommit child note if it is not there.
>>
>> It is needed because you can have one led connected two both
>> outputs. This allows to describe such a design.
>
> Ok.
>
>>>> +- maxim,trigger-type : Array of trigger types in order: flash, torch
>>>> + Possible trigger types:
>>>> + 0 - Rising edge of the signal triggers the flash/torch,
>>>> + 1 - Signal level controls duration of the flash/torch.
>>>> +- maxim,trigger : Array of flags indicating which trigger can activate given led
>>>> + in order: fled1, fled2
>>>> + Possible flag values (can be combined):
>>>> + 1 - FLASH pin of the chip,
>>>> + 2 - TORCH pin of the chip,
>>>> + 4 - software via I2C command.
>>>
>>> Is it good idea to have bitfields like this?
>>>
>>> Make these required properties of the subnode?
>>
>> This is related to a single property: trigger. I think that splitting
>> it to three properties would make unnecessary noise in the
>> binding.
>
> Well, maybe it is not that much noise, and you'll have useful names
> (not a bitfield).
I think we'd need an opinion of at least one more person :)
> Should these properties move to the LED subnode?
I would leave them device specific.
Regards,
Jacek
Hi Pavel,
On 12/01/2014 02:04 PM, Pavel Machek wrote:
> Hi!
>
>>> How are faults cleared? Should it be list of strings, instead of
>>> bitmask? We may want to add new fault modes in future...
>>
>> Faults are cleared by reading the attribute. I will add this note.
>> There can be more than one fault at a time. I think that the bitmask
>> is a flexible solution. I don't see any troubles related to adding
>> new fault modes in the future, do you?
>
> I do not think that "read attribute to clear" is good idea. Normally,
> you'd want the error attribute world-readable, but you don't want
> non-root users to clear the errors.
This is also V4L2_CID_FLASH_FAULT control semantics.
Moreover many devices clear the errors upon reading register.
I don't see anything wrong in the fact that an user can clear
an error. If the user has a permission to use a device then
it also should be allowed to clear the errors.
> I am not sure if bitmask is good solution. I'd return space-separated
> strings like "overtemp". That way, there's good chance that other LED
> drivers would be able to use similar interface...
The format of a sysfs attribute should be concise.
The error codes are generic and map directly to the V4L2 Flash
error codes.
Best Regards,
Jacek Anaszewski
On 11/28/2014 10:17 AM, Jacek Anaszewski wrote:
> This patch adds helper functions for registering/unregistering
> LED Flash class devices as V4L2 sub-devices. The functions should
> be called from the LED subsystem device driver. In case the
> support for V4L2 Flash sub-devices is disabled in the kernel
> config the functions' empty versions will be used.
>
> Signed-off-by: Jacek Anaszewski <[email protected]>
> Acked-by: Kyungmin Park <[email protected]>
> Cc: Sakari Ailus <[email protected]>
> Cc: Hans Verkuil <[email protected]>
> ---
> drivers/media/v4l2-core/Kconfig | 11 +
> drivers/media/v4l2-core/Makefile | 2 +
> drivers/media/v4l2-core/v4l2-flash.c | 516 ++++++++++++++++++++++++++++++++++
> include/media/v4l2-flash.h | 138 +++++++++
> 4 files changed, 667 insertions(+)
> create mode 100644 drivers/media/v4l2-core/v4l2-flash.c
> create mode 100644 include/media/v4l2-flash.h
>
> diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
> index ba7e21a..f034f1a 100644
> --- a/drivers/media/v4l2-core/Kconfig
> +++ b/drivers/media/v4l2-core/Kconfig
> @@ -44,6 +44,17 @@ config V4L2_MEM2MEM_DEV
> tristate
> depends on VIDEOBUF2_CORE
>
> +# Used by LED subsystem flash drivers
> +config V4L2_FLASH_LED_CLASS
> + tristate "Enable support for Flash sub-devices"
> + depends on VIDEO_V4L2_SUBDEV_API
> + depends on LEDS_CLASS_FLASH
> + ---help---
> + Say Y here to enable support for Flash sub-devices, which allow
> + to control LED class devices with use of V4L2 Flash controls.
> +
> + When in doubt, say N.
> +
> # 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 63d29f2..44e858c 100644
> --- a/drivers/media/v4l2-core/Makefile
> +++ b/drivers/media/v4l2-core/Makefile
> @@ -22,6 +22,8 @@ obj-$(CONFIG_VIDEO_TUNER) += tuner.o
>
> obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
>
> +obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash.o
> +
> obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
> obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
> obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
> diff --git a/drivers/media/v4l2-core/v4l2-flash.c b/drivers/media/v4l2-core/v4l2-flash.c
> new file mode 100644
> index 0000000..f5075b0
> --- /dev/null
> +++ b/drivers/media/v4l2-core/v4l2-flash.c
> @@ -0,0 +1,516 @@
> +/*
> + * V4L2 Flash LED sub-device registration helpers.
> + *
> + * Copyright (C) 2014 Samsung Electronics Co., Ltd
> + * Author: Jacek Anaszewski <[email protected]>
> + *
> + * 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."
> + */
> +
> +#include <linux/led-class-flash.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <media/v4l2-flash.h>
> +
> +#define has_flash_op(v4l2_flash, op) \
> + (v4l2_flash && v4l2_flash->ops->op)
> +
> +#define call_flash_op(v4l2_flash, op, args...) \
> + (has_flash_op(v4l2_flash, op) ? \
> + v4l2_flash->ops->op(args) : \
> + -EINVAL)
> +
> +static inline enum led_brightness v4l2_flash_intensity_to_led_brightness(
> + struct v4l2_ctrl *ctrl,
> + s32 intensity)
> +{
> + s64 __intensity = intensity - ctrl->minimum;
> +
> + do_div(__intensity, ctrl->step);
> +
> + return __intensity + 1;
> +}
> +
> +static inline s32 v4l2_flash_led_brightness_to_intensity(
> + struct v4l2_ctrl *ctrl,
> + enum led_brightness brightness)
> +{
> + return ((brightness - 1) * ctrl->step) + ctrl->minimum;
> +}
> +
> +static int v4l2_flash_g_volatile_ctrl(struct v4l2_ctrl *c)
> +{
> + struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
> + struct led_classdev_flash *flash = v4l2_flash->flash;
> + struct led_classdev *led_cdev = &flash->led_cdev;
> + struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
> + bool is_strobing;
> + int ret;
> +
> + switch (c->id) {
> + case V4L2_CID_FLASH_TORCH_INTENSITY:
> + /*
> + * Update torch brightness only if in TORCH_MODE.
> + * In other modes torch led is turned off, which
> + * would spuriously inform the user space that
> + * V4L2_CID_FLASH_TORCH_INTENSITY control setting
> + * has changed.
> + */
> + if (ctrls[LED_MODE]->val == V4L2_FLASH_LED_MODE_TORCH) {
> + ret = led_update_brightness(led_cdev);
> + if (ret < 0)
> + return ret;
> + c->val = v4l2_flash_led_brightness_to_intensity(
> + ctrls[TORCH_INTENSITY],
> + led_cdev->brightness);
> + }
> + return 0;
> + case V4L2_CID_FLASH_INDICATOR_INTENSITY:
> + ret = led_update_brightness(led_cdev);
> + if (ret < 0)
> + return ret;
> + c->val = v4l2_flash_led_brightness_to_intensity(
> + ctrls[INDICATOR_INTENSITY],
> + led_cdev->brightness);
> + return 0;
> + case V4L2_CID_FLASH_INTENSITY:
> + ret = led_update_flash_brightness(flash);
> + if (ret < 0)
> + return ret;
> + /* no conversion is needed */
> + c->val = flash->brightness.val;
> + return 0;
> + case V4L2_CID_FLASH_STROBE_STATUS:
> + ret = led_get_flash_strobe(flash, &is_strobing);
> + if (ret < 0)
> + return ret;
> + c->val = is_strobing;
> + return 0;
> + case V4L2_CID_FLASH_FAULT:
> + /* led faults map directly to V4L2 flash faults */
> + return led_get_flash_fault(flash, &c->val);
> + case V4L2_CID_FLASH_SYNC_STROBE:
> + c->val = flash->sync_strobe;
> + return 0;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c)
> +{
> + struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
> + struct led_classdev_flash *flash = v4l2_flash->flash;
> + struct led_classdev *led_cdev = &flash->led_cdev;
> + struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
> + enum led_brightness brightness;
> + bool external_strobe;
> + int ret = 0;
> +
> + switch (c->id) {
> + case V4L2_CID_FLASH_LED_MODE:
> + switch (c->val) {
> + case V4L2_FLASH_LED_MODE_NONE:
> + led_set_brightness(led_cdev, LED_OFF);
> + return led_set_flash_strobe(flash, false);
> + case V4L2_FLASH_LED_MODE_FLASH:
> + /* Turn the torch LED off */
> + led_set_brightness(led_cdev, LED_OFF);
> + external_strobe = (ctrls[STROBE_SOURCE]->val ==
> + V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
> +
> + if (has_flash_op(v4l2_flash, external_strobe_set))
> + ret = call_flash_op(v4l2_flash,
> + external_strobe_set, v4l2_flash,
> + external_strobe);
> + return ret;
> + case V4L2_FLASH_LED_MODE_TORCH:
> + /* Stop flash strobing */
> + ret = led_set_flash_strobe(flash, false);
> + if (ret < 0)
> + return ret;
> +
> + brightness =
> + v4l2_flash_intensity_to_led_brightness(
> + ctrls[TORCH_INTENSITY],
> + ctrls[TORCH_INTENSITY]->val);
> + led_set_brightness(led_cdev, brightness);
> + return 0;
> + }
> + break;
> + case V4L2_CID_FLASH_STROBE_SOURCE:
> + external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
> +
> + return call_flash_op(v4l2_flash, external_strobe_set,
> + v4l2_flash, external_strobe);
> + case V4L2_CID_FLASH_STROBE:
> + if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH ||
> + ctrls[STROBE_SOURCE]->val !=
> + V4L2_FLASH_STROBE_SOURCE_SOFTWARE)
> + return -EINVAL;
> + return led_set_flash_strobe(flash, true);
> + case V4L2_CID_FLASH_STROBE_STOP:
> + if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH ||
> + ctrls[STROBE_SOURCE]->val !=
> + V4L2_FLASH_STROBE_SOURCE_SOFTWARE)
> + return -EINVAL;
> + return led_set_flash_strobe(flash, false);
> + case V4L2_CID_FLASH_TIMEOUT:
> + /* no conversion is needed */
> + return led_set_flash_timeout(flash, c->val);
> + case V4L2_CID_FLASH_INTENSITY:
> + /* no conversion is needed */
> + return led_set_flash_brightness(flash, c->val);
> + case V4L2_CID_FLASH_INDICATOR_INTENSITY:
> + brightness = v4l2_flash_intensity_to_led_brightness(
> + ctrls[INDICATOR_INTENSITY],
> + c->val);
> + led_set_brightness(led_cdev, brightness);
> + return 0;
> + case V4L2_CID_FLASH_TORCH_INTENSITY:
> + /*
> + * If not in MODE_TORCH don't call led-class brightness_set
> + * op, as it would result in turning the torch led on.
> + * Instead the value is cached only and will be written
> + * to the device upon transition to MODE_TORCH.
> + */
> + if (ctrls[LED_MODE]->val == V4L2_FLASH_LED_MODE_TORCH) {
> + brightness =
> + v4l2_flash_intensity_to_led_brightness(
> + ctrls[TORCH_INTENSITY],
> + c->val);
> + led_set_brightness(led_cdev, brightness);
> + }
> + return 0;
> + case V4L2_CID_FLASH_SYNC_STROBE:
> + flash->sync_strobe = c->val;
> + return 0;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static const struct v4l2_ctrl_ops v4l2_flash_ctrl_ops = {
> + .g_volatile_ctrl = v4l2_flash_g_volatile_ctrl,
> + .s_ctrl = v4l2_flash_s_ctrl,
> +};
> +
> +static void fill_ctrl_init_data(struct v4l2_flash *v4l2_flash,
> + struct v4l2_flash_ctrl_config *flash_cfg,
> + struct v4l2_flash_ctrl_data *ctrl_init_data)
> +{
> + struct led_classdev_flash *flash = v4l2_flash->flash;
> + const struct led_flash_ops *flash_ops = flash->ops;
> + struct led_classdev *led_cdev = &flash->led_cdev;
> + struct v4l2_ctrl_config *ctrl_cfg;
> + u32 mask;
> + s64 max;
> +
> + /* Init FLASH_LED_MODE ctrl data */
> + mask = 1 << V4L2_FLASH_LED_MODE_NONE |
> + 1 << V4L2_FLASH_LED_MODE_TORCH;
> + if (led_cdev->flags & LED_DEV_CAP_FLASH)
> + mask |= 1 << V4L2_FLASH_LED_MODE_FLASH;
I didn't take into account the case when there is no LED_DEV_CAP_FLASH
flag set but there is has_indicator flag set in the
v4l2_flash_ctrl_config. In fact this is the only valid arrangement
in the new approach where the indicator led will have its own
LED Flash class device and the related v4l2_subdev.
Actually has_indicator is the residue after previous version of the
patch set. It should be renamed to e.g. indicator_led. Similarly
indicator_intensity should be removed, torch_intensity renamed to
something more generic for both torch and indicator leds e.g.
led_intensity or just intensity like in case of proposed
new leds/common.txt bindings where the property max-microamp doesn't
have any additional segment and there is flash-max-microamp property
for flash leds. Mode and torch intensity controls also shouldn't be
created for indicator leds.
Summarizing - indicator led should have maximum two controls:
V4L2_CID_FLASH_INDICATOR_INTENSITY and V4L2_CID_FLASH_FAULT
Best Regards,
Jacek Anaszewski
> + ctrl_init_data[LED_MODE].supported = true;
> + ctrl_cfg = &ctrl_init_data[LED_MODE].config;
> + ctrl_cfg->id = V4L2_CID_FLASH_LED_MODE;
> + ctrl_cfg->max = V4L2_FLASH_LED_MODE_TORCH;
> + ctrl_cfg->menu_skip_mask = ~mask;
> + ctrl_cfg->def = V4L2_FLASH_LED_MODE_NONE;
> + ctrl_cfg->flags = 0;
> +
> + /* Init TORCH_INTENSITY ctrl data */
> + ctrl_init_data[TORCH_INTENSITY].supported = true;
> + ctrl_init_data[TORCH_INTENSITY].config = flash_cfg->torch_intensity;
> + ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config;
> + ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY;
> + ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
> +
> + if (!(led_cdev->flags & LED_DEV_CAP_FLASH))
> + return;
> +
> + /* Init FLASH_STROBE_SOURCE ctrl data */
> + mask = 1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
> + if (flash_cfg->has_external_strobe) {
> + mask |= 1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
> + max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
> + } else {
> + max = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
> + }
> +
> + ctrl_init_data[STROBE_SOURCE].supported = true;
> + ctrl_cfg = &ctrl_init_data[STROBE_SOURCE].config;
> + ctrl_cfg->id = V4L2_CID_FLASH_STROBE_SOURCE;
> + ctrl_cfg->max = max;
> + ctrl_cfg->menu_skip_mask = ~mask;
> + ctrl_cfg->def = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
> +
> + /* Init FLASH_STROBE ctrl data */
> + ctrl_init_data[FLASH_STROBE].supported = true;
> + ctrl_cfg = &ctrl_init_data[FLASH_STROBE].config;
> + ctrl_cfg->id = V4L2_CID_FLASH_STROBE;
> +
> + /* Init STROBE_STOP ctrl data */
> + ctrl_init_data[STROBE_STOP].supported = true;
> + ctrl_cfg = &ctrl_init_data[STROBE_STOP].config;
> + ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STOP;
> +
> + /* Init STROBE_STATUS ctrl data */
> + if (flash_ops->strobe_get) {
> + ctrl_init_data[STROBE_STATUS].supported = true;
> + ctrl_cfg = &ctrl_init_data[STROBE_STATUS].config;
> + ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STATUS;
> + ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
> + V4L2_CTRL_FLAG_READ_ONLY;
> + }
> +
> + /* Init FLASH_TIMEOUT ctrl data */
> + if (flash_ops->timeout_set) {
> + ctrl_init_data[FLASH_TIMEOUT].supported = true;
> + ctrl_init_data[FLASH_TIMEOUT].config = flash_cfg->flash_timeout;
> + ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config;
> + ctrl_cfg->id = V4L2_CID_FLASH_TIMEOUT;
> + ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
> + }
> +
> + /* Init FLASH_INTENSITY ctrl data */
> + if (flash_ops->flash_brightness_set) {
> + ctrl_init_data[FLASH_INTENSITY].supported = true;
> + ctrl_init_data[FLASH_INTENSITY].config =
> + flash_cfg->flash_intensity;
> + ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config;
> + ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY;
> + ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
> + }
> +
> + /* Init INDICATOR_INTENSITY ctrl data */
> + if (flash_cfg->has_indicator) {
> + ctrl_init_data[INDICATOR_INTENSITY].supported = true;
> + ctrl_init_data[INDICATOR_INTENSITY].config =
> + flash_cfg->indicator_intensity;
> + ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config;
> + ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY;
> + ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
> + }
> +
> + /* Init FLASH_FAULT ctrl data */
> + if (flash_cfg->flash_faults) {
> + ctrl_init_data[FLASH_FAULT].supported = true;
> + ctrl_cfg = &ctrl_init_data[FLASH_FAULT].config;
> + ctrl_cfg->id = V4L2_CID_FLASH_FAULT;
> + ctrl_cfg->max = flash_cfg->flash_faults;
> + ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
> + V4L2_CTRL_FLAG_READ_ONLY;
> + }
> +
> + if (led_cdev->flags & LED_DEV_CAP_COMPOUND) {
> + ctrl_init_data[SYNC_STROBE].supported = true;
> + ctrl_cfg = &ctrl_init_data[SYNC_STROBE].config;
> + ctrl_cfg->id = V4L2_CID_FLASH_SYNC_STROBE;
> + ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
> + }
> +}
> +
> +static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash,
> + struct v4l2_flash_ctrl_config *flash_cfg)
> +
> +{
> + struct v4l2_flash_ctrl_data *ctrl_init_data;
> + struct v4l2_ctrl *ctrl;
> + struct v4l2_ctrl_config *ctrl_cfg;
> + int i, ret, num_ctrls = 0;
> +
> + /* allocate memory dynamically so as not to exceed stack frame size */
> + ctrl_init_data = kcalloc(NUM_FLASH_CTRLS, sizeof(*ctrl_init_data),
> + GFP_KERNEL);
> + if (!ctrl_init_data)
> + return -ENOMEM;
> +
> + memset(ctrl_init_data, 0, sizeof(*ctrl_init_data));
> +
> + fill_ctrl_init_data(v4l2_flash, flash_cfg, ctrl_init_data);
> +
> + for (i = 0; i < NUM_FLASH_CTRLS; ++i)
> + if (ctrl_init_data[i].supported)
> + ++num_ctrls;
> +
> + v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls);
> +
> + for (i = 0; i < NUM_FLASH_CTRLS; ++i) {
> + ctrl_cfg = &ctrl_init_data[i].config;
> + if (!ctrl_init_data[i].supported)
> + continue;
> +
> + if (ctrl_cfg->id == V4L2_CID_FLASH_LED_MODE ||
> + ctrl_cfg->id == V4L2_CID_FLASH_STROBE_SOURCE)
> + ctrl = v4l2_ctrl_new_std_menu(&v4l2_flash->hdl,
> + &v4l2_flash_ctrl_ops,
> + ctrl_cfg->id,
> + ctrl_cfg->max,
> + ctrl_cfg->menu_skip_mask,
> + ctrl_cfg->def);
> + else
> + ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl,
> + &v4l2_flash_ctrl_ops,
> + ctrl_cfg->id,
> + ctrl_cfg->min,
> + ctrl_cfg->max,
> + ctrl_cfg->step,
> + ctrl_cfg->def);
> +
> + if (ctrl)
> + ctrl->flags |= ctrl_cfg->flags;
> +
> + if (i <= STROBE_SOURCE)
> + v4l2_flash->ctrls[i] = ctrl;
> + }
> +
> + kfree(ctrl_init_data);
> +
> + if (v4l2_flash->hdl.error) {
> + ret = v4l2_flash->hdl.error;
> + goto error_free_handler;
> + }
> +
> + v4l2_ctrl_handler_setup(&v4l2_flash->hdl);
> +
> + v4l2_flash->sd.ctrl_handler = &v4l2_flash->hdl;
> +
> + return 0;
> +
> +error_free_handler:
> + v4l2_ctrl_handler_free(&v4l2_flash->hdl);
> + return ret;
> +}
> +
> +/*
> + * V4L2 subdev internal operations
> + */
> +
> +static int v4l2_flash_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> + struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd);
> + struct led_classdev_flash *flash = v4l2_flash->flash;
> + struct led_classdev *led_cdev = &flash->led_cdev;
> + int ret = 0;
> +
> + mutex_lock(&led_cdev->led_access);
> +
> + if (!v4l2_fh_is_singular(&fh->vfh)) {
> + ret = -EBUSY;
> + goto unlock;
> + }
> +
> + led_sysfs_disable(led_cdev);
> +
> +unlock:
> + mutex_unlock(&led_cdev->led_access);
> + return ret;
> +}
> +
> +static int v4l2_flash_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> + struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd);
> + struct led_classdev_flash *flash = v4l2_flash->flash;
> + struct led_classdev *led_cdev = &flash->led_cdev;
> + int ret = 0;
> +
> + mutex_lock(&led_cdev->led_access);
> +
> + if (has_flash_op(v4l2_flash, external_strobe_set))
> + ret = call_flash_op(v4l2_flash, external_strobe_set,
> + v4l2_flash, false);
> + led_sysfs_enable(led_cdev);
> +
> + mutex_unlock(&led_cdev->led_access);
> +
> + return ret;
> +}
> +
> +static const struct v4l2_subdev_internal_ops v4l2_flash_subdev_internal_ops = {
> + .open = v4l2_flash_open,
> + .close = v4l2_flash_close,
> +};
> +
> +static const struct v4l2_subdev_core_ops v4l2_flash_core_ops = {
> + .queryctrl = v4l2_subdev_queryctrl,
> + .querymenu = v4l2_subdev_querymenu,
> +};
> +
> +static const struct v4l2_subdev_ops v4l2_flash_subdev_ops = {
> + .core = &v4l2_flash_core_ops,
> +};
> +
> +struct v4l2_flash *v4l2_flash_init(struct led_classdev_flash *flash,
> + const struct v4l2_flash_ops *ops,
> + struct v4l2_flash_ctrl_config *config)
> +{
> + struct v4l2_flash *v4l2_flash;
> + struct led_classdev *led_cdev = &flash->led_cdev;
> + struct v4l2_subdev *sd;
> + int ret;
> +
> + if (!flash || !ops || !config)
> + return ERR_PTR(-EINVAL);
> +
> + v4l2_flash = kzalloc(sizeof(*v4l2_flash), GFP_KERNEL);
> + if (!v4l2_flash)
> + return ERR_PTR(-ENOMEM);
> +
> + sd = &v4l2_flash->sd;
> + v4l2_flash->flash = flash;
> + v4l2_flash->ops = ops;
> + sd->dev = led_cdev->dev->parent;
> + v4l2_subdev_init(sd, &v4l2_flash_subdev_ops);
> + sd->internal_ops = &v4l2_flash_subdev_internal_ops;
> + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> + snprintf(sd->name, sizeof(sd->name), led_cdev->name);
> +
> + ret = v4l2_flash_init_controls(v4l2_flash, config);
> + if (ret < 0)
> + goto err_init_controls;
> +
> + ret = media_entity_init(&sd->entity, 0, NULL, 0);
> + if (ret < 0)
> + goto err_init_entity;
> +
> + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
> +
> + ret = v4l2_async_register_subdev(sd);
> + if (ret < 0)
> + goto err_init_entity;
> +
> + return v4l2_flash;
> +
> +err_init_entity:
> + media_entity_cleanup(&sd->entity);
> +err_init_controls:
> + v4l2_ctrl_handler_free(sd->ctrl_handler);
> + kfree(v4l2_flash);
> +
> + return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(v4l2_flash_init);
> +
> +void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
> +{
> + struct v4l2_subdev *sd = &v4l2_flash->sd;
> +
> + if (!v4l2_flash)
> + return;
> +
> + v4l2_async_unregister_subdev(sd);
> + media_entity_cleanup(&sd->entity);
> + v4l2_ctrl_handler_free(sd->ctrl_handler);
> + kfree(v4l2_flash);
> +}
> +EXPORT_SYMBOL_GPL(v4l2_flash_release);
> +
> +MODULE_AUTHOR("Jacek Anaszewski <[email protected]>");
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("V4L2 Flash sub-device helpers");
> diff --git a/include/media/v4l2-flash.h b/include/media/v4l2-flash.h
> new file mode 100644
> index 0000000..1e18df5
> --- /dev/null
> +++ b/include/media/v4l2-flash.h
> @@ -0,0 +1,138 @@
> +/*
> + * V4L2 Flash LED sub-device registration helpers.
> + *
> + * Copyright (C) 2014 Samsung Electronics Co., Ltd
> + * Author: Jacek Anaszewski <[email protected]>
> + *
> + * 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."
> + */
> +
> +#ifndef _V4L2_FLASH_H
> +#define _V4L2_FLASH_H
> +
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-ioctl.h>
> +
> +struct led_classdev_flash;
> +struct led_classdev;
> +struct v4l2_flash;
> +enum led_brightness;
> +
> +enum ctrl_init_data_id {
> + LED_MODE,
> + TORCH_INTENSITY,
> + STROBE_SOURCE,
> + FLASH_STROBE,
> + STROBE_STOP,
> + STROBE_STATUS,
> + FLASH_TIMEOUT,
> + FLASH_INTENSITY,
> + INDICATOR_INTENSITY,
> + FLASH_FAULT,
> + SYNC_STROBE,
> + NUM_FLASH_CTRLS,
> +};
> +
> +/*
> + * struct v4l2_flash_ctrl_data - flash control initialization data -
> + * filled basing on the features declared
> + * by the LED Flash class driver
> + * @config: initialization data for a control
> + * @supported: indicates whether a control is supported
> + * by the LED Flash class driver
> + */
> +struct v4l2_flash_ctrl_data {
> + struct v4l2_ctrl_config config;
> + bool supported;
> +};
> +
> +struct v4l2_flash_ops {
> + /* setup strobing the flash by hardware pin state assertion */
> + int (*external_strobe_set)(struct v4l2_flash *v4l2_flash,
> + bool enable);
> +};
> +
> +/**
> + * struct v4l2_flash_ctrl_config - V4L2 Flash controls initialization data
> + * @torch_intensity: V4L2_CID_FLASH_TORCH_INTENSITY constraints
> + * @flash_intensity: V4L2_CID_FLASH_INTENSITY constraints
> + * @indicator_intensity: V4L2_CID_FLASH_INDICATOR_INTENSITY constraints
> + * @flash_timeout: V4L2_CID_FLASH_TIMEOUT constraints
> + * @flash_fault: possible flash faults
> + * @has_external_strobe: external strobe capability
> + */
> +struct v4l2_flash_ctrl_config {
> + struct v4l2_ctrl_config torch_intensity;
> + struct v4l2_ctrl_config flash_intensity;
> + struct v4l2_ctrl_config indicator_intensity;
> + struct v4l2_ctrl_config flash_timeout;
> + u32 flash_faults;
> + bool has_external_strobe:1;
> + bool has_indicator:1;
> +};
> +
> +/**
> + * struct v4l2_flash - Flash sub-device context
> + * @flash: LED Flash Class device controlled by this sub-device
> + * @ops: V4L2 specific flash ops
> + * @sd: V4L2 sub-device
> + * @hdl: flash controls handler
> + * @ctrls: array of pointers to controls, whose values define
> + the sub-device state
> + */
> +struct v4l2_flash {
> + struct led_classdev_flash *flash;
> + const struct v4l2_flash_ops *ops;
> +
> + struct v4l2_subdev sd;
> + struct v4l2_ctrl_handler hdl;
> + struct v4l2_ctrl *ctrls[STROBE_SOURCE + 1];
> +};
> +
> +static inline struct v4l2_flash *v4l2_subdev_to_v4l2_flash(
> + struct v4l2_subdev *sd)
> +{
> + return container_of(sd, struct v4l2_flash, sd);
> +}
> +
> +static inline struct v4l2_flash *v4l2_ctrl_to_v4l2_flash(struct v4l2_ctrl *c)
> +{
> + return container_of(c->handler, struct v4l2_flash, hdl);
> +}
> +
> +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
> +/**
> + * v4l2_flash_init - initialize V4L2 flash led sub-device
> + * @led_fdev: the LED Flash Class device to wrap
> + * @config: initialization data for V4L2 Flash controls
> + * @flash_ops: V4L2 Flash device ops
> + *
> + * Create V4L2 subdev wrapping given LED subsystem device.
> +
> + * Returns: A valid pointer, or, when an error occurs, the return
> + * value is encoded using ERR_PTR(). Use IS_ERR() to check and
> + * PTR_ERR() to obtain the numeric return value.
> + */
> +struct v4l2_flash *v4l2_flash_init(struct led_classdev_flash *led_fdev,
> + const struct v4l2_flash_ops *ops,
> + struct v4l2_flash_ctrl_config *config);
> +
> +/**
> + * v4l2_flash_release - release V4L2 Flash sub-device
> + * @flash: the V4L2 Flash device to release
> + *
> + * Release V4L2 flash led subdev.
> + */
> +void v4l2_flash_release(struct v4l2_flash *v4l2_flash);
> +
> +#else
> +#define v4l2_flash_init(led_cdev, ops, config) (NULL)
> +#define v4l2_flash_release(v4l2_flash)
> +#endif /* CONFIG_V4L2_FLASH_LED_CLASS */
> +
> +#endif /* _V4L2_FLASH_H */
>
Hi Jacek and Pavel,
On Mon, Dec 01, 2014 at 02:58:56PM +0100, Jacek Anaszewski wrote:
> Hi Pavel,
>
> On 12/01/2014 02:04 PM, Pavel Machek wrote:
> >Hi!
> >
> >>>How are faults cleared? Should it be list of strings, instead of
> >>>bitmask? We may want to add new fault modes in future...
> >>
> >>Faults are cleared by reading the attribute. I will add this note.
> >>There can be more than one fault at a time. I think that the bitmask
> >>is a flexible solution. I don't see any troubles related to adding
> >>new fault modes in the future, do you?
> >
> >I do not think that "read attribute to clear" is good idea. Normally,
> >you'd want the error attribute world-readable, but you don't want
> >non-root users to clear the errors.
>
> This is also V4L2_CID_FLASH_FAULT control semantics.
> Moreover many devices clear the errors upon reading register.
> I don't see anything wrong in the fact that an user can clear
> an error. If the user has a permission to use a device then
> it also should be allowed to clear the errors.
I agree. Some of these such as the timeout are not hardware related
problems at all, but others may be. I'd keep the same semantics as V4L2
already does.
> >I am not sure if bitmask is good solution. I'd return space-separated
> >strings like "overtemp". That way, there's good chance that other LED
> >drivers would be able to use similar interface...
>
> The format of a sysfs attribute should be concise.
> The error codes are generic and map directly to the V4L2 Flash
> error codes.
I'd guess a single application accesses either of the interfaces. From that
point of view it doesn't matter what are the bit definitions in V4L2.
The behaviour on sysfs could also be different, e.g. writing the attribute
would clear the faults. This would need more functionality from drivers.
The V4L2 behaviour mirrors the typical behaviour of flash controllers ---
the chips mostly do not operate until the faults have been read, and the
interface as well as the interface take no stance to that. So when the user
reads the fault control value, the fault register on the chip is cleared as
well.
--
Kind regards,
Sakari Ailus
e-mail: [email protected] XMPP: [email protected]
On Mon, Dec 1, 2014 at 5:58 AM, Jacek Anaszewski
<[email protected]> wrote:
> Hi Pavel,
>
> On 12/01/2014 02:04 PM, Pavel Machek wrote:
>>
>> Hi!
>>
>>>> How are faults cleared? Should it be list of strings, instead of
>>>> bitmask? We may want to add new fault modes in future...
>>>
>>>
>>> Faults are cleared by reading the attribute. I will add this note.
>>> There can be more than one fault at a time. I think that the bitmask
>>> is a flexible solution. I don't see any troubles related to adding
>>> new fault modes in the future, do you?
>>
>>
>> I do not think that "read attribute to clear" is good idea. Normally,
>> you'd want the error attribute world-readable, but you don't want
>> non-root users to clear the errors.
>
>
> This is also V4L2_CID_FLASH_FAULT control semantics.
> Moreover many devices clear the errors upon reading register.
> I don't see anything wrong in the fact that an user can clear
> an error. If the user has a permission to use a device then
> it also should be allowed to clear the errors.
>
>> I am not sure if bitmask is good solution. I'd return space-separated
>> strings like "overtemp". That way, there's good chance that other LED
>> drivers would be able to use similar interface...
>
>
> The format of a sysfs attribute should be concise.
> The error codes are generic and map directly to the V4L2 Flash
> error codes.
>
Actually I'd like to see those flash fault code defined in LED
subsystem. And V4L2 will just include LED flash header file to use it.
Because flash fault code is not for V4L2 specific but it's a feature
of LED flash devices.
For clearing error code of flash devices, I think it depends on the
hardware. If most of our LED flash is using reading to clear error
code, we probably can make it simple as this now. But what if some
other LED flash devices are using writing to clear error code? we
should provide a API to that?
-Bryan
On Fri, Nov 28, 2014 at 1:17 AM, Jacek Anaszewski
<[email protected]> wrote:
> This patch extends LED Flash class documention by
> the description of interactions with v4l2-flash sub-device.
>
> Signed-off-by: Jacek Anaszewski <[email protected]>
> Acked-by: Kyungmin Park <[email protected]>
> Cc: Bryan Wu <[email protected]>
> Cc: Richard Purdie <[email protected]>
> ---
This patch looks good to me. I will take it with other patches!
Thanks,
-Bryan
> Documentation/leds/leds-class-flash.txt | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
> diff --git a/Documentation/leds/leds-class-flash.txt b/Documentation/leds/leds-class-flash.txt
> index d68565c..1a611ec 100644
> --- a/Documentation/leds/leds-class-flash.txt
> +++ b/Documentation/leds/leds-class-flash.txt
> @@ -46,3 +46,16 @@ Following sysfs attributes are exposed for controlling flash led devices:
> until this flag is no longer set
> * 0x100 - the temperature of the LED has exceeded its allowed
> upper limit
> +
> +A LED subsystem driver can be controlled also from the level of VideoForLinux2
> +subsystem. In order to enable this CONFIG_V4L2_FLASH_LED_CLASS symbol has to
> +be defined in the kernel config. The driver must call the v4l2_flash_init
> +function to get registered in the V4L2 subsystem. On remove the
> +v4l2_flash_release function has to be called (see <media/v4l2-flash.h>).
> +
> +After proper initialization a V4L2 Flash sub-device is created. The sub-device
> +exposes a number of V4L2 controls, which allow for controlling a LED Flash class
> +device with use of its internal kernel API.
> +Opening the V4L2 Flash sub-device makes the LED subsystem sysfs interface
> +unavailable. The interface is re-enabled after the V4L2 Flash sub-device
> +is closed.
> --
> 1.7.9.5
>
> > The format of a sysfs attribute should be concise.
> > The error codes are generic and map directly to the V4L2 Flash
> > error codes.
> >
>
> Actually I'd like to see those flash fault code defined in LED
> subsystem. And V4L2 will just include LED flash header file to use it.
> Because flash fault code is not for V4L2 specific but it's a feature
> of LED flash devices.
>
> For clearing error code of flash devices, I think it depends on the
> hardware. If most of our LED flash is using reading to clear error
> code, we probably can make it simple as this now. But what if some
> other LED flash devices are using writing to clear error code? we
> should provide a API to that?
Actually, we should provide API that makes sense, and that is easy to
use by userspace.
I believe "read" is called read because it does not change anything,
and it should stay that way in /sysfs. You may want to talk to sysfs
maintainers if you plan on doing another semantics.
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
On 12/06/2014 01:43 PM, Pavel Machek wrote:
>
>>> The format of a sysfs attribute should be concise.
>>> The error codes are generic and map directly to the V4L2 Flash
>>> error codes.
>>>
>>
>> Actually I'd like to see those flash fault code defined in LED
>> subsystem. And V4L2 will just include LED flash header file to use it.
>> Because flash fault code is not for V4L2 specific but it's a feature
>> of LED flash devices.
>>
>> For clearing error code of flash devices, I think it depends on the
>> hardware. If most of our LED flash is using reading to clear error
>> code, we probably can make it simple as this now. But what if some
>> other LED flash devices are using writing to clear error code? we
>> should provide a API to that?
>
> Actually, we should provide API that makes sense, and that is easy to
> use by userspace.
>
> I believe "read" is called read because it does not change anything,
> and it should stay that way in /sysfs. You may want to talk to sysfs
> maintainers if you plan on doing another semantics.
How would you proceed in case of devices which clear their fault
register upon I2C readout (e.g. AS3645)? In this case read does have
a side effect. For such devices attribute semantics would have to be
different than for the devices which don't clear faults on readout.
In case of devices which use writing to clear error code - I'd do that
after reading flash_fault attribute, in the same callback.
Best Regards,
Jacek Anaszewski
On 12/05/2014 08:27 PM, Bryan Wu wrote:
> On Fri, Nov 28, 2014 at 1:17 AM, Jacek Anaszewski
> <[email protected]> wrote:
>> Some LED devices support two operation modes - torch and flash.
>> This patch provides support for flash LED devices in the LED subsystem
>> by introducing new sysfs attributes and kernel internal interface.
>> The attributes being introduced are: flash_brightness, flash_strobe,
>> flash_timeout, max_flash_timeout, max_flash_brightness, flash_fault
>> and flash_sync_strobe. All the flash related features are placed
>> in a separate module. Torch mode is supported by the LED class
>> interface.
>>
>> The modifications aim to be compatible with V4L2 framework requirements
>> related to the flash devices management. The design assumes that V4L2
>> sub-device can take of the LED class device control and communicate
>> with it through the kernel internal interface. When V4L2 Flash sub-device
>> file is opened, the LED class device sysfs interface is made
>> unavailable.
>>
>> Signed-off-by: Jacek Anaszewski <[email protected]>
>> Acked-by: Kyungmin Park <[email protected]>
>> Cc: Bryan Wu <[email protected]>
>> Cc: Richard Purdie <[email protected]>
>> ---
>> drivers/leds/Kconfig | 10 +
>> drivers/leds/Makefile | 1 +
>> drivers/leds/led-class-flash.c | 446 +++++++++++++++++++++++++++++++++++++++
>> drivers/leds/led-class.c | 4 +
>> include/linux/led-class-flash.h | 198 +++++++++++++++++
>> include/linux/leds.h | 3 +
>> 6 files changed, 662 insertions(+)
>> create mode 100644 drivers/leds/led-class-flash.c
>> create mode 100644 include/linux/led-class-flash.h
>>
>> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
>> index b3c0d8a..fa8021e 100644
>> --- a/drivers/leds/Kconfig
>> +++ b/drivers/leds/Kconfig
>> @@ -19,6 +19,16 @@ config LEDS_CLASS
>> This option enables the led sysfs class in /sys/class/leds. You'll
>> need this to do anything useful with LEDs. If unsure, say N.
>>
>> +config LEDS_CLASS_FLASH
>> + tristate "LED Flash Class Support"
>> + depends on LEDS_CLASS
>
> Looks like it requires V4L2, so probably add depends on V4L2 or similar.
> But actually I want to see LED Flash class doesn't depends on V4L2. Is
> that possible to do that?
> For example a non-V4L2 application want to control the LED as a flash?
It doesn't require V4L2. It only used v4l2-controls.h for error code
macros, but this is also going to be changed.
> Other than this main concern, I'm good with this patch now.
>
> -Bryan
>
>> + help
>> + This option enables the flash led sysfs class in /sys/class/leds.
>> + It wrapps LED Class and adds flash LEDs specific sysfs attributes
>> + and kernel internal API to it. You'll need this to provide support
>> + for the flash related features of a LED device. It can be built
>> + as a module.
>> +
>> comment "LED drivers"
>>
>> config LEDS_88PM860X
>> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
>> index 1c65a19..cbba921 100644
>> --- a/drivers/leds/Makefile
>> +++ b/drivers/leds/Makefile
>> @@ -2,6 +2,7 @@
>> # LED Core
>> obj-$(CONFIG_NEW_LEDS) += led-core.o
>> obj-$(CONFIG_LEDS_CLASS) += led-class.o
>> +obj-$(CONFIG_LEDS_CLASS_FLASH) += led-class-flash.o
>> obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
>>
>> # LED Platform Drivers
>> diff --git a/drivers/leds/led-class-flash.c b/drivers/leds/led-class-flash.c
>> new file mode 100644
>> index 0000000..219b414
>> --- /dev/null
>> +++ b/drivers/leds/led-class-flash.c
>> @@ -0,0 +1,446 @@
>> +/*
>> + * LED Flash class interface
>> + *
>> + * Copyright (C) 2014 Samsung Electronics Co., Ltd.
>> + * Author: Jacek Anaszewski <[email protected]>
>> + *
>> + * 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.
>> + */
>> +
>> +#include <linux/device.h>
>> +#include <linux/init.h>
>> +#include <linux/led-class-flash.h>
>> +#include <linux/leds.h>
>> +#include <linux/module.h>
>> +#include <linux/slab.h>
>> +#include "leds.h"
>> +
>> +#define has_flash_op(flash, op) \
>> + (flash && flash->ops->op)
>> +
>> +#define call_flash_op(flash, op, args...) \
>> + ((has_flash_op(flash, op)) ? \
>> + (flash->ops->op(flash, args)) : \
>> + -EINVAL)
>> +
>> +static ssize_t flash_brightness_store(struct device *dev,
>> + struct device_attribute *attr, const char *buf, size_t size)
>> +{
>> + struct led_classdev *led_cdev = dev_get_drvdata(dev);
>> + struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
>> + unsigned long state;
>> + ssize_t ret;
>> +
>> + mutex_lock(&led_cdev->led_access);
>> +
>> + if (led_sysfs_is_disabled(led_cdev)) {
>> + ret = -EBUSY;
>> + goto unlock;
>> + }
>> +
>> + ret = kstrtoul(buf, 10, &state);
>> + if (ret)
>> + goto unlock;
>> +
>> + ret = led_set_flash_brightness(flash, state);
>> + if (ret < 0)
>> + goto unlock;
>> +
>> + ret = size;
>> +unlock:
>> + mutex_unlock(&led_cdev->led_access);
>> + return ret;
>> +}
>> +
>> +static ssize_t flash_brightness_show(struct device *dev,
>> + struct device_attribute *attr, char *buf)
>> +{
>> + struct led_classdev *led_cdev = dev_get_drvdata(dev);
>> + struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
>> +
>> + /* no lock needed for this */
>> + led_update_flash_brightness(flash);
>> +
>> + return sprintf(buf, "%u\n", flash->brightness.val);
>> +}
>> +static DEVICE_ATTR_RW(flash_brightness);
>> +
>> +static ssize_t max_flash_brightness_show(struct device *dev,
>> + struct device_attribute *attr, char *buf)
>> +{
>> + struct led_classdev *led_cdev = dev_get_drvdata(dev);
>> + struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
>> +
>> + return sprintf(buf, "%u\n", flash->brightness.max);
>> +}
>> +static DEVICE_ATTR_RO(max_flash_brightness);
>> +
>> +static ssize_t flash_strobe_store(struct device *dev,
>> + struct device_attribute *attr, const char *buf, size_t size)
>> +{
>> + struct led_classdev *led_cdev = dev_get_drvdata(dev);
>> + struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
>> + unsigned long state;
>> + ssize_t ret = -EINVAL;
>> +
>> + mutex_lock(&led_cdev->led_access);
>> +
>> + if (led_sysfs_is_disabled(led_cdev)) {
>> + ret = -EBUSY;
>> + goto unlock;
>> + }
>> +
>> + ret = kstrtoul(buf, 10, &state);
>> + if (ret)
>> + goto unlock;
>> +
>> + if (state < 0 || state > 1) {
>> + ret = -EINVAL;
>> + goto unlock;
>> + }
>> +
>> + ret = led_set_flash_strobe(flash, state);
>> + if (ret < 0)
>> + goto unlock;
>> + ret = size;
>> +unlock:
>> + mutex_unlock(&led_cdev->led_access);
>> + return ret;
>> +}
>> +
>> +static ssize_t flash_strobe_show(struct device *dev,
>> + struct device_attribute *attr, char *buf)
>> +{
>> + struct led_classdev *led_cdev = dev_get_drvdata(dev);
>> + struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
>> + bool state;
>> + int ret;
>> +
>> + /* no lock needed for this */
>> + ret = led_get_flash_strobe(flash, &state);
>> + if (ret < 0)
>> + return ret;
>> +
>> + return sprintf(buf, "%u\n", state);
>> +}
>> +static DEVICE_ATTR_RW(flash_strobe);
>> +
>> +static ssize_t flash_timeout_store(struct device *dev,
>> + struct device_attribute *attr, const char *buf, size_t size)
>> +{
>> + struct led_classdev *led_cdev = dev_get_drvdata(dev);
>> + struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
>> + unsigned long flash_timeout;
>> + ssize_t ret;
>> +
>> + mutex_lock(&led_cdev->led_access);
>> +
>> + if (led_sysfs_is_disabled(led_cdev)) {
>> + ret = -EBUSY;
>> + goto unlock;
>> + }
>> +
>> + ret = kstrtoul(buf, 10, &flash_timeout);
>> + if (ret)
>> + goto unlock;
>> +
>> + ret = led_set_flash_timeout(flash, flash_timeout);
>> + if (ret < 0)
>> + goto unlock;
>> +
>> + ret = size;
>> +unlock:
>> + mutex_unlock(&led_cdev->led_access);
>> + return ret;
>> +}
>> +
>> +static ssize_t flash_timeout_show(struct device *dev,
>> + struct device_attribute *attr, char *buf)
>> +{
>> + struct led_classdev *led_cdev = dev_get_drvdata(dev);
>> + struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
>> +
>> + return sprintf(buf, "%u\n", flash->timeout.val);
>> +}
>> +static DEVICE_ATTR_RW(flash_timeout);
>> +
>> +static ssize_t max_flash_timeout_show(struct device *dev,
>> + struct device_attribute *attr, char *buf)
>> +{
>> + struct led_classdev *led_cdev = dev_get_drvdata(dev);
>> + struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
>> +
>> + return sprintf(buf, "%u\n", flash->timeout.max);
>> +}
>> +static DEVICE_ATTR_RO(max_flash_timeout);
>> +
>> +static ssize_t flash_fault_show(struct device *dev,
>> + struct device_attribute *attr, char *buf)
>> +{
>> + struct led_classdev *led_cdev = dev_get_drvdata(dev);
>> + struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
>> + u32 fault;
>> + int ret;
>> +
>> + ret = led_get_flash_fault(flash, &fault);
>> + if (ret < 0)
>> + return -EINVAL;
>> +
>> + return sprintf(buf, "0x%8.8x\n", fault);
>> +}
>> +static DEVICE_ATTR_RO(flash_fault);
>> +
>> +static ssize_t flash_sync_strobe_store(struct device *dev,
>> + struct device_attribute *attr, const char *buf, size_t size)
>> +{
>> + struct led_classdev *led_cdev = dev_get_drvdata(dev);
>> + struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
>> + unsigned long sync_strobe;
>> + ssize_t ret;
>> +
>> + mutex_lock(&led_cdev->led_access);
>> +
>> + if (led_sysfs_is_disabled(led_cdev)) {
>> + ret = -EBUSY;
>> + goto unlock;
>> + }
>> +
>> + ret = kstrtoul(buf, 10, &sync_strobe);
>> + if (ret)
>> + goto unlock;
>> +
>> + flash->sync_strobe = sync_strobe;
>> +
>> + ret = size;
>> +unlock:
>> + mutex_unlock(&led_cdev->led_access);
>> + return ret;
>> +}
>> +
>> +static ssize_t flash_sync_strobe_show(struct device *dev,
>> + struct device_attribute *attr, char *buf)
>> +{
>> + struct led_classdev *led_cdev = dev_get_drvdata(dev);
>> + struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
>> +
>> + return sprintf(buf, "%u\n", flash->sync_strobe);
>> +}
>> +static DEVICE_ATTR_RW(flash_sync_strobe);
>> +
>> +static struct attribute *led_flash_strobe_attrs[] = {
>> + &dev_attr_flash_strobe.attr,
>> + NULL,
>> +};
>> +
>> +static struct attribute *led_flash_timeout_attrs[] = {
>> + &dev_attr_flash_timeout.attr,
>> + &dev_attr_max_flash_timeout.attr,
>> + NULL,
>> +};
>> +
>> +static struct attribute *led_flash_brightness_attrs[] = {
>> + &dev_attr_flash_brightness.attr,
>> + &dev_attr_max_flash_brightness.attr,
>> + NULL,
>> +};
>> +
>> +static struct attribute *led_flash_fault_attrs[] = {
>> + &dev_attr_flash_fault.attr,
>> + NULL,
>> +};
>> +
>> +static struct attribute *led_flash_sync_strobe_attrs[] = {
>> + &dev_attr_flash_sync_strobe.attr,
>> + NULL,
>> +};
>> +
>> +static const struct attribute_group led_flash_strobe_group = {
>> + .attrs = led_flash_strobe_attrs,
>> +};
>> +
>> +static const struct attribute_group led_flash_timeout_group = {
>> + .attrs = led_flash_timeout_attrs,
>> +};
>> +
>> +static const struct attribute_group led_flash_brightness_group = {
>> + .attrs = led_flash_brightness_attrs,
>> +};
>> +
>> +static const struct attribute_group led_flash_fault_group = {
>> + .attrs = led_flash_fault_attrs,
>> +};
>> +
>> +static const struct attribute_group led_flash_sync_strobe_group = {
>> + .attrs = led_flash_sync_strobe_attrs,
>> +};
>> +
>> +static const struct attribute_group *flash_groups[] = {
>> + &led_flash_strobe_group,
>> + NULL,
>> + NULL,
>> + NULL,
>> + NULL,
>> + NULL,
>> + NULL
>> +};
>> +
>> +static void led_flash_resume(struct led_classdev *led_cdev)
>> +{
>> + struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
>> +
>> + call_flash_op(flash, flash_brightness_set, flash->brightness.val);
>> + call_flash_op(flash, timeout_set, flash->timeout.val);
>> +}
>> +
>> +static void led_flash_init_sysfs_groups(struct led_classdev_flash *flash)
>> +{
>> + struct led_classdev *led_cdev = &flash->led_cdev;
>> + const struct led_flash_ops *ops = flash->ops;
>> + int num_sysfs_groups = 1;
>> +
>> + if (ops->flash_brightness_set)
>> + flash_groups[num_sysfs_groups++] = &led_flash_brightness_group;
>> +
>> + if (ops->timeout_set)
>> + flash_groups[num_sysfs_groups++] = &led_flash_timeout_group;
>> +
>> + if (ops->fault_get)
>> + flash_groups[num_sysfs_groups++] = &led_flash_fault_group;
>> +
>> + if (led_cdev->flags & LED_DEV_CAP_COMPOUND)
>> + flash_groups[num_sysfs_groups++] = &led_flash_sync_strobe_group;
>> +
>> + led_cdev->groups = flash_groups;
>> +}
>> +
>> +int led_classdev_flash_register(struct device *parent,
>> + struct led_classdev_flash *flash)
>> +{
>> + struct led_classdev *led_cdev;
>> + const struct led_flash_ops *ops;
>> + int ret;
>> +
>> + if (!flash)
>> + return -EINVAL;
>> +
>> + led_cdev = &flash->led_cdev;
>> +
>> + if (led_cdev->flags & LED_DEV_CAP_FLASH) {
>> + if (!led_cdev->brightness_set_sync)
>> + return -EINVAL;
>> +
>> + ops = flash->ops;
>> + if (!ops || !ops->strobe_set)
>> + return -EINVAL;
>> +
>> + led_cdev->flash_resume = led_flash_resume;
>> +
>> + /* Select the sysfs attributes to be created for the device */
>> + led_flash_init_sysfs_groups(flash);
>> + }
>> +
>> + /* Register led class device */
>> + ret = led_classdev_register(parent, led_cdev);
>> + if (ret < 0)
>> + return ret;
>> +
>> + /* Setting a torch brightness needs to have immediate effect */
>> + led_cdev->flags &= ~SET_BRIGHTNESS_ASYNC;
>> + led_cdev->flags |= SET_BRIGHTNESS_SYNC;
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(led_classdev_flash_register);
>> +
>> +void led_classdev_flash_unregister(struct led_classdev_flash *flash)
>> +{
>> + if (!flash)
>> + return;
>> +
>> + led_classdev_unregister(&flash->led_cdev);
>> +}
>> +EXPORT_SYMBOL_GPL(led_classdev_flash_unregister);
>> +
>> +int led_set_flash_strobe(struct led_classdev_flash *flash, bool state)
>> +{
>> + return call_flash_op(flash, strobe_set, state);
>> +}
>> +EXPORT_SYMBOL_GPL(led_set_flash_strobe);
>> +
>> +int led_get_flash_strobe(struct led_classdev_flash *flash, bool *state)
>> +{
>> + return call_flash_op(flash, strobe_get, state);
>> +}
>> +EXPORT_SYMBOL_GPL(led_get_flash_strobe);
>> +
>> +static void led_clamp_align(struct led_flash_setting *s)
>> +{
>> + u32 v, offset;
>> +
>> + v = s->val + s->step / 2;
>> + v = clamp(v, s->min, s->max);
>> + offset = v - s->min;
>> + offset = s->step * (offset / s->step);
>> + s->val = s->min + offset;
>> +}
>> +
>> +int led_set_flash_timeout(struct led_classdev_flash *flash, u32 timeout)
>> +{
>> + struct led_classdev *led_cdev = &flash->led_cdev;
>> + struct led_flash_setting *s = &flash->timeout;
>> +
>> + s->val = timeout;
>> + led_clamp_align(s);
>> +
>> + if (!(led_cdev->flags & LED_SUSPENDED))
>> + return call_flash_op(flash, timeout_set, s->val);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(led_set_flash_timeout);
>> +
>> +int led_get_flash_fault(struct led_classdev_flash *flash, u32 *fault)
>> +{
>> + return call_flash_op(flash, fault_get, fault);
>> +}
>> +EXPORT_SYMBOL_GPL(led_get_flash_fault);
>> +
>> +int led_set_flash_brightness(struct led_classdev_flash *flash,
>> + u32 brightness)
>> +{
>> + struct led_classdev *led_cdev = &flash->led_cdev;
>> + struct led_flash_setting *s = &flash->brightness;
>> +
>> + s->val = brightness;
>> + led_clamp_align(s);
>> +
>> + if (!(led_cdev->flags & LED_SUSPENDED))
>> + return call_flash_op(flash, flash_brightness_set, s->val);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(led_set_flash_brightness);
>> +
>> +int led_update_flash_brightness(struct led_classdev_flash *flash)
>> +{
>> + struct led_flash_setting *s = &flash->brightness;
>> + u32 brightness;
>> +
>> + if (has_flash_op(flash, flash_brightness_get)) {
>> + int ret = call_flash_op(flash, flash_brightness_get,
>> + &brightness);
>> + if (ret < 0)
>> + return ret;
>> +
>> + s->val = brightness;
>> + }
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(led_update_flash_brightness);
>> +
>> +MODULE_AUTHOR("Jacek Anaszewski <[email protected]>");
>> +MODULE_LICENSE("GPL");
>> +MODULE_DESCRIPTION("LED Flash class Interface");
>> diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
>> index dbeebac..02564c5 100644
>> --- a/drivers/leds/led-class.c
>> +++ b/drivers/leds/led-class.c
>> @@ -179,6 +179,10 @@ EXPORT_SYMBOL_GPL(led_classdev_suspend);
>> void led_classdev_resume(struct led_classdev *led_cdev)
>> {
>> led_cdev->brightness_set(led_cdev, led_cdev->brightness);
>> +
>> + if (led_cdev->flash_resume)
>> + led_cdev->flash_resume(led_cdev);
>> +
>> led_cdev->flags &= ~LED_SUSPENDED;
>> }
>> EXPORT_SYMBOL_GPL(led_classdev_resume);
>> diff --git a/include/linux/led-class-flash.h b/include/linux/led-class-flash.h
>> new file mode 100644
>> index 0000000..5188d9fd
>> --- /dev/null
>> +++ b/include/linux/led-class-flash.h
>> @@ -0,0 +1,198 @@
>> +/*
>> + * LED Flash class interface
>> + *
>> + * Copyright (C) 2014 Samsung Electronics Co., Ltd.
>> + * Author: Jacek Anaszewski <[email protected]>
>> + *
>> + * 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.
>> + *
>> + */
>> +#ifndef __LINUX_FLASH_LEDS_H_INCLUDED
>> +#define __LINUX_FLASH_LEDS_H_INCLUDED
>> +
>> +#include <linux/leds.h>
>> +#include <uapi/linux/v4l2-controls.h>
>> +
>> +struct device_node;
>> +struct led_classdev_flash;
>> +
>> +/*
>> + * Supported led fault bits - must be kept in synch
>> + * with V4L2_FLASH_FAULT bits.
>> + */
>> +#define LED_FAULT_OVER_VOLTAGE V4L2_FLASH_FAULT_OVER_VOLTAGE
>> +#define LED_FAULT_TIMEOUT V4L2_FLASH_FAULT_TIMEOUT
>> +#define LED_FAULT_OVER_TEMPERATURE V4L2_FLASH_FAULT_OVER_TEMPERATURE
>> +#define LED_FAULT_SHORT_CIRCUIT V4L2_FLASH_FAULT_SHORT_CIRCUIT
>> +#define LED_FAULT_OVER_CURRENT V4L2_FLASH_FAULT_OVER_CURRENT
>> +#define LED_FAULT_INDICATOR V4L2_FLASH_FAULT_INDICATOR
>> +#define LED_FAULT_UNDER_VOLTAGE V4L2_FLASH_FAULT_UNDER_VOLTAGE
>> +#define LED_FAULT_INPUT_VOLTAGE V4L2_FLASH_FAULT_INPUT_VOLTAGE
>> +#define LED_FAULT_LED_OVER_TEMPERATURE V4L2_FLASH_OVER_TEMPERATURE
>> +
>
> I suggest we move those V4L2_FLASH_FAULT_* to our LED subsystem as LED_FAULT_*.
> Then V4L2 Flash control code should include that LED header file and
> #define V4L2_FLASH_FAULT_* LED_FAULT_*
>
> Because this flash fault feature is for LED actually not for V4L2.
>
> Then we can decouple V4L2 and LED Flash class driver here.
I'm OK with it. Sakari, what do you think about spinning it the other
way round - V4L2 will use LED Flash class fault definitions?
>> +struct led_flash_ops {
>> + /* set flash brightness */
>> + int (*flash_brightness_set)(struct led_classdev_flash *flash,
>> + u32 brightness);
>> + /* get flash brightness */
>> + int (*flash_brightness_get)(struct led_classdev_flash *flash,
>> + u32 *brightness);
>> + /* set flash strobe state */
>> + int (*strobe_set)(struct led_classdev_flash *flash, bool state);
>> + /* get flash strobe state */
>> + int (*strobe_get)(struct led_classdev_flash *flash, bool *state);
>> + /* set flash timeout */
>> + int (*timeout_set)(struct led_classdev_flash *flash, u32 timeout);
>> + /* get the flash LED fault */
>> + int (*fault_get)(struct led_classdev_flash *flash, u32 *fault);
>> +};
>> +
>> +/*
>> + * Current value of a flash setting along
>> + * with its constraints.
>> + */
>> +struct led_flash_setting {
>> + /* maximum allowed value */
>> + u32 min;
>> + /* maximum allowed value */
>> + u32 max;
>> + /* step value */
>> + u32 step;
>> + /* current value */
>> + u32 val;
>> +};
>> +
>> +/*
>> + * Aggregated flash settings - designed for ease
>> + * of passing initialization data to the clients
>> + * wrapping a LED Flash class device.
>> + */
>> +struct led_flash_config {
>> + struct led_flash_setting torch_brightness;
>> + struct led_flash_setting flash_brightness;
>> + struct led_flash_setting flash_timeout;
>> + u32 flash_faults;
>> +};
>> +
>> +struct led_classdev_flash {
>> + /* led class device */
>> + struct led_classdev led_cdev;
>> +
>> + /* flash led specific ops */
>> + const struct led_flash_ops *ops;
>> +
>> + /* flash brightness value in microamperes along with its constraints */
>> + struct led_flash_setting brightness;
>> +
>> + /* flash timeout value in microseconds along with its constraints */
>> + struct led_flash_setting timeout;
>> +
>> + /*
>> + * Indicates whether the flash sub-led should strobe
>> + * upon strobe activation on any of the remaining sub-leds.
>> + */
>> + bool sync_strobe:1;
>> +};
>> +
>> +static inline struct led_classdev_flash *lcdev_to_flash(
>> + struct led_classdev *lcdev)
>> +{
>> + return container_of(lcdev, struct led_classdev_flash, led_cdev);
>> +}
>> +
>> +/**
>> + * led_classdev_flash_register - register a new object of led_classdev class
>> + * with support for flash LEDs
>> + * @parent: the flash LED to register
>> + * @flash: the led_classdev_flash structure for this device
>> + *
>> + * Returns: 0 on success or negative error value on failure
>> + */
>> +int led_classdev_flash_register(struct device *parent,
>> + struct led_classdev_flash *flash);
>> +
>> +/**
>> + * led_classdev_flash_unregister - unregisters an object of led_classdev class
>> + * with support for flash LEDs
>> + * @flash: the flash LED to unregister
>> + *
>> + * Unregister a previously registered via led_classdev_flash_register object
>> + */
>> +void led_classdev_flash_unregister(struct led_classdev_flash *flash);
>> +
>> +/**
>> + * led_set_flash_strobe - setup flash strobe
>> + * @flash: the flash LED to set strobe on
>> + * @state: 1 - strobe flash, 0 - stop flash strobe
>> + *
>> + * Strobe the flash LED.
>> + *
>> + * Returns: 0 on success or negative error value on failure
>> + */
>> +extern int led_set_flash_strobe(struct led_classdev_flash *flash,
>> + bool state);
>> +
>> +/**
>> + * led_get_flash_strobe - get flash strobe status
>> + * @flash: the flash LED to query
>> + * @state: 1 - flash is strobing, 0 - flash is off
>> + *
>> + * Check whether the flash is strobing at the moment.
>> + *
>> + * Returns: 0 on success or negative error value on failure
>> + */
>> +extern int led_get_flash_strobe(struct led_classdev_flash *flash,
>> + bool *state);
>> +
>> +/**
>> + * led_set_flash_brightness - set flash LED brightness
>> + * @flash: the flash LED to set
>> + * @brightness: the brightness to set it to
>> + *
>> + * Set a flash LED's brightness.
>> + *
>> + * Returns: 0 on success or negative error value on failure
>> + */
>> +extern int led_set_flash_brightness(struct led_classdev_flash *flash,
>> + u32 brightness);
>> +
>> +/**
>> + * led_update_flash_brightness - update flash LED brightness
>> + * @flash: the flash LED to query
>> + *
>> + * Get a flash LED's current brightness and update led_flash->brightness
>> + * member with the obtained value.
>> + *
>> + * Returns: 0 on success or negative error value on failure
>> + */
>> +extern int led_update_flash_brightness(struct led_classdev_flash *flash);
>> +
>> +/**
>> + * led_set_flash_timeout - set flash LED timeout
>> + * @flash: the flash LED to set
>> + * @timeout: the flash timeout to set it to
>> + *
>> + * Set the flash strobe duration. The duration set by the driver
>> + * is returned in the timeout argument and may differ from the
>> + * one that was originally passed.
>> + *
>> + * Returns: 0 on success or negative error value on failure
>> + */
>> +extern int led_set_flash_timeout(struct led_classdev_flash *flash,
>> + u32 timeout);
>> +
>> +/**
>> + * led_get_flash_fault - get the flash LED fault
>> + * @flash: the flash LED to query
>> + * @fault: bitmask containing flash faults
>> + *
>> + * Get the flash LED fault.
>> + *
>> + * Returns: 0 on success or negative error value on failure
>> + */
>> +extern int led_get_flash_fault(struct led_classdev_flash *flash,
>> + u32 *fault);
>> +
>> +#endif /* __LINUX_FLASH_LEDS_H_INCLUDED */
>> diff --git a/include/linux/leds.h b/include/linux/leds.h
>> index cfceef3..c359f35 100644
>> --- a/include/linux/leds.h
>> +++ b/include/linux/leds.h
>> @@ -46,6 +46,8 @@ struct led_classdev {
>> #define LED_SYSFS_DISABLE (1 << 20)
>> #define SET_BRIGHTNESS_ASYNC (1 << 21)
>> #define SET_BRIGHTNESS_SYNC (1 << 22)
>> +#define LED_DEV_CAP_FLASH (1 << 23)
>> +#define LED_DEV_CAP_COMPOUND (1 << 24)
>>
> What's the use case of CAP_COMPOUND?
It signifies that the device controls many sub-leds and the sub-leds
will expose flash_strobe_sync sysfs attribute for strobing the flash
synchronously with the other sub-leds. Please see the driver for
MAX77693.
>> /* Set LED brightness level */
>> /* Must not sleep, use a workqueue if needed */
>> @@ -81,6 +83,7 @@ struct led_classdev {
>> unsigned long blink_delay_on, blink_delay_off;
>> struct timer_list blink_timer;
>> int blink_brightness;
>> + void (*flash_resume)(struct led_classdev *led_cdev);
>>
>> struct work_struct set_brightness_work;
>> int delayed_set_value;
>> --
>> 1.7.9.5
>>
>
Best Regards,
Jacek Anaszewski
On Mon 2014-12-08 17:55:20, Jacek Anaszewski wrote:
> On 12/06/2014 01:43 PM, Pavel Machek wrote:
> >
> >>>The format of a sysfs attribute should be concise.
> >>>The error codes are generic and map directly to the V4L2 Flash
> >>>error codes.
> >>>
> >>
> >>Actually I'd like to see those flash fault code defined in LED
> >>subsystem. And V4L2 will just include LED flash header file to use it.
> >>Because flash fault code is not for V4L2 specific but it's a feature
> >>of LED flash devices.
> >>
> >>For clearing error code of flash devices, I think it depends on the
> >>hardware. If most of our LED flash is using reading to clear error
> >>code, we probably can make it simple as this now. But what if some
> >>other LED flash devices are using writing to clear error code? we
> >>should provide a API to that?
> >
> >Actually, we should provide API that makes sense, and that is easy to
> >use by userspace.
> >
> >I believe "read" is called read because it does not change anything,
> >and it should stay that way in /sysfs. You may want to talk to sysfs
> >maintainers if you plan on doing another semantics.
>
> How would you proceed in case of devices which clear their fault
> register upon I2C readout (e.g. AS3645)? In this case read does have
> a side effect. For such devices attribute semantics would have to be
> different than for the devices which don't clear faults on readout.
No, semantics should be same for all devices.
If device clears fault register during I2C readout, kernel will simply
gather faults in an variable, and clear them upon write to sysfs file.
Best regards,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Hi Pavel,
On 12/08/2014 09:18 PM, Pavel Machek wrote:
> On Mon 2014-12-08 17:55:20, Jacek Anaszewski wrote:
>> On 12/06/2014 01:43 PM, Pavel Machek wrote:
>>>
>>>>> The format of a sysfs attribute should be concise.
>>>>> The error codes are generic and map directly to the V4L2 Flash
>>>>> error codes.
>>>>>
>>>>
>>>> Actually I'd like to see those flash fault code defined in LED
>>>> subsystem. And V4L2 will just include LED flash header file to use it.
>>>> Because flash fault code is not for V4L2 specific but it's a feature
>>>> of LED flash devices.
>>>>
>>>> For clearing error code of flash devices, I think it depends on the
>>>> hardware. If most of our LED flash is using reading to clear error
>>>> code, we probably can make it simple as this now. But what if some
>>>> other LED flash devices are using writing to clear error code? we
>>>> should provide a API to that?
>>>
>>> Actually, we should provide API that makes sense, and that is easy to
>>> use by userspace.
>>>
>>> I believe "read" is called read because it does not change anything,
>>> and it should stay that way in /sysfs. You may want to talk to sysfs
>>> maintainers if you plan on doing another semantics.
>>
>> How would you proceed in case of devices which clear their fault
>> register upon I2C readout (e.g. AS3645)? In this case read does have
>> a side effect. For such devices attribute semantics would have to be
>> different than for the devices which don't clear faults on readout.
>
> No, semantics should be same for all devices.
>
> If device clears fault register during I2C readout, kernel will simply
> gather faults in an variable, and clear them upon write to sysfs file.
This approach would require implementing additional mechanisms on
both sides: LED Flash class core and a LED Flash class driver.
In the former the sysfs attribute write permissions would have
to be decided in the runtime and in the latter caching mechanism
would have to be implemented per driver. We would have to also
consider how to approach the issue in case of sub-leds.
The only reason for this overhead is trying to avoid side effects
on reading sysfs attribute. After weighing the pros and cons,
I am not sure if it is worthwhile.
Best Regards,
Jacek Anaszewski
On Tue 2014-12-09 09:54:06, Jacek Anaszewski wrote:
> Hi Pavel,
>
> On 12/08/2014 09:18 PM, Pavel Machek wrote:
> >On Mon 2014-12-08 17:55:20, Jacek Anaszewski wrote:
> >>On 12/06/2014 01:43 PM, Pavel Machek wrote:
> >>>
> >>>>>The format of a sysfs attribute should be concise.
> >>>>>The error codes are generic and map directly to the V4L2 Flash
> >>>>>error codes.
> >>>>>
> >>>>
> >>>>Actually I'd like to see those flash fault code defined in LED
> >>>>subsystem. And V4L2 will just include LED flash header file to use it.
> >>>>Because flash fault code is not for V4L2 specific but it's a feature
> >>>>of LED flash devices.
> >>>>
> >>>>For clearing error code of flash devices, I think it depends on the
> >>>>hardware. If most of our LED flash is using reading to clear error
> >>>>code, we probably can make it simple as this now. But what if some
> >>>>other LED flash devices are using writing to clear error code? we
> >>>>should provide a API to that?
> >>>
> >>>Actually, we should provide API that makes sense, and that is easy to
> >>>use by userspace.
> >>>
> >>>I believe "read" is called read because it does not change anything,
> >>>and it should stay that way in /sysfs. You may want to talk to sysfs
> >>>maintainers if you plan on doing another semantics.
> >>
> >>How would you proceed in case of devices which clear their fault
> >>register upon I2C readout (e.g. AS3645)? In this case read does have
> >>a side effect. For such devices attribute semantics would have to be
> >>different than for the devices which don't clear faults on readout.
> >
> >No, semantics should be same for all devices.
> >
> >If device clears fault register during I2C readout, kernel will simply
> >gather faults in an variable, and clear them upon write to sysfs file.
>
> This approach would require implementing additional mechanisms on
> both sides: LED Flash class core and a LED Flash class driver.
> In the former the sysfs attribute write permissions would have
> to be decided in the runtime and in the latter caching mechanism
Write attributes at runtime? Why? We can emulate sane and consistent
behaviour for all the controllers: read gives you list of faults,
write clears it. We can do it for all the controllers.
Only cost is few lines of code in the drivers where hardware clears
faults at read.
> would have to be implemented per driver. We would have to also
> consider how to approach the issue in case of sub-leds.
Actually.. sub-leds. That is one physical LED being connected to two
current sources at the same time, right?
> The only reason for this overhead is trying to avoid side effects
> on reading sysfs attribute. After weighing the pros and cons,
> I am not sure if it is worthwhile.
I am pretty sure.
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
On 12/09/2014 04:50 PM, Pavel Machek wrote:
> On Tue 2014-12-09 09:54:06, Jacek Anaszewski wrote:
>> Hi Pavel,
>>
>> On 12/08/2014 09:18 PM, Pavel Machek wrote:
>>> On Mon 2014-12-08 17:55:20, Jacek Anaszewski wrote:
>>>> On 12/06/2014 01:43 PM, Pavel Machek wrote:
>>>>>
>>>>>>> The format of a sysfs attribute should be concise.
>>>>>>> The error codes are generic and map directly to the V4L2 Flash
>>>>>>> error codes.
>>>>>>>
>>>>>>
>>>>>> Actually I'd like to see those flash fault code defined in LED
>>>>>> subsystem. And V4L2 will just include LED flash header file to use it.
>>>>>> Because flash fault code is not for V4L2 specific but it's a feature
>>>>>> of LED flash devices.
>>>>>>
>>>>>> For clearing error code of flash devices, I think it depends on the
>>>>>> hardware. If most of our LED flash is using reading to clear error
>>>>>> code, we probably can make it simple as this now. But what if some
>>>>>> other LED flash devices are using writing to clear error code? we
>>>>>> should provide a API to that?
>>>>>
>>>>> Actually, we should provide API that makes sense, and that is easy to
>>>>> use by userspace.
>>>>>
>>>>> I believe "read" is called read because it does not change anything,
>>>>> and it should stay that way in /sysfs. You may want to talk to sysfs
>>>>> maintainers if you plan on doing another semantics.
>>>>
>>>> How would you proceed in case of devices which clear their fault
>>>> register upon I2C readout (e.g. AS3645)? In this case read does have
>>>> a side effect. For such devices attribute semantics would have to be
>>>> different than for the devices which don't clear faults on readout.
>>>
>>> No, semantics should be same for all devices.
>>>
>>> If device clears fault register during I2C readout, kernel will simply
>>> gather faults in an variable, and clear them upon write to sysfs file.
>>
>> This approach would require implementing additional mechanisms on
>> both sides: LED Flash class core and a LED Flash class driver.
>> In the former the sysfs attribute write permissions would have
>> to be decided in the runtime and in the latter caching mechanism
>
> Write attributes at runtime? Why? We can emulate sane and consistent
> behaviour for all the controllers: read gives you list of faults,
> write clears it. We can do it for all the controllers.
I don't like the idea of listing the faults in the form of strings.
I'd like to see the third opinion :)
> Only cost is few lines of code in the drivers where hardware clears
> faults at read.
As above - the third opinion would be appreciated.
>> would have to be implemented per driver. We would have to also
>> consider how to approach the issue in case of sub-leds.
>
> Actually.. sub-leds. That is one physical LED being connected to two
> current sources at the same time, right?
There are possible designs with two separate LEDs.
--
Best Regards,
Jacek Anaszewski
Hi!
> >>both sides: LED Flash class core and a LED Flash class driver.
> >>In the former the sysfs attribute write permissions would have
> >>to be decided in the runtime and in the latter caching mechanism
> >
> >Write attributes at runtime? Why? We can emulate sane and consistent
> >behaviour for all the controllers: read gives you list of faults,
> >write clears it. We can do it for all the controllers.
>
> I don't like the idea of listing the faults in the form of strings.
> I'd like to see the third opinion :)
Well, I see that you don't like to change existing code. But "I hacked
it this way and I'm going to ask n-th opinion so that I don't have to
touch it" is not a way to design interfaces.
> >Only cost is few lines of code in the drivers where hardware clears
> >faults at read.
>
> As above - the third opinion would be appreciated.
Just email Greg if he likes /sys files with sideffects on read.
Or just think. You never do grep in /sys?
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Hi Pavel and Jacek,
On Tue, Dec 09, 2014 at 04:50:33PM +0100, Pavel Machek wrote:
> On Tue 2014-12-09 09:54:06, Jacek Anaszewski wrote:
> > Hi Pavel,
> >
> > On 12/08/2014 09:18 PM, Pavel Machek wrote:
> > >On Mon 2014-12-08 17:55:20, Jacek Anaszewski wrote:
> > >>On 12/06/2014 01:43 PM, Pavel Machek wrote:
> > >>>
> > >>>>>The format of a sysfs attribute should be concise.
> > >>>>>The error codes are generic and map directly to the V4L2 Flash
> > >>>>>error codes.
> > >>>>>
> > >>>>
> > >>>>Actually I'd like to see those flash fault code defined in LED
> > >>>>subsystem. And V4L2 will just include LED flash header file to use it.
> > >>>>Because flash fault code is not for V4L2 specific but it's a feature
> > >>>>of LED flash devices.
> > >>>>
> > >>>>For clearing error code of flash devices, I think it depends on the
> > >>>>hardware. If most of our LED flash is using reading to clear error
> > >>>>code, we probably can make it simple as this now. But what if some
> > >>>>other LED flash devices are using writing to clear error code? we
> > >>>>should provide a API to that?
> > >>>
> > >>>Actually, we should provide API that makes sense, and that is easy to
> > >>>use by userspace.
> > >>>
> > >>>I believe "read" is called read because it does not change anything,
> > >>>and it should stay that way in /sysfs. You may want to talk to sysfs
> > >>>maintainers if you plan on doing another semantics.
> > >>
> > >>How would you proceed in case of devices which clear their fault
> > >>register upon I2C readout (e.g. AS3645)? In this case read does have
> > >>a side effect. For such devices attribute semantics would have to be
> > >>different than for the devices which don't clear faults on readout.
> > >
> > >No, semantics should be same for all devices.
> > >
> > >If device clears fault register during I2C readout, kernel will simply
> > >gather faults in an variable, and clear them upon write to sysfs file.
> >
> > This approach would require implementing additional mechanisms on
> > both sides: LED Flash class core and a LED Flash class driver.
> > In the former the sysfs attribute write permissions would have
> > to be decided in the runtime and in the latter caching mechanism
>
> Write attributes at runtime? Why? We can emulate sane and consistent
> behaviour for all the controllers: read gives you list of faults,
> write clears it. We can do it for all the controllers.
>
> Only cost is few lines of code in the drivers where hardware clears
> faults at read.
Please take the time to read this, and consider it.
I'd say the cost is I2C register access, not so much a few lines added to
the drivers. The functionality and behaviour between the flash controllers
varies. They have different faults, presence of (some) faults may prevent
strobing, some support reading the flash status and some don't.
Some of the flash faults are mostly relevant in production testing, some can
be used to find hardware issues during use (rare) and some are produced in
common use (timeout, for instance).
The V4L2 flash API defines that reading the faults clears them, but does not
state whether presence of faults would prevent further use of the flash.
This is flash controller chip specific.
I think you *could* force a policy on the level of kernel API, for instance
require that the user clears the faults before strobing again rather than
relying on the chip requiring this instead.
Most of the time there are no faults. When there are, they may appear at
some point of time after the strobing, but how long? Probably roughly after
the timeout period the flash should have faults available if there were any
--- except if the strobe is external such as a sensor timed strobe. In that
case the software running on the CPU has no knowledge when the flash is
strobed nor when the faults should be read. So the requirement of checking
the faults would probably have to be limited to software strobe only. The
user would still have to be able to check the faults for externally strobed
pulses. Would it be acceptable that the interface was different there?
So, after the user has strobed, when the user should check the flash faults?
After the timeout period has passed? Right before strobing again? If this
was a requirement, it adds an additional I2C access to potentially the place
which should absolutely have no extra delay --- the flash strobe time. This
would be highly unwanted.
The faults seldom happened in regular use, but more recent flash controllers
have LED overtemperature or undervoltage faults, the latter of which isn't
really a fault, but status information telling that the flash current will
be limited. Reading the faults in this case is more important than it has
used to be.
Finally, should the LED flash class enforce such a policy, would the V4L2
flash API which is provided to the same devices be changed as well? I'm not
against that if we have
1) can come up with a good policy that is understood to be
meaningful for all thinkable flash controller implementations and
2) agreement the behaviour can be changed.
Btw. I think I'm slightly leaning towards liking flash faults in form of
strings better; that's what much of the sysfs interface already uses. V4L2
is quite a bit different from that; we have a bitmask control for faults
with well defined meanings for the bits in the spec. The LED class API is
much more usable from the command line, and using strings for flash faults
is in line with that. I have no strict stance towards that however;
hexadecimal numbers have advantages as well such as being slightly more
practicable to check in a C program. The importance of good documentatation
increases in that case though, and probably a header file with the bit
definitions is needed as well.
--
Kind regards,
Sakari Ailus
e-mail: [email protected] XMPP: [email protected]