2013-11-12 15:08:58 -08:00
/*
* Simple driver for Texas Instruments LM3630A Backlight driver chip
* Copyright ( C ) 2012 Texas Instruments
*
* 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 .
*
*/
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/i2c.h>
# include <linux/backlight.h>
# include <linux/err.h>
# include <linux/delay.h>
# include <linux/uaccess.h>
# include <linux/interrupt.h>
# include <linux/regmap.h>
# include <linux/pwm.h>
# include <linux/platform_data/lm3630a_bl.h>
# define REG_CTRL 0x00
# define REG_BOOST 0x02
# define REG_CONFIG 0x01
# define REG_BRT_A 0x03
# define REG_BRT_B 0x04
# define REG_I_A 0x05
# define REG_I_B 0x06
# define REG_INT_STATUS 0x09
# define REG_INT_EN 0x0A
# define REG_FAULT 0x0B
# define REG_PWM_OUTLOW 0x12
# define REG_PWM_OUTHIGH 0x13
# define REG_MAX 0x1F
# define INT_DEBOUNCE_MSEC 10
struct lm3630a_chip {
struct device * dev ;
struct delayed_work work ;
int irq ;
struct workqueue_struct * irqthread ;
struct lm3630a_platform_data * pdata ;
struct backlight_device * bleda ;
struct backlight_device * bledb ;
struct regmap * regmap ;
struct pwm_device * pwmd ;
} ;
/* i2c access */
static int lm3630a_read ( struct lm3630a_chip * pchip , unsigned int reg )
{
int rval ;
unsigned int reg_val ;
rval = regmap_read ( pchip - > regmap , reg , & reg_val ) ;
if ( rval < 0 )
return rval ;
return reg_val & 0xFF ;
}
static int lm3630a_write ( struct lm3630a_chip * pchip ,
unsigned int reg , unsigned int data )
{
return regmap_write ( pchip - > regmap , reg , data ) ;
}
static int lm3630a_update ( struct lm3630a_chip * pchip ,
unsigned int reg , unsigned int mask ,
unsigned int data )
{
return regmap_update_bits ( pchip - > regmap , reg , mask , data ) ;
}
/* initialize chip */
static int lm3630a_chip_init ( struct lm3630a_chip * pchip )
{
int rval ;
struct lm3630a_platform_data * pdata = pchip - > pdata ;
usleep_range ( 1000 , 2000 ) ;
/* set Filter Strength Register */
rval = lm3630a_write ( pchip , 0x50 , 0x03 ) ;
/* set Cofig. register */
rval | = lm3630a_update ( pchip , REG_CONFIG , 0x07 , pdata - > pwm_ctrl ) ;
/* set boost control */
rval | = lm3630a_write ( pchip , REG_BOOST , 0x38 ) ;
/* set current A */
rval | = lm3630a_update ( pchip , REG_I_A , 0x1F , 0x1F ) ;
/* set current B */
rval | = lm3630a_write ( pchip , REG_I_B , 0x1F ) ;
/* set control */
2013-11-12 15:09:01 -08:00
rval | = lm3630a_update ( pchip , REG_CTRL , 0x14 , pdata - > leda_ctrl ) ;
rval | = lm3630a_update ( pchip , REG_CTRL , 0x0B , pdata - > ledb_ctrl ) ;
2013-11-12 15:08:58 -08:00
usleep_range ( 1000 , 2000 ) ;
/* set brightness A and B */
rval | = lm3630a_write ( pchip , REG_BRT_A , pdata - > leda_init_brt ) ;
rval | = lm3630a_write ( pchip , REG_BRT_B , pdata - > ledb_init_brt ) ;
if ( rval < 0 )
dev_err ( pchip - > dev , " i2c failed to access register \n " ) ;
return rval ;
}
/* interrupt handling */
static void lm3630a_delayed_func ( struct work_struct * work )
{
2013-11-12 15:08:59 -08:00
int rval ;
2013-11-12 15:08:58 -08:00
struct lm3630a_chip * pchip ;
pchip = container_of ( work , struct lm3630a_chip , work . work ) ;
rval = lm3630a_read ( pchip , REG_INT_STATUS ) ;
if ( rval < 0 ) {
dev_err ( pchip - > dev ,
" i2c failed to access REG_INT_STATUS Register \n " ) ;
return ;
}
dev_info ( pchip - > dev , " REG_INT_STATUS Register is 0x%x \n " , rval ) ;
}
static irqreturn_t lm3630a_isr_func ( int irq , void * chip )
{
int rval ;
struct lm3630a_chip * pchip = chip ;
unsigned long delay = msecs_to_jiffies ( INT_DEBOUNCE_MSEC ) ;
queue_delayed_work ( pchip - > irqthread , & pchip - > work , delay ) ;
rval = lm3630a_update ( pchip , REG_CTRL , 0x80 , 0x00 ) ;
if ( rval < 0 ) {
dev_err ( pchip - > dev , " i2c failed to access register \n " ) ;
return IRQ_NONE ;
}
return IRQ_HANDLED ;
}
static int lm3630a_intr_config ( struct lm3630a_chip * pchip )
{
int rval ;
rval = lm3630a_write ( pchip , REG_INT_EN , 0x87 ) ;
if ( rval < 0 )
return rval ;
INIT_DELAYED_WORK ( & pchip - > work , lm3630a_delayed_func ) ;
pchip - > irqthread = create_singlethread_workqueue ( " lm3630a-irqthd " ) ;
if ( ! pchip - > irqthread ) {
dev_err ( pchip - > dev , " create irq thread fail \n " ) ;
return - ENOMEM ;
}
if ( request_threaded_irq
( pchip - > irq , NULL , lm3630a_isr_func ,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT , " lm3630a_irq " , pchip ) ) {
dev_err ( pchip - > dev , " request threaded irq fail \n " ) ;
2013-11-12 15:09:37 -08:00
destroy_workqueue ( pchip - > irqthread ) ;
2013-11-12 15:08:58 -08:00
return - ENOMEM ;
}
return rval ;
}
static void lm3630a_pwm_ctrl ( struct lm3630a_chip * pchip , int br , int br_max )
{
unsigned int period = pwm_get_period ( pchip - > pwmd ) ;
unsigned int duty = br * period / br_max ;
pwm_config ( pchip - > pwmd , duty , period ) ;
if ( duty )
pwm_enable ( pchip - > pwmd ) ;
else
pwm_disable ( pchip - > pwmd ) ;
}
/* update and get brightness */
static int lm3630a_bank_a_update_status ( struct backlight_device * bl )
{
int ret ;
struct lm3630a_chip * pchip = bl_get_data ( bl ) ;
enum lm3630a_pwm_ctrl pwm_ctrl = pchip - > pdata - > pwm_ctrl ;
/* pwm control */
if ( ( pwm_ctrl & LM3630A_PWM_BANK_A ) ! = 0 ) {
lm3630a_pwm_ctrl ( pchip , bl - > props . brightness ,
bl - > props . max_brightness ) ;
return bl - > props . brightness ;
}
/* disable sleep */
ret = lm3630a_update ( pchip , REG_CTRL , 0x80 , 0x00 ) ;
if ( ret < 0 )
goto out_i2c_err ;
usleep_range ( 1000 , 2000 ) ;
/* minimum brightness is 0x04 */
ret = lm3630a_write ( pchip , REG_BRT_A , bl - > props . brightness ) ;
if ( bl - > props . brightness < 0x4 )
ret | = lm3630a_update ( pchip , REG_CTRL , LM3630A_LEDA_ENABLE , 0 ) ;
else
ret | = lm3630a_update ( pchip , REG_CTRL ,
LM3630A_LEDA_ENABLE , LM3630A_LEDA_ENABLE ) ;
if ( ret < 0 )
goto out_i2c_err ;
return bl - > props . brightness ;
out_i2c_err :
dev_err ( pchip - > dev , " i2c failed to access \n " ) ;
return bl - > props . brightness ;
}
static int lm3630a_bank_a_get_brightness ( struct backlight_device * bl )
{
int brightness , rval ;
struct lm3630a_chip * pchip = bl_get_data ( bl ) ;
enum lm3630a_pwm_ctrl pwm_ctrl = pchip - > pdata - > pwm_ctrl ;
if ( ( pwm_ctrl & LM3630A_PWM_BANK_A ) ! = 0 ) {
rval = lm3630a_read ( pchip , REG_PWM_OUTHIGH ) ;
if ( rval < 0 )
goto out_i2c_err ;
brightness = ( rval & 0x01 ) < < 8 ;
rval = lm3630a_read ( pchip , REG_PWM_OUTLOW ) ;
if ( rval < 0 )
goto out_i2c_err ;
brightness | = rval ;
goto out ;
}
/* disable sleep */
rval = lm3630a_update ( pchip , REG_CTRL , 0x80 , 0x00 ) ;
if ( rval < 0 )
goto out_i2c_err ;
usleep_range ( 1000 , 2000 ) ;
rval = lm3630a_read ( pchip , REG_BRT_A ) ;
if ( rval < 0 )
goto out_i2c_err ;
brightness = rval ;
out :
bl - > props . brightness = brightness ;
return bl - > props . brightness ;
out_i2c_err :
dev_err ( pchip - > dev , " i2c failed to access register \n " ) ;
return 0 ;
}
static const struct backlight_ops lm3630a_bank_a_ops = {
. options = BL_CORE_SUSPENDRESUME ,
. update_status = lm3630a_bank_a_update_status ,
. get_brightness = lm3630a_bank_a_get_brightness ,
} ;
/* update and get brightness */
static int lm3630a_bank_b_update_status ( struct backlight_device * bl )
{
int ret ;
struct lm3630a_chip * pchip = bl_get_data ( bl ) ;
enum lm3630a_pwm_ctrl pwm_ctrl = pchip - > pdata - > pwm_ctrl ;
/* pwm control */
if ( ( pwm_ctrl & LM3630A_PWM_BANK_B ) ! = 0 ) {
lm3630a_pwm_ctrl ( pchip , bl - > props . brightness ,
bl - > props . max_brightness ) ;
return bl - > props . brightness ;
}
/* disable sleep */
ret = lm3630a_update ( pchip , REG_CTRL , 0x80 , 0x00 ) ;
if ( ret < 0 )
goto out_i2c_err ;
usleep_range ( 1000 , 2000 ) ;
/* minimum brightness is 0x04 */
ret = lm3630a_write ( pchip , REG_BRT_B , bl - > props . brightness ) ;
if ( bl - > props . brightness < 0x4 )
ret | = lm3630a_update ( pchip , REG_CTRL , LM3630A_LEDB_ENABLE , 0 ) ;
else
ret | = lm3630a_update ( pchip , REG_CTRL ,
LM3630A_LEDB_ENABLE , LM3630A_LEDB_ENABLE ) ;
if ( ret < 0 )
goto out_i2c_err ;
return bl - > props . brightness ;
out_i2c_err :
dev_err ( pchip - > dev , " i2c failed to access REG_CTRL \n " ) ;
return bl - > props . brightness ;
}
static int lm3630a_bank_b_get_brightness ( struct backlight_device * bl )
{
int brightness , rval ;
struct lm3630a_chip * pchip = bl_get_data ( bl ) ;
enum lm3630a_pwm_ctrl pwm_ctrl = pchip - > pdata - > pwm_ctrl ;
if ( ( pwm_ctrl & LM3630A_PWM_BANK_B ) ! = 0 ) {
rval = lm3630a_read ( pchip , REG_PWM_OUTHIGH ) ;
if ( rval < 0 )
goto out_i2c_err ;
brightness = ( rval & 0x01 ) < < 8 ;
rval = lm3630a_read ( pchip , REG_PWM_OUTLOW ) ;
if ( rval < 0 )
goto out_i2c_err ;
brightness | = rval ;
goto out ;
}
/* disable sleep */
rval = lm3630a_update ( pchip , REG_CTRL , 0x80 , 0x00 ) ;
if ( rval < 0 )
goto out_i2c_err ;
usleep_range ( 1000 , 2000 ) ;
rval = lm3630a_read ( pchip , REG_BRT_B ) ;
if ( rval < 0 )
goto out_i2c_err ;
brightness = rval ;
out :
bl - > props . brightness = brightness ;
return bl - > props . brightness ;
out_i2c_err :
dev_err ( pchip - > dev , " i2c failed to access register \n " ) ;
return 0 ;
}
static const struct backlight_ops lm3630a_bank_b_ops = {
. options = BL_CORE_SUSPENDRESUME ,
. update_status = lm3630a_bank_b_update_status ,
. get_brightness = lm3630a_bank_b_get_brightness ,
} ;
static int lm3630a_backlight_register ( struct lm3630a_chip * pchip )
{
struct backlight_properties props ;
struct lm3630a_platform_data * pdata = pchip - > pdata ;
props . type = BACKLIGHT_RAW ;
if ( pdata - > leda_ctrl ! = LM3630A_LEDA_DISABLE ) {
props . brightness = pdata - > leda_init_brt ;
props . max_brightness = pdata - > leda_max_brt ;
pchip - > bleda =
devm_backlight_device_register ( pchip - > dev , " lm3630a_leda " ,
pchip - > dev , pchip ,
& lm3630a_bank_a_ops , & props ) ;
if ( IS_ERR ( pchip - > bleda ) )
return PTR_ERR ( pchip - > bleda ) ;
}
if ( ( pdata - > ledb_ctrl ! = LM3630A_LEDB_DISABLE ) & &
( pdata - > ledb_ctrl ! = LM3630A_LEDB_ON_A ) ) {
props . brightness = pdata - > ledb_init_brt ;
props . max_brightness = pdata - > ledb_max_brt ;
pchip - > bledb =
devm_backlight_device_register ( pchip - > dev , " lm3630a_ledb " ,
pchip - > dev , pchip ,
& lm3630a_bank_b_ops , & props ) ;
if ( IS_ERR ( pchip - > bledb ) )
return PTR_ERR ( pchip - > bledb ) ;
}
return 0 ;
}
static const struct regmap_config lm3630a_regmap = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = REG_MAX ,
} ;
static int lm3630a_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
2013-11-12 15:09:04 -08:00
struct lm3630a_platform_data * pdata = dev_get_platdata ( & client - > dev ) ;
2013-11-12 15:08:58 -08:00
struct lm3630a_chip * pchip ;
int rval ;
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) ) {
dev_err ( & client - > dev , " fail : i2c functionality check \n " ) ;
return - EOPNOTSUPP ;
}
pchip = devm_kzalloc ( & client - > dev , sizeof ( struct lm3630a_chip ) ,
GFP_KERNEL ) ;
if ( ! pchip )
return - ENOMEM ;
pchip - > dev = & client - > dev ;
pchip - > regmap = devm_regmap_init_i2c ( client , & lm3630a_regmap ) ;
if ( IS_ERR ( pchip - > regmap ) ) {
rval = PTR_ERR ( pchip - > regmap ) ;
dev_err ( & client - > dev , " fail : allocate reg. map: %d \n " , rval ) ;
return rval ;
}
i2c_set_clientdata ( client , pchip ) ;
if ( pdata = = NULL ) {
2013-11-12 15:09:00 -08:00
pdata = devm_kzalloc ( pchip - > dev ,
sizeof ( struct lm3630a_platform_data ) ,
GFP_KERNEL ) ;
if ( pdata = = NULL )
2013-11-12 15:08:58 -08:00
return - ENOMEM ;
/* default values */
2013-11-12 15:09:00 -08:00
pdata - > leda_ctrl = LM3630A_LEDA_ENABLE ;
pdata - > ledb_ctrl = LM3630A_LEDB_ENABLE ;
pdata - > leda_max_brt = LM3630A_MAX_BRIGHTNESS ;
pdata - > ledb_max_brt = LM3630A_MAX_BRIGHTNESS ;
pdata - > leda_init_brt = LM3630A_MAX_BRIGHTNESS ;
pdata - > ledb_init_brt = LM3630A_MAX_BRIGHTNESS ;
2013-11-12 15:08:58 -08:00
}
2013-11-12 15:09:00 -08:00
pchip - > pdata = pdata ;
2013-11-12 15:08:58 -08:00
/* chip initialize */
rval = lm3630a_chip_init ( pchip ) ;
if ( rval < 0 ) {
dev_err ( & client - > dev , " fail : init chip \n " ) ;
return rval ;
}
/* backlight register */
rval = lm3630a_backlight_register ( pchip ) ;
if ( rval < 0 ) {
dev_err ( & client - > dev , " fail : backlight register. \n " ) ;
return rval ;
}
/* pwm */
if ( pdata - > pwm_ctrl ! = LM3630A_PWM_DISABLE ) {
pchip - > pwmd = devm_pwm_get ( pchip - > dev , " lm3630a-pwm " ) ;
if ( IS_ERR ( pchip - > pwmd ) ) {
dev_err ( & client - > dev , " fail : get pwm device \n " ) ;
return PTR_ERR ( pchip - > pwmd ) ;
}
}
pchip - > pwmd - > period = pdata - > pwm_period ;
/* interrupt enable : irq 0 is not allowed */
pchip - > irq = client - > irq ;
if ( pchip - > irq ) {
rval = lm3630a_intr_config ( pchip ) ;
if ( rval < 0 )
return rval ;
}
dev_info ( & client - > dev , " LM3630A backlight register OK. \n " ) ;
return 0 ;
}
static int lm3630a_remove ( struct i2c_client * client )
{
int rval ;
struct lm3630a_chip * pchip = i2c_get_clientdata ( client ) ;
rval = lm3630a_write ( pchip , REG_BRT_A , 0 ) ;
if ( rval < 0 )
dev_err ( pchip - > dev , " i2c failed to access register \n " ) ;
rval = lm3630a_write ( pchip , REG_BRT_B , 0 ) ;
if ( rval < 0 )
dev_err ( pchip - > dev , " i2c failed to access register \n " ) ;
if ( pchip - > irq ) {
free_irq ( pchip - > irq , pchip ) ;
flush_workqueue ( pchip - > irqthread ) ;
destroy_workqueue ( pchip - > irqthread ) ;
}
return 0 ;
}
static const struct i2c_device_id lm3630a_id [ ] = {
{ LM3630A_NAME , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , lm3630a_id ) ;
static struct i2c_driver lm3630a_i2c_driver = {
. driver = {
. name = LM3630A_NAME ,
} ,
. probe = lm3630a_probe ,
. remove = lm3630a_remove ,
. id_table = lm3630a_id ,
} ;
module_i2c_driver ( lm3630a_i2c_driver ) ;
MODULE_DESCRIPTION ( " Texas Instruments Backlight driver for LM3630A " ) ;
MODULE_AUTHOR ( " Daniel Jeong <gshark.jeong@gmail.com> " ) ;
MODULE_AUTHOR ( " LDD MLP <ldd-mlp@list.ti.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;