/*
* lp5523.c - LP5523 LED Driver
*
* Copyright (C) 2010 Nokia Corporation
*
* Contact: Samu Onkalo <samu.p.onkalo@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/leds.h>
#include <linux/leds-lp5523.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#define LP5523_REG_ENABLE 0x00
#define LP5523_REG_OP_MODE 0x01
#define LP5523_REG_RATIOMETRIC_MSB 0x02
#define LP5523_REG_RATIOMETRIC_LSB 0x03
#define LP5523_REG_ENABLE_LEDS_MSB 0x04
#define LP5523_REG_ENABLE_LEDS_LSB 0x05
#define LP5523_REG_LED_CNTRL_BASE 0x06
#define LP5523_REG_LED_PWM_BASE 0x16
#define LP5523_REG_LED_CURRENT_BASE 0x26
#define LP5523_REG_CONFIG 0x36
#define LP5523_REG_CHANNEL1_PC 0x37
#define LP5523_REG_CHANNEL2_PC 0x38
#define LP5523_REG_CHANNEL3_PC 0x39
#define LP5523_REG_STATUS 0x3a
#define LP5523_REG_GPO 0x3b
#define LP5523_REG_VARIABLE 0x3c
#define LP5523_REG_RESET 0x3d
#define LP5523_REG_TEMP_CTRL 0x3e
#define LP5523_REG_TEMP_READ 0x3f
#define LP5523_REG_TEMP_WRITE 0x40
#define LP5523_REG_LED_TEST_CTRL 0x41
#define LP5523_REG_LED_TEST_ADC 0x42
#define LP5523_REG_ENG1_VARIABLE 0x45
#define LP5523_REG_ENG2_VARIABLE 0x46
#define LP5523_REG_ENG3_VARIABLE 0x47
#define LP5523_REG_MASTER_FADER1 0x48
#define LP5523_REG_MASTER_FADER2 0x49
#define LP5523_REG_MASTER_FADER3 0x4a
#define LP5523_REG_CH1_PROG_START 0x4c
#define LP5523_REG_CH2_PROG_START 0x4d
#define LP5523_REG_CH3_PROG_START 0x4e
#define LP5523_REG_PROG_PAGE_SEL 0x4f
#define LP5523_REG_PROG_MEM 0x50
#define LP5523_CMD_LOAD 0x15 /* 00010101 */
#define LP5523_CMD_RUN 0x2a /* 00101010 */
#define LP5523_CMD_DISABLED 0x00 /* 00000000 */
#define LP5523_ENABLE 0x40
#define LP5523_AUTO_INC 0x40
#define LP5523_PWR_SAVE 0x20
#define LP5523_PWM_PWR_SAVE 0x04
#define LP5523_CP_1 0x08
#define LP5523_CP_1_5 0x10
#define LP5523_CP_AUTO 0x18
#define LP5523_INT_CLK 0x01
#define LP5523_AUTO_CLK 0x02
#define LP5523_EN_LEDTEST 0x80
#define LP5523_LEDTEST_DONE 0x80
#define LP5523_DEFAULT_CURRENT 50 /* microAmps */
#define LP5523_PROGRAM_LENGTH 32 /* in bytes */
#define LP5523_PROGRAM_PAGES 6
#define LP5523_ADC_SHORTCIRC_LIM 80
#define LP5523_LEDS 9
#define LP5523_ENGINES 3
#define LP5523_ENG_MASK_BASE 0x30 /* 00110000 */
#define LP5523_ENG_STATUS_MASK 0x07 /* 00000111 */
#define LP5523_IRQ_FLAGS IRQF_TRIGGER_FALLING
#define LP5523_EXT_CLK_USED 0x08
#define LED_ACTIVE(mux, led) (!!(mux & (0x0001 << led)))
#define SHIFT_MASK(id) (((id) - 1) * 2)
enum lp5523_chip_id {
LP5523,
LP55231,
};
struct lp5523_engine {
int id;
u8 mode;
u8 prog_page;
u8 mux_page;
u16 led_mux;
u8 engine_mask;
};
struct lp5523_led {
int id;
u8 chan_nr;
u8 led_current;
u8 max_current;
struct led_classdev cdev;
struct work_struct brightness_work;
u8 brightness;
};
struct lp5523_chip {
struct mutex lock; /* Serialize control */
struct i2c_client *client;
struct lp5523_engine engines[LP5523_ENGINES];
struct lp5523_led leds[LP5523_LEDS];
struct lp5523_platform_data *pdata;
u8 num_channels;
u8 num_leds;
};
static inline struct lp5523_led *cdev_to_led(struct led_classdev *cdev)
{
return container_of(cdev, struct lp5523_led, cdev);
}
static inline struct lp5523_chip *engine_to_lp5523(struct lp5523_engine *engine)
{
return container_of(engine, struct lp5523_chip,
engines[engine->id - 1]);
}
static inline struct lp5523_chip *led_to_lp5523(struct lp5523_led *led)
{
return container_of(led, struct lp5523_chip,
leds[led->id]);
}
static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode);
static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode);
static int lp5523_load_program(struct lp5523_engine *engine, const u8 *pattern);
static void lp5523_led_brightness_work(struct work_struct *work);
static int lp5523_write(struct i2c_client *client, u8 reg, u8 value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
static int lp5523_read(struct i2c_client *client, u8 reg, u8 *buf)
{
s32 ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0)
return ret;
*buf = ret;
return 0;
}
static int lp5523_detect(struct i2c_client *client)
{
int ret;
u8 buf;
ret = lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE);
if (ret)
return ret;
ret = lp5523_read(client,