[lm-sensors] [PATCH 1/1] leds: support for Philips PCA9531 8-bit led dimmer.
Riku Voipio
riku.voipio at movial.fi
Mon Sep 22 16:24:22 CEST 2008
Rodolfo Giometti wrote:
> From: Rodolfo Giometti <giometti at linux.it>
>
The functionality of pca9531 seems to be a simplified pca9532,
perhaps it would be better to fold both into the same driver?
> Signed-off-by: Rodolfo Giometti <giometti at linux.it>
> ---
> drivers/leds/Kconfig | 7 +
> drivers/leds/Makefile | 1 +
> drivers/leds/leds-pca9531.c | 325 +++++++++++++++++++++++++++++++++++++++++++
> include/linux/i2c/pca9531.h | 25 ++++
> 4 files changed, 358 insertions(+), 0 deletions(-)
> create mode 100644 drivers/leds/leds-pca9531.c
> create mode 100644 include/linux/i2c/pca9531.h
>
> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> index 9556262..52fca4c 100644
> --- a/drivers/leds/Kconfig
> +++ b/drivers/leds/Kconfig
> @@ -163,6 +163,13 @@ config LEDS_PCA955X
> LED driver chips accessed via the I2C bus. Supported
> devices include PCA9550, PCA9551, PCA9552, and PCA9553.
>
> +config LEDS_PCA9531
> + tristate "LED Support using PCA9531 8-bit led dimmer"
> + depends on LEDS_CLASS && I2C
> + help
> + This option enables led support by using Philips PCA9531
> + 8-bit led dimmer.
> +
> comment "LED Triggers"
>
> config LEDS_TRIGGERS
> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
> index ff7982b..4901575 100644
> --- a/drivers/leds/Makefile
> +++ b/drivers/leds/Makefile
> @@ -22,6 +22,7 @@ obj-$(CONFIG_LEDS_CM_X270) += leds-cm-x270.o
> obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
> obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
> obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
> +obj-$(CONFIG_LEDS_PCA9531) += leds-pca9531.o
> obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o
>
> # LED Triggers
> diff --git a/drivers/leds/leds-pca9531.c b/drivers/leds/leds-pca9531.c
> new file mode 100644
> index 0000000..8cad2aa
> --- /dev/null
> +++ b/drivers/leds/leds-pca9531.c
> @@ -0,0 +1,325 @@
> +/*
> + * pca9531.c - driver for 8-bit I2C led dimmer
> + *
> + * Copyright (C) 2008 Rodolfo Giometti <giometti at linux.it>
> + * Copyright (C) 2008 Eurotech S.p.A. <info at eurotech.it>
> + *
> + * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +#include <linux/mutex.h>
> +#include <linux/leds.h>
> +
> +#include <linux/i2c/pca9531.h>
> +
> +#define DRIVER_VERSION "0.40.0"
> +
> +/*
> + * Defines
> + */
> +
> +#define INPUT 0x00
> +#define PWM0 0x02
> +#define PWM1 0x04
> +#define LS0 0x05
> +#define LS1 0x06
> +#define LED_OFF 0
> +#define LED_ON 1
> +#define LED_PWM0 2
> +#define LED_PWM1 3
> +#define LED_MASK 3
> +
> +/*
> + * Structs
> + */
> +
> +struct pca9531_data;
> +struct pca9531_led {
> + struct pca9531_data *data; /* back link */
> + struct led_classdev dev;
> + int new_level;
> +
> + unsigned long pwm:2;
> + unsigned long inverted:1;
>
You don't really need a long for that..
> +
> + struct work_struct work;
> +};
> +
> +struct pca9531_data {
> + struct i2c_client *client;
> + struct mutex update_lock;
> +
> + struct pca9531_led led[8];
> +};
> +
> +/*
> + * Management functions
> + */
> +
> +static int write_led_brightness(struct i2c_client *client, int id,
> + int pwm, unsigned int brightness)
> +{
> + struct pca9531_data *data = i2c_get_clientdata(client);
> + u8 addr = id <= 3 ? LS0 : LS1;
> + u8 tmp;
> + int ret;
> +
> + if (brightness > 255 ||
> + (pwm != PCA9531_PWM0 && pwm != PCA9531_PWM1 &&
> + pwm != PCA9531_ONOFF))
> + return -EINVAL;
> +
> + mutex_lock(&data->update_lock);
> +
> + /* Set brightness intensity.
> + *
> + * Please, note that if you (the driver user) define two or more
> + * leds sharing one PWM unit, then all that leds will change
> + * their intensity when you change the brightness intensity!
> + * So, when configuring the drive, you should define at most one
> + * led for each PWM unit.
> + * However you (the driver user) are a real-programmer so you
> + * shouldn't do such (evil) thing... ;)
> + */
> + if (brightness && pwm != PCA9531_ONOFF) {
> + ret = i2c_smbus_write_byte_data(client,
> + pwm == PCA9531_PWM0 ? PWM0 : PWM1, brightness);
> + if (ret < 0)
> + goto exit;
> + }
> +
> + /* Enable/disable led */
> + ret = i2c_smbus_read_byte_data(client, addr);
> + if (ret < 0)
> + goto exit;
> +
> + ret &= ~(LED_MASK << ((id % 4) * 2));
> + tmp = LED_OFF;
> + if (brightness)
> + switch (pwm) {
> + case PCA9531_ONOFF:
> + tmp = LED_ON;
> + break;
> + case PCA9531_PWM0:
> + tmp = LED_PWM0;
> + break;
> + case PCA9531_PWM1:
> + tmp = LED_PWM1;
> + break;
> + }
> + ret |= tmp << ((id % 4) * 2);
> +
> + ret = i2c_smbus_write_byte_data(client, addr, ret);
> + if (ret < 0)
> + goto exit;
> +
> + ret = 0;
> +exit:
> + mutex_unlock(&data->update_lock);
> +
> + return ret;
> +}
> +
> +/*
> + * Led support
> + */
> +
> +static void pca9531_led_set_work(struct work_struct *p)
> +{
> + struct pca9531_led *led = container_of(p, struct pca9531_led, work);
> + struct pca9531_data *data = led->data;
> + struct i2c_client *client = data->client;
> + int id = led - &data->led[0];
> + int ret;
> +
> + ret = write_led_brightness(client, id, led->pwm,
> + led->inverted ?
> + 255 - led->new_level :
> + led->new_level);
> + if (ret)
> + dev_warn(&client->dev, "unable to set brightness for led%d\n",
> + id);
> +}
> +
> +static void pca9531_led_set(struct led_classdev *p, enum led_brightness value)
> +{
> + struct pca9531_led *led = container_of(p, struct pca9531_led, dev);
> +
> + led->new_level = value;
> + schedule_work(&led->work);
> +}
> +
> +/*
> + * Device init function
> + */
> +
> +static int pca9531_init_client(struct i2c_client *client,
> + struct pca9531_platform_data *pdata)
> +{
> + int i, ret;
> +
> + /* Set the platform defaults */
> + for (i = PCA9531_LED0; i <= PCA9531_LED7; i++) {
> + if (pdata[i].name)
> + ret = write_led_brightness(client, i, pdata[i].pwm,
> + pdata[i].inverted ?
> + 255 - pdata[i].brightness :
> + pdata[i].brightness);
> + else
> + ret = write_led_brightness(client, i, PCA9531_ONOFF, 0);
> + if (ret)
> + dev_warn(&client->dev,
> + "unable to init LED%d brightness to %d\n", i,
> + pdata[i].brightness);
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * I2C init/probing/exit functions
> + */
> +
> +static struct pca9531_platform_data defs[8]; /* all values are set to '0' */
> +
> +static struct i2c_driver pca9531_driver;
> +static int __devinit pca9531_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
> + struct pca9531_data *data;
> + struct pca9531_platform_data *pdata;
> + int i, ret = 0;
> +
> + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE
> + | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) {
> + ret = -EIO;
> + goto exit;
> + }
> +
> + data = kzalloc(sizeof(struct pca9531_data), GFP_KERNEL);
> + if (!data) {
> + ret = -ENOMEM;
> + goto exit;
> + }
> + data->client = client;
> + i2c_set_clientdata(client, data);
> +
> + /* Check platform data */
> + pdata = client->dev.platform_data;
> + if (!pdata)
> + pdata = defs;
> + for (i = PCA9531_LED0; i <= PCA9531_LED7; i++)
> + if (pdata[i].name) {
> + dev_dbg(&client->dev, "led%d:\n", i);
> + dev_dbg(&client->dev, "\tname %s\n", pdata[i].name);
> + dev_dbg(&client->dev, "\tdefault brightness %d\n",
> + pdata[i].brightness);
> + dev_dbg(&client->dev, "\tinverted brightness %d\n",
> + pdata[i].inverted);
> +#ifdef CONFIG_LEDS_TRIGGERS
> + dev_dbg(&client->dev, "\ttrigger %s\n",
> + pdata[i].trigger);
> +#endif
> + dev_dbg(&client->dev, "\tpwm %d\n", pdata[i].pwm);
> + }
> +
> + mutex_init(&data->update_lock);
> +
> + /* Initialize the PCA9531 chip */
> + ret = pca9531_init_client(client, pdata);
> + if (ret)
> + goto exit_kfree;
> +
> + /* Register the led devices */
> + for (i = PCA9531_LED0; i <= PCA9531_LED7; i++)
> + if (pdata[i].name) {
> + data->led[i].data = data;
> + data->led[i].pwm = pdata[i].pwm;
> + data->led[i].dev.name = pdata[i].name;
> + data->led[i].inverted = pdata[i].inverted;
> + data->led[i].dev.brightness = pdata[i].brightness;
> + data->led[i].dev.brightness_set = pca9531_led_set;
> +#ifdef CONFIG_LEDS_TRIGGERS
> + data->led[i].dev.default_trigger = pdata[i].trigger;
> +#endif
> + INIT_WORK(&data->led[i].work, pca9531_led_set_work);
> + ret = led_classdev_register(&client->dev,
> + &data->led[i].dev);
> + if (ret < 0)
> + dev_warn(&client->dev, "unable to register "
> + "led%d into the system\n", i);
> + }
> +
> + dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);
> +
> + return 0;
> +
> +exit_kfree:
> + kfree(data);
> +exit:
> + return ret;
> +}
> +
> +static int __devexit pca9531_remove(struct i2c_client *client)
> +{
> + struct pca9531_data *data = i2c_get_clientdata(client);
> + int i;
> +
> + for (i = PCA9531_LED1; i <= PCA9531_LED7; i++)
> + led_classdev_unregister(&data->led[i].dev);
> +
> + kfree(i2c_get_clientdata(client));
> +
> + return 0;
> +}
> +
> +static const struct i2c_device_id pca9531_id[] = {
> + { "pca9531", 0 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, pca9531_id);
> +
> +static struct i2c_driver pca9531_driver = {
> + .driver = {
> + .name = "pca9531",
> + .owner = THIS_MODULE,
> + },
> + .probe = pca9531_probe,
> + .remove = __devexit_p(pca9531_remove),
> + .id_table = pca9531_id,
> +};
> +
> +static int __init pca9531_init(void)
> +{
> + return i2c_add_driver(&pca9531_driver);
> +}
> +
> +static void __exit pca9531_exit(void)
> +{
> + i2c_del_driver(&pca9531_driver);
> +}
> +
> +MODULE_AUTHOR("Rodolfo Giometti <giometti at linux.it>");
> +MODULE_DESCRIPTION("PCA9531 driver for 8-bit I2C led dimmer"
> + "dual LDO");
> +MODULE_LICENSE("GPL");
> +
> +module_init(pca9531_init);
> +module_exit(pca9531_exit);
> diff --git a/include/linux/i2c/pca9531.h b/include/linux/i2c/pca9531.h
> new file mode 100644
> index 0000000..c00201d
> --- /dev/null
> +++ b/include/linux/i2c/pca9531.h
> @@ -0,0 +1,25 @@
> +#ifndef __LINUX_PCA9531_H
> +#define __LINUX_PCA9531_H
> +
> +#define PCA9531_LED0 0
> +#define PCA9531_LED1 1
> +#define PCA9531_LED2 2
> +#define PCA9531_LED3 3
> +#define PCA9531_LED4 4
> +#define PCA9531_LED5 5
> +#define PCA9531_LED6 6
> +#define PCA9531_LED7 7
>
These definitions are not really needed.
> +
> +#define PCA9531_ONOFF 0
> +#define PCA9531_PWM0 1
> +#define PCA9531_PWM1 2
> +
> +struct pca9531_platform_data {
> + char *name;
> + char *trigger; /* see available led triggers */
> + unsigned int brightness; /* use values in [0:31] */
> + unsigned int inverted:1; /* inverted polarity led */
> + unsigned int pwm:2; /* 0 = pwm0, 1 = pwm1, 2 = none */
> +};
> +
> +#endif /* __LINUX_PCA9531_H */
>
More information about the lm-sensors
mailing list