drivers: improved the flashlight's ICS compatibility.
This commit is contained in:
		| @@ -18,10 +18,18 @@ | ||||
| #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 | ||||
| @@ -43,10 +51,15 @@ 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) | ||||
| { | ||||
| @@ -74,7 +87,7 @@ static int ADP1650_I2C_RxData(char *rxData, int length) | ||||
| 	} | ||||
|  | ||||
| 	if (loop_i >= ADP1650_RETRY_COUNT) { | ||||
| 		printk(KERN_ERR "%s retry over %d\n", __func__, | ||||
| 		FLT_ERR_LOG("%s retry over %d\n", __func__, | ||||
| 							ADP1650_RETRY_COUNT); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| @@ -102,7 +115,7 @@ static int ADP1650_I2C_TxData(char *txData, int length) | ||||
| 	} | ||||
|  | ||||
| 	if (loop_i >= ADP1650_RETRY_COUNT) { | ||||
| 		printk(KERN_ERR "%s retry over %d\n", __func__, | ||||
| 		FLT_ERR_LOG("%s retry over %d\n", __func__, | ||||
| 							ADP1650_RETRY_COUNT); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| @@ -121,7 +134,7 @@ int chip_info(uint8_t reg) | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
|  | ||||
| 	printk(KERN_INFO "%s: %x\n", __func__, buffer[0]); | ||||
| 	FLT_INFO_LOG("%s: %x\n", __func__, buffer[0]); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| @@ -171,6 +184,20 @@ static int flash_light(uint8_t i_fl, uint8_t fl_tim) | ||||
| 	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]; | ||||
| @@ -193,6 +220,8 @@ static int turn_off(void) | ||||
| 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: | ||||
| @@ -200,15 +229,25 @@ int adp1650_flashlight_control(int mode) | ||||
| 		break; | ||||
|  | ||||
| 	case FL_MODE_TORCH: | ||||
| 		assist_light(CUR_TOR_125MA); | ||||
| 		assist_light(CUR_TOR_75MA); | ||||
| 		break; | ||||
|  | ||||
| 	case FL_MODE_FLASH: | ||||
| 		flash_light(CUR_FL_700MA, TIMER_200MS); | ||||
| 		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_200MA); | ||||
| 		assist_light(CUR_TOR_125MA); | ||||
| 		break; | ||||
|  | ||||
| 	case FL_MODE_TORCH_LEVEL_1: | ||||
| @@ -221,11 +260,22 @@ int adp1650_flashlight_control(int mode) | ||||
|  | ||||
| 	default: | ||||
| 		turn_off(); | ||||
| 		printk(KERN_ERR "%s: unknown mode %d\n", __func__, mode); | ||||
| 		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; | ||||
| } | ||||
|  | ||||
| @@ -237,7 +287,7 @@ static void fl_lcdev_brightness_set(struct led_classdev *led_cdev, | ||||
| 	char *mode_name = NULL; | ||||
|  | ||||
| 	if (brightness < 0 || brightness > LED_FULL) { | ||||
| 		printk(KERN_ERR "%s: invalid brightness %d\n", | ||||
| 		FLT_ERR_LOG("%s: invalid brightness %d\n", | ||||
| 						__func__, brightness); | ||||
| 	} | ||||
|  | ||||
| @@ -250,15 +300,14 @@ static void fl_lcdev_brightness_set(struct led_classdev *led_cdev, | ||||
| 	} | ||||
|  | ||||
| 	if (!mode_name) { | ||||
| 		printk(KERN_ERR "%s: no matching brightness for %d\n", | ||||
| 		FLT_ERR_LOG("%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); | ||||
| 		FLT_ERR_LOG("%s: control failure rc:%d\n", __func__, ret); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| @@ -269,10 +318,19 @@ 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__); | ||||
|  | ||||
| 	printk(KERN_INFO "%s\n", __func__); | ||||
| 	if (fl_str != NULL && fl_str->mode_status) | ||||
| 	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) | ||||
| @@ -280,13 +338,35 @@ 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; | ||||
|  | ||||
| 	printk(KERN_INFO "%s:\n", __func__); | ||||
| 	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; | ||||
| @@ -295,7 +375,7 @@ static int adp1650_probe(struct i2c_client *client, | ||||
|  | ||||
| 	adp1650 = kzalloc(sizeof(struct adp1650_data), GFP_KERNEL); | ||||
| 	if (!adp1650) { | ||||
| 		printk(KERN_ERR "%s: kzalloc fail !!!\n", __func__); | ||||
| 		FLT_ERR_LOG("%s: kzalloc fail !!!\n", __func__); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
|  | ||||
| @@ -306,9 +386,19 @@ static int adp1650_probe(struct i2c_client *client, | ||||
| 	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) { | ||||
| 		printk(KERN_ERR "failed on led_classdev_register\n"); | ||||
| 		FLT_ERR_LOG("%s: failed on led_classdev_register\n", __func__); | ||||
| 		goto platform_data_null; | ||||
| 	} | ||||
|  | ||||
| @@ -321,10 +411,15 @@ static int adp1650_probe(struct i2c_client *client, | ||||
| 	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; | ||||
| @@ -334,11 +429,16 @@ 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); | ||||
|  | ||||
| 	printk(KERN_INFO "%s:\n", __func__); | ||||
| 	FLT_INFO_LOG("%s:\n", __func__); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @@ -358,7 +458,7 @@ static struct i2c_driver adp1650_driver = { | ||||
|  | ||||
| static int __init adp1650_init(void) | ||||
| { | ||||
| 	printk(KERN_INFO "adp1650 Led Flash driver: init\n"); | ||||
| 	FLT_INFO_LOG("adp1650 Led Flash driver: init\n"); | ||||
| 	return i2c_add_driver(&adp1650_driver); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -22,6 +22,7 @@ | ||||
|  | ||||
| /* VREF and timer register */ | ||||
| #define TIMER_200MS			(uint8_t) 0x01 | ||||
| #define TIMER_600MS                     (uint8_t) 0x05 | ||||
| #define TIMER_1600MS			(uint8_t) 0x0f | ||||
|  | ||||
| /* Current set register */ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user