2014-07-11 14:04:36

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 00/21] LED / flash API integration

This is is the fourth version of the patch series being a follow up
of the discussion on Media summit 2013-10-23, related to the
LED / flash API integration (the notes from the discussion were
enclosed in the message [1], paragraph 5).
The series is based on linux-next-20140707

Description of the proposed modifications according to
the kernel components they are relevant to:
- LED subsystem modifications
* added led_flash module which, when enabled in the config,
registers flash specific sysfs attributes, if the driver
declares related features:
- flash_brightness
- max_flash_brightness
- indicator_brightness
- max_indicator_brightness
- flash_timeout
- max_flash_timeout
- flash_strobe
- external_strobe
- flash_fault
and exposes kernel internal API:
- led_classdev_flash_register
- led_classdev_flash_unregister
- led_set_flash_strobe
- led_get_flash_strobe
- led_set_flash_brightness
- led_update_flash_brightness
- led_set_indicator_brightness
- led_update_indicator_brightness
- led_set_flash_timeout
- led_get_flash_fault
- led_set_external_strobe
- led_sysfs_lock
- led_sysfs_unlock
* added Flash Manager functionality, available when
led_flash module is enable in the config;
if the device tree node of a flash led device contains
relevant subnodes, it registers following sysfs attributes:
- strobe_provider
- strobe_providerN
- blocking_strobe
following kernel internal API is exposed by the flash manager:
- led_flash_manager_register_flash
- led_flash_manager_unregister_flash
- led_flash_manager_setup_strobe
- led_flash_manager_bind_async_mux
- led_flash_manager_unbind_async_mux
- Addition of a V4L2 Flash sub-device registration helpers
* added v4l2-flash.c and v4l2-flash.h files with helper
functions that facilitate registration/unregistration
of a subdevice, which wrapps a LED subsystem device and
exposes V4L2 Flash control interface
- Addition of a LED Flash Class driver for the flash cell of
the MAX77693 mfd
- Addition of a LED Flash Class driver for the AAT1290 current
regulator for flash leds along with its DT binding for the
exynos4412-trats2 board, where standalone multiplexer is
used for modifying strobe signal routing - either from the SoC
GPIO or from a camera sensor. This arrangment is handled
by the newly introduced Flash Manager functionality.
- Update of the max77693.txt DT bindings documentation
- Update of the common leds DT bindings documentation

================
Changes since v2
================

- refactored the code so that it is possible to build
led-core without led-flash module
- added v4l2-flash ops which slackens dependency from
the led-flash module
- implemented led_clamp_align_val function and led_ctrl
structure which allows to align led control values
in the manner compatible with V4L2 Flash controls;
the flash brightness and timeout units have been defined
as microamperes and microseconds respectively to properly
support devices which define current and time levels
as fractions of 1/1000.
- added support for the flash privacy leds
- modified LED sysfs locking mechanism - now it locks/unlocks
the interface on V4L2 Flash sub-device file open/close
- changed hw_triggered attribute name to external_strobe,
which maps on the V4L2_FLASH_STROBE_SOURCE_EXTERNAL name
more intuitively
- made external_strobe and indicator related sysfs attributes
created optionally only if related features are declared
by the led device driver
- removed from the series patches modifying exynos4-is media
controller - a proposal for "flash manager" which will take
care of flash devices registration is due to be submitted
- removed modifications to the LED class devices documentation,
it will be covered after the whole functionality is accepted

================
Changes since v3
================

- added Flash Manager feature
- added generic LED Flash Class gpio mux driver
- added sample async mux driver
- added of helpers for parsing Flash Manager related
device tree data
- added V4L2_CID_FLASH_STROBE_PROVIDER control
- introduced struct led_classdev_flash, which wrapps
struct led_classdev
- made all flash ops, except strobe_set, optional; if an op
is absent the related sysfs attribute isn't created
- added LED_DEV_CAP* flags
- modified v4l2-flash helpers to create v4l2_device
for v4l2_flash subdevices to register in it
- modified max77693-flash driver and its DT bindings
to allow for registering either one or two LED Flash
Class devices, depending on the device tree settings.
- added new API for setting torch_brightness
- extended leds common DT binding documentation

Issues:

1) Who should register V4L2 Flash sub-device?

LED Flash Class devices, after introduction of the Flash Manager,
are not tightly coupled with any media controller. They are maintained
by the Flash Manager and made available for dynamic assignment to
any media system they are connected to through multiplexing devices.

In the proposed rough solution, when support for V4L2 Flash sub-devices
is enabled, there is a v4l2_device created for them to register in.
This however implies that V4L2 Flash device will not be available
in any media controller, which calls its existence into question.

Therefore I'd like to consult possible ways of solving this issue.
The option I see is implementing a mechanism for moving V4L2 Flash
sub-devices between media controllers. A V4L2 Flash sub-device
would initially be assigned to one media system in the relevant
device tree binding, but it could be dynamically reassigned to
the other one. However I'm not sure if media controller design
is prepared for dynamic modifications of its graph and how many
modifications in the existing drivers this solution would require.

2) Consequences of locking the Flash Manager during flash strobe

In case a LED Flash Class device depends on muxes involved in
routing the other LED Flash Class device's strobe signals,
the Flash Manager must be locked for the time of strobing
to prevent reconfiguration of the strobe signal routing
by the other device.

A blocking_strobe sysfs attribute was added to indicate whether
this is the case for the device.

Nonetheless, this modifies behaviour of led_set_external_strobe
API, which also must block the caller for the time of strobing
to protect the strobe signal routing while the external strobe
signal provider asserts the flash_en pin.

Use case for user space application would be following in this case:
- spawn a new thread in which external strobe is activated
- in the parent thread instruct the external strobe provider
to strobe the flash

As an improvement there could be an op added for notifying
the application that the strobe signal routing has been
set up. It would have to be called from the function
led_flash_manager_setup_strobe after led_flash_manager_set_external_strobe
returns.

The blocking_strobe attribute would have to be also
mapped to a new V4L2 Flash control. Unfortunately it poses
a problem for the existing users of the V4L2 Flash API
which are not aware that setting V4L2_CID_FLASH_STROBE_SOURCE
may be blocking.

As a solution led_set_external_strobe API could be extended
by a parameter telling whether the caller is prepared for
the blocking call. led_flash_manager_setup_strobe should
be extended accordingly then.
In V4L2 "V4L2_FLASH_STROBE_SOURCE_EXTERNAL_BLOCKING" menu item
could be added to handle this.
With existing V4L2_FLASH_STROBE_SOURCE_EXTERNAL the flash manager
wouldn't protect muxes against reconfiguration.

TODO:
- switch to using V4L2 array controls
- add s_power op to the LED Flash Class


I will be off-line for three weeks from now on and will respond
to any questions after getting back.


Thanks,
Jacek Anaszewski

Jacek Anaszewski (21):
leds: make brightness type consistent across whole subsystem
leds: implement sysfs interface locking mechanism
leds: Improve and export led_update_brightness
leds: Reorder include directives
leds: avoid using deprecated DEVICE_ATTR macro
leds: add API for setting torch brightness
of: add of_node_ncmp wrapper
leds: Add sysfs and kernel internal API for flash LEDs
Documentation: leds: Add description of LED Flash Class extension
Documentation: leds: add exemplary asynchronous mux driver
DT: leds: Add flash led devices related properties
DT: Add documentation for LED Class Flash Manger
v4l2-device: add v4l2_device_register_subdev_node API
v4l2-ctrls: add control for flash strobe signal providers
media: Add registration helpers for V4L2 flash
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
ARM: dts: add aat1290 current regulator device node

Documentation/DocBook/media/v4l/controls.xml | 11 +
Documentation/devicetree/bindings/leds/common.txt | 16 +
.../devicetree/bindings/leds/leds-aat1290.txt | 17 +
.../bindings/leds/leds-flash-manager.txt | 165 +++
Documentation/devicetree/bindings/mfd/max77693.txt | 62 ++
.../devicetree/bindings/vendor-prefixes.txt | 1 +
Documentation/leds/leds-async-mux.c | 65 ++
Documentation/leds/leds-class-flash.txt | 126 +++
arch/arm/boot/dts/exynos4412-trats2.dts | 24 +
drivers/leds/Kconfig | 28 +
drivers/leds/Makefile | 7 +
drivers/leds/led-class-flash.c | 715 +++++++++++++
drivers/leds/led-class.c | 58 +-
drivers/leds/led-core.c | 51 +-
drivers/leds/led-flash-gpio-mux.c | 102 ++
drivers/leds/led-flash-manager.c | 698 +++++++++++++
drivers/leds/led-triggers.c | 16 +-
drivers/leds/leds-aat1290.c | 455 +++++++++
drivers/leds/leds-max77693.c | 1070 ++++++++++++++++++++
drivers/leds/of_led_flash_manager.c | 155 +++
drivers/media/v4l2-core/Kconfig | 11 +
drivers/media/v4l2-core/Makefile | 2 +
drivers/media/v4l2-core/v4l2-ctrls.c | 2 +
drivers/media/v4l2-core/v4l2-device.c | 63 +-
drivers/media/v4l2-core/v4l2-flash.c | 580 +++++++++++
drivers/mfd/max77693.c | 5 +-
include/linux/led-class-flash.h | 290 ++++++
include/linux/led-flash-gpio-mux.h | 68 ++
include/linux/led-flash-manager.h | 121 +++
include/linux/leds.h | 73 +-
include/linux/mfd/max77693.h | 40 +
include/linux/of.h | 1 +
include/linux/of_led_flash_manager.h | 80 ++
include/media/v4l2-device.h | 7 +
include/media/v4l2-flash.h | 137 +++
include/uapi/linux/v4l2-controls.h | 2 +
36 files changed, 5272 insertions(+), 52 deletions(-)
create mode 100644 Documentation/devicetree/bindings/leds/leds-aat1290.txt
create mode 100644 Documentation/devicetree/bindings/leds/leds-flash-manager.txt
create mode 100644 Documentation/leds/leds-async-mux.c
create mode 100644 Documentation/leds/leds-class-flash.txt
create mode 100644 drivers/leds/led-class-flash.c
create mode 100644 drivers/leds/led-flash-gpio-mux.c
create mode 100644 drivers/leds/led-flash-manager.c
create mode 100644 drivers/leds/leds-aat1290.c
create mode 100644 drivers/leds/leds-max77693.c
create mode 100644 drivers/leds/of_led_flash_manager.c
create mode 100644 drivers/media/v4l2-core/v4l2-flash.c
create mode 100644 include/linux/led-class-flash.h
create mode 100644 include/linux/led-flash-gpio-mux.h
create mode 100644 include/linux/led-flash-manager.h
create mode 100644 include/linux/of_led_flash_manager.h
create mode 100644 include/media/v4l2-flash.h

--
1.7.9.5


2014-07-11 14:04:44

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 02/21] leds: implement sysfs interface locking mechanism

Add mechanism for locking LED subsystem sysfs interface.
This patch prepares ground for addition of LED Flash Class extension.

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/led-class.c | 13 +++++++++++--
drivers/leds/led-core.c | 18 ++++++++++++++++++
drivers/leds/led-triggers.c | 11 ++++++++---
include/linux/leds.h | 31 +++++++++++++++++++++++++++++++
4 files changed, 68 insertions(+), 5 deletions(-)

diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index aa29198..ea04891 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -45,17 +45,23 @@ static ssize_t brightness_store(struct device *dev,
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
unsigned long state;
- ssize_t ret = -EINVAL;
+ ssize_t ret;
+
+ mutex_lock(&led_cdev->led_lock);

ret = kstrtoul(buf, 10, &state);
if (ret)
- return ret;
+ goto unlock;

if (state == LED_OFF)
led_trigger_remove(led_cdev);
__led_set_brightness(led_cdev, state);

return size;
+ ret = size;
+unlock:
+ mutex_unlock(&led_cdev->led_lock);
+ return ret;
}
static DEVICE_ATTR_RW(brightness);

@@ -219,6 +225,7 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
#ifdef CONFIG_LEDS_TRIGGERS
init_rwsem(&led_cdev->trigger_lock);
#endif
+ mutex_init(&led_cdev->led_lock);
/* add to the list of leds */
down_write(&leds_list_lock);
list_add_tail(&led_cdev->node, &leds_list);
@@ -272,6 +279,8 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
down_write(&leds_list_lock);
list_del(&led_cdev->node);
up_write(&leds_list_lock);
+
+ mutex_destroy(&led_cdev->led_lock);
}
EXPORT_SYMBOL_GPL(led_classdev_unregister);

diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index 71b40d3..4d7cb31 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -126,3 +126,21 @@ void led_set_brightness(struct led_classdev *led_cdev,
__led_set_brightness(led_cdev, brightness);
}
EXPORT_SYMBOL(led_set_brightness);
+
+/* Caller must ensure led_cdev->led_lock held */
+void led_sysfs_lock(struct led_classdev *led_cdev)
+{
+ WARN_ON(!mutex_is_locked(&led_cdev->led_lock));
+
+ led_cdev->flags |= LED_SYSFS_LOCK;
+}
+EXPORT_SYMBOL_GPL(led_sysfs_lock);
+
+/* Caller must ensure led_cdev->led_lock held */
+void led_sysfs_unlock(struct led_classdev *led_cdev)
+{
+ WARN_ON(!mutex_is_locked(&led_cdev->led_lock));
+
+ led_cdev->flags &= ~LED_SYSFS_LOCK;
+}
+EXPORT_SYMBOL_GPL(led_sysfs_unlock);
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index c3734f1..0545530 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -37,6 +37,9 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
char trigger_name[TRIG_NAME_MAX];
struct led_trigger *trig;
size_t len;
+ int ret = count;
+
+ mutex_lock(&led_cdev->led_lock);

trigger_name[sizeof(trigger_name) - 1] = '\0';
strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
@@ -47,7 +50,7 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,

if (!strcmp(trigger_name, "none")) {
led_trigger_remove(led_cdev);
- return count;
+ goto exit_unlock;
}

down_read(&triggers_list_lock);
@@ -58,12 +61,14 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
up_write(&led_cdev->trigger_lock);

up_read(&triggers_list_lock);
- return count;
+ goto exit_unlock;
}
}
up_read(&triggers_list_lock);

- return -EINVAL;
+exit_unlock:
+ mutex_unlock(&led_cdev->led_lock);
+ return ret;
}
EXPORT_SYMBOL_GPL(led_trigger_store);

diff --git a/include/linux/leds.h b/include/linux/leds.h
index 995f933..da7c6b5 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -13,6 +13,7 @@
#define __LINUX_LEDS_H_INCLUDED

#include <linux/list.h>
+#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/rwsem.h>
#include <linux/timer.h>
@@ -42,6 +43,7 @@ struct led_classdev {
#define LED_BLINK_ONESHOT (1 << 17)
#define LED_BLINK_ONESHOT_STOP (1 << 18)
#define LED_BLINK_INVERT (1 << 19)
+#define LED_SYSFS_LOCK (1 << 20)

/* Set LED brightness level */
/* Must not sleep, use a workqueue if needed */
@@ -85,6 +87,9 @@ struct led_classdev {
/* true if activated - deactivate routine uses it to do cleanup */
bool activated;
#endif
+
+ /* Ensures consistent access to the LED Flash Class device */
+ struct mutex led_lock;
};

extern int led_classdev_register(struct device *parent,
@@ -140,6 +145,32 @@ extern void led_blink_set_oneshot(struct led_classdev *led_cdev,
*/
extern void led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness brightness);
+/**
+ * led_sysfs_lock - lock LED sysfs interface
+ * @led_cdev: the LED to set
+ *
+ * Lock the LED's sysfs interface
+ */
+extern void led_sysfs_lock(struct led_classdev *led_cdev);
+
+/**
+ * led_sysfs_unlock - unlock LED sysfs interface
+ * @led_cdev: the LED to set
+ *
+ * Unlock the LED's sysfs interface
+ */
+extern void led_sysfs_unlock(struct led_classdev *led_cdev);
+
+/**
+ * led_sysfs_is_locked
+ * @led_cdev: the LED to query
+ *
+ * Returns: true if the sysfs interface of the led is locked
+ */
+static inline bool led_sysfs_is_locked(struct led_classdev *led_cdev)
+{
+ return led_cdev->flags & LED_SYSFS_LOCK;
+}

/*
* LED Triggers
--
1.7.9.5

2014-07-11 14:04:49

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 04/21] leds: Reorder include directives

Reorder include directives so that they are arranged
in alphabetical order.

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/led-class.c | 13 +++++++------
drivers/leds/led-core.c | 3 ++-
include/linux/leds.h | 2 +-
3 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index da79bbb..0127783 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -9,16 +9,17 @@
* published by the Free Software Foundation.
*/

-#include <linux/module.h>
-#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/err.h>
#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/spinlock.h>
-#include <linux/device.h>
#include <linux/timer.h>
-#include <linux/err.h>
-#include <linux/ctype.h>
-#include <linux/leds.h>
#include "leds.h"

static struct class *leds_class;
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index 0ac06ed..d156fb6 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -12,10 +12,11 @@
*/

#include <linux/kernel.h>
+#include <linux/leds.h>
#include <linux/list.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/rwsem.h>
-#include <linux/leds.h>
#include "leds.h"

DECLARE_RWSEM(leds_list_lock);
diff --git a/include/linux/leds.h b/include/linux/leds.h
index e9b025d..1a130cc 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -14,8 +14,8 @@

#include <linux/list.h>
#include <linux/mutex.h>
-#include <linux/spinlock.h>
#include <linux/rwsem.h>
+#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/workqueue.h>

--
1.7.9.5

2014-07-11 14:04:55

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 06/21] leds: add API for setting torch brightness

This patch prepares ground for addition of LED Flash Class extension to
the LED subsystem. Since turning the torch on must have guaranteed
immediate effect the brightness_set op can't be used for it. Drivers must
schedule a work queue task in this op to be compatible with led-triggers,
which call brightess_set from timer irqs. In order to address this
limitiation a torch_brightness_set op and led_set_torch_brightness API
is introduced. Setting brightness sysfs attribute will result in calling
brightness_set op for LED Class decices and torch_brightness_set op for
LED Flash Class devices, whereas triggers will still call brightness
op in both cases.

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/led-class.c | 18 ++++++++++++++++--
drivers/leds/led-core.c | 15 +++++++++++++++
include/linux/leds.h | 22 ++++++++++++++++++++++
3 files changed, 53 insertions(+), 2 deletions(-)

diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index a96a1a7..c17dda0 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -44,15 +44,29 @@ static ssize_t brightness_store(struct device *dev,

mutex_lock(&led_cdev->led_lock);

+ /*
+ * Having LED_DEV_CAP_TORCH implies this is LED Flash Class
+ * device and we need to check sysfs accessibility.
+ */
+ if (led_cdev->flags & LED_DEV_CAP_TORCH) {
+ if (led_sysfs_is_locked(led_cdev)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+ }
+
ret = kstrtoul(buf, 10, &state);
if (ret)
goto unlock;

if (state == LED_OFF)
led_trigger_remove(led_cdev);
- __led_set_brightness(led_cdev, state);

- return size;
+ if (led_cdev->flags & LED_DEV_CAP_TORCH)
+ ret = led_set_torch_brightness(led_cdev, state);
+ else
+ __led_set_brightness(led_cdev, state);
+
ret = size;
unlock:
mutex_unlock(&led_cdev->led_lock);
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index d156fb6..0ce087a 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -143,6 +143,21 @@ int led_update_brightness(struct led_classdev *led_cdev)
return ret;
}
EXPORT_SYMBOL(led_update_brightness);
+
+int led_set_torch_brightness(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ int ret = 0;
+
+ led_cdev->brightness = min(brightness, led_cdev->max_brightness);
+
+ if (!(led_cdev->flags & LED_SUSPENDED))
+ ret = led_cdev->torch_brightness_set(led_cdev,
+ led_cdev->brightness);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(led_set_torch_brightness);
+
/* Caller must ensure led_cdev->led_lock held */
void led_sysfs_lock(struct led_classdev *led_cdev)
{
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 1a130cc..9bea9e6 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -44,11 +44,21 @@ struct led_classdev {
#define LED_BLINK_ONESHOT_STOP (1 << 18)
#define LED_BLINK_INVERT (1 << 19)
#define LED_SYSFS_LOCK (1 << 20)
+#define LED_DEV_CAP_TORCH (1 << 21)

/* Set LED brightness level */
/* Must not sleep, use a workqueue if needed */
void (*brightness_set)(struct led_classdev *led_cdev,
enum led_brightness brightness);
+ /*
+ * Set LED brightness immediately - it is required for flash led
+ * devices as they require setting torch brightness to have immediate
+ * effect. brightness_set op cannot be used for this purpose because
+ * the led drivers schedule a work queue task in it to allow for
+ * being called from led-triggers, i.e. from the timer irq context.
+ */
+ int (*torch_brightness_set)(struct led_classdev *led_cdev,
+ enum led_brightness brightness);
/* Get LED brightness level */
enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);

@@ -156,6 +166,18 @@ extern void led_set_brightness(struct led_classdev *led_cdev,
*/
extern int led_update_brightness(struct led_classdev *led_cdev);

+/**
+ * led_set_torch_brightness - set torch LED brightness
+ * @led_cdev: the LED to set
+ * @brightness: the brightness to set it to
+ *
+ * Returns: 0 on success or negative error value on failure
+ *
+ * Set a torch LED's brightness.
+ */
+extern int led_set_torch_brightness(struct led_classdev *led_cdev,
+ enum led_brightness brightness);
+/**
* led_sysfs_lock - lock LED sysfs interface
* @led_cdev: the LED to set
*
--
1.7.9.5

2014-07-11 14:05:11

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 09/21] Documentation: leds: Add description of LED Flash Class extension

The documentation being added contains overall description of the
LED Flash Class and the related sysfs attributes. There are also
chapters devoted specifically to the Flash Manager feature.

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 | 126 +++++++++++++++++++++++++++++++
1 file changed, 126 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..0927804
--- /dev/null
+++ b/Documentation/leds/leds-class-flash.txt
@@ -0,0 +1,126 @@
+
+Flash LED handling under Linux
+==============================
+
+Some LED devices support two modes - torch and flash. In order to enable
+support for flash LEDs the 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)
+ - external_strobe - some devices expose dedicated hardware pins for
+ triggering a flash LED - this attribute allows to
+ set this mode (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
+
+The LED subsystem driver can be controlled also from the level of VideoForLinux2
+subsystem. In order to enable this the CONFIG_V4L2_FLASH_LED_CLASS symbol has to
+be defined in the kernel config. The driver must call v4l2_flash_init function
+to get registered in the V4L2 subsystem. On remove v4l2_flash_release function
+has to be called (see <media/v4l2-flash.h>).
+
+After proper initialization V4L2 Flash sub-device is created. The sub-device
+exposes a number of V4L2 controls. When the V4L2_CID_FLASH_LED_MODE control
+is set to V4L2_FLASH_LED_MODE_TORCH or V4L2_FLASH_LED_MODE_FLASH the
+LED subsystem sysfs interface becomes unavailable. The interface can be unlocked
+by setting the mode back to V4L2_FLASH_LED_MODE_NONE.
+
+
+Flash Manager
+=============
+
+Flash LED devices often provide two ways of strobing the flash: software
+(e.g. by setting a bit in a register) and hardware, by asserting dedicated pin,
+which is usually connected to a camera sensor device. There are also flash led
+devices which support only hardware strobing - in such cases a multiplexer
+device is employed to route the flash strobe signal either from the SoC GPIO or
+from the external strobe signal provider, e.g. camera sensor.
+Use of multiplexers allows also to change the flash-sensor connection
+dynamically if there is more than one flash or external strobe signal provider
+available on the board.
+
+In order to address the aforementioned cases the Flash Manager functionality
+has been introduced. Flash Manager is a part of LED Flash Class. It maintains
+information about flashes, software and external strobe signal providers and
+multiplexers that route strobe signals among them.
+
+To register a LED Flash Class device in the flash manager the device_node
+of a flash device has to be passed as the third argument to the
+led_classdev_flash_register function. The device_node is expected to include one
+gate-software-strobe subnode and at least one gate-external-strobeN subnode.
+Besides that there must defined a flash_muxes node aggregating all the
+multiplexers that can be referenced to by the flash led devices.
+(for mote details see Documentation/devicetree/bindings/leds/
+leds-flash-manager.txt).
+
+Flash manager adds following sysfs attributes to the LED Flash Clash
+device sysfs interface:
+
+ - strobe_provider - id of the external strobe signal provider associated
+ with the flash led device. It is created only if
+ there is more than one external strobe signal
+ provider defined (RW).
+ - strobe_providerN - names of the strobe signal providers available
+ for the flash led device, where N is the
+ identifier of a strobe signal provider (RO)
+ - blocking_strobe - informs if enabling either software or external
+ strobe will block the caller for the expected
+ time of strobing (1 - true, 0 - false). The call
+ needs to be blocking if the multiplexers involved
+ in the strobe signal routing are connected to more
+ than one flash led device. In such a case flash
+ manager becomes locked for the expected time of
+ strobing to prevent reconfigurarion of multiplexers
+ by the other flash led devices willing to strobe
+ in the same time (RO).
+
+
+LED Flash Class multiplexers
+============================
+
+Multiplexers are an indispensable part of the Flash Manager. Flash Manager has
+its own led_flash_gpio_mux* helpers for creating, releasing and operating on
+the simple gpio driven multiplexers (the ones whose lines are selected by
+changing the state of its selector pins) and the user doesn't have to bother
+with it.
+
+It is however possible that a more advanced device will be used for routing
+strobe signals. This kind of devices are known to the Flash Manager as
+"asynchronous muxes" and can be registered in runtime with use of
+led_flash_manager_bind_async_mux API and unregistered with
+led_flash_manager_unbind_async_mux. (see Documentation/leds/leds-async-mux.c
+for the exemplary implementation of the async mux driver).
+
+If a LED Flash Class device declares dependency on an async mux, then strobing
+the flash, or setting external strobe, will succeed only wnen the async mux
+has been bound to the Flash Manager. Async mux module, once bound, can be
+removed only after all LED Flash Class devices depending on it are unregistered
+from the Flash Manager.
--
1.7.9.5

2014-07-11 14:05:23

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 12/21] DT: Add documentation for LED Class Flash Manger

This patch documents LED Class Flash Manager
related bindings.

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]>
---
.../bindings/leds/leds-flash-manager.txt | 165 ++++++++++++++++++++
1 file changed, 165 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/leds-flash-manager.txt

diff --git a/Documentation/devicetree/bindings/leds/leds-flash-manager.txt b/Documentation/devicetree/bindings/leds/leds-flash-manager.txt
new file mode 100644
index 0000000..c98ee2e
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-flash-manager.txt
@@ -0,0 +1,165 @@
+* LED Flash Manager
+
+Flash manager is a part of LED Flash Class. It maintains
+all the flash led devices which have their external strobe
+signals routed through multiplexing devices.
+The multiplexers are aggregated in the standalone 'flash_muxes'
+node as subnodes which are referenced by the flash led devices.
+
+
+flash_muxes node
+----------------
+
+muxN subnode
+------------
+
+There must be at least one muxN subnode, where N is the identifier
+of the node, present in the flash_muxes node. One muxN node
+represents one multiplexer.
+
+Required properties (mutually exclusive):
+- gpios : specifies the gpio pins used to set the states
+ of mux selectors, LSB first
+- mux-async : phandle to the node of the multiplexing device
+
+
+
+flash led device node
+---------------------
+
+Following subnodes must be added to the LED Flash Class device
+tree node described in Documentation/devicetree/bindings/leds/common.txt.
+
+
+gate-software-strobe subnode
+----------------------------
+
+The node defines configuration of multiplexers that needs
+to be applied to route software strobe signal to the flash
+led device.
+
+Required properties:
+- mux : phandle to the muxN node defined
+ in the flash_muxes node
+- mux-line-id : mux line identifier
+
+Optional subnodes:
+- gate-software-strobe : if there are many multiplexers to configure,
+ they can be recursively nested.
+
+
+gate-external-strobeN subnode
+-----------------------------
+
+The node defines configuration of multiplexers that needs
+to be applied to route external strobe signal to the flash
+led device. A flash led device can have many external strobe
+signal sources.
+
+Required properties:
+- mux : phandle to the muxN node defined
+ in the flash_muxes node
+- mux-line-id : mux line identifier
+Optional properties:
+- strobe-provider : phandle to the device providing the
+ strobe signal. It is expected only
+ on the first level node. The referenced
+ node is expected to have 'compatible'
+ property, as providers are labelled
+ with it in the LED subsystem
+
+Optional subnodes:
+- gate-external-strobeN : if there are many multiplexers to configure,
+ they can be recursively nested.
+
+
+Example:
+
+Following board configuration is assumed in this example:
+
+ ---------- ----------
+ | FLASH1 | | FLASH2 |
+ ---------- ----------
+ \(0) /(1)
+ ----------
+ | MUX1 |
+ ----------
+ |
+ ----------
+ | MUX2 |
+ ----------
+ /(0) \(1)
+ ---------- --------------------
+ | MUX3 | | SOC FLASHEN GPIO |
+ ---------- --------------------
+ /(0) \(1)
+----------- -----------
+| SENSOR1 | | SENSOR2 |
+----------- -----------
+
+
+dummy_mux: led_mux {
+ compatible = "led-async-mux";
+};
+
+flash_muxes {
+ flash_mux1: mux1 {
+ gpios = <&gpj1 1 0>, <&gpj1 2 0>;
+ };
+
+ flash_mux2: mux2 {
+ mux-async = <&dummy_mux>;
+ };
+
+ flash_mux3: mux3 {
+ gpios = <&gpl1 1 0>, <&gpl1 2 0>;
+ };
+};
+
+max77693-flash {
+ compatible = "maxim,max77693-flash";
+
+ //other device specific properties here
+
+ gate-software-strobe {
+ mux = <&flash_mux1>;
+ mux-line-id = <0>;
+
+ gate-software-strobe {
+ mux = <&flash_mux2>;
+ mux-line-id = <1>;
+ };
+ };
+
+ gate-external-strobe1 {
+ strobe-provider = <&s5c73m3_spi>;
+ mux = <&flash_mux1>;
+ mux-line-id = <0>;
+
+ gate-external-strobe1 {
+ mux = <&flash_mux2>;
+ mux-line-id = <0>;
+
+ gate-external-strobe1 {
+ mux = <&flash_mux3>;
+ mux-line-id = <0>;
+ };
+ };
+ };
+
+ gate-external-strobe2 {
+ strobe-provider = <&s5k6a3>;
+ mux = <&flash_mux1>;
+ mux-line-id = <0>;
+
+ gate-external-strobe2 {
+ mux = <&flash_mux2>;
+ mux-line-id = <0>;
+
+ gate-external-strobe2 {
+ mux = <&flash_mux3>;
+ mux-line-id = <1>;
+ };
+ };
+ };
+};
--
1.7.9.5

2014-07-11 14:05:35

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 15/21] media: Add registration helpers for V4L2 flash

This patch adds helper functions for registering/unregistering
LED class flash devices as V4L2 subdevs. 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 | 580 ++++++++++++++++++++++++++++++++++
include/media/v4l2-flash.h | 137 ++++++++
4 files changed, 730 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 9ca0f8d..3ae3f0f 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -35,6 +35,17 @@ config V4L2_MEM2MEM_DEV
tristate
depends on VIDEOBUF2_CORE

+# Used by LED subsystem flash drivers
+config V4L2_FLASH_LED_CLASS
+ bool "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..21080c6
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-flash.c
@@ -0,0 +1,580 @@
+/*
+ * 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/mutex.h>
+#include <linux/of_led_flash_manager.h>
+#include <linux/slab.h>
+#include <media/v4l2-flash.h>
+
+#define call_flash_op(v4l2_flash, op, args...) \
+ (v4l2_flash->ops->op ? \
+ v4l2_flash->ops->op(args) : \
+ -EINVAL)
+
+static struct v4l2_device *v4l2_dev;
+static int registered_flashes;
+
+static inline enum led_brightness v4l2_flash_intensity_to_led_brightness(
+ struct v4l2_ctrl_config *config,
+ s32 intensity)
+{
+ return ((intensity - config->min) / config->step) + 1;
+}
+
+static inline s32 v4l2_flash_led_brightness_to_intensity(
+ struct v4l2_ctrl_config *config,
+ enum led_brightness brightness)
+{
+ return ((brightness - 1) * config->step) + config->min;
+}
+
+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_flash_ctrl_config *config = &v4l2_flash->config;
+ struct v4l2_flash_ctrl *ctrl = &v4l2_flash->ctrl;
+ bool is_strobing;
+ int ret;
+
+ switch (c->id) {
+ case V4L2_CID_FLASH_TORCH_INTENSITY:
+ /*
+ * Update torch brightness only if in TORCH_MODE,
+ * as otherwise brightness_update op returns 0,
+ * which would spuriously inform user space that
+ * V4L2_CID_FLASH_TORCH_INTENSITY control value
+ * has changed.
+ */
+ if (ctrl->led_mode->val == V4L2_FLASH_LED_MODE_TORCH) {
+ ret = call_flash_op(v4l2_flash, torch_brightness_update,
+ led_cdev);
+ if (ret < 0)
+ return ret;
+ ctrl->torch_intensity->val =
+ v4l2_flash_led_brightness_to_intensity(
+ &config->torch_intensity,
+ led_cdev->brightness);
+ }
+ return 0;
+ case V4L2_CID_FLASH_INTENSITY:
+ ret = call_flash_op(v4l2_flash, flash_brightness_update,
+ flash);
+ if (ret < 0)
+ return ret;
+ /* no conversion is needed */
+ c->val = flash->brightness.val;
+ return 0;
+ case V4L2_CID_FLASH_INDICATOR_INTENSITY:
+ ret = call_flash_op(v4l2_flash, indicator_brightness_update,
+ flash);
+ if (ret < 0)
+ return ret;
+ /* no conversion is needed */
+ c->val = flash->indicator_brightness->val;
+ return 0;
+ case V4L2_CID_FLASH_STROBE_STATUS:
+ ret = call_flash_op(v4l2_flash, strobe_get, 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 */
+ ret = call_flash_op(v4l2_flash, fault_get, flash, &c->val);
+ return ret;
+ case V4L2_CID_FLASH_STROBE_SOURCE:
+ c->val = flash->external_strobe;
+ return 0;
+ case V4L2_CID_FLASH_STROBE_PROVIDER:
+ c->val = flash->strobe_provider_id;
+ 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 v4l2_flash_ctrl *ctrl = &v4l2_flash->ctrl;
+ struct v4l2_flash_ctrl_config *config = &v4l2_flash->config;
+ enum led_brightness torch_brightness;
+ bool external_strobe;
+ int ret;
+
+ switch (c->id) {
+ case V4L2_CID_FLASH_LED_MODE:
+ switch (c->val) {
+ case V4L2_FLASH_LED_MODE_NONE:
+ call_flash_op(v4l2_flash, torch_brightness_set,
+ &flash->led_cdev, 0);
+ return call_flash_op(v4l2_flash, strobe_set, flash,
+ false);
+ case V4L2_FLASH_LED_MODE_FLASH:
+ /* Turn off torch LED */
+ call_flash_op(v4l2_flash, torch_brightness_set,
+ &flash->led_cdev, 0);
+ external_strobe = (ctrl->source->val ==
+ V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
+ return call_flash_op(v4l2_flash, external_strobe_set,
+ flash, external_strobe);
+ case V4L2_FLASH_LED_MODE_TORCH:
+ /* Stop flash strobing */
+ ret = call_flash_op(v4l2_flash, strobe_set, flash,
+ false);
+ if (ret)
+ return ret;
+
+ torch_brightness =
+ v4l2_flash_intensity_to_led_brightness(
+ &config->torch_intensity,
+ ctrl->torch_intensity->val);
+ call_flash_op(v4l2_flash, torch_brightness_set,
+ &flash->led_cdev, torch_brightness);
+ return ret;
+ }
+ 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, flash,
+ external_strobe);
+ case V4L2_CID_FLASH_STROBE:
+ if (ctrl->led_mode->val != V4L2_FLASH_LED_MODE_FLASH ||
+ ctrl->source->val != V4L2_FLASH_STROBE_SOURCE_SOFTWARE)
+ return -EINVAL;
+ return call_flash_op(v4l2_flash, strobe_set, flash, true);
+ case V4L2_CID_FLASH_STROBE_STOP:
+ return call_flash_op(v4l2_flash, strobe_set, flash, false);
+ case V4L2_CID_FLASH_TIMEOUT:
+ return call_flash_op(v4l2_flash, timeout_set, flash, c->val);
+ case V4L2_CID_FLASH_INTENSITY:
+ /* no conversion is needed */
+ return call_flash_op(v4l2_flash, flash_brightness_set, flash,
+ c->val);
+ case V4L2_CID_FLASH_INDICATOR_INTENSITY:
+ /* no conversion is needed */
+ return call_flash_op(v4l2_flash, indicator_brightness_set,
+ flash, c->val);
+ 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 (ctrl->led_mode->val == V4L2_FLASH_LED_MODE_TORCH) {
+ torch_brightness =
+ v4l2_flash_intensity_to_led_brightness(
+ &config->torch_intensity,
+ ctrl->torch_intensity->val);
+ call_flash_op(v4l2_flash, torch_brightness_set,
+ &flash->led_cdev, torch_brightness);
+ }
+ return 0;
+ case V4L2_CID_FLASH_STROBE_PROVIDER:
+ flash->strobe_provider_id = 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 int v4l2_flash_init_strobe_providers_menu(struct v4l2_flash *v4l2_flash)
+{
+ struct led_classdev_flash *flash = v4l2_flash->flash;
+ struct led_flash_strobe_provider *provider;
+ struct v4l2_ctrl *ctrl;
+ int i = 0;
+
+ v4l2_flash->strobe_providers_menu =
+ kzalloc(sizeof(char *) * (flash->num_strobe_providers),
+ GFP_KERNEL);
+ if (!v4l2_flash->strobe_providers_menu)
+ return -ENOMEM;
+
+ list_for_each_entry(provider, &flash->strobe_providers, list)
+ v4l2_flash->strobe_providers_menu[i++] =
+ (char *) provider->name;
+
+ ctrl = v4l2_ctrl_new_std_menu_items(
+ &v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
+ V4L2_CID_FLASH_STROBE_PROVIDER,
+ flash->num_strobe_providers - 1,
+ 0, 0,
+ (const char * const *) v4l2_flash->strobe_providers_menu);
+
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ return 0;
+}
+
+static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash)
+
+{
+ 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_flash_ctrl_config *config = &v4l2_flash->config;
+ struct v4l2_ctrl *ctrl;
+ struct v4l2_ctrl_config *ctrl_cfg;
+ bool has_flash = led_cdev->flags & LED_DEV_CAP_FLASH;
+ bool has_indicator = led_cdev->flags & LED_DEV_CAP_INDICATOR;
+ bool has_strobe_providers = (flash->num_strobe_providers > 1);
+ unsigned int mask;
+ int ret, max, num_ctrls;
+
+ num_ctrls = has_flash ? 5 : 2;
+ if (has_flash) {
+ if (flash_ops->flash_brightness_set)
+ ++num_ctrls;
+ if (flash_ops->timeout_set)
+ ++num_ctrls;
+ if (flash_ops->strobe_get)
+ ++num_ctrls;
+ if (has_indicator)
+ ++num_ctrls;
+ if (config->flash_faults)
+ ++num_ctrls;
+ if (has_strobe_providers)
+ ++num_ctrls;
+ }
+
+ v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls);
+
+ mask = 1 << V4L2_FLASH_LED_MODE_NONE |
+ 1 << V4L2_FLASH_LED_MODE_TORCH;
+ if (flash)
+ mask |= 1 << V4L2_FLASH_LED_MODE_FLASH;
+
+ /* Configure FLASH_LED_MODE ctrl */
+ v4l2_flash->ctrl.led_mode = v4l2_ctrl_new_std_menu(
+ &v4l2_flash->hdl,
+ &v4l2_flash_ctrl_ops, V4L2_CID_FLASH_LED_MODE,
+ V4L2_FLASH_LED_MODE_TORCH, ~mask,
+ V4L2_FLASH_LED_MODE_NONE);
+
+ /* Configure TORCH_INTENSITY ctrl */
+ ctrl_cfg = &config->torch_intensity;
+ ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
+ V4L2_CID_FLASH_TORCH_INTENSITY,
+ ctrl_cfg->min, ctrl_cfg->max,
+ ctrl_cfg->step, ctrl_cfg->def);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ v4l2_flash->ctrl.torch_intensity = ctrl;
+
+ if (has_flash) {
+ /* Configure FLASH_STROBE_SOURCE ctrl */
+ mask = 1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
+
+ if (flash->has_external_strobe) {
+ mask |= 1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
+ max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
+ } else {
+ max = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
+ }
+
+ v4l2_flash->ctrl.source = v4l2_ctrl_new_std_menu(
+ &v4l2_flash->hdl,
+ &v4l2_flash_ctrl_ops,
+ V4L2_CID_FLASH_STROBE_SOURCE,
+ max,
+ ~mask,
+ V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
+ if (v4l2_flash->ctrl.source)
+ v4l2_flash->ctrl.source->flags |=
+ V4L2_CTRL_FLAG_VOLATILE;
+
+ /* Configure FLASH_STROBE ctrl */
+ ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
+ V4L2_CID_FLASH_STROBE, 0, 1, 1, 0);
+
+ /* Configure FLASH_STROBE_STOP ctrl */
+ ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
+ V4L2_CID_FLASH_STROBE_STOP,
+ 0, 1, 1, 0);
+
+ /* Configure FLASH_STROBE_STATUS ctrl */
+ ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
+ V4L2_CID_FLASH_STROBE_STATUS,
+ 0, 1, 1, 1);
+
+ if (flash_ops->strobe_get)
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+ V4L2_CTRL_FLAG_READ_ONLY;
+
+ if (flash_ops->timeout_set) {
+ /* Configure FLASH_TIMEOUT ctrl */
+ ctrl_cfg = &config->flash_timeout;
+ ctrl = v4l2_ctrl_new_std(
+ &v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
+ V4L2_CID_FLASH_TIMEOUT, ctrl_cfg->min,
+ ctrl_cfg->max, ctrl_cfg->step,
+ ctrl_cfg->def);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ }
+
+ if (flash_ops->flash_brightness_set) {
+ /* Configure FLASH_INTENSITY ctrl */
+ ctrl_cfg = &config->flash_intensity;
+ ctrl = v4l2_ctrl_new_std(
+ &v4l2_flash->hdl,
+ &v4l2_flash_ctrl_ops,
+ V4L2_CID_FLASH_INTENSITY,
+ ctrl_cfg->min, ctrl_cfg->max,
+ ctrl_cfg->step, ctrl_cfg->def);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ }
+
+ if (config->flash_faults) {
+ /* Configure FLASH_FAULT ctrl */
+ ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl,
+ &v4l2_flash_ctrl_ops,
+ V4L2_CID_FLASH_FAULT, 0,
+ config->flash_faults,
+ 0, 0);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+ V4L2_CTRL_FLAG_READ_ONLY;
+ }
+ if (has_indicator) {
+ /* Configure FLASH_INDICATOR_INTENSITY ctrl */
+ ctrl_cfg = &config->indicator_intensity;
+ ctrl = v4l2_ctrl_new_std(
+ &v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
+ V4L2_CID_FLASH_INDICATOR_INTENSITY,
+ ctrl_cfg->min, ctrl_cfg->max,
+ ctrl_cfg->step, ctrl_cfg->def);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ }
+
+ if (has_strobe_providers) {
+ /* Configure V4L2_CID_FLASH_STROBE_PROVIDERS ctrl */
+ ret = v4l2_flash_init_strobe_providers_menu(v4l2_flash);
+ if (ret < 0)
+ goto error_free_handler;
+ }
+ }
+
+ if (v4l2_flash->hdl.error) {
+ ret = v4l2_flash->hdl.error;
+ goto error_free_handler;
+ }
+
+ ret = v4l2_ctrl_handler_setup(&v4l2_flash->hdl);
+ if (ret < 0)
+ goto error_free_handler;
+
+ 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;
+
+ mutex_lock(&led_cdev->led_lock);
+ call_flash_op(v4l2_flash, sysfs_lock, led_cdev);
+ mutex_unlock(&led_cdev->led_lock);
+
+ return 0;
+}
+
+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;
+
+ mutex_lock(&led_cdev->led_lock);
+ call_flash_op(v4l2_flash, sysfs_unlock, led_cdev);
+ mutex_unlock(&led_cdev->led_lock);
+
+ return 0;
+}
+
+int v4l2_flash_register(struct v4l2_flash *v4l2_flash)
+{
+ struct led_classdev_flash *flash = v4l2_flash->flash;
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ int ret;
+
+ if (!v4l2_dev) {
+ v4l2_dev = kzalloc(sizeof(*v4l2_dev), GFP_KERNEL);
+ if (!v4l2_dev)
+ return -ENOMEM;
+
+ strlcpy(v4l2_dev->name, "v4l2-flash-manager",
+ sizeof(v4l2_dev->name));
+ ret = v4l2_device_register(NULL, v4l2_dev);
+ if (ret < 0) {
+ dev_err(led_cdev->dev->parent,
+ "Failed to register v4l2_device: %d\n", ret);
+ goto err_v4l2_device_register;
+ }
+ }
+
+ ret = v4l2_device_register_subdev(v4l2_dev, &v4l2_flash->sd);
+ if (ret < 0) {
+ dev_err(led_cdev->dev->parent,
+ "Failed to register v4l2_subdev: %d\n", ret);
+ goto err_v4l2_device_register;
+ }
+
+ ret = v4l2_device_register_subdev_node(&v4l2_flash->sd, v4l2_dev);
+ if (ret < 0) {
+ dev_err(led_cdev->dev->parent,
+ "Failed to register v4l2_subdev node: %d\n", ret);
+ goto err_register_subdev_node;
+ }
+
+ ++registered_flashes;
+
+ return 0;
+
+err_register_subdev_node:
+ v4l2_device_unregister_subdev(&v4l2_flash->sd);
+err_v4l2_device_register:
+ kfree(v4l2_flash->strobe_providers_menu);
+ if (v4l2_dev && registered_flashes == 0) {
+ v4l2_device_unregister(v4l2_dev);
+ kfree(v4l2_dev);
+ v4l2_dev = NULL;
+ }
+
+ return ret;
+}
+
+static void v4l2_flash_unregister(struct v4l2_flash *v4l2_flash)
+{
+ if (registered_flashes == 0)
+ return;
+
+ v4l2_device_unregister_subdev(&v4l2_flash->sd);
+
+ --registered_flashes;
+
+ if (registered_flashes == 0) {
+ v4l2_device_unregister(v4l2_dev);
+ kfree(v4l2_dev);
+ v4l2_dev = NULL;
+ }
+}
+
+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,
+};
+
+int v4l2_flash_init(struct led_classdev_flash *flash,
+ struct v4l2_flash_ctrl_config *config,
+ const struct v4l2_flash_ops *flash_ops,
+ struct v4l2_flash **out_flash)
+{
+ struct v4l2_flash *v4l2_flash;
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ if (!flash || !config || !out_flash)
+ return -EINVAL;
+
+ v4l2_flash = kzalloc(sizeof(*v4l2_flash), GFP_KERNEL);
+ if (!v4l2_flash)
+ return -ENOMEM;
+
+ sd = &v4l2_flash->sd;
+ v4l2_flash->flash = flash;
+ v4l2_flash->ops = flash_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);
+
+ v4l2_flash->config = *config;
+ ret = v4l2_flash_init_controls(v4l2_flash);
+ 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_flash_register(v4l2_flash);
+ if (ret < 0)
+ goto err_init_entity;
+
+ *out_flash = v4l2_flash;
+
+ return 0;
+
+err_init_entity:
+ media_entity_cleanup(&sd->entity);
+err_init_controls:
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ kfree(v4l2_flash);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_flash_init);
+
+void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
+{
+ if (!v4l2_flash)
+ return;
+
+ v4l2_flash_unregister(v4l2_flash);
+ v4l2_ctrl_handler_free(v4l2_flash->sd.ctrl_handler);
+ media_entity_cleanup(&v4l2_flash->sd.entity);
+ kfree(v4l2_flash->strobe_providers_menu);
+ kfree(v4l2_flash);
+}
+EXPORT_SYMBOL_GPL(v4l2_flash_release);
diff --git a/include/media/v4l2-flash.h b/include/media/v4l2-flash.h
new file mode 100644
index 0000000..effa46b
--- /dev/null
+++ b/include/media/v4l2-flash.h
@@ -0,0 +1,137 @@
+/*
+ * 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;
+enum led_brightness;
+
+struct v4l2_flash_ops {
+ int (*torch_brightness_set)(struct led_classdev *led_cdev,
+ enum led_brightness brightness);
+ int (*torch_brightness_update)(struct led_classdev *led_cdev);
+ int (*flash_brightness_set)(struct led_classdev_flash *flash,
+ u32 brightness);
+ int (*flash_brightness_update)(struct led_classdev_flash *flash);
+ int (*strobe_set)(struct led_classdev_flash *flash, bool state);
+ int (*strobe_get)(struct led_classdev_flash *flash, bool *state);
+ int (*timeout_set)(struct led_classdev_flash *flash, u32 timeout);
+ int (*indicator_brightness_set)(struct led_classdev_flash *flash,
+ u32 brightness);
+ int (*indicator_brightness_update)(struct led_classdev_flash *flash);
+ int (*external_strobe_set)(struct led_classdev_flash *flash,
+ bool enable);
+ int (*fault_get)(struct led_classdev_flash *flash, u32 *fault);
+ void (*sysfs_lock)(struct led_classdev *led_cdev);
+ void (*sysfs_unlock)(struct led_classdev *led_cdev);
+};
+
+/**
+ * struct v4l2_flash_ctrl - controls that define the sub-dev's state
+ * @source: V4L2_CID_FLASH_STROBE_SOURCE control
+ * @led_mode: V4L2_CID_FLASH_LED_MODE control
+ * @torch_intensity: V4L2_CID_FLASH_TORCH_INTENSITY control
+ */
+struct v4l2_flash_ctrl {
+ struct v4l2_ctrl *source;
+ struct v4l2_ctrl *led_mode;
+ struct v4l2_ctrl *torch_intensity;
+};
+
+/**
+ * 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
+ */
+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;
+};
+
+/**
+ * struct v4l2_flash - Flash sub-device context
+ * @flash: LED Flash Class device controlled by this sub-device
+ * @ops: LED Flash Class device ops
+ * @sd: V4L2 sub-device
+ * @hdl: flash controls handler
+ * @ctrl: state defining controls
+ * @config: V4L2 Flash controlsrconfiguration data
+ * @software_strobe_gates: route to the software strobe signal
+ * @external_strobe_gates: route to the external strobe signal
+ * @sensors: available external strobe sources
+ */
+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_flash_ctrl ctrl;
+ struct v4l2_flash_ctrl_config config;
+ char **strobe_providers_menu;
+};
+
+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);
+}
+
+#ifdef 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
+ * @out_flash: handler to the new V4L2 Flash device
+ *
+ * Create V4L2 subdev wrapping given LED subsystem device.
+
+ * Returns: 0 on success or negative error value on failure
+ */
+int v4l2_flash_init(struct led_classdev_flash *led_fdev,
+ struct v4l2_flash_ctrl_config *config,
+ const struct v4l2_flash_ops *flash_ops,
+ struct v4l2_flash **out_flash);
+
+/**
+ * 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, config, flash_ops, out_flash) (0)
+#define v4l2_flash_release(v4l2_flash)
+#endif /* CONFIG_V4L2_FLASH_LED_CLASS */
+
+#endif /* _V4L2_FLASH_H */
--
1.7.9.5

2014-07-11 14:05:47

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 18/21] leds: Add driver for AAT1290 current regulator

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 | 6 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-aat1290.c | 455 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 462 insertions(+)
create mode 100644 drivers/leds/leds-aat1290.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 794055e..ef774fd 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -42,6 +42,12 @@ 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
+ help
+ This option enables support for the LEDs on the AAT1290.
+
config LEDS_ATMEL_PWM
tristate "LED Support using Atmel PWM outputs"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index da1a4ba..41911aa 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -11,6 +11,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_ATMEL_PWM) += leds-atmel-pwm.o
obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
diff --git a/drivers/leds/leds-aat1290.c b/drivers/leds/leds-aat1290.c
new file mode 100644
index 0000000..07d7384
--- /dev/null
+++ b/drivers/leds/leds-aat1290.c
@@ -0,0 +1,455 @@
+/*
+ * 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/led-flash-manager.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_LED_NAME "aat1290"
+#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_RATIO_STEP 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
+
+static const unsigned int flash_level_to_reg[] = {
+ 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
+};
+
+enum aat1290_led_mode {
+ MODE_OFF,
+ MODE_TORCH,
+ MODE_FLASH,
+ MODE_FLASH_EXTERNAL,
+};
+
+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;
+
+ 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_divisor_reg =
+ flash_level_to_reg[(micro_sec / flash_tm->step) - 1];
+
+ aat1290_as2cwire_write(led, AAT1290_FLASH_SAFETY_TIMER_ADDR,
+ flash_tm_divisor_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 = "flash-timeout";
+
+ ret = of_property_read_u32(dev->of_node, pname, &led->max_flash_tm);
+ if (ret) {
+ dev_err(dev, "Unable to get property '%s'(%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 = 16;
+ setting->step = 1;
+ setting->val = setting->max;
+
+ /* Init flash timeout setting */
+ setting = &s->flash_timeout;
+ setting->min = led->max_flash_tm / AAT1290_FLASH_TM_RATIO_STEP;
+ setting->max = setting->min * AAT1290_FLASH_TM_RATIO_STEP;
+ setting->step = setting->min;
+ setting->val = setting->max;
+}
+
+#ifdef 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;
+}
+#else
+#define aat1290_init_v4l2_ctrl_config(s, config)
+#endif
+
+const struct led_flash_ops flash_ops = {
+ .strobe_set = aat1290_led_flash_strobe_set,
+ .timeout_set = aat1290_led_flash_timeout_set,
+};
+
+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;
+ struct v4l2_flash_ctrl_config v4l2_flash_config;
+ 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);
+ /* 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 = AAT1290_LED_NAME;
+ led_cdev->brightness_set = aat1290_led_brightness_set;
+ led_cdev->torch_brightness_set = aat1290_torch_brightness_set;
+ led_cdev->max_brightness = AAT1290_MAX_MM_CURR_PERCENT_0 -
+ AAT1290_MAX_MM_CURR_PERCENT_100;
+ led_cdev->flags |= LED_DEV_CAP_FLASH;
+
+ INIT_WORK(&led->work_brightness_set, aat1290_brightness_set_work);
+
+ flash->ops = &flash_ops;
+
+ flash->timeout = settings.flash_timeout;
+
+ /* Register in the LED subsystem. */
+ ret = led_classdev_flash_register(&pdev->dev, flash, dev->of_node);
+ if (ret < 0)
+ goto error_gpio_en_set;
+
+ /* Create V4L2 Flash subdev. */
+ ret = v4l2_flash_init(flash,
+ &v4l2_flash_config,
+ led_get_v4l2_flash_ops(),
+ &led->v4l2_flash);
+ if (ret < 0)
+ 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

2014-07-11 14:05:55

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 20/21] DT: Add documentation for the Skyworks AAT1290

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: Rob Herring <[email protected]>
Cc: Pawel Moll <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: Ian Campbell <[email protected]>
Cc: Kumar Gala <[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..9a9ad15
--- /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
+- skyworks,flash-timeout : 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 = <1940000>;
+}
--
1.7.9.5

2014-07-11 14:06:22

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 21/21] ARM: dts: add aat1290 current regulator device node

Add device node for AAT1290 1.5A Step-Up Current Regulator
for Flash LEDs along with flash_muxes node containing
information about a multiplexer that is used for switching
between software and external strobe signal source.

Signed-off-by: Jacek Anaszewski <[email protected]>
Signed-off-by: Kyungmin Park <[email protected]>
Cc: Kukjin Kim <[email protected]>
---
arch/arm/boot/dts/exynos4412-trats2.dts | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)

diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts b/arch/arm/boot/dts/exynos4412-trats2.dts
index 7787844..cbb76ba 100644
--- a/arch/arm/boot/dts/exynos4412-trats2.dts
+++ b/arch/arm/boot/dts/exynos4412-trats2.dts
@@ -785,4 +785,28 @@
pulldown-ohm = <100000>; /* 100K */
io-channels = <&adc 2>; /* Battery temperature */
};
+
+ flash_muxes {
+ flash_mux1: mux1 {
+ gpios = <&gpj1 0 0>;
+ };
+ };
+
+ aat1290 {
+ compatible = "skyworks,aat1290";
+ gpios = <&gpj1 1 0>, <&gpj1 2 0>;
+ flash-timeout = <1940000>;
+ status = "okay";
+
+ gate-software-strobe {
+ mux = <&flash_mux1>;
+ mux-line-id = <0>;
+ };
+
+ gate-external-strobe {
+ strobe-provider = <&s5c73m3_spi>;
+ mux = <&flash_mux1>;
+ mux-line-id = <1>;
+ };
+ };
};
--
1.7.9.5

2014-07-11 14:06:47

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 19/21] of: Add Skyworks Solutions, Inc. vendor prefix

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]>
---
.../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 5d27e5a..8d0df4e 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -113,6 +113,7 @@ renesas Renesas Electronics Corporation
ricoh Ricoh Co. Ltd.
rockchip Fuzhou Rockchip Electronics Co., Ltd
samsung Samsung Semiconductor
+skyworks Skyworks Solutions, Inc.
sbs Smart Battery System
schindler Schindler
seagate Seagate Technology PLC
--
1.7.9.5

2014-07-11 14:05:42

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 16/21] leds: Add support for max77693 mfd flash cell

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 subdevice when the support
for V4L2 Flash sub-devices is enabled. Device supports up to
two leds which can work in flash and torch mode. Leds can
be triggered externally or by software.

Signed-off-by: Jacek Anaszewski <[email protected]>
Signed-off-by: Andrzej Hajda <[email protected]>
Acked-by: Lee Jones <[email protected]>
Acked-by: Kyungmin Park <[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 | 9 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-max77693.c | 1070 ++++++++++++++++++++++++++++++++++++++++++
drivers/mfd/max77693.c | 5 +-
include/linux/mfd/max77693.h | 40 ++
5 files changed, 1124 insertions(+), 1 deletion(-)
create mode 100644 drivers/leds/leds-max77693.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 5032c6f..794055e 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -457,6 +457,15 @@ 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
+ 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 237c5ba..da1a4ba 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -55,6 +55,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..38a2398
--- /dev/null
+++ b/drivers/leds/leds-max77693.c
@@ -0,0 +1,1070 @@
+/*
+ * 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/led-flash-manager.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 MAX77693_LED_NAME_1 "max77693-flash_1"
+#define MAX77693_LED_NAME_2 "max77693-flash_2"
+
+#define MAX77693_TORCH_IOUT_BITS 4
+
+#define MAX77693_TORCH_NO_TIMER 0x40
+#define MAX77693_FLASH_TIMER_LEVEL 0x80
+
+#define MAX77693_FLASH_EN_OFF 0
+#define MAX77693_FLASH_EN_FLASH 1
+#define MAX77693_FLASH_EN_TORCH 2
+#define MAX77693_FLASH_EN_ON 3
+
+#define MAX77693_FLASH_EN1_SHIFT 6
+#define MAX77693_FLASH_EN2_SHIFT 4
+#define MAX77693_TORCH_EN1_SHIFT 2
+#define MAX77693_TORCH_EN2_SHIFT 0
+
+#define MAX77693_FLASH_LOW_BATTERY_EN 0x80
+
+#define MAX77693_FLASH_BOOST_FIXED 0x04
+#define MAX77693_FLASH_BOOST_LEDNUM_2 0x80
+
+#define MAX77693_FLASH_TIMEOUT_MIN 62500
+#define MAX77693_FLASH_TIMEOUT_MAX 1000000
+#define MAX77693_FLASH_TIMEOUT_STEP 62500
+
+#define MAX77693_TORCH_TIMEOUT_MIN 262000
+#define MAX77693_TORCH_TIMEOUT_MAX 15728000
+
+#define MAX77693_FLASH_IOUT_MIN 15625
+#define MAX77693_FLASH_IOUT_MAX_1LED 1000000
+#define MAX77693_FLASH_IOUT_MAX_2LEDS 625000
+#define MAX77693_FLASH_IOUT_STEP 15625
+
+#define MAX77693_TORCH_IOUT_MIN 15625
+#define MAX77693_TORCH_IOUT_MAX 250000
+#define MAX77693_TORCH_IOUT_STEP 15625
+
+#define MAX77693_FLASH_VSYS_MIN 2400
+#define MAX77693_FLASH_VSYS_MAX 3400
+#define MAX77693_FLASH_VSYS_STEP 33
+
+#define MAX77693_FLASH_VOUT_MIN 3300
+#define MAX77693_FLASH_VOUT_MAX 5500
+#define MAX77693_FLASH_VOUT_STEP 25
+#define MAX77693_FLASH_VOUT_RMIN 0x0c
+
+#define MAX77693_LED_STATUS_FLASH_ON (1 << 3)
+#define MAX77693_LED_STATUS_TORCH_ON (1 << 2)
+
+#define MAX77693_LED_FLASH_INT_FLED2_OPEN (1 << 0)
+#define MAX77693_LED_FLASH_INT_FLED2_SHORT (1 << 1)
+#define MAX77693_LED_FLASH_INT_FLED1_OPEN (1 << 2)
+#define MAX77693_LED_FLASH_INT_FLED1_SHORT (1 << 3)
+#define MAX77693_LED_FLASH_INT_OVER_CURRENT (1 << 4)
+
+#define MAX77693_MODE_OFF 0
+#define MAX77693_MODE_FLASH1 (1 << 0)
+#define MAX77693_MODE_FLASH2 (1 << 1)
+#define MAX77693_MODE_TORCH1 (1 << 2)
+#define MAX77693_MODE_TORCH2 (1 << 3)
+#define MAX77693_MODE_FLASH_EXTERNAL1 (1 << 4)
+#define MAX77693_MODE_FLASH_EXTERNAL2 (1 << 5)
+
+enum {
+ FLED1,
+ FLED2
+};
+
+enum {
+ FLASH,
+ TORCH
+};
+
+struct max77693_led {
+ struct regmap *regmap;
+ struct platform_device *pdev;
+ struct max77693_led_platform_data *pdata;
+ struct mutex lock;
+
+ struct led_classdev_flash ldev1;
+ struct work_struct work1_brightness_set;
+ struct v4l2_flash *v4l2_flash1;
+
+ struct led_classdev_flash ldev2;
+ struct work_struct work2_brightness_set;
+ struct v4l2_flash *v4l2_flash2;
+
+ unsigned int torch1_brightness;
+ unsigned int torch2_brightness;
+ unsigned int flash1_timeout;
+ unsigned int flash2_timeout;
+ unsigned int current_flash_timeout;
+ unsigned int mode_flags;
+ u8 torch_iout_reg;
+ bool iout_joint;
+};
+
+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 < MAX77693_FLASH_IOUT_MIN)
+ ua = MAX77693_FLASH_IOUT_MIN;
+ return (ua - MAX77693_FLASH_IOUT_MIN) / MAX77693_FLASH_IOUT_STEP;
+}
+
+static u8 max77693_flash_timeout_to_reg(u32 us)
+{
+ return (us - MAX77693_FLASH_TIMEOUT_MIN) / MAX77693_FLASH_TIMEOUT_STEP;
+}
+
+static inline struct max77693_led *ldev1_to_led(
+ struct led_classdev_flash *ldev1)
+{
+ return container_of(ldev1, struct max77693_led, ldev1);
+}
+
+static inline struct max77693_led *ldev2_to_led(
+ struct led_classdev_flash *ldev2)
+{
+ return container_of(ldev2, struct max77693_led, ldev2);
+}
+
+static u8 max77693_led_vsys_to_reg(u32 mv)
+{
+ return ((mv - MAX77693_FLASH_VSYS_MIN) / MAX77693_FLASH_VSYS_STEP) << 2;
+}
+
+static u8 max77693_led_vout_to_reg(u32 mv)
+{
+ return (mv - MAX77693_FLASH_VOUT_MIN) / MAX77693_FLASH_VOUT_STEP +
+ MAX77693_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 / MAX77693_FLASH_IOUT_STEP * MAX77693_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 & MAX77693_MODE_TORCH1) {
+ if (p->trigger[FLED1] & MAX77693_LED_TRIG_SOFT)
+ v |= MAX77693_FLASH_EN_ON << MAX77693_TORCH_EN1_SHIFT;
+ }
+
+ if (mode & MAX77693_MODE_TORCH2) {
+ if (p->trigger[FLED2] & MAX77693_LED_TRIG_SOFT)
+ v |= MAX77693_FLASH_EN_ON << MAX77693_TORCH_EN2_SHIFT;
+ }
+
+ if (mode & MAX77693_MODE_FLASH1) {
+ if (p->trigger[FLED1] & MAX77693_LED_TRIG_SOFT)
+ v |= MAX77693_FLASH_EN_ON << MAX77693_FLASH_EN1_SHIFT;
+ } else if (mode & MAX77693_MODE_FLASH_EXTERNAL1) {
+ if (p->trigger[FLED1] & MAX77693_LED_TRIG_EXT)
+ v |= MAX77693_FLASH_EN_FLASH << MAX77693_FLASH_EN1_SHIFT;
+ /*
+ * 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 |= MAX77693_FLASH_EN_TORCH << MAX77693_TORCH_EN1_SHIFT;
+ }
+
+ if (mode & MAX77693_MODE_FLASH2) {
+ if (p->trigger[FLED2] & MAX77693_LED_TRIG_SOFT)
+ v |= MAX77693_FLASH_EN_ON << MAX77693_FLASH_EN2_SHIFT;
+ } else if (mode & MAX77693_MODE_FLASH_EXTERNAL1) {
+ if (p->trigger[FLED2] & MAX77693_LED_TRIG_EXT)
+ v |= MAX77693_FLASH_EN_FLASH << MAX77693_FLASH_EN2_SHIFT;
+ /*
+ * 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 |= MAX77693_FLASH_EN_TORCH << MAX77693_TORCH_EN2_SHIFT;
+ }
+
+ /* Reset the register only prior setting flash modes */
+ if (mode & ~(MAX77693_MODE_TORCH1 | MAX77693_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 int max77693_add_mode(struct max77693_led *led, unsigned int mode)
+{
+ int ret;
+
+ /* Span 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 & MAX77693_MODE_TORCH1) &&
+ (led->mode_flags & MAX77693_MODE_TORCH1))
+ return 0;
+ if ((mode & MAX77693_MODE_TORCH2) &&
+ (led->mode_flags & MAX77693_MODE_TORCH2))
+ return 0;
+ /*
+ * FLASH_EXTERNAL mode activates HW triggered flash and torch
+ * modes in the device. The related register settings interfere
+ * with SW triggerred modes, thus clear them to ensure proper
+ * device configuration.
+ */
+ if (mode & MAX77693_MODE_FLASH_EXTERNAL1)
+ led->mode_flags &= (~MAX77693_MODE_TORCH1 &
+ ~MAX77693_MODE_FLASH1);
+ if (mode & MAX77693_MODE_FLASH_EXTERNAL2)
+ led->mode_flags &= (~MAX77693_MODE_TORCH2 &
+ ~MAX77693_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
+ * spurous flash strobing on every subsequent torch mode
+ * setting.
+ */
+ if (mode & MAX77693_MODE_FLASH1 ||
+ mode & MAX77693_MODE_FLASH_EXTERNAL1 ||
+ mode & MAX77693_MODE_FLASH2 ||
+ mode & MAX77693_MODE_FLASH_EXTERNAL2)
+ led->mode_flags &= ~mode;
+
+ return ret;
+}
+
+static int max77693_clear_mode(struct max77693_led *led, unsigned int mode)
+{
+ int ret;
+
+ /* Span mode on FLED2 for joint iouts case */
+ if (led->iout_joint)
+ mode |= (mode << 1);
+
+ 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 | iout2_reg <<
+ MAX77693_TORCH_IOUT_BITS);
+
+ 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 |= MAX77693_FLASH_TIMER_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_INT_STATUS, &v);
+ if (ret < 0)
+ return ret;
+
+ *state = v & MAX77693_LED_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, firstLed, lastLed, 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) {
+ firstLed = FLED1;
+ lastLed = FLED1;
+ max_flash_curr[FLED1] = p->iout_flash[FLED1] +
+ p->iout_flash[FLED2];
+ } else {
+ firstLed = p->fleds[FLED1] ? FLED1 : FLED2;
+ lastLed = p->num_leds == 2 ? FLED2 : firstLed;
+ max_flash_curr[FLED1] = p->iout_flash[FLED1];
+ max_flash_curr[FLED2] = p->iout_flash[FLED2];
+ }
+
+ for (i = firstLed; i <= lastLed; ++i) {
+ ret = max77693_set_flash_current(led, i,
+ max_flash_curr[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ v = MAX77693_TORCH_NO_TIMER | MAX77693_LED_TRIG_TYPE_LEVEL;
+ ret = regmap_write(rmap, MAX77693_LED_REG_ITORCHTIMER, v);
+ if (ret < 0)
+ return ret;
+
+ ret = max77693_set_timeout(led, p->flash_timeout);
+ if (ret < 0)
+ return ret;
+
+ if (p->low_vsys > 0)
+ v = max77693_led_vsys_to_reg(p->low_vsys) |
+ MAX77693_FLASH_LOW_BATTERY_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 = MAX77693_FLASH_BOOST_FIXED;
+ else
+ v = p->boost_mode | p->boost_mode << 1;
+ if (p->fleds[FLED1] && p->fleds[FLED2])
+ v |= MAX77693_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, MAX77693_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, MAX77693_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 *
+ MAX77693_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, MAX77693_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_led *led = container_of(work, \
+ struct max77693_led, \
+ work##ID##_brightness_set); \
+ \
+ max77693_led_brightness_set(led, FLED##ID, \
+ led->torch##ID##_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); \
+ \
+ led->torch##ID##_brightness = value; \
+ schedule_work(&led->work##ID##_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); \
+ int ret; \
+ \
+ mutex_lock(&led->lock); \
+ \
+ if (!state) { \
+ ret = max77693_clear_mode(led, \
+ MAX77693_MODE_FLASH##ID); \
+ goto unlock; \
+ } \
+ \
+ if (led->flash##ID##_timeout != led->current_flash_timeout) { \
+ ret = max77693_set_timeout(led, \
+ led->flash##ID##_timeout); \
+ if (ret < 0) \
+ goto unlock; \
+ } \
+ \
+ ret = max77693_add_mode(led, MAX77693_MODE_FLASH##ID); \
+ \
+unlock: \
+ mutex_unlock(&led->lock); \
+ return ret; \
+}
+
+#define MAX77693_LED_FLASH_EXTERNAL_STROBE_SET(ID) \
+static int max77693_led##ID##_external_strobe_set( \
+ struct led_classdev_flash *flash, \
+ bool enable) \
+{ \
+ struct max77693_led *led = ldev##ID##_to_led(flash); \
+ int ret; \
+ \
+ mutex_lock(&led->lock); \
+ \
+ if (enable) \
+ ret = max77693_add_mode(led, \
+ MAX77693_MODE_FLASH_EXTERNAL##ID); \
+ else \
+ ret = max77693_clear_mode(led, \
+ MAX77693_MODE_FLASH_EXTERNAL##ID); \
+ \
+ mutex_unlock(&led->lock); \
+ \
+ return ret; \
+}
+
+#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 & MAX77693_LED_FLASH_INT_FLED##ID##_OPEN) \
+ *fault |= LED_FAULT_OVER_VOLTAGE; \
+ if (v & MAX77693_LED_FLASH_INT_FLED##ID##_SHORT) \
+ *fault |= LED_FAULT_SHORT_CIRCUIT; \
+ if (v & MAX77693_LED_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); \
+ \
+ if (!state) \
+ return -EINVAL; \
+ \
+ return max77693_strobe_status_get(led, state); \
+}
+
+#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); \
+ \
+ mutex_lock(&led->lock); \
+ led->flash##ID##_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 void max77693_led_parse_dt(struct max77693_led_platform_data *p,
+ struct device_node *node)
+{
+ of_property_read_u32_array(node, "iout-torch", p->iout_torch, 2);
+ of_property_read_u32_array(node, "iout-flash", p->iout_flash, 2);
+ 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, "flash-timeout", &p->flash_timeout);
+ 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,num-leds", &p->num_leds);
+ of_property_read_u32(node, "maxim,vsys-min", &p->low_vsys);
+}
+
+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);
+ p->num_leds = clamp_val(p->num_leds, 1, 2);
+
+ 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 ? MAX77693_FLASH_IOUT_MAX_2LEDS :
+ MAX77693_FLASH_IOUT_MAX_1LED;
+
+ if (p->fleds[FLED1]) {
+ clamp_align(&p->iout_torch[FLED1], MAX77693_TORCH_IOUT_MIN,
+ MAX77693_TORCH_IOUT_MAX, MAX77693_TORCH_IOUT_STEP);
+ clamp_align(&p->iout_flash[FLED1], MAX77693_FLASH_IOUT_MIN,
+ max, MAX77693_FLASH_IOUT_STEP);
+ } else {
+ p->iout_torch[FLED1] = p->iout_flash[FLED1] = 0;
+ }
+ if (p->fleds[FLED2]) {
+ clamp_align(&p->iout_torch[FLED2], MAX77693_TORCH_IOUT_MIN,
+ MAX77693_TORCH_IOUT_MAX, MAX77693_TORCH_IOUT_STEP);
+ clamp_align(&p->iout_flash[FLED2], MAX77693_FLASH_IOUT_MIN,
+ max, MAX77693_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);
+
+ clamp_align(&p->flash_timeout, MAX77693_FLASH_TIMEOUT_MIN,
+ MAX77693_FLASH_TIMEOUT_MAX, MAX77693_FLASH_TIMEOUT_STEP);
+
+ clamp_align(&p->boost_vout, MAX77693_FLASH_VOUT_MIN,
+ MAX77693_FLASH_VOUT_MAX, MAX77693_FLASH_VOUT_STEP);
+
+ if (p->low_vsys)
+ clamp_align(&p->low_vsys, MAX77693_FLASH_VSYS_MIN,
+ MAX77693_FLASH_VSYS_MAX, MAX77693_FLASH_VSYS_STEP);
+}
+
+static int max77693_led_get_platform_data(struct max77693_led *led)
+{
+ struct max77693_led_platform_data *p;
+ struct device *dev = &led->pdev->dev;
+
+ if (dev->of_node) {
+ p = devm_kzalloc(dev, sizeof(*led->pdata), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ max77693_led_parse_dt(p, dev->of_node);
+ } else {
+ p = dev_get_platdata(dev);
+ if (!p)
+ return -ENODEV;
+ }
+ led->pdata = p;
+
+ max77693_led_validate_platform_data(p);
+
+ return 0;
+}
+
+#define MAX77693_LED_INIT_FLASH_OPS(ID) \
+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, \
+ .external_strobe_set = max77693_led##ID##_external_strobe_set, \
+ .fault_get = max77693_led##ID##_flash_fault_get, \
+};
+
+MAX77693_LED_INIT_FLASH_OPS(1)
+MAX77693_LED_INIT_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 ?
+ MAX77693_TORCH_IOUT_MIN * 2 :
+ MAX77693_TORCH_IOUT_MIN;
+ setting->max = led->iout_joint ?
+ p->iout_torch[FLED1] + p->iout_torch[FLED2] :
+ p->iout_torch[led_id];
+ setting->step = MAX77693_TORCH_IOUT_STEP;
+ setting->val = setting->max;
+
+ /* Init flash intensity setting */
+ setting = &s->flash_brightness;
+ setting->min = led->iout_joint ?
+ MAX77693_FLASH_IOUT_MIN * 2 :
+ MAX77693_FLASH_IOUT_MIN;
+ setting->max = led->iout_joint ?
+ p->iout_flash[FLED1] + p->iout_flash[FLED2] :
+ p->iout_flash[led_id];
+ setting->step = MAX77693_FLASH_IOUT_STEP;
+ setting->val = setting->max;
+
+ /* Init flash timeout setting */
+ setting = &s->flash_timeout;
+ setting->min = MAX77693_FLASH_TIMEOUT_MIN;
+ setting->max = MAX77693_FLASH_TIMEOUT_MAX;
+ setting->step = MAX77693_FLASH_TIMEOUT_STEP;
+ setting->val = p->flash_timeout;
+}
+
+#ifdef CONFIG_V4L2_FLASH_LED_CLASS
+static void max77693_init_v4l2_ctrl_config(struct max77693_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_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;
+}
+#else
+#define max77693_init_v4l2_ctrl_config(s, config)
+#endif
+
+#define MAX77693_LED_REGISTER_FLASH(ID) \
+static int max77693_register_led##ID(struct max77693_led *led) \
+{ \
+ struct platform_device *pdev = led->pdev; \
+ struct device *dev = &pdev->dev; \
+ struct led_classdev_flash *flash; \
+ struct led_classdev *led_cdev; \
+ struct max77693_led_platform_data *p = led->pdata; \
+ struct v4l2_flash_ctrl_config v4l2_flash_config; \
+ struct max77693_led_settings settings; \
+ int ret; \
+ \
+ flash = &led->ldev##ID; \
+ \
+ /* Init flash settings */ \
+ max77693_init_flash_settings(led, &settings, FLED##ID); \
+ /* Init V4L2 Flash controls basing on initialized settings */ \
+ max77693_init_v4l2_ctrl_config(&settings, &v4l2_flash_config); \
+ \
+ /* Init led class */ \
+ led_cdev = &flash->led_cdev; \
+ led_cdev->name = MAX77693_LED_NAME_##ID; \
+ led_cdev->brightness_set = max77693_led##ID##_brightness_set; \
+ led_cdev->torch_brightness_set = \
+ max77693_led##ID##_torch_brightness_set; \
+ led_cdev->max_brightness = settings.torch_brightness.val / \
+ MAX77693_TORCH_IOUT_STEP; \
+ led_cdev->flags |= LED_DEV_CAP_FLASH; \
+ \
+ INIT_WORK(&led->work##ID##_brightness_set, \
+ max77693_led##ID##_brightness_set_work); \
+ \
+ flash->ops = &flash_ops##ID; \
+ flash->brightness = settings.flash_brightness; \
+ flash->timeout = settings.flash_timeout; \
+ led->flash##ID##_timeout = flash->timeout.val; \
+ \
+ if (p->trigger[FLED##ID] & MAX77693_LED_TRIG_FLASH) \
+ flash->has_external_strobe = true; \
+ \
+ /* Register in the LED subsystem. */ \
+ ret = led_classdev_flash_register(&pdev->dev, flash, \
+ dev->of_node); \
+ if (ret < 0) \
+ return ret; \
+ \
+ ret = v4l2_flash_init(flash, \
+ &v4l2_flash_config, \
+ led_get_v4l2_flash_ops(), \
+ &led->v4l2_flash##ID); \
+ if (ret < 0) \
+ goto err_v4l2_flash_init; \
+ \
+ return 0; \
+ \
+err_v4l2_flash_init: \
+ led_classdev_flash_unregister(flash); \
+ \
+ return ret; \
+}
+
+MAX77693_LED_REGISTER_FLASH(1)
+MAX77693_LED_REGISTER_FLASH(2)
+
+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;
+ int ret;
+
+ led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->pdev = pdev;
+ led->regmap = iodev->regmap;
+ 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)
+ return ret;
+
+ if (led->iout_joint || p->fleds[FLED1]) {
+ ret = max77693_register_led1(led);
+ if (ret < 0)
+ goto err_register_led1;
+ }
+
+ if (!led->iout_joint && p->fleds[FLED2]) {
+ ret = max77693_register_led2(led);
+ if (ret < 0)
+ goto err_register_led2;
+ }
+
+ return 0;
+
+err_register_led2:
+ if (!p->fleds[FLED1])
+ goto err_register_led1;
+ v4l2_flash_release(led->v4l2_flash1);
+ led_classdev_flash_unregister(&led->ldev1);
+err_register_led1:
+ 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;
+
+ if (led->iout_joint || p->fleds[FLED1]) {
+ v4l2_flash_release(led->v4l2_flash1);
+ led_classdev_flash_unregister(&led->ldev1);
+ cancel_work_sync(&led->work1_brightness_set);
+ }
+ if (!led->iout_joint && p->fleds[FLED2]) {
+ v4l2_flash_release(led->v4l2_flash2);
+ led_classdev_flash_unregister(&led->ldev2);
+ cancel_work_sync(&led->work2_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");
diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c
index 249c139..cf008f4 100644
--- a/drivers/mfd/max77693.c
+++ b/drivers/mfd/max77693.c
@@ -44,9 +44,12 @@
static const struct mfd_cell max77693_devs[] = {
{ .name = "max77693-pmic", },
{ .name = "max77693-charger", },
- { .name = "max77693-flash", },
{ .name = "max77693-muic", },
{ .name = "max77693-haptic", },
+ {
+ .name = "max77693-flash",
+ .of_compatible = "maxim,max77693-flash",
+ },
};

static const struct regmap_config max77693_regmap_config = {
diff --git a/include/linux/mfd/max77693.h b/include/linux/mfd/max77693.h
index 3f3dc45..f0b6585 100644
--- a/include/linux/mfd/max77693.h
+++ b/include/linux/mfd/max77693.h
@@ -63,6 +63,45 @@ struct max77693_muic_platform_data {
int path_uart;
};

+/* MAX77693 led flash */
+
+/* triggers */
+enum max77693_led_trigger {
+ MAX77693_LED_TRIG_OFF,
+ MAX77693_LED_TRIG_FLASH,
+ MAX77693_LED_TRIG_TORCH,
+ MAX77693_LED_TRIG_EXT,
+ MAX77693_LED_TRIG_SOFT,
+};
+
+/* trigger types */
+enum max77693_led_trigger_type {
+ MAX77693_LED_TRIG_TYPE_EDGE,
+ MAX77693_LED_TRIG_TYPE_LEVEL,
+};
+
+/* boost modes */
+enum max77693_led_boost_mode {
+ MAX77693_LED_BOOST_NONE,
+ MAX77693_LED_BOOST_ADAPTIVE,
+ MAX77693_LED_BOOST_FIXED,
+};
+
+struct max77693_led_platform_data {
+ u32 fleds[2];
+ u32 iout_torch[2];
+ u32 iout_flash[2];
+ u32 trigger[2];
+ u32 trigger_type[2];
+ u32 num_leds;
+ u32 boost_mode;
+ u32 flash_timeout;
+ u32 boost_vout;
+ u32 low_vsys;
+};
+
+/* MAX77693 */
+
struct max77693_platform_data {
/* regulator data */
struct max77693_regulator_data *regulators;
@@ -70,5 +109,6 @@ struct max77693_platform_data {

/* muic data */
struct max77693_muic_platform_data *muic_data;
+ struct max77693_led_platform_data *led_data;
};
#endif /* __LINUX_MFD_MAX77693_H */
--
1.7.9.5

2014-07-11 14:07:37

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 17/21] DT: Add documentation for the mfd Maxim max77693

This patch adds device tree binding documentation for
the flash cell of the Maxim max77693 multifunctional device.

Signed-off-by: Andrzej Hajda <[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]>
---
Documentation/devicetree/bindings/mfd/max77693.txt | 62 ++++++++++++++++++++
1 file changed, 62 insertions(+)

diff --git a/Documentation/devicetree/bindings/mfd/max77693.txt b/Documentation/devicetree/bindings/mfd/max77693.txt
index 11921cc..0c3db3d 100644
--- a/Documentation/devicetree/bindings/mfd/max77693.txt
+++ b/Documentation/devicetree/bindings/mfd/max77693.txt
@@ -27,6 +27,55 @@ Optional properties:

[*] refer Documentation/devicetree/bindings/regulator/regulator.txt

+Optional node:
+- led-flash : the LED submodule device node
+
+Required properties of "led-flash" node:
+- compatible : must be "maxim,max77693-flash"
+- maxim,num-leds : number of connected leds
+ Possible values: 1 or 2.
+- 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.
+
+Optional properties of "led-flash" node:
+- 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 maxim,num-leds == <2> boost must be enabled
+ (it defaults to 1 if not set):
+ Possible values:
+ 0 - no boost,
+ 1 - adaptive mode,
+ 2 - fixed mode.
+- iout-torch : Array of maximum intensities in microamperes of the torch
+ led currents in order: fled1, fled2.
+ 15625 - 250000
+- iout-flash : Array of maximum intensities in microamperes of the flash
+ led currents in order: fled1, fled2.
+ Range:
+ 15625 - 1000000 (max 625000 if boost mode is enabled)
+- flash-timeout : timeout in microseconds after which flash led
+ is turned off
+ Range:
+ 62500 - 1000000
+- 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,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,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.
+
Example:
max77693@66 {
compatible = "maxim,max77693";
@@ -52,4 +101,17 @@ Example:
regulator-boot-on;
};
};
+ led_flash: led-flash {
+ compatible = "maxim,max77693-flash";
+ iout-torch = <500000 0>;
+ iout-flash = <1250000 0>;
+ flash-timeout = <1000000 1000000>;
+ maxim,num-leds = <1>;
+ maxim,fleds = <1 1>;
+ maxim,trigger = <7 7>;
+ maxim,trigger-type = <0 1>;
+ maxim,boost-mode = <1>;
+ maxim,boost-vout = <5000>;
+ maxim,vsys-min = <2400>;
+ };
};
--
1.7.9.5

2014-07-11 14:05:29

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 14/21] v4l2-ctrls: add control for flash strobe signal providers

Add V4L2_CID_STROBE_PROVIDER of type menu, which allows
for enumerating of available external flash strobe signal
providers and setting the active one.

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 | 11 +++++++++++
drivers/media/v4l2-core/v4l2-ctrls.c | 2 ++
include/uapi/linux/v4l2-controls.h | 2 ++
3 files changed, 15 insertions(+)

diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml
index 47198ee..d9f6c3f 100644
--- a/Documentation/DocBook/media/v4l/controls.xml
+++ b/Documentation/DocBook/media/v4l/controls.xml
@@ -4300,6 +4300,17 @@ interface and may change in the future.</para>
is strobing at the moment or not. This is a read-only
control.</entry>
</row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE_PROVIDER</constant></entry>
+ <entry>menu</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Provider of the external strobe signal. If a flash
+ device declares more than one available external strobe signal provider then
+ this control allows to select the active one. &VIDIOC-QUERYCTRL; has to be
+ used to get the list of available strobe providers.
+ </entry>
+ </row>
<row>
<entry spanname="id"><constant>V4L2_CID_FLASH_TIMEOUT</constant></entry>
<entry>integer</entry>
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 55c6832..f298f7e 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -825,6 +825,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_STROBE_PROVIDER: return "Strobe Provider";

/* JPEG encoder controls */
/* Keep the order of the 'case's the same as in videodev2.h! */
@@ -988,6 +989,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_TEST_PATTERN:
case V4L2_CID_TUNE_DEEMPHASIS:
case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL:
+ case V4L2_CID_FLASH_STROBE_PROVIDER:
*type = V4L2_CTRL_TYPE_MENU;
break;
case V4L2_CID_LINK_FREQ:
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 2ac5597..1f05c7c 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -822,6 +822,8 @@ 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_STROBE_PROVIDER (V4L2_CID_FLASH_CLASS_BASE + 13)
+

/* JPEG-class control IDs */

--
1.7.9.5

2014-07-11 14:08:24

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 13/21] v4l2-device: add v4l2_device_register_subdev_node API

Extract the code executed for each entry of the subdev list
and put it to the separate function. Export it as a public API.
It allows for registering single sub-device at a time.

Signed-off-by: Jacek Anaszewski <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Hans Verkuil <[email protected]>
Cc: Laurent Pinchart <[email protected]>
---
drivers/media/v4l2-core/v4l2-device.c | 63 +++++++++++++++++++--------------
include/media/v4l2-device.h | 7 ++++
2 files changed, 44 insertions(+), 26 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c
index 015f92a..0e91ef7 100644
--- a/drivers/media/v4l2-core/v4l2-device.c
+++ b/drivers/media/v4l2-core/v4l2-device.c
@@ -216,9 +216,43 @@ static void v4l2_device_release_subdev_node(struct video_device *vdev)
kfree(vdev);
}

-int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
+int v4l2_device_register_subdev_node(struct v4l2_subdev *sd,
+ struct v4l2_device *v4l2_dev)
{
struct video_device *vdev;
+ int err;
+
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
+ return 0;
+
+ vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+ if (!vdev)
+ return -ENOMEM;
+
+ video_set_drvdata(vdev, sd);
+ strlcpy(vdev->name, sd->name, sizeof(vdev->name));
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->fops = &v4l2_subdev_fops;
+ vdev->release = v4l2_device_release_subdev_node;
+ vdev->ctrl_handler = sd->ctrl_handler;
+ err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
+ sd->owner);
+ if (err < 0) {
+ kfree(vdev);
+ return err;
+ }
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ sd->entity.info.v4l.major = VIDEO_MAJOR;
+ sd->entity.info.v4l.minor = vdev->minor;
+#endif
+ sd->devnode = vdev;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_node);
+
+int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
+{
struct v4l2_subdev *sd;
int err;

@@ -226,32 +260,9 @@ int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
* V4L2_SUBDEV_FL_HAS_DEVNODE flag.
*/
list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
- if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
- continue;
-
- vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
- if (!vdev) {
- err = -ENOMEM;
- goto clean_up;
- }
-
- video_set_drvdata(vdev, sd);
- strlcpy(vdev->name, sd->name, sizeof(vdev->name));
- vdev->v4l2_dev = v4l2_dev;
- vdev->fops = &v4l2_subdev_fops;
- vdev->release = v4l2_device_release_subdev_node;
- vdev->ctrl_handler = sd->ctrl_handler;
- err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
- sd->owner);
- if (err < 0) {
- kfree(vdev);
+ err = v4l2_device_register_subdev_node(sd, v4l2_dev);
+ if (err < 0)
goto clean_up;
- }
-#if defined(CONFIG_MEDIA_CONTROLLER)
- sd->entity.info.v4l.major = VIDEO_MAJOR;
- sd->entity.info.v4l.minor = vdev->minor;
-#endif
- sd->devnode = vdev;
}
return 0;

diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h
index ffb69da..76594fc 100644
--- a/include/media/v4l2-device.h
+++ b/include/media/v4l2-device.h
@@ -114,6 +114,13 @@ int __must_check v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
wasn't registered. In that case it will do nothing. */
void v4l2_device_unregister_subdev(struct v4l2_subdev *sd);

+/* Register device node for the subdev of the v4l2 device if it is marked with
+ * the V4L2_SUBDEV_FL_HAS_DEVNODE flag.
+ */
+int __must_check
+v4l2_device_register_subdev_node(struct v4l2_subdev *sd,
+ struct v4l2_device *v4l2_dev);
+
/* Register device nodes for all subdev of the v4l2 device that are marked with
* the V4L2_SUBDEV_FL_HAS_DEVNODE flag.
*/
--
1.7.9.5

2014-07-11 14:05:16

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 11/21] DT: leds: Add flash led devices related properties

Addition of a LED Flash Class extension entails the need for flash led
specific device tree properties. The properties being added are:
iout-torch, iout-flash, iout-indicator and flash-timeout.

Signed-off-by: Jacek Anaszewski <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Stephen Warren <[email protected]>
Cc: Grant Likely <[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]>
---
Documentation/devicetree/bindings/leds/common.txt | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/Documentation/devicetree/bindings/leds/common.txt b/Documentation/devicetree/bindings/leds/common.txt
index 2d88816..40f4b9a 100644
--- a/Documentation/devicetree/bindings/leds/common.txt
+++ b/Documentation/devicetree/bindings/leds/common.txt
@@ -3,6 +3,17 @@ Common leds properties.
Optional properties for child nodes:
- label : The label for this LED. If omitted, the label is
taken from the node name (excluding the unit address).
+- iout-torch : Array of maximum intensities in microamperes of the torch
+ led currents in order from sub-led 0 to N-1, where N is the number
+ of torch sub-leds exposed by the device
+- iout-flash : Array of maximum intensities in microamperes of the flash
+ led currents in order from sub-led 0 to N-1, where N is the number
+ of flash sub-leds exposed by the device
+- iout-indicator : Array of maximum intensities in microamperes of
+ the indicator led currents in order from sub-led 0 to N-1,
+ where N is the number of indicator sub-leds exposed by the device
+- flash-timeout : timeout in microseconds after which flash led
+ is turned off

- linux,default-trigger : This parameter, if present, is a
string defining the trigger assigned to the LED. Current triggers are:
@@ -19,5 +30,10 @@ Examples:
system-status {
label = "Status";
linux,default-trigger = "heartbeat";
+ iout-torch = <500 500>;
+ iout-flash = <1000 1000>;
+ iout-indicator = <100 100>;
+ flash-timeout = <1000>;
+
...
};
--
1.7.9.5

2014-07-11 14:05:09

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 08/21] leds: Add sysfs and kernel internal API for flash LEDs

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,
external_strobe, indicator_brightness, max_indicator_brightness,
strobe_provider, strobe_providerN, blocking_strobe. All the flash
related features are placed in a separate module.

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 | 13 +
drivers/leds/Makefile | 5 +
drivers/leds/led-class-flash.c | 715 ++++++++++++++++++++++++++++++++++
drivers/leds/led-class.c | 4 +
drivers/leds/led-flash-gpio-mux.c | 102 +++++
drivers/leds/led-flash-manager.c | 698 +++++++++++++++++++++++++++++++++
drivers/leds/led-triggers.c | 5 +
drivers/leds/of_led_flash_manager.c | 155 ++++++++
include/linux/led-class-flash.h | 290 ++++++++++++++
include/linux/led-flash-gpio-mux.h | 68 ++++
include/linux/led-flash-manager.h | 121 ++++++
include/linux/leds.h | 4 +
include/linux/of_led_flash_manager.h | 80 ++++
13 files changed, 2260 insertions(+)
create mode 100644 drivers/leds/led-class-flash.c
create mode 100644 drivers/leds/led-flash-gpio-mux.c
create mode 100644 drivers/leds/led-flash-manager.c
create mode 100644 drivers/leds/of_led_flash_manager.c
create mode 100644 include/linux/led-class-flash.h
create mode 100644 include/linux/led-flash-gpio-mux.h
create mode 100644 include/linux/led-flash-manager.h
create mode 100644 include/linux/of_led_flash_manager.h

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 6784c17..5032c6f 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -19,6 +19,19 @@ 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
+ depends on OF
+ 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. It allows also for dynamic routing
+ of external strobe signals basing on the information provided
+ in the Device Tree binding. 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 79c5155..237c5ba 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -2,6 +2,11 @@
# LED Core
obj-$(CONFIG_NEW_LEDS) += led-core.o
obj-$(CONFIG_LEDS_CLASS) += led-class.o
+obj-$(CONFIG_LEDS_CLASS_FLASH) += led-flash.o
+led-flash-objs := led-class-flash.o \
+ led-flash-manager.o \
+ led-flash-gpio-mux.o \
+ of_led_flash_manager.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..607f2d7
--- /dev/null
+++ b/drivers/leds/led-class-flash.c
@@ -0,0 +1,715 @@
+/*
+ * 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/leds.h>
+#include <linux/led-class-flash.h>
+#include <linux/led-flash-manager.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <media/v4l2-flash.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_lock);
+
+ if (led_sysfs_is_locked(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_lock);
+ 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 indicator_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_lock);
+
+ if (led_sysfs_is_locked(led_cdev)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ ret = kstrtoul(buf, 10, &state);
+ if (ret)
+ goto unlock;
+
+ ret = led_set_indicator_brightness(flash, state);
+ if (ret < 0)
+ goto unlock;
+
+ ret = size;
+unlock:
+ mutex_unlock(&led_cdev->led_lock);
+ return ret;
+}
+
+static ssize_t indicator_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_indicator_brightness(flash);
+
+ return sprintf(buf, "%u\n", flash->indicator_brightness->val);
+}
+static DEVICE_ATTR_RW(indicator_brightness);
+
+static ssize_t max_indicator_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->indicator_brightness->max);
+}
+static DEVICE_ATTR_RO(max_indicator_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_lock);
+
+ if (led_sysfs_is_locked(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_lock);
+ 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_lock);
+
+ if (led_sysfs_is_locked(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_lock);
+ 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 external_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 external_strobe;
+ ssize_t ret;
+
+ mutex_lock(&led_cdev->led_lock);
+
+ if (led_sysfs_is_locked(led_cdev)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ ret = kstrtoul(buf, 10, &external_strobe);
+ if (ret)
+ goto unlock;
+
+ if (external_strobe > 1) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ret = led_set_external_strobe(flash, external_strobe);
+ if (ret < 0)
+ goto unlock;
+ ret = size;
+unlock:
+ mutex_unlock(&led_cdev->led_lock);
+ return ret;
+}
+
+static ssize_t external_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->external_strobe);
+}
+static DEVICE_ATTR_RW(external_strobe);
+
+static struct attribute *led_flash_strobe_attrs[] = {
+ &dev_attr_flash_strobe.attr,
+ NULL,
+};
+
+static struct attribute *led_flash_indicator_attrs[] = {
+ &dev_attr_indicator_brightness.attr,
+ &dev_attr_max_indicator_brightness.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_external_strobe_attrs[] = {
+ &dev_attr_external_strobe.attr,
+ NULL,
+};
+
+static struct attribute *led_flash_fault_attrs[] = {
+ &dev_attr_flash_fault.attr,
+ NULL,
+};
+
+static struct attribute_group led_flash_strobe_group = {
+ .attrs = led_flash_strobe_attrs,
+};
+
+static struct attribute_group led_flash_brightness_group = {
+ .attrs = led_flash_brightness_attrs,
+};
+
+static struct attribute_group led_flash_timeout_group = {
+ .attrs = led_flash_timeout_attrs,
+};
+
+static struct attribute_group led_flash_indicator_group = {
+ .attrs = led_flash_indicator_attrs,
+};
+
+static struct attribute_group led_flash_fault_group = {
+ .attrs = led_flash_fault_attrs,
+};
+
+static struct attribute_group led_flash_external_strobe_group = {
+ .attrs = led_flash_external_strobe_attrs,
+};
+
+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);
+ call_flash_op(flash, indicator_brightness_set,
+ flash->indicator_brightness->val);
+}
+
+#ifdef CONFIG_V4L2_FLASH_LED_CLASS
+const struct v4l2_flash_ops led_flash_v4l2_ops = {
+ .torch_brightness_set = led_set_torch_brightness,
+ .torch_brightness_update = led_update_brightness,
+ .flash_brightness_set = led_set_flash_brightness,
+ .flash_brightness_update = led_update_flash_brightness,
+ .indicator_brightness_set = led_set_indicator_brightness,
+ .indicator_brightness_update = led_update_indicator_brightness,
+ .strobe_set = led_set_flash_strobe,
+ .strobe_get = led_get_flash_strobe,
+ .timeout_set = led_set_flash_timeout,
+ .external_strobe_set = led_set_external_strobe,
+ .fault_get = led_get_flash_fault,
+ .sysfs_lock = led_sysfs_lock,
+ .sysfs_unlock = led_sysfs_unlock,
+};
+
+const struct v4l2_flash_ops *led_get_v4l2_flash_ops(void)
+{
+ return &led_flash_v4l2_ops;
+}
+EXPORT_SYMBOL_GPL(led_get_v4l2_flash_ops);
+#endif
+
+static void led_flash_remove_sysfs_groups(struct led_classdev_flash *flash)
+{
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ int i;
+
+ for (i = 0; i < LED_FLASH_MAX_SYSFS_GROUPS; ++i)
+ if (flash->sysfs_groups[i])
+ sysfs_remove_group(&led_cdev->dev->kobj,
+ flash->sysfs_groups[i]);
+}
+
+static int led_flash_create_sysfs_groups(struct led_classdev_flash *flash)
+{
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ const struct led_flash_ops *ops = flash->ops;
+ int ret, num_sysfs_groups = 0;
+
+ memset(flash->sysfs_groups, 0, sizeof(*flash->sysfs_groups) *
+ LED_FLASH_MAX_SYSFS_GROUPS);
+
+ ret = sysfs_create_group(&led_cdev->dev->kobj, &led_flash_strobe_group);
+ if (ret < 0)
+ goto err_create_group;
+ flash->sysfs_groups[num_sysfs_groups++] = &led_flash_strobe_group;
+
+ if (flash->indicator_brightness) {
+ ret = sysfs_create_group(&led_cdev->dev->kobj,
+ &led_flash_indicator_group);
+ if (ret < 0)
+ goto err_create_group;
+ flash->sysfs_groups[num_sysfs_groups++] =
+ &led_flash_indicator_group;
+ }
+
+ if (ops->flash_brightness_set) {
+ ret = sysfs_create_group(&led_cdev->dev->kobj,
+ &led_flash_brightness_group);
+ if (ret < 0)
+ goto err_create_group;
+ flash->sysfs_groups[num_sysfs_groups++] =
+ &led_flash_brightness_group;
+ }
+
+ if (ops->timeout_set) {
+ ret = sysfs_create_group(&led_cdev->dev->kobj,
+ &led_flash_timeout_group);
+ if (ret < 0)
+ goto err_create_group;
+ flash->sysfs_groups[num_sysfs_groups++] =
+ &led_flash_timeout_group;
+ }
+
+ if (ops->fault_get) {
+ ret = sysfs_create_group(&led_cdev->dev->kobj,
+ &led_flash_fault_group);
+ if (ret < 0)
+ goto err_create_group;
+ flash->sysfs_groups[num_sysfs_groups++] =
+ &led_flash_fault_group;
+ }
+
+ if (flash->has_external_strobe) {
+ ret = sysfs_create_group(&led_cdev->dev->kobj,
+ &led_flash_external_strobe_group);
+ if (ret < 0)
+ goto err_create_group;
+ flash->sysfs_groups[num_sysfs_groups++] =
+ &led_flash_external_strobe_group;
+ }
+
+ return 0;
+
+err_create_group:
+ led_flash_remove_sysfs_groups(flash);
+ return ret;
+}
+
+int led_classdev_flash_register(struct device *parent,
+ struct led_classdev_flash *flash,
+ struct device_node *node)
+{
+ struct led_classdev *led_cdev;
+ const struct led_flash_ops *ops;
+ int ret = -EINVAL;
+
+ if (!flash)
+ return -EINVAL;
+
+ led_cdev = &flash->led_cdev;
+
+ /* Torch capability is default for every LED Flash Class device */
+ led_cdev->flags |= LED_DEV_CAP_TORCH;
+
+ if (led_cdev->flags & LED_DEV_CAP_FLASH) {
+ if (!led_cdev->torch_brightness_set)
+ return -EINVAL;
+
+ ops = flash->ops;
+ if (!ops || !ops->strobe_set)
+ return -EINVAL;
+
+ if ((led_cdev->flags & LED_DEV_CAP_INDICATOR) &&
+ (!flash->indicator_brightness ||
+ !ops->indicator_brightness_set))
+ return -EINVAL;
+
+ led_cdev->flash_resume = led_flash_resume;
+ }
+
+ /* Register led class device */
+ ret = led_classdev_register(parent, led_cdev);
+ if (ret < 0)
+ return -EINVAL;
+
+ /* Register in the flash manager if there is related data to parse */
+ if (node) {
+ ret = led_flash_manager_register_flash(flash, node);
+ if (ret < 0)
+ goto err_flash_manager_register;
+ }
+
+ /* Create flash led specific sysfs attributes */
+ ret = led_flash_create_sysfs_groups(flash);
+ if (ret < 0)
+ goto err_create_sysfs_groups;
+
+ return 0;
+
+err_create_sysfs_groups:
+ led_flash_manager_unregister_flash(flash);
+err_flash_manager_register:
+ led_classdev_unregister(led_cdev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(led_classdev_flash_register);
+
+void led_classdev_flash_unregister(struct led_classdev_flash *flash)
+{
+ led_flash_remove_sysfs_groups(flash);
+ led_flash_manager_unregister_flash(flash);
+ 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)
+{
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ int ret = 0;
+
+ if (flash->external_strobe)
+ return -EBUSY;
+
+ /* strobe can be stopped without flash manager involvement */
+ if (!state)
+ return call_flash_op(flash, strobe_set, state);
+
+ /*
+ * Flash manager needs to be involved in setting flash
+ * strobe if there were strobe gates defined in the
+ * device tree binding. This call blocks the caller for
+ * the current flash timeout period if state == true and
+ * the flash led device depends on shared muxes. Locking is
+ * required for assuring that nobody will reconfigure muxes
+ * in the meantime.
+ */
+ if ((led_cdev->flags & LED_DEV_CAP_FL_MANAGER))
+ ret = led_flash_manager_setup_strobe(flash, false);
+ else
+ ret = call_flash_op(flash, strobe_set, true);
+
+ return ret;
+}
+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);
+
+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;
+ int ret = 0;
+
+ s->val = timeout;
+ led_clamp_align(s);
+
+ if (!(led_cdev->flags & LED_SUSPENDED))
+ ret = call_flash_op(flash, timeout_set, s->val);
+
+ return ret;
+}
+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_external_strobe(struct led_classdev_flash *flash, bool enable)
+{
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ int ret;
+
+ if (flash->has_external_strobe) {
+ /*
+ * Some flash led devices need altering their register
+ * settings to start listen to the external strobe signal.
+ */
+ if (has_flash_op(flash, external_strobe_set)) {
+ ret = call_flash_op(flash, external_strobe_set, enable);
+ if (ret < 0)
+ return ret;
+ }
+
+ flash->external_strobe = enable;
+
+ /*
+ * Flash manager needs to be involved in setting external
+ * strobe mode if there were strobe gates defined in the
+ * device tree binding. This call blocks the caller for
+ * the current flash timeout period if enable == true and
+ * the flash led device depends on shared muxes. Locking is
+ * required for assuring that nobody will reconfigure muxes
+ * while the flash device is awaiting external strobe signal.
+ */
+ if (enable && (led_cdev->flags & LED_DEV_CAP_FL_MANAGER)) {
+ ret = led_flash_manager_setup_strobe(flash, true);
+ if (ret < 0)
+ return ret;
+ }
+ } else if (enable) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(led_set_external_strobe);
+
+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;
+ int ret = 0;
+
+ s->val = brightness;
+ led_clamp_align(s);
+
+ if (!(led_cdev->flags & LED_SUSPENDED))
+ ret = call_flash_op(flash, flash_brightness_set, s->val);
+
+ return ret;
+}
+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;
+ int ret = 0;
+
+ if (has_flash_op(flash, flash_brightness_get)) {
+ ret = call_flash_op(flash, flash_brightness_get,
+ &brightness);
+ if (ret < 0)
+ return ret;
+ s->val = brightness;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(led_update_flash_brightness);
+
+int led_set_indicator_brightness(struct led_classdev_flash *flash,
+ u32 brightness)
+{
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ struct led_flash_setting *s = flash->indicator_brightness;
+ int ret = 0;
+
+ if (!s)
+ return -EINVAL;
+
+ s->val = brightness;
+ led_clamp_align(s);
+
+ if (!(led_cdev->flags & LED_SUSPENDED))
+ ret = call_flash_op(flash, indicator_brightness_set, s->val);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(led_set_indicator_brightness);
+
+int led_update_indicator_brightness(struct led_classdev_flash *flash)
+{
+ struct led_flash_setting *s = flash->indicator_brightness;
+ u32 brightness;
+ int ret = 0;
+
+ if (!s)
+ return -EINVAL;
+
+ if (has_flash_op(flash, indicator_brightness_get)) {
+ ret = call_flash_op(flash, indicator_brightness_get,
+ &brightness);
+ if (ret < 0)
+ return ret;
+ s->val = brightness;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(led_update_indicator_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 c17dda0..165a1fb 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -189,6 +189,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/drivers/leds/led-flash-gpio-mux.c b/drivers/leds/led-flash-gpio-mux.c
new file mode 100644
index 0000000..2803fcc
--- /dev/null
+++ b/drivers/leds/led-flash-gpio-mux.c
@@ -0,0 +1,102 @@
+/*
+ * LED Flash Class gpio mux
+ *
+ * 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/gpio.h>
+#include <linux/led-flash-gpio-mux.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+
+int led_flash_gpio_mux_select_line(u32 line_id, void *mux)
+{
+ struct led_flash_gpio_mux *gpio_mux = (struct led_flash_gpio_mux *) mux;
+ struct led_flash_gpio_mux_selector *sel;
+ u32 mask = 1;
+
+ /* Setup selectors */
+ list_for_each_entry(sel, &gpio_mux->selectors, list) {
+ gpio_set_value(sel->gpio, !!(line_id & mask));
+ mask <<= 1;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(led_flash_gpio_mux_select_line);
+
+/* Create standard gpio mux */
+int led_flash_gpio_mux_create(struct led_flash_gpio_mux **new_mux,
+ struct device_node *mux_node)
+{
+ struct led_flash_gpio_mux *mux;
+ struct led_flash_gpio_mux_selector *sel;
+ int gpio_num, gpio, ret, i;
+ char gpio_name[20];
+ static int cnt_gpio;
+
+ /* Get the number of mux selectors */
+ gpio_num = of_gpio_count(mux_node);
+ if (gpio_num == 0)
+ return -EINVAL;
+
+ mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&mux->selectors);
+
+ /* Request gpios for all selectors */
+ for (i = 0; i < gpio_num; ++i) {
+ gpio = of_get_gpio(mux_node, i);
+ if (gpio_is_valid(gpio)) {
+ sprintf(gpio_name, "v4l2_mux selector %d", cnt_gpio++);
+ ret = gpio_request_one(gpio, GPIOF_DIR_OUT, gpio_name);
+ if (ret < 0)
+ goto err_gpio_request;
+
+ /* Add new entry to the gpio selectors list */
+ sel = kzalloc(sizeof(*sel), GFP_KERNEL);
+ if (!sel) {
+ ret = -ENOMEM;
+ goto err_gpio_request;
+ }
+ sel->gpio = gpio;
+
+ list_add_tail(&sel->list, &mux->selectors);
+ } else {
+ ret = -EINVAL;
+ goto err_gpio_request;
+ }
+
+ }
+
+ *new_mux = mux;
+
+ return 0;
+
+err_gpio_request:
+ led_flash_gpio_mux_release(mux);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(led_flash_gpio_mux_create);
+
+void led_flash_gpio_mux_release(void *mux)
+{
+ struct led_flash_gpio_mux *gpio_mux = (struct led_flash_gpio_mux *) mux;
+ struct led_flash_gpio_mux_selector *sel, *n;
+
+ list_for_each_entry_safe(sel, n, &gpio_mux->selectors, list) {
+ if (gpio_is_valid(sel->gpio))
+ gpio_free(sel->gpio);
+ kfree(sel);
+ }
+ kfree(gpio_mux);
+}
+EXPORT_SYMBOL_GPL(led_flash_gpio_mux_release);
diff --git a/drivers/leds/led-flash-manager.c b/drivers/leds/led-flash-manager.c
new file mode 100644
index 0000000..2133277
--- /dev/null
+++ b/drivers/leds/led-flash-manager.c
@@ -0,0 +1,698 @@
+/*
+ * LED Flash Manager for maintaining LED Flash Class devices
+ * along with their corresponding muxex, faciliating dynamic
+ * reconfiguration of the strobe signal source.
+ *
+ * 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/led-class-flash.h>
+#include <linux/led-flash-gpio-mux.h>
+#include <linux/led-flash-manager.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_gpio.h>
+#include <linux/of_led_flash_manager.h>
+#include <linux/slab.h>
+
+static LIST_HEAD(flash_list);
+static LIST_HEAD(mux_bound_list);
+static LIST_HEAD(mux_waiting_list);
+static DEFINE_MUTEX(fm_lock);
+
+static ssize_t strobe_provider_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 provider_id;
+ ssize_t ret;
+
+ mutex_lock(&led_cdev->led_lock);
+
+ if (led_sysfs_is_locked(led_cdev)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ ret = kstrtoul(buf, 10, &provider_id);
+ if (ret)
+ goto unlock;
+
+ if (provider_id > flash->num_strobe_providers - 1) {
+ ret = -ERANGE;
+ goto unlock;
+ }
+
+ flash->strobe_provider_id = provider_id;
+
+ ret = size;
+unlock:
+ mutex_unlock(&led_cdev->led_lock);
+ return ret;
+}
+
+static ssize_t strobe_provider_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->strobe_provider_id);
+}
+static DEVICE_ATTR_RW(strobe_provider);
+
+static ssize_t available_strobe_providers_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_flash_strobe_provider *provider =
+ container_of(attr, struct led_flash_strobe_provider, attr);
+ const char *no_name = "undefined";
+ const char *provider_name;
+
+ provider_name = provider->name ? provider->name :
+ no_name;
+
+ return sprintf(buf, "%s\n", provider_name);
+}
+
+static ssize_t blocking_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->num_shared_muxes);
+}
+static DEVICE_ATTR_RO(blocking_strobe);
+
+static int led_flash_manager_create_providers_attrs(
+ struct led_classdev_flash *flash)
+{
+ struct led_flash_strobe_provider *provider;
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ int cnt_attr = 0;
+ int ret;
+
+ list_for_each_entry(provider, &flash->strobe_providers, list) {
+ provider->attr.show = available_strobe_providers_show;
+ provider->attr.attr.mode = S_IRUGO;
+
+ sprintf(provider->attr_name, "strobe_provider%d",
+ cnt_attr++);
+ provider->attr.attr.name = provider->attr_name;
+
+ sysfs_attr_init(&provider->attr.attr);
+
+ ret = sysfs_create_file(&led_cdev->dev->kobj,
+ &provider->attr.attr);
+ if (ret < 0)
+ goto error_create_attr;
+
+ provider->attr_registered = true;
+ }
+
+ /*
+ * strobe_provider attribute is required only if there have been more
+ * than one strobe source defined for the LED Flash Class device.
+ */
+ if (cnt_attr > 1) {
+ ret = sysfs_create_file(&led_cdev->dev->kobj,
+ &dev_attr_strobe_provider.attr);
+ if (ret < 0)
+ goto error_create_attr;
+ }
+
+ return 0;
+
+error_create_attr:
+ list_for_each_entry(provider, &flash->strobe_providers, list) {
+ if (!provider->attr_registered)
+ break;
+ sysfs_remove_file(&led_cdev->dev->kobj, &provider->attr.attr);
+ }
+
+ return ret;
+}
+
+static void led_flash_manager_remove_providers_attrs(
+ struct led_classdev_flash *flash)
+{
+ struct led_flash_strobe_provider *provider;
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ int cnt_attr = 0;
+
+ list_for_each_entry(provider, &flash->strobe_providers, list) {
+ if (!provider->attr_registered)
+ break;
+ sysfs_remove_file(&led_cdev->dev->kobj, &provider->attr.attr);
+ provider->attr_registered = false;
+ ++cnt_attr;
+ }
+
+ /*
+ * If there was more than one strobe_providerN attr to remove
+ * than there is also strobe_provider attr to remove.
+ */
+ if (cnt_attr > 1)
+ sysfs_remove_file(&led_cdev->dev->kobj,
+ &dev_attr_strobe_provider.attr);
+}
+
+/* Return mux associated with gate */
+static struct led_flash_mux *led_flash_manager_get_mux_by_gate(
+ struct led_flash_strobe_gate *gate,
+ struct list_head *mux_list)
+{
+ struct led_flash_mux *mux;
+
+ list_for_each_entry(mux, mux_list, list)
+ if (mux->node == gate->mux_node)
+ return mux;
+
+ return NULL;
+}
+
+/* Setup all muxes in the gate list */
+static int led_flash_manager_setup_muxes(struct led_classdev_flash *flash,
+ struct list_head *gate_list)
+{
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ struct led_flash_strobe_gate *gate;
+ struct led_flash_mux *mux;
+ struct device *dev = led_cdev->dev->parent;
+ int ret = 0;
+
+ list_for_each_entry(gate, gate_list, list) {
+ mux = led_flash_manager_get_mux_by_gate(gate, &mux_bound_list);
+ if (!mux) {
+ dev_err(dev, "Flash mux not bound (%s)\n",
+ gate->mux_node->name);
+ return -ENODEV;
+ }
+
+ ret = mux->ops->select_line(gate->line_id,
+ mux->private_data);
+ }
+
+ return ret;
+}
+
+/*
+ * Setup all muxes required to open the route
+ * to the external strobe signal provider
+ */
+static int led_flash_manager_select_strobe_provider(
+ struct led_classdev_flash *flash,
+ int provider_id)
+{
+ struct led_flash_strobe_provider *provider;
+ int ret, provider_cnt = 0;
+
+ list_for_each_entry(provider, &flash->strobe_providers, list)
+ if (provider_cnt++ == provider_id) {
+ ret = led_flash_manager_setup_muxes(flash,
+ &provider->strobe_gates);
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Setup all muxes required to open the route
+ * either to software or external strobe source.
+ */
+static int led_flash_manager_set_external_strobe(
+ struct led_classdev_flash *flash,
+ bool external)
+{
+ int ret;
+
+ if (external)
+ ret = led_flash_manager_select_strobe_provider(flash,
+ flash->strobe_provider_id);
+ else
+ ret = led_flash_manager_setup_muxes(flash,
+ &flash->software_strobe_gates);
+
+ return ret;
+}
+
+/* Notify flash manager that async mux is available. */
+int led_flash_manager_bind_async_mux(struct led_flash_mux *async_mux)
+{
+ struct led_flash_mux *mux;
+ bool mux_found = false;
+ int ret = 0;
+
+ if (!async_mux)
+ return -EINVAL;
+
+ mutex_lock(&fm_lock);
+
+ /*
+ * Check whether the LED Flash Class device using this
+ * mux has been already registered in the flash manager.
+ */
+ list_for_each_entry(mux, &mux_waiting_list, list)
+ if (async_mux->node == mux->node) {
+ /* Move the mux to the bound muxes list */
+
+ list_move(&mux->list, &mux_bound_list);
+ mux_found = true;
+
+ if (!try_module_get(async_mux->owner)) {
+ ret = -ENODEV;
+ goto unlock;
+ }
+
+ break;
+ }
+
+ if (!mux_found) {
+ /* This is a new mux - create its representation */
+ mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+ if (!mux) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ INIT_LIST_HEAD(&mux->refs);
+
+ /* Add the mux to the bound list. */
+ list_add(&mux->list, &mux_bound_list);
+ }
+
+ mux->ops = async_mux->ops;
+ mux->node = async_mux->node;
+ mux->owner = async_mux->owner;
+
+unlock:
+ mutex_unlock(&fm_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(led_flash_manager_bind_async_mux);
+
+int led_flash_manager_unbind_async_mux(struct device_node *mux_node)
+{
+ struct led_flash_mux *mux;
+ int ret = -ENODEV;
+
+ mutex_lock(&fm_lock);
+
+ /*
+ * Mux can be unbound only when is not used by any
+ * flash led device, otherwise this is erroneous call.
+ */
+ list_for_each_entry(mux, &mux_waiting_list, list)
+ if (mux->node == mux_node) {
+ list_move(&mux->list, &mux_waiting_list);
+ ret = 0;
+ break;
+ }
+
+ mutex_unlock(&fm_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(led_flash_manager_unbind_async_mux);
+
+struct led_flash_mux_ops gpio_mux_ops = {
+ .select_line = led_flash_gpio_mux_select_line,
+ .release_private_data = led_flash_gpio_mux_release,
+};
+
+static int __add_mux_ref(struct led_flash_mux_ref *ref,
+ struct led_flash_mux *mux)
+{
+ struct led_flash_mux_ref *r;
+ int ret;
+
+ /*
+ * A flash can be associated with a mux through
+ * more than one gate - increment mux reference
+ * count in such a case.
+ */
+ list_for_each_entry(r, &mux->refs, list)
+ if (r->flash == ref->flash)
+ return 0;
+
+ /* protect async mux against rmmod */
+ if (mux->owner) {
+ ret = try_module_get(mux->owner);
+ if (ret < 0)
+ return ret;
+ }
+
+ list_add(&ref->list, &mux->refs);
+ ++mux->num_refs;
+
+ if (mux->num_refs == 2) {
+ list_for_each_entry(r, &mux->refs, list) {
+ ++r->flash->num_shared_muxes;
+ }
+ return 0;
+ }
+
+ if (mux->num_refs > 2)
+ ++ref->flash->num_shared_muxes;
+
+ return 0;
+}
+
+static void __remove_mux_ref(struct led_flash_mux_ref *ref,
+ struct led_flash_mux *mux)
+{
+ struct led_flash_mux_ref *r;
+
+ /* decrement async mux refcount */
+ if (mux->owner)
+ module_put(mux->owner);
+
+ list_del(&ref->list);
+ --mux->num_refs;
+
+ if (mux->num_refs == 1) {
+ r = list_first_entry(&mux->refs, struct led_flash_mux_ref,
+ list);
+ --r->flash->num_shared_muxes;
+ return;
+ }
+
+ if (mux->num_refs > 1)
+ --ref->flash->num_shared_muxes;
+}
+
+/*
+ * Parse mux node and add the mux it refers to either to waiting
+ * or bound list depending on the mux type (gpio or asynchronous).
+ */
+static int led_flash_manager_parse_mux_node(struct led_classdev_flash *flash,
+ struct led_flash_strobe_gate *gate)
+{
+ struct led_flash_mux *mux;
+ struct device_node *async_mux_node;
+ struct led_flash_gpio_mux *gpio_mux;
+ struct led_flash_mux_ref *ref;
+ int ret = -EINVAL;
+
+ /* Create flash ref to be added to the mux references list */
+ ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+ if (!ref)
+ return -ENOMEM;
+ ref->flash = flash;
+
+ /* if this is async mux update gate's mux_node accordingly */
+ async_mux_node = of_parse_phandle(gate->mux_node, "mux-async", 0);
+ if (async_mux_node)
+ gate->mux_node = async_mux_node;
+
+ /* Check if the mux isn't already on waiting list */
+ list_for_each_entry(mux, &mux_waiting_list, list)
+ if (mux->node == gate->mux_node)
+ return __add_mux_ref(ref, mux);
+
+ /* Check if the mux isn't already on bound list */
+ list_for_each_entry(mux, &mux_bound_list, list)
+ if (mux->node == gate->mux_node)
+ return __add_mux_ref(ref, mux);
+
+ /* This is a new mux - create its representation */
+ mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return -ENOMEM;
+
+ mux->node = gate->mux_node;
+
+ INIT_LIST_HEAD(&mux->refs);
+
+ /* Increment reference count */
+ ret = __add_mux_ref(ref, mux);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Check if this mux has its own driver
+ * or this is standard gpio mux.
+ */
+ if (async_mux_node) {
+ /* Add async mux to the waiting list */
+ list_add(&mux->list, &mux_waiting_list);
+ } else {
+ /* Create default gpio mux */
+ ret = led_flash_gpio_mux_create(&gpio_mux, gate->mux_node);
+ if (ret < 0)
+ goto err_gpio_mux_init;
+
+ /* Register gpio mux device */
+ mux->private_data = gpio_mux;
+ mux->ops = &gpio_mux_ops;
+ list_add(&mux->list, &mux_bound_list);
+ }
+
+ return 0;
+
+err_gpio_mux_init:
+ kfree(mux);
+ kfree(ref);
+
+ return ret;
+}
+
+static void led_flash_manager_release_mux(struct led_flash_mux *mux)
+{
+ if (mux->ops->release_private_data) {
+ if (mux->ops->release_private_data)
+ mux->ops->release_private_data(mux->private_data);
+ list_del(&mux->list);
+ kfree(mux);
+ }
+}
+
+static void led_flash_manager_remove_flash_refs(
+ struct led_classdev_flash *flash)
+{
+ struct led_flash_mux_ref *ref, *rn;
+ struct led_flash_mux *mux, *mn;
+
+ /*
+ * Remove references to the flash from
+ * all the muxes on the list.
+ */
+ list_for_each_entry_safe(mux, mn, &mux_bound_list, list)
+ /* Seek for matching flash ref */
+ list_for_each_entry_safe(ref, rn, &mux->refs, list)
+ if (ref->flash == flash) {
+ /* Decrement reference count */
+ __remove_mux_ref(ref, mux);
+ kfree(ref);
+ /*
+ * Release mux if there are no more
+ * references pointing to it.
+ */
+ if (list_empty(&mux->refs))
+ led_flash_manager_release_mux(mux);
+ }
+}
+
+int led_flash_manager_setup_strobe(struct led_classdev_flash *flash,
+ bool external)
+{
+ u32 flash_timeout = flash->timeout.val / 1000;
+ int ret;
+
+ mutex_lock(&fm_lock);
+
+ /* Setup muxes */
+ ret = led_flash_manager_set_external_strobe(flash, external);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * Trigger software strobe under flash manager lock
+ * to make sure that nobody will reconfigure muxes
+ * in the meantime.
+ */
+ if (!external) {
+ ret = flash->ops->strobe_set(flash, true);
+ if (ret < 0)
+ goto unlock;
+ }
+
+ /*
+ * Hold lock for the time of strobing if the
+ * LED Flash Class device depends on shared muxes.
+ */
+ if (flash->num_shared_muxes > 0) {
+ msleep(flash_timeout);
+ /*
+ * external strobe is turned on only for the time of
+ * this call if there are shared muxes involved
+ * in strobe signal routing.
+ */
+ flash->external_strobe = false;
+ }
+
+unlock:
+ mutex_unlock(&fm_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(led_flash_manager_setup_strobe);
+
+int led_flash_manager_create_sysfs_attrs(struct led_classdev_flash *flash)
+{
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ int ret;
+
+ /*
+ * Create sysfs attribtue for indicating if activation of
+ * software or external flash strobe will block the caller.
+ */
+ sysfs_attr_init(&dev_attr_blocking_strobe.attr);
+ ret = sysfs_create_file(&led_cdev->dev->kobj,
+ &dev_attr_blocking_strobe.attr);
+ if (ret < 0)
+ return ret;
+
+ /* Create strobe_providerN attributes */
+ ret = led_flash_manager_create_providers_attrs(flash);
+ if (ret < 0)
+ goto err_create_provider_attrs;
+
+ return 0;
+
+err_create_provider_attrs:
+ sysfs_remove_file(&led_cdev->dev->kobj,
+ &dev_attr_blocking_strobe.attr);
+
+ return ret;
+}
+
+void led_flash_manager_remove_sysfs_attrs(struct led_classdev_flash *flash)
+{
+ struct led_classdev *led_cdev = &flash->led_cdev;
+
+ led_flash_manager_remove_providers_attrs(flash);
+ sysfs_remove_file(&led_cdev->dev->kobj,
+ &dev_attr_blocking_strobe.attr);
+}
+
+int led_flash_manager_register_flash(struct led_classdev_flash *flash,
+ struct device_node *node)
+{
+ struct led_flash_strobe_provider *provider;
+ struct led_flash_strobe_gate *gate;
+ struct led_classdev_flash *fl;
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ int ret = 0;
+
+ if (!flash || !node)
+ return -EINVAL;
+
+ mutex_lock(&fm_lock);
+
+ /* Don't allow to register the same flash more than once */
+ list_for_each_entry(fl, &flash_list, list)
+ if (fl == flash)
+ goto unlock;
+
+ INIT_LIST_HEAD(&flash->software_strobe_gates);
+ INIT_LIST_HEAD(&flash->strobe_providers);
+
+ ret = of_led_flash_manager_parse_dt(flash, node);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * Register mux devices declared by the flash device
+ * if they have not been yet known to the flash manager.
+ */
+ list_for_each_entry(gate, &flash->software_strobe_gates, list) {
+ ret = led_flash_manager_parse_mux_node(flash, gate);
+ if (ret < 0)
+ goto err_parse_mux_node;
+ }
+
+ list_for_each_entry(provider, &flash->strobe_providers, list) {
+ list_for_each_entry(gate, &provider->strobe_gates, list) {
+ ret = led_flash_manager_parse_mux_node(flash, gate);
+ if (ret < 0)
+ goto err_parse_mux_node;
+ }
+ ++flash->num_strobe_providers;
+ }
+
+ /*
+ * It doesn't make sense to register in the flash manager
+ * if there are no strobe providers defined.
+ */
+ if (flash->num_strobe_providers == 0)
+ goto unlock;
+
+ led_cdev->flags |= LED_DEV_CAP_FL_MANAGER;
+ flash->has_external_strobe = true;
+ flash->strobe_provider_id = 0;
+
+ ret = led_flash_manager_create_sysfs_attrs(flash);
+ if (ret < 0)
+ goto err_parse_mux_node;
+
+ /* Add flash to the list of flashes maintained by the flash manager. */
+ list_add_tail(&flash->list, &flash_list);
+
+ mutex_unlock(&fm_lock);
+
+ return ret;
+
+err_parse_mux_node:
+ led_flash_manager_remove_flash_refs(flash);
+ of_led_flash_manager_release_dt_data(flash);
+unlock:
+ mutex_unlock(&fm_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(led_flash_manager_register_flash);
+
+void led_flash_manager_unregister_flash(struct led_classdev_flash *flash)
+{
+ struct led_classdev_flash *fl, *n;
+ bool found_flash = false;
+
+ mutex_lock(&fm_lock);
+
+ /* Remove flash from the list, if found */
+ list_for_each_entry_safe(fl, n, &flash_list, list)
+ if (fl == flash) {
+ found_flash = true;
+ break;
+ }
+
+ if (!found_flash)
+ goto unlock;
+
+ list_del(&fl->list);
+
+ led_flash_manager_remove_sysfs_attrs(flash);
+
+ /* Remove all references to the flash */
+ led_flash_manager_remove_flash_refs(flash);
+ of_led_flash_manager_release_dt_data(flash);
+
+unlock:
+ mutex_unlock(&fm_lock);
+}
+EXPORT_SYMBOL_GPL(led_flash_manager_unregister_flash);
+
+MODULE_AUTHOR("Jacek Anaszewski <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LED Flash Manager");
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index 0545530..e8380bb 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -41,6 +41,11 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,

mutex_lock(&led_cdev->led_lock);

+ if (led_sysfs_is_locked(led_cdev)) {
+ ret = -EBUSY;
+ goto exit_unlock;
+ }
+
trigger_name[sizeof(trigger_name) - 1] = '\0';
strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
len = strlen(trigger_name);
diff --git a/drivers/leds/of_led_flash_manager.c b/drivers/leds/of_led_flash_manager.c
new file mode 100644
index 0000000..d22d853
--- /dev/null
+++ b/drivers/leds/of_led_flash_manager.c
@@ -0,0 +1,155 @@
+/*
+ * LED flash manager of 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/device.h>
+#include <linux/led-class-flash.h>
+#include <linux/of.h>
+#include <linux/of_led_flash_manager.h>
+#include <linux/slab.h>
+
+static int __parse_strobe_gate_node(struct led_classdev_flash *flash,
+ struct device_node *node,
+ struct list_head *gates)
+{
+ struct device_node *mux_node, *subnode;
+ struct led_flash_strobe_gate *gate;
+ struct led_classdev *led_cdev = &flash->led_cdev;
+ u32 line_id;
+ int ret, num_gates = 0;
+
+ /* Get node mux node */
+ mux_node = of_parse_phandle(node, "mux", 0);
+ if (!mux_node)
+ return -EINVAL;
+
+ /* Get the value the mux has to be written to open the gate */
+ ret = of_property_read_u32(node, "mux-line-id", &line_id);
+ if (ret < 0)
+ return ret;
+
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return -ENOMEM;
+
+ gate->mux_node = mux_node;
+ gate->line_id = line_id;
+ list_add_tail(&gate->list, gates);
+
+ /* Parse nested gate nodes */
+ for_each_available_child_of_node(node, subnode) {
+ if (!of_node_ncmp(subnode->name, "gate", 4)) {
+ ret = __parse_strobe_gate_node(flash, subnode, gates);
+ if (ret < 0) {
+ dev_dbg(led_cdev->dev->parent,
+ "Failed to parse gate node (%d)\n",
+ ret);
+ goto err_parse_gate;
+ }
+
+ if (++num_gates > 1) {
+ dev_dbg(led_cdev->dev->parent,
+ "Only one available child gate is allowed.\n");
+ ret = -EINVAL;
+ goto err_parse_gate;
+ }
+ }
+ }
+
+ return 0;
+
+err_parse_gate:
+ kfree(gate);
+
+ return ret;
+}
+
+static int __parse_strobe_provider_node(struct led_classdev_flash *flash,
+ struct device_node *node)
+{
+ struct device_node *provider_node;
+ struct led_flash_strobe_provider *provider;
+ int ret;
+
+ /* Create strobe provider representation */
+ provider = kzalloc(sizeof(*provider), GFP_KERNEL);
+ if (!provider)
+ return -ENOMEM;
+
+ /* Get phandle of the device generating strobe source signal */
+ provider_node = of_parse_phandle(node, "strobe-provider", 0);
+
+ /* provider property may be absent */
+ if (provider_node) {
+ /* Use compatible property as a strobe provider name */
+ ret = of_property_read_string(provider_node, "compatible",
+ (const char **) &provider->name);
+ if (ret < 0)
+ goto err_read_name;
+ }
+
+ INIT_LIST_HEAD(&provider->strobe_gates);
+
+ list_add_tail(&provider->list, &flash->strobe_providers);
+
+ ret = __parse_strobe_gate_node(flash, node, &provider->strobe_gates);
+ if (ret < 0)
+ goto err_read_name;
+
+ return 0;
+
+err_read_name:
+ kfree(provider);
+
+ return ret;
+}
+
+int of_led_flash_manager_parse_dt(struct led_classdev_flash *flash,
+ struct device_node *node)
+{
+ struct device_node *subnode;
+ int ret;
+
+ for_each_available_child_of_node(node, subnode) {
+ if (!of_node_cmp(subnode->name, "gate-software-strobe")) {
+ ret = __parse_strobe_gate_node(flash, subnode,
+ &flash->software_strobe_gates);
+ if (ret < 0)
+ return ret;
+ }
+ if (!of_node_ncmp(subnode->name, "gate-external-strobe", 20)) {
+ ret = __parse_strobe_provider_node(flash, subnode);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_led_flash_manager_parse_dt);
+
+void of_led_flash_manager_release_dt_data(struct led_classdev_flash *flash)
+{
+ struct list_head *gate_list;
+ struct led_flash_strobe_gate *gate, *gn;
+ struct led_flash_strobe_provider *provider, *sn;
+
+ gate_list = &flash->software_strobe_gates;
+ list_for_each_entry_safe(gate, gn, gate_list, list)
+ kfree(gate);
+
+ list_for_each_entry_safe(provider, sn, &flash->strobe_providers, list) {
+ list_for_each_entry_safe(gate, gn, &provider->strobe_gates,
+ list)
+ kfree(gate);
+ kfree(provider);
+ }
+}
+EXPORT_SYMBOL_GPL(of_led_flash_manager_release_dt_data);
diff --git a/include/linux/led-class-flash.h b/include/linux/led-class-flash.h
new file mode 100644
index 0000000..6ed7e8a
--- /dev/null
+++ b/include/linux/led-class-flash.h
@@ -0,0 +1,290 @@
+/*
+ * 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>
+
+struct v4l2_flash_ops;
+struct led_classdev_flash;
+struct device_node;
+
+/*
+ * 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
+
+#define LED_FLASH_MAX_SYSFS_GROUPS 6
+
+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 indicator brightness */
+ int (*indicator_brightness_set)(struct led_classdev_flash *flash,
+ u32 brightness);
+ /* get flash indicator brightness */
+ int (*indicator_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);
+ /* setup the device to strobe the flash upon a pin state assertion */
+ int (*external_strobe_set)(struct led_classdev_flash *flash,
+ bool enable);
+ /* 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 indicator_brightness;
+ struct led_flash_setting flash_timeout;
+ u32 flash_faults;
+};
+
+struct led_classdev_flash {
+ /* led-flash-manager uses it to link flashes */
+ struct list_head list;
+ /* led class device */
+ struct led_classdev led_cdev;
+ /* flash led specific ops */
+ const struct led_flash_ops *ops;
+
+ /* flash sysfs groups */
+ struct attribute_group *sysfs_groups[LED_FLASH_MAX_SYSFS_GROUPS];
+
+ /* flash brightness value in microamperes along with its constraints */
+ struct led_flash_setting brightness;
+
+ /* timeout value in microseconds along with its constraints */
+ struct led_flash_setting timeout;
+
+ /*
+ * Indicator brightness value in microamperes along with
+ * its constraints - this is an optional setting and must
+ * be allocated by the driver if the device supports privacy
+ * indicator led.
+ */
+ struct led_flash_setting *indicator_brightness;
+
+ /*
+ * determines if a device supports external
+ * flash strobe sources
+ */
+ bool has_external_strobe;
+
+ /* If true the flash led is strobed from external source */
+ bool external_strobe;
+
+ /* Flash manager data */
+ /* Strobe signals topology data */
+ struct list_head software_strobe_gates;
+ struct list_head strobe_providers;
+
+ /* identifier of the selected strobe signal provider */
+ int strobe_provider_id;
+
+ /* number of defined strobe providers */
+ int num_strobe_providers;
+
+ /*
+ * number of muxes that this device shares
+ * with other LED Flash Class devices.
+ */
+ int num_shared_muxes;
+};
+
+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_flash class
+ with support for flash LEDs
+ * @parent: the device to register
+ * @flash: the led_classdev_flash structure for this device
+ * @node: device tree node of the LED Flash Class device - it must be
+ initialized if the device is to be registered in the flash manager
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+int led_classdev_flash_register(struct device *parent,
+ struct led_classdev_flash *flash,
+ struct device_node *node);
+
+/**
+ * led_classdev_flash_unregister - unregisters an object of led_classdev_flash class
+ with support for flash LEDs
+ * @flash: the flash led device to unregister
+ *
+ * Unregisters 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
+ *
+ * Setup flash strobe - trigger flash strobe
+ *
+ * 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 LED to query
+ * @state: 1 - flash is strobing, 0 - flash is off
+ *
+ * Check whether the flash is strobing at the moment or not.
+ *
+u* 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 LED to set
+ * @brightness: the brightness to set it to
+ *
+ * Returns: 0 on success or negative error value on failure
+ *
+ * Set a flash LED's brightness.
+ */
+extern int led_set_flash_brightness(struct led_classdev_flash *flash,
+ u32 brightness);
+
+/**
+ * led_update_flash_brightness - update flash LED brightness
+ * @flash: the 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 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 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);
+
+/**
+ * led_set_external_strobe - set the flash LED external_strobe mode
+ * @flash: the LED to set
+ * @enable: the state to set it to
+ *
+ * Enable/disable strobing the flash LED with use of external source
+ *
+ Returns: 0 on success or negative error value on failure
+ */
+extern int led_set_external_strobe(struct led_classdev_flash *flash,
+ bool enable);
+
+/**
+ * led_set_indicator_brightness - set indicator LED brightness
+ * @flash: the LED to set
+ * @brightness: the brightness to set it to
+ *
+ * Set an indicator LED's brightness.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+extern int led_set_indicator_brightness(struct led_classdev_flash *flash,
+ u32 led_brightness);
+
+/**
+ * led_update_indicator_brightness - update flash indicator LED brightness
+ * @flash: the LED to query
+ *
+ * Get a flash indicator LED's current brightness and update
+ * led_flash->indicator_brightness member with the obtained value.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+extern int led_update_indicator_brightness(struct led_classdev_flash *flash);
+
+#ifdef CONFIG_V4L2_FLASH_LED_CLASS
+/**
+ * led_get_v4l2_flash_ops - get ops for controlling LED Flash Class
+ device with use of V4L2 Flash controls
+ * Returns: v4l2_flash_ops
+ */
+const struct v4l2_flash_ops *led_get_v4l2_flash_ops(void);
+#else
+#define led_get_v4l2_flash_ops() (0)
+#endif
+
+#endif /* __LINUX_FLASH_LEDS_H_INCLUDED */
diff --git a/include/linux/led-flash-gpio-mux.h b/include/linux/led-flash-gpio-mux.h
new file mode 100644
index 0000000..c56d38f
--- /dev/null
+++ b/include/linux/led-flash-gpio-mux.h
@@ -0,0 +1,68 @@
+/*
+ * LED Flash Class gpio mux
+ *
+ * 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 _LED_FLASH_GPIO_MUX_H
+#define _LED_FLASH_GPIO_MUX_H
+
+struct device_node;
+
+struct list_head;
+
+/**
+ * struct led_flash_gpio_mux_selector - element of gpio mux selectors list
+ * @list: links mux selectors
+ * @gpio: mux selector gpio
+ */
+struct led_flash_gpio_mux_selector {
+ struct list_head list;
+ int gpio;
+};
+
+/**
+ * struct led_flash_gpio_mux - gpio mux
+ * @selectors: mux selectors
+ */
+struct led_flash_gpio_mux {
+ struct list_head selectors;
+};
+
+/**
+ * led_flash_gpio_mux_create - create gpio mux
+ * @new_mux: created mux
+ * @mux_node: device tree node with gpio definitions
+ *
+ * Create V4L2 subdev wrapping given LED subsystem device.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+int led_flash_gpio_mux_create(struct led_flash_gpio_mux **new_mux,
+ struct device_node *mux_node);
+
+/**
+ * led_flash_gpio_mux_release - release gpio mux
+ * @gpio_mux: mux to be released
+ *
+ * Create V4L2 subdev wrapping given LED subsystem device.
+ */
+void led_flash_gpio_mux_release(void *gpio_mux);
+
+/**
+ * led_flash_gpio_mux_select_line - select mux line
+ * @line_id: id of the line to be selected
+ * @mux: mux to be set
+ *
+ * Create V4L2 subdev wrapping given LED subsystem device.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+int led_flash_gpio_mux_select_line(u32 line_id, void *mux);
+
+#endif /* _LED_FLASH_GPIO_MUX_H */
diff --git a/include/linux/led-flash-manager.h b/include/linux/led-flash-manager.h
new file mode 100644
index 0000000..174eedb
--- /dev/null
+++ b/include/linux/led-flash-manager.h
@@ -0,0 +1,121 @@
+/*
+ * LED Flash Manager for maintaining LED Flash Class devices
+ * along with their corresponding muxes that faciliate dynamic
+ * reconfiguration of the strobe signal source.
+ *
+ * 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 _LED_FLASH_MANAGER_H
+#define _LED_FLASH_MANAGER_H
+
+#include <linux/types.h>
+
+struct list_head;
+
+struct led_flash_mux_ops {
+ /* select mux line */
+ int (*select_line)(u32 line_id, void *mux);
+ /*
+ * release private mux data - it is intended for gpio muxes
+ * only and mustn't be initialized by asynchronous muxes.
+ */
+ void (*release_private_data)(void *mux);
+};
+
+/**
+ * struct led_flash_mux_ref - flash mux reference
+ * @list: links flashes pointing to a mux
+ * @flash: flash holding a mux reference
+ */
+struct led_flash_mux_ref {
+ struct list_head list;
+ struct led_classdev_flash *flash;
+};
+
+/**
+ * struct led_flash_mux - flash mux used for routing strobe signal
+ * @list: list of muxes declared by registered flashes
+ * @node: mux Device Tree node
+ * @private_data: mux internal data
+ * @ops: ops facilitating mux state manipulation
+ * @refs: list of flashes using the mux
+ * @num_refs: number of flashes using the mux
+ * @owner: module owning an async mux driver
+ */
+struct led_flash_mux {
+ struct list_head list;
+ struct device_node *node;
+ void *private_data;
+ struct led_flash_mux_ops *ops;
+ struct list_head refs;
+ int num_refs;
+ struct module *owner;
+};
+
+/**
+ * led_flash_manager_setup_strobe - setup flash strobe
+ * @flash: the LED Flash Class device to strobe
+ * @external: true - setup external strobe,
+ * false - setup software strobe
+ *
+ * Configure muxes to route relevant strobe signals to the
+ * flash led device and strobe the flash if software strobe
+ * is to be activated. If the flash device depends on shared
+ * muxes the caller is blocked for the flash_timeout period.
+ *
+ * Returns: 0 on success or negative error value on failure.
+ */
+int led_flash_manager_setup_strobe(struct led_classdev_flash *flash,
+ bool external);
+
+/**
+ * led_flash_manager_bind_async_mux - bind asynchronous mulitplexer
+ * @async_mux: mux registration data
+ *
+ * Notify the flash manager that an asychronous mux has been probed.
+ *
+ * Returns: 0 on success or negative error value on failure.
+ */
+int led_flash_manager_bind_async_mux(struct led_flash_mux *async_mux);
+
+/**
+ * led_flash_manager_unbind_async_mux - unbind asynchronous mulitplexer
+ * @mux_node: device node of the mux to be unbound
+ *
+ * Notify the flash manager that an asychronous mux has been removed.
+ *
+ * Returns: 0 on success or negative error value on failure.
+ */
+int led_flash_manager_unbind_async_mux(struct device_node *mux_node);
+
+/**
+ * led_flash_manager_register_flash - register LED Flash Class device
+ in the flash manager
+ * @flash: the LED Flash Class device to be registered
+ * @node: Device Tree node - it is expected to contain information
+ * about strobe signal topology
+ *
+ * Register LED Flash Class device and retrieve information
+ * about related strobe signals topology.
+ *
+ * Returns: 0 on success or negative error value on failure.
+ */
+int led_flash_manager_register_flash(struct led_classdev_flash *flash,
+ struct device_node *node);
+
+/**
+ * led_flash_manager_unregister_flash - unregister LED Flash Class device
+ * from the flash manager
+ * @flash: the LED Flash Class device to be unregistered
+ *
+ * Unregister LED Flash Class device from the flash manager.
+ */
+void led_flash_manager_unregister_flash(struct led_classdev_flash *flash);
+
+#endif /* _LED_FLASH_MANAGER_H */
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 9bea9e6..1f12d36 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -45,6 +45,9 @@ struct led_classdev {
#define LED_BLINK_INVERT (1 << 19)
#define LED_SYSFS_LOCK (1 << 20)
#define LED_DEV_CAP_TORCH (1 << 21)
+#define LED_DEV_CAP_FLASH (1 << 22)
+#define LED_DEV_CAP_INDICATOR (1 << 23)
+#define LED_DEV_CAP_FL_MANAGER (1 << 24)

/* Set LED brightness level */
/* Must not sleep, use a workqueue if needed */
@@ -83,6 +86,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;
diff --git a/include/linux/of_led_flash_manager.h b/include/linux/of_led_flash_manager.h
new file mode 100644
index 0000000..d27cb46
--- /dev/null
+++ b/include/linux/of_led_flash_manager.h
@@ -0,0 +1,80 @@
+/*
+ * LED flash manager of 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 _OF_LED_FLASH_MANAGER_H
+#define _OF_LED_FLASH_MANAGER_H
+
+#include <linux/sysfs.h>
+#include <linux/device.h>
+
+struct led_classdev_flash;
+struct device_node;
+struct list_head;
+
+#define MAX_ATTR_NAME 18 /* allows for strobe providers ids up to 999 */
+
+/**
+ * struct led_flash_strobe_gate - strobe signal gate
+ * @list: links gates
+ * @mux_node: gate's parent mux
+ * @line_id: id of a mux line that the gate represents
+ */
+struct led_flash_strobe_gate {
+ struct list_head list;
+ struct device_node *mux_node;
+ u32 line_id;
+};
+
+/**
+ * struct led_flash_strobe_provider - external strobe signal provider that
+ may be associated with the flash device
+ * @list: links strobe providers
+ * @strobe_gates: list of gates that route strobe signal
+ from the strobe provider to the flash device
+ * @node: device node of the strobe provider device
+ */
+struct led_flash_strobe_provider {
+ struct list_head list;
+ struct list_head strobe_gates;
+ const char *name;
+ struct device_attribute attr;
+ char attr_name[MAX_ATTR_NAME];
+ bool attr_registered;
+};
+
+/**
+ * of_led_flash_manager_parse_dt - parse flash manager data of
+ * a LED Flash Class device DT node
+ *
+ * @flash: the LED Flash Class device to store the parsed data in
+ * @node: device node to parse
+ *
+ * Parse the multiplexers' settings that need to be applied for
+ * opening a route to all the flash strobe signals available for
+ * the device.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+int of_led_flash_manager_parse_dt(struct led_classdev_flash *flash,
+ struct device_node *node);
+
+
+/**
+ * of_led_flash_manager_release_dt_data - release parsed DT data
+ *
+ * @flash: the LED Flash Class device to release data from
+ *
+ * Release data structures containing information about strobe
+ * source routes available for the device.
+ */
+void of_led_flash_manager_release_dt_data(struct led_classdev_flash *flash);
+
+#endif /* _OF_LED_FLASH_MANAGER_H */
--
1.7.9.5

2014-07-11 14:09:36

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 10/21] Documentation: leds: add exemplary asynchronous mux driver

Exemplary driver showing usage of the Flash Manager API
for registering/unregistering asynchronous multiplexers

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-async-mux.c | 65 +++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
create mode 100644 Documentation/leds/leds-async-mux.c

diff --git a/Documentation/leds/leds-async-mux.c b/Documentation/leds/leds-async-mux.c
new file mode 100644
index 0000000..ee35d2f
--- /dev/null
+++ b/Documentation/leds/leds-async-mux.c
@@ -0,0 +1,65 @@
+/*
+ * Exemplary driver showing usage of the Flash Manager API
+ * for registering/unregistering asynchronous multiplexers.
+ *
+ * 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/platform_device.h>
+#include <linux/module.h>
+#include <linux/led-class-flash.h>
+#include <linux/led-flash-manager.h>
+#include <linux/leds.h>
+#include <linux/of.h>
+
+static int led_async_mux_select_line(u32 line_id, void *mux)
+{
+ pr_info("led_async_mux_select_line line_id: %d\n", line_id);
+ return 0;
+}
+
+struct led_flash_mux_ops mux_ops = {
+ .select_line = led_async_mux_select_line,
+};
+
+static int led_async_mux_probe(struct platform_device *pdev)
+{
+ struct led_flash_mux mux;
+
+ mux.ops = &mux_ops;
+ mux.owner = THIS_MODULE;
+ mux.node = pdev->dev->of_node;
+
+ return led_flash_manager_bind_async_mux(&mux);
+}
+
+static int led_async_mux_remove(struct platform_device *pdev)
+{
+ return led_flash_manager_unbind_async_mux(pdev->dev->of_node);
+}
+
+static struct of_device_id led_async_mux_dt_match[] = {
+ {.compatible = "led-async-mux"},
+ {},
+};
+
+static struct platform_driver led_async_mux_driver = {
+ .probe = led_async_mux_probe,
+ .remove = led_async_mux_remove,
+ .driver = {
+ .name = "led-async-mux",
+ .owner = THIS_MODULE,
+ .of_match_table = led_async_mux_dt_match,
+ },
+};
+
+module_platform_driver(led_async_mux_driver);
+
+MODULE_AUTHOR("Jacek Anaszewski <[email protected]>");
+MODULE_DESCRIPTION("LED async mux");
+MODULE_LICENSE("GPL");
--
1.7.9.5

2014-07-11 14:05:01

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 07/21] of: add of_node_ncmp wrapper

The wrapper for strnicmp is required for checking whether a node has
expected prefix.

Signed-off-by: Jacek Anaszewski <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Grant Likely <[email protected]>
Cc: Benjamin Herrenschmidt <[email protected]>
Cc: Michal Simek <[email protected]>
---
include/linux/of.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/include/linux/of.h b/include/linux/of.h
index 692b56c..9a53eea 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -199,6 +199,7 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size)
#define of_compat_cmp(s1, s2, l) strcasecmp((s1), (s2))
#define of_prop_cmp(s1, s2) strcmp((s1), (s2))
#define of_node_cmp(s1, s2) strcasecmp((s1), (s2))
+#define of_node_ncmp(s1, s2, n) strnicmp((s1), (s2), (n))
#endif

/* flag descriptions */
--
1.7.9.5

2014-07-11 14:04:52

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 05/21] leds: avoid using deprecated DEVICE_ATTR macro

Make the sysfs attributes definition consistent in the whole file.
The modification entails change of the function name:
led_max_brightness_show -> max_brightness_show

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/led-class.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 0127783..a96a1a7 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -60,14 +60,14 @@ unlock:
}
static DEVICE_ATTR_RW(brightness);

-static ssize_t led_max_brightness_show(struct device *dev,
+static ssize_t max_brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);

return sprintf(buf, "%u\n", led_cdev->max_brightness);
}
-static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL);
+static DEVICE_ATTR_RO(max_brightness);

#ifdef CONFIG_LEDS_TRIGGERS
static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
--
1.7.9.5

2014-07-11 14:11:28

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 03/21] leds: Improve and export led_update_brightness

led_update_brightness helper function used to be exploited only locally
in the led-class.c module, where its result was being passed to the
brightness_show sysfs callback. With the introduction of v4l2-flash
subdevice the same functionality became required for reading current
brightness from a LED device. This patch adds checking of return value
of the brightness_get callback and moves the led_update_brightness()
function to the LED subsystem public API.

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/led-class.c | 6 ------
drivers/leds/led-core.c | 15 +++++++++++++++
include/linux/leds.h | 10 ++++++++++
3 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index ea04891..da79bbb 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -23,12 +23,6 @@

static struct class *leds_class;

-static void led_update_brightness(struct led_classdev *led_cdev)
-{
- if (led_cdev->brightness_get)
- led_cdev->brightness = led_cdev->brightness_get(led_cdev);
-}
-
static ssize_t brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index 4d7cb31..0ac06ed 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -127,6 +127,21 @@ void led_set_brightness(struct led_classdev *led_cdev,
}
EXPORT_SYMBOL(led_set_brightness);

+int led_update_brightness(struct led_classdev *led_cdev)
+{
+ int ret = 0;
+
+ if (led_cdev->brightness_get) {
+ ret = led_cdev->brightness_get(led_cdev);
+ if (ret >= 0) {
+ led_cdev->brightness = ret;
+ return 0;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(led_update_brightness);
/* Caller must ensure led_cdev->led_lock held */
void led_sysfs_lock(struct led_classdev *led_cdev)
{
diff --git a/include/linux/leds.h b/include/linux/leds.h
index da7c6b5..e9b025d 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -146,6 +146,16 @@ extern void led_blink_set_oneshot(struct led_classdev *led_cdev,
extern void led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness brightness);
/**
+ * led_update_brightness - update LED brightness
+ * @led_cdev: the LED to query
+ *
+ * Get an LED's current brightness and update led_cdev->brightness
+ * member with the obtained value.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+extern int led_update_brightness(struct led_classdev *led_cdev);
+
* led_sysfs_lock - lock LED sysfs interface
* @led_cdev: the LED to set
*
--
1.7.9.5

2014-07-11 14:12:01

by Jacek Anaszewski

[permalink] [raw]
Subject: [PATCH/RFC v4 01/21] leds: make brightness type consistent across whole subsystem

Documentations states that brightness units type is enum led_brightness
and this is the type used by the led API functions. Adjust the type
of brightness variables in the struct led_classdev accordingly.

Signed-off-by: Jacek Anaszewski <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
Cc: Bryan Wu <[email protected]>
Cc: Richard Purdie <[email protected]>
---
include/linux/leds.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/include/linux/leds.h b/include/linux/leds.h
index e436864..995f933 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -31,8 +31,8 @@ enum led_brightness {

struct led_classdev {
const char *name;
- int brightness;
- int max_brightness;
+ enum led_brightness brightness;
+ enum led_brightness max_brightness;
int flags;

/* Lower 16 bits reflect status */
--
1.7.9.5

2014-07-16 15:36:01

by Sakari Ailus

[permalink] [raw]
Subject: Re: [PATCH/RFC v4 02/21] leds: implement sysfs interface locking mechanism

Hi Jacek,

Thank you for the update!

Jacek Anaszewski wrote:
...
> diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
> index 71b40d3..4d7cb31 100644
> --- a/drivers/leds/led-core.c
> +++ b/drivers/leds/led-core.c
> @@ -126,3 +126,21 @@ void led_set_brightness(struct led_classdev *led_cdev,
> __led_set_brightness(led_cdev, brightness);
> }
> EXPORT_SYMBOL(led_set_brightness);
> +
> +/* Caller must ensure led_cdev->led_lock held */
> +void led_sysfs_lock(struct led_classdev *led_cdev)
> +{
> + WARN_ON(!mutex_is_locked(&led_cdev->led_lock));

How about lockdep_assert_held() instead?

--
Sakari Ailus
e-mail: [email protected] XMPP: [email protected]

2014-07-16 15:42:44

by Sakari Ailus

[permalink] [raw]
Subject: Re: [PATCH/RFC v4 04/21] leds: Reorder include directives

Hi Jacek,

Jacek Anaszewski wrote:
> Reorder include directives so that they are arranged
> in alphabetical order.
>
> 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/led-class.c | 13 +++++++------
> drivers/leds/led-core.c | 3 ++-
> include/linux/leds.h | 2 +-
> 3 files changed, 10 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
> index da79bbb..0127783 100644
> --- a/drivers/leds/led-class.c
> +++ b/drivers/leds/led-class.c
> @@ -9,16 +9,17 @@
> * published by the Free Software Foundation.
> */
>
> -#include <linux/module.h>
> -#include <linux/kernel.h>
> +#include <linux/ctype.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> #include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/leds.h>
> #include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>

Do you also need slab.h?

> #include <linux/spinlock.h>
> -#include <linux/device.h>
> #include <linux/timer.h>
> -#include <linux/err.h>
> -#include <linux/ctype.h>
> -#include <linux/leds.h>
> #include "leds.h"
>
> static struct class *leds_class;
> diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
> index 0ac06ed..d156fb6 100644
> --- a/drivers/leds/led-core.c
> +++ b/drivers/leds/led-core.c
> @@ -12,10 +12,11 @@
> */
>
> #include <linux/kernel.h>
> +#include <linux/leds.h>
> #include <linux/list.h>
> #include <linux/module.h>
> +#include <linux/mutex.h>

And mutex.h here?

With that fixed, if there are some other patches that are essentially
cleanups that could go in well before the rest of the set. Some of the
patches will take a little longer, I presume.

I'll let you know if/when there's an update regarding the compound
controls patchset.

--
Kind regards,

Sakari Ailus
e-mail: [email protected] XMPP: [email protected]

2014-07-16 15:47:35

by Sakari Ailus

[permalink] [raw]
Subject: Re: [PATCH/RFC v4 05/21] leds: avoid using deprecated DEVICE_ATTR macro

Hi Jacek,

Jacek Anaszewski wrote:
> Make the sysfs attributes definition consistent in the whole file.
> The modification entails change of the function name:
> led_max_brightness_show -> max_brightness_show

I'm not sure whether DEVICE_ATTR() is really deprecated but nevertheless
this is cleaner.

Acked-by: Sakari Ailus <[email protected]>

--
Kind regards,

Sakari Ailus
e-mail: [email protected] XMPP: [email protected]

2014-07-16 17:19:29

by Bryan Wu

[permalink] [raw]
Subject: Re: [PATCH/RFC v4 00/21] LED / flash API integration

On Fri, Jul 11, 2014 at 7:04 AM, Jacek Anaszewski
<[email protected]> wrote:
> This is is the fourth version of the patch series being a follow up
> of the discussion on Media summit 2013-10-23, related to the
> LED / flash API integration (the notes from the discussion were
> enclosed in the message [1], paragraph 5).
> The series is based on linux-next-20140707
>

I really apologize that I missed your discussion email in my Gmail
inbox, it was archived some where. Even in this series some of these
patches are archived in different tab.

I will start to review and help to push this.

Thanks,
-Bryan


> Description of the proposed modifications according to
> the kernel components they are relevant to:
> - LED subsystem modifications
> * added led_flash module which, when enabled in the config,
> registers flash specific sysfs attributes, if the driver
> declares related features:
> - flash_brightness
> - max_flash_brightness
> - indicator_brightness
> - max_indicator_brightness
> - flash_timeout
> - max_flash_timeout
> - flash_strobe
> - external_strobe
> - flash_fault
> and exposes kernel internal API:
> - led_classdev_flash_register
> - led_classdev_flash_unregister
> - led_set_flash_strobe
> - led_get_flash_strobe
> - led_set_flash_brightness
> - led_update_flash_brightness
> - led_set_indicator_brightness
> - led_update_indicator_brightness
> - led_set_flash_timeout
> - led_get_flash_fault
> - led_set_external_strobe
> - led_sysfs_lock
> - led_sysfs_unlock
> * added Flash Manager functionality, available when
> led_flash module is enable in the config;
> if the device tree node of a flash led device contains
> relevant subnodes, it registers following sysfs attributes:
> - strobe_provider
> - strobe_providerN
> - blocking_strobe
> following kernel internal API is exposed by the flash manager:
> - led_flash_manager_register_flash
> - led_flash_manager_unregister_flash
> - led_flash_manager_setup_strobe
> - led_flash_manager_bind_async_mux
> - led_flash_manager_unbind_async_mux
> - Addition of a V4L2 Flash sub-device registration helpers
> * added v4l2-flash.c and v4l2-flash.h files with helper
> functions that facilitate registration/unregistration
> of a subdevice, which wrapps a LED subsystem device and
> exposes V4L2 Flash control interface
> - Addition of a LED Flash Class driver for the flash cell of
> the MAX77693 mfd
> - Addition of a LED Flash Class driver for the AAT1290 current
> regulator for flash leds along with its DT binding for the
> exynos4412-trats2 board, where standalone multiplexer is
> used for modifying strobe signal routing - either from the SoC
> GPIO or from a camera sensor. This arrangment is handled
> by the newly introduced Flash Manager functionality.
> - Update of the max77693.txt DT bindings documentation
> - Update of the common leds DT bindings documentation
>
> ================
> Changes since v2
> ================
>
> - refactored the code so that it is possible to build
> led-core without led-flash module
> - added v4l2-flash ops which slackens dependency from
> the led-flash module
> - implemented led_clamp_align_val function and led_ctrl
> structure which allows to align led control values
> in the manner compatible with V4L2 Flash controls;
> the flash brightness and timeout units have been defined
> as microamperes and microseconds respectively to properly
> support devices which define current and time levels
> as fractions of 1/1000.
> - added support for the flash privacy leds
> - modified LED sysfs locking mechanism - now it locks/unlocks
> the interface on V4L2 Flash sub-device file open/close
> - changed hw_triggered attribute name to external_strobe,
> which maps on the V4L2_FLASH_STROBE_SOURCE_EXTERNAL name
> more intuitively
> - made external_strobe and indicator related sysfs attributes
> created optionally only if related features are declared
> by the led device driver
> - removed from the series patches modifying exynos4-is media
> controller - a proposal for "flash manager" which will take
> care of flash devices registration is due to be submitted
> - removed modifications to the LED class devices documentation,
> it will be covered after the whole functionality is accepted
>
> ================
> Changes since v3
> ================
>
> - added Flash Manager feature
> - added generic LED Flash Class gpio mux driver
> - added sample async mux driver
> - added of helpers for parsing Flash Manager related
> device tree data
> - added V4L2_CID_FLASH_STROBE_PROVIDER control
> - introduced struct led_classdev_flash, which wrapps
> struct led_classdev
> - made all flash ops, except strobe_set, optional; if an op
> is absent the related sysfs attribute isn't created
> - added LED_DEV_CAP* flags
> - modified v4l2-flash helpers to create v4l2_device
> for v4l2_flash subdevices to register in it
> - modified max77693-flash driver and its DT bindings
> to allow for registering either one or two LED Flash
> Class devices, depending on the device tree settings.
> - added new API for setting torch_brightness
> - extended leds common DT binding documentation
>
> Issues:
>
> 1) Who should register V4L2 Flash sub-device?
>
> LED Flash Class devices, after introduction of the Flash Manager,
> are not tightly coupled with any media controller. They are maintained
> by the Flash Manager and made available for dynamic assignment to
> any media system they are connected to through multiplexing devices.
>
> In the proposed rough solution, when support for V4L2 Flash sub-devices
> is enabled, there is a v4l2_device created for them to register in.
> This however implies that V4L2 Flash device will not be available
> in any media controller, which calls its existence into question.
>
> Therefore I'd like to consult possible ways of solving this issue.
> The option I see is implementing a mechanism for moving V4L2 Flash
> sub-devices between media controllers. A V4L2 Flash sub-device
> would initially be assigned to one media system in the relevant
> device tree binding, but it could be dynamically reassigned to
> the other one. However I'm not sure if media controller design
> is prepared for dynamic modifications of its graph and how many
> modifications in the existing drivers this solution would require.
>
> 2) Consequences of locking the Flash Manager during flash strobe
>
> In case a LED Flash Class device depends on muxes involved in
> routing the other LED Flash Class device's strobe signals,
> the Flash Manager must be locked for the time of strobing
> to prevent reconfiguration of the strobe signal routing
> by the other device.
>
> A blocking_strobe sysfs attribute was added to indicate whether
> this is the case for the device.
>
> Nonetheless, this modifies behaviour of led_set_external_strobe
> API, which also must block the caller for the time of strobing
> to protect the strobe signal routing while the external strobe
> signal provider asserts the flash_en pin.
>
> Use case for user space application would be following in this case:
> - spawn a new thread in which external strobe is activated
> - in the parent thread instruct the external strobe provider
> to strobe the flash
>
> As an improvement there could be an op added for notifying
> the application that the strobe signal routing has been
> set up. It would have to be called from the function
> led_flash_manager_setup_strobe after led_flash_manager_set_external_strobe
> returns.
>
> The blocking_strobe attribute would have to be also
> mapped to a new V4L2 Flash control. Unfortunately it poses
> a problem for the existing users of the V4L2 Flash API
> which are not aware that setting V4L2_CID_FLASH_STROBE_SOURCE
> may be blocking.
>
> As a solution led_set_external_strobe API could be extended
> by a parameter telling whether the caller is prepared for
> the blocking call. led_flash_manager_setup_strobe should
> be extended accordingly then.
> In V4L2 "V4L2_FLASH_STROBE_SOURCE_EXTERNAL_BLOCKING" menu item
> could be added to handle this.
> With existing V4L2_FLASH_STROBE_SOURCE_EXTERNAL the flash manager
> wouldn't protect muxes against reconfiguration.
>
> TODO:
> - switch to using V4L2 array controls
> - add s_power op to the LED Flash Class
>
>
> I will be off-line for three weeks from now on and will respond
> to any questions after getting back.
>
>
> Thanks,
> Jacek Anaszewski
>
> Jacek Anaszewski (21):
> leds: make brightness type consistent across whole subsystem
> leds: implement sysfs interface locking mechanism
> leds: Improve and export led_update_brightness
> leds: Reorder include directives
> leds: avoid using deprecated DEVICE_ATTR macro
> leds: add API for setting torch brightness
> of: add of_node_ncmp wrapper
> leds: Add sysfs and kernel internal API for flash LEDs
> Documentation: leds: Add description of LED Flash Class extension
> Documentation: leds: add exemplary asynchronous mux driver
> DT: leds: Add flash led devices related properties
> DT: Add documentation for LED Class Flash Manger
> v4l2-device: add v4l2_device_register_subdev_node API
> v4l2-ctrls: add control for flash strobe signal providers
> media: Add registration helpers for V4L2 flash
> 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
> ARM: dts: add aat1290 current regulator device node
>
> Documentation/DocBook/media/v4l/controls.xml | 11 +
> Documentation/devicetree/bindings/leds/common.txt | 16 +
> .../devicetree/bindings/leds/leds-aat1290.txt | 17 +
> .../bindings/leds/leds-flash-manager.txt | 165 +++
> Documentation/devicetree/bindings/mfd/max77693.txt | 62 ++
> .../devicetree/bindings/vendor-prefixes.txt | 1 +
> Documentation/leds/leds-async-mux.c | 65 ++
> Documentation/leds/leds-class-flash.txt | 126 +++
> arch/arm/boot/dts/exynos4412-trats2.dts | 24 +
> drivers/leds/Kconfig | 28 +
> drivers/leds/Makefile | 7 +
> drivers/leds/led-class-flash.c | 715 +++++++++++++
> drivers/leds/led-class.c | 58 +-
> drivers/leds/led-core.c | 51 +-
> drivers/leds/led-flash-gpio-mux.c | 102 ++
> drivers/leds/led-flash-manager.c | 698 +++++++++++++
> drivers/leds/led-triggers.c | 16 +-
> drivers/leds/leds-aat1290.c | 455 +++++++++
> drivers/leds/leds-max77693.c | 1070 ++++++++++++++++++++
> drivers/leds/of_led_flash_manager.c | 155 +++
> drivers/media/v4l2-core/Kconfig | 11 +
> drivers/media/v4l2-core/Makefile | 2 +
> drivers/media/v4l2-core/v4l2-ctrls.c | 2 +
> drivers/media/v4l2-core/v4l2-device.c | 63 +-
> drivers/media/v4l2-core/v4l2-flash.c | 580 +++++++++++
> drivers/mfd/max77693.c | 5 +-
> include/linux/led-class-flash.h | 290 ++++++
> include/linux/led-flash-gpio-mux.h | 68 ++
> include/linux/led-flash-manager.h | 121 +++
> include/linux/leds.h | 73 +-
> include/linux/mfd/max77693.h | 40 +
> include/linux/of.h | 1 +
> include/linux/of_led_flash_manager.h | 80 ++
> include/media/v4l2-device.h | 7 +
> include/media/v4l2-flash.h | 137 +++
> include/uapi/linux/v4l2-controls.h | 2 +
> 36 files changed, 5272 insertions(+), 52 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/leds/leds-aat1290.txt
> create mode 100644 Documentation/devicetree/bindings/leds/leds-flash-manager.txt
> create mode 100644 Documentation/leds/leds-async-mux.c
> create mode 100644 Documentation/leds/leds-class-flash.txt
> create mode 100644 drivers/leds/led-class-flash.c
> create mode 100644 drivers/leds/led-flash-gpio-mux.c
> create mode 100644 drivers/leds/led-flash-manager.c
> create mode 100644 drivers/leds/leds-aat1290.c
> create mode 100644 drivers/leds/leds-max77693.c
> create mode 100644 drivers/leds/of_led_flash_manager.c
> create mode 100644 drivers/media/v4l2-core/v4l2-flash.c
> create mode 100644 include/linux/led-class-flash.h
> create mode 100644 include/linux/led-flash-gpio-mux.h
> create mode 100644 include/linux/led-flash-manager.h
> create mode 100644 include/linux/of_led_flash_manager.h
> create mode 100644 include/media/v4l2-flash.h
>
> --
> 1.7.9.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2014-07-16 17:22:01

by Bryan Wu

[permalink] [raw]
Subject: Re: [PATCH/RFC v4 00/21] LED / flash API integration

On Wed, Jul 16, 2014 at 10:19 AM, Bryan Wu <[email protected]> wrote:
> On Fri, Jul 11, 2014 at 7:04 AM, Jacek Anaszewski
> <[email protected]> wrote:
>> This is is the fourth version of the patch series being a follow up
>> of the discussion on Media summit 2013-10-23, related to the
>> LED / flash API integration (the notes from the discussion were
>> enclosed in the message [1], paragraph 5).
>> The series is based on linux-next-20140707
>>
>
> I really apologize that I missed your discussion email in my Gmail
> inbox, it was archived some where. Even in this series some of these
> patches are archived in different tab.
>
> I will start to review and help to push this.
>

In the mean time, could you please provide an git tree for me to pull?
It's much easier for me to review in my git.

-Bryan

>
>
>> Description of the proposed modifications according to
>> the kernel components they are relevant to:
>> - LED subsystem modifications
>> * added led_flash module which, when enabled in the config,
>> registers flash specific sysfs attributes, if the driver
>> declares related features:
>> - flash_brightness
>> - max_flash_brightness
>> - indicator_brightness
>> - max_indicator_brightness
>> - flash_timeout
>> - max_flash_timeout
>> - flash_strobe
>> - external_strobe
>> - flash_fault
>> and exposes kernel internal API:
>> - led_classdev_flash_register
>> - led_classdev_flash_unregister
>> - led_set_flash_strobe
>> - led_get_flash_strobe
>> - led_set_flash_brightness
>> - led_update_flash_brightness
>> - led_set_indicator_brightness
>> - led_update_indicator_brightness
>> - led_set_flash_timeout
>> - led_get_flash_fault
>> - led_set_external_strobe
>> - led_sysfs_lock
>> - led_sysfs_unlock
>> * added Flash Manager functionality, available when
>> led_flash module is enable in the config;
>> if the device tree node of a flash led device contains
>> relevant subnodes, it registers following sysfs attributes:
>> - strobe_provider
>> - strobe_providerN
>> - blocking_strobe
>> following kernel internal API is exposed by the flash manager:
>> - led_flash_manager_register_flash
>> - led_flash_manager_unregister_flash
>> - led_flash_manager_setup_strobe
>> - led_flash_manager_bind_async_mux
>> - led_flash_manager_unbind_async_mux
>> - Addition of a V4L2 Flash sub-device registration helpers
>> * added v4l2-flash.c and v4l2-flash.h files with helper
>> functions that facilitate registration/unregistration
>> of a subdevice, which wrapps a LED subsystem device and
>> exposes V4L2 Flash control interface
>> - Addition of a LED Flash Class driver for the flash cell of
>> the MAX77693 mfd
>> - Addition of a LED Flash Class driver for the AAT1290 current
>> regulator for flash leds along with its DT binding for the
>> exynos4412-trats2 board, where standalone multiplexer is
>> used for modifying strobe signal routing - either from the SoC
>> GPIO or from a camera sensor. This arrangment is handled
>> by the newly introduced Flash Manager functionality.
>> - Update of the max77693.txt DT bindings documentation
>> - Update of the common leds DT bindings documentation
>>
>> ================
>> Changes since v2
>> ================
>>
>> - refactored the code so that it is possible to build
>> led-core without led-flash module
>> - added v4l2-flash ops which slackens dependency from
>> the led-flash module
>> - implemented led_clamp_align_val function and led_ctrl
>> structure which allows to align led control values
>> in the manner compatible with V4L2 Flash controls;
>> the flash brightness and timeout units have been defined
>> as microamperes and microseconds respectively to properly
>> support devices which define current and time levels
>> as fractions of 1/1000.
>> - added support for the flash privacy leds
>> - modified LED sysfs locking mechanism - now it locks/unlocks
>> the interface on V4L2 Flash sub-device file open/close
>> - changed hw_triggered attribute name to external_strobe,
>> which maps on the V4L2_FLASH_STROBE_SOURCE_EXTERNAL name
>> more intuitively
>> - made external_strobe and indicator related sysfs attributes
>> created optionally only if related features are declared
>> by the led device driver
>> - removed from the series patches modifying exynos4-is media
>> controller - a proposal for "flash manager" which will take
>> care of flash devices registration is due to be submitted
>> - removed modifications to the LED class devices documentation,
>> it will be covered after the whole functionality is accepted
>>
>> ================
>> Changes since v3
>> ================
>>
>> - added Flash Manager feature
>> - added generic LED Flash Class gpio mux driver
>> - added sample async mux driver
>> - added of helpers for parsing Flash Manager related
>> device tree data
>> - added V4L2_CID_FLASH_STROBE_PROVIDER control
>> - introduced struct led_classdev_flash, which wrapps
>> struct led_classdev
>> - made all flash ops, except strobe_set, optional; if an op
>> is absent the related sysfs attribute isn't created
>> - added LED_DEV_CAP* flags
>> - modified v4l2-flash helpers to create v4l2_device
>> for v4l2_flash subdevices to register in it
>> - modified max77693-flash driver and its DT bindings
>> to allow for registering either one or two LED Flash
>> Class devices, depending on the device tree settings.
>> - added new API for setting torch_brightness
>> - extended leds common DT binding documentation
>>
>> Issues:
>>
>> 1) Who should register V4L2 Flash sub-device?
>>
>> LED Flash Class devices, after introduction of the Flash Manager,
>> are not tightly coupled with any media controller. They are maintained
>> by the Flash Manager and made available for dynamic assignment to
>> any media system they are connected to through multiplexing devices.
>>
>> In the proposed rough solution, when support for V4L2 Flash sub-devices
>> is enabled, there is a v4l2_device created for them to register in.
>> This however implies that V4L2 Flash device will not be available
>> in any media controller, which calls its existence into question.
>>
>> Therefore I'd like to consult possible ways of solving this issue.
>> The option I see is implementing a mechanism for moving V4L2 Flash
>> sub-devices between media controllers. A V4L2 Flash sub-device
>> would initially be assigned to one media system in the relevant
>> device tree binding, but it could be dynamically reassigned to
>> the other one. However I'm not sure if media controller design
>> is prepared for dynamic modifications of its graph and how many
>> modifications in the existing drivers this solution would require.
>>
>> 2) Consequences of locking the Flash Manager during flash strobe
>>
>> In case a LED Flash Class device depends on muxes involved in
>> routing the other LED Flash Class device's strobe signals,
>> the Flash Manager must be locked for the time of strobing
>> to prevent reconfiguration of the strobe signal routing
>> by the other device.
>>
>> A blocking_strobe sysfs attribute was added to indicate whether
>> this is the case for the device.
>>
>> Nonetheless, this modifies behaviour of led_set_external_strobe
>> API, which also must block the caller for the time of strobing
>> to protect the strobe signal routing while the external strobe
>> signal provider asserts the flash_en pin.
>>
>> Use case for user space application would be following in this case:
>> - spawn a new thread in which external strobe is activated
>> - in the parent thread instruct the external strobe provider
>> to strobe the flash
>>
>> As an improvement there could be an op added for notifying
>> the application that the strobe signal routing has been
>> set up. It would have to be called from the function
>> led_flash_manager_setup_strobe after led_flash_manager_set_external_strobe
>> returns.
>>
>> The blocking_strobe attribute would have to be also
>> mapped to a new V4L2 Flash control. Unfortunately it poses
>> a problem for the existing users of the V4L2 Flash API
>> which are not aware that setting V4L2_CID_FLASH_STROBE_SOURCE
>> may be blocking.
>>
>> As a solution led_set_external_strobe API could be extended
>> by a parameter telling whether the caller is prepared for
>> the blocking call. led_flash_manager_setup_strobe should
>> be extended accordingly then.
>> In V4L2 "V4L2_FLASH_STROBE_SOURCE_EXTERNAL_BLOCKING" menu item
>> could be added to handle this.
>> With existing V4L2_FLASH_STROBE_SOURCE_EXTERNAL the flash manager
>> wouldn't protect muxes against reconfiguration.
>>
>> TODO:
>> - switch to using V4L2 array controls
>> - add s_power op to the LED Flash Class
>>
>>
>> I will be off-line for three weeks from now on and will respond
>> to any questions after getting back.
>>
>>
>> Thanks,
>> Jacek Anaszewski
>>
>> Jacek Anaszewski (21):
>> leds: make brightness type consistent across whole subsystem
>> leds: implement sysfs interface locking mechanism
>> leds: Improve and export led_update_brightness
>> leds: Reorder include directives
>> leds: avoid using deprecated DEVICE_ATTR macro
>> leds: add API for setting torch brightness
>> of: add of_node_ncmp wrapper
>> leds: Add sysfs and kernel internal API for flash LEDs
>> Documentation: leds: Add description of LED Flash Class extension
>> Documentation: leds: add exemplary asynchronous mux driver
>> DT: leds: Add flash led devices related properties
>> DT: Add documentation for LED Class Flash Manger
>> v4l2-device: add v4l2_device_register_subdev_node API
>> v4l2-ctrls: add control for flash strobe signal providers
>> media: Add registration helpers for V4L2 flash
>> 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
>> ARM: dts: add aat1290 current regulator device node
>>
>> Documentation/DocBook/media/v4l/controls.xml | 11 +
>> Documentation/devicetree/bindings/leds/common.txt | 16 +
>> .../devicetree/bindings/leds/leds-aat1290.txt | 17 +
>> .../bindings/leds/leds-flash-manager.txt | 165 +++
>> Documentation/devicetree/bindings/mfd/max77693.txt | 62 ++
>> .../devicetree/bindings/vendor-prefixes.txt | 1 +
>> Documentation/leds/leds-async-mux.c | 65 ++
>> Documentation/leds/leds-class-flash.txt | 126 +++
>> arch/arm/boot/dts/exynos4412-trats2.dts | 24 +
>> drivers/leds/Kconfig | 28 +
>> drivers/leds/Makefile | 7 +
>> drivers/leds/led-class-flash.c | 715 +++++++++++++
>> drivers/leds/led-class.c | 58 +-
>> drivers/leds/led-core.c | 51 +-
>> drivers/leds/led-flash-gpio-mux.c | 102 ++
>> drivers/leds/led-flash-manager.c | 698 +++++++++++++
>> drivers/leds/led-triggers.c | 16 +-
>> drivers/leds/leds-aat1290.c | 455 +++++++++
>> drivers/leds/leds-max77693.c | 1070 ++++++++++++++++++++
>> drivers/leds/of_led_flash_manager.c | 155 +++
>> drivers/media/v4l2-core/Kconfig | 11 +
>> drivers/media/v4l2-core/Makefile | 2 +
>> drivers/media/v4l2-core/v4l2-ctrls.c | 2 +
>> drivers/media/v4l2-core/v4l2-device.c | 63 +-
>> drivers/media/v4l2-core/v4l2-flash.c | 580 +++++++++++
>> drivers/mfd/max77693.c | 5 +-
>> include/linux/led-class-flash.h | 290 ++++++
>> include/linux/led-flash-gpio-mux.h | 68 ++
>> include/linux/led-flash-manager.h | 121 +++
>> include/linux/leds.h | 73 +-
>> include/linux/mfd/max77693.h | 40 +
>> include/linux/of.h | 1 +
>> include/linux/of_led_flash_manager.h | 80 ++
>> include/media/v4l2-device.h | 7 +
>> include/media/v4l2-flash.h | 137 +++
>> include/uapi/linux/v4l2-controls.h | 2 +
>> 36 files changed, 5272 insertions(+), 52 deletions(-)
>> create mode 100644 Documentation/devicetree/bindings/leds/leds-aat1290.txt
>> create mode 100644 Documentation/devicetree/bindings/leds/leds-flash-manager.txt
>> create mode 100644 Documentation/leds/leds-async-mux.c
>> create mode 100644 Documentation/leds/leds-class-flash.txt
>> create mode 100644 drivers/leds/led-class-flash.c
>> create mode 100644 drivers/leds/led-flash-gpio-mux.c
>> create mode 100644 drivers/leds/led-flash-manager.c
>> create mode 100644 drivers/leds/leds-aat1290.c
>> create mode 100644 drivers/leds/leds-max77693.c
>> create mode 100644 drivers/leds/of_led_flash_manager.c
>> create mode 100644 drivers/media/v4l2-core/v4l2-flash.c
>> create mode 100644 include/linux/led-class-flash.h
>> create mode 100644 include/linux/led-flash-gpio-mux.h
>> create mode 100644 include/linux/led-flash-manager.h
>> create mode 100644 include/linux/of_led_flash_manager.h
>> create mode 100644 include/media/v4l2-flash.h
>>
>> --
>> 1.7.9.5
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-media" in
>> the body of a message to [email protected]
>> More majordomo info at http://vger.kernel.org/majordomo-info.html

2014-07-16 21:55:24

by Sakari Ailus

[permalink] [raw]
Subject: Re: [PATCH/RFC v4 06/21] leds: add API for setting torch brightness

Hi Jacek,

Jacek Anaszewski wrote:
...
> diff --git a/include/linux/leds.h b/include/linux/leds.h
> index 1a130cc..9bea9e6 100644
> --- a/include/linux/leds.h
> +++ b/include/linux/leds.h
> @@ -44,11 +44,21 @@ struct led_classdev {
> #define LED_BLINK_ONESHOT_STOP (1 << 18)
> #define LED_BLINK_INVERT (1 << 19)
> #define LED_SYSFS_LOCK (1 << 20)
> +#define LED_DEV_CAP_TORCH (1 << 21)
>
> /* Set LED brightness level */
> /* Must not sleep, use a workqueue if needed */
> void (*brightness_set)(struct led_classdev *led_cdev,
> enum led_brightness brightness);
> + /*
> + * Set LED brightness immediately - it is required for flash led
> + * devices as they require setting torch brightness to have immediate
> + * effect. brightness_set op cannot be used for this purpose because
> + * the led drivers schedule a work queue task in it to allow for
> + * being called from led-triggers, i.e. from the timer irq context.
> + */

Do we need to classify actual devices based on this? I think it's rather
a different API behaviour between the LED and the V4L2 APIs.

On devices that are slow to control, the behaviour should be asynchronous
over the LED API and synchronous when accessed through the V4L2 API. How
about implementing the work queue, as I have suggested, in the framework, so
that individual drivers don't need to care about this and just implement the
synchronous variant of this op? A flag could be added to distinguish devices
that are fast so that the work queue isn't needed.

It'd be nice to avoid individual drivers having to implement multiple ops to
do the same thing, just for differing user space interfacs.

--
Kind regards,

Sakari Ailus
e-mail: [email protected] XMPP: [email protected]

2014-07-16 22:00:54

by Sakari Ailus

[permalink] [raw]
Subject: Re: [PATCH/RFC v4 07/21] of: add of_node_ncmp wrapper

On Fri, Jul 11, 2014 at 04:04:10PM +0200, Jacek Anaszewski wrote:
> The wrapper for strnicmp is required for checking whether a node has
> expected prefix.

Acked-by: Sakari Ailus <[email protected]>

--
Sakari Ailus
e-mail: [email protected] XMPP: [email protected]

2014-07-21 11:12:00

by Sakari Ailus

[permalink] [raw]
Subject: Re: [PATCH/RFC v4 15/21] media: Add registration helpers for V4L2 flash

Hi Jacek,

Jacek Anaszewski wrote:
> This patch adds helper functions for registering/unregistering
> LED class flash devices as V4L2 subdevs. 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 | 580 ++++++++++++++++++++++++++++++++++
> include/media/v4l2-flash.h | 137 ++++++++
> 4 files changed, 730 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 9ca0f8d..3ae3f0f 100644
> --- a/drivers/media/v4l2-core/Kconfig
> +++ b/drivers/media/v4l2-core/Kconfig
> @@ -35,6 +35,17 @@ config V4L2_MEM2MEM_DEV
> tristate
> depends on VIDEOBUF2_CORE
>
> +# Used by LED subsystem flash drivers
> +config V4L2_FLASH_LED_CLASS
> + bool "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..21080c6
> --- /dev/null
> +++ b/drivers/media/v4l2-core/v4l2-flash.c
> @@ -0,0 +1,580 @@
> +/*
> + * 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/mutex.h>
> +#include <linux/of_led_flash_manager.h>
> +#include <linux/slab.h>
> +#include <media/v4l2-flash.h>
> +
> +#define call_flash_op(v4l2_flash, op, args...) \
> + (v4l2_flash->ops->op ? \
> + v4l2_flash->ops->op(args) : \
> + -EINVAL)
> +
> +static struct v4l2_device *v4l2_dev;
> +static int registered_flashes;
> +
> +static inline enum led_brightness v4l2_flash_intensity_to_led_brightness(
> + struct v4l2_ctrl_config *config,
> + s32 intensity)
> +{
> + return ((intensity - config->min) / config->step) + 1;
> +}
> +
> +static inline s32 v4l2_flash_led_brightness_to_intensity(
> + struct v4l2_ctrl_config *config,
> + enum led_brightness brightness)
> +{
> + return ((brightness - 1) * config->step) + config->min;
> +}
> +
> +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_flash_ctrl_config *config = &v4l2_flash->config;
> + struct v4l2_flash_ctrl *ctrl = &v4l2_flash->ctrl;
> + bool is_strobing;
> + int ret;
> +
> + switch (c->id) {
> + case V4L2_CID_FLASH_TORCH_INTENSITY:
> + /*
> + * Update torch brightness only if in TORCH_MODE,
> + * as otherwise brightness_update op returns 0,
> + * which would spuriously inform user space that
> + * V4L2_CID_FLASH_TORCH_INTENSITY control value
> + * has changed.
> + */
> + if (ctrl->led_mode->val == V4L2_FLASH_LED_MODE_TORCH) {
> + ret = call_flash_op(v4l2_flash, torch_brightness_update,
> + led_cdev);
> + if (ret < 0)
> + return ret;
> + ctrl->torch_intensity->val =
> + v4l2_flash_led_brightness_to_intensity(
> + &config->torch_intensity,
> + led_cdev->brightness);
> + }
> + return 0;
> + case V4L2_CID_FLASH_INTENSITY:
> + ret = call_flash_op(v4l2_flash, flash_brightness_update,
> + flash);
> + if (ret < 0)
> + return ret;
> + /* no conversion is needed */
> + c->val = flash->brightness.val;
> + return 0;
> + case V4L2_CID_FLASH_INDICATOR_INTENSITY:
> + ret = call_flash_op(v4l2_flash, indicator_brightness_update,
> + flash);
> + if (ret < 0)
> + return ret;
> + /* no conversion is needed */
> + c->val = flash->indicator_brightness->val;
> + return 0;
> + case V4L2_CID_FLASH_STROBE_STATUS:
> + ret = call_flash_op(v4l2_flash, strobe_get, 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 */
> + ret = call_flash_op(v4l2_flash, fault_get, flash, &c->val);
> + return ret;
> + case V4L2_CID_FLASH_STROBE_SOURCE:
> + c->val = flash->external_strobe;
> + return 0;
> + case V4L2_CID_FLASH_STROBE_PROVIDER:
> + c->val = flash->strobe_provider_id;
> + 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 v4l2_flash_ctrl *ctrl = &v4l2_flash->ctrl;
> + struct v4l2_flash_ctrl_config *config = &v4l2_flash->config;
> + enum led_brightness torch_brightness;
> + bool external_strobe;
> + int ret;
> +
> + switch (c->id) {
> + case V4L2_CID_FLASH_LED_MODE:
> + switch (c->val) {
> + case V4L2_FLASH_LED_MODE_NONE:
> + call_flash_op(v4l2_flash, torch_brightness_set,
> + &flash->led_cdev, 0);
> + return call_flash_op(v4l2_flash, strobe_set, flash,
> + false);
> + case V4L2_FLASH_LED_MODE_FLASH:
> + /* Turn off torch LED */
> + call_flash_op(v4l2_flash, torch_brightness_set,
> + &flash->led_cdev, 0);
> + external_strobe = (ctrl->source->val ==
> + V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
> + return call_flash_op(v4l2_flash, external_strobe_set,
> + flash, external_strobe);
> + case V4L2_FLASH_LED_MODE_TORCH:
> + /* Stop flash strobing */
> + ret = call_flash_op(v4l2_flash, strobe_set, flash,
> + false);
> + if (ret)
> + return ret;
> +
> + torch_brightness =
> + v4l2_flash_intensity_to_led_brightness(
> + &config->torch_intensity,
> + ctrl->torch_intensity->val);
> + call_flash_op(v4l2_flash, torch_brightness_set,
> + &flash->led_cdev, torch_brightness);
> + return ret;
> + }
> + break;
> + case V4L2_CID_FLASH_STROBE_SOURCE:
> + external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL);

Is the external_strobe argument match exactly to the strobe source
control? You seem to assume that in g_volatile_ctrl() above. I think
having it the either way is fine but not both. :-)

> + return call_flash_op(v4l2_flash, external_strobe_set, flash,
> + external_strobe);
> + case V4L2_CID_FLASH_STROBE:
> + if (ctrl->led_mode->val != V4L2_FLASH_LED_MODE_FLASH ||
> + ctrl->source->val != V4L2_FLASH_STROBE_SOURCE_SOFTWARE)
> + return -EINVAL;
> + return call_flash_op(v4l2_flash, strobe_set, flash, true);
> + case V4L2_CID_FLASH_STROBE_STOP:

Should we check the flash mode here? I guess so. How about strobe source
as well?

> + return call_flash_op(v4l2_flash, strobe_set, flash, false);
> + case V4L2_CID_FLASH_TIMEOUT:
> + return call_flash_op(v4l2_flash, timeout_set, flash, c->val);
> + case V4L2_CID_FLASH_INTENSITY:
> + /* no conversion is needed */
> + return call_flash_op(v4l2_flash, flash_brightness_set, flash,
> + c->val);
> + case V4L2_CID_FLASH_INDICATOR_INTENSITY:
> + /* no conversion is needed */
> + return call_flash_op(v4l2_flash, indicator_brightness_set,
> + flash, c->val);
> + 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 (ctrl->led_mode->val == V4L2_FLASH_LED_MODE_TORCH) {
> + torch_brightness =
> + v4l2_flash_intensity_to_led_brightness(
> + &config->torch_intensity,
> + ctrl->torch_intensity->val);
> + call_flash_op(v4l2_flash, torch_brightness_set,
> + &flash->led_cdev, torch_brightness);
> + }
> + return 0;
> + case V4L2_CID_FLASH_STROBE_PROVIDER:
> + flash->strobe_provider_id = 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 int v4l2_flash_init_strobe_providers_menu(struct v4l2_flash *v4l2_flash)
> +{
> + struct led_classdev_flash *flash = v4l2_flash->flash;
> + struct led_flash_strobe_provider *provider;
> + struct v4l2_ctrl *ctrl;
> + int i = 0;
> +
> + v4l2_flash->strobe_providers_menu =
> + kzalloc(sizeof(char *) * (flash->num_strobe_providers),
> + GFP_KERNEL);
> + if (!v4l2_flash->strobe_providers_menu)
> + return -ENOMEM;
> +
> + list_for_each_entry(provider, &flash->strobe_providers, list)
> + v4l2_flash->strobe_providers_menu[i++] =
> + (char *) provider->name;
> +
> + ctrl = v4l2_ctrl_new_std_menu_items(
> + &v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
> + V4L2_CID_FLASH_STROBE_PROVIDER,
> + flash->num_strobe_providers - 1,
> + 0, 0,
> + (const char * const *) v4l2_flash->strobe_providers_menu);
> +
> + if (ctrl)
> + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
> +
> + return 0;
> +}
> +
> +static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash)
> +
> +{
> + 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_flash_ctrl_config *config = &v4l2_flash->config;
> + struct v4l2_ctrl *ctrl;
> + struct v4l2_ctrl_config *ctrl_cfg;
> + bool has_flash = led_cdev->flags & LED_DEV_CAP_FLASH;
> + bool has_indicator = led_cdev->flags & LED_DEV_CAP_INDICATOR;
> + bool has_strobe_providers = (flash->num_strobe_providers > 1);
> + unsigned int mask;
> + int ret, max, num_ctrls;
> +
> + num_ctrls = has_flash ? 5 : 2;
> + if (has_flash) {
> + if (flash_ops->flash_brightness_set)
> + ++num_ctrls;
> + if (flash_ops->timeout_set)
> + ++num_ctrls;
> + if (flash_ops->strobe_get)
> + ++num_ctrls;
> + if (has_indicator)
> + ++num_ctrls;
> + if (config->flash_faults)
> + ++num_ctrls;
> + if (has_strobe_providers)
> + ++num_ctrls;
> + }
> +
> + v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls);
> +
> + mask = 1 << V4L2_FLASH_LED_MODE_NONE |
> + 1 << V4L2_FLASH_LED_MODE_TORCH;
> + if (flash)
> + mask |= 1 << V4L2_FLASH_LED_MODE_FLASH;
> +
> + /* Configure FLASH_LED_MODE ctrl */
> + v4l2_flash->ctrl.led_mode = v4l2_ctrl_new_std_menu(
> + &v4l2_flash->hdl,
> + &v4l2_flash_ctrl_ops, V4L2_CID_FLASH_LED_MODE,
> + V4L2_FLASH_LED_MODE_TORCH, ~mask,
> + V4L2_FLASH_LED_MODE_NONE);
> +
> + /* Configure TORCH_INTENSITY ctrl */
> + ctrl_cfg = &config->torch_intensity;
> + ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
> + V4L2_CID_FLASH_TORCH_INTENSITY,
> + ctrl_cfg->min, ctrl_cfg->max,
> + ctrl_cfg->step, ctrl_cfg->def);
> + if (ctrl)
> + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
> + v4l2_flash->ctrl.torch_intensity = ctrl;
> +
> + if (has_flash) {
> + /* Configure FLASH_STROBE_SOURCE ctrl */
> + mask = 1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
> +
> + if (flash->has_external_strobe) {
> + mask |= 1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
> + max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
> + } else {
> + max = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
> + }
> +
> + v4l2_flash->ctrl.source = v4l2_ctrl_new_std_menu(
> + &v4l2_flash->hdl,
> + &v4l2_flash_ctrl_ops,
> + V4L2_CID_FLASH_STROBE_SOURCE,
> + max,
> + ~mask,
> + V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
> + if (v4l2_flash->ctrl.source)
> + v4l2_flash->ctrl.source->flags |=
> + V4L2_CTRL_FLAG_VOLATILE;
> +
> + /* Configure FLASH_STROBE ctrl */
> + ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
> + V4L2_CID_FLASH_STROBE, 0, 1, 1, 0);
> +
> + /* Configure FLASH_STROBE_STOP ctrl */
> + ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
> + V4L2_CID_FLASH_STROBE_STOP,
> + 0, 1, 1, 0);
> +
> + /* Configure FLASH_STROBE_STATUS ctrl */
> + ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
> + V4L2_CID_FLASH_STROBE_STATUS,
> + 0, 1, 1, 1);

I think you only should implement the strobe status control if you
really can know it, i.e. have strobe_get().

> + if (flash_ops->strobe_get)
> + if (ctrl)
> + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
> + V4L2_CTRL_FLAG_READ_ONLY;
> +
> + if (flash_ops->timeout_set) {
> + /* Configure FLASH_TIMEOUT ctrl */
> + ctrl_cfg = &config->flash_timeout;
> + ctrl = v4l2_ctrl_new_std(
> + &v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
> + V4L2_CID_FLASH_TIMEOUT, ctrl_cfg->min,
> + ctrl_cfg->max, ctrl_cfg->step,
> + ctrl_cfg->def);
> + if (ctrl)
> + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
> + }
> +
> + if (flash_ops->flash_brightness_set) {
> + /* Configure FLASH_INTENSITY ctrl */
> + ctrl_cfg = &config->flash_intensity;
> + ctrl = v4l2_ctrl_new_std(
> + &v4l2_flash->hdl,
> + &v4l2_flash_ctrl_ops,
> + V4L2_CID_FLASH_INTENSITY,
> + ctrl_cfg->min, ctrl_cfg->max,
> + ctrl_cfg->step, ctrl_cfg->def);
> + if (ctrl)
> + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
> + }
> +
> + if (config->flash_faults) {
> + /* Configure FLASH_FAULT ctrl */
> + ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl,
> + &v4l2_flash_ctrl_ops,
> + V4L2_CID_FLASH_FAULT, 0,
> + config->flash_faults,
> + 0, 0);
> + if (ctrl)
> + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
> + V4L2_CTRL_FLAG_READ_ONLY;
> + }
> + if (has_indicator) {
> + /* Configure FLASH_INDICATOR_INTENSITY ctrl */
> + ctrl_cfg = &config->indicator_intensity;
> + ctrl = v4l2_ctrl_new_std(
> + &v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
> + V4L2_CID_FLASH_INDICATOR_INTENSITY,
> + ctrl_cfg->min, ctrl_cfg->max,
> + ctrl_cfg->step, ctrl_cfg->def);
> + if (ctrl)
> + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
> + }

Could you implement the above in a loop? You're essentially repeating
the same but with different parameters.

> + if (has_strobe_providers) {
> + /* Configure V4L2_CID_FLASH_STROBE_PROVIDERS ctrl */
> + ret = v4l2_flash_init_strobe_providers_menu(v4l2_flash);
> + if (ret < 0)
> + goto error_free_handler;
> + }
> + }
> +
> + if (v4l2_flash->hdl.error) {
> + ret = v4l2_flash->hdl.error;
> + goto error_free_handler;
> + }
> +
> + ret = v4l2_ctrl_handler_setup(&v4l2_flash->hdl);
> + if (ret < 0)
> + goto error_free_handler;
> +
> + 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;
> +
> + mutex_lock(&led_cdev->led_lock);
> + call_flash_op(v4l2_flash, sysfs_lock, led_cdev);

What if you have the device open through multiple file handles? I
believe v4l2_subdev_fh_is_singular(&fh->vfh) would prove helpful here.

> + mutex_unlock(&led_cdev->led_lock);
> +
> + return 0;
> +}
> +
> +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;
> +
> + mutex_lock(&led_cdev->led_lock);
> + call_flash_op(v4l2_flash, sysfs_unlock, led_cdev);
> + mutex_unlock(&led_cdev->led_lock);
> +
> + return 0;
> +}
> +
> +int v4l2_flash_register(struct v4l2_flash *v4l2_flash)

Do you expect to call this from elsewhere than this file?

> +{
> + struct led_classdev_flash *flash = v4l2_flash->flash;
> + struct led_classdev *led_cdev = &flash->led_cdev;
> + int ret;
> +
> + if (!v4l2_dev) {
> + v4l2_dev = kzalloc(sizeof(*v4l2_dev), GFP_KERNEL);
> + if (!v4l2_dev)
> + return -ENOMEM;
> +
> + strlcpy(v4l2_dev->name, "v4l2-flash-manager",
> + sizeof(v4l2_dev->name));
> + ret = v4l2_device_register(NULL, v4l2_dev);
> + if (ret < 0) {
> + dev_err(led_cdev->dev->parent,
> + "Failed to register v4l2_device: %d\n", ret);
> + goto err_v4l2_device_register;
> + }
> + }
> +
> + ret = v4l2_device_register_subdev(v4l2_dev, &v4l2_flash->sd);
> + if (ret < 0) {
> + dev_err(led_cdev->dev->parent,
> + "Failed to register v4l2_subdev: %d\n", ret);
> + goto err_v4l2_device_register;
> + }
> +
> + ret = v4l2_device_register_subdev_node(&v4l2_flash->sd, v4l2_dev);
> + if (ret < 0) {
> + dev_err(led_cdev->dev->parent,
> + "Failed to register v4l2_subdev node: %d\n", ret);
> + goto err_register_subdev_node;
> + }

This way you can create a V4L2 sub-device node. However, flash devices
are seldom alone in the system. They are physically close to a sensor,
and this connection is shown in the Media controller interface. This
means that the flash sub-device (entity) should be part of the Media
device created by the driver in control of it. This can be achieved by
the master driver creating the sub-device. You should register an async
sub-device here.

This results in the sub-device not being registered if there's no such
master driver, but I wouldn't expect that to be an issue since the V4L2
flash API is mostly relevant in such cases.

> + ++registered_flashes;
> +
> + return 0;
> +
> +err_register_subdev_node:
> + v4l2_device_unregister_subdev(&v4l2_flash->sd);
> +err_v4l2_device_register:
> + kfree(v4l2_flash->strobe_providers_menu);
> + if (v4l2_dev && registered_flashes == 0) {
> + v4l2_device_unregister(v4l2_dev);
> + kfree(v4l2_dev);
> + v4l2_dev = NULL;
> + }
> +
> + return ret;
> +}
> +
> +static void v4l2_flash_unregister(struct v4l2_flash *v4l2_flash)
> +{
> + if (registered_flashes == 0)
> + return;
> +
> + v4l2_device_unregister_subdev(&v4l2_flash->sd);
> +
> + --registered_flashes;
> +
> + if (registered_flashes == 0) {
> + v4l2_device_unregister(v4l2_dev);
> + kfree(v4l2_dev);
> + v4l2_dev = NULL;
> + }
> +}
> +
> +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,
> +};
> +
> +int v4l2_flash_init(struct led_classdev_flash *flash,
> + struct v4l2_flash_ctrl_config *config,
> + const struct v4l2_flash_ops *flash_ops,
> + struct v4l2_flash **out_flash)
> +{
> + struct v4l2_flash *v4l2_flash;
> + struct led_classdev *led_cdev = &flash->led_cdev;
> + struct v4l2_subdev *sd;
> + int ret;
> +
> + if (!flash || !config || !out_flash)
> + return -EINVAL;
> +
> + v4l2_flash = kzalloc(sizeof(*v4l2_flash), GFP_KERNEL);
> + if (!v4l2_flash)
> + return -ENOMEM;
> +
> + sd = &v4l2_flash->sd;
> + v4l2_flash->flash = flash;
> + v4l2_flash->ops = flash_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);
> +
> + v4l2_flash->config = *config;
> + ret = v4l2_flash_init_controls(v4l2_flash);
> + 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_flash_register(v4l2_flash);
> + if (ret < 0)
> + goto err_init_entity;
> +
> + *out_flash = v4l2_flash;

How about returning the pointer instead?

> + return 0;
> +
> +err_init_entity:
> + media_entity_cleanup(&sd->entity);
> +err_init_controls:
> + v4l2_ctrl_handler_free(sd->ctrl_handler);
> + kfree(v4l2_flash);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_flash_init);
> +
> +void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
> +{
> + if (!v4l2_flash)
> + return;
> +
> + v4l2_flash_unregister(v4l2_flash);
> + v4l2_ctrl_handler_free(v4l2_flash->sd.ctrl_handler);
> + media_entity_cleanup(&v4l2_flash->sd.entity);
> + kfree(v4l2_flash->strobe_providers_menu);
> + kfree(v4l2_flash);
> +}
> +EXPORT_SYMBOL_GPL(v4l2_flash_release);
> diff --git a/include/media/v4l2-flash.h b/include/media/v4l2-flash.h
> new file mode 100644
> index 0000000..effa46b
> --- /dev/null
> +++ b/include/media/v4l2-flash.h
> @@ -0,0 +1,137 @@
> +/*
> + * 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;
> +enum led_brightness;
> +
> +struct v4l2_flash_ops {
> + int (*torch_brightness_set)(struct led_classdev *led_cdev,
> + enum led_brightness brightness);
> + int (*torch_brightness_update)(struct led_classdev *led_cdev);
> + int (*flash_brightness_set)(struct led_classdev_flash *flash,
> + u32 brightness);
> + int (*flash_brightness_update)(struct led_classdev_flash *flash);
> + int (*strobe_set)(struct led_classdev_flash *flash, bool state);
> + int (*strobe_get)(struct led_classdev_flash *flash, bool *state);
> + int (*timeout_set)(struct led_classdev_flash *flash, u32 timeout);
> + int (*indicator_brightness_set)(struct led_classdev_flash *flash,
> + u32 brightness);
> + int (*indicator_brightness_update)(struct led_classdev_flash *flash);
> + int (*external_strobe_set)(struct led_classdev_flash *flash,
> + bool enable);
> + int (*fault_get)(struct led_classdev_flash *flash, u32 *fault);
> + void (*sysfs_lock)(struct led_classdev *led_cdev);
> + void (*sysfs_unlock)(struct led_classdev *led_cdev);

These functions are not driver specific and there's going to be just one
implementation (I suppose). Could you refresh my memory regarding why
the LED framework functions aren't called directly?

> +};
> +
> +/**
> + * struct v4l2_flash_ctrl - controls that define the sub-dev's state
> + * @source: V4L2_CID_FLASH_STROBE_SOURCE control
> + * @led_mode: V4L2_CID_FLASH_LED_MODE control
> + * @torch_intensity: V4L2_CID_FLASH_TORCH_INTENSITY control
> + */
> +struct v4l2_flash_ctrl {
> + struct v4l2_ctrl *source;
> + struct v4l2_ctrl *led_mode;
> + struct v4l2_ctrl *torch_intensity;
> +};
> +
> +/**
> + * 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
> + */
> +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;
> +};
> +
> +/**
> + * struct v4l2_flash - Flash sub-device context
> + * @flash: LED Flash Class device controlled by this sub-device
> + * @ops: LED Flash Class device ops
> + * @sd: V4L2 sub-device
> + * @hdl: flash controls handler
> + * @ctrl: state defining controls
> + * @config: V4L2 Flash controlsrconfiguration data
> + * @software_strobe_gates: route to the software strobe signal
> + * @external_strobe_gates: route to the external strobe signal
> + * @sensors: available external strobe sources
> + */
> +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_flash_ctrl ctrl;
> + struct v4l2_flash_ctrl_config config;
> + char **strobe_providers_menu;
> +};
> +
> +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);
> +}
> +
> +#ifdef 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
> + * @out_flash: handler to the new V4L2 Flash device
> + *
> + * Create V4L2 subdev wrapping given LED subsystem device.
> +
> + * Returns: 0 on success or negative error value on failure
> + */
> +int v4l2_flash_init(struct led_classdev_flash *led_fdev,
> + struct v4l2_flash_ctrl_config *config,
> + const struct v4l2_flash_ops *flash_ops,
> + struct v4l2_flash **out_flash);
> +
> +/**
> + * 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, config, flash_ops, out_flash) (0)
> +#define v4l2_flash_release(v4l2_flash)
> +#endif /* CONFIG_V4L2_FLASH_LED_CLASS */
> +
> +#endif /* _V4L2_FLASH_H */
>

--
Kind regards,

Sakari Ailus
[email protected]

2014-07-21 14:11:46

by Sakari Ailus

[permalink] [raw]
Subject: Re: [PATCH/RFC v4 16/21] leds: Add support for max77693 mfd flash cell

Hi Jacek,

Thanks for the patchset.

Jacek Anaszewski wrote:
> 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 subdevice when the support
> for V4L2 Flash sub-devices is enabled. Device supports up to
> two leds which can work in flash and torch mode. Leds can
> be triggered externally or by software.
>
> Signed-off-by: Jacek Anaszewski <[email protected]>
> Signed-off-by: Andrzej Hajda <[email protected]>
> Acked-by: Lee Jones <[email protected]>
> Acked-by: Kyungmin Park <[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 | 9 +
> drivers/leds/Makefile | 1 +
> drivers/leds/leds-max77693.c | 1070 ++++++++++++++++++++++++++++++++++++++++++
> drivers/mfd/max77693.c | 5 +-
> include/linux/mfd/max77693.h | 40 ++
> 5 files changed, 1124 insertions(+), 1 deletion(-)
> create mode 100644 drivers/leds/leds-max77693.c
>
> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> index 5032c6f..794055e 100644
> --- a/drivers/leds/Kconfig
> +++ b/drivers/leds/Kconfig
> @@ -457,6 +457,15 @@ 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
> + 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 237c5ba..da1a4ba 100644
> --- a/drivers/leds/Makefile
> +++ b/drivers/leds/Makefile
> @@ -55,6 +55,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..38a2398
> --- /dev/null
> +++ b/drivers/leds/leds-max77693.c
> @@ -0,0 +1,1070 @@
> +/*
> + * 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/led-flash-manager.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 MAX77693_LED_NAME_1 "max77693-flash_1"
> +#define MAX77693_LED_NAME_2 "max77693-flash_2"
> +
> +#define MAX77693_TORCH_IOUT_BITS 4
> +
> +#define MAX77693_TORCH_NO_TIMER 0x40
> +#define MAX77693_FLASH_TIMER_LEVEL 0x80
> +
> +#define MAX77693_FLASH_EN_OFF 0
> +#define MAX77693_FLASH_EN_FLASH 1
> +#define MAX77693_FLASH_EN_TORCH 2
> +#define MAX77693_FLASH_EN_ON 3
> +
> +#define MAX77693_FLASH_EN1_SHIFT 6
> +#define MAX77693_FLASH_EN2_SHIFT 4
> +#define MAX77693_TORCH_EN1_SHIFT 2
> +#define MAX77693_TORCH_EN2_SHIFT 0

Which registers are these definitions related to? Could they be defined
next to the registers instead?

You could parameterise these macros, e.g.

#define MAX77693_FLASH_EN_SHIFT(a) (6 - ((a) - 1) * 2)

> +#define MAX77693_FLASH_LOW_BATTERY_EN 0x80
> +
> +#define MAX77693_FLASH_BOOST_FIXED 0x04
> +#define MAX77693_FLASH_BOOST_LEDNUM_2 0x80
> +
> +#define MAX77693_FLASH_TIMEOUT_MIN 62500
> +#define MAX77693_FLASH_TIMEOUT_MAX 1000000
> +#define MAX77693_FLASH_TIMEOUT_STEP 62500
> +
> +#define MAX77693_TORCH_TIMEOUT_MIN 262000
> +#define MAX77693_TORCH_TIMEOUT_MAX 15728000
> +
> +#define MAX77693_FLASH_IOUT_MIN 15625
> +#define MAX77693_FLASH_IOUT_MAX_1LED 1000000
> +#define MAX77693_FLASH_IOUT_MAX_2LEDS 625000
> +#define MAX77693_FLASH_IOUT_STEP 15625
> +
> +#define MAX77693_TORCH_IOUT_MIN 15625
> +#define MAX77693_TORCH_IOUT_MAX 250000
> +#define MAX77693_TORCH_IOUT_STEP 15625
> +
> +#define MAX77693_FLASH_VSYS_MIN 2400
> +#define MAX77693_FLASH_VSYS_MAX 3400
> +#define MAX77693_FLASH_VSYS_STEP 33
> +
> +#define MAX77693_FLASH_VOUT_MIN 3300
> +#define MAX77693_FLASH_VOUT_MAX 5500
> +#define MAX77693_FLASH_VOUT_STEP 25
> +#define MAX77693_FLASH_VOUT_RMIN 0x0c
> +
> +#define MAX77693_LED_STATUS_FLASH_ON (1 << 3)
> +#define MAX77693_LED_STATUS_TORCH_ON (1 << 2)
> +
> +#define MAX77693_LED_FLASH_INT_FLED2_OPEN (1 << 0)
> +#define MAX77693_LED_FLASH_INT_FLED2_SHORT (1 << 1)
> +#define MAX77693_LED_FLASH_INT_FLED1_OPEN (1 << 2)
> +#define MAX77693_LED_FLASH_INT_FLED1_SHORT (1 << 3)
> +#define MAX77693_LED_FLASH_INT_OVER_CURRENT (1 << 4)
> +
> +#define MAX77693_MODE_OFF 0
> +#define MAX77693_MODE_FLASH1 (1 << 0)
> +#define MAX77693_MODE_FLASH2 (1 << 1)
> +#define MAX77693_MODE_TORCH1 (1 << 2)
> +#define MAX77693_MODE_TORCH2 (1 << 3)
> +#define MAX77693_MODE_FLASH_EXTERNAL1 (1 << 4)
> +#define MAX77693_MODE_FLASH_EXTERNAL2 (1 << 5)
> +
> +enum {
> + FLED1,
> + FLED2
> +};
> +
> +enum {
> + FLASH,
> + TORCH
> +};
> +
> +struct max77693_led {
> + struct regmap *regmap;
> + struct platform_device *pdev;
> + struct max77693_led_platform_data *pdata;
> + struct mutex lock;
> +
> + struct led_classdev_flash ldev1;
> + struct work_struct work1_brightness_set;
> + struct v4l2_flash *v4l2_flash1;
> +
> + struct led_classdev_flash ldev2;
> + struct work_struct work2_brightness_set;
> + struct v4l2_flash *v4l2_flash2;
> +
> + unsigned int torch1_brightness;
> + unsigned int torch2_brightness;
> + unsigned int flash1_timeout;
> + unsigned int flash2_timeout;

Could you use e.g. an array of structs instead? You could remove a lot
of redundant code operating on the other led.

> + unsigned int current_flash_timeout;
> + unsigned int mode_flags;
> + u8 torch_iout_reg;
> + bool iout_joint;
> +};

...

> +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;
> + int ret;
> +
> + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
> + if (!led)
> + return -ENOMEM;
> +
> + led->pdev = pdev;
> + led->regmap = iodev->regmap;
> + 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)
> + return ret;

goto err_register_led1;

> + if (led->iout_joint || p->fleds[FLED1]) {
> + ret = max77693_register_led1(led);
> + if (ret < 0)
> + goto err_register_led1;
> + }
> +
> + if (!led->iout_joint && p->fleds[FLED2]) {
> + ret = max77693_register_led2(led);
> + if (ret < 0)
> + goto err_register_led2;
> + }
> +
> + return 0;
> +
> +err_register_led2:
> + if (!p->fleds[FLED1])
> + goto err_register_led1;
> + v4l2_flash_release(led->v4l2_flash1);
> + led_classdev_flash_unregister(&led->ldev1);
> +err_register_led1:
> + mutex_destroy(&led->lock);
> +
> + return ret;
> +}

...

--
Kind regards,

Sakari Ailus
[email protected]

2014-07-28 13:41:12

by Grant Likely

[permalink] [raw]
Subject: Re: [PATCH/RFC v4 07/21] of: add of_node_ncmp wrapper

On Fri, 11 Jul 2014 16:04:10 +0200, Jacek Anaszewski <[email protected]> wrote:
> The wrapper for strnicmp is required for checking whether a node has
> expected prefix.
>
> Signed-off-by: Jacek Anaszewski <[email protected]>
> Acked-by: Kyungmin Park <[email protected]>
> Cc: Grant Likely <[email protected]>
> Cc: Benjamin Herrenschmidt <[email protected]>
> Cc: Michal Simek <[email protected]>
> ---
> include/linux/of.h | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/include/linux/of.h b/include/linux/of.h
> index 692b56c..9a53eea 100644
> --- a/include/linux/of.h
> +++ b/include/linux/of.h
> @@ -199,6 +199,7 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size)
> #define of_compat_cmp(s1, s2, l) strcasecmp((s1), (s2))
> #define of_prop_cmp(s1, s2) strcmp((s1), (s2))
> #define of_node_cmp(s1, s2) strcasecmp((s1), (s2))
> +#define of_node_ncmp(s1, s2, n) strnicmp((s1), (s2), (n))
> #endif

Don't forget to add an of_node_ncmp() define to
arch/sparc/include/asm/prom.h. Sparc has its own rules.

g.