blob: 4aa2900487584bde9e1a41d24b8d20cb6e38d43c [file] [log] [blame]
/* driver/leds/leds-lp5521_htc.c
*
* Copyright (C) 2010 HTC Corporation.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/alarmtimer.h>
#include <linux/leds.h>
#include <linux/leds-lp5521_htc.h>
#include <linux/regulator/consumer.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#define LP5521_MAX_LEDS 9 /* Maximum number of LEDs */
#define D(x...) printk(KERN_DEBUG "[LED]" x)
#define I(x...) printk(KERN_INFO "[LED]" x)
static int led_rw_delay, chip_enable;
static int current_time;
static struct i2c_client *private_lp5521_client;
static struct mutex led_mutex;
static struct workqueue_struct *g_led_work_queue;
static uint32_t ModeRGB;
#define Mode_Mask (0xff << 24)
#define Red_Mask (0xff << 16)
#define Green_Mask (0xff << 8)
#define Blue_Mask 0xff
struct lp5521_led {
int id;
u8 chan_nr;
u8 led_current;
u8 max_current;
struct led_classdev cdev;
struct mutex led_data_mutex;
struct alarm led_alarm;
struct work_struct led_work;
struct work_struct led_work_multicolor;
uint8_t Mode;
uint8_t Red;
uint8_t Green;
uint8_t Blue;
struct delayed_work blink_delayed_work;
};
struct lp5521_chip {
struct led_i2c_platform_data *pdata;
struct mutex led_i2c_rw_mutex; /* Serialize control */
struct i2c_client *client;
struct lp5521_led leds[LP5521_MAX_LEDS];
};
static int lp5521_parse_dt(struct device *, struct led_i2c_platform_data *);
static char *hex2string(uint8_t *data, int len)
{
static char buf[LED_I2C_WRITE_BLOCK_SIZE*4];
int i;
i = LED_I2C_WRITE_BLOCK_SIZE -1;
if (len > i)
len = i;
for (i = 0; i < len; i++)
sprintf(buf + i * 4, "[%02X]", data[i]);
return buf;
}
static int i2c_write_block(struct i2c_client *client, uint8_t addr,
uint8_t *data, int length)
{
int retry;
uint8_t buf[LED_I2C_WRITE_BLOCK_SIZE];
int i;
struct lp5521_chip *cdata;
struct i2c_msg msg[] = {
{
.addr = client->addr,
.flags = 0,
.len = length + 1,
.buf = buf,
}
};
dev_dbg(&client->dev, "W [%02X] = %s\n",
addr, hex2string(data, length));
cdata = i2c_get_clientdata(client);
if (length + 1 > LED_I2C_WRITE_BLOCK_SIZE) {
dev_err(&client->dev, "[LED] i2c_write_block length too long\n");
return -E2BIG;
}
buf[0] = addr;
for (i = 0; i < length; i++)
buf[i+1] = data[i];
mutex_lock(&cdata->led_i2c_rw_mutex);
msleep(1);
for (retry = 0; retry < I2C_WRITE_RETRY_TIMES; retry++) {
if (i2c_transfer(client->adapter, msg, 1) == 1)
break;
msleep(led_rw_delay);
}
if (retry >= I2C_WRITE_RETRY_TIMES) {
dev_err(&client->dev, "[LED] i2c_write_block retry over %d times\n",
I2C_WRITE_RETRY_TIMES);
mutex_unlock(&cdata->led_i2c_rw_mutex);
return -EIO;
}
mutex_unlock(&cdata->led_i2c_rw_mutex);
return 0;
}
static int I2C_RxData_2(char *rxData, int length)
{
uint8_t loop_i;
struct i2c_msg msgs[] = {
{
.addr = private_lp5521_client->addr,
.flags = 0,
.len = 1,
.buf = rxData,
},
{
.addr = private_lp5521_client->addr,
.flags = I2C_M_RD,
.len = length,
.buf = rxData,
},
};
for (loop_i = 0; loop_i < I2C_WRITE_RETRY_TIMES; loop_i++) {
if (i2c_transfer(private_lp5521_client->adapter, msgs, 2) > 0)
break;
msleep(10);
}
if (loop_i >= I2C_WRITE_RETRY_TIMES) {
printk(KERN_ERR "[LED] %s retry over %d times\n",
__func__, I2C_WRITE_RETRY_TIMES);
return -EIO;
}
return 0;
}
static int i2c_read_block(struct i2c_client *client,
uint8_t cmd, uint8_t *pdata, int length)
{
char buffer[3] = {0};
int ret = 0, i;
if (pdata == NULL)
return -EFAULT;
if (length > 2) {
pr_err("[LED]%s: length %d> 2: \n", __func__, length);
return ret;
}
buffer[0] = cmd;
ret = I2C_RxData_2(buffer, length);
if (ret < 0) {
pr_err("[LED]%s: I2C_RxData fail \n", __func__);
return ret;
}
for (i = 0; i < length; i++) {
*(pdata+i) = buffer[i];
}
return ret;
}
static void lp5521_led_enable(struct i2c_client *client)
{
int ret = 0;
uint8_t data;
struct lp5521_chip *cdata = i2c_get_clientdata(client);
struct led_i2c_platform_data *pdata = cdata->pdata;
char data1[1] = {0};
I(" %s +++\n" , __func__);
/* === led pin enable ===*/
if (pdata->ena_gpio) {
ret = gpio_direction_output(pdata->ena_gpio, 1);
if (ret < 0) {
pr_err("[LED] %s: gpio_direction_output high failed %d\n", __func__, ret);
gpio_free(pdata->ena_gpio);
}
}
msleep(1);
/* === reset ===*/
data = 0xFF;
ret = i2c_write_block(client, 0x0d, &data, 1);
msleep(20);
ret = i2c_read_block(client, 0x05, data1, 1);
if (data1[0] != 0xaf) {
I(" %s reset not ready %x\n" , __func__, data1[0]);
}
chip_enable = 1;
mutex_lock(&led_mutex);
/* === enable CHIP_EN === */
data = 0x40;
ret = i2c_write_block(client, ENABLE_REGISTER, &data, 1);
udelay(550);
/* === configuration control in power save mode=== */
data = 0x29;
ret = i2c_write_block(client, 0x08, &data, 1);
/* === set RGB current to 9.5mA === */
data = (u8)95;
ret = i2c_write_block(client, 0x05, &data, 1);
data = (u8)95;
ret = i2c_write_block(client, 0x06, &data, 1);
data = (u8)95;
ret = i2c_write_block(client, 0x07, &data, 1);
mutex_unlock(&led_mutex);
I(" %s ---\n" , __func__);
}
static void lp5521_red_long_blink(struct i2c_client *client)
{
uint8_t data = 0x00;
int ret;
I(" %s +++\n" , __func__);
mutex_lock(&led_mutex);
data = 0x10;
ret = i2c_write_block(client, OPRATION_REGISTER, &data, 1);
udelay(200);
/* === set pwm to 200 === */
data = 0x40;
ret = i2c_write_block(client, 0x10, &data, 1);
data = 0xc8;
ret = i2c_write_block(client, 0x11, &data, 1);
/* === wait 0.999s === */
data = 0x7f;
ret = i2c_write_block(client, 0x12, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x13, &data, 1);
/* === set pwm to 0 === */
data = 0x40;
ret = i2c_write_block(client, 0x14, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x15, &data, 1);
/* === wait 0.999s === */
data = 0x7f;
ret = i2c_write_block(client, 0x16, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x17, &data, 1);
/* === clear register === */
data = 0x00;
ret = i2c_write_block(client, 0x18, &data, 1);
ret = i2c_write_block(client, 0x19, &data, 1);
/* === run program === */
data = 0x20;
ret = i2c_write_block(client, OPRATION_REGISTER, &data, 1);
udelay(200);
data = 0x60;
ret = i2c_write_block(client, ENABLE_REGISTER, &data, 1);
udelay(550);
mutex_unlock(&led_mutex);
I(" %s ---\n" , __func__);
}
static void lp5521_breathing(struct i2c_client *client, uint8_t red, uint8_t green, uint8_t blue)
{
uint8_t data = 0x00;
// uint8_t data1[2]={0};
int ret;
uint8_t mode = 0x00;
I(" %s +++ red:%d, green:%d, blue:%d\n" , __func__, red, green, blue);
mutex_lock(&led_mutex);
if (red)
mode |= (3 << 4);
if (green)
mode |= (3 << 2);
if (blue)
mode |= 3;
data = mode & 0x15;
ret = i2c_write_block(client, OPRATION_REGISTER, &data, 1);
udelay(200);
if (red) {
data = 0x0a;
ret = i2c_write_block(client, 0x10, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x11, &data, 1);
data = 0x33;
ret = i2c_write_block(client, 0x12, &data, 1);
data = 0x13;
ret = i2c_write_block(client, 0x13, &data, 1);
data = 0x14;
ret = i2c_write_block(client, 0x14, &data, 1);
data = 0x31;
ret = i2c_write_block(client, 0x15, &data, 1);
data = 0x0c;
ret = i2c_write_block(client, 0x16, &data, 1);
data = 0x31;
ret = i2c_write_block(client, 0x17, &data, 1);
data = 0x1f;
ret = i2c_write_block(client, 0x18, &data, 1);
data = 0x13;
ret = i2c_write_block(client, 0x19, &data, 1);
data = 0x60;
ret = i2c_write_block(client, 0x1a, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x1b, &data, 1);
data = 0x1f;
ret = i2c_write_block(client, 0x1c, &data, 1);
data = 0x93;
ret = i2c_write_block(client, 0x1d, &data, 1);
data = 0x0c;
ret = i2c_write_block(client, 0x1e, &data, 1);
data = 0xb1;
ret = i2c_write_block(client, 0x1f, &data, 1);
data = 0x14;
ret = i2c_write_block(client, 0x20, &data, 1);
data = 0xb1;
ret = i2c_write_block(client, 0x21, &data, 1);
data = 0x3d;
ret = i2c_write_block(client, 0x22, &data, 1);
data = 0x93;
ret = i2c_write_block(client, 0x23, &data, 1);
data = 0x73;
ret = i2c_write_block(client, 0x24, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x25, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x26, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x27, &data, 1);
}
if (green) {
data = 0x0a;
ret = i2c_write_block(client, 0x30, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x31, &data, 1);
data = 0x33;
ret = i2c_write_block(client, 0x32, &data, 1);
data = 0x13;
ret = i2c_write_block(client, 0x33, &data, 1);
data = 0x14;
ret = i2c_write_block(client, 0x34, &data, 1);
data = 0x31;
ret = i2c_write_block(client, 0x35, &data, 1);
data = 0x0c;
ret = i2c_write_block(client, 0x36, &data, 1);
data = 0x31;
ret = i2c_write_block(client, 0x37, &data, 1);
data = 0x1f;
ret = i2c_write_block(client, 0x38, &data, 1);
data = 0x13;
ret = i2c_write_block(client, 0x39, &data, 1);
data = 0x60;
ret = i2c_write_block(client, 0x3a, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x3b, &data, 1);
data = 0x1f;
ret = i2c_write_block(client, 0x3c, &data, 1);
data = 0x93;
ret = i2c_write_block(client, 0x3d, &data, 1);
data = 0x0c;
ret = i2c_write_block(client, 0x3e, &data, 1);
data = 0xb1;
ret = i2c_write_block(client, 0x3f, &data, 1);
data = 0x14;
ret = i2c_write_block(client, 0x40, &data, 1);
data = 0xb1;
ret = i2c_write_block(client, 0x41, &data, 1);
data = 0x3d;
ret = i2c_write_block(client, 0x42, &data, 1);
data = 0x93;
ret = i2c_write_block(client, 0x43, &data, 1);
data = 0x73;
ret = i2c_write_block(client, 0x44, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x45, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x46, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x47, &data, 1);
}
if (blue) {
data = 0x0a;
ret = i2c_write_block(client, 0x50, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x51, &data, 1);
data = 0x33;
ret = i2c_write_block(client, 0x52, &data, 1);
data = 0x13;
ret = i2c_write_block(client, 0x53, &data, 1);
data = 0x14;
ret = i2c_write_block(client, 0x54, &data, 1);
data = 0x31;
ret = i2c_write_block(client, 0x55, &data, 1);
data = 0x0c;
ret = i2c_write_block(client, 0x56, &data, 1);
data = 0x31;
ret = i2c_write_block(client, 0x57, &data, 1);
data = 0x1f;
ret = i2c_write_block(client, 0x58, &data, 1);
data = 0x13;
ret = i2c_write_block(client, 0x59, &data, 1);
data = 0x60;
ret = i2c_write_block(client, 0x5a, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x5b, &data, 1);
data = 0x1f;
ret = i2c_write_block(client, 0x5c, &data, 1);
data = 0x93;
ret = i2c_write_block(client, 0x5d, &data, 1);
data = 0x0c;
ret = i2c_write_block(client, 0x5e, &data, 1);
data = 0xb1;
ret = i2c_write_block(client, 0x5f, &data, 1);
data = 0x14;
ret = i2c_write_block(client, 0x60, &data, 1);
data = 0xb1;
ret = i2c_write_block(client, 0x61, &data, 1);
data = 0x3d;
ret = i2c_write_block(client, 0x62, &data, 1);
data = 0x93;
ret = i2c_write_block(client, 0x63, &data, 1);
data = 0x73;
ret = i2c_write_block(client, 0x64, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x65, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x66, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x67, &data, 1);
}
/* === run program === */
data = mode & 0x2a;
ret = i2c_write_block(client, OPRATION_REGISTER, &data, 1);
udelay(200);
data = (mode & 0x2a) | 0x40;
ret = i2c_write_block(client, ENABLE_REGISTER, &data, 1);
udelay(550);
mutex_unlock(&led_mutex);
I(" %s ---\n" , __func__);
}
static void lp5521_color_blink(struct i2c_client *client, uint8_t red, uint8_t green, uint8_t blue)
{
uint8_t data = 0x00;
int ret;
uint8_t mode = 0x00;
I(" %s +++ red:%d, green:%d, blue:%d\n" , __func__, red, green, blue);
mutex_lock(&led_mutex);
if (red)
mode |= (3 << 4);
if (green)
mode |= (3 << 2);
if (blue)
mode |= 3;
data = mode & 0x15;
ret = i2c_write_block(client, OPRATION_REGISTER, &data, 1);
udelay(200);
if (red) {
/* === wait 0.999s === */
data = 0x7f;
ret = i2c_write_block(client, 0x10, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x11, &data, 1);
/* === set red pwm === */
data = 0x40;
ret = i2c_write_block(client, 0x12, &data, 1);
ret = i2c_write_block(client, 0x13, &red, 1);
/* === wait 0.064s === */
data = 0x44;
ret = i2c_write_block(client, 0x14, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x15, &data, 1);
/* === set pwm to 0 === */
data = 0x40;
ret = i2c_write_block(client, 0x16, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x17, &data, 1);
/* === wait 0.935s === */
data = 0x7c;
ret = i2c_write_block(client, 0x18, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x19, &data, 1);
/* === clear register === */
data = 0x00;
ret = i2c_write_block(client, 0x1a, &data, 1);
ret = i2c_write_block(client, 0x1b, &data, 1);
}
if (green) {
/* === wait 0.999s === */
data = 0x7f;
ret = i2c_write_block(client, 0x30, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x31, &data, 1);
/* === set green pwm === */
data = 0x40;
ret = i2c_write_block(client, 0x32, &data, 1);
ret = i2c_write_block(client, 0x33, &green, 1);
/* === wait 0.064s === */
data = 0x44;
ret = i2c_write_block(client, 0x34, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x35, &data, 1);
/* === set pwm to 0 === */
data = 0x40;
ret = i2c_write_block(client, 0x36, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x37, &data, 1);
/* === wait 0.935s === */
data = 0x7c;
ret = i2c_write_block(client, 0x38, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x39, &data, 1);
/* === clear register === */
data = 0x00;
ret = i2c_write_block(client, 0x3a, &data, 1);
ret = i2c_write_block(client, 0x3b, &data, 1);
}
if (blue) {
/* === wait 0.999s === */
data = 0x7f;
ret = i2c_write_block(client, 0x50, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x51, &data, 1);
/* === set blue pwm === */
data = 0x40;
ret = i2c_write_block(client, 0x52, &data, 1);
ret = i2c_write_block(client, 0x53, &blue, 1);
/* === wait 0.064s === */
data = 0x44;
ret = i2c_write_block(client, 0x54, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x55, &data, 1);
/* === set pwm to 0 === */
data = 0x40;
ret = i2c_write_block(client, 0x56, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x57, &data, 1);
/* === wait 0.935s === */
data = 0x7c;
ret = i2c_write_block(client, 0x58, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x59, &data, 1);
/* === clear register === */
data = 0x00;
ret = i2c_write_block(client, 0x5a, &data, 1);
ret = i2c_write_block(client, 0x5b, &data, 1);
}
/* === run program === */
data = mode & 0x2a;
ret = i2c_write_block(client, OPRATION_REGISTER, &data, 1);
udelay(200);
data = (mode & 0x2a)|0x40;
ret = i2c_write_block(client, ENABLE_REGISTER, &data, 1);
udelay(550);
mutex_unlock(&led_mutex);
I(" %s ---\n" , __func__);
}
static void lp5521_dual_color_blink(struct i2c_client *client)
{
uint8_t data = 0x00;
int ret;
I(" %s +++\n" , __func__);
lp5521_led_enable(client);
mutex_lock(&led_mutex);
data = 0x14;
ret = i2c_write_block(client, OPRATION_REGISTER, &data, 1);
udelay(200);
/* === set pwm to 200 === */
data = 0x40;
ret = i2c_write_block(client, 0x10, &data, 1);
data = 0xc8;
ret = i2c_write_block(client, 0x11, &data, 1);
/* === wait 0.064s === */
data = 0x44;
ret = i2c_write_block(client, 0x12, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x13, &data, 1);
/* === set pwm to 0 === */
data = 0x40;
ret = i2c_write_block(client, 0x14, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x15, &data, 1);
/* === wait 0.25s === */
data = 0x50;
ret = i2c_write_block(client, 0x16, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x17, &data, 1);
/* === trigger sg, wg === */
data = 0xe1;
ret = i2c_write_block(client, 0x18, &data, 1);
data = 0x04;
ret = i2c_write_block(client, 0x19, &data, 1);
/* === clear register === */
data = 0x00;
ret = i2c_write_block(client, 0x1a, &data, 1);
ret = i2c_write_block(client, 0x1b, &data, 1);
udelay(550);
/* === trigger wr === */
data = 0xe0;
ret = i2c_write_block(client, 0x30, &data, 1);
data = 0x80;
ret = i2c_write_block(client, 0x31, &data, 1);
udelay(550);
/* set pwm to 200 */
data = 0x40;
ret = i2c_write_block(client, 0x32, &data, 1);
data = 0xc8;
ret = i2c_write_block(client, 0x33, &data, 1);
/* === wait 0.064s === */
data = 0x44;
ret = i2c_write_block(client, 0x34, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x35, &data, 1);
/* === set pwm to 0 === */
data = 0x40;
ret = i2c_write_block(client, 0x36, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x37, &data, 1);
/* === wait 0.999s === */
data = 0x7f;
ret = i2c_write_block(client, 0x38, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x39, &data, 1);
/* === wait 0.622s === */
data = 0x68;
ret = i2c_write_block(client, 0x3a, &data, 1);
data = 0x00;
ret = i2c_write_block(client, 0x3b, &data, 1);
/* === trigger sr === */
data = 0xe0;
ret = i2c_write_block(client, 0x3c, &data, 1);
data = 0x02;
ret = i2c_write_block(client, 0x3d, &data, 1);
/* === clear register === */
data = 0x00;
ret = i2c_write_block(client, 0x3e, &data, 1);
ret = i2c_write_block(client, 0x3f, &data, 1);
udelay(550);
/* === run program === */
data = 0x28;
ret = i2c_write_block(client, OPRATION_REGISTER, &data, 1);
udelay(200);
data = 0x68;
ret = i2c_write_block(client, ENABLE_REGISTER, &data, 1);
udelay(550);
mutex_unlock(&led_mutex);
I(" %s ---\n" , __func__);
}
static void lp5521_led_off(struct i2c_client *client)
{
uint8_t data = 0x00;
int ret;
char data1[1] = {0};
struct lp5521_chip *cdata = i2c_get_clientdata(client);
struct led_i2c_platform_data *pdata = cdata->pdata;
I(" %s +++\n" , __func__);
if (!chip_enable) {
I(" %s return, chip already disable\n" , __func__);
return;
}
ret = i2c_read_block(client, 0x00, data1, 1);
if (!data1[0]) {
I(" %s return, chip already disable\n" , __func__);
return;
}
mutex_lock(&led_mutex);
/* === reset red green blue === */
data = 0x00;
ret = i2c_write_block(client, B_PWM_CONTROL, &data, 1);
ret = i2c_write_block(client, G_PWM_CONTROL, &data, 1);
ret = i2c_write_block(client, R_PWM_CONTROL, &data, 1);
ret = i2c_write_block(client, OPRATION_REGISTER, &data, 1);
ret = i2c_write_block(client, ENABLE_REGISTER, &data, 1);
mutex_unlock(&led_mutex);
if (pdata->ena_gpio) {
ret = gpio_direction_output(pdata->ena_gpio, 0);
if (ret < 0) {
pr_err("[LED] %s: gpio_direction_output high failed %d\n", __func__, ret);
gpio_free(pdata->ena_gpio);
}
}
chip_enable = 0;
I(" %s ---\n" , __func__);
}
static void led_work_func(struct work_struct *work)
{
struct i2c_client *client = private_lp5521_client;
struct lp5521_led *ldata;
I(" %s +++\n" , __func__);
ldata = container_of(work, struct lp5521_led, led_work);
lp5521_led_off(client);
I(" %s ---\n" , __func__);
}
static void multicolor_work_func(struct work_struct *work)
{
struct i2c_client *client = private_lp5521_client;
struct lp5521_led *ldata;
int ret;
uint8_t data = 0x00;
ldata = container_of(work, struct lp5521_led, led_work_multicolor);
I(" %s , ModeRGB = %x\n" , __func__, ldata->Mode);
if (ldata->Mode < 5)
lp5521_led_enable(client);
if (ldata->Mode == 0) {
lp5521_led_off(client);
} else if (ldata->Mode == 1) { /* === set red, green, blue direct control === */
mutex_lock(&led_mutex);
ret = i2c_write_block(client, R_PWM_CONTROL, &ldata->Red, 1);
ret = i2c_write_block(client, G_PWM_CONTROL, &ldata->Green, 1);
ret = i2c_write_block(client, B_PWM_CONTROL, &ldata->Blue, 1);
data = 0x3f;
ret = i2c_write_block(client, OPRATION_REGISTER, &data, 1);
udelay(200);
data = 0x40;
ret = i2c_write_block(client, ENABLE_REGISTER, &data, 1);
udelay(500);
mutex_unlock(&led_mutex);
} else if (ldata->Mode == 2) { /* === set short blink === */
lp5521_color_blink(client, ldata->Red, ldata->Green, ldata->Blue);
} else if (ldata->Mode == 3) { /* === set delayed short blink === */
msleep(1000);
lp5521_color_blink(client, ldata->Red, ldata->Green, ldata->Blue);
} else if (ldata->Mode == 4 && ldata->Red && !ldata->Green && !ldata->Blue) { /* === set red long blink === */
lp5521_red_long_blink(client);
} else if (ldata->Mode == 5 && ldata->Red && ldata->Green && !ldata->Blue) { /* === set red green blink === */
lp5521_dual_color_blink(client);
} else if (ldata->Mode == 6) { /* === set breathing === */
lp5521_breathing(client, ldata->Red, ldata->Green, ldata->Blue);
}
}
static enum alarmtimer_restart led_alarm_handler(struct alarm *alarm, ktime_t now)
{
struct lp5521_led *ldata;
I(" %s +++\n" , __func__);
ldata = container_of(alarm, struct lp5521_led, led_alarm);
queue_work(g_led_work_queue, &ldata->led_work);
I(" %s ---\n" , __func__);
return ALARMTIMER_NORESTART;
}
static void led_blink_do_work(struct work_struct *work)
{
struct i2c_client *client = private_lp5521_client;
struct lp5521_led *ldata;
I(" %s +++\n" , __func__);
ldata = container_of(work, struct lp5521_led, blink_delayed_work.work);
lp5521_color_blink(client, ldata->Red, ldata->Green, ldata->Blue);
I(" %s ---\n" , __func__);
}
static ssize_t lp5521_led_off_timer_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", current_time);;
}
static ssize_t lp5521_led_off_timer_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct led_classdev *led_cdev;
struct lp5521_led *ldata;
int min, sec;
uint16_t off_timer;
ktime_t interval;
ktime_t next_alarm;
min = -1;
sec = -1;
sscanf(buf, "%d %d", &min, &sec);
I(" %s , min = %d, sec = %d\n" , __func__, min, sec);
if (min < 0 || min > 255)
return -EINVAL;
if (sec < 0 || sec > 255)
return -EINVAL;
led_cdev = (struct led_classdev *)dev_get_drvdata(dev);
ldata = container_of(led_cdev, struct lp5521_led, cdev);
off_timer = min * 60 + sec;
alarm_cancel(&ldata->led_alarm);
cancel_work_sync(&ldata->led_work);
if (off_timer) {
interval = ktime_set(off_timer, 0);
next_alarm = ktime_add(ktime_get_real(), interval);
alarm_start(&ldata->led_alarm, next_alarm);
}
return count;
}
static DEVICE_ATTR(off_timer, 0644, lp5521_led_off_timer_show,
lp5521_led_off_timer_store);
static ssize_t lp5521_led_multi_color_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%x\n", ModeRGB);
}
static ssize_t lp5521_led_multi_color_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct led_classdev *led_cdev;
struct lp5521_led *ldata;
uint32_t val;
sscanf(buf, "%d", &val);
if (val < 0 || val > 0xFFFFFFFF)
return -EINVAL;
led_cdev = (struct led_classdev *)dev_get_drvdata(dev);
ldata = container_of(led_cdev, struct lp5521_led, cdev);
ldata->Mode = (val & Mode_Mask) >> 24;
ldata->Red = (val & Red_Mask) >> 16;
ldata->Green = (val & Green_Mask) >> 8;
ldata->Blue = val & Blue_Mask;
I(" %s , ModeRGB = %x\n" , __func__, val);
queue_work(g_led_work_queue, &ldata->led_work_multicolor);
return count;
}
static DEVICE_ATTR(ModeRGB, 0644, lp5521_led_multi_color_show,
lp5521_led_multi_color_store);
/* === read/write i2c and control enable pin for debug === */
static ssize_t lp5521_led_i2c_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
char data[1] = {0};
int i;
struct i2c_client *client = private_lp5521_client;
for (i = 0; i <= 0x6f; i++) {
ret = i2c_read_block(client, i, data, 1);
I(" %s i2c(%x) = 0x%x\n", __func__, i, data[0]);
}
return ret;
}
static ssize_t lp5521_led_i2c_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = private_lp5521_client;
int i, ret;
char *token[10];
unsigned long ul_reg, ul_data = 0;
uint8_t reg = 0, data;
char value[1] = {0};
struct lp5521_chip *cdata = i2c_get_clientdata(client);
struct led_i2c_platform_data *pdata = cdata->pdata;
for (i = 0; i < 2; i++) {
token[i] = strsep((char **)&buf, " ");
D("%s: token[%d] = %s\n", __func__, i, token[i]);
}
ret = strict_strtoul(token[0], 16, &ul_reg);
ret = strict_strtoul(token[1], 16, &ul_data);
reg = ul_reg;
data = ul_data;
if (reg < 0x6F) {
ret = i2c_write_block(client, reg, &data, 1);
ret = i2c_read_block(client, reg, value, 1);
I(" %s , ret = %d, Set REG=0x%x, data=0x%x\n" , __func__, ret, reg, data);
ret = i2c_read_block(client, reg, value, 1);
I(" %s , ret = %d, Get REG=0x%x, data=0x%x\n" , __func__, ret, reg, value[0]);
}
if (reg == 0x99) {
if (data == 1) {
I("%s , pull up enable pin\n", __func__);
if (pdata->ena_gpio) {
ret = gpio_direction_output(pdata->ena_gpio, 1);
if (ret < 0) {
pr_err("[LED] %s: gpio_direction_output high failed %d\n", __func__, ret);
gpio_free(pdata->ena_gpio);
}
}
} else if (data == 0) {
I("%s , pull down enable pin\n", __func__);
if (pdata->ena_gpio) {
ret = gpio_direction_output(pdata->ena_gpio, 1);
if (ret < 0) {
pr_err("[LED] %s: gpio_direction_output high failed %d\n", __func__, ret);
gpio_free(pdata->ena_gpio);
}
}
}
}
return count;
}
static DEVICE_ATTR(i2c, 0644, lp5521_led_i2c_show, lp5521_led_i2c_store);
static int lp5521_parse_dt(struct device *dev, struct led_i2c_platform_data *pdata)
{
struct property *prop;
struct device_node *dt = dev->of_node;
prop = of_find_property(dt, "lp5521,lp5521_en", NULL);
if (prop) {
pdata->ena_gpio = of_get_named_gpio(dt, "lp5521,lp5521_en", 0);
}
prop = of_find_property(dt, "lp5521,num_leds", NULL);
if (prop) {
of_property_read_u32(dt, "lp5521,num_leds", &pdata->num_leds);
}
return 0;
}
static int lp5521_led_probe(struct i2c_client *client
, const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct lp5521_chip *cdata;
struct led_i2c_platform_data *pdata;
int ret =0;
int i;
printk("[LED][PROBE] led driver probe +++\n");
/* === init platform and client data === */
cdata = devm_kzalloc(dev, sizeof(struct lp5521_chip), GFP_KERNEL);
if (!cdata) {
ret = -ENOMEM;
dev_err(&client->dev, "[LED][PROBE_ERR] failed on allocat cdata\n");
goto err_cdata;
}
i2c_set_clientdata(client, cdata);
cdata->client = client;
pdata = devm_kzalloc(dev, sizeof(*cdata->pdata), GFP_KERNEL);
if (!pdata) {
ret = -ENOMEM;
dev_err(&client->dev, "[LED][PROBE_ERR] failed on allocate pdata\n");
goto err_exit;
}
cdata->pdata = pdata;
if (client->dev.platform_data) {
memcpy(pdata, client->dev.platform_data, sizeof(*pdata));
} else {
ret = lp5521_parse_dt(&client->dev, pdata);
if (ret < 0) {
dev_err(&client->dev, "[LED][PROBE_ERR] failed on get pdata\n");
goto err_exit;
}
}
led_rw_delay = 5;
/* === led enable pin === */
if (pdata->ena_gpio) {
ret = gpio_request(pdata->ena_gpio, "led_enable");
if (ret < 0) {
pr_err("[LED] %s: gpio_request failed %d\n", __func__, ret);
return ret;
}
ret = gpio_direction_output(pdata->ena_gpio, 1);
if (ret < 0) {
pr_err("[LED] %s: gpio_direction_output failed %d\n", __func__, ret);
gpio_free(pdata->ena_gpio);
return ret;
}
}
/* === led trigger signal pin === */
if (pdata->tri_gpio) {
ret = gpio_request(pdata->tri_gpio, "led_trigger");
if (ret < 0) {
pr_err("[LED] %s: gpio_request failed %d\n", __func__, ret);
return ret;
}
ret = gpio_direction_output(pdata->tri_gpio, 0);
if (ret < 0) {
pr_err("[LED] %s: gpio_direction_output failed %d\n", __func__, ret);
gpio_free(pdata->tri_gpio);
return ret;
}
}
private_lp5521_client = client;
g_led_work_queue = create_workqueue("led");
if (!g_led_work_queue) {
ret = -10;
pr_err("[LED] %s: create workqueue fail %d\n", __func__, ret);
goto err_create_work_queue;
}
for (i = 0; i < pdata->num_leds; i++) {
cdata->leds[i].cdev.name = "indicator";
ret = led_classdev_register(dev, &cdata->leds[i].cdev);
if (ret < 0) {
dev_err(dev, "couldn't register led[%d]\n", i);
return ret;
}
ret = device_create_file(cdata->leds[i].cdev.dev, &dev_attr_ModeRGB);
if (ret < 0) {
pr_err("%s: failed on create attr ModeRGB [%d]\n", __func__, i);
goto err_register_attr_ModeRGB;
}
ret = device_create_file(cdata->leds[i].cdev.dev, &dev_attr_off_timer);
if (ret < 0) {
pr_err("%s: failed on create attr off_timer [%d]\n", __func__, i);
goto err_register_attr_off_timer;
}
ret = device_create_file(cdata->leds[i].cdev.dev, &dev_attr_i2c);
if (ret < 0) {
pr_err("%s: failed on create attr off_timer [%d]\n", __func__, i);
}
INIT_WORK(&cdata->leds[i].led_work, led_work_func);
INIT_WORK(&cdata->leds[i].led_work_multicolor, multicolor_work_func);
INIT_DELAYED_WORK(&cdata->leds[i].blink_delayed_work, led_blink_do_work);
alarm_init(&cdata->leds[i].led_alarm,
ALARM_REALTIME,
led_alarm_handler);
}
mutex_init(&cdata->led_i2c_rw_mutex);
mutex_init(&led_mutex);
printk("[LED][PROBE] led driver probe ---\n");
return 0;
err_register_attr_off_timer:
kfree(cdata);
for (i = 0; i < pdata->num_leds; i++) {
device_remove_file(cdata->leds[i].cdev.dev,&dev_attr_off_timer);
}
err_register_attr_ModeRGB:
for (i = 0; i < pdata->num_leds; i++) {
if (!strcmp(cdata->leds[i].cdev.name, "multi_color"))
device_remove_file(cdata->leds[i].cdev.dev,&dev_attr_ModeRGB);
}
err_create_work_queue:
err_exit:
err_cdata:
return ret;
}
static const struct i2c_device_id led_i2c_id[] = {
{ "LP5521-LED", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, led_i2c_id);
#ifdef CONFIG_OF
static const struct of_device_id lp5521_mttable[] = {
{ .compatible = "LP5521-LED"},
{ },
};
#endif
static struct i2c_driver led_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "LP5521-LED",
#ifdef CONFIG_OF
.of_match_table = lp5521_mttable,
#endif
},
.id_table = led_i2c_id,
.probe = lp5521_led_probe,
};
module_i2c_driver(led_i2c_driver);
#ifndef CONFIG_OF
static int __init lp5521_led_init(void)
{
int ret;
ret = i2c_add_driver(&led_i2c_driver);
if (ret)
return ret;
return 0;
}
static void __exit lp5521_led_exit(void)
{
i2c_del_driver(&led_i2c_driver);
}
module_init(lp5521_led_init);
module_exit(lp5521_led_exit);
#endif
MODULE_AUTHOR("<ShihHao_Shiung@htc.com>, <Dirk_Chang@htc.com>");
MODULE_DESCRIPTION("LP5521 LED driver");