aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/leds
diff options
context:
space:
mode:
authorcodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
committercodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
commitc6da2cfeb05178a11c6d062a06f8078150ee492f (patch)
treef3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/leds
parentc6d7c4dbff353eac7919342ae6b3299a378160a6 (diff)
downloadkernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip
kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz
kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2
samsung update 1
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/Kconfig47
-rw-r--r--drivers/leds/Makefile6
-rw-r--r--drivers/leds/leds-aat1290a.c247
-rw-r--r--drivers/leds/leds-an30259a.c876
-rw-r--r--drivers/leds/leds-lp5521.c294
-rw-r--r--drivers/leds/leds-max77693.c359
-rw-r--r--drivers/leds/leds-max8997.c313
-rw-r--r--drivers/leds/leds-spfcw043.c202
-rw-r--r--drivers/leds/ledtrig-sleep.c80
9 files changed, 2406 insertions, 18 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 713d43b..d63605c 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -5,6 +5,13 @@ config LEDS_GPIO_REGISTER
As this function is used by arch code it must not be compiled as a
module.
+config LEDS_SPFCW043
+ bool "LED support for the SPFCW043"
+ default n
+ help
+ This option enables support for the LEDs on the SPFCW043.
+
+
menuconfig NEW_LEDS
bool "LED Support"
help
@@ -211,6 +218,15 @@ config LEDS_LP3944
To compile this driver as a module, choose M here: the
module will be called leds-lp3944.
+config LEDS_AN30259A
+ tristate "LED driver for panasonic AN30259A RGB LED"
+ depends on I2C
+ help
+ This option enables support for AN30259A RGB LED chips
+ accessed via the I2C bus.
+ AN30259A has three channel LED driver. By synchronous clock
+ function, combined operation is possible.
+
config LEDS_LP5521
tristate "LED Support for N.S. LP5521 LED driver chip"
depends on LEDS_CLASS && I2C
@@ -399,6 +415,31 @@ config LEDS_ASIC3
cannot be used. This driver supports hardware blinking with an on+off
period from 62ms to 125s. Say Y to enable LEDs on the HP iPAQ hx4700.
+config LEDS_MAX77693
+ bool "LED support for the MAX77693"
+ depends on LEDS_CLASS
+ depends on MFD_MAX77693
+ default y
+ help
+ This option enables support for the LEDs on the MAX77693.
+
+config LEDS_MAX8997
+ tristate "MAX8997 LED support"
+ depends on LEDS_CLASS
+ default n
+ help
+ This option enables support for the LEDs on the Max8997.
+ You may control the hard drive or power LEDs on the Max8997
+ function. Using this driver can support the brightness of LED
+ by controling current of the Max8997.
+
+config LEDS_AAT1290A
+ bool "LED support for the AAT1290A"
+ depends on LEDS_CLASS
+ default n
+ help
+ This option enables support for the LEDs on the AAT1290A.
+
config LEDS_TRIGGERS
bool "LED Trigger support"
depends on LEDS_CLASS
@@ -466,6 +507,12 @@ config LEDS_TRIGGER_DEFAULT_ON
This allows LEDs to be initialised in the ON state.
If unsure, say Y.
+config LEDS_TRIGGER_SLEEP
+ tristate "LED Sleep Mode Trigger"
+ depends on LEDS_TRIGGERS && HAS_EARLYSUSPEND
+ help
+ This turns LEDs on when the screen is off but the cpu still running.
+
comment "iptables trigger is under Netfilter config (LED target)"
depends on LEDS_TRIGGERS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index bbfd2e3..e538739 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -43,6 +43,11 @@ 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_AAT1290A) += leds-aat1290a.o
+obj-$(CONFIG_LEDS_SPFCW043) += leds-spfcw043.o
+obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
+obj-$(CONFIG_LEDS_AN30259A) += leds-an30259a.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
@@ -54,3 +59,4 @@ obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o
obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o
obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o
obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o
+obj-$(CONFIG_LEDS_TRIGGER_SLEEP) += ledtrig-sleep.o
diff --git a/drivers/leds/leds-aat1290a.c b/drivers/leds/leds-aat1290a.c
new file mode 100644
index 0000000..8889360
--- /dev/null
+++ b/drivers/leds/leds-aat1290a.c
@@ -0,0 +1,247 @@
+/*
+ * LED driver for AAT1290A - leds-aat1290a.c
+ *
+ * Copyright (C) 2011 DongHyun Chang <dh348.chang@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/leds-aat1290a.h>
+int *aat1290a_ctrl;
+struct aat1290a_led_platform_data *led_pdata;
+
+struct class *flash_class;
+struct device *aat1290a_dev;
+
+static int aat1290a_setPower(int onoff, int level)
+{
+ int i;
+ if (led_pdata->status == STATUS_AVAILABLE) {
+ led_pdata->torch_en(0);
+ led_pdata->torch_set(0);
+ led_pdata->switch_sel(!onoff);
+ /* onoff = 0 - select aat1290a, onoff = 1 - select ISP*/
+ udelay(10);
+ if (onoff) {
+ led_pdata->torch_set(1);
+ for (i = 1; i < level; i++) {
+ udelay(1);
+ led_pdata->torch_set(0);
+ udelay(1);
+ led_pdata->torch_set(1);
+ }
+ }
+ } else {
+ LED_ERROR("GPIO is not available!");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int aat1290a_freeGpio(void)
+{
+ if (led_pdata->status == STATUS_AVAILABLE) {
+ if (led_pdata->freeGpio()) {
+ LED_ERROR("Can't free GPIO for led\n");
+ return -ENODEV;
+ }
+ led_pdata->status = STATUS_UNAVAILABLE;
+ } else {
+ LED_ERROR("GPIO already free!");
+ }
+
+ return 0;
+}
+
+static int aat1290a_setGpio(void)
+{
+ if (led_pdata->status != STATUS_AVAILABLE) {
+ if (led_pdata->setGpio()) {
+ LED_ERROR("Can't set GPIO for led\n");
+ return -ENODEV;
+ }
+ led_pdata->status = STATUS_AVAILABLE;
+ } else {
+ LED_ERROR("GPIO already set!");
+ }
+
+ return 0;
+}
+
+static int aat1290a_open(struct inode *inode, struct file *file)
+{
+ int ret;
+ led_pdata->brightness = TORCH_BRIGHTNESS_50; /*normal setting*/
+ led_pdata->status = STATUS_INVALID;
+
+ ret = aat1290a_setGpio();
+
+ file->private_data = (int *) aat1290a_ctrl;
+
+ return ret;
+}
+
+static int aat1290a_release(struct inode *inode, struct file *file)
+{
+ if (aat1290a_freeGpio()) {
+ LED_ERROR("aat1290a_freeGpio failed!\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+
+static long aat1290a_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int *ctrl = (int *)file->private_data;
+
+ if (!ctrl) {
+ LED_ERROR("invalid input data\n");
+ return -1;
+ }
+
+ switch (cmd) {
+ case IOCTL_AAT1290A_SET_BRIGHTNESS:
+ if (*ctrl < 0 || *ctrl > TORCH_BRIGHTNESS_0)
+ return -EINVAL;
+ led_pdata->brightness = *ctrl;
+ break;
+ case IOCTL_AAT1290A_GET_STATUS:
+ *ctrl = led_pdata->status;
+ break;
+ case IOCTL_AAT1290A_SET_POWER:
+ aat1290a_setPower(*ctrl, led_pdata->brightness);
+ break;
+ default:
+ LED_ERROR("invalid input data\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static ssize_t aat1290a_power(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int brightness = 0;
+
+ if (buf[0] < 0x30 || buf[0] > 0x39) {
+ LED_ERROR("data is wrong!");
+ return count;
+ } else if (buf[1] < 0x30 || buf[1] > 0x39)
+ brightness = (int) (buf[0] - 0x30);
+ else {
+ brightness = brightness + (int) (buf[0] - 0x30) * 10;
+ brightness = brightness + (int) (buf[1] - 0x30);
+ }
+
+ if (brightness < 0 || brightness >= TORCH_BRIGHTNESS_INVALID) {
+ LED_ERROR("brightness data is wrong! %d", brightness);
+ return count;
+ }
+
+ if (brightness == 0) {
+ aat1290a_setPower(0, 0);
+ /*
+ if (aat1290a_freeGpio()) {
+ LED_ERROR("aat1290a_freeGpio failed!\n");
+ return count;
+ }
+ */
+ } else {
+ if (aat1290a_setGpio()) {
+ LED_ERROR("aat1290a_setGpio failed!\n");
+ return count;
+ }
+ aat1290a_setPower(1, brightness);
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(flash_power, S_IWUSR|S_IWGRP|S_IROTH,
+ NULL, aat1290a_power);
+
+static const struct file_operations aat1290a_fops = {
+ .owner = THIS_MODULE,
+ .open = aat1290a_open,
+ .release = aat1290a_release,
+ .unlocked_ioctl = aat1290a_ioctl,
+};
+
+static struct miscdevice aat1290a_miscdev = {
+ .minor = 255,
+ .name = "aat1290a_led",
+ .fops = &aat1290a_fops,
+};
+
+static int aat1290a_led_probe(struct platform_device *pdev)
+{
+ LED_ERROR("Probe\n");
+ led_pdata =
+ (struct aat1290a_led_platform_data *) pdev->dev.platform_data;
+
+ if (misc_register(&aat1290a_miscdev)) {
+ LED_ERROR("Failed to register misc driver\n");
+ return -ENODEV;
+ }
+
+ flash_class = class_create(THIS_MODULE, "flash");
+ if (IS_ERR(flash_class))
+ LED_ERROR("Failed to create class(flash)!\n");
+ aat1290a_dev = device_create(flash_class, NULL, 0, NULL, "flash");
+ if (IS_ERR(aat1290a_dev))
+ LED_ERROR("Failed to create device(flash)!\n");
+
+ if (device_create_file(aat1290a_dev, &dev_attr_flash_power) < 0) {
+ LED_ERROR("failed to create device file, %s\n",
+ dev_attr_flash_power.attr.name);
+ }
+ led_pdata->initGpio();
+ aat1290a_setGpio();
+ return 0;
+}
+
+static int __devexit aat1290a_led_remove(struct platform_device *pdev)
+{
+ led_pdata->freeGpio();
+ misc_deregister(&aat1290a_miscdev);
+
+ device_remove_file(aat1290a_dev, &dev_attr_flash_power);
+ device_destroy(flash_class, 0);
+ class_destroy(flash_class);
+
+ return 0;
+}
+
+static struct platform_driver aat1290a_led_driver = {
+ .probe = aat1290a_led_probe,
+ .remove = __devexit_p(aat1290a_led_remove),
+ .driver = {
+ .name = "aat1290a-led",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init aat1290a_led_init(void)
+{
+ return platform_driver_register(&aat1290a_led_driver);
+}
+
+static void __exit aat1290a_led_exit(void)
+{
+ platform_driver_unregister(&aat1290a_led_driver);
+}
+
+module_init(aat1290a_led_init);
+module_exit(aat1290a_led_exit);
+
+MODULE_AUTHOR("DongHyun Chang <dh348.chang@samsung.com.com>");
+MODULE_DESCRIPTION("AAT1290A LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-an30259a.c b/drivers/leds/leds-an30259a.c
new file mode 100644
index 0000000..1b7e0af
--- /dev/null
+++ b/drivers/leds/leds-an30259a.c
@@ -0,0 +1,876 @@
+/*
+ * leds_an30259a.c - driver for panasonic AN30259A led control chip
+ *
+ * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved.
+ *
+ * Contact: Haeil Hyun <haeil.hyun@samsung.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/leds.h>
+#include <linux/leds-an30259a.h>
+#include <linux/workqueue.h>
+#include <linux/wakelock.h>
+
+/* AN30259A register map */
+#define AN30259A_REG_SRESET 0x00
+#define AN30259A_REG_LEDON 0x01
+#define AN30259A_REG_SEL 0x02
+
+#define AN30259A_REG_LED1CC 0x03
+#define AN30259A_REG_LED2CC 0x04
+#define AN30259A_REG_LED3CC 0x05
+
+#define AN30259A_REG_LED1SLP 0x06
+#define AN30259A_REG_LED2SLP 0x07
+#define AN30259A_REG_LED3SLP 0x08
+
+#define AN30259A_REG_LED1CNT1 0x09
+#define AN30259A_REG_LED1CNT2 0x0a
+#define AN30259A_REG_LED1CNT3 0x0b
+#define AN30259A_REG_LED1CNT4 0x0c
+
+#define AN30259A_REG_LED2CNT1 0x0d
+#define AN30259A_REG_LED2CNT2 0x0e
+#define AN30259A_REG_LED2CNT3 0x0f
+#define AN30259A_REG_LED2CNT4 0x10
+
+#define AN30259A_REG_LED3CNT1 0x11
+#define AN30259A_REG_LED3CNT2 0x12
+#define AN30259A_REG_LED3CNT3 0x13
+#define AN30259A_REG_LED3CNT4 0x14
+#define AN30259A_REG_MAX 0x15
+/* MASK */
+#define AN30259A_MASK_IMAX 0xc0
+#define AN30259A_MASK_DELAY 0xf0
+#define AN30259A_SRESET 0x01
+#define LED_SLOPE_MODE 0x10
+#define LED_ON 0x01
+
+#define DUTYMAX_MAX_VALUE 0x7f
+#define DUTYMIN_MIN_VALUE 0x00
+#define SLPTT_MAX_VALUE 7500
+
+#define AN30259A_TIME_UNIT 500
+
+#define LED_R_MASK 0x00ff0000
+#define LED_G_MASK 0x0000ff00
+#define LED_B_MASK 0x000000ff
+#define LED_R_SHIFT 16
+#define LED_G_SHIFT 8
+#define LED_IMAX_SHIFT 6
+#define AN30259A_CTN_RW_FLG 0x80
+
+#define LED_R_CURRENT 0x28
+#define LED_G_CURRENT 0x28
+#define LED_B_CURRENT 0x28
+#define LED_OFF 0x00
+
+#define MAX_NUM_LEDS 3
+
+static struct an30259_led_conf led_conf[] = {
+ {
+ .name = "led_r",
+ .brightness = LED_OFF,
+ .max_brightness = 0x3C,
+ .flags = 0,
+ }, {
+ .name = "led_g",
+ .brightness = LED_OFF,
+ .max_brightness = 0x3C,
+ .flags = 0,
+ }, {
+ .name = "led_b",
+ .brightness = LED_OFF,
+ .max_brightness = 0x3C,
+ .flags = 0,
+ }
+};
+
+enum an30259a_led_enum {
+ LED_R,
+ LED_G,
+ LED_B,
+};
+
+enum an30259a_pattern {
+ PATTERN_OFF,
+ CHARGING,
+ CHARGING_ERR,
+ MISSED_NOTI,
+ LOW_BATTERY,
+ FULLY_CHARGED,
+ POWERING,
+};
+
+struct an30259a_led {
+ u8 channel;
+ u8 brightness;
+ struct led_classdev cdev;
+ struct work_struct brightness_work;
+ unsigned long delay_on_time_ms;
+ unsigned long delay_off_time_ms;
+};
+
+struct an30259a_data {
+ struct i2c_client *client;
+ struct mutex mutex;
+ struct an30259a_led leds[MAX_NUM_LEDS];
+ u8 shadow_reg[AN30259A_REG_MAX];
+};
+
+struct i2c_client *b_client;
+
+#define SEC_LED_SPECIFIC
+#define LED_DEEP_DEBUG
+
+#ifdef SEC_LED_SPECIFIC
+extern struct class *sec_class;
+struct device *led_dev;
+/*path : /sys/class/sec/led/led_pattern*/
+/*path : /sys/class/sec/led/led_blink*/
+/*path : /sys/class/leds/led_r/brightness*/
+/*path : /sys/class/leds/led_g/brightness*/
+/*path : /sys/class/leds/led_b/brightness*/
+#endif
+
+static void leds_on(enum an30259a_led_enum led, bool on, bool slopemode,
+ u8 ledcc);
+
+static inline struct an30259a_led *cdev_to_led(struct led_classdev *cdev)
+{
+ return container_of(cdev, struct an30259a_led, cdev);
+}
+
+#ifdef LED_DEEP_DEBUG
+static void an30259a_debug(struct i2c_client *client)
+{
+ struct an30259a_data *data = i2c_get_clientdata(client);
+ int ret;
+ u8 buff[21] = {0,};
+ ret = i2c_smbus_read_i2c_block_data(client,
+ AN30259A_REG_SRESET|AN30259A_CTN_RW_FLG,
+ sizeof(buff), buff);
+ if (ret != sizeof(buff)) {
+ dev_err(&data->client->dev,
+ "%s: failure on i2c_smbus_read_i2c_block_data\n",
+ __func__);
+ }
+ print_hex_dump(KERN_ERR, "an30259a: ",
+ DUMP_PREFIX_OFFSET, 32, 1, buff,
+ sizeof(buff), false);
+}
+#endif
+
+static int leds_i2c_write_all(struct i2c_client *client)
+{
+ struct an30259a_data *data = i2c_get_clientdata(client);
+ int ret;
+
+ /*we need to set all the configs setting first, then LEDON later*/
+ mutex_lock(&data->mutex);
+ ret = i2c_smbus_write_i2c_block_data(client,
+ AN30259A_REG_SEL | AN30259A_CTN_RW_FLG,
+ AN30259A_REG_MAX - AN30259A_REG_SEL,
+ &data->shadow_reg[AN30259A_REG_SEL]);
+ if (ret < 0) {
+ dev_err(&client->adapter->dev,
+ "%s: failure on i2c block write\n",
+ __func__);
+ goto exit;
+ }
+ ret = i2c_smbus_write_byte_data(client, AN30259A_REG_LEDON,
+ data->shadow_reg[AN30259A_REG_LEDON]);
+ if (ret < 0) {
+ dev_err(&client->adapter->dev,
+ "%s: failure on i2c byte write\n",
+ __func__);
+ goto exit;
+ }
+ mutex_unlock(&data->mutex);
+ return 0;
+
+exit:
+ mutex_unlock(&data->mutex);
+ return ret;
+}
+
+void an30259a_set_brightness(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct an30259a_led *led = cdev_to_led(cdev);
+ led->brightness = (u8)brightness;
+ schedule_work(&led->brightness_work);
+}
+
+static void an30259a_led_brightness_work(struct work_struct *work)
+{
+ struct i2c_client *client = b_client;
+ struct an30259a_led *led = container_of(work,
+ struct an30259a_led, brightness_work);
+ leds_on(led->channel, true, false, led->brightness);
+ leds_i2c_write_all(client);
+}
+
+/*
+ * leds_set_slope_mode() sets correct values to corresponding shadow registers.
+ * led: stands for LED_R or LED_G or LED_B.
+ * delay: represents for starting delay time in multiple of .5 second.
+ * dutymax: led at slope lighting maximum PWM Duty setting.
+ * dutymid: led at slope lighting middle PWM Duty setting.
+ * dutymin: led at slope lighting minimum PWM Duty Setting.
+ * slptt1: total time of slope operation 1 and 2, in multiple of .5 second.
+ * slptt2: total time of slope operation 3 and 4, in multiple of .5 second.
+ * dt1: detention time at each step in slope operation 1, in multiple of 4ms.
+ * dt2: detention time at each step in slope operation 2, in multiple of 4ms.
+ * dt3: detention time at each step in slope operation 3, in multiple of 4ms.
+ * dt4: detention time at each step in slope operation 4, in multiple of 4ms.
+ */
+static void leds_set_slope_mode(struct i2c_client *client,
+ enum an30259a_led_enum led, u8 delay,
+ u8 dutymax, u8 dutymid, u8 dutymin,
+ u8 slptt1, u8 slptt2,
+ u8 dt1, u8 dt2, u8 dt3, u8 dt4)
+{
+ struct an30259a_data *data = i2c_get_clientdata(client);
+
+ data->shadow_reg[AN30259A_REG_LED1CNT1 + led * 4] =
+ dutymax << 4 | dutymid;
+ data->shadow_reg[AN30259A_REG_LED1CNT2 + led * 4] =
+ delay << 4 | dutymin;
+ data->shadow_reg[AN30259A_REG_LED1CNT3 + led * 4] = dt2 << 4 | dt1;
+ data->shadow_reg[AN30259A_REG_LED1CNT4 + led * 4] = dt4 << 4 | dt3;
+ data->shadow_reg[AN30259A_REG_LED1SLP + led] = slptt2 << 4 | slptt1;
+}
+
+static void leds_on(enum an30259a_led_enum led, bool on, bool slopemode,
+ u8 ledcc)
+{
+ struct an30259a_data *data = i2c_get_clientdata(b_client);
+
+ if (on)
+ data->shadow_reg[AN30259A_REG_LEDON] |= LED_ON << led;
+ else {
+ data->shadow_reg[AN30259A_REG_LEDON] &= ~(LED_ON << led);
+ data->shadow_reg[AN30259A_REG_LED1CNT2 + led * 4] &=
+ ~AN30259A_MASK_DELAY;
+ }
+ if (slopemode)
+ data->shadow_reg[AN30259A_REG_LEDON] |= LED_SLOPE_MODE << led;
+ else
+ data->shadow_reg[AN30259A_REG_LEDON] &=
+ ~(LED_SLOPE_MODE << led);
+
+ data->shadow_reg[AN30259A_REG_LED1CC + led] = ledcc;
+}
+
+static int leds_set_imax(struct i2c_client *client, u8 imax)
+{
+ int ret;
+ struct an30259a_data *data = i2c_get_clientdata(client);
+
+ data->shadow_reg[AN30259A_REG_SEL] &= ~AN30259A_MASK_IMAX;
+ data->shadow_reg[AN30259A_REG_SEL] |= imax << LED_IMAX_SHIFT;
+
+ ret = i2c_smbus_write_byte_data(client, AN30259A_REG_SEL,
+ data->shadow_reg[AN30259A_REG_SEL]);
+ if (ret < 0) {
+ dev_err(&client->adapter->dev,
+ "%s: failure on i2c write\n",
+ __func__);
+ }
+ return 0;
+}
+
+#ifdef SEC_LED_SPECIFIC
+static void an30259a_reset_register_work(struct work_struct *work)
+{
+ int retval;
+ struct i2c_client *client;
+ client = b_client;
+
+ leds_on(LED_R, false, false, 0);
+ leds_on(LED_G, false, false, 0);
+ leds_on(LED_B, false, false, 0);
+
+ retval = leds_i2c_write_all(client);
+ if (retval)
+ printk(KERN_WARNING "leds_i2c_write_all failed\n");
+}
+
+static void an30259a_start_led_pattern(int mode)
+{
+ int retval;
+
+ struct i2c_client *client;
+ struct work_struct *reset = 0;
+ client = b_client;
+
+ if (mode > POWERING)
+ return;
+ /* Set all LEDs Off */
+ an30259a_reset_register_work(reset);
+ if (mode == LED_OFF)
+ return;
+
+ switch (mode) {
+ /* leds_set_slope_mode(client, LED_SEL, DELAY, MAX, MID, MIN,
+ SLPTT1, SLPTT2, DT1, DT2, DT3, DT4) */
+ case CHARGING:
+ leds_on(LED_R, true, false, LED_R_CURRENT);
+ leds_on(LED_G, false, false, LED_OFF);
+ leds_on(LED_B, false, false, LED_OFF);
+ break;
+
+ case CHARGING_ERR:
+ leds_on(LED_R, true, true, LED_R_CURRENT);
+ leds_on(LED_G, false, false, LED_OFF);
+ leds_on(LED_B, false, false, LED_OFF);
+ leds_set_slope_mode(client, LED_R,
+ 1, 15, 15, 0, 1, 1, 0, 0, 0, 0);
+ break;
+
+ case MISSED_NOTI:
+ leds_on(LED_R, false, false, LED_OFF);
+ leds_on(LED_G, false, false, LED_OFF);
+ leds_on(LED_B, true, true, LED_B_CURRENT);
+ leds_set_slope_mode(client, LED_B,
+ 10, 15, 15, 0, 1, 10, 0, 0, 0, 0);
+ break;
+
+ case LOW_BATTERY:
+ leds_on(LED_R, true, true, LED_R_CURRENT);
+ leds_on(LED_G, false, false, LED_OFF);
+ leds_on(LED_B, false, false, LED_OFF);
+ leds_set_slope_mode(client, LED_R,
+ 10, 15, 15, 0, 1, 10, 0, 0, 0, 0);
+ break;
+
+ case FULLY_CHARGED:
+ leds_on(LED_R, false, false, LED_OFF);
+ leds_on(LED_G, true, false, LED_G_CURRENT);
+ leds_on(LED_B, false, false, LED_OFF);
+ break;
+
+ case POWERING:
+ leds_on(LED_R, false, false, 0);
+ leds_on(LED_G, true, true, LED_G_CURRENT);
+ leds_on(LED_B, true, true, LED_B_CURRENT);
+ leds_set_slope_mode(client, LED_G,
+ 0, 8, 4, 1, 2, 2, 3, 3, 3, 3);
+ leds_set_slope_mode(client, LED_B,
+ 0, 15, 14, 12, 2, 2, 7, 7, 7, 7);
+ break;
+
+ default:
+ return;
+ break;
+ }
+ retval = leds_i2c_write_all(client);
+ if (retval)
+ printk(KERN_WARNING "leds_i2c_write_all failed\n");
+}
+
+static void an30259a_set_led_blink(enum an30259a_led_enum led,
+ unsigned int delay_on_time,
+ unsigned int delay_off_time,
+ u8 brightness)
+{
+ struct i2c_client *client;
+ client = b_client;
+
+ if (delay_on_time > SLPTT_MAX_VALUE)
+ delay_on_time = SLPTT_MAX_VALUE;
+ if (delay_off_time > SLPTT_MAX_VALUE)
+ delay_off_time = SLPTT_MAX_VALUE;
+
+ if (delay_off_time == LED_OFF) {
+ leds_on(led, true, false, brightness);
+ if (brightness == LED_OFF)
+ leds_on(led, false, false, brightness);
+ return;
+ } else if (brightness == LED_OFF) {
+ leds_on(led, false, false, brightness);
+ return;
+ } else
+ leds_on(led, true, true, brightness);
+
+ leds_set_slope_mode(client, led, 0, 15, 15, 0,
+ (delay_on_time + AN30259A_TIME_UNIT - 1) /
+ AN30259A_TIME_UNIT,
+ (delay_off_time + AN30259A_TIME_UNIT - 1) /
+ AN30259A_TIME_UNIT,
+ 0, 0, 0, 0);
+}
+
+static ssize_t store_an30259a_led_br_lev(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int retval;
+ unsigned long brightness_lev;
+ struct i2c_client *client;
+ struct an30259a_data *data = dev_get_drvdata(dev);
+ client = b_client;
+
+ retval = strict_strtoul(buf, 16, &brightness_lev);
+ if (retval != 0) {
+ dev_err(&data->client->dev, "fail to get led_pattern mode.\n");
+ return count;
+ }
+
+ leds_set_imax(client, brightness_lev);
+
+ return count;
+}
+
+static ssize_t store_an30259a_led_pattern(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int retval;
+ unsigned long mode;
+ struct an30259a_data *data = dev_get_drvdata(dev);
+
+ retval = strict_strtoul(buf, 16, &mode);
+ if (retval != 0) {
+ dev_err(&data->client->dev, "fail to get led_pattern mode.\n");
+ return count;
+ }
+
+ an30259a_start_led_pattern(mode);
+ printk(KERN_DEBUG "led pattern : %lu is activated\n", mode);
+
+ return count;
+}
+
+static ssize_t store_an30259a_led_blink(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int retval;
+ unsigned int led_brightness = 0;
+ unsigned int delay_on_time = 0;
+ unsigned int delay_off_time = 0;
+ struct an30259a_data *data = dev_get_drvdata(dev);
+ u8 led_r_brightness = 0;
+ u8 led_g_brightness = 0;
+ u8 led_b_brightness = 0;
+
+ retval = sscanf(buf, "0x%x %d %d", &led_brightness,
+ &delay_on_time, &delay_off_time);
+
+ if (retval == 0) {
+ dev_err(&data->client->dev, "fail to get led_blink value.\n");
+ return count;
+ }
+ /*Reset an30259a*/
+ an30259a_start_led_pattern(LED_OFF);
+
+ /*Set LED blink mode*/
+ led_r_brightness = ((u32)led_brightness & LED_R_MASK)
+ >> LED_R_SHIFT;
+ led_g_brightness = ((u32)led_brightness & LED_G_MASK)
+ >> LED_G_SHIFT;
+ led_b_brightness = ((u32)led_brightness & LED_B_MASK);
+
+ an30259a_set_led_blink(LED_R, delay_on_time,
+ delay_off_time, led_r_brightness);
+ an30259a_set_led_blink(LED_G, delay_on_time,
+ delay_off_time, led_g_brightness);
+ an30259a_set_led_blink(LED_B, delay_on_time,
+ delay_off_time, led_b_brightness);
+
+ leds_i2c_write_all(data->client);
+
+ printk(KERN_DEBUG "led_blink is called\n");
+
+ return count;
+}
+
+
+static ssize_t store_led_r(struct device *dev,
+ struct device_attribute *devattr, const char *buf, size_t count)
+{
+ struct an30259a_data *data = dev_get_drvdata(dev);
+ char buff[10] = {0,};
+ int cnt, ret;
+ u8 brightness;
+
+ cnt = count;
+ cnt = (buf[cnt-1] == '\n') ? cnt-1 : cnt;
+ memcpy(buff, buf, cnt);
+ buff[cnt] = '\0';
+
+ ret = kstrtou8(buff, 0, &brightness);
+ if (ret != 0) {
+ dev_err(&data->client->dev, "fail to get brightness.\n");
+ goto out;
+ }
+
+ if (brightness == 0)
+ leds_on(LED_R, false, false, 0);
+ else
+ leds_on(LED_R, true, false, brightness);
+
+ leds_i2c_write_all(data->client);
+ an30259a_debug(data->client);
+out:
+ return count;
+}
+
+static ssize_t store_led_g(struct device *dev,
+ struct device_attribute *devattr, const char *buf, size_t count)
+{
+ struct an30259a_data *data = dev_get_drvdata(dev);
+ char buff[10] = {0,};
+ int cnt, ret;
+ u8 brightness;
+
+ cnt = count;
+ cnt = (buf[cnt-1] == '\n') ? cnt-1 : cnt;
+ memcpy(buff, buf, cnt);
+ buff[cnt] = '\0';
+
+ ret = kstrtou8(buff, 0, &brightness);
+ if (ret != 0) {
+ dev_err(&data->client->dev, "fail to get brightness.\n");
+ goto out;
+ }
+
+ if (brightness == 0)
+ leds_on(LED_G, false, false, 0);
+ else
+ leds_on(LED_G, true, false, brightness);
+
+ leds_i2c_write_all(data->client);
+ an30259a_debug(data->client);
+out:
+ return count;
+}
+
+static ssize_t store_led_b(struct device *dev,
+ struct device_attribute *devattr, const char *buf, size_t count)
+{
+ struct an30259a_data *data = dev_get_drvdata(dev);
+ char buff[10] = {0,};
+ int cnt, ret;
+ u8 brightness;
+
+ cnt = count;
+ cnt = (buf[cnt-1] == '\n') ? cnt-1 : cnt;
+ memcpy(buff, buf, cnt);
+ buff[cnt] = '\0';
+
+ ret = kstrtou8(buff, 0, &brightness);
+ if (ret != 0) {
+ dev_err(&data->client->dev, "fail to get brightness.\n");
+ goto out;
+ }
+
+ if (brightness == 0)
+ leds_on(LED_B, false, false, 0);
+ else
+ leds_on(LED_B, true, false, brightness);
+
+ leds_i2c_write_all(data->client);
+ an30259a_debug(data->client);
+out:
+ return count;
+
+}
+#endif
+
+/* Added for led common class */
+static ssize_t led_delay_on_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct an30259a_led *led = cdev_to_led(led_cdev);
+
+ return sprintf(buf, "%lu\n", led->delay_on_time_ms);
+}
+
+static ssize_t led_delay_on_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct an30259a_led *led = cdev_to_led(led_cdev);
+ unsigned long time;
+
+ if (strict_strtoul(buf, 0, &time))
+ return -EINVAL;
+
+ led->delay_on_time_ms = (int)time;
+ return len;
+}
+
+static ssize_t led_delay_off_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct an30259a_led *led = cdev_to_led(led_cdev);
+
+ return sprintf(buf, "%lu\n", led->delay_off_time_ms);
+}
+
+static ssize_t led_delay_off_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct an30259a_led *led = cdev_to_led(led_cdev);
+ unsigned long time;
+
+ if (strict_strtoul(buf, 0, &time))
+ return -EINVAL;
+
+ led->delay_off_time_ms = (int)time;
+
+ return len;
+}
+
+static ssize_t led_blink_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct an30259a_led *led = cdev_to_led(led_cdev);
+ unsigned long blink_set;
+
+ if (strict_strtoul(buf, 0, &blink_set))
+ return -EINVAL;
+
+ if (!blink_set) {
+ led->delay_on_time_ms = LED_OFF;
+ an30259a_set_brightness(led_cdev, LED_OFF);
+ }
+
+ led_blink_set(led_cdev,
+ &led->delay_on_time_ms, &led->delay_off_time_ms);
+
+ return len;
+}
+
+/* permission for sysfs node */
+static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
+static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);
+static DEVICE_ATTR(blink, 0644, NULL, led_blink_store);
+
+#ifdef SEC_LED_SPECIFIC
+/* below nodes is SAMSUNG specific nodes */
+static DEVICE_ATTR(led_r, 0664, NULL, store_led_r);
+static DEVICE_ATTR(led_g, 0664, NULL, store_led_g);
+static DEVICE_ATTR(led_b, 0664, NULL, store_led_b);
+/* led_pattern node permission is 664 */
+/* To access sysfs node from other groups */
+static DEVICE_ATTR(led_pattern, 0664, NULL, \
+ store_an30259a_led_pattern);
+static DEVICE_ATTR(led_blink, 0664, NULL, \
+ store_an30259a_led_blink);
+static DEVICE_ATTR(led_br_lev, 0664, NULL, \
+ store_an30259a_led_br_lev);
+
+#endif
+static struct attribute *led_class_attrs[] = {
+ &dev_attr_delay_on.attr,
+ &dev_attr_delay_off.attr,
+ &dev_attr_blink.attr,
+ NULL,
+};
+
+static struct attribute_group common_led_attr_group = {
+ .attrs = led_class_attrs,
+};
+
+#ifdef SEC_LED_SPECIFIC
+static struct attribute *sec_led_attributes[] = {
+ &dev_attr_led_r.attr,
+ &dev_attr_led_g.attr,
+ &dev_attr_led_b.attr,
+ &dev_attr_led_pattern.attr,
+ &dev_attr_led_blink.attr,
+ &dev_attr_led_br_lev.attr,
+ NULL,
+};
+
+static struct attribute_group sec_led_attr_group = {
+ .attrs = sec_led_attributes,
+};
+#endif
+
+static int __devinit an30259a_initialize(struct i2c_client *client,
+ struct an30259a_led *led, int channel)
+{
+ struct an30259a_data *data = i2c_get_clientdata(client);
+ struct device *dev = &client->dev;
+ int ret;
+
+ /* reset an30259a*/
+ ret = i2c_smbus_write_byte_data(client, AN30259A_REG_SRESET,
+ AN30259A_SRESET);
+ if (ret < 0) {
+ dev_err(&client->adapter->dev,
+ "%s: failure on i2c write (reg = 0x%2x)\n",
+ __func__, AN30259A_REG_SRESET);
+ return ret;
+ }
+ ret = i2c_smbus_read_i2c_block_data(client,
+ AN30259A_REG_SRESET | AN30259A_CTN_RW_FLG,
+ AN30259A_REG_MAX, data->shadow_reg);
+ if (ret < 0) {
+ dev_err(&client->adapter->dev,
+ "%s: failure on i2c read block(ledxcc)\n",
+ __func__);
+ return ret;
+ }
+ led->channel = channel;
+ led->cdev.brightness_set = an30259a_set_brightness;
+ led->cdev.name = led_conf[channel].name;
+ led->cdev.brightness = led_conf[channel].brightness;
+ led->cdev.max_brightness = led_conf[channel].max_brightness;
+ led->cdev.flags = led_conf[channel].flags;
+
+ ret = led_classdev_register(dev, &led->cdev);
+
+ if (ret < 0) {
+ dev_err(dev, "can not register led channel : %d\n", channel);
+ return ret;
+ }
+
+ ret = sysfs_create_group(&led->cdev.dev->kobj,
+ &common_led_attr_group);
+
+ if (ret < 0) {
+ dev_err(dev, "can not register sysfs attribute\n");
+ return ret;
+ }
+
+ leds_set_imax(client, 0x00);
+
+ return 0;
+}
+
+
+static int __devinit an30259a_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct an30259a_data *data;
+ int ret, i;
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "need I2C_FUNC_I2C.\n");
+ return -ENODEV;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&client->adapter->dev,
+ "failed to allocate driver data.\n");
+ return -ENOMEM;
+ }
+
+ i2c_set_clientdata(client, data);
+ data->client = client;
+ b_client = client;
+
+ mutex_init(&data->mutex);
+ /* initialize LED */
+ for (i = 0; i < MAX_NUM_LEDS; i++) {
+
+ ret = an30259a_initialize(client, &data->leds[i], i);
+
+ if (ret < 0) {
+ dev_err(&client->adapter->dev, "failure on initialization\n");
+ goto exit;
+ }
+ INIT_WORK(&(data->leds[i].brightness_work),
+ an30259a_led_brightness_work);
+ }
+
+#ifdef SEC_LED_SPECIFIC
+ led_dev = device_create(sec_class, NULL, 0, data, "led");
+ if (IS_ERR(led_dev)) {
+ dev_err(&client->dev,
+ "Failed to create device for samsung specific led\n");
+ ret = -ENODEV;
+ }
+ ret = sysfs_create_group(&led_dev->kobj, &sec_led_attr_group);
+ if (ret)
+ dev_err(&client->dev,
+ "Failed to create sysfs group for samsung specific led\n");
+#endif
+ return ret;
+exit:
+ mutex_destroy(&data->mutex);
+ kfree(data);
+ return ret;
+}
+
+static int __devexit an30259a_remove(struct i2c_client *client)
+{
+ struct an30259a_data *data = i2c_get_clientdata(client);
+ int i;
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+#ifdef SEC_LED_SPECIFIC
+ sysfs_remove_group(&led_dev->kobj, &sec_led_attr_group);
+#endif
+ for (i = 0; i < MAX_NUM_LEDS; i++) {
+ sysfs_remove_group(&data->leds[i].cdev.dev->kobj,
+ &common_led_attr_group);
+ led_classdev_unregister(&data->leds[i].cdev);
+ cancel_work_sync(&data->leds[i].brightness_work);
+ }
+ mutex_destroy(&data->mutex);
+ kfree(data);
+ return 0;
+}
+
+static struct i2c_device_id an30259a_id[] = {
+ {"an30259a", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, an30259a_id);
+
+static struct i2c_driver an30259a_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "an30259a",
+ },
+ .id_table = an30259a_id,
+ .probe = an30259a_probe,
+ .remove = __devexit_p(an30259a_remove),
+};
+
+static int __init an30259a_init(void)
+{
+ return i2c_add_driver(&an30259a_i2c_driver);
+}
+
+static void __exit an30259a_exit(void)
+{
+ i2c_del_driver(&an30259a_i2c_driver);
+}
+
+module_init(an30259a_init);
+module_exit(an30259a_exit);
+
+MODULE_DESCRIPTION("AN30259A LED driver");
+MODULE_AUTHOR("Haeil Hyun <haeil.hyun@samsung.com");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
index cc1dc48..58a5dee 100644
--- a/drivers/leds/leds-lp5521.c
+++ b/drivers/leds/leds-lp5521.c
@@ -5,6 +5,8 @@
*
* Contact: Samu Onkalo <samu.p.onkalo@nokia.com>
*
+ * Updated: Milo(Woogyom) Kim <milo.kim@ti.com>
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
@@ -35,6 +37,11 @@
#include <linux/workqueue.h>
#include <linux/slab.h>
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#endif
+
#define LP5521_PROGRAM_LENGTH 32 /* in bytes */
#define LP5521_MAX_LEDS 3 /* Maximum number of LEDs */
@@ -82,21 +89,13 @@
#define LP5521_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */
#define LP5521_EXEC_RUN 0x2A
-/* Bits in CONFIG register */
-#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */
-#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */
-#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */
-#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */
-#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */
-#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */
-#define LP5521_R_TO_BATT 4 /* R out: 0 = CP, 1 = Vbat */
-#define LP5521_CLK_SRC_EXT 0 /* Ext-clk source (CLK_32K) */
-#define LP5521_CLK_INT 1 /* Internal clock */
-#define LP5521_CLK_AUTO 2 /* Automatic clock selection */
-
/* Status */
#define LP5521_EXT_CLK_USED 0x08
+#define LED_DEBUG 1
+
+extern struct class *sec_class;
+
struct lp5521_engine {
int id;
u8 mode;
@@ -114,6 +113,13 @@ struct lp5521_led {
u8 brightness;
};
+#ifdef CONFIG_DEBUG_FS
+struct dbg_dentry {
+ struct dentry *dir;
+ struct dentry *reg;
+};
+#endif
+
struct lp5521_chip {
struct lp5521_platform_data *pdata;
struct mutex lock; /* Serialize control */
@@ -122,8 +128,18 @@ struct lp5521_chip {
struct lp5521_led leds[LP5521_MAX_LEDS];
u8 num_channels;
u8 num_leds;
+#ifdef CONFIG_DEBUG_FS
+ struct dbg_dentry dd;
+#endif
+ struct device *led_dev;
};
+static struct lp5521_chip *g_chip;
+
+#ifdef LED_DEBUG
+static struct i2c_client *g_client;
+#endif
+
static inline struct lp5521_led *cdev_to_led(struct led_classdev *cdev)
{
return container_of(cdev, struct lp5521_led, cdev);
@@ -141,6 +157,8 @@ static inline struct lp5521_chip *led_to_lp5521(struct lp5521_led *led)
leds[led->id]);
}
+void lp5521_led_brightness(u8 channel, u8 brightness);
+
static void lp5521_led_brightness_work(struct work_struct *work);
static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value)
@@ -237,6 +255,7 @@ static void lp5521_init_engine(struct lp5521_chip *chip)
static int lp5521_configure(struct i2c_client *client)
{
struct lp5521_chip *chip = i2c_get_clientdata(client);
+ u8 cfg = chip->pdata->update_config;
int ret;
lp5521_init_engine(chip);
@@ -244,9 +263,8 @@ static int lp5521_configure(struct i2c_client *client)
/* Set all PWMs to direct control mode */
ret = lp5521_write(client, LP5521_REG_OP_MODE, 0x3F);
- /* Enable auto-powersave, set charge pump to auto, red to battery */
- ret |= lp5521_write(client, LP5521_REG_CONFIG,
- LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT);
+ if (cfg)
+ ret |= lp5521_write(client, LP5521_REG_CONFIG, cfg);
/* Initialize all channels PWM to zero -> leds off */
ret |= lp5521_write(client, LP5521_REG_R_PWM, 0);
@@ -574,6 +592,218 @@ static const struct attribute_group lp5521_group = {
.attrs = lp5521_attributes,
};
+#ifdef CONFIG_DEBUG_FS
+static int lp5521_dbg_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t lp5521_help_register(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ char buf[320];
+ unsigned int len;
+ const char *help = "\n How to read/write LP5521 registers\n\n";
+/* (example) To read 0x00 register,\n \
+ echo 0x00 r > /sys/kernel/debug/lp5521/registers\n \
+ To write 0xff into 0x1 address,\n \
+ echo 0x00 0xff w > /sys/kernel/debug/lp5521/registers \n \
+ To dump values from 0x00 to 0x0a address,\n \
+ echo 0x00 0x0a d > /sys/kernel/debug/lp5521/registers\n";
+*/
+ len = snprintf(buf, sizeof(buf), "%s\n", help);
+ if (len > sizeof(buf))
+ len = sizeof(buf);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, len);
+}
+
+static char *lp5521_parse_register_cmd(const char *cmd, u8 *byte)
+{
+ char tmp[10];
+ char *blank;
+ unsigned long arg;
+
+ blank = strchr(cmd, ' ');
+ memset(tmp, 0x0, sizeof(tmp));
+ memcpy(tmp, cmd, blank - cmd);
+
+ if (strict_strtol(tmp, 16, &arg) < 0)
+ return NULL;
+
+ *byte = arg;
+ return blank;
+}
+
+static ssize_t lp5521_ctrl_register(struct file *file,
+ const char __user *userbuf, size_t count,
+ loff_t *ppos)
+{
+ char mode, buf[20];
+ char *pos, *pos2;
+ u8 i, arg1, arg2, val = 0;
+ struct lp5521_chip *chip = file->private_data;
+ struct i2c_client *cl = chip->client;
+
+ if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+ return -EFAULT;
+
+ mode = buf[count - 2];
+ switch (mode) {
+ case 'r':
+ if (!lp5521_parse_register_cmd(buf, &arg1))
+ return -EINVAL;
+
+ lp5521_read(cl, arg1, &val);
+ dev_info(&cl->dev, "Read [0x%.2x] = 0x%.2x\n", arg1, val);
+ break;
+ case 'w':
+ pos = lp5521_parse_register_cmd(buf, &arg1);
+ if (!pos)
+ return -EINVAL;
+ pos2 = lp5521_parse_register_cmd(pos + 1, &arg2);
+ if (!pos2)
+ return -EINVAL;
+
+ lp5521_write(cl, arg1, arg2);
+ dev_info(&cl->dev, "Written [0x%.2x] = 0x%.2x\n", arg1, arg2);
+ break;
+ case 'd':
+ pos = lp5521_parse_register_cmd(buf, &arg1);
+ if (!pos)
+ return -EINVAL;
+ pos2 = lp5521_parse_register_cmd(pos + 1, &arg2);
+ if (!pos2)
+ return -EINVAL;
+
+ for (i = arg1; i <= arg2; i++) {
+ lp5521_read(cl, i, &val);
+ dev_info(&cl->dev, "Read [0x%.2x] = 0x%.2x\n", i, val);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return count;
+}
+
+static const struct file_operations fops_registers = {
+ .open = lp5521_dbg_open,
+ .read = lp5521_help_register,
+ .write = lp5521_ctrl_register,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static void lp5521_create_debugfs(struct lp5521_chip *chip)
+{
+ struct dbg_dentry *dd = &chip->dd;
+
+ dd->dir = debugfs_create_dir("lp5521", NULL);
+ dd->reg = debugfs_create_file("registers", S_IWUSR | S_IRUGO,
+ dd->dir, chip, &fops_registers);
+}
+
+static void lp5521_remove_debugfs(struct lp5521_chip *chip)
+{
+ struct dbg_dentry *dd = &chip->dd;
+
+ debugfs_remove(dd->reg);
+ debugfs_remove(dd->dir);
+}
+#else
+static inline void lp5521_create_debugfs(struct lp5521_chip *chip)
+{
+ return;
+}
+
+static inline void lp5521_remove_debugfs(struct lp5521_chip *chip)
+{
+ return;
+}
+#endif
+
+#define LP5521_ENABLE_DEFAULT \
+ (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM)
+#define LP5521_ENABLE_RUN_PROGRAM \
+ (LP5521_ENABLE_DEFAULT | LP5521_EXEC_RUN)
+
+static void lp5521_program_pattern(int mode, struct lp5521_chip *chip)
+{
+ struct i2c_client *cl = chip->client;
+ int i;
+ u8 mode1_red[] = {
+ 0x40, 0xED, 0x41, 0x00, /* 15.6ms on with red 0xff */
+ 0x40, 0x00, 0x46, 0x00, /* 100ms off */
+ 0x40, 0xED, 0x41, 0x00, /* 15.6ms on with red 0xff */
+ 0x40, 0x00, 0x60, 0x00, /* 500ms off */
+ 0xa4, 0x87, /* 4500ms off = 500ms off x 9 times */
+ };
+
+ u8 mode1_green[] = {
+ 0x40, 0x59, 0x41, 0x00, /* 15.6ms on with green 0x25 */
+ 0x40, 0x00, 0x46, 0x00, /* 100ms off */
+ 0x40, 0x59, 0x41, 0x00, /* 15.6ms on with green 0x25 */
+ 0x40, 0x00, 0x60, 0x00, /* 500ms off */
+ 0xa4, 0x87, /* 4500ms off = 500ms off x 9 times */
+ };
+
+ if (mode == 0) {
+ lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_DEFAULT);
+ lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
+ } else if (mode == 1) {
+ lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_LOAD);
+
+ for (i = 0 ; i < ARRAY_SIZE(mode1_red) ; i++)
+ lp5521_write(cl, LP5521_REG_R_PROG_MEM + i,
+ mode1_red[i]);
+
+ for (i = 0 ; i < ARRAY_SIZE(mode1_green) ; i++)
+ lp5521_write(cl, LP5521_REG_G_PROG_MEM + i,
+ mode1_green[i]);
+
+ lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_RUN);
+ lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM);
+ }
+}
+
+static ssize_t lp5521_store_pattern(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ unsigned long val;
+ int ret;
+
+ ret = strict_strtoul(buf, 16, &val);
+ if (ret)
+ return ret;
+
+ lp5521_program_pattern(val, g_chip);
+
+ return count;
+}
+
+static struct device_attribute lp5521_pattern_attr = {
+ .attr = {
+ .name = "led-pattern",
+ .mode = 0644,
+ },
+ .store = lp5521_store_pattern,
+};
+
+static int lp5521_create_pattern_nodes(struct lp5521_chip *chip)
+{
+ return device_create_file(chip->led_dev, &lp5521_pattern_attr);
+}
+
+static void lp5521_remove_pattern_nodes(struct lp5521_chip *chip)
+{
+ device_remove_file(chip->led_dev, &lp5521_pattern_attr);
+}
+
static int lp5521_register_sysfs(struct i2c_client *client)
{
struct device *dev = &client->dev;
@@ -617,10 +847,16 @@ static int __devinit lp5521_init_led(struct lp5521_led *led,
return -EINVAL;
}
- snprintf(name, sizeof(name), "%s:channel%d",
- pdata->label ?: client->name, chan);
led->cdev.brightness_set = lp5521_set_brightness;
- led->cdev.name = name;
+
+ if (pdata->led_config[chan].name) {
+ led->cdev.name = pdata->led_config[chan].name;
+ } else {
+ snprintf(name, sizeof(name), "%s:channel%d",
+ pdata->label ?: client->name, chan);
+ led->cdev.name = name;
+ }
+
res = led_classdev_register(dev, &led->cdev);
if (res < 0) {
dev_err(dev, "couldn't register led on channel %d\n", chan);
@@ -650,7 +886,11 @@ static int __devinit lp5521_probe(struct i2c_client *client,
i2c_set_clientdata(client, chip);
chip->client = client;
+ g_chip = chip;
+#ifdef LED_DEBUG
+ g_client = client;
+#endif
pdata = client->dev.platform_data;
if (!pdata) {
@@ -700,6 +940,7 @@ static int __devinit lp5521_probe(struct i2c_client *client,
chip->num_channels = pdata->num_channels;
chip->num_leds = 0;
led = 0;
+ chip->led_dev = device_create(sec_class, NULL, 0, NULL, "leds");
for (i = 0; i < pdata->num_channels; i++) {
/* Do not initialize channels that are not connected */
if (pdata->led_config[i].led_current == 0)
@@ -728,6 +969,10 @@ static int __devinit lp5521_probe(struct i2c_client *client,
dev_err(&client->dev, "registering sysfs failed\n");
goto fail3;
}
+
+ lp5521_create_pattern_nodes(chip);
+ lp5521_create_debugfs(chip);
+
return ret;
fail3:
for (i = 0; i < chip->num_leds; i++) {
@@ -744,11 +989,24 @@ fail1:
return ret;
}
+#ifdef LED_DEBUG
+void lp5521_led_brightness(u8 channel, u8 brightness)
+{
+
+ lp5521_write(g_client, LP5521_REG_LED_PWM_BASE + channel,
+ brightness);
+}
+EXPORT_SYMBOL(lp5521_led_brightness);
+#endif
+
static int lp5521_remove(struct i2c_client *client)
{
struct lp5521_chip *chip = i2c_get_clientdata(client);
int i;
+ lp5521_program_pattern(0, chip);
+ lp5521_remove_pattern_nodes(chip);
+ lp5521_remove_debugfs(chip);
lp5521_unregister_sysfs(client);
for (i = 0; i < chip->num_leds; i++) {
diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c
new file mode 100644
index 0000000..bdbb953
--- /dev/null
+++ b/drivers/leds/leds-max77693.c
@@ -0,0 +1,359 @@
+/*
+ * LED driver for Maxim MAX77693 - leds-max77673.c
+ *
+ * Copyright (C) 2011 ByungChang Cha <bc.cha@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-private.h>
+#include <linux/leds-max77693.h>
+
+struct max77693_led_data {
+ struct led_classdev led;
+ struct max77693_dev *max77693;
+ struct max77693_led *data;
+ struct i2c_client *i2c;
+ struct work_struct work;
+ struct mutex lock;
+ spinlock_t value_lock;
+ int brightness;
+ int test_brightness;
+};
+
+static u8 led_en_mask[MAX77693_LED_MAX] = {
+ MAX77693_FLASH_FLED1_EN,
+ MAX77693_FLASH_FLED2_EN,
+ MAX77693_TORCH_FLED1_EN,
+ MAX77693_TORCH_FLED2_EN
+};
+
+static u8 led_en_shift[MAX77693_LED_MAX] = {
+ 6,
+ 4,
+ 2,
+ 0,
+};
+
+static u8 reg_led_timer[MAX77693_LED_MAX] = {
+ MAX77693_LED_REG_FLASH_TIMER,
+ MAX77693_LED_REG_FLASH_TIMER,
+ MAX77693_LED_REG_ITORCHTORCHTIMER,
+ MAX77693_LED_REG_ITORCHTORCHTIMER,
+};
+
+static u8 reg_led_current[MAX77693_LED_MAX] = {
+ MAX77693_LED_REG_IFLASH1,
+ MAX77693_LED_REG_IFLASH2,
+ MAX77693_LED_REG_ITORCH,
+ MAX77693_LED_REG_ITORCH,
+};
+
+static u8 led_current_mask[MAX77693_LED_MAX] = {
+ MAX77693_FLASH_IOUT1,
+ MAX77693_FLASH_IOUT2,
+ MAX77693_TORCH_IOUT1,
+ MAX77693_TORCH_IOUT2
+};
+
+static u8 led_current_shift[MAX77693_LED_MAX] = {
+ 0,
+ 0,
+ 0,
+ 4,
+};
+
+static int max77693_set_bits(struct i2c_client *client, const u8 reg,
+ const u8 mask, const u8 inval)
+{
+ int ret;
+ u8 value;
+
+ ret = max77693_read_reg(client, reg, &value);
+ if (unlikely(ret < 0))
+ return ret;
+
+ value = (value & ~mask) | (inval & mask);
+
+ ret = max77693_write_reg(client, reg, value);
+
+ return ret;
+}
+
+static void print_all_reg_value(struct i2c_client *client)
+{
+ u8 value;
+ u8 i;
+
+ for (i = 0; i != 0x11; ++i) {
+ max77693_read_reg(client, i, &value);
+ printk(KERN_ERR "LEDS_MAX77693 REG(%d) = %x\n", i, value);
+ }
+}
+
+static int max77693_led_get_en_value(struct max77693_led_data *led_data, int on)
+{
+ if (on)
+ return 0x03;
+
+ if (led_data->data->cntrl_mode == MAX77693_LED_CTRL_BY_I2C)
+ return 0x00;
+ else if (led_data->data->id < 2)
+ return 0x01;
+ else
+ return 0x02;
+}
+
+static void max77693_led_set(struct led_classdev *led_cdev, enum led_brightness value)
+{
+ unsigned long flags;
+ struct max77693_led_data *led_data
+ = container_of(led_cdev, struct max77693_led_data, led);
+
+ pr_debug("[LED] %s\n", __func__);
+
+ spin_lock_irqsave(&led_data->value_lock, flags);
+ led_data->test_brightness = min((int)value, MAX77693_FLASH_IOUT1);
+ spin_unlock_irqrestore(&led_data->value_lock, flags);
+
+ schedule_work(&led_data->work);
+}
+
+static void led_set(struct max77693_led_data *led_data)
+{
+ int ret;
+ struct max77693_led *data = led_data->data;
+ int id = data->id;
+ u8 shift = led_current_shift[id];
+ int value;
+
+ if (led_data->test_brightness == LED_OFF)
+ {
+ value = max77693_led_get_en_value(led_data, 0);
+ ret = max77693_set_bits(led_data->i2c,
+ MAX77693_LED_REG_FLASH_EN,
+ led_en_mask[id],
+ value << led_en_shift[id]);
+ if (unlikely(ret))
+ goto error_set_bits;
+
+ ret = max77693_set_bits(led_data->i2c, reg_led_current[id],
+ led_current_mask[id],
+ led_data->brightness << shift);
+ if (unlikely(ret))
+ goto error_set_bits;
+
+ return;
+ }
+
+ /* Set current */
+ ret = max77693_set_bits(led_data->i2c, reg_led_current[id],
+ led_current_mask[id],
+ led_data->test_brightness << shift);
+ if (unlikely(ret))
+ goto error_set_bits;
+
+ /* Turn off LED */
+ value = max77693_led_get_en_value(led_data, 0);
+ ret = max77693_set_bits(led_data->i2c, MAX77693_LED_REG_FLASH_EN,
+ led_en_mask[id],
+ value << led_en_shift[id]);
+
+ if (unlikely(ret))
+ goto error_set_bits;
+
+ /* Turn on LED */
+ ret = max77693_set_bits(led_data->i2c, MAX77693_LED_REG_FLASH_EN,
+ led_en_mask[id], led_en_mask[id]);
+
+ if (unlikely(ret))
+ goto error_set_bits;
+
+ return;
+
+error_set_bits:
+ pr_err("%s: can't set led level %d\n", __func__, ret);
+ return;
+}
+
+static void max77693_led_work(struct work_struct *work)
+{
+ struct max77693_led_data *led_data
+ = container_of(work, struct max77693_led_data, work);
+
+ pr_debug("[LED] %s\n", __func__);
+
+ mutex_lock(&led_data->lock);
+ led_set(led_data);
+ mutex_unlock(&led_data->lock);
+}
+
+static int max77693_led_setup(struct max77693_led_data *led_data)
+{
+ int ret = 0;
+ struct max77693_led *data = led_data->data;
+ int id = data->id;
+ int value;
+
+ ret |= max77693_write_reg(led_data->i2c, MAX77693_LED_REG_VOUT_CNTL,
+ MAX77693_BOOST_FLASH_FLEDNUM_2
+ | MAX77693_BOOST_FLASH_MODE_BOTH);
+ ret |= max77693_write_reg(led_data->i2c, MAX77693_LED_REG_VOUT_FLASH1,
+ MAX77693_BOOST_VOUT_FLASH_FROM_VOLT(5000));
+ ret |= max77693_write_reg(led_data->i2c,
+ MAX77693_LED_REG_MAX_FLASH1, 0xEC);
+ ret |= max77693_write_reg(led_data->i2c,
+ MAX77693_LED_REG_MAX_FLASH2, 0x00);
+
+ value = max77693_led_get_en_value(led_data, 0);
+
+ ret |= max77693_set_bits(led_data->i2c, MAX77693_LED_REG_FLASH_EN,
+ led_en_mask[id],
+ value << led_en_shift[id]);
+
+ /* Set TORCH_TMR_DUR or FLASH_TMR_DUR */
+ if (id < 2) {
+ ret |= max77693_write_reg(led_data->i2c, reg_led_timer[id],
+ (data->timer | data->timer_mode << 7));
+ } else {
+ ret |= max77693_write_reg(led_data->i2c, reg_led_timer[id],
+ 0xC0);
+ }
+
+ /* Set current */
+ ret |= max77693_set_bits(led_data->i2c, reg_led_current[id],
+ led_current_mask[id],
+ led_data->brightness << led_current_shift[id]);
+
+ return ret;
+}
+
+static int max77693_led_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ int i;
+ struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent);
+ struct max77693_platform_data *max77693_pdata
+ = dev_get_platdata(max77693->dev);
+ struct max77693_led_platform_data *pdata = max77693_pdata->led_data;
+ struct max77693_led_data *led_data;
+ struct max77693_led *data;
+ struct max77693_led_data **led_datas;
+
+ if (pdata == NULL) {
+ pr_err("[LED] no platform data for this led is found\n");
+ return -EFAULT;
+ }
+
+ led_datas = kzalloc(sizeof(struct max77693_led_data *)
+ * MAX77693_LED_MAX, GFP_KERNEL);
+ if (unlikely(!led_datas)) {
+ pr_err("[LED] memory allocation error %s", __func__);
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, led_datas);
+
+ pr_debug("[LED] %s\n", __func__);
+
+ for (i = 0; i != pdata->num_leds; ++i) {
+ data = &(pdata->leds[i]);
+
+ led_data = kzalloc(sizeof(struct max77693_led_data),
+ GFP_KERNEL);
+ led_datas[i] = led_data;
+ if (unlikely(!led_data)) {
+ pr_err("[LED] memory allocation error %s\n", __func__);
+ ret = -ENOMEM;
+ continue;
+ }
+
+ led_data->max77693 = max77693;
+ led_data->i2c = max77693->i2c;
+ led_data->data = data;
+ led_data->led.name = data->name;
+ led_data->led.brightness_set = max77693_led_set;
+ led_data->led.brightness = LED_OFF;
+ led_data->brightness = data->brightness;
+ led_data->led.flags = LED_CORE_SUSPENDRESUME;
+ led_data->led.max_brightness = data->id < 2
+ ? MAX_FLASH_DRV_LEVEL : MAX_TORCH_DRV_LEVEL;
+
+ mutex_init(&led_data->lock);
+ spin_lock_init(&led_data->value_lock);
+ INIT_WORK(&led_data->work, max77693_led_work);
+
+ ret = led_classdev_register(&pdev->dev, &led_data->led);
+ if (unlikely(ret)) {
+ pr_err("unable to register LED\n");
+ kfree(led_data);
+ ret = -EFAULT;
+ continue;
+ }
+
+ ret = max77693_led_setup(led_data);
+ if (unlikely(ret)) {
+ pr_err("unable to register LED\n");
+ led_classdev_unregister(&led_data->led);
+ kfree(led_data);
+ ret = -EFAULT;
+ }
+ }
+ /* print_all_reg_value(max77693->i2c); */
+ return ret;
+}
+
+static int __devexit max77693_led_remove(struct platform_device *pdev)
+{
+ struct max77693_led_data **led_datas = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i != MAX77693_LED_MAX; ++i) {
+ if (led_datas[i] == NULL)
+ continue;
+
+ cancel_work_sync(&led_datas[i]->work);
+ mutex_destroy(&led_datas[i]->lock);
+ led_classdev_unregister(&led_datas[i]->led);
+ kfree(led_datas[i]);
+ }
+ kfree(led_datas);
+
+ return 0;
+}
+
+static struct platform_driver max77693_led_driver =
+{
+ .probe = max77693_led_probe,
+ .remove = __devexit_p(max77693_led_remove),
+ .driver =
+ {
+ .name = "max77693-led",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init max77693_led_init(void)
+{
+ return platform_driver_register(&max77693_led_driver);
+}
+module_init(max77693_led_init);
+
+static void __exit max77693_led_exit(void)
+{
+ platform_driver_unregister(&max77693_led_driver);
+}
+module_exit(max77693_led_exit);
+
+MODULE_AUTHOR("ByungChang Cha <bc.cha@samsung.com.com>");
+MODULE_DESCRIPTION("MAX77693 LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-max8997.c b/drivers/leds/leds-max8997.c
new file mode 100644
index 0000000..54c4a04
--- /dev/null
+++ b/drivers/leds/leds-max8997.c
@@ -0,0 +1,313 @@
+/*
+ * leds-max8997.c - LED class driver for MAX8997 LEDs.
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ *
+ * Inspired by leds-regulator driver.
+ *
+ * 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/module.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/leds.h>
+#include <linux/leds-max8997.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#define to_regulator_led(led_cdev) \
+ container_of(led_cdev, struct regulator_led, cdev)
+
+struct regulator_led {
+ struct led_classdev cdev;
+ enum led_brightness value;
+ int enabled;
+ struct mutex mutex;
+ struct work_struct work;
+
+ struct regulator *vcc;
+};
+
+static inline int led_regulator_get_max_brightness(struct regulator *supply)
+{
+ int ret;
+ int voltage = regulator_list_voltage(supply, 0);
+
+ /*TODO*/
+#if defined(CONFIG_MACH_Q1_BD)
+ return LED_BRIGHTNESS_MAX_LEVEL;
+#else
+ return 1;
+#endif
+
+ if (voltage <= 0)
+ return 1;
+
+ /* even if regulator can't change voltages,
+ * we still assume it can change status
+ * and the LED can be turned on and off.
+ */
+ ret = regulator_set_voltage(supply, voltage, voltage);
+ if (ret < 0)
+ return 1;
+
+ return regulator_count_voltages(supply);
+}
+
+static int led_regulator_get_voltage(struct regulator *supply,
+ enum led_brightness brightness)
+{
+ if (brightness == 0)
+ return -EINVAL;
+
+ return regulator_list_voltage(supply, brightness - 1);
+}
+
+
+static void regulator_led_enable(struct regulator_led *led)
+{
+ int ret;
+
+ if (led->enabled)
+ return;
+
+ ret = regulator_enable(led->vcc);
+ if (ret != 0) {
+ dev_err(led->cdev.dev, "Failed to enable vcc: %d\n", ret);
+ return;
+ }
+
+ led->enabled = 1;
+}
+
+static void regulator_led_disable(struct regulator_led *led)
+{
+ int ret;
+
+ if (!led->enabled)
+ return;
+
+ ret = regulator_disable(led->vcc);
+ if (ret != 0) {
+ dev_err(led->cdev.dev, "Failed to disable vcc: %d\n", ret);
+ return;
+ }
+
+ led->enabled = 0;
+}
+
+static void regulator_led_set_value(struct regulator_led *led)
+{
+ int voltage;
+ int ret;
+ struct regulator *movie;
+
+ mutex_lock(&led->mutex);
+
+ if (led->cdev.flags & LED_SUSPENDED)
+ goto out;
+
+ if (led->value == LED_OFF) {
+ regulator_led_disable(led);
+ goto out;
+ }
+
+ movie = regulator_get(NULL, "led_movie");
+ if (IS_ERR(movie)) {
+ pr_err("%s: led_movie is failed.\n", __func__);
+ goto out;
+ }
+
+/* Q1 has torch light widget, and it changes */
+/* its brightness level without disabling rerulator */
+/* So, remove the below code for Q1 */
+#if !defined(CONFIG_MACH_Q1_BD)
+ if (regulator_is_enabled(movie) > 0) {
+ pr_info("%s: led_movie is enabled.\n", __func__);
+ goto end;
+ }
+#endif
+
+#if 0 /*TODO*/
+ if (led->cdev.max_brightness > 1) {
+ voltage = led_regulator_get_voltage(led->vcc, led->value);
+ dev_dbg(led->cdev.dev, "brightness: %d voltage: %d\n",
+ led->value, voltage);
+
+ ret = regulator_set_voltage(led->vcc, voltage, voltage);
+ if (ret != 0)
+ dev_err(led->cdev.dev, "Failed to set voltage %d: %d\n",
+ voltage, ret);
+ }
+#endif
+
+#if defined(CONFIG_MACH_Q1_BD)
+ switch (led->value) {
+ case LED_BRIGHTNESS_LEVEL1:
+ regulator_set_current_limit(led->vcc, 10000, 20000);
+ break;
+ case LED_BRIGHTNESS_LEVEL2:
+ regulator_set_current_limit(led->vcc, 90000, 110000);
+ break;
+ case LED_BRIGHTNESS_LEVEL3:
+ regulator_set_current_limit(led->vcc, 180000, 200000);
+ break;
+ default:
+ pr_err("%s: fail to set current, out of range\n", __func__);
+ goto end;
+ break;
+ }
+#else
+ regulator_set_current_limit(led->vcc, 90000, 110000);
+#endif
+
+ regulator_led_enable(led);
+
+end:
+ regulator_put(movie);
+
+out:
+ mutex_unlock(&led->mutex);
+}
+
+static void led_work(struct work_struct *work)
+{
+ struct regulator_led *led;
+
+ led = container_of(work, struct regulator_led, work);
+ regulator_led_set_value(led);
+}
+
+static void regulator_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct regulator_led *led = to_regulator_led(led_cdev);
+
+ led->value = value;
+ schedule_work(&led->work);
+}
+
+static int __devinit regulator_led_probe(struct platform_device *pdev)
+{
+ struct led_max8997_platform_data *pdata = pdev->dev.platform_data;
+ struct regulator_led *led;
+ struct regulator *vcc;
+ int ret = 0;
+
+ pr_info("%s +\n", __func__);
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -ENODEV;
+ }
+
+ vcc = regulator_get_exclusive(&pdev->dev, "led_torch");
+ if (IS_ERR(vcc)) {
+ dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name);
+ return PTR_ERR(vcc);
+ }
+
+ led = kzalloc(sizeof(*led), GFP_KERNEL);
+ if (led == NULL) {
+ ret = -ENOMEM;
+ goto err_vcc;
+ }
+
+ led->cdev.max_brightness = led_regulator_get_max_brightness(vcc);
+ if (pdata->brightness > led->cdev.max_brightness) {
+ dev_err(&pdev->dev, "Invalid default brightness %d\n",
+ pdata->brightness);
+ ret = -EINVAL;
+ goto err_led;
+ }
+
+ /* Set default brightness */
+ led->value = pdata->brightness;
+
+ led->cdev.brightness_set = regulator_led_brightness_set;
+ /* Set leds name for class driver */
+ led->cdev.name = pdata->name;
+ led->cdev.flags |= LED_CORE_SUSPENDRESUME;
+ led->vcc = vcc;
+
+ mutex_init(&led->mutex);
+ INIT_WORK(&led->work, led_work);
+
+ platform_set_drvdata(pdev, led);
+
+ ret = led_classdev_register(&pdev->dev, &led->cdev);
+ if (ret < 0) {
+ cancel_work_sync(&led->work);
+ goto err_led;
+ }
+
+ /* to expose the default value to userspace */
+ led->cdev.brightness = led->value;
+
+ /* Set the default led status */
+ regulator_led_set_value(led);
+
+ pr_info("%s -\n", __func__);
+
+ return 0;
+
+err_led:
+ kfree(led);
+err_vcc:
+ regulator_put(vcc);
+ return ret;
+}
+
+static int __devexit regulator_led_remove(struct platform_device *pdev)
+{
+ struct regulator_led *led = platform_get_drvdata(pdev);
+
+ led_classdev_unregister(&led->cdev);
+ cancel_work_sync(&led->work);
+ regulator_led_disable(led);
+ regulator_put(led->vcc);
+ kfree(led);
+ return 0;
+}
+
+static void regulator_led_shutdown(struct platform_device *pdev)
+{
+ struct regulator_led *led = platform_get_drvdata(pdev);
+
+ if (regulator_is_enabled(led->vcc))
+ regulator_led_disable(led);
+
+ return;
+}
+
+static struct platform_driver regulator_led_driver = {
+ .driver = {
+ .name = "leds-max8997",
+ .owner = THIS_MODULE,
+ },
+ .probe = regulator_led_probe,
+ .remove = __devexit_p(regulator_led_remove),
+ .shutdown = regulator_led_shutdown,
+};
+
+static int __init regulator_led_init(void)
+{
+ return platform_driver_register(&regulator_led_driver);
+}
+module_init(regulator_led_init);
+
+static void __exit regulator_led_exit(void)
+{
+ platform_driver_unregister(&regulator_led_driver);
+}
+module_exit(regulator_led_exit);
+
+MODULE_AUTHOR("Byun.C.W");
+MODULE_DESCRIPTION("MAX8997 LED driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:leds-max8997");
diff --git a/drivers/leds/leds-spfcw043.c b/drivers/leds/leds-spfcw043.c
new file mode 100644
index 0000000..555c49c
--- /dev/null
+++ b/drivers/leds/leds-spfcw043.c
@@ -0,0 +1,202 @@
+/*
+ * LED driver for SPFCW043 - leds-spfcw043.c
+ *
+ * Copyright (C) 2011 DongHyun Chang <dh348.chang@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/leds-spfcw043.h>
+int *spfcw043_ctrl;
+struct spfcw043_led_platform_data *led_pdata;
+
+extern struct class *camera_class;
+extern struct device *camera_rear;
+
+static int spfcw043_setPower(int onoff, int level)
+{
+ int i;
+ if (led_pdata->status == STATUS_AVAILABLE) {
+ led_pdata->torch_en(0);
+ led_pdata->torch_set(0);
+ /* onoff = 0 - select spfcw043, onoff = 1 - select ISP*/
+ udelay(10);
+ if (onoff) {
+ led_pdata->torch_set(1);
+ for (i = 1; i < level; i++) {
+ udelay(1);
+ led_pdata->torch_set(0);
+ udelay(1);
+ led_pdata->torch_set(1);
+ }
+ }
+ } else {
+ LED_ERROR("GPIO is not available!");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int spfcw043_freeGpio(void)
+{
+ if (led_pdata->status == STATUS_AVAILABLE) {
+ if (led_pdata->freeGpio()) {
+ LED_ERROR("Can't free GPIO for led\n");
+ return -ENODEV;
+ }
+ led_pdata->status = STATUS_UNAVAILABLE;
+ } else {
+ LED_ERROR("GPIO already free!");
+ }
+
+ return 0;
+}
+
+static int spfcw043_setGpio(void)
+{
+ if (led_pdata->status != STATUS_AVAILABLE) {
+ if (led_pdata->setGpio()) {
+ LED_ERROR("Can't set GPIO for led\n");
+ return -ENODEV;
+ }
+ led_pdata->status = STATUS_AVAILABLE;
+ } else {
+ LED_ERROR("GPIO already set!");
+ }
+
+ return 0;
+}
+
+static int spfcw043_open(struct inode *inode, struct file *file)
+{
+ int ret;
+ led_pdata->brightness = TORCH_BRIGHTNESS_50; /*normal setting*/
+ led_pdata->status = STATUS_INVALID;
+
+ ret = spfcw043_setGpio();
+
+ file->private_data = (int *) spfcw043_ctrl;
+
+ return ret;
+}
+
+static int spfcw043_release(struct inode *inode, struct file *file)
+{
+ if (spfcw043_freeGpio()) {
+ LED_ERROR("spfcw043_freeGpio failed!\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static ssize_t spfcw043_power(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int brightness = 0;
+
+ if (buf[0] < 0x30 || buf[0] > 0x39) {
+ LED_ERROR("data is wrong!");
+ return count;
+ } else if (buf[1] < 0x30 || buf[1] > 0x39)
+ brightness = (int) (buf[0] - 0x30);
+ else {
+ brightness = brightness + (int) (buf[0] - 0x30) * 10;
+ brightness = brightness + (int) (buf[1] - 0x30);
+ }
+
+ if (brightness < 0 || brightness >= TORCH_BRIGHTNESS_INVALID) {
+ LED_ERROR("brightness data is wrong! %d", brightness);
+ return count;
+ }
+
+ if (brightness == 0) {
+ spfcw043_setPower(0, 0);
+ } else {
+ if (spfcw043_setGpio()) {
+ LED_ERROR("spfcw043_setGpio failed!\n");
+ return count;
+ }
+ spfcw043_setPower(1, brightness);
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(rear_flash, S_IWUSR|S_IWGRP|S_IROTH, NULL, spfcw043_power);
+
+static const struct file_operations spfcw043_fops = {
+ .owner = THIS_MODULE,
+ .open = spfcw043_open,
+ .release = spfcw043_release,
+};
+
+static struct miscdevice spfcw043_miscdev = {
+ .minor = 255,
+ .name = "spfcw043_led",
+ .fops = &spfcw043_fops,
+};
+
+static int spfcw043_led_probe(struct platform_device *pdev)
+{
+ LED_ERROR("Probe\n");
+ led_pdata =
+ (struct spfcw043_led_platform_data *) pdev->dev.platform_data;
+
+ if (misc_register(&spfcw043_miscdev)) {
+ LED_ERROR("Failed to register misc driver\n");
+ return -ENODEV;
+ }
+
+ if (IS_ERR(camera_rear))
+ LED_ERROR("Failed to create device(flash)!\n");
+
+ if (device_create_file(camera_rear, &dev_attr_rear_flash) < 0) {
+ LED_ERROR("failed to create device file, %s\n",
+ dev_attr_rear_flash.attr.name);
+ }
+ spfcw043_setGpio();
+ return 0;
+}
+
+static int __devexit spfcw043_led_remove(struct platform_device *pdev)
+{
+ led_pdata->freeGpio();
+ misc_deregister(&spfcw043_miscdev);
+
+ device_remove_file(camera_rear, &dev_attr_rear_flash);
+ device_destroy(camera_class, 0);
+ class_destroy(camera_class);
+
+ return 0;
+}
+
+static struct platform_driver spfcw043_led_driver = {
+ .probe = spfcw043_led_probe,
+ .remove = __devexit_p(spfcw043_led_remove),
+ .driver = {
+ .name = "spfcw043-led",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init spfcw043_led_init(void)
+{
+ return platform_driver_register(&spfcw043_led_driver);
+}
+
+static void __exit spfcw043_led_exit(void)
+{
+ platform_driver_unregister(&spfcw043_led_driver);
+}
+
+module_init(spfcw043_led_init);
+module_exit(spfcw043_led_exit);
+
+MODULE_AUTHOR("DongHyun Chang <dh348.chang@samsung.com.com>");
+MODULE_DESCRIPTION("SPFCW043 LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/ledtrig-sleep.c b/drivers/leds/ledtrig-sleep.c
new file mode 100644
index 0000000..f164042
--- /dev/null
+++ b/drivers/leds/ledtrig-sleep.c
@@ -0,0 +1,80 @@
+/* drivers/leds/ledtrig-sleep.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/earlysuspend.h>
+#include <linux/leds.h>
+#include <linux/suspend.h>
+
+static int ledtrig_sleep_pm_callback(struct notifier_block *nfb,
+ unsigned long action,
+ void *ignored);
+
+DEFINE_LED_TRIGGER(ledtrig_sleep)
+static struct notifier_block ledtrig_sleep_pm_notifier = {
+ .notifier_call = ledtrig_sleep_pm_callback,
+ .priority = 0,
+};
+
+static void ledtrig_sleep_early_suspend(struct early_suspend *h)
+{
+ led_trigger_event(ledtrig_sleep, LED_FULL);
+}
+
+static void ledtrig_sleep_early_resume(struct early_suspend *h)
+{
+ led_trigger_event(ledtrig_sleep, LED_OFF);
+}
+
+static struct early_suspend ledtrig_sleep_early_suspend_handler = {
+ .suspend = ledtrig_sleep_early_suspend,
+ .resume = ledtrig_sleep_early_resume,
+};
+
+static int ledtrig_sleep_pm_callback(struct notifier_block *nfb,
+ unsigned long action,
+ void *ignored)
+{
+ switch (action) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ led_trigger_event(ledtrig_sleep, LED_OFF);
+ return NOTIFY_OK;
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ led_trigger_event(ledtrig_sleep, LED_FULL);
+ return NOTIFY_OK;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int __init ledtrig_sleep_init(void)
+{
+ led_trigger_register_simple("sleep", &ledtrig_sleep);
+ register_pm_notifier(&ledtrig_sleep_pm_notifier);
+ register_early_suspend(&ledtrig_sleep_early_suspend_handler);
+ return 0;
+}
+
+static void __exit ledtrig_sleep_exit(void)
+{
+ unregister_early_suspend(&ledtrig_sleep_early_suspend_handler);
+ unregister_pm_notifier(&ledtrig_sleep_pm_notifier);
+ led_trigger_unregister_simple(ledtrig_sleep);
+}
+
+module_init(ledtrig_sleep_init);
+module_exit(ledtrig_sleep_exit);
+