lkml.org 
[lkml]   [2009]   [Feb]   [18]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    SubjectRe: [PATCH] leds: introduce lp5521 led driver
    On Wed, Feb 18, 2009 at 01:18:13PM +0100, ext Richard Purdie wrote:
    > On Mon, 2009-02-16 at 16:35 +0200, Felipe Balbi wrote:
    > > diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
    > > new file mode 100644
    > > index 0000000..455da20
    > > --- /dev/null
    > > +++ b/drivers/leds/leds-lp5521.c
    > > +
    > > +#include <linux/module.h>
    > > +#include <linux/init.h>
    > > +#include <linux/i2c.h>
    > > +#include <linux/leds.h>
    > > +#include <linux/mutex.h>
    > > +#include <linux/workqueue.h>
    > > +#include <linux/i2c/lp5521.h>
    >
    > Testing shows the above header is missing so I'll have to drop this
    > until that header is added.

    my bad, here it is. Forgot to git add it


    ========== cut here ==========
    From a22cd4139a8f36f8072a5f7f699c34ec0d739fbe Mon Sep 17 00:00:00 2001
    From: Felipe Balbi <felipe.balbi@nokia.com>
    Date: Mon, 16 Feb 2009 16:26:39 +0200
    Subject: [PATCH] leds: introduce lp5521 led driver

    LP5521 is a three channel led driver with support
    for hardware accelerated patterns (currently used
    via lp5521-only sysfs interface).

    Currently, it's used on n810 device.

    Cc: Richard Purdie <rpurdie@rpsys.net>
    Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com>
    ---
    drivers/leds/Kconfig | 10 +
    drivers/leds/Makefile | 1 +
    drivers/leds/leds-lp5521.c | 724 ++++++++++++++++++++++++++++++++++++++++++++
    include/linux/i2c/lp5521.h | 43 +++
    4 files changed, 778 insertions(+), 0 deletions(-)
    create mode 100644 drivers/leds/leds-lp5521.c
    create mode 100644 include/linux/i2c/lp5521.h

    diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
    index 7427136..3b35fa5 100644
    --- a/drivers/leds/Kconfig
    +++ b/drivers/leds/Kconfig
    @@ -119,6 +119,16 @@ config LEDS_GPIO
    outputs. To be useful the particular board must have LEDs
    and they must be connected to the GPIO lines.

    +config LEDS_LP5521
    + tristate "LED Support for the LP5521 LEDs"
    + depends on LEDS_CLASS && I2C
    + help
    + If you say 'Y' here you get support for the National Semiconductor
    + LP5521 LED driver used in n8x0 boards.
    +
    + This driver can be built as a module by choosing 'M'. The module
    + will be called leds-lp5521.
    +
    config LEDS_CLEVO_MAIL
    tristate "Mail LED on Clevo notebook (EXPERIMENTAL)"
    depends on LEDS_CLASS && X86 && SERIO_I8042 && DMI && EXPERIMENTAL
    diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
    index 9d76f0f..baebce8 100644
    --- a/drivers/leds/Makefile
    +++ b/drivers/leds/Makefile
    @@ -18,6 +18,7 @@ obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
    obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o
    obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o
    obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
    +obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o
    obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
    obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
    obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
    diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
    new file mode 100644
    index 0000000..455da20
    --- /dev/null
    +++ b/drivers/leds/leds-lp5521.c
    @@ -0,0 +1,724 @@
    +/*
    + * lp5521.c - LP5521 LED Driver
    + *
    + * Copyright (C) 2007 Nokia Corporation
    + *
    + * Written by Mathias Nyman <mathias.nyman@nokia.com>
    + * Updated by Felipe Balbi <felipe.balbi@nokia.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; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * 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.
    + *
    + * You should have received a copy of the GNU General Public License
    + * 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/leds.h>
    +#include <linux/mutex.h>
    +#include <linux/workqueue.h>
    +#include <linux/i2c/lp5521.h>
    +
    +#define LP5521_DRIVER_NAME "lp5521"
    +
    +#define LP5521_REG_R_PWM 0x02
    +#define LP5521_REG_B_PWM 0x04
    +#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_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 {
    + /* device lock */
    + struct mutex lock;
    + struct i2c_client *client;
    +
    + struct work_struct red_work;
    + struct work_struct green_work;
    + struct work_struct blue_work;
    +
    + struct led_classdev ledr;
    + struct led_classdev ledg;
    + struct led_classdev ledb;
    +
    + enum lp5521_mode mode;
    +
    + int red;
    + int green;
    + int blue;
    +};
    +
    +static int lp5521_set_mode(struct lp5521_chip *chip, enum lp5521_mode mode);
    +
    +static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value)
    +{
    + return i2c_smbus_write_byte_data(client, reg, value);
    +}
    +
    +static inline int lp5521_read(struct i2c_client *client, u8 reg)
    +{
    + return i2c_smbus_read_byte_data(client, reg);
    +}
    +
    +static int lp5521_configure(struct i2c_client *client)
    +{
    + 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);
    +
    + /* 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);
    +
    + /* 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)
    +{
    + struct i2c_client *client = chip->client;
    + int ret = 0;
    +
    + /* 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);
    +
    + return ret;
    +}
    +
    +static int lp5521_run_program(struct lp5521_chip *chip)
    +{
    + struct i2c_client *client = chip->client;
    + int reg;
    + u8 mask = 0xc0;
    + u8 exec_state = 0;
    +
    + reg = lp5521_read(client, LP5521_REG_ENABLE);
    + if (reg < 0)
    + return reg;
    +
    + reg &= mask;
    +
    + /* set all active channels exec state to countinous run*/
    + exec_state |= (chip->red << 5);
    + exec_state |= (chip->green << 3);
    + exec_state |= (chip->blue << 1);
    +
    + reg |= exec_state;
    +
    + if (lp5521_write(client, LP5521_REG_ENABLE, reg))
    + dev_dbg(&client->dev, "failed writing to register %02x\n",
    + LP5521_REG_ENABLE);
    +
    + /* set op-mode to run for active channels, disabled for others */
    + if (lp5521_write(client, LP5521_REG_OP_MODE, exec_state))
    + dev_dbg(&client->dev, "failed writing to register %02x\n",
    + LP5521_REG_OP_MODE);
    +
    + return 0;
    +}
    +
    +/*--------------------------------------------------------------*/
    +/* Sysfs interface */
    +/*--------------------------------------------------------------*/
    +
    +static ssize_t show_active_channels(struct device *dev,
    + struct device_attribute *attr,
    + char *buf)
    +{
    + struct lp5521_chip *chip = dev_get_drvdata(dev);
    + char channels[4];
    + int pos = 0;
    +
    + if (chip->red)
    + pos += sprintf(channels + pos, "r");
    + if (chip->green)
    + pos += sprintf(channels + pos, "g");
    + if (chip->blue)
    + pos += sprintf(channels + pos, "b");
    +
    + channels[pos] = '\0';
    +
    + return sprintf(buf, "%s\n", channels);
    +}
    +
    +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;
    +
    + if (strchr(buf, 'r') != NULL)
    + chip->red = 1;
    + if (strchr(buf, 'b') != NULL)
    + chip->blue = 1;
    + if (strchr(buf, 'g') != NULL)
    + chip->green = 1;
    +
    + return len;
    +}
    +
    +static ssize_t show_color(struct device *dev,
    + struct device_attribute *attr,
    + char *buf)
    +{
    + struct i2c_client *client = to_i2c_client(dev);
    + int r, g, b;
    +
    + r = lp5521_read(client, LP5521_REG_R_PWM);
    + g = lp5521_read(client, LP5521_REG_G_PWM);
    + b = lp5521_read(client, LP5521_REG_B_PWM);
    +
    + if (r < 0 || g < 0 || b < 0)
    + return -EINVAL;
    +
    + return sprintf(buf, "%.2x:%.2x:%.2x\n", r, g, b);
    +}
    +
    +static ssize_t store_color(struct device *dev,
    + struct device_attribute *attr,
    + const char *buf, size_t len)
    +{
    + struct i2c_client *client = to_i2c_client(dev);
    + struct lp5521_chip *chip = i2c_get_clientdata(client);
    + int ret;
    + unsigned r, g, b;
    +
    +
    + ret = sscanf(buf, "%2x:%2x:%2x", &r, &g, &b);
    + if (ret != 3)
    + return -EINVAL;
    +
    + mutex_lock(&chip->lock);
    +
    + 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++;
    + }
    +
    + /* 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;
    +}
    +
    +static ssize_t show_mode(struct device *dev,
    + struct device_attribute *attr,
    + char *buf)
    +{
    + struct lp5521_chip *chip = dev_get_drvdata(dev);
    + char *mode;
    +
    + mutex_lock(&chip->lock);
    + switch (chip->mode) {
    + case LP5521_MODE_RUN:
    + mode = "run";
    + break;
    + case LP5521_MODE_LOAD:
    + mode = "load";
    + break;
    + case LP5521_MODE_DIRECT_CONTROL:
    + mode = "direct";
    + break;
    + default:
    + mode = "undefined";
    + }
    + mutex_unlock(&chip->lock);
    +
    + return sprintf(buf, "%s\n", 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 (sysfs_streq(buf, "run"))
    + lp5521_set_mode(chip, LP5521_MODE_RUN);
    + else if (sysfs_streq(buf, "load"))
    + lp5521_set_mode(chip, LP5521_MODE_LOAD);
    + else if (sysfs_streq(buf, "direct"))
    + lp5521_set_mode(chip, LP5521_MODE_DIRECT_CONTROL);
    + else
    + len = -EINVAL;
    +
    + mutex_unlock(&chip->lock);
    +
    + return len;
    +}
    +
    +static ssize_t show_current(struct device *dev,
    + struct device_attribute *attr,
    + char *buf)
    +{
    + struct i2c_client *client = to_i2c_client(dev);
    + int r, g, b;
    +
    + r = lp5521_read(client, LP5521_REG_R_CNTRL);
    + g = lp5521_read(client, LP5521_REG_G_CNTRL);
    + b = lp5521_read(client, LP5521_REG_B_CNTRL);
    +
    + if (r < 0 || g < 0 || b < 0)
    + return -EINVAL;
    +
    + r >>= 4;
    + g >>= 4;
    + b >>= 4;
    +
    + return sprintf(buf, "%x %x %x\n", r, g, b);
    +}
    +
    +static ssize_t store_current(struct device *dev,
    + struct device_attribute *attr,
    + const char *buf, size_t len)
    +{
    + 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);
    +
    + mutex_unlock(&chip->lock);
    +
    + return len;
    +}
    +
    +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 int lp5521_register_sysfs(struct i2c_client *client)
    +{
    + struct device *dev = &client->dev;
    + int ret;
    +
    + 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;
    +
    +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;
    +}
    +
    +static void lp5521_unregister_sysfs(struct i2c_client *client)
    +{
    + struct device *dev = &client->dev;
    +
    + 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);
    + device_remove_file(dev, &dev_attr_load);
    +}
    +
    +/*--------------------------------------------------------------*/
    +/* Set chip operating mode */
    +/*--------------------------------------------------------------*/
    +
    +static int lp5521_set_mode(struct lp5521_chip *chip, enum lp5521_mode mode)
    +{
    + struct i2c_client *client = chip->client ;
    + int ret = 0;
    +
    + /* if in that mode already do nothing, except for run */
    + if (chip->mode == mode && mode != LP5521_MODE_RUN)
    + return 0;
    +
    + switch (mode) {
    + case LP5521_MODE_RUN:
    + ret = lp5521_run_program(chip);
    + break;
    + case LP5521_MODE_LOAD:
    + ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x15);
    + break;
    + case LP5521_MODE_DIRECT_CONTROL:
    + ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x3F);
    + break;
    + default:
    + dev_dbg(&client->dev, "unsupported mode %d\n", mode);
    + }
    +
    + chip->mode = mode;
    +
    + return ret;
    +}
    +
    +static void lp5521_red_work(struct work_struct *work)
    +{
    + struct lp5521_chip *chip = container_of(work, struct lp5521_chip, red_work);
    + int ret;
    +
    + ret = lp5521_configure(chip->client);
    + if (ret) {
    + dev_dbg(&chip->client->dev, "could not configure lp5521, %d\n",
    + ret);
    + return;
    + }
    +
    + ret = lp5521_write(chip->client, LP5521_REG_R_PWM, chip->red);
    + if (ret)
    + dev_dbg(&chip->client->dev, "could not set brightness, %d\n",
    + ret);
    +}
    +
    +static void lp5521_red_set(struct led_classdev *led,
    + enum led_brightness value)
    +{
    + struct lp5521_chip *chip = container_of(led, struct lp5521_chip, ledr);
    +
    + chip->red = value;
    + schedule_work(&chip->red_work);
    +}
    +
    +static void lp5521_green_work(struct work_struct *work)
    +{
    + struct lp5521_chip *chip = container_of(work, struct lp5521_chip, green_work);
    + int ret;
    +
    + ret = lp5521_configure(chip->client);
    + if (ret) {
    + dev_dbg(&chip->client->dev, "could not configure lp5521, %d\n",
    + ret);
    + return;
    + }
    +
    + ret = lp5521_write(chip->client, LP5521_REG_G_PWM, chip->green);
    + if (ret)
    + dev_dbg(&chip->client->dev, "could not set brightness, %d\n",
    + ret);
    +}
    +
    +static void lp5521_green_set(struct led_classdev *led,
    + enum led_brightness value)
    +{
    + struct lp5521_chip *chip = container_of(led, struct lp5521_chip, ledg);
    +
    + chip->green = value;
    + schedule_work(&chip->green_work);
    +}
    +
    +static void lp5521_blue_work(struct work_struct *work)
    +{
    + struct lp5521_chip *chip = container_of(work, struct lp5521_chip, blue_work);
    + int ret;
    +
    + ret = lp5521_configure(chip->client);
    + if (ret) {
    + dev_dbg(&chip->client->dev, "could not configure lp5521, %d\n",
    + ret);
    + return;
    + }
    +
    + ret = lp5521_write(chip->client, LP5521_REG_B_PWM, chip->blue);
    + if (ret)
    + dev_dbg(&chip->client->dev, "could not set brightness, %d\n",
    + ret);
    +}
    +
    +static void lp5521_blue_set(struct led_classdev *led,
    + enum led_brightness value)
    +{
    + struct lp5521_chip *chip = container_of(led, struct lp5521_chip, ledb);
    +
    + chip->blue = value;
    + schedule_work(&chip->blue_work);
    +}
    +
    +/*--------------------------------------------------------------*/
    +/* Probe, Attach, Remove */
    +/*--------------------------------------------------------------*/
    +
    +static int __init lp5521_probe(struct i2c_client *client,
    + const struct i2c_device_id *id)
    +{
    + struct lp5521_platform_data *pdata = client->dev.platform_data;
    + struct lp5521_chip *chip;
    + char name[16];
    + int ret = 0;
    +
    + if (!pdata) {
    + dev_err(&client->dev, "platform_data is missing\n");
    + return -EINVAL;
    + }
    +
    + chip = kzalloc(sizeof(*chip), GFP_KERNEL);
    + if (!chip)
    + return -ENOMEM;
    +
    + chip->client = client;
    + strncpy(client->name, LP5521_DRIVER_NAME, I2C_NAME_SIZE);
    + i2c_set_clientdata(client, chip);
    +
    + mutex_init(&chip->lock);
    +
    + INIT_WORK(&chip->red_work, lp5521_red_work);
    + INIT_WORK(&chip->green_work, lp5521_green_work);
    + INIT_WORK(&chip->blue_work, lp5521_blue_work);
    +
    + ret = lp5521_configure(client);
    + if (ret < 0) {
    + dev_err(&client->dev, "lp5521 error configuring chip \n");
    + goto fail1;
    + }
    +
    + /* Set default values */
    + chip->mode = pdata->mode;
    + chip->red = pdata->red_present;
    + chip->green = pdata->green_present;
    + chip->blue = pdata->blue_present;
    +
    + chip->ledr.brightness_set = lp5521_red_set;
    + chip->ledr.default_trigger = NULL;
    + snprintf(name, sizeof(name), "%s::red", pdata->label);
    + chip->ledr.name = name;
    + ret = led_classdev_register(&client->dev, &chip->ledr);
    + if (ret < 0) {
    + dev_dbg(&client->dev, "failed to register led %s, %d\n",
    + chip->ledb.name, ret);
    + goto fail1;
    + }
    +
    + chip->ledg.brightness_set = lp5521_green_set;
    + chip->ledg.default_trigger = NULL;
    + snprintf(name, sizeof(name), "%s::green", pdata->label);
    + chip->ledg.name = name;
    + ret = led_classdev_register(&client->dev, &chip->ledg);
    + if (ret < 0) {
    + dev_dbg(&client->dev, "failed to register led %s, %d\n",
    + chip->ledb.name, ret);
    + goto fail2;
    + }
    +
    + chip->ledb.brightness_set = lp5521_blue_set;
    + chip->ledb.default_trigger = NULL;
    + snprintf(name, sizeof(name), "%s::blue", pdata->label);
    + chip->ledb.name = name;
    + ret = led_classdev_register(&client->dev, &chip->ledb);
    + if (ret < 0) {
    + dev_dbg(&client->dev, "failed to register led %s, %d\n", chip->ledb.name, ret);
    + goto fail3;
    + }
    +
    + ret = lp5521_register_sysfs(client);
    + if (ret) {
    + dev_err(&client->dev, "lp5521 registering sysfs failed \n");
    + goto fail4;
    + }
    +
    + return 0;
    +
    +fail4:
    + led_classdev_unregister(&chip->ledb);
    +fail3:
    + led_classdev_unregister(&chip->ledg);
    +fail2:
    + led_classdev_unregister(&chip->ledr);
    +fail1:
    + i2c_set_clientdata(client, NULL);
    + kfree(chip);
    +
    + return ret;
    +}
    +
    +static int __exit lp5521_remove(struct i2c_client *client)
    +{
    + struct lp5521_chip *chip = i2c_get_clientdata(client);
    +
    + lp5521_unregister_sysfs(client);
    + i2c_set_clientdata(client, NULL);
    +
    + led_classdev_unregister(&chip->ledb);
    + led_classdev_unregister(&chip->ledg);
    + led_classdev_unregister(&chip->ledr);
    +
    + kfree(chip);
    +
    + return 0;
    +}
    +
    +static const struct i2c_device_id lp5521_id[] = {
    + { LP5521_DRIVER_NAME, 0},
    + { },
    +};
    +MODULE_DEVICE_TABLE(i2c, lp5521_id);
    +
    +static struct i2c_driver lp5521_driver = {
    + .driver = {
    + .name = LP5521_DRIVER_NAME,
    + },
    + .probe = lp5521_probe,
    + .remove = __exit_p(lp5521_remove),
    + .id_table = lp5521_id,
    +};
    +
    +static int __init lp5521_init(void)
    +{
    + return i2c_add_driver(&lp5521_driver);
    +}
    +module_init(lp5521_init);
    +
    +static void __exit lp5521_exit(void)
    +{
    + i2c_del_driver(&lp5521_driver);
    +}
    +module_exit(lp5521_exit);
    +
    +MODULE_AUTHOR("Mathias Nyman <mathias.nyman@nokia.com>");
    +MODULE_DESCRIPTION("lp5521 LED driver");
    +MODULE_LICENSE("GPL");
    diff --git a/include/linux/i2c/lp5521.h b/include/linux/i2c/lp5521.h
    new file mode 100644
    index 0000000..070e8be
    --- /dev/null
    +++ b/include/linux/i2c/lp5521.h
    @@ -0,0 +1,43 @@
    +/*
    + * lp5521.h - header for LP5521 LED driver
    + *
    + * Copyright (C) 2009 Nokia Corporation
    + *
    + * Contact: Felipe Balbi <felipe.balbi@nokia.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; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * 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.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    + *
    + */
    +
    +#ifndef __LP5521_H_
    +#define __LP5521_H_
    +
    +enum lp5521_mode {
    + LP5521_MODE_LOAD,
    + LP5521_MODE_RUN,
    + LP5521_MODE_DIRECT_CONTROL,
    +};
    +
    +struct lp5521_platform_data {
    + enum lp5521_mode mode;
    +
    + unsigned red_present:1;
    + unsigned green_present:1;
    + unsigned blue_present:1;
    +
    + const char *label;
    +};
    +
    +#endif /* End of __LP5521_H */
    --
    1.6.1.265.g9a013
    --
    balbi


    \
     
     \ /
      Last update: 2009-02-18 13:23    [W:0.094 / U:1.920 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site