diff options
author | codeworkx <codeworkx@cyanogenmod.com> | 2012-09-22 09:48:20 +0200 |
---|---|---|
committer | codeworkx <codeworkx@cyanogenmod.com> | 2012-09-22 14:02:16 +0200 |
commit | 2489007e7d740ccbc3e0a202914e243ad5178787 (patch) | |
tree | b8e6380ea7b1da63474ad68a5dba997e01146043 /drivers/leds | |
parent | 5f67568eb31e3a813c7c52461dcf66ade15fc2e7 (diff) | |
download | kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.zip kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.tar.gz kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.tar.bz2 |
merge opensource jb u5
Change-Id: I1aaec157aa196f3448eff8636134fce89a814cf2
Diffstat (limited to 'drivers/leds')
-rw-r--r-- | drivers/leds/Kconfig | 17 | ||||
-rw-r--r-- | drivers/leds/leds-aat1290a.c | 32 | ||||
-rw-r--r-- | drivers/leds/leds-an30259a.c | 62 | ||||
-rw-r--r-- | drivers/leds/leds-lp5521.c | 520 | ||||
-rw-r--r-- | drivers/leds/leds-max77693.c | 101 | ||||
-rw-r--r-- | drivers/leds/leds-max8997.c | 6 |
6 files changed, 409 insertions, 329 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index d63605c..7cdcd46 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -331,6 +331,14 @@ config LEDS_REGULATOR help This option enables support for regulator driven LEDs. +config LEDS_MAX8997 + tristate "MAX8997 LED support" + default n + help + This option + enables support + for LEDs + of MAX8997. config LEDS_BD2802 tristate "LED driver for BD2802 RGB LED" depends on LEDS_CLASS @@ -415,11 +423,18 @@ 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_SWITCH + bool "LED control switch support" + depends on LEDS_CLASS + default n + help + This option enables support for the LED SWITCH. + config LEDS_MAX77693 bool "LED support for the MAX77693" depends on LEDS_CLASS depends on MFD_MAX77693 - default y + default n help This option enables support for the LEDs on the MAX77693. diff --git a/drivers/leds/leds-aat1290a.c b/drivers/leds/leds-aat1290a.c index 493f422..cf916ee 100644 --- a/drivers/leds/leds-aat1290a.c +++ b/drivers/leds/leds-aat1290a.c @@ -12,7 +12,7 @@ int *aat1290a_ctrl; struct aat1290a_led_platform_data *led_pdata; -struct class *flash_class; +extern struct class *camera_class; /*sys/class/camera*/ struct device *aat1290a_dev; static int aat1290a_setPower(int onoff, int level) @@ -165,8 +165,15 @@ ssize_t aat1290a_power(struct device *dev, return count; } -static DEVICE_ATTR(flash_power, S_IWUSR|S_IWGRP|S_IROTH, - NULL, aat1290a_power); +ssize_t aat1290a_get_max_brightness(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, + sizeof(led_pdata->brightness), "%d", TORCH_BRIGHTNESS_100); +} + +static DEVICE_ATTR(rear_flash, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH, + aat1290a_get_max_brightness, aat1290a_power); static const struct file_operations aat1290a_fops = { .owner = THIS_MODULE, @@ -192,18 +199,17 @@ static int aat1290a_led_probe(struct platform_device *pdev) 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"); + aat1290a_dev = device_create(camera_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) { + if (device_create_file(aat1290a_dev, &dev_attr_rear_flash) < 0) { LED_ERROR("failed to create device file, %s\n", - dev_attr_flash_power.attr.name); + dev_attr_rear_flash.attr.name); } - led_pdata->initGpio(); + + if (led_pdata) + led_pdata->initGpio(); aat1290a_setGpio(); return 0; } @@ -213,9 +219,9 @@ 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); + device_remove_file(aat1290a_dev, &dev_attr_rear_flash); + device_destroy(camera_class, 0); + class_destroy(camera_class); return 0; } diff --git a/drivers/leds/leds-an30259a.c b/drivers/leds/leds-an30259a.c index 5d351c4..965ef3e 100644 --- a/drivers/leds/leds-an30259a.c +++ b/drivers/leds/leds-an30259a.c @@ -82,6 +82,9 @@ #define MAX_NUM_LEDS 3 +u8 LED_DYNAMIC_CURRENT = 0x8; +u8 LED_LOWPOWER_MODE = 0x0; + static struct an30259_led_conf led_conf[] = { { .name = "led_r", @@ -317,7 +320,6 @@ static void an30259a_reset_register_work(struct work_struct *work) static void an30259a_start_led_pattern(int mode) { int retval; - struct i2c_client *client; struct work_struct *reset = 0; client = b_client; @@ -329,6 +331,12 @@ static void an30259a_start_led_pattern(int mode) if (mode == LED_OFF) return; + /* Set to low power consumption mode */ + if (LED_LOWPOWER_MODE == 1) + LED_DYNAMIC_CURRENT = 0x8; + else + LED_DYNAMIC_CURRENT = 0x1; + switch (mode) { /* leds_set_slope_mode(client, LED_SEL, DELAY, MAX, MID, MIN, SLPTT1, SLPTT2, DT1, DT2, DT3, DT4) */ @@ -437,6 +445,27 @@ static void an30259a_set_led_blink(enum an30259a_led_enum led, } } +static ssize_t store_an30259a_led_lowpower(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + int retval; + u8 led_lowpower; + struct an30259a_data *data = dev_get_drvdata(dev); + + retval = kstrtou8(buf, 0, &led_lowpower); + if (retval != 0) { + dev_err(&data->client->dev, "fail to get led_lowpower.\n"); + return count; + } + + LED_LOWPOWER_MODE = led_lowpower; + + printk(KERN_DEBUG "led_lowpower mode set to %i\n", led_lowpower); + + return count; +} + static ssize_t store_an30259a_led_br_lev(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -449,7 +478,7 @@ static ssize_t store_an30259a_led_br_lev(struct device *dev, retval = strict_strtoul(buf, 16, &brightness_lev); if (retval != 0) { - dev_err(&data->client->dev, "fail to get led_pattern mode.\n"); + dev_err(&data->client->dev, "fail to get led_br_lev.\n"); return count; } @@ -517,7 +546,8 @@ static ssize_t store_an30259a_led_blink(struct device *dev, leds_i2c_write_all(data->client); - printk(KERN_DEBUG "led_blink is called\n"); + printk(KERN_DEBUG "led_blink is called, Color:0x%X Brightness:%i\n", + led_brightness, LED_DYNAMIC_CURRENT); return count; } @@ -739,6 +769,9 @@ static DEVICE_ATTR(led_fade, 0664, show_an30259a_led_fade, \ store_an30259a_led_fade); static DEVICE_ATTR(led_br_lev, 0664, NULL, \ store_an30259a_led_br_lev); +static DEVICE_ATTR(led_lowpower, 0664, NULL, \ + store_an30259a_led_lowpower); + #endif static struct attribute *led_class_attrs[] = { @@ -761,6 +794,7 @@ static struct attribute *sec_led_attributes[] = { &dev_attr_led_blink.attr, &dev_attr_led_fade.attr, &dev_attr_led_br_lev.attr, + &dev_attr_led_lowpower.attr, NULL, }; @@ -867,11 +901,14 @@ static int __devinit an30259a_probe(struct i2c_client *client, dev_err(&client->dev, "Failed to create device for samsung specific led\n"); ret = -ENODEV; + goto exit; } ret = sysfs_create_group(&led_dev->kobj, &sec_led_attr_group); - if (ret) + if (ret) { dev_err(&client->dev, "Failed to create sysfs group for samsung specific led\n"); + goto exit; + } #endif return ret; exit: @@ -885,22 +922,6 @@ 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__); - - // this is not an ugly hack to shutdown led. - data->shadow_reg[AN30259A_REG_LEDON] &= ~(LED_ON << 0); - data->shadow_reg[AN30259A_REG_LEDON] &= ~(LED_ON << 1); - data->shadow_reg[AN30259A_REG_LEDON] &= ~(LED_ON << 2); - data->shadow_reg[AN30259A_REG_LED1CNT2 + 0 * 4] &= ~AN30259A_MASK_DELAY; - data->shadow_reg[AN30259A_REG_LED1CNT2 + 1 * 4] &= ~AN30259A_MASK_DELAY; - data->shadow_reg[AN30259A_REG_LED1CNT2 + 2 * 4] &= ~AN30259A_MASK_DELAY; - data->shadow_reg[AN30259A_REG_LEDON] &= ~(LED_SLOPE_MODE << 0); - data->shadow_reg[AN30259A_REG_LEDON] &= ~(LED_SLOPE_MODE << 1); - data->shadow_reg[AN30259A_REG_LEDON] &= ~(LED_SLOPE_MODE << 2); - data->shadow_reg[AN30259A_REG_LED1CC + 0] = 0; - data->shadow_reg[AN30259A_REG_LED1CC + 1] = 0; - data->shadow_reg[AN30259A_REG_LED1CC + 2] = 0; - msleep(200); - #ifdef SEC_LED_SPECIFIC sysfs_remove_group(&led_dev->kobj, &sec_led_attr_group); #endif @@ -910,7 +931,6 @@ static int __devexit an30259a_remove(struct i2c_client *client) led_classdev_unregister(&data->leds[i].cdev); cancel_work_sync(&data->leds[i].brightness_work); } - mutex_destroy(&data->mutex); kfree(data); return 0; diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 58a5dee..b33dc63 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -5,8 +5,6 @@ * * 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. @@ -37,11 +35,6 @@ #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 */ @@ -88,13 +81,28 @@ #define LP5521_MASTER_ENABLE 0x40 /* Chip master enable */ #define LP5521_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */ #define LP5521_EXEC_RUN 0x2A +#define LP5521_ENABLE_DEFAULT \ + (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM) +#define LP5521_ENABLE_RUN_PROGRAM \ + (LP5521_ENABLE_DEFAULT | LP5521_EXEC_RUN) /* Status */ #define LP5521_EXT_CLK_USED 0x08 -#define LED_DEBUG 1 +/* default R channel current register value */ +#define LP5521_REG_R_CURR_DEFAULT 0xAF -extern struct class *sec_class; +/* Pattern Mode */ +#define PATTERN_OFF 0 + +/* Blink time unit */ +#define MAX_BLINK_TIME 7500 + +/* Program Commands */ +#define CMD_SET_PWM 0x40 +#define CMD_WAIT_MSB 0x60 /* 500ms unit */ +#define CMD_WAIT_LSB 0x00 +#define WAIT_UNIT 500 struct lp5521_engine { int id; @@ -113,13 +121,6 @@ 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 */ @@ -128,17 +129,16 @@ 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 +struct lp5521_pattern_cmd { + u8 r[LP5521_PROGRAM_LENGTH]; + u8 g[LP5521_PROGRAM_LENGTH]; + u8 b[LP5521_PROGRAM_LENGTH]; + int pc_r; + int pc_g; + int pc_b; +}; static inline struct lp5521_led *cdev_to_led(struct led_classdev *cdev) { @@ -157,8 +157,6 @@ 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) @@ -193,14 +191,14 @@ static int lp5521_set_engine_mode(struct lp5521_engine *engine, u8 mode) mode = LP5521_CMD_DIRECT; ret = lp5521_read(client, LP5521_REG_OP_MODE, &engine_state); + if (ret < 0) + return ret; /* set mode only for this engine */ engine_state &= ~(engine->engine_mask); mode &= engine->engine_mask; engine_state |= mode; - ret |= lp5521_write(client, LP5521_REG_OP_MODE, engine_state); - - return ret; + return lp5521_write(client, LP5521_REG_OP_MODE, engine_state); } static int lp5521_load_program(struct lp5521_engine *eng, const u8 *pattern) @@ -213,9 +211,14 @@ static int lp5521_load_program(struct lp5521_engine *eng, const u8 *pattern) /* move current engine to direct mode and remember the state */ ret = lp5521_set_engine_mode(eng, LP5521_CMD_DIRECT); + if (ret) + return ret; + /* Mode change requires min 500 us delay. 1 - 2 ms with margin */ usleep_range(1000, 2000); - ret |= lp5521_read(client, LP5521_REG_OP_MODE, &mode); + ret = lp5521_read(client, LP5521_REG_OP_MODE, &mode); + if (ret) + return ret; /* For loading, all the engines to load mode */ lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); @@ -231,8 +234,7 @@ static int lp5521_load_program(struct lp5521_engine *eng, const u8 *pattern) LP5521_PROG_MEM_SIZE, pattern); - ret |= lp5521_write(client, LP5521_REG_OP_MODE, mode); - return ret; + return lp5521_write(client, LP5521_REG_OP_MODE, mode); } static int lp5521_set_led_current(struct lp5521_chip *chip, int led, u8 curr) @@ -255,16 +257,17 @@ 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; + u8 cfg; lp5521_init_engine(chip); /* Set all PWMs to direct control mode */ - ret = lp5521_write(client, LP5521_REG_OP_MODE, 0x3F); + ret = lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); - if (cfg) - ret |= lp5521_write(client, LP5521_REG_CONFIG, cfg); + cfg = chip->pdata->update_config ? + : (LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT); + 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); @@ -273,8 +276,7 @@ static int lp5521_configure(struct i2c_client *client) /* Set engines are set to run state when OP_MODE enables engines */ ret |= lp5521_write(client, LP5521_REG_ENABLE, - LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM | - LP5521_EXEC_RUN); + LP5521_ENABLE_RUN_PROGRAM); /* enable takes 500us. 1 - 2 ms leaves some margin */ usleep_range(1000, 2000); @@ -325,8 +327,7 @@ static int lp5521_detect(struct i2c_client *client) int ret; u8 buf; - ret = lp5521_write(client, LP5521_REG_ENABLE, - LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM); + ret = lp5521_write(client, LP5521_REG_ENABLE, LP5521_ENABLE_DEFAULT); if (ret) return ret; /* enable takes 500us. 1 - 2 ms leaves some margin */ @@ -334,7 +335,7 @@ static int lp5521_detect(struct i2c_client *client) ret = lp5521_read(client, LP5521_REG_ENABLE, &buf); if (ret) return ret; - if (buf != (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM)) + if (buf != LP5521_ENABLE_DEFAULT) return -ENODEV; return 0; @@ -519,7 +520,7 @@ static ssize_t store_current(struct device *dev, ssize_t ret; unsigned long curr; - if (strict_strtoul(buf, 0, &curr)) + if (kstrtoul(buf, 0, &curr)) return -EINVAL; if (curr > led->max_current) @@ -551,229 +552,93 @@ static ssize_t lp5521_selftest(struct device *dev, return sprintf(buf, "%s\n", ret ? "FAIL" : "OK"); } -/* led class device attributes */ -static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current); -static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); - -static struct attribute *lp5521_led_attributes[] = { - &dev_attr_led_current.attr, - &dev_attr_max_current.attr, - NULL, -}; - -static struct attribute_group lp5521_led_attribute_group = { - .attrs = lp5521_led_attributes -}; - -/* device attributes */ -static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, - show_engine1_mode, store_engine1_mode); -static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUSR, - show_engine2_mode, store_engine2_mode); -static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUSR, - show_engine3_mode, store_engine3_mode); -static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load); -static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load); -static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load); -static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); - -static struct attribute *lp5521_attributes[] = { - &dev_attr_engine1_mode.attr, - &dev_attr_engine2_mode.attr, - &dev_attr_engine3_mode.attr, - &dev_attr_selftest.attr, - &dev_attr_engine1_load.attr, - &dev_attr_engine2_load.attr, - &dev_attr_engine3_load.attr, - NULL -}; - -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) +static void lp5521_clear_program_memory(struct i2c_client *cl) { - 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); + int i; + u8 rgb_mem[] = { + LP5521_REG_R_PROG_MEM, + LP5521_REG_G_PROG_MEM, + LP5521_REG_B_PROG_MEM, + }; + + for (i = 0; i < ARRAY_SIZE(rgb_mem); i++) { + lp5521_write(cl, rgb_mem[i], 0); + lp5521_write(cl, rgb_mem[i] + 1, 0); + } } -static char *lp5521_parse_register_cmd(const char *cmd, u8 *byte) +static void lp5521_write_program_memory(struct i2c_client *cl, + u8 base, u8 *rgb, int size) { - char tmp[10]; - char *blank; - unsigned long arg; + int i; - blank = strchr(cmd, ' '); - memset(tmp, 0x0, sizeof(tmp)); - memcpy(tmp, cmd, blank - cmd); + if (!rgb || size <= 0) + return; - if (strict_strtol(tmp, 16, &arg) < 0) - return NULL; + for (i = 0; i < size; i++) + lp5521_write(cl, base + i, *(rgb + i)); - *byte = arg; - return blank; + lp5521_write(cl, base + i, 0); + lp5521_write(cl, base + i + 1, 0); } -static ssize_t lp5521_ctrl_register(struct file *file, - const char __user *userbuf, size_t count, - loff_t *ppos) +static inline struct lp5521_led_pattern *lp5521_get_pattern + (struct lp5521_chip *chip, u8 offset) { - 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; + struct lp5521_led_pattern *ptn; + ptn = chip->pdata->patterns + (offset - 1); + return ptn; } -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) +static void _run_led_pattern(struct lp5521_chip *chip, + struct lp5521_led_pattern *ptn) { - struct dbg_dentry *dd = &chip->dd; + struct i2c_client *cl = chip->client; - dd->dir = debugfs_create_dir("lp5521", NULL); - dd->reg = debugfs_create_file("registers", S_IWUSR | S_IRUGO, - dd->dir, chip, &fops_registers); -} + lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_LOAD); + usleep_range(1000, 2000); -static void lp5521_remove_debugfs(struct lp5521_chip *chip) -{ - struct dbg_dentry *dd = &chip->dd; + lp5521_clear_program_memory(cl); - debugfs_remove(dd->reg); - debugfs_remove(dd->dir); -} -#else -static inline void lp5521_create_debugfs(struct lp5521_chip *chip) -{ - return; -} + lp5521_write_program_memory(cl, LP5521_REG_R_PROG_MEM, + ptn->r, ptn->size_r); + lp5521_write_program_memory(cl, LP5521_REG_G_PROG_MEM, + ptn->g, ptn->size_g); + lp5521_write_program_memory(cl, LP5521_REG_B_PROG_MEM, + ptn->b, ptn->size_b); -static inline void lp5521_remove_debugfs(struct lp5521_chip *chip) -{ - return; + lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_RUN); + usleep_range(1000, 2000); + lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM); } -#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) +static void lp5521_run_led_pattern(int mode, struct lp5521_chip *chip) { + struct lp5521_led_pattern *ptn; 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); + int num_patterns = chip->pdata->num_patterns; - for (i = 0 ; i < ARRAY_SIZE(mode1_red) ; i++) - lp5521_write(cl, LP5521_REG_R_PROG_MEM + i, - mode1_red[i]); + if (mode > num_patterns || !(chip->pdata->patterns)) + return; - for (i = 0 ; i < ARRAY_SIZE(mode1_green) ; i++) - lp5521_write(cl, LP5521_REG_G_PROG_MEM + i, - mode1_green[i]); + if (mode == PATTERN_OFF) { + lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_DEFAULT); + usleep_range(1000, 2000); + lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); + } else { + ptn = lp5521_get_pattern(chip, mode); + if (!ptn) + return; - lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_RUN); - lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM); + _run_led_pattern(chip, ptn); } } -static ssize_t lp5521_store_pattern(struct device *dev, +static ssize_t store_led_pattern(struct device *dev, struct device_attribute *attr, - const char *buf, - size_t count) + const char *buf, size_t len) { + struct lp5521_chip *chip = i2c_get_clientdata(to_i2c_client(dev)); unsigned long val; int ret; @@ -781,29 +646,141 @@ static ssize_t lp5521_store_pattern(struct device *dev, if (ret) return ret; - lp5521_program_pattern(val, g_chip); + lp5521_run_led_pattern(val, chip); - return count; + return len; } -static struct device_attribute lp5521_pattern_attr = { - .attr = { - .name = "led-pattern", - .mode = 0644, - }, - .store = lp5521_store_pattern, -}; +static void _set_pwm_cmd(struct lp5521_pattern_cmd *cmd, unsigned int color) +{ + u8 r = (color >> 16) & 0xFF; + u8 g = (color >> 8) & 0xFF; + u8 b = color & 0xFF; + + cmd->r[cmd->pc_r++] = CMD_SET_PWM; + cmd->r[cmd->pc_r++] = r; + cmd->g[cmd->pc_g++] = CMD_SET_PWM; + cmd->g[cmd->pc_g++] = g; + cmd->b[cmd->pc_b++] = CMD_SET_PWM; + cmd->b[cmd->pc_b++] = b; +} -static int lp5521_create_pattern_nodes(struct lp5521_chip *chip) +static void _set_wait_cmd(struct lp5521_pattern_cmd *cmd, + unsigned int ms, u8 jump) { - return device_create_file(chip->led_dev, &lp5521_pattern_attr); + u8 msb, lsb; + u16 branch; + int loop = ms / WAIT_UNIT; + + /* wait command */ + cmd->r[cmd->pc_r++] = CMD_WAIT_MSB; + cmd->r[cmd->pc_r++] = CMD_WAIT_LSB; + cmd->g[cmd->pc_g++] = CMD_WAIT_MSB; + cmd->g[cmd->pc_g++] = CMD_WAIT_LSB; + cmd->b[cmd->pc_b++] = CMD_WAIT_MSB; + cmd->b[cmd->pc_b++] = CMD_WAIT_LSB; + + /* branch command : if wait time is bigger than 980ms, + branch is used for command looping */ + if (loop > 1) { + branch = (5 << 13) | ((loop - 1) << 7) | jump; + msb = (branch >> 8) & 0xFF; + lsb = branch & 0xFF; + + cmd->r[cmd->pc_r++] = msb; + cmd->r[cmd->pc_r++] = lsb; + cmd->g[cmd->pc_g++] = msb; + cmd->g[cmd->pc_g++] = lsb; + cmd->b[cmd->pc_b++] = msb; + cmd->b[cmd->pc_b++] = lsb; + } } -static void lp5521_remove_pattern_nodes(struct lp5521_chip *chip) +static ssize_t store_led_blink(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) { - device_remove_file(chip->led_dev, &lp5521_pattern_attr); + struct lp5521_chip *chip = i2c_get_clientdata(to_i2c_client(dev)); + unsigned int rgb = 0, on = 0, off = 0; + struct lp5521_led_pattern ptn = { }; + struct lp5521_pattern_cmd cmd = { }; + u8 jump_pc; + + sscanf(buf, "0x%06x %d %d", &rgb, &on, &off); + + if (on > MAX_BLINK_TIME || off > MAX_BLINK_TIME) + return len; + + lp5521_run_led_pattern(PATTERN_OFF, chip); + + if (on == 0 && off == 0) { + _set_pwm_cmd(&cmd, rgb); + } else { + jump_pc = 0; + _set_pwm_cmd(&cmd, rgb); + _set_wait_cmd(&cmd, on, jump_pc); + jump_pc = cmd.pc_r / 2; /* 16bit size program counter */ + _set_pwm_cmd(&cmd, 0); + _set_wait_cmd(&cmd, off, jump_pc); + } + + ptn.r = cmd.r; + ptn.size_r = cmd.pc_r; + ptn.g = cmd.g; + ptn.size_g = cmd.pc_g; + ptn.b = cmd.b; + ptn.size_b = cmd.pc_b; + + _run_led_pattern(chip, &ptn); + + return len; } +/* led class device attributes */ +static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current); +static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); + +static struct attribute *lp5521_led_attributes[] = { + &dev_attr_led_current.attr, + &dev_attr_max_current.attr, + NULL, +}; + +static struct attribute_group lp5521_led_attribute_group = { + .attrs = lp5521_led_attributes +}; + +/* device attributes */ +static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, + show_engine1_mode, store_engine1_mode); +static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUSR, + show_engine2_mode, store_engine2_mode); +static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUSR, + show_engine3_mode, store_engine3_mode); +static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load); +static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load); +static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load); +static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); +static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, store_led_pattern); +static DEVICE_ATTR(led_blink, S_IWUSR, NULL, store_led_blink); + +static struct attribute *lp5521_attributes[] = { + &dev_attr_engine1_mode.attr, + &dev_attr_engine2_mode.attr, + &dev_attr_engine3_mode.attr, + &dev_attr_selftest.attr, + &dev_attr_engine1_load.attr, + &dev_attr_engine2_load.attr, + &dev_attr_engine3_load.attr, + &dev_attr_led_pattern.attr, + &dev_attr_led_blink.attr, + NULL +}; + +static const struct attribute_group lp5521_group = { + .attrs = lp5521_attributes, +}; + static int lp5521_register_sysfs(struct i2c_client *client) { struct device *dev = &client->dev; @@ -848,7 +825,6 @@ static int __devinit lp5521_init_led(struct lp5521_led *led, } led->cdev.brightness_set = lp5521_set_brightness; - if (pdata->led_config[chan].name) { led->cdev.name = pdata->led_config[chan].name; } else { @@ -879,6 +855,7 @@ static int __devinit lp5521_probe(struct i2c_client *client, struct lp5521_chip *chip; struct lp5521_platform_data *pdata; int ret, i, led; + u8 buf; chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (!chip) @@ -886,11 +863,7 @@ 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) { @@ -921,6 +894,20 @@ static int __devinit lp5521_probe(struct i2c_client *client, * Exact value is not available. 10 - 20ms * appears to be enough for reset. */ + + /* + * Make sure that the chip is reset by reading back the r channel + * current reg. This is dummy read is required on some platforms - + * otherwise further access to the R G B channels in the + * LP5521_REG_ENABLE register will not have any effect - strange! + */ + ret = lp5521_read(client, LP5521_REG_R_CURRENT, &buf); + if (ret || buf != LP5521_REG_R_CURR_DEFAULT) { + dev_err(&client->dev, "error in resetting chip\n"); + goto fail2; + } + usleep_range(10000, 20000); + ret = lp5521_detect(client); if (ret) { @@ -940,7 +927,6 @@ 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) @@ -969,10 +955,6 @@ 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++) { @@ -989,24 +971,12 @@ 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) +static int __devexit 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_run_led_pattern(PATTERN_OFF, chip); lp5521_unregister_sysfs(client); for (i = 0; i < chip->num_leds; i++) { @@ -1033,7 +1003,7 @@ static struct i2c_driver lp5521_driver = { .name = "lp5521", }, .probe = lp5521_probe, - .remove = lp5521_remove, + .remove = __devexit_p(lp5521_remove), .id_table = lp5521_id, }; diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c index bdbb953..05499b8 100644 --- a/drivers/leds/leds-max77693.c +++ b/drivers/leds/leds-max77693.c @@ -18,6 +18,12 @@ #include <linux/mfd/max77693.h> #include <linux/mfd/max77693-private.h> #include <linux/leds-max77693.h> +#include <linux/ctype.h> + +#ifdef CONFIG_LEDS_SWITCH +#include <linux/gpio.h> +#define FLASH_SWITCH_REMOVED_REVISION 0x05 +#endif struct max77693_led_data { struct led_classdev led; @@ -73,6 +79,9 @@ static u8 led_current_shift[MAX77693_LED_MAX] = { 4, }; +extern struct class *camera_class; /*sys/class/camera*/ +struct device *flash_dev; + static int max77693_set_bits(struct i2c_client *client, const u8 reg, const u8 mask, const u8 inval) { @@ -104,14 +113,17 @@ static void print_all_reg_value(struct i2c_client *client) static int max77693_led_get_en_value(struct max77693_led_data *led_data, int on) { if (on) - return 0x03; + return 0x03; /*triggered via serial interface*/ +#if 0 if (led_data->data->cntrl_mode == MAX77693_LED_CTRL_BY_I2C) return 0x00; - else if (led_data->data->id < 2) - return 0x01; +#endif + + if (led_data->data->id < 2) + return 0x01; /*Flash triggered via FLASHEN*/ else - return 0x02; + return 0x02; /*Torch triggered via TORCHEN*/ } static void max77693_led_set(struct led_classdev *led_cdev, enum led_brightness value) @@ -163,8 +175,8 @@ static void led_set(struct max77693_led_data *led_data) if (unlikely(ret)) goto error_set_bits; - /* Turn off LED */ - value = max77693_led_get_en_value(led_data, 0); + /* Turn on LED */ + value = max77693_led_get_en_value(led_data, 1); ret = max77693_set_bits(led_data->i2c, MAX77693_LED_REG_FLASH_EN, led_en_mask[id], value << led_en_shift[id]); @@ -172,13 +184,6 @@ static void led_set(struct max77693_led_data *led_data) 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: @@ -211,7 +216,7 @@ static int max77693_led_setup(struct max77693_led_data *led_data) 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); + MAX77693_LED_REG_MAX_FLASH1, 0x80); ret |= max77693_write_reg(led_data->i2c, MAX77693_LED_REG_MAX_FLASH2, 0x00); @@ -227,7 +232,7 @@ static int max77693_led_setup(struct max77693_led_data *led_data) (data->timer | data->timer_mode << 7)); } else { ret |= max77693_write_reg(led_data->i2c, reg_led_timer[id], - 0xC0); + 0x40); } /* Set current */ @@ -238,6 +243,34 @@ static int max77693_led_setup(struct max77693_led_data *led_data) return ret; } +static ssize_t max77693_flash(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + char *after; + unsigned long state = simple_strtoul(buf, &after, 10); + size_t count = after - buf; + + if (isspace(*after)) + count++; + + if (count == size) { + ret = count; + + if (state > led_cdev->max_brightness) + state = led_cdev->max_brightness; + led_cdev->brightness = state; + if (!(led_cdev->flags & LED_SUSPENDED)) + led_cdev->brightness_set(led_cdev, state); + } + + return ret; +} + +static DEVICE_ATTR(rear_flash, S_IWUSR|S_IWGRP|S_IROTH, + NULL, max77693_flash); + static int max77693_led_probe(struct platform_device *pdev) { int ret = 0; @@ -284,7 +317,7 @@ static int max77693_led_probe(struct platform_device *pdev) 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.flags = 0; led_data->led.max_brightness = data->id < 2 ? MAX_FLASH_DRV_LEVEL : MAX_TORCH_DRV_LEVEL; @@ -303,12 +336,32 @@ static int max77693_led_probe(struct platform_device *pdev) ret = max77693_led_setup(led_data); if (unlikely(ret)) { pr_err("unable to register LED\n"); + mutex_destroy(&led_data->lock); led_classdev_unregister(&led_data->led); kfree(led_data); ret = -EFAULT; } } /* print_all_reg_value(max77693->i2c); */ + + flash_dev = device_create(camera_class, NULL, 0, led_datas[2], "flash"); + if (flash_dev < 0) + pr_err("Failed to create device(flash)!\n"); + + if (device_create_file(flash_dev, &dev_attr_rear_flash) < 0) { + pr_err("failed to create device file, %s\n", + dev_attr_rear_flash.attr.name); + } + +#ifdef CONFIG_LEDS_SWITCH + if (system_rev < FLASH_SWITCH_REMOVED_REVISION) { + if (gpio_request(GPIO_CAM_SW_EN, "CAM_SW_EN")) + pr_err("failed to request CAM_SW_EN\n"); + else + gpio_direction_output(GPIO_CAM_SW_EN, 1); + } +#endif + return ret; } @@ -328,9 +381,24 @@ static int __devexit max77693_led_remove(struct platform_device *pdev) } kfree(led_datas); + device_remove_file(flash_dev, &dev_attr_rear_flash); + device_destroy(camera_class, 0); + class_destroy(camera_class); + return 0; } +void max77693_led_shutdown(struct device *dev) +{ + struct max77693_led_data **led_datas = dev_get_drvdata(dev); + + /* Turn off LED */ + max77693_set_bits(led_datas[2]->i2c, + MAX77693_LED_REG_FLASH_EN, + led_en_mask[2], + 0x02 << led_en_shift[2]); +} + static struct platform_driver max77693_led_driver = { .probe = max77693_led_probe, @@ -339,6 +407,7 @@ static struct platform_driver max77693_led_driver = { .name = "max77693-led", .owner = THIS_MODULE, + .shutdown = max77693_led_shutdown, }, }; diff --git a/drivers/leds/leds-max8997.c b/drivers/leds/leds-max8997.c index 54c4a04..2b7a8c7 100644 --- a/drivers/leds/leds-max8997.c +++ b/drivers/leds/leds-max8997.c @@ -39,7 +39,7 @@ static inline int led_regulator_get_max_brightness(struct regulator *supply) int voltage = regulator_list_voltage(supply, 0); /*TODO*/ -#if defined(CONFIG_MACH_Q1_BD) +#if defined(CONFIG_MACH_Q1_BD) || defined(CONFIG_MACH_U1_NA_USCC) return LED_BRIGHTNESS_MAX_LEVEL; #else return 1; @@ -126,7 +126,7 @@ static void regulator_led_set_value(struct regulator_led *led) /* 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 !defined(CONFIG_MACH_Q1_BD) && !defined(CONFIG_MACH_U1_NA_USCC) if (regulator_is_enabled(movie) > 0) { pr_info("%s: led_movie is enabled.\n", __func__); goto end; @@ -146,7 +146,7 @@ static void regulator_led_set_value(struct regulator_led *led) } #endif -#if defined(CONFIG_MACH_Q1_BD) +#if defined(CONFIG_MACH_Q1_BD) || defined(CONFIG_MACH_U1_NA_USCC) switch (led->value) { case LED_BRIGHTNESS_LEVEL1: regulator_set_current_limit(led->vcc, 10000, 20000); |