- provide clean sysfs to control RGB channel directly.
  They are looked like the following:

        channel_mode
        channel_pwm
        channel_cur

- add platform data to export RGB channel accordingly.

Signed-off-by: Matt Hsu <matt_...@openmoko.org>
---
 drivers/leds/leds-lp5521.c |  637 ++++++++++++++------------------------------
 include/linux/lp5521.h     |   67 +++++-
 2 files changed, 270 insertions(+), 434 deletions(-)

diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
index 7bbf79a..0415b9e 100644
--- a/drivers/leds/leds-lp5521.c
+++ b/drivers/leds/leds-lp5521.c
@@ -1,10 +1,7 @@
-/*
- * drivers/leds/leds-lp5521.c
- *
- * Copyright (C) 2007 Nokia Corporation
+/* NS LP5521 Programmable LED driver.
  *
- * Written by Mathias Nyman <mathias.ny...@nokia.com>
- *           Matt Hsu      <matt_...@openmoko.org>
+ * (C) 2009 by Openmoko, Inc.
+ * Author: Matt Hsu <matt_...@openmoko.org>
  *
  * 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
@@ -20,491 +17,247 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/i2c.h>
 #include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
 
 #include <linux/lp5521.h>
 
-#define LP5521_DRIVER_NAME             "lp5521"
-
-#ifdef LED_CONNECTED_WRONG
-#define LP5521_REG_R_PWM               0x04
-#define LP5521_REG_B_PWM               0x02
-#else
-#define LP5521_REG_R_PWM               0x02
-#define LP5521_REG_B_PWM               0x04
-#endif
-#define LP5521_REG_ENABLE              0x00
-#define LP5521_REG_OP_MODE             0x01
-#define LP5521_REG_G_PWM               0x03
-#define LP5521_REG_R_CNTRL             0x05
-#define LP5521_REG_G_CNTRL             0x06
-#define LP5521_REG_B_CNTRL             0x07
-#define LP5521_REG_MISC                        0x08
-#define LP5521_REG_R_CHANNEL_PC                0x09
-#define LP5521_REG_G_CHANNEL_PC                0x0a
-#define LP5521_REG_B_CHANNEL_PC                0x0b
-#define LP5521_REG_STATUS              0x0c
-#define LP5521_REG_RESET               0x0d
-#define LP5521_REG_GPO                 0x0e
-#define LP5521_REG_R_PROG_MEM          0x10
-#define LP5521_REG_G_PROG_MEM          0x30
-#define LP5521_REG_B_PROG_MEM          0x50
-
-#define LP5521_MODE_LOAD               "load"
-#define LP5521_MODE_RUN                        "run"
-#define LP5521_MODE_DIRECT_CONTROL     "direct"
-
-#define LP5521_CURRENT_1m5             0x0f
-#define LP5521_CURRENT_3m1             0x1f
-#define LP5521_CURRENT_4m7             0x2f
-#define LP5521_CURRENT_6m3             0x3f
-#define LP5521_CURRENT_7m9             0x4f
-#define LP5521_CURRENT_9m5             0x5f
-#define LP5521_CURRENT_11m1            0x6f
-#define LP5521_CURRENT_12m7            0x7f
-#define LP5521_CURRENT_14m3            0x8f
-#define LP5521_CURRENT_15m9            0x9f
-#define LP5521_CURRENT_17m5            0xaf
-#define LP5521_CURRENT_19m1            0xbf
-#define LP5521_CURRENT_20m7            0xcf
-#define LP5521_CURRENT_22m3            0xdf
-#define LP5521_CURRENT_23m9            0xef
-#define LP5521_CURRENT_25m5            0xff
-
-#define LP5521_PROGRAM_LENGTH          32      /* in bytes */
-
-struct lp5521_chip {
-       struct mutex            lock;
-       struct i2c_client       *client;
-       char                    *mode;
-       int                     red;
-       int                     green;
-       int                     blue;
-};
-
-static int lp5521_set_mode(struct lp5521_chip *chip, char *mode);
+#define LP5521_DRIVER_NAME              "lp5521"
 
-static int lp5521_write(struct i2c_client *client, u8 reg, u8 value)
+static int __lp5521_reg_write(struct lp5521 *lp, u8 reg, u8 value)
 {
-       return i2c_smbus_write_byte_data(client, reg, value);
+       return i2c_smbus_write_byte_data(lp->client, reg, value);
 }
 
-static int lp5521_read(struct i2c_client *client, u8 reg, u8 *buf)
+static int reg_write(struct lp5521 *lp, u_int8_t reg, u_int8_t val)
 {
-       s32 ret = i2c_smbus_read_byte_data(client, reg);
+       int ret;
 
-       if (ret < 0)
-               return -EIO;
+       mutex_lock(&lp->lock);
+       ret = __lp5521_reg_write(lp, reg, val);
+       mutex_unlock(&lp->lock);
 
-       *buf = ret;
-       return 0;
+       return ret;
 }
 
-static int lp5521_configure(struct i2c_client *client)
+static int __lp5521_reg_read(struct lp5521 *lp, u8 reg)
 {
-       int ret = 0;
-
-       /* Enable chip and set light to logarithmic mode*/
-       ret |= lp5521_write(client, LP5521_REG_ENABLE, 0xc0);
-
-       /* setting all color pwms to direct control mode */
-       ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x3f);
+       int32_t ret;
 
-       /* setting current to 4.7 mA for all channels */
-       ret |= lp5521_write(client, LP5521_REG_R_CNTRL, LP5521_CURRENT_4m7);
-       ret |= lp5521_write(client, LP5521_REG_G_CNTRL, LP5521_CURRENT_4m7);
-       ret |= lp5521_write(client, LP5521_REG_B_CNTRL, LP5521_CURRENT_4m7);
+       ret = i2c_smbus_read_byte_data(lp->client, reg);
 
-       /* Enable auto-powersave, set charge pump to auto, red to battery */
-       ret |= lp5521_write(client, LP5521_REG_MISC, 0x3c);
-
-       /* initialize all channels pwm to zero */
-       ret |= lp5521_write(client, LP5521_REG_R_PWM, 0);
-       ret |= lp5521_write(client, LP5521_REG_G_PWM, 0);
-       ret |= lp5521_write(client, LP5521_REG_B_PWM, 0);
-
-       /* Not much can be done about errors at this point */
        return ret;
 }
 
-static int lp5521_load_program(struct lp5521_chip *chip, u8 *pattern)
+static u_int8_t reg_read(struct lp5521 *lp, u_int8_t reg)
 {
-       struct i2c_client *client = chip->client;
-       int ret = 0;
+       int32_t ret;
 
-       /* Enter load program mode for all led channels */
-       ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x15); /* 0001 0101 */
-       if (ret)
-               return ret;
-
-       if (chip->red)
-               ret |= i2c_smbus_write_i2c_block_data(client,
-                                                     LP5521_REG_R_PROG_MEM,
-                                                     LP5521_PROGRAM_LENGTH,
-                                                     pattern);
-       if (chip->green)
-               ret |= i2c_smbus_write_i2c_block_data(client,
-                                                     LP5521_REG_G_PROG_MEM,
-                                                     LP5521_PROGRAM_LENGTH,
-                                                     pattern);
-       if (chip->blue)
-               ret |= i2c_smbus_write_i2c_block_data(client,
-                                                     LP5521_REG_B_PROG_MEM,
-                                                     LP5521_PROGRAM_LENGTH,
-                                                     pattern);
+       mutex_lock(&lp->lock);
+       ret = __lp5521_reg_read(lp, reg);
+       mutex_unlock(&lp->lock);
 
-       return ret;
+       return ret & 0xff;
 }
 
-static int lp5521_run_program(struct lp5521_chip *chip)
+static int reg_set_bit_mask(struct lp5521 *lp,
+                           u_int8_t reg, u_int8_t mask, u_int8_t val)
 {
-       struct i2c_client *client = chip->client;
        int ret;
-       u8 mask = 0xc0;
-       u8 exec_state = 0;
-       u8 enable_reg;
-
-       ret = lp5521_read(client, LP5521_REG_ENABLE, &enable_reg);
-       if (ret)
-               goto fail;
-
-       enable_reg &= mask;
+       u_int8_t tmp;
 
-       /* set all active channels exec state to countinous run*/
-       exec_state |= (chip->red   << 5);
-       exec_state |= (chip->green << 3);
-       exec_state |= (chip->blue  << 1);
+       val &= mask;
 
-       enable_reg |= exec_state;
+       mutex_lock(&lp->lock);
 
-       ret |= lp5521_write(client, LP5521_REG_ENABLE, enable_reg);
+       tmp = __lp5521_reg_read(lp, reg);
+       tmp &= ~mask;
+       tmp |= val;
+       ret = __lp5521_reg_write(lp, reg, tmp);
 
-       /* set op-mode to run for active channels, disabled for others */
-       ret |= lp5521_write(client, LP5521_REG_OP_MODE, exec_state);
+       mutex_unlock(&lp->lock);
 
-fail:
        return ret;
 }
 
-/*--------------------------------------------------------------*/
-/*                     Sysfs interface                         */
-/*--------------------------------------------------------------*/
+static const char *lp5521_ch_name[] = {
+       "blue", "green", "red",
+};
 
-static ssize_t show_active_channels(struct device *dev,
-                           struct device_attribute *attr,
-                           char *buf)
+static inline int channel_id_by_name(const char *name)
 {
-       struct lp5521_chip *chip = dev_get_drvdata(dev);
-       char channels[4];
-       int pos = 0;
-
-#ifdef LED_CONNECTED_WRONG
-       if (chip->blue)
-               pos += sprintf(channels + pos, "r");
-       if (chip->green)
-               pos += sprintf(channels + pos, "g");
-       if (chip->red)
-               pos += sprintf(channels + pos, "b");
-
-#else
-       if (chip->red)
-               pos += sprintf(channels + pos, "r");
-       if (chip->green)
-               pos += sprintf(channels + pos, "g");
-       if (chip->blue)
-               pos += sprintf(channels + pos, "b");
-#endif
-
-       channels[pos] = '\0';
-
-       return sprintf(buf, "%s\n", channels);
+       int channel_id = -1;
+
+       if (!strncmp(name, lp5521_ch_name[LP5521_BLUE],
+                               strlen(lp5521_ch_name[LP5521_BLUE])))
+               channel_id = LP5521_BLUE;
+       else if (!strncmp(name, lp5521_ch_name[LP5521_GREEN],
+                               strlen(lp5521_ch_name[LP5521_GREEN])))
+               channel_id = LP5521_GREEN;
+       else if (!strncmp(name, lp5521_ch_name[LP5521_RED],
+                               strlen(lp5521_ch_name[LP5521_RED])))
+               channel_id = LP5521_RED;
+
+       return channel_id;
 }
 
-static ssize_t store_active_channels(struct device *dev,
-                            struct device_attribute *attr,
-                            const char *buf, size_t len)
-{
-       struct lp5521_chip *chip = dev_get_drvdata(dev);
-
-       chip->red = 0;
-       chip->green = 0;
-       chip->blue = 0;
-
-#ifdef LED_CONNECTED_WRONG
-       if (strchr(buf, 'r') != NULL)
-               chip->blue = 1;
-       if (strchr(buf, 'b') != NULL)
-               chip->red = 1;
-#else
-       if (strchr(buf, 'r') != NULL)
-               chip->red = 1;
-       if (strchr(buf, 'b') != NULL)
-               chip->blue = 1;
-#endif
-       if (strchr(buf, 'g') != NULL)
-               chip->green = 1;
-
-       return len;
-}
+static const char *lp5521_ch_mode[] = {
+       "disable", "load", "run",
+       "direct",
+};
 
-static ssize_t show_color(struct device *dev,
-                           struct device_attribute *attr,
-                           char *buf)
+/*
+ * Individual mode control
+ */
+static ssize_t show_mode(struct device *dev, struct device_attribute
+                                       *attr, char *buf)
 {
        struct i2c_client *client = to_i2c_client(dev);
-       int ret = 0;
-       u8 r, g, b;
+       struct lp5521 *lp = i2c_get_clientdata(client);
+       int id;
+       uint8_t val;
 
-       ret |= lp5521_read(client, LP5521_REG_R_PWM, &r);
-       ret |= lp5521_read(client, LP5521_REG_G_PWM, &g);
-       ret |= lp5521_read(client, LP5521_REG_B_PWM, &b);
+       id = channel_id_by_name(attr->attr.name);
+       val = reg_read(lp, LP5521_REG_OP_MODE);
 
-       if (ret)
-               return ret;
+       val = val >> (id * 2);
+       val &= 0x3;
 
-       return sprintf(buf, "%.2x:%.2x:%.2x\n", r, g, b);
+       return sprintf(buf, "%s\n", lp5521_ch_mode[val]);
 }
 
-static ssize_t store_color(struct device *dev,
-                            struct device_attribute *attr,
-                            const char *buf, size_t len)
+static ssize_t set_mode(struct device *dev, struct device_attribute *attr,
+                       const char *buf, size_t count)
 {
        struct i2c_client *client = to_i2c_client(dev);
-       struct lp5521_chip *chip = i2c_get_clientdata(client);
-       int ret;
-       unsigned r, g, b;
-
+       struct lp5521 *lp = i2c_get_clientdata(client);
+       int id;
+       uint8_t mask, i;
 
-       ret = sscanf(buf, "%2x:%2x:%2x", &r, &g, &b);
-       if (ret != 3)
-               return  -EINVAL;
+       id = channel_id_by_name(attr->attr.name);
 
-       mutex_lock(&chip->lock);
+       mask = (0x3 << (id * 2));
 
-       ret = lp5521_write(client, LP5521_REG_R_PWM, (u8)r);
-       ret = lp5521_write(client, LP5521_REG_G_PWM, (u8)g);
-       ret = lp5521_write(client, LP5521_REG_B_PWM, (u8)b);
-
-       mutex_unlock(&chip->lock);
-
-       return len;
-}
-
-static ssize_t store_load(struct device *dev,
-                            struct device_attribute *attr,
-                            const char *buf, size_t len)
-{
-       struct lp5521_chip *chip = dev_get_drvdata(dev);
-       int  ret, nrchars, offset = 0, i = 0;
-       char c[3];
-       unsigned cmd;
-       u8 pattern[LP5521_PROGRAM_LENGTH] = {0};
-
-       while ((offset < len - 1) && (i < LP5521_PROGRAM_LENGTH)) {
-
-               /* separate sscanfs because length is working only for %s */
-               ret = sscanf(buf + offset, "%2s%n ", c, &nrchars);
-               ret = sscanf(c, "%2x", &cmd);
-               if (ret != 1)
-                       goto fail;
-               pattern[i] = (u8)cmd;
-
-               offset += nrchars;
-               i++;
+       for (i = LP5521_REG_OP_MODE; i <= LP5521_MODE_DIRECT; i++) {
+               if (!strncmp(buf, lp5521_ch_mode[i], 
strlen(lp5521_ch_mode[i]))) {
+                       reg_set_bit_mask(lp,
+                               LP5521_REG_OP_MODE, mask, (i << (id * 2)));
+               }
        }
 
-       /* pattern commands are always two bytes long */
-       if (i % 2)
-               goto fail;
-
-       mutex_lock(&chip->lock);
-
-       ret = lp5521_load_program(chip, pattern);
-       mutex_unlock(&chip->lock);
-
-       if (ret) {
-               dev_err(dev, "lp5521 failed loading pattern\n");
-               return ret;
-       }
-
-       return len;
-fail:
-       dev_err(dev, "lp5521 wrong pattern format\n");
-       return -EINVAL;
+       return count;
 }
 
-static ssize_t show_mode(struct device *dev,
-                        struct device_attribute *attr,
-                        char *buf)
-{
-       struct lp5521_chip *chip = dev_get_drvdata(dev);
-
-       return sprintf(buf, "%s\n", chip->mode);
-}
-
-static ssize_t store_mode(struct device *dev,
-                         struct device_attribute *attr,
-                         const char *buf, size_t len)
-{
-       struct lp5521_chip *chip = dev_get_drvdata(dev);
-
-       mutex_lock(&chip->lock);
-
-       if (!strncmp(buf, "run", 3))
-               lp5521_set_mode(chip, LP5521_MODE_RUN);
-       else if (!strncmp(buf, "load", 4))
-               lp5521_set_mode(chip, LP5521_MODE_LOAD);
-       else if (!strncmp(buf, "direct", 6))
-               lp5521_set_mode(chip, LP5521_MODE_DIRECT_CONTROL);
+static DEVICE_ATTR(red_mode, S_IRUGO | S_IWUSR, show_mode, set_mode);
+static DEVICE_ATTR(green_mode, S_IRUGO | S_IWUSR, show_mode, set_mode);
+static DEVICE_ATTR(blue_mode, S_IRUGO | S_IWUSR, show_mode, set_mode);
 
-       mutex_unlock(&chip->lock);
-
-       return len;
-}
-
-static ssize_t show_current(struct device *dev,
-                           struct device_attribute *attr,
-                           char *buf)
+/*
+ * Individual pwm control
+ */
+static ssize_t show_pwm(struct device *dev, struct device_attribute
+                                       *attr, char *buf)
 {
        struct i2c_client *client = to_i2c_client(dev);
-       int ret = 0;
-       u8 r_curr, g_curr, b_curr;
-
-       ret |= lp5521_read(client, LP5521_REG_R_CNTRL, &r_curr);
-       ret |= lp5521_read(client, LP5521_REG_G_CNTRL, &g_curr);
-       ret |= lp5521_read(client, LP5521_REG_B_CNTRL, &b_curr);
+       struct lp5521 *lp = i2c_get_clientdata(client);
+       int id;
+       uint8_t val;
 
-       if (ret)
-               return ret;
+       id = channel_id_by_name(attr->attr.name);
+       val = reg_read(lp, LP5521_REG_B_PWM-id);
 
-       r_curr = r_curr >> 4;
-       g_curr = g_curr >> 4;
-       b_curr = b_curr >> 4;
-
-       if (r_curr == g_curr && g_curr == b_curr)
-               return sprintf(buf, "%x\n", r_curr);
-       else
-               return sprintf(buf, "%x %x %x\n", r_curr, g_curr, b_curr);
+       return sprintf(buf, "%d\n", val);
 }
 
-static ssize_t store_current(struct device *dev,
-                            struct device_attribute *attr,
-                            const char *buf, size_t len)
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
+                       const char *buf, size_t count)
 {
-       struct lp5521_chip *chip = dev_get_drvdata(dev);
-       struct i2c_client *client = chip->client;
-       int ret;
-       unsigned curr;
-
-       ret = sscanf(buf, "%1x", &curr);
-       if (ret != 1)
-               return  -EINVAL;
-
-       /* current level is determined by the 4 upper bits, rest is ones */
-       curr = (curr << 4) | 0x0f;
-
-       mutex_lock(&chip->lock);
-
-       ret |= lp5521_write(client, LP5521_REG_R_CNTRL, (u8)curr);
-       ret |= lp5521_write(client, LP5521_REG_G_CNTRL, (u8)curr);
-       ret |= lp5521_write(client, LP5521_REG_B_CNTRL, (u8)curr);
+       int id;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lp5521 *lp = i2c_get_clientdata(client);
+       unsigned int pwm = simple_strtoul(buf, NULL, 10);
 
-       mutex_unlock(&chip->lock);
+       id = channel_id_by_name(attr->attr.name);
+       reg_write(lp, LP5521_REG_B_PWM-id, pwm);
 
-       return len;
+       return count;
 }
 
-static DEVICE_ATTR(color, S_IRUGO | S_IWUGO, show_color, store_color);
-static DEVICE_ATTR(load, S_IWUGO, NULL, store_load);
-static DEVICE_ATTR(mode, S_IRUGO | S_IWUGO, show_mode, store_mode);
-static DEVICE_ATTR(active_channels, S_IRUGO | S_IWUGO,
-                  show_active_channels, store_active_channels);
-static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current, 
store_current);
+static DEVICE_ATTR(red_pwm, S_IRUGO | S_IWUSR, show_pwm, set_pwm);
+static DEVICE_ATTR(green_pwm, S_IRUGO | S_IWUSR, show_pwm, set_pwm);
+static DEVICE_ATTR(blue_pwm, S_IRUGO | S_IWUSR, show_pwm, set_pwm);
 
-static int lp5521_register_sysfs(struct i2c_client *client)
+/*
+ * Individual current control
+ */
+static ssize_t show_cur(struct device *dev, struct device_attribute
+                                       *attr, char *buf)
 {
-       struct device *dev = &client->dev;
-       int ret;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lp5521 *lp = i2c_get_clientdata(client);
+       int id;
+       uint8_t val;
 
-       ret = device_create_file(dev, &dev_attr_color);
-       if (ret)
-               goto fail1;
-       ret = device_create_file(dev, &dev_attr_load);
-       if (ret)
-               goto fail2;
-       ret = device_create_file(dev, &dev_attr_active_channels);
-       if (ret)
-               goto fail3;
-       ret = device_create_file(dev, &dev_attr_mode);
-       if (ret)
-               goto fail4;
-       ret = device_create_file(dev, &dev_attr_led_current);
-       if (ret)
-               goto fail5;
-       return 0;
+       id = channel_id_by_name(attr->attr.name);
+       val = reg_read(lp, LP5521_REG_B_CUR-id);
 
-fail5:
-       device_remove_file(dev, &dev_attr_mode);
-fail4:
-       device_remove_file(dev, &dev_attr_active_channels);
-fail3:
-       device_remove_file(dev, &dev_attr_load);
-fail2:
-       device_remove_file(dev, &dev_attr_color);
-fail1:
-       return ret;
+       return sprintf(buf, "%d (100uA)\n", val);
 }
 
-#ifdef MODULE
-static void lp5521_unregister_sysfs(struct i2c_client *client)
+static ssize_t set_cur(struct device *dev, struct device_attribute *attr,
+                       const char *buf, size_t count)
 {
-       struct lp5521_chip *chip = i2c_get_clientdata(client);
-       struct device *dev = &client->dev;
+       int id;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lp5521 *lp = i2c_get_clientdata(client);
+       unsigned int cur = simple_strtoul(buf, NULL, 10);
 
-       device_remove_file(dev, &dev_attr_led_current);
-       device_remove_file(dev, &dev_attr_mode);
-       device_remove_file(dev, &dev_attr_active_channels);
-       device_remove_file(dev, &dev_attr_color);
+       id = channel_id_by_name(attr->attr.name);
+       reg_write(lp, LP5521_REG_B_CUR-id, cur);
 
-       if (!strcmp(chip->mode, LP5521_MODE_LOAD))
-               device_remove_file(dev, &dev_attr_load);
+       return count;
 }
-#endif
 
-/*--------------------------------------------------------------*/
-/*                     Set chip operating mode                 */
-/*--------------------------------------------------------------*/
-
-static int lp5521_set_mode(struct lp5521_chip *chip, char *mode)
-{
-       struct i2c_client *client = chip->client ;
-       int ret = 0;
+static DEVICE_ATTR(red_cur, S_IRUGO | S_IWUSR, show_cur, set_cur);
+static DEVICE_ATTR(green_cur, S_IRUGO | S_IWUSR, show_cur, set_cur);
+static DEVICE_ATTR(blue_cur, S_IRUGO | S_IWUSR, show_cur, set_cur);
 
-       /* if in that mode already do nothing, except for run */
-       if (!strcmp(mode, chip->mode) && strcmp(mode, LP5521_MODE_RUN))
-               return 0;
+static struct attribute *lp_sysfs_entries[16];
 
-       if (!strcmp(mode, LP5521_MODE_RUN))
-               ret = lp5521_run_program(chip);
+static struct attribute_group lp_attr_group = {
+       .name   = NULL,
+       .attrs  = lp_sysfs_entries,
+};
 
-       if (!strcmp(mode, LP5521_MODE_LOAD))
-               ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x15);
+static void populate_sysfs_group(struct lp5521 *lp)
+{
+       int i = 0;
 
-       if (!strcmp(mode, LP5521_MODE_DIRECT_CONTROL))
-               ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x3F);
+       if (lp->pdata->channels[LP5521_RED] & LP5521_CONNECTED) {
+               lp_sysfs_entries[i++] = &dev_attr_red_mode.attr;
+               lp_sysfs_entries[i++] = &dev_attr_red_pwm.attr;
+               lp_sysfs_entries[i++] = &dev_attr_red_cur.attr;
+       }
 
-       chip->mode = mode;
+       if (lp->pdata->channels[LP5521_GREEN] & LP5521_CONNECTED) {
+               lp_sysfs_entries[i++] = &dev_attr_green_mode.attr;
+               lp_sysfs_entries[i++] = &dev_attr_green_pwm.attr;
+               lp_sysfs_entries[i++] = &dev_attr_green_cur.attr;
+       }
 
-       return ret;
+       if (lp->pdata->channels[LP5521_BLUE] & LP5521_CONNECTED) {
+               lp_sysfs_entries[i++] = &dev_attr_blue_mode.attr;
+               lp_sysfs_entries[i++] = &dev_attr_blue_pwm.attr;
+               lp_sysfs_entries[i++] = &dev_attr_blue_cur.attr;
+       }
 }
 
-/*--------------------------------------------------------------*/
-/*                     Probe, Attach, Remove                   */
-/*--------------------------------------------------------------*/
 static struct i2c_driver lp5521_driver;
 
 #ifdef CONFIG_PM
@@ -527,60 +280,78 @@ static int lp5521_resume(struct device *dev)
 #define lp5521_resume NULL
 #endif
 
-static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id 
*id)
+static irqreturn_t lp5521_irq(int irq, void *_lp)
 {
-       struct lp5521_chip *chip;
+       struct lp5521 *lp = _lp;
+       dev_info(lp->dev, "lp5521 interrupt\n");
+
+       return IRQ_HANDLED;
+}
+
+static int __devinit
+lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+       struct lp5521 *lp;
        int ret = 0;
        struct lp5521_platform_data *pdata = client->dev.platform_data;
 
-       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
-       if (!chip)
+       lp = kzalloc(sizeof(*lp), GFP_KERNEL);
+       if (!lp)
                return -ENOMEM;
 
-       chip->client = client;
-       strncpy(client->name, LP5521_DRIVER_NAME, I2C_NAME_SIZE);
-       i2c_set_clientdata(client, chip);
+       lp->client = client;
+       lp->irq = client->irq;
+       lp->dev = &client->dev;
+       i2c_set_clientdata(client, lp);
 
-       mutex_init(&chip->lock);
+       lp->pdata = pdata;
+       mutex_init(&lp->lock);
 
        /* enter start-up mode */
        if (pdata->ext_enable)
                (pdata->ext_enable)(1);
 
-       ret = lp5521_configure(client);
-       if (ret < 0) {
-               dev_err(&client->dev, "lp5521 error configuring chip \n");
-               goto fail1;
+       reg_write(lp, LP5521_REG_ENABLE, 0x40);
+
+       /* charge pump mode and clk src selection */
+       reg_write(lp, LP5521_REG_CONFIG, 0x11);
+
+       /* allocate IRQ resource */
+       if (lp->irq) {
+               ret = request_irq(client->irq, lp5521_irq,
+                               IRQF_TRIGGER_LOW, LP5521_DRIVER_NAME, lp);
+               if (ret) {
+                       dev_err(lp->dev, "request IRQ failed\n");
+                       goto fail;
+               }
+       } else {
+               dev_err(lp->dev, "No IRQ allocated \n");
        }
 
-       /* Set default values */
-       chip->mode      = LP5521_MODE_DIRECT_CONTROL;
-       chip->red       = 1;
-       chip->green     = 1;
-       chip->blue      = 1;
+       populate_sysfs_group(lp);
 
-       ret = lp5521_register_sysfs(client);
-       if (ret)
-               dev_err(&client->dev, "lp5521 registering sysfs failed \n");
+       ret = sysfs_create_group(&client->dev.kobj, &lp_attr_group);
+
+       if (ret) {
+               dev_err(lp->dev, "error creating sysfs group\n");
+               goto fail;
+       }
 
        return ret;
 
-fail1:
-       kfree(chip);
+fail:
+       kfree(lp);
        return ret;
 }
 
-#ifdef MODULE
-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);
+       struct lp5521 *lp = i2c_get_clientdata(client);
 
-       lp5521_unregister_sysfs(client);
-       kfree(chip);
+       kfree(lp);
 
        return 0;
 }
-#endif
 
 static struct i2c_device_id lp5521_id[] = {
        {LP5521_DRIVER_NAME, },
@@ -607,9 +378,9 @@ static void __exit lp5521_exit(void)
        i2c_del_driver(&lp5521_driver);
 }
 
-MODULE_AUTHOR("Mathias Nyman <mathias.ny...@nokia.com>");
-MODULE_DESCRIPTION("lp5521 LED driver");
-MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Matt Hsu <matt_...@openmoko.org>");
+MODULE_DESCRIPTION("NS lp5521 LED driver");
+MODULE_LICENSE("GPLv2");
 
 module_init(lp5521_init);
 module_exit(lp5521_exit);
diff --git a/include/linux/lp5521.h b/include/linux/lp5521.h
index 4515a02..65f9fb0 100644
--- a/include/linux/lp5521.h
+++ b/include/linux/lp5521.h
@@ -1,10 +1,75 @@
 #ifndef _LINUX_LP5521_H_
 #define _LINUX_LP5521_H_
 
-struct lp5521_platform_data {
+#define LP5521_REG_ENABLE      0x00
+#define LP5521_REG_OP_MODE     0x01
+
+#define LP5521_REG_R_PWM       0x02
+#define LP5521_REG_G_PWM       0x03
+#define LP5521_REG_B_PWM       0x04
+
+#define LP5521_REG_R_CUR       0x05
+#define LP5521_REG_G_CUR       0x06
+#define LP5521_REG_B_CUR       0x07
+
+#define LP5521_REG_CONFIG      0x08
+
+#define LP5521_REG_R_PC                0x09
+#define LP5521_REG_G_PC                0x0a
+#define LP5521_REG_B_PC                0x0b
+
+#define LP5521_REG_STATUS      0x0c
+#define LP5521_REG_RESET       0x0d
+#define LP5521_REG_GPO         0x0e
 
+enum {
+       LP5521_NC,
+       LP5521_CONNECTED,
+};
+
+enum {
+       LP5521_BLUE,
+       LP5521_GREEN,
+       LP5521_RED,
+       LP5521_NUM_CH,
+};
+
+enum {
+       LP5521_MODE_DISABLE,
+       LP5521_MODE_LOAD,
+       LP5521_MODE_RUN,
+       LP5521_MODE_DIRECT,
+};
+
+enum CP_MODE {
+       LP5521_CPM_OFF,
+       LP5521_CPM_BY_PASS,
+       LP5521_CPM_1_5X,
+       LP5521_CPM_AUTO,
+};
+
+enum CLK_SRC {
+       LP5521_EXT_CLK,
+       LP5521_INT_CLK,
+       LP5521_AUTO_CLK,
+};
+
+#define LP5521_FEAT_TRIG       0x00000001
+#define LP5521_FEAT_GPO                0x00000002
+
+
+struct lp5521_platform_data {
+       int channels[LP5521_NUM_CH];
        /* chip enable */
        void (*ext_enable)(int level);
 };
 
+struct lp5521 {
+       struct device *dev;
+       struct i2c_client *client;
+       struct mutex lock;
+       int irq;
+
+       struct lp5521_platform_data *pdata;
+};
 #endif /* LINUX_LP5521_H */
-- 
1.5.6.5


Reply via email to