android_kernel_cmhtcleo/drivers/i2c/chips/adp1650_flashlight.c

475 lines
10 KiB
C

/* drivers/i2c/chips/adp1650_flashlight.c
*
* Copyright (C) 2008-2009 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 <mach/msm_iomap.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/earlysuspend.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <mach/msm_flashlight.h>
#include <linux/adp1650_flashlight.h>
#define FLT_DBG_LOG(fmt, ...) \
printk(KERN_DEBUG "[FLT]" fmt, ##__VA_ARGS__)
#define FLT_INFO_LOG(fmt, ...) \
printk(KERN_INFO "[FLT]" fmt, ##__VA_ARGS__)
#define FLT_ERR_LOG(fmt, ...) \
printk(KERN_ERR "[FLT][ERR]" fmt, ##__VA_ARGS__)
#define DEBUG 1
#define ADP1650_RETRY_COUNT 10
static struct {
enum flashlight_mode_flags mode;
char *name;
enum led_brightness brightness;
} fl_mode_t[6] = {
{FL_MODE_OFF, "off", 0},
{FL_MODE_TORCH, "torch", LED_HALF},
{FL_MODE_FLASH, "flash", LED_FULL},
{FL_MODE_PRE_FLASH, "pre-flash", (LED_HALF+1)},
{FL_MODE_TORCH_LEVEL_1, "torch level 1", (LED_HALF-2)},
{FL_MODE_TORCH_LEVEL_2, "torch level 2", (LED_HALF-1)},
};
struct adp1650_data {
struct led_classdev fl_lcdev;
struct early_suspend fl_early_suspend;
enum flashlight_mode_flags mode_status;
uint32_t flash;
uint32_t timeout;
struct hrtimer timer;
spinlock_t spin_lock;
};
static struct i2c_client *this_client;
static struct adp1650_data *this_adp1650;
static ktime_t ktime;
static int ADP1650_I2C_RxData(char *rxData, int length)
{
uint8_t loop_i;
struct i2c_msg msgs[] = {
{
.addr = this_client->addr,
.flags = 0,
.len = 1,
.buf = rxData,
},
{
.addr = this_client->addr,
.flags = I2C_M_RD,
.len = length,
.buf = rxData,
},
};
for (loop_i = 0; loop_i < ADP1650_RETRY_COUNT; loop_i++) {
if (i2c_transfer(this_client->adapter, msgs, 2) > 0)
break;
mdelay(10);
}
if (loop_i >= ADP1650_RETRY_COUNT) {
FLT_ERR_LOG("%s retry over %d\n", __func__,
ADP1650_RETRY_COUNT);
return -EIO;
}
return 0;
}
static int ADP1650_I2C_TxData(char *txData, int length)
{
uint8_t loop_i;
struct i2c_msg msg[] = {
{
.addr = this_client->addr,
.flags = 0,
.len = length,
.buf = txData,
},
};
for (loop_i = 0; loop_i < ADP1650_RETRY_COUNT; loop_i++) {
if (i2c_transfer(this_client->adapter, msg, 1) > 0)
break;
mdelay(10);
}
if (loop_i >= ADP1650_RETRY_COUNT) {
FLT_ERR_LOG("%s retry over %d\n", __func__,
ADP1650_RETRY_COUNT);
return -EIO;
}
return 0;
}
int chip_info(uint8_t reg)
{
uint8_t buffer[2];
int ret;
buffer[0] = reg;
/* Read data */
ret = ADP1650_I2C_RxData(buffer, 1);
if (ret < 0)
return ret;
FLT_INFO_LOG("%s: %x\n", __func__, buffer[0]);
return ret;
}
static int assist_light(uint8_t i_tor)
{
uint8_t buffer[2];
int ret;
buffer[0] = CURRENT_SET_REG;
buffer[1] = i_tor;
ret = ADP1650_I2C_TxData(buffer, 2);
if (ret < 0)
return ret;
buffer[0] = OUTPUT_MODE_REG;
buffer[1] = OUTPUT_MODE_DEF | OUTPUT_MODE_ASSIST;
ret = ADP1650_I2C_TxData(buffer, 2);
if (ret < 0)
return ret;
return 0;
}
static int flash_light(uint8_t i_fl, uint8_t fl_tim)
{
uint8_t buffer[2];
int ret;
buffer[0] = CURRENT_SET_REG;
buffer[1] = i_fl;
ret = ADP1650_I2C_TxData(buffer, 2);
if (ret < 0)
return ret;
buffer[0] = TIMER_REG;
buffer[1] = fl_tim;
ret = ADP1650_I2C_TxData(buffer, 2);
if (ret < 0)
return ret;
buffer[0] = OUTPUT_MODE_REG;
buffer[1] = OUTPUT_MODE_DEF | OUTPUT_MODE_FLASH;
ret = ADP1650_I2C_TxData(buffer, 2);
if (ret < 0)
return ret;
return 0;
}
static int enable_low_batt_support(uint8_t value)
{
uint8_t buffer[2];
int ret;
buffer[0] = BATT_LOW_REG;
buffer[1] = value;
ret = ADP1650_I2C_TxData(buffer, 2);
if (ret < 0)
return ret;
return 0;
}
static int turn_off(void)
{
uint8_t buffer[2];
int ret;
if (this_adp1650->mode_status == FL_MODE_OFF)
return 0;
buffer[0] = OUTPUT_MODE_REG;
buffer[1] = OUTPUT_MODE_DEF;
ret = ADP1650_I2C_TxData(buffer, 2);
if (ret < 0)
return ret;
this_adp1650->mode_status = FL_MODE_OFF;
this_adp1650->fl_lcdev.brightness = 0;
return 0;
}
int adp1650_flashlight_control(int mode)
{
int ret = 0;
unsigned long flag = 0;
uint32_t prev_mode = this_adp1650->mode_status;
switch (mode) {
case FL_MODE_OFF:
turn_off();
break;
case FL_MODE_TORCH:
assist_light(CUR_TOR_75MA);
break;
case FL_MODE_FLASH:
if (prev_mode == FL_MODE_FLASH) {
spin_lock_irqsave(&this_adp1650->spin_lock, flag);
hrtimer_cancel(&this_adp1650->timer);
spin_unlock_irqrestore(&this_adp1650->spin_lock, flag);
}
flash_light(CUR_FL_700MA, TIMER_600MS);
spin_lock_irqsave(&this_adp1650->spin_lock, flag);
gpio_set_value(this_adp1650->flash, 1);
hrtimer_start(&this_adp1650->timer, ktime, HRTIMER_MODE_REL);
spin_unlock_irqrestore(&this_adp1650->spin_lock, flag);
break;
case FL_MODE_PRE_FLASH:
assist_light(CUR_TOR_125MA);
break;
case FL_MODE_TORCH_LEVEL_1:
assist_light(CUR_TOR_25MA);
break;
case FL_MODE_TORCH_LEVEL_2:
assist_light(CUR_TOR_50MA);
break;
default:
turn_off();
FLT_ERR_LOG("%s: unknown mode %d\n", __func__, mode);
return -EINVAL;
}
if (mode != FL_MODE_FLASH) {
spin_lock_irqsave(&this_adp1650->spin_lock, flag);
if (prev_mode == FL_MODE_FLASH) {
if (hrtimer_cancel(&this_adp1650->timer))
gpio_set_value(this_adp1650->flash, 0);
}
spin_unlock_irqrestore(&this_adp1650->spin_lock, flag);
}
this_adp1650->mode_status = mode;
FLT_INFO_LOG("%s: mode: %d\n", FLASHLIGHT_NAME, mode);
return ret;
}
static void fl_lcdev_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
enum flashlight_mode_flags mode = FL_MODE_OFF;
int i = 0, ret = -1;
char *mode_name = NULL;
if (brightness < 0 || brightness > LED_FULL) {
FLT_ERR_LOG("%s: invalid brightness %d\n",
__func__, brightness);
}
for (i = 0; i < ARRAY_SIZE(fl_mode_t); i++) {
if (brightness == fl_mode_t[i].brightness) {
mode = fl_mode_t[i].mode;
mode_name = fl_mode_t[i].name;
break;
}
}
if (!mode_name) {
FLT_ERR_LOG("%s: no matching brightness for %d\n",
__func__, brightness);
return;
}
ret = adp1650_flashlight_control(mode);
if (ret) {
FLT_ERR_LOG("%s: control failure rc:%d\n", __func__, ret);
return;
}
this_adp1650->fl_lcdev.brightness = brightness;
}
static void flashlight_early_suspend(struct early_suspend *handler)
{
struct adp1650_data *fl_str = container_of(handler,
struct adp1650_data, fl_early_suspend);
unsigned long flag = 0;
uint32_t prev_mode = fl_str->mode_status;
FLT_INFO_LOG("%s\n", __func__);
if (fl_str != NULL && fl_str->mode_status) {
turn_off();
spin_lock_irqsave(&fl_str->spin_lock, flag);
if (prev_mode == FL_MODE_FLASH) {
if (hrtimer_cancel(&fl_str->timer))
gpio_set_value(fl_str->flash, 0);
}
spin_unlock_irqrestore(&fl_str->spin_lock, flag);
}
}
static void flashlight_late_resume(struct early_suspend *handler)
{
}
static enum hrtimer_restart flash_gpio_timer_func(struct hrtimer *timer)
{
struct adp1650_data *adp1650 = container_of(timer,
struct adp1650_data, timer);
unsigned long flag = 0;
spin_lock_irqsave(&this_adp1650->spin_lock, flag);
if (gpio_get_value(adp1650->flash))
gpio_set_value(adp1650->flash, 0);
spin_unlock_irqrestore(&this_adp1650->spin_lock, flag);
return HRTIMER_NORESTART;
}
static int adp1650_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adp1650_data *adp1650;
struct flashlight_platform_data *pdata;
int err = 0;
FLT_INFO_LOG("%s:\n", __func__);
pdata = client->dev.platform_data;
if (!pdata) {
FLT_ERR_LOG("%s: Assign platform_data error!!\n", __func__);
return -EINVAL;
}
if (pdata->gpio_init)
pdata->gpio_init();
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
err = -ENODEV;
goto check_functionality_failed;
}
adp1650 = kzalloc(sizeof(struct adp1650_data), GFP_KERNEL);
if (!adp1650) {
FLT_ERR_LOG("%s: kzalloc fail !!!\n", __func__);
return -ENOMEM;
}
i2c_set_clientdata(client, adp1650);
this_client = client;
/* Register led class device */
adp1650->fl_lcdev.name = client->name;
adp1650->fl_lcdev.brightness = 0;
adp1650->fl_lcdev.brightness_set = fl_lcdev_brightness_set;
adp1650->flash = pdata->flash;
adp1650->timeout = pdata->flash_duration_ms;
if (adp1650->timeout <= 0)
adp1650->timeout = 600;
ktime = ktime_set(adp1650->timeout / 1000,
(adp1650->timeout % 1000) * 1000000);
hrtimer_init(&adp1650->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
adp1650->timer.function = flash_gpio_timer_func;
spin_lock_init(&adp1650->spin_lock);
err = led_classdev_register(&client->dev, &adp1650->fl_lcdev);
if (err < 0) {
FLT_ERR_LOG("%s: failed on led_classdev_register\n", __func__);
goto platform_data_null;
}
#ifdef CONFIG_HAS_EARLYSUSPEND
adp1650->fl_early_suspend.suspend = flashlight_early_suspend;
adp1650->fl_early_suspend.resume = flashlight_late_resume;
register_early_suspend(&adp1650->fl_early_suspend);
#endif
this_adp1650 = adp1650;
chip_info(0);
enable_low_batt_support(0x81);
return 0;
platform_data_null:
if (adp1650->flash) {
gpio_free(adp1650->flash);
adp1650->flash = 0;
}
kfree(adp1650);
check_functionality_failed:
return err;
}
static int adp1650_remove(struct i2c_client *client)
{
struct adp1650_data *adp1650 = i2c_get_clientdata(client);
hrtimer_cancel(&adp1650->timer);
if (adp1650->flash) {
gpio_free(adp1650->flash);
adp1650->flash = 0;
}
led_classdev_unregister(&adp1650->fl_lcdev);
//i2c_detach_client(client);
kfree(adp1650);
FLT_INFO_LOG("%s:\n", __func__);
return 0;
}
static const struct i2c_device_id adp1650_id[] = {
{ FLASHLIGHT_NAME, 0 },
{ }
};
static struct i2c_driver adp1650_driver = {
.probe = adp1650_probe,
.remove = adp1650_remove,
.id_table = adp1650_id,
.driver = {
.name = FLASHLIGHT_NAME,
},
};
static int __init adp1650_init(void)
{
FLT_INFO_LOG("adp1650 Led Flash driver: init\n");
return i2c_add_driver(&adp1650_driver);
}
static void __exit adp1650_exit(void)
{
i2c_del_driver(&adp1650_driver);
}
module_init(adp1650_init);
module_exit(adp1650_exit);
MODULE_DESCRIPTION("ADP1650 Led Flash driver");
MODULE_LICENSE("GPL");