android_kernel_cmhtcleo/drivers/i2c/chips/adp1650_flashlight.c
2010-08-27 11:19:57 +02:00

375 lines
7.6 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 <mach/msm_flashlight.h>
#include <linux/adp1650_flashlight.h>
#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;
};
static struct i2c_client *this_client;
static struct adp1650_data *this_adp1650;
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) {
printk(KERN_ERR "%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) {
printk(KERN_ERR "%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;
printk(KERN_INFO "%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 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;
switch (mode) {
case FL_MODE_OFF:
turn_off();
break;
case FL_MODE_TORCH:
assist_light(CUR_TOR_125MA);
break;
case FL_MODE_FLASH:
flash_light(CUR_FL_700MA, TIMER_200MS);
break;
case FL_MODE_PRE_FLASH:
assist_light(CUR_TOR_200MA);
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();
printk(KERN_ERR "%s: unknown mode %d\n", __func__, mode);
return -EINVAL;
}
this_adp1650->mode_status = 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) {
printk(KERN_ERR "%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) {
printk(KERN_ERR "%s: no matching brightness for %d\n",
__func__, brightness);
return;
}
printk(KERN_INFO "%s: %s\n", __func__, mode_name);
ret = adp1650_flashlight_control(mode);
if (ret) {
printk(KERN_ERR "%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);
printk(KERN_INFO "%s\n", __func__);
if (fl_str != NULL && fl_str->mode_status)
turn_off();
}
static void flashlight_late_resume(struct early_suspend *handler)
{
}
static int adp1650_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adp1650_data *adp1650;
int err = 0;
printk(KERN_INFO "%s:\n", __func__);
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) {
printk(KERN_ERR "%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;
err = led_classdev_register(&client->dev, &adp1650->fl_lcdev);
if (err < 0) {
printk(KERN_ERR "failed on led_classdev_register\n");
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);
return 0;
platform_data_null:
kfree(adp1650);
check_functionality_failed:
return err;
}
static int adp1650_remove(struct i2c_client *client)
{
struct adp1650_data *adp1650 = i2c_get_clientdata(client);
led_classdev_unregister(&adp1650->fl_lcdev);
//i2c_detach_client(client);
kfree(adp1650);
printk(KERN_INFO "%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)
{
printk(KERN_INFO "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");