leds: Added driver for the NXP PCA9685 I2C chip
PATCH 1/1 leds: Added driver for the NXP PCA9685 I2C chip
The NXP PCA9685 supports 16 channels/leds using a 12-bit PWM (4095 levels of brightness)
This driver supports configuration using platform_data.
--- drivers/leds/Kconfig | 10 ++ drivers/leds/Makefile | 1 + drivers/leds/leds-pca9685.c | 219 +++++++++++++++++++++++++++++ include/linux/platform_data/leds-pca9685.h | 34 +++++ 4 files changed, 264 insertions(+) create mode 100644 drivers/leds/leds-pca9685.c create mode 100644 include/linux/platform_data/leds-pca9685.h
drivers/leds/Kconfig +10 -0
@@ -300,6 +300,16 @@ config LEDS_PCA963X LED driver chip accessed via the I2C bus. Supported devices include PCA9633 and PCA9634 +config LEDS_PCA9685 + tristate "LED support for PCA9685 I2C chip" + depends on LEDS_CLASS + depends on I2C + help + This option enables support for LEDs connected to the PCA9685 + LED driver chips accessed via the I2C bus. + The PCA9685 offers 12-bit PWM (4095 levels of brightness) on + 16 individual channels. + config LEDS_WM831X_STATUS tristate "LED support for status LEDs on WM831x PMICs" depends on LEDS_CLASS
drivers/leds/Makefile +1 -0
@@ -36,6 +36,7 @@ obj-$(CONFIG_LEDS_OT200) += leds-ot200.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o obj-$(CONFIG_LEDS_PCA963X) += leds-pca963x.o +obj-$(CONFIG_LEDS_PCA9685) += leds-pca9685.o obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o obj-$(CONFIG_LEDS_DA9052) += leds-da9052.o obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o
drivers/leds/leds-pca9685.c new file +219 -0
219 lines not on this page · raw message
include/linux/platform_data/leds-pca9685.h new file +34 -0
@@ -0,0 +1,34 @@ +/* +* Copyright 2013 Maximilian Güntner <[email protected]> +* +* This file is subject to the terms and conditions of version 2 of +* the GNU General Public License. See the file COPYING in the main +* directory of this archive for more details. +* +* Based on leds-pca963x.h by Peter Meerwald <[email protected]> +* +* LED driver for the NXP PCA9685 PWM chip +* +*/ + +#ifndef __LINUX_PCA9685_H +#define __LINUX_PCA9685_H +#include <linux/leds.h> + +enum pca9685_outdrv { +PCA9685_OPEN_DRAIN , +PCA9685_TOTEM_POLE , +}; + +enum pca9685_inverted { +PCA9685_NOT_INVERTED , +PCA9685_INVERTED , +}; + +struct pca9685_platform_data { +struct led_platform_data leds; +enum pca9685_outdrv outdrv; +enum pca9685_inverted inverted; +}; + +#endif /* __LINUX_PCA9685_H */
Signature · 1 line
1.8.4
Quoted 4 lines · Maximilian Güntner
The NXP PCA9685 supports 16 channels/leds using a 12-bit PWM (4095 levels of brightness)
This driver supports configuration usingplatform_data.
I'm OK for this patch basically, just a little picky coding style issue as below.
Thanks,
-Bryan
Quoted 133 lines
Signed-off-by Maximilian Güntner <[email protected]>--- drivers/leds/Kconfig | 10 ++ drivers/leds/Makefile | 1 + drivers/leds/leds-pca9685.c | 219 +++++++++++++++++++++++++++++ include/linux/platform_data/leds-pca9685.h | 34 +++++ 4 files changed, 264 insertions(+) create mode 100644 drivers/leds/leds-pca9685.c create mode 100644 include/linux/platform_data/leds-pca9685.hdrivers/leds/Kconfig +10
-0@@ -300,6 +300,16 @@ config LEDS_PCA963X LED driver chip accessed via the I2C bus. Supported devices include PCA9633 and PCA9634 +config LEDS_PCA9685 + tristate "LED support for PCA9685 I2C chip" + depends on LEDS_CLASS + depends on I2C + help + This option enables support for LEDs connected to the PCA9685 + LED driver chips accessed via the I2C bus. + The PCA9685 offers 12-bit PWM (4095 levels of brightness) on + 16 individual channels. + config LEDS_WM831X_STATUS tristate "LED support for status LEDs on WM831x PMICs" depends on LEDS_CLASSdrivers/leds/Makefile +1
-0@@ -36,6 +36,7 @@ obj-$(CONFIG_LEDS_OT200) += leds-ot200.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o obj-$(CONFIG_LEDS_PCA963X) += leds-pca963x.o +obj-$(CONFIG_LEDS_PCA9685) += leds-pca9685.o obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o obj-$(CONFIG_LEDS_DA9052) += leds-da9052.o obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.odrivers/leds/leds-pca9685.c new file +84
-0@@ -0,0 +1,219 @@ +/* +* Copyright 2013 Maximilian Güntner <[email protected]> +* +* This file is subject to the terms and conditions of version 2 of +* the GNU General Public License. See the file COPYING in the main +* directory of this archive for more details. +* +* Based on leds-pca963x.c driver by +* Peter Meerwald <[email protected]> +* +* Driver for the NXP PCA9685 12-Bit I2C chip. +* +*/ + +#include <linux/module.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/leds.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/workqueue.h> +#include <linux/slab.h> + +#include <linux/platform_data/leds-pca9685.h> + +/* Register Addresses */ +#define PCA9685_MODE1 0x00 +#define PCA9685_MODE2 0x01 +#define PCA9685_LED0_ON_L 0x06 +#define PCA9685_ALL_LED_ON_L 0xFA + +/* MODE1 Register */ +#define PCA9685_ALLCALL 0x00 +#define PCA9685_SLEEP 0x04 +#define PCA9685_AI 0x05 + +/* MODE2 Register */ +#define PCA9685_INVRT 0x04 +#define PCA9685_OUTDRV 0x02 + +static const struct i2c_device_id pca9685_id[] = { + {"pca9685" ,0 }, + { } +}; +MODULE_DEVICE_TABLE (i2c, pca9685_id); + +struct pca9685_led { +struct i2c_client *client; +struct work_struct work; +u16 brightness; +struct led_classdev led_cdev; +int led_num;/* 0-15 */ +char name[32 ]; +}; + +static void pca9685_write_msg (struct i2c_client *client,u8 *buf,u8 len) +{ +struct i2c_msg msg = { + .addr = client->addr, + .flags =0x00 , + .len = len, + .buf = buf + }; +i2c_transfer (client->adapter, &msg,1 ); +} + +static void pca9685_all_off (struct i2c_client *client) +{ +u8 i2c_buffer[5 ] = {PCA9685_ALL_LED_ON_L ,0x00 ,0x00 ,0x00 ,0x10 }; +pca9685_write_msg (client, i2c_buffer,5 ); +} + +static void pca9685_led_work (struct work_struct *work) +{ +struct pca9685_led *pca9685; +u16 brightness; +u8 i2c_buffer[5 ]; + pca9685 = container_of(work, struct pca9685_led, work); +/* +* 4095 is the maximum brightness, so we set the ON time to 0x1000 +* which disables the PWM generator for that LED +*/ +if (pca9685->brightness ==4095 )
drivers/leds/leds-pca9685.c line 94
Quoted 10 lines
drivers/leds/leds-pca9685.c new file +10
-0+ brightness =4096 ; + else + brightness = pca9685->brightness; + + i2c_buffer[0 ] =PCA9685_LED0_ON_L +4 * pca9685->led_num; + +if (brightness ==4096 ) + *((u16 *)(i2c_buffer+1 )) =cpu_to_le16 (0x1000 ); +else + *((u16 *)(i2c_buffer+1 )) =0x0000 ;
One empty line here probably is better.
Quoted 20 lines
drivers/leds/leds-pca9685.c new file +20
-0+if (brightness ==0 ) + *((u16 *)(i2c_buffer+3 )) =cpu_to_le16 (0x1000 ); +else if (brightness ==4096 ) + *((u16 *)(i2c_buffer+3 )) =0x0000 ; +else + *((u16 *)(i2c_buffer+3 )) =cpu_to_le16 (brightness); + +pca9685_write_msg (pca9685->client, i2c_buffer,5 ); +} + +static void pca9685_led_set (struct led_classdev *led_cdev, +enum led_brightness value) +{ +struct pca9685_led *pca9685; + pca9685 = container_of(led_cdev, struct pca9685_led, led_cdev); + pca9685->brightness = value; + +schedule_work (&pca9685->work); +} +
drivers/leds/leds-pca9685.c line 124
Quoted 10 lines
drivers/leds/leds-pca9685.c new file +10
-0+static int pca9685_probe (struct i2c_client *client, +const struct i2c_device_id *id) +{ +struct pca9685_led *pca9685; +struct pca9685_platform_data *pdata; +int err; +u8 i; + + pdata =dev_get_platdata (&client->dev); +
No need empty line here.
drivers/leds/leds-pca9685.c line 133
Quoted 9 lines
drivers/leds/leds-pca9685.c new file +9
-0+if (pdata) { +if (pdata->leds.num_leds <1 || pdata->leds.num_leds >15 ) { +dev_err (&client->dev,"board info must claim 1-16 LEDs" ); +return -EINVAL ; + } + } + + pca9685 =devm_kzalloc (&client->dev,16 *sizeof (*pca9685),GFP_KERNEL ); +
No need empty line here.
Quoted 28 lines
drivers/leds/leds-pca9685.c new file +28
-0+if (!pca9685) +return -ENOMEM ; + +i2c_set_clientdata (client, pca9685); +pca9685_all_off (client); + +for (i =0 ; i <16 ; i++) { + pca9685[i].client = client; + pca9685[i].led_num = i; + pca9685[i].name[0 ] ='\0' ; +if (pdata && i < pdata->leds.num_leds) { +if (pdata->leds.leds[i].name) +strncpy (pca9685[i].name, + pdata->leds.leds[i].name, +sizeof (pca9685[i].name)-1 ); +if (pdata->leds.leds[i].default_trigger) + pca9685[i].led_cdev.default_trigger = + pdata->leds.leds[i].default_trigger; + } +if (strlen (pca9685[i].name) ==0 ) { +/* +* Write adapter and address to the name as well. +* Otherwise multiple chips attached to one host would +* not work. +*/ +snprintf (pca9685[i].name,sizeof (pca9685[i].name), +"pca9685:%d:x%.2x:%d" , + client->adapter->nr, client->addr, i);
drivers/leds/leds-pca9685.c line 171
Quoted 10 lines
drivers/leds/leds-pca9685.c new file +10
-0+ } + pca9685[i].led_cdev.name = pca9685[i].name; + pca9685[i].led_cdev.max_brightness =0xfff ; + pca9685[i].led_cdev.brightness_set = pca9685_led_set; + +INIT_WORK (&pca9685[i].work, pca9685_led_work); + err =led_classdev_register (&client->dev, &pca9685[i].led_cdev); +if (err <0 ) + goto exit; + }
You can add one empty line here.
Quoted 89 lines
drivers/leds/leds-pca9685.c new file +48
-0+if (pdata) +i2c_smbus_write_byte_data (client,PCA9685_MODE2 , + pdata->outdrv <<PCA9685_OUTDRV | + pdata->inverted <<PCA9685_INVRT ); +else +i2c_smbus_write_byte_data (client,PCA9685_MODE2 , +PCA9685_TOTEM_POLE <<PCA9685_OUTDRV ); +/* Enable Auto-Increment, enable oscillator, ALLCALL/SUBADDR disabled */ +i2c_smbus_write_byte_data (client,PCA9685_MODE1 ,1 <<PCA9685_AI ); + +return 0 ; + +exit: +while (i--) { +led_classdev_unregister (&pca9685[i].led_cdev); +cancel_work_sync (&pca9685[i].work); + } +return err; +} + +static int pca9685_remove (struct i2c_client *client) +{ +struct pca9685_led *pca9685 =i2c_get_clientdata (client); +u8 i; + +for (i =0 ; i <16 ; i++) { +led_classdev_unregister (&pca9685[i].led_cdev); +cancel_work_sync (&pca9685[i].work); + } +pca9685_all_off (client); +return 0 ; +} + +static struct i2c_driver pca9685_driver = { + .driver = { + .name ="leds-pca9685" , + .owner =THIS_MODULE , + }, + .probe = pca9685_probe, + .remove = pca9685_remove, + .id_table = pca9685_id, +}; + +module_i2c_driver (pca9685_driver); + +MODULE_AUTHOR ("Maximilian Güntner <[email protected]>" ); +MODULE_DESCRIPTION ("PCA9685 LED Driver" ); +MODULE_LICENSE ("GPL v2" );include/linux/platform_data/leds-pca9685.h new file +34
-1@@ -0,0 +1,34 @@ +/* +* Copyright 2013 Maximilian Güntner <[email protected]> +* +* This file is subject to the terms and conditions of version 2 of +* the GNU General Public License. See the file COPYING in the main +* directory of this archive for more details. +* +* Based on leds-pca963x.h by Peter Meerwald <[email protected]> +* +* LED driver for the NXP PCA9685 PWM chip +* +*/ + +#ifndef __LINUX_PCA9685_H +#define __LINUX_PCA9685_H +#include <linux/leds.h> + +enum pca9685_outdrv { +PCA9685_OPEN_DRAIN , +PCA9685_TOTEM_POLE , +}; + +enum pca9685_inverted { +PCA9685_NOT_INVERTED , +PCA9685_INVERTED , +}; + +struct pca9685_platform_data { +struct led_platform_data leds; +enum pca9685_outdrv outdrv; +enum pca9685_inverted inverted; +}; + +#endif /* __LINUX_PCA9685_H */ --
Quoted 2 lines
1.8.4
--
To unsubscribe from this list: send the line "unsubscribe linux-leds" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 4 lines
The NXP PCA9685 supports 16 channels/leds using a 12-bit PWM (4095 levels of brightness)
This driver supports configuration usingplatform_data.
some nitpicking inline
Quoted 15 lines
Signed-off-by Maximilian Güntner <[email protected]>--- drivers/leds/Kconfig | 10 ++ drivers/leds/Makefile | 1 + drivers/leds/leds-pca9685.c | 219 +++++++++++++++++++++++++++++ include/linux/platform_data/leds-pca9685.h | 34 +++++ 4 files changed, 264 insertions(+) create mode 100644 drivers/leds/leds-pca9685.c create mode 100644 include/linux/platform_data/leds-pca9685.hdrivers/leds/Kconfig +0
-0
drivers/leds/Kconfig config LEDS_PCA963X · line 309
Quoted 10 lines
drivers/leds/Kconfig +7
-0@@ -300,6 +300,16 @@ config LEDS_PCA963X LED driver chip accessed via the I2C bus. Supported devices include PCA9633 and PCA9634 +config LEDS_PCA9685 + tristate "LED support for PCA9685 I2C chip" + depends on LEDS_CLASS + depends on I2C + help + This option enables support for LEDs connected to the PCA9685 + LED driver chips accessed via the I2C bus.
chip
Quoted 25 lines
drivers/leds/Kconfig +3
-0+ The PCA9685 offers 12-bit PWM (4095 levels of brightness) on + 16 individual channels. + config LEDS_WM831X_STATUS tristate "LED support for status LEDs on WM831x PMICs" depends on LEDS_CLASSdrivers/leds/Makefile +1
-0@@ -36,6 +36,7 @@ obj-$(CONFIG_LEDS_OT200) += leds-ot200.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o obj-$(CONFIG_LEDS_PCA963X) += leds-pca963x.o +obj-$(CONFIG_LEDS_PCA9685) += leds-pca9685.o obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o obj-$(CONFIG_LEDS_DA9052) += leds-da9052.o obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.odrivers/leds/leds-pca9685.c new file +1
-0@@ -0,0 +1,219 @@ +/*
drivers/leds/leds-pca9685.c line 11
Quoted 10 lines
drivers/leds/leds-pca9685.c new file +10
-0+* Copyright 2013 Maximilian Güntner <[email protected]> +* +* This file is subject to the terms and conditions of version 2 of +* the GNU General Public License. See the file COPYING in the main +* directory of this archive for more details. +* +* Based on leds-pca963x.c driver by +* Peter Meerwald <[email protected]> +* +* Driver for the NXP PCA9685 12-Bit I2C chip.
this could be more descriptive: 12-bit PWM LED driver chip
Quoted 64 lines
drivers/leds/leds-pca9685.c new file +64
-0+* +*/ + +#include <linux/module.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/leds.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/workqueue.h> +#include <linux/slab.h> + +#include <linux/platform_data/leds-pca9685.h> + +/* Register Addresses */ +#define PCA9685_MODE1 0x00 +#define PCA9685_MODE2 0x01 +#define PCA9685_LED0_ON_L 0x06 +#define PCA9685_ALL_LED_ON_L 0xFA + +/* MODE1 Register */ +#define PCA9685_ALLCALL 0x00 +#define PCA9685_SLEEP 0x04 +#define PCA9685_AI 0x05 + +/* MODE2 Register */ +#define PCA9685_INVRT 0x04 +#define PCA9685_OUTDRV 0x02 + +static const struct i2c_device_id pca9685_id[] = { + {"pca9685" ,0 }, + { } +}; +MODULE_DEVICE_TABLE (i2c, pca9685_id); + +struct pca9685_led { +struct i2c_client *client; +struct work_struct work; +u16 brightness; +struct led_classdev led_cdev; +int led_num;/* 0-15 */ +char name[32 ]; +}; + +static void pca9685_write_msg (struct i2c_client *client,u8 *buf,u8 len) +{ +struct i2c_msg msg = { + .addr = client->addr, + .flags =0x00 , + .len = len, + .buf = buf + }; +i2c_transfer (client->adapter, &msg,1 ); +} + +static void pca9685_all_off (struct i2c_client *client) +{ +u8 i2c_buffer[5 ] = {PCA9685_ALL_LED_ON_L ,0x00 ,0x00 ,0x00 ,0x10 }; +pca9685_write_msg (client, i2c_buffer,5 ); +} + +static void pca9685_led_work (struct work_struct *work) +{
drivers/leds/leds-pca9685.c line 85
Quoted 10 lines
drivers/leds/leds-pca9685.c new file +10
-0+struct pca9685_led *pca9685; +u16 brightness; +u8 i2c_buffer[5 ]; + pca9685 = container_of(work, struct pca9685_led, work); +/* +* 4095 is the maximum brightness, so we set the ON time to 0x1000 +* which disables the PWM generator for that LED +*/ +if (pca9685->brightness ==4095 ) + brightness =4096 ;
why not check for 4095 instead of 4096 below and save this if statement?
Quoted 85 lines
drivers/leds/leds-pca9685.c new file +85
-0+else + brightness = pca9685->brightness; + + i2c_buffer[0 ] =PCA9685_LED0_ON_L +4 * pca9685->led_num; + +if (brightness ==4096 ) + *((u16 *)(i2c_buffer+1 )) =cpu_to_le16 (0x1000 ); +else + *((u16 *)(i2c_buffer+1 )) =0x0000 ; +if (brightness ==0 ) + *((u16 *)(i2c_buffer+3 )) =cpu_to_le16 (0x1000 ); +else if (brightness ==4096 ) + *((u16 *)(i2c_buffer+3 )) =0x0000 ; +else + *((u16 *)(i2c_buffer+3 )) =cpu_to_le16 (brightness); + +pca9685_write_msg (pca9685->client, i2c_buffer,5 ); +} + +static void pca9685_led_set (struct led_classdev *led_cdev, +enum led_brightness value) +{ +struct pca9685_led *pca9685; + pca9685 = container_of(led_cdev, struct pca9685_led, led_cdev); + pca9685->brightness = value; + +schedule_work (&pca9685->work); +} + +static int pca9685_probe (struct i2c_client *client, +const struct i2c_device_id *id) +{ +struct pca9685_led *pca9685; +struct pca9685_platform_data *pdata; +int err; +u8 i; + + pdata =dev_get_platdata (&client->dev); + +if (pdata) { +if (pdata->leds.num_leds <1 || pdata->leds.num_leds >15 ) { +dev_err (&client->dev,"board info must claim 1-16 LEDs" ); +return -EINVAL ; + } + } + + pca9685 =devm_kzalloc (&client->dev,16 *sizeof (*pca9685),GFP_KERNEL ); + +if (!pca9685) +return -ENOMEM ; + +i2c_set_clientdata (client, pca9685); +pca9685_all_off (client); + +for (i =0 ; i <16 ; i++) { + pca9685[i].client = client; + pca9685[i].led_num = i; + pca9685[i].name[0 ] ='\0' ; +if (pdata && i < pdata->leds.num_leds) { +if (pdata->leds.leds[i].name) +strncpy (pca9685[i].name, + pdata->leds.leds[i].name, +sizeof (pca9685[i].name)-1 ); +if (pdata->leds.leds[i].default_trigger) + pca9685[i].led_cdev.default_trigger = + pdata->leds.leds[i].default_trigger; + } +if (strlen (pca9685[i].name) ==0 ) { +/* +* Write adapter and address to the name as well. +* Otherwise multiple chips attached to one host would +* not work. +*/ +snprintf (pca9685[i].name,sizeof (pca9685[i].name), +"pca9685:%d:x%.2x:%d" , + client->adapter->nr, client->addr, i); + } + pca9685[i].led_cdev.name = pca9685[i].name; + pca9685[i].led_cdev.max_brightness =0xfff ; + pca9685[i].led_cdev.brightness_set = pca9685_led_set; + +INIT_WORK (&pca9685[i].work, pca9685_led_work); + err =led_classdev_register (&client->dev, &pca9685[i].led_cdev); +if (err <0 ) + goto exit;
drivers/leds/leds-pca9685.c line 180
Quoted 10 lines
drivers/leds/leds-pca9685.c new file +10
-0+ } +if (pdata) +i2c_smbus_write_byte_data (client,PCA9685_MODE2 , + pdata->outdrv <<PCA9685_OUTDRV | + pdata->inverted <<PCA9685_INVRT ); +else +i2c_smbus_write_byte_data (client,PCA9685_MODE2 , +PCA9685_TOTEM_POLE <<PCA9685_OUTDRV ); +/* Enable Auto-Increment, enable oscillator, ALLCALL/SUBADDR disabled */ +i2c_smbus_write_byte_data (client,PCA9685_MODE1 ,1 <<PCA9685_AI );
maybe BIT(PCA9685_AI)
Quoted 80 lines
drivers/leds/leds-pca9685.c new file +39
-0+ +return 0 ; + +exit: +while (i--) { +led_classdev_unregister (&pca9685[i].led_cdev); +cancel_work_sync (&pca9685[i].work); + } +return err; +} + +static int pca9685_remove (struct i2c_client *client) +{ +struct pca9685_led *pca9685 =i2c_get_clientdata (client); +u8 i; + +for (i =0 ; i <16 ; i++) { +led_classdev_unregister (&pca9685[i].led_cdev); +cancel_work_sync (&pca9685[i].work); + } +pca9685_all_off (client); +return 0 ; +} + +static struct i2c_driver pca9685_driver = { + .driver = { + .name ="leds-pca9685" , + .owner =THIS_MODULE , + }, + .probe = pca9685_probe, + .remove = pca9685_remove, + .id_table = pca9685_id, +}; + +module_i2c_driver (pca9685_driver); + +MODULE_AUTHOR ("Maximilian Güntner <[email protected]>" ); +MODULE_DESCRIPTION ("PCA9685 LED Driver" ); +MODULE_LICENSE ("GPL v2" );include/linux/platform_data/leds-pca9685.h new file +34
-0@@ -0,0 +1,34 @@ +/* +* Copyright 2013 Maximilian Güntner <[email protected]> +* +* This file is subject to the terms and conditions of version 2 of +* the GNU General Public License. See the file COPYING in the main +* directory of this archive for more details. +* +* Based on leds-pca963x.h by Peter Meerwald <[email protected]> +* +* LED driver for the NXP PCA9685 PWM chip +* +*/ + +#ifndef __LINUX_PCA9685_H +#define __LINUX_PCA9685_H +#include <linux/leds.h> + +enum pca9685_outdrv { +PCA9685_OPEN_DRAIN , +PCA9685_TOTEM_POLE , +}; + +enum pca9685_inverted { +PCA9685_NOT_INVERTED , +PCA9685_INVERTED , +}; + +struct pca9685_platform_data { +struct led_platform_data leds; +enum pca9685_outdrv outdrv; +enum pca9685_inverted inverted; +}; + +#endif /* __LINUX_PCA9685_H */
Signature · 2 lines
Peter Meerwald +43-664-2444418 (mobile)
Quoted 5 lines · On Tuesday 15 October 2013 00:38:13 Peter Meerwald wrote:
Quoted 3 lines
The NXP PCA9685 supports 16 channels/leds using a 12-bit PWM (4095 levels of brightness)
This driver supports configuration usingplatform_data.some nitpicking inline
Thank you for your comments.
Quoted 1 line
chip
fixed.
Quoted 3 lines
Quoted 2 lines
+ * Driver for the NXP PCA9685 12-Bit I2C chip.
this could be more descriptive: 12-bit PWM LED driver chip
fixed.
Quoted 8 lines
drivers/leds/leds-pca9685.c line 85
Quoted 6 lines
drivers/leds/leds-pca9685.c new file +6
-0+/* +* 4095 is the maximum brightness, so we set the ON time to 0x1000 +* which disables the PWM generator for that LED +*/ +if (pca9685->brightness ==4095 ) + brightness =4096 ;why not check for 4095 instead of 4096 below and save this if statement?
removed.
I left it there in order to improve the code readability, which should be equally good when we just remove the statement and check for 4095 below.
Quoted 3 lines
drivers/leds/leds-pca9685.c line 180
Quoted 1 line
drivers/leds/leds-pca9685.c new file +1
-0+i2c_smbus_write_byte_data (client,PCA9685_MODE1 ,1 <<PCA9685_AI );maybe
BIT(PCA9685_AI)
replaced.
Quoted 7 lines · On Monday 14 October 2013 15:08:15 Bryan Wu wrote:
On Mon, Oct 14, 2013 at 10:31 AM, Maximilian Güntner
Quoted 3 lines · <[email protected]> wrote:
The NXP PCA9685 supports 16 channels/leds using a 12-bit PWM (4095 levels of brightness)
This driver supports configuration usingplatform_data.
Quoted 8 lines
I'm OK for this patch basically, just a little picky coding style issue as below.
drivers/leds/leds-pca9685.c line 94
Quoted 4 lines
drivers/leds/leds-pca9685.c new file +4
-0+if (brightness ==4096 ) + *((u16 *)(i2c_buffer+1 )) =cpu_to_le16 (0x1000 ); +else + *((u16 *)(i2c_buffer+1 )) =0x0000 ;One empty line here probably is better.
fixed.
Quoted 5 lines
drivers/leds/leds-pca9685.c line 124
Quoted 3 lines
drivers/leds/leds-pca9685.c new file +3
-0+ + pdata =dev_get_platdata (&client->dev); +No need empty line here.
fixed.
Quoted 4 lines
Quoted 2 lines
+ pca9685 = devm_kzalloc(&client->dev, 16 *
sizeof(*pca9685),GFP_KERNEL); +No need empty line here.
fixed.
Quoted 5 lines
Review
Quoted 3 lines
&pca9685[i].led_cdev); + if (err < 0)
(quoted diff) +2
-0+ goto exit; + }You can add one empty line here.
fixed
Thanks for the quick review. A v2 of this patch is on its way which addresses your and Peter Meerwald's comments.
Maximilian
--
To unsubscribe from this list: send the line "unsubscribe linux-leds" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 6 lines · Maximilian Guntner
The NXP PCA9685 supports 16 channels/leds using a 12-bit PWM (4095 levels of brightness)
This driver supports configuration usingplatform_data.Signed-off-by Maximilian Güntner <[email protected]>
I added some nit-picking comments. :-)
Quoted 9 lines
--- drivers/leds/Kconfig | 10 ++ drivers/leds/Makefile | 1 + drivers/leds/leds-pca9685.c | 219 +++++++++++++++++++++++++++++ include/linux/platform_data/leds-pca9685.h | 34 +++++ 4 files changed, 264 insertions(+) create mode 100644 drivers/leds/leds-pca9685.c create mode 100644 include/linux/platform_data/leds-pca9685.h
[.....]
drivers/leds/leds-pca9685.c line 25
Quoted 12 lines
drivers/leds/leds-pca9685.c new file +12
-0+ +#include <linux/module.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/leds.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/workqueue.h> +#include <linux/slab.h> + +#include <linux/platform_data/leds-pca9685.h>
Please, sort these header inclusions alphabetically for better readability.
[.....]
include/linux/platform_data/leds-pca9685.h line 15
Quoted 2 lines
include/linux/platform_data/leds-pca9685.h new file +2
-0+#ifndef __LINUX_PCA9685_H +#define __LINUX_PCA9685_H
You can add one empty line here.
Review
Quoted 2 lines
(quoted diff) +2
-0+#include <linux/leds.h> +
Best regards,
Jingoo Han
--
To unsubscribe from this list: send the line "unsubscribe linux-leds" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v2 1/1 leds: Added driver for the NXP PCA9685 I2C chip
In reply to Bryan WuThe NXP PCA9685 supports 16 channels/leds using a 12-bit PWM (4095 levels of brightness)
This driver supports configuration using platform_data.
---
v2:
addresses Bryan Wu's comments:
* removed/added some empty lines
addresses Peter Meerwald's comments:
* fixed typos
* replaced/moved one if statement
* replaced (1 << PCA9685_AI) with BIT(PCA9685_AI)
addresses Jingoo Han's comments:
* added an empty line
* sorted the header inclusions alphabetically
drivers/leds/Kconfig | 10 ++ drivers/leds/Makefile | 1 + drivers/leds/leds-pca9685.c | 213 +++++++++++++++++++++++++++++ include/linux/platform_data/leds-pca9685.h | 35 +++++ 4 files changed, 259 insertions(+) create mode 100644 drivers/leds/leds-pca9685.c create mode 100644 include/linux/platform_data/leds-pca9685.h
drivers/leds/Kconfig +10 -0
@@ -300,6 +300,16 @@ config LEDS_PCA963X LED driver chip accessed via the I2C bus. Supported devices include PCA9633 and PCA9634 +config LEDS_PCA9685 + tristate "LED support for PCA9685 I2C chip" + depends on LEDS_CLASS + depends on I2C + help + This option enables support for LEDs connected to the PCA9685 + LED driver chip accessed via the I2C bus. + The PCA9685 offers 12-bit PWM (4095 levels of brightness) on + 16 individual channels. + config LEDS_WM831X_STATUS tristate "LED support for status LEDs on WM831x PMICs" depends on LEDS_CLASS
drivers/leds/Makefile +1 -0
@@ -36,6 +36,7 @@ obj-$(CONFIG_LEDS_OT200) += leds-ot200.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o obj-$(CONFIG_LEDS_PCA963X) += leds-pca963x.o +obj-$(CONFIG_LEDS_PCA9685) += leds-pca9685.o obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o obj-$(CONFIG_LEDS_DA9052) += leds-da9052.o obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o
drivers/leds/leds-pca9685.c new file +213 -0
213 lines not on this page · raw message
include/linux/platform_data/leds-pca9685.h new file +35 -0
@@ -0,0 +1,35 @@ +/* +* Copyright 2013 Maximilian Güntner <[email protected]> +* +* This file is subject to the terms and conditions of version 2 of +* the GNU General Public License. See the file COPYING in the main +* directory of this archive for more details. +* +* Based on leds-pca963x.h by Peter Meerwald <[email protected]> +* +* LED driver for the NXP PCA9685 PWM chip +* +*/ + +#ifndef __LINUX_PCA9685_H +#define __LINUX_PCA9685_H + +#include <linux/leds.h> + +enum pca9685_outdrv { +PCA9685_OPEN_DRAIN , +PCA9685_TOTEM_POLE , +}; + +enum pca9685_inverted { +PCA9685_NOT_INVERTED , +PCA9685_INVERTED , +}; + +struct pca9685_platform_data { +struct led_platform_data leds; +enum pca9685_outdrv outdrv; +enum pca9685_inverted inverted; +}; + +#endif /* __LINUX_PCA9685_H */
Signature · 6 lines
1.8.4 -- To unsubscribe from this list: send the line "unsubscribe linux-leds" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 7 lines · Maximilian Güntner
The NXP PCA9685 supports 16 channels/leds using a 12-bit PWM (4095 levels of brightness)
This driver supports configuration usingplatform_data.Signed-off-by Maximilian Güntner <[email protected]>---
Quoted 11 lines
v2:
addresses Bryan Wu's comments:
* removed/added some empty lines
addresses Peter Meerwald's comments:
* fixed typos
* replaced/moved one if statement
* replaced (1 <<PCA9685_AI) withBIT(PCA9685_AI)
addresses Jingoo Han's comments:
* added an empty line
* sorted the header inclusions alphabetically
Thanks, I merged it into my tree.
-Bryan
Quoted 304 lines
drivers/leds/Kconfig | 10 ++ drivers/leds/Makefile | 1 + drivers/leds/leds-pca9685.c | 213 +++++++++++++++++++++++++++++ include/linux/platform_data/leds-pca9685.h | 35 +++++ 4 files changed, 259 insertions(+) create mode 100644 drivers/leds/leds-pca9685.c create mode 100644 include/linux/platform_data/leds-pca9685.hdrivers/leds/Kconfig +10
-0@@ -300,6 +300,16 @@ config LEDS_PCA963X LED driver chip accessed via the I2C bus. Supported devices include PCA9633 and PCA9634 +config LEDS_PCA9685 + tristate "LED support for PCA9685 I2C chip" + depends on LEDS_CLASS + depends on I2C + help + This option enables support for LEDs connected to the PCA9685 + LED driver chip accessed via the I2C bus. + The PCA9685 offers 12-bit PWM (4095 levels of brightness) on + 16 individual channels. + config LEDS_WM831X_STATUS tristate "LED support for status LEDs on WM831x PMICs" depends on LEDS_CLASSdrivers/leds/Makefile +1
-0@@ -36,6 +36,7 @@ obj-$(CONFIG_LEDS_OT200) += leds-ot200.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o obj-$(CONFIG_LEDS_PCA963X) += leds-pca963x.o +obj-$(CONFIG_LEDS_PCA9685) += leds-pca9685.o obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o obj-$(CONFIG_LEDS_DA9052) += leds-da9052.o obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.odrivers/leds/leds-pca9685.c new file +213
-0@@ -0,0 +1,213 @@ +/* +* Copyright 2013 Maximilian Güntner <[email protected]> +* +* This file is subject to the terms and conditions of version 2 of +* the GNU General Public License. See the file COPYING in the main +* directory of this archive for more details. +* +* Based on leds-pca963x.c driver by +* Peter Meerwald <[email protected]> +* +* Driver for the NXP PCA9685 12-Bit PWM LED driver chip. +* +*/ + +#include <linux/ctype.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/workqueue.h> + +#include <linux/platform_data/leds-pca9685.h> + +/* Register Addresses */ +#define PCA9685_MODE1 0x00 +#define PCA9685_MODE2 0x01 +#define PCA9685_LED0_ON_L 0x06 +#define PCA9685_ALL_LED_ON_L 0xFA + +/* MODE1 Register */ +#define PCA9685_ALLCALL 0x00 +#define PCA9685_SLEEP 0x04 +#define PCA9685_AI 0x05 + +/* MODE2 Register */ +#define PCA9685_INVRT 0x04 +#define PCA9685_OUTDRV 0x02 + +static const struct i2c_device_id pca9685_id[] = { + {"pca9685" ,0 }, + { } +}; +MODULE_DEVICE_TABLE (i2c, pca9685_id); + +struct pca9685_led { +struct i2c_client *client; +struct work_struct work; +u16 brightness; +struct led_classdev led_cdev; +int led_num;/* 0-15 */ +char name[32 ]; +}; + +static void pca9685_write_msg (struct i2c_client *client,u8 *buf,u8 len) +{ +struct i2c_msg msg = { + .addr = client->addr, + .flags =0x00 , + .len = len, + .buf = buf + }; +i2c_transfer (client->adapter, &msg,1 ); +} + +static void pca9685_all_off (struct i2c_client *client) +{ +u8 i2c_buffer[5 ] = {PCA9685_ALL_LED_ON_L ,0x00 ,0x00 ,0x00 ,0x10 }; +pca9685_write_msg (client, i2c_buffer,5 ); +} + +static void pca9685_led_work (struct work_struct *work) +{ +struct pca9685_led *pca9685; +u8 i2c_buffer[5 ]; + + pca9685 = container_of(work, struct pca9685_led, work); + i2c_buffer[0 ] =PCA9685_LED0_ON_L +4 * pca9685->led_num; +/* +* 4095 is the maximum brightness, so we set the ON time to 0x1000 +* which disables the PWM generator for that LED +*/ +if (pca9685->brightness ==4095 ) + *((u16 *)(i2c_buffer+1 )) =cpu_to_le16 (0x1000 ); +else + *((u16 *)(i2c_buffer+1 )) =0x0000 ; + +if (pca9685->brightness ==0 ) + *((u16 *)(i2c_buffer+3 )) =cpu_to_le16 (0x1000 ); +else if (pca9685->brightness ==4095 ) + *((u16 *)(i2c_buffer+3 )) =0x0000 ; +else + *((u16 *)(i2c_buffer+3 )) =cpu_to_le16 (pca9685->brightness); + +pca9685_write_msg (pca9685->client, i2c_buffer,5 ); +} + +static void pca9685_led_set (struct led_classdev *led_cdev, +enum led_brightness value) +{ +struct pca9685_led *pca9685; + pca9685 = container_of(led_cdev, struct pca9685_led, led_cdev); + pca9685->brightness = value; + +schedule_work (&pca9685->work); +} + +static int pca9685_probe (struct i2c_client *client, +const struct i2c_device_id *id) +{ +struct pca9685_led *pca9685; +struct pca9685_platform_data *pdata; +int err; +u8 i; + + pdata =dev_get_platdata (&client->dev); +if (pdata) { +if (pdata->leds.num_leds <1 || pdata->leds.num_leds >15 ) { +dev_err (&client->dev,"board info must claim 1-16 LEDs" ); +return -EINVAL ; + } + } + + pca9685 =devm_kzalloc (&client->dev,16 *sizeof (*pca9685),GFP_KERNEL ); +if (!pca9685) +return -ENOMEM ; + +i2c_set_clientdata (client, pca9685); +pca9685_all_off (client); + +for (i =0 ; i <16 ; i++) { + pca9685[i].client = client; + pca9685[i].led_num = i; + pca9685[i].name[0 ] ='\0' ; +if (pdata && i < pdata->leds.num_leds) { +if (pdata->leds.leds[i].name) +strncpy (pca9685[i].name, + pdata->leds.leds[i].name, +sizeof (pca9685[i].name)-1 ); +if (pdata->leds.leds[i].default_trigger) + pca9685[i].led_cdev.default_trigger = + pdata->leds.leds[i].default_trigger; + } +if (strlen (pca9685[i].name) ==0 ) { +/* +* Write adapter and address to the name as well. +* Otherwise multiple chips attached to one host would +* not work. +*/ +snprintf (pca9685[i].name,sizeof (pca9685[i].name), +"pca9685:%d:x%.2x:%d" , + client->adapter->nr, client->addr, i); + } + pca9685[i].led_cdev.name = pca9685[i].name; + pca9685[i].led_cdev.max_brightness =0xfff ; + pca9685[i].led_cdev.brightness_set = pca9685_led_set; + +INIT_WORK (&pca9685[i].work, pca9685_led_work); + err =led_classdev_register (&client->dev, &pca9685[i].led_cdev); +if (err <0 ) + goto exit; + } + +if (pdata) +i2c_smbus_write_byte_data (client,PCA9685_MODE2 , + pdata->outdrv <<PCA9685_OUTDRV | + pdata->inverted <<PCA9685_INVRT ); +else +i2c_smbus_write_byte_data (client,PCA9685_MODE2 , +PCA9685_TOTEM_POLE <<PCA9685_OUTDRV ); +/* Enable Auto-Increment, enable oscillator, ALLCALL/SUBADDR disabled */ +i2c_smbus_write_byte_data (client,PCA9685_MODE1 ,BIT (PCA9685_AI )); + +return 0 ; + +exit: +while (i--) { +led_classdev_unregister (&pca9685[i].led_cdev); +cancel_work_sync (&pca9685[i].work); + } +return err; +} + +static int pca9685_remove (struct i2c_client *client) +{ +struct pca9685_led *pca9685 =i2c_get_clientdata (client); +u8 i; + +for (i =0 ; i <16 ; i++) { +led_classdev_unregister (&pca9685[i].led_cdev); +cancel_work_sync (&pca9685[i].work); + } +pca9685_all_off (client); +return 0 ; +} + +static struct i2c_driver pca9685_driver = { + .driver = { + .name ="leds-pca9685" , + .owner =THIS_MODULE , + }, + .probe = pca9685_probe, + .remove = pca9685_remove, + .id_table = pca9685_id, +}; + +module_i2c_driver (pca9685_driver); + +MODULE_AUTHOR ("Maximilian Güntner <[email protected]>" ); +MODULE_DESCRIPTION ("PCA9685 LED Driver" ); +MODULE_LICENSE ("GPL v2" );include/linux/platform_data/leds-pca9685.h new file +35
-1@@ -0,0 +1,35 @@ +/* +* Copyright 2013 Maximilian Güntner <[email protected]> +* +* This file is subject to the terms and conditions of version 2 of +* the GNU General Public License. See the file COPYING in the main +* directory of this archive for more details. +* +* Based on leds-pca963x.h by Peter Meerwald <[email protected]> +* +* LED driver for the NXP PCA9685 PWM chip +* +*/ + +#ifndef __LINUX_PCA9685_H +#define __LINUX_PCA9685_H + +#include <linux/leds.h> + +enum pca9685_outdrv { +PCA9685_OPEN_DRAIN , +PCA9685_TOTEM_POLE , +}; + +enum pca9685_inverted { +PCA9685_NOT_INVERTED , +PCA9685_INVERTED , +}; + +struct pca9685_platform_data { +struct led_platform_data leds; +enum pca9685_outdrv outdrv; +enum pca9685_inverted inverted; +}; + +#endif /* __LINUX_PCA9685_H */ --1.8.4
PATCH v3 1/1 leds: Added driver for the NXP PCA9685 I2C chip
The NXP PCA9685 supports 16 channels/leds using a 12-bit PWM (4095 levels of brightness)
This driver supports configuration using platform_data.
---
v3:
fixed warnings when running make C=1 CF=-D__CHECK_ENDIAN__:
* replaced u16* by __le16*
v2:
addresses bryan wu's comments:
* removed/added some empty lines
addresses peter meerwald's comments:
* fixed typos
* replaced/moved one if statement
* replaced (1 << pca9685_ai) with bit(pca9685_ai)
addresses jingoo han's comments:
* added an empty line
* sorted the header inclusions alphabetically
drivers/leds/Kconfig | 10 ++ drivers/leds/Makefile | 1 + drivers/leds/leds-pca9685.c | 213 +++++++++++++++++++++++++++++ include/linux/platform_data/leds-pca9685.h | 35 +++++ 4 files changed, 259 insertions(+) create mode 100644 drivers/leds/leds-pca9685.c create mode 100644 include/linux/platform_data/leds-pca9685.h
drivers/leds/Kconfig +10 -0
@@ -300,6 +300,16 @@ config LEDS_PCA963X LED driver chip accessed via the I2C bus. Supported devices include PCA9633 and PCA9634 +config LEDS_PCA9685 + tristate "LED support for PCA9685 I2C chip" + depends on LEDS_CLASS + depends on I2C + help + This option enables support for LEDs connected to the PCA9685 + LED driver chip accessed via the I2C bus. + The PCA9685 offers 12-bit PWM (4095 levels of brightness) on + 16 individual channels. + config LEDS_WM831X_STATUS tristate "LED support for status LEDs on WM831x PMICs" depends on LEDS_CLASS
drivers/leds/Makefile +1 -0
@@ -36,6 +36,7 @@ obj-$(CONFIG_LEDS_OT200) += leds-ot200.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o obj-$(CONFIG_LEDS_PCA963X) += leds-pca963x.o +obj-$(CONFIG_LEDS_PCA9685) += leds-pca9685.o obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o obj-$(CONFIG_LEDS_DA9052) += leds-da9052.o obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o
drivers/leds/leds-pca9685.c new file +213 -0
213 lines not on this page · raw message
include/linux/platform_data/leds-pca9685.h new file +35 -0
@@ -0,0 +1,35 @@ +/* +* Copyright 2013 Maximilian Güntner <[email protected]> +* +* This file is subject to the terms and conditions of version 2 of +* the GNU General Public License. See the file COPYING in the main +* directory of this archive for more details. +* +* Based on leds-pca963x.h by Peter Meerwald <[email protected]> +* +* LED driver for the NXP PCA9685 PWM chip +* +*/ + +#ifndef __LINUX_PCA9685_H +#define __LINUX_PCA9685_H + +#include <linux/leds.h> + +enum pca9685_outdrv { +PCA9685_OPEN_DRAIN , +PCA9685_TOTEM_POLE , +}; + +enum pca9685_inverted { +PCA9685_NOT_INVERTED , +PCA9685_INVERTED , +}; + +struct pca9685_platform_data { +struct led_platform_data leds; +enum pca9685_outdrv outdrv; +enum pca9685_inverted inverted; +}; + +#endif /* __LINUX_PCA9685_H */
Signature · 6 lines
1.8.4 -- To unsubscribe from this list: send the line "unsubscribe linux-leds" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 10 lines · Maximilian Güntner
The NXP PCA9685 supports 16 channels/leds using a 12-bit PWM (4095 levels of brightness)
This driver supports configuration usingplatform_data.Signed-off-by Maximilian Güntner <[email protected]>---
v3:
fixed warnings when running make C=1 CF=-D__CHECK_ENDIAN__:
* replaced u16* by __le16*
OK, replaced the old one in my tree.
Thanks,
-Bryan
Quoted 313 lines
v2:
addresses bryan wu's comments:
* removed/added some empty lines
addresses peter meerwald's comments:
* fixed typos
* replaced/moved one if statement
* replaced (1 <<pca9685_ai) withbit(pca9685_ai)
addresses jingoo han's comments:
* added an empty line
* sorted the header inclusions alphabeticallydrivers/leds/Kconfig | 10 ++ drivers/leds/Makefile | 1 + drivers/leds/leds-pca9685.c | 213 +++++++++++++++++++++++++++++ include/linux/platform_data/leds-pca9685.h | 35 +++++ 4 files changed, 259 insertions(+) create mode 100644 drivers/leds/leds-pca9685.c create mode 100644 include/linux/platform_data/leds-pca9685.hdrivers/leds/Kconfig +10
-0@@ -300,6 +300,16 @@ config LEDS_PCA963X LED driver chip accessed via the I2C bus. Supported devices include PCA9633 and PCA9634 +config LEDS_PCA9685 + tristate "LED support for PCA9685 I2C chip" + depends on LEDS_CLASS + depends on I2C + help + This option enables support for LEDs connected to the PCA9685 + LED driver chip accessed via the I2C bus. + The PCA9685 offers 12-bit PWM (4095 levels of brightness) on + 16 individual channels. + config LEDS_WM831X_STATUS tristate "LED support for status LEDs on WM831x PMICs" depends on LEDS_CLASSdrivers/leds/Makefile +1
-0@@ -36,6 +36,7 @@ obj-$(CONFIG_LEDS_OT200) += leds-ot200.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o obj-$(CONFIG_LEDS_PCA963X) += leds-pca963x.o +obj-$(CONFIG_LEDS_PCA9685) += leds-pca9685.o obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o obj-$(CONFIG_LEDS_DA9052) += leds-da9052.o obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.odrivers/leds/leds-pca9685.c new file +213
-0@@ -0,0 +1,213 @@ +/* +* Copyright 2013 Maximilian Güntner <[email protected]> +* +* This file is subject to the terms and conditions of version 2 of +* the GNU General Public License. See the file COPYING in the main +* directory of this archive for more details. +* +* Based on leds-pca963x.c driver by +* Peter Meerwald <[email protected]> +* +* Driver for the NXP PCA9685 12-Bit PWM LED driver chip. +* +*/ + +#include <linux/ctype.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/workqueue.h> + +#include <linux/platform_data/leds-pca9685.h> + +/* Register Addresses */ +#define PCA9685_MODE1 0x00 +#define PCA9685_MODE2 0x01 +#define PCA9685_LED0_ON_L 0x06 +#define PCA9685_ALL_LED_ON_L 0xFA + +/* MODE1 Register */ +#define PCA9685_ALLCALL 0x00 +#define PCA9685_SLEEP 0x04 +#define PCA9685_AI 0x05 + +/* MODE2 Register */ +#define PCA9685_INVRT 0x04 +#define PCA9685_OUTDRV 0x02 + +static const struct i2c_device_id pca9685_id[] = { + {"pca9685" ,0 }, + { } +}; +MODULE_DEVICE_TABLE (i2c, pca9685_id); + +struct pca9685_led { +struct i2c_client *client; +struct work_struct work; +u16 brightness; +struct led_classdev led_cdev; +int led_num;/* 0-15 */ +char name[32 ]; +}; + +static void pca9685_write_msg (struct i2c_client *client,u8 *buf,u8 len) +{ +struct i2c_msg msg = { + .addr = client->addr, + .flags =0x00 , + .len = len, + .buf = buf + }; +i2c_transfer (client->adapter, &msg,1 ); +} + +static void pca9685_all_off (struct i2c_client *client) +{ +u8 i2c_buffer[5 ] = {PCA9685_ALL_LED_ON_L ,0x00 ,0x00 ,0x00 ,0x10 }; +pca9685_write_msg (client, i2c_buffer,5 ); +} + +static void pca9685_led_work (struct work_struct *work) +{ +struct pca9685_led *pca9685; +u8 i2c_buffer[5 ]; + + pca9685 = container_of(work, struct pca9685_led, work); + i2c_buffer[0 ] =PCA9685_LED0_ON_L +4 * pca9685->led_num; +/* +* 4095 is the maximum brightness, so we set the ON time to 0x1000 +* which disables the PWM generator for that LED +*/ +if (pca9685->brightness ==4095 ) + *((__le16 *)(i2c_buffer+1 )) =cpu_to_le16 (0x1000 ); +else + *((__le16 *)(i2c_buffer+1 )) =0x0000 ; + +if (pca9685->brightness ==0 ) + *((__le16 *)(i2c_buffer+3 )) =cpu_to_le16 (0x1000 ); +else if (pca9685->brightness ==4095 ) + *((__le16 *)(i2c_buffer+3 )) =0x0000 ; +else + *((__le16 *)(i2c_buffer+3 )) =cpu_to_le16 (pca9685->brightness); + +pca9685_write_msg (pca9685->client, i2c_buffer,5 ); +} + +static void pca9685_led_set (struct led_classdev *led_cdev, +enum led_brightness value) +{ +struct pca9685_led *pca9685; + pca9685 = container_of(led_cdev, struct pca9685_led, led_cdev); + pca9685->brightness = value; + +schedule_work (&pca9685->work); +} + +static int pca9685_probe (struct i2c_client *client, +const struct i2c_device_id *id) +{ +struct pca9685_led *pca9685; +struct pca9685_platform_data *pdata; +int err; +u8 i; + + pdata =dev_get_platdata (&client->dev); +if (pdata) { +if (pdata->leds.num_leds <1 || pdata->leds.num_leds >15 ) { +dev_err (&client->dev,"board info must claim 1-16 LEDs" ); +return -EINVAL ; + } + } + + pca9685 =devm_kzalloc (&client->dev,16 *sizeof (*pca9685),GFP_KERNEL ); +if (!pca9685) +return -ENOMEM ; + +i2c_set_clientdata (client, pca9685); +pca9685_all_off (client); + +for (i =0 ; i <16 ; i++) { + pca9685[i].client = client; + pca9685[i].led_num = i; + pca9685[i].name[0 ] ='\0' ; +if (pdata && i < pdata->leds.num_leds) { +if (pdata->leds.leds[i].name) +strncpy (pca9685[i].name, + pdata->leds.leds[i].name, +sizeof (pca9685[i].name)-1 ); +if (pdata->leds.leds[i].default_trigger) + pca9685[i].led_cdev.default_trigger = + pdata->leds.leds[i].default_trigger; + } +if (strlen (pca9685[i].name) ==0 ) { +/* +* Write adapter and address to the name as well. +* Otherwise multiple chips attached to one host would +* not work. +*/ +snprintf (pca9685[i].name,sizeof (pca9685[i].name), +"pca9685:%d:x%.2x:%d" , + client->adapter->nr, client->addr, i); + } + pca9685[i].led_cdev.name = pca9685[i].name; + pca9685[i].led_cdev.max_brightness =0xfff ; + pca9685[i].led_cdev.brightness_set = pca9685_led_set; + +INIT_WORK (&pca9685[i].work, pca9685_led_work); + err =led_classdev_register (&client->dev, &pca9685[i].led_cdev); +if (err <0 ) + goto exit; + } + +if (pdata) +i2c_smbus_write_byte_data (client,PCA9685_MODE2 , + pdata->outdrv <<PCA9685_OUTDRV | + pdata->inverted <<PCA9685_INVRT ); +else +i2c_smbus_write_byte_data (client,PCA9685_MODE2 , +PCA9685_TOTEM_POLE <<PCA9685_OUTDRV ); +/* Enable Auto-Increment, enable oscillator, ALLCALL/SUBADDR disabled */ +i2c_smbus_write_byte_data (client,PCA9685_MODE1 ,BIT (PCA9685_AI )); + +return 0 ; + +exit: +while (i--) { +led_classdev_unregister (&pca9685[i].led_cdev); +cancel_work_sync (&pca9685[i].work); + } +return err; +} + +static int pca9685_remove (struct i2c_client *client) +{ +struct pca9685_led *pca9685 =i2c_get_clientdata (client); +u8 i; + +for (i =0 ; i <16 ; i++) { +led_classdev_unregister (&pca9685[i].led_cdev); +cancel_work_sync (&pca9685[i].work); + } +pca9685_all_off (client); +return 0 ; +} + +static struct i2c_driver pca9685_driver = { + .driver = { + .name ="leds-pca9685" , + .owner =THIS_MODULE , + }, + .probe = pca9685_probe, + .remove = pca9685_remove, + .id_table = pca9685_id, +}; + +module_i2c_driver (pca9685_driver); + +MODULE_AUTHOR ("Maximilian Güntner <[email protected]>" ); +MODULE_DESCRIPTION ("PCA9685 LED Driver" ); +MODULE_LICENSE ("GPL v2" );include/linux/platform_data/leds-pca9685.h new file +35
-1@@ -0,0 +1,35 @@ +/* +* Copyright 2013 Maximilian Güntner <[email protected]> +* +* This file is subject to the terms and conditions of version 2 of +* the GNU General Public License. See the file COPYING in the main +* directory of this archive for more details. +* +* Based on leds-pca963x.h by Peter Meerwald <[email protected]> +* +* LED driver for the NXP PCA9685 PWM chip +* +*/ + +#ifndef __LINUX_PCA9685_H +#define __LINUX_PCA9685_H + +#include <linux/leds.h> + +enum pca9685_outdrv { +PCA9685_OPEN_DRAIN , +PCA9685_TOTEM_POLE , +}; + +enum pca9685_inverted { +PCA9685_NOT_INVERTED , +PCA9685_INVERTED , +}; + +struct pca9685_platform_data { +struct led_platform_data leds; +enum pca9685_outdrv outdrv; +enum pca9685_inverted inverted; +}; + +#endif /* __LINUX_PCA9685_H */ --
Quoted 2 lines
1.8.4
--
To unsubscribe from this list: send the line "unsubscribe linux-leds" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Hello Bryan,
thank you for applying the patch so quickly.
Maximilian
2013/10/18 Bryan Wu <[email protected]>:
Quoted 13 lines
Quoted 10 lines · Maximilian Güntner
The NXP PCA9685 supports 16 channels/leds using a 12-bit PWM (4095 levels of brightness)
This driver supports configuration usingplatform_data.Signed-off-by Maximilian Güntner <[email protected]>---
v3:
fixed warnings when running make C=1 CF=-D__CHECK_ENDIAN__:
* replaced u16* by __le16*
Quoted 5 lines
OK, replaced the old one in my tree.
Thanks,
-Bryan
--
To unsubscribe from this list: send the line "unsubscribe linux-leds" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html