609 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			609 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *
 | |
|  * Copyright (C) 2009 HTC, Inc.
 | |
|  *
 | |
|  * 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 <linux/delay.h>
 | |
| #include <linux/earlysuspend.h>
 | |
| #include <linux/hrtimer.h>
 | |
| #include <linux/i2c.h>
 | |
| #include <linux/input.h>
 | |
| #include <linux/interrupt.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/platform_device.h>
 | |
| #include <linux/workqueue.h>
 | |
| #include <linux/irq.h>
 | |
| #include <linux/errno.h>
 | |
| #include <linux/err.h>
 | |
| #include <linux/gpio.h>
 | |
| 
 | |
| #include <linux/miscdevice.h>
 | |
| #include <linux/lightsensor.h>
 | |
| #include <asm/uaccess.h>
 | |
| #include <mach/atmega_microp.h>
 | |
| #include <asm/mach-types.h>
 | |
| 
 | |
| struct microp_ls_info {
 | |
| 	struct microp_function_config *ls_config;
 | |
| 	struct input_dev *ls_input_dev;
 | |
| 	struct early_suspend early_suspend;
 | |
| 	struct i2c_client *client;
 | |
| 	struct workqueue_struct *ls_wq;
 | |
| 
 | |
| 	uint32_t als_func;
 | |
| 	uint32_t als_kadc;
 | |
| 	uint32_t als_gadc;
 | |
| 	uint8_t als_calibrating;
 | |
| 	int als_intr_enabled;
 | |
| 	int is_suspend;
 | |
| 	int old_intr_cmd;
 | |
| };
 | |
| 
 | |
| struct microp_ls_info *ls_info;
 | |
| static int ls_enable_flag;
 | |
| static int ls_enable_num;
 | |
| 
 | |
| static void enable_intr_do_work(struct work_struct *w);
 | |
| static DECLARE_DELAYED_WORK(enable_intr_work, enable_intr_do_work);
 | |
| 
 | |
| static void lightsensor_do_work(struct work_struct *w);
 | |
| static DECLARE_WORK(lightsensor_work, lightsensor_do_work);
 | |
| 
 | |
| void set_ls_kvalue(struct microp_ls_info *li)
 | |
| {
 | |
| 
 | |
| 	if (!li) {
 | |
| 		pr_err("%s: ls_info is empty\n", __func__);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	printk(KERN_INFO "%s: ALS calibrated als_kadc=0x%x\n",
 | |
| 			__func__, als_kadc);
 | |
| 	if (als_kadc >> 16 == ALS_CALIBRATED)
 | |
| 		li->als_kadc = als_kadc & 0xFFFF;
 | |
| 	else {
 | |
| 		li->als_kadc = 0;
 | |
| 		printk(KERN_INFO "%s: no ALS calibrated\n", __func__);
 | |
| 	}
 | |
| 
 | |
| 	if (li->als_kadc && li->ls_config->golden_adc > 0) {
 | |
| 		li->als_kadc = (li->als_kadc > 0 && li->als_kadc < 0x400) ?
 | |
| 				li->als_kadc : li->ls_config->golden_adc;
 | |
| 		li->als_gadc = li->ls_config->golden_adc;
 | |
| 	} else {
 | |
| 		li->als_kadc = 1;
 | |
| 		li->als_gadc = 1;
 | |
| 	}
 | |
| 	printk(KERN_INFO "%s: als_kadc=0x%x, als_gadc=0x%x\n",
 | |
| 			__func__, li->als_kadc, li->als_gadc);
 | |
| }
 | |
| 
 | |
| static int upload_ls_table(struct microp_ls_info *li)
 | |
| {
 | |
| 	uint8_t data[20];
 | |
| 	int i;
 | |
| 	for (i = 0; i < 10; i++) {
 | |
| 		if (li->ls_config->levels[i] < 0x3FF) {
 | |
| 			data[i] = (uint8_t)(li->ls_config->levels[i]
 | |
| 					* li->als_kadc / li->als_gadc >> 8);
 | |
| 			data[i + 10] = (uint8_t)(li->ls_config->levels[i]
 | |
| 					* li->als_kadc / li->als_gadc);
 | |
| 		} else {
 | |
| 			data[i] = (uint8_t)(li->ls_config->levels[i] >> 8);
 | |
| 			data[i + 10] = (uint8_t)(li->ls_config->levels[i] & 0xFF);
 | |
| 		}
 | |
| 		printk("ls_table: data[%d] , data[%d] = %x, %x\n", i, i, data[i], data[i+10]);
 | |
| 	}
 | |
| 
 | |
| 	return microp_i2c_write(MICROP_I2C_WCMD_ADC_TABLE, data, 20);
 | |
| }
 | |
| 
 | |
| static int get_ls_adc_level(uint8_t *data)
 | |
| {
 | |
| 	struct microp_ls_info *li = ls_info;
 | |
| 	uint8_t i, adc_level = 0;
 | |
| 	uint16_t adc_value = 0;
 | |
| 
 | |
| 	data[0] = 0x00;
 | |
| 	data[1] = li->ls_config->channel;
 | |
| 	if (microp_read_adc(data))
 | |
| 		return -1;
 | |
| 
 | |
| 	adc_value = data[0]<<8 | data[1];
 | |
| 	if (adc_value > 0x3FF) {
 | |
| 		printk(KERN_WARNING "%s: get wrong value: 0x%X\n",
 | |
| 			__func__, adc_value);
 | |
| 		return -1;
 | |
| 	} else {
 | |
| 		if (!li->als_calibrating) {
 | |
| 			adc_value = adc_value * li->als_gadc / li->als_kadc;
 | |
| 			if (adc_value > 0x3FF)
 | |
| 				adc_value = 0x3FF;
 | |
| 			data[0] = adc_value >> 8;
 | |
| 			data[1] = adc_value & 0xFF;
 | |
| 		}
 | |
| 		for (i = 0; i < 10; i++) {
 | |
| 			if (adc_value <=
 | |
| 				li->ls_config->levels[i]) {
 | |
| 				adc_level = i;
 | |
| 				if (li->ls_config->levels[i])
 | |
| 					break;
 | |
| 			}
 | |
| 		}
 | |
| 		printk(KERN_DEBUG "ALS value: 0x%X, level: %d #\n",
 | |
| 				adc_value, adc_level);
 | |
| 		data[2] = adc_level;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void report_lightseneor_data(void)
 | |
| {
 | |
| 	uint8_t data[3];
 | |
| 	int ret;
 | |
| 	struct microp_ls_info *li = ls_info;
 | |
| 
 | |
| 	ret = get_ls_adc_level(data);
 | |
| 	if (!ret) {
 | |
| 		input_report_abs(li->ls_input_dev,
 | |
| 				ABS_MISC, (int)data[2]);
 | |
| 		input_sync(li->ls_input_dev);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int ls_microp_intr_enable(uint8_t enable)
 | |
| {
 | |
| 
 | |
| 	int ret;
 | |
| 	uint8_t data[2];
 | |
| 	struct microp_ls_info *li = ls_info;
 | |
| 
 | |
| 	if (li->old_intr_cmd) {
 | |
| 		data[0] = 0;
 | |
| 		if (enable)
 | |
| 			data[1] = 1;
 | |
| 		else
 | |
| 			data[1] = 0;
 | |
| 
 | |
| 		ret = microp_i2c_write(MICROP_I2C_WCMD_AUTO_BL_CTL, data, 2);
 | |
| 	} else {
 | |
| 		ret = microp_write_interrupt(li->client,
 | |
| 			li->ls_config->int_pin, enable);
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static void enable_intr_do_work(struct work_struct *w)
 | |
| {
 | |
| 	struct microp_ls_info *li = ls_info;
 | |
| 	int ret;
 | |
| 
 | |
| 	if (ls_enable_flag) {
 | |
| 		ret = ls_microp_intr_enable(1);
 | |
| 		if (ret < 0)
 | |
| 			pr_err("%s error\n", __func__);
 | |
| 		else {
 | |
| 			li->als_intr_enabled = 1;
 | |
| 			ls_enable_flag = 0;
 | |
| 			input_report_abs(li->ls_input_dev, ABS_MISC, -1);
 | |
| 			input_sync(li->ls_input_dev);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	report_lightseneor_data();
 | |
| }
 | |
| 
 | |
| static void lightsensor_do_work(struct work_struct *w)
 | |
| {
 | |
| 	/* Wait for Framework event polling ready */
 | |
| 	if (ls_enable_num == 0) {
 | |
| 		ls_enable_num = 1;
 | |
| 		msleep(300);
 | |
| 	}
 | |
| 
 | |
| 	report_lightseneor_data();
 | |
| }
 | |
| 
 | |
| static irqreturn_t lightsensor_irq_handler(int irq, void *data)
 | |
| {
 | |
| 	struct microp_ls_info *li = ls_info;
 | |
| 	queue_work(li->ls_wq, &lightsensor_work);
 | |
| 
 | |
| 	return IRQ_HANDLED;
 | |
| }
 | |
| 
 | |
| static int ls_power(int enable)
 | |
| {
 | |
| 	struct microp_ls_info *li = ls_info;
 | |
| 
 | |
| 	if (li->ls_config->ls_gpio_on)
 | |
| 		gpio_set_value(li->ls_config->ls_gpio_on, enable ? 0 : 1);
 | |
| 
 | |
| 	if (li->ls_config->ls_power)
 | |
| 		li->ls_config->ls_power(LS_PWR_ON, enable);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int lightsensor_enable(void)
 | |
| {
 | |
| 	int ret;
 | |
| 	struct microp_ls_info *li = ls_info;
 | |
| 
 | |
| 	pr_info("%s\n", __func__);
 | |
| 
 | |
| 	ls_enable_flag = 1;
 | |
| 	if (li->is_suspend) {
 | |
| 		li->als_intr_enabled = 1;
 | |
| 		pr_err("%s: microp is suspended\n", __func__);
 | |
| 		return 0;
 | |
| 	}
 | |
| 	if (!li->als_intr_enabled) {
 | |
| 		ret = ls_microp_intr_enable(1);
 | |
| 		if (ret < 0)
 | |
| 			pr_err("%s: set auto light sensor fail\n", __func__);
 | |
| 		else {
 | |
| 			li->als_intr_enabled = 1;
 | |
| 			/* report an invalid value first to ensure we trigger an event
 | |
| 			* when adc_level is zero.
 | |
| 			*/
 | |
| 			input_report_abs(li->ls_input_dev, ABS_MISC, -1);
 | |
| 			input_sync(li->ls_input_dev);
 | |
| 		}
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int lightsensor_disable(void)
 | |
| {
 | |
| 	/* update trigger data when done */
 | |
| 	struct microp_ls_info *li = ls_info;
 | |
| 	int ret;
 | |
| 
 | |
| 	pr_info("%s\n", __func__);
 | |
| 	ls_enable_flag = 0;
 | |
| 	if (li->is_suspend) {
 | |
| 		li->als_intr_enabled = 0;
 | |
| 		pr_err("%s: microp is suspended\n", __func__);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	if (li->als_intr_enabled) {
 | |
| 		ret = ls_microp_intr_enable(0);
 | |
| 		if (ret < 0)
 | |
| 			pr_err("%s: disable auto light sensor fail\n",
 | |
| 		       __func__);
 | |
| 		else
 | |
| 			li->als_intr_enabled = 0;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| DEFINE_MUTEX(ls_i2c_api_lock);
 | |
| static int lightsensor_opened;
 | |
| 
 | |
| static int lightsensor_open(struct inode *inode, struct file *file)
 | |
| {
 | |
| 	int rc = 0;
 | |
| 	pr_debug("%s\n", __func__);
 | |
| 	mutex_lock(&ls_i2c_api_lock);
 | |
| 	if (lightsensor_opened) {
 | |
| 		pr_err("%s: already opened\n", __func__);
 | |
| 		rc = -EBUSY;
 | |
| 	}
 | |
| 	lightsensor_opened = 1;
 | |
| 	mutex_unlock(&ls_i2c_api_lock);
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| static int lightsensor_release(struct inode *inode, struct file *file)
 | |
| {
 | |
| 	pr_debug("%s\n", __func__);
 | |
| 	mutex_lock(&ls_i2c_api_lock);
 | |
| 	lightsensor_opened = 0;
 | |
| 	mutex_unlock(&ls_i2c_api_lock);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static long lightsensor_ioctl(struct file *file, unsigned int cmd,
 | |
| 		unsigned long arg)
 | |
| {
 | |
| 	int rc, val;
 | |
| 	struct microp_ls_info *li = ls_info;
 | |
| 	mutex_lock(&ls_i2c_api_lock);
 | |
| 	pr_debug("%s cmd %d\n", __func__, _IOC_NR(cmd));
 | |
| 
 | |
| 	switch (cmd) {
 | |
| 	case LIGHTSENSOR_IOCTL_ENABLE:
 | |
| 		if (get_user(val, (unsigned long __user *)arg)) {
 | |
| 			rc = -EFAULT;
 | |
| 			break;
 | |
| 		}
 | |
| 		pr_info("%s set value = %d\n", __func__, val);
 | |
| 		rc = val ? lightsensor_enable() : lightsensor_disable();
 | |
| 		break;
 | |
| 	case LIGHTSENSOR_IOCTL_GET_ENABLED:
 | |
| 		val = li->als_intr_enabled;
 | |
| 		pr_info("%s get enabled status: %d\n", __func__, val);
 | |
| 		rc = put_user(val, (unsigned long __user *)arg);
 | |
| 		break;
 | |
| 	default:
 | |
| 		pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd));
 | |
| 		rc = -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	mutex_unlock(&ls_i2c_api_lock);
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| static struct file_operations lightsensor_fops = {
 | |
| 	.owner = THIS_MODULE,
 | |
| 	.open = lightsensor_open,
 | |
| 	.release = lightsensor_release,
 | |
| 	.unlocked_ioctl = lightsensor_ioctl
 | |
| };
 | |
| 
 | |
| static struct miscdevice lightsensor_misc = {
 | |
| 	.minor = MISC_DYNAMIC_MINOR,
 | |
| 	.name = "lightsensor",
 | |
| 	.fops = &lightsensor_fops
 | |
| };
 | |
| 
 | |
| static ssize_t ls_adc_show(struct device *dev,
 | |
| 				  struct device_attribute *attr, char *buf)
 | |
| {
 | |
| 
 | |
| 	uint8_t data[3];
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = get_ls_adc_level(data);
 | |
| 
 | |
| 	ret = sprintf(buf,
 | |
| 				"ADC[0x%03X] => level %d\n",
 | |
| 				(data[0] << 8 | data[1]), data[2]);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static DEVICE_ATTR(ls_adc, 0666, ls_adc_show, NULL);
 | |
| 
 | |
| static ssize_t ls_enable_show(struct device *dev,
 | |
| 				  struct device_attribute *attr, char *buf)
 | |
| {
 | |
| 	uint8_t data[2] = {0, 0};
 | |
| 	int ret;
 | |
| 
 | |
| 	microp_i2c_read(MICROP_I2C_RCMD_SPI_BL_STATUS, data, 2);
 | |
| 	ret = sprintf(buf, "Light sensor Auto = %d, SPI enable = %d\n",
 | |
| 				data[0], data[1]);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static ssize_t ls_enable_store(struct device *dev,
 | |
| 				   struct device_attribute *attr,
 | |
| 				   const char *buf, size_t count)
 | |
| {
 | |
| 	struct microp_ls_info *li = ls_info;
 | |
| 	uint8_t enable = 0;
 | |
| 	int ls_auto;
 | |
| 	int ret;
 | |
| 
 | |
| 	ls_auto = -1;
 | |
| 	sscanf(buf, "%d", &ls_auto);
 | |
| 
 | |
| 	if (ls_auto != 0 && ls_auto != 1 && ls_auto != 147)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	if (ls_auto) {
 | |
| 		enable = 1;
 | |
| 		li->als_calibrating = (ls_auto == 147) ? 1 : 0;
 | |
| 		li->als_intr_enabled = 1;
 | |
| 	} else {
 | |
| 		enable = 0;
 | |
| 		li->als_calibrating = 0;
 | |
| 		li->als_intr_enabled = 0;
 | |
| 	}
 | |
| 
 | |
| 	ret = ls_microp_intr_enable(enable);
 | |
| 	if (ret < 0)
 | |
| 		pr_err("%s: ls intr enable fail\n", __func__);
 | |
| 
 | |
| 	return count;
 | |
| }
 | |
| 
 | |
| static DEVICE_ATTR(ls_auto, 0666, ls_enable_show, ls_enable_store);
 | |
| 
 | |
| static ssize_t ls_kadc_show(struct device *dev,
 | |
| 				  struct device_attribute *attr, char *buf)
 | |
| {
 | |
| 	struct microp_ls_info *li = ls_info;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = sprintf(buf, "kadc = 0x%x, gadc = 0x%x, real kadc = 0x%x\n",
 | |
| 				li->als_kadc, li->als_gadc, als_kadc);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static ssize_t ls_kadc_store(struct device *dev,
 | |
| 				   struct device_attribute *attr,
 | |
| 				   const char *buf, size_t count)
 | |
| {
 | |
| 	struct microp_ls_info *li = ls_info;
 | |
| 	int kadc_temp = 0;
 | |
| 
 | |
| 	sscanf(buf, "%d", &kadc_temp);
 | |
| 	if (kadc_temp <= 0 || li->ls_config->golden_adc <= 0) {
 | |
| 		printk(KERN_ERR "%s: kadc_temp=0x%x, als_gadc=0x%x\n",
 | |
| 			__func__,
 | |
| 			kadc_temp,
 | |
| 			li->ls_config->golden_adc);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	li->als_kadc = kadc_temp;
 | |
| 	li->als_gadc = li->ls_config->golden_adc;
 | |
| 	printk(KERN_INFO "%s: als_kadc=0x%x, als_gadc=0x%x\n",
 | |
| 			__func__, li->als_kadc, li->als_gadc);
 | |
| 
 | |
| 	if (upload_ls_table(li) < 0)
 | |
| 		printk(KERN_ERR "%s: upload ls table fail\n", __func__);
 | |
| 
 | |
| 	return count;
 | |
| }
 | |
| 
 | |
| static DEVICE_ATTR(ls_kadc, 0666, ls_kadc_show, ls_kadc_store);
 | |
| 
 | |
| #ifdef CONFIG_HAS_EARLYSUSPEND
 | |
| static void light_sensor_suspend(struct early_suspend *h)
 | |
| {
 | |
| 	struct microp_ls_info *li = ls_info;
 | |
| 	int ret;
 | |
| 
 | |
| 	li->is_suspend = 1;
 | |
| 	cancel_delayed_work(&enable_intr_work);
 | |
| 	if (li->als_intr_enabled) {
 | |
| 		ret = ls_microp_intr_enable(0);
 | |
| 		if (ret < 0)
 | |
| 			pr_err("%s: disable auto light sensor fail\n",
 | |
| 				__func__);
 | |
| 		else
 | |
| 			li->als_intr_enabled = 0;
 | |
| 	}
 | |
| 	ls_power(0);
 | |
| }
 | |
| 
 | |
| static void light_sensor_resume(struct early_suspend *h)
 | |
| {
 | |
| 	struct microp_ls_info *li = ls_info;
 | |
| 
 | |
| 	ls_power(1);
 | |
| 	queue_delayed_work(li->ls_wq, &enable_intr_work, msecs_to_jiffies(800));
 | |
| 	li->is_suspend = 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static int lightsensor_probe(struct platform_device *pdev)
 | |
| {
 | |
| 	int ret, irq;
 | |
| 	struct microp_ls_info *li;
 | |
| 	struct lightsensor_platform_data *pdata = pdev->dev.platform_data;
 | |
| 
 | |
| 	li = kzalloc(sizeof(struct microp_ls_info), GFP_KERNEL);
 | |
| 	if (!li)
 | |
| 		return -ENOMEM;
 | |
| 	ls_info = li;
 | |
| 	li->client = dev_get_drvdata(&pdev->dev);
 | |
| 
 | |
| 	if (!li->client) {
 | |
| 		pr_err("%s: can't get microp i2c client\n", __func__);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	li->ls_input_dev = input_allocate_device();
 | |
| 	if (!li->ls_input_dev) {
 | |
| 		pr_err("%s: could not allocate input device\n", __func__);
 | |
| 		return -ENOMEM;
 | |
| 	}
 | |
| 	li->ls_input_dev->name = "lightsensor-level";
 | |
| 	set_bit(EV_ABS, li->ls_input_dev->evbit);
 | |
| 	input_set_abs_params(li->ls_input_dev, ABS_MISC, 0, 9, 0, 0);
 | |
| 
 | |
| 	ret = input_register_device(li->ls_input_dev);
 | |
| 	if (ret < 0) {
 | |
| 		pr_err("%s: can not register input device\n",
 | |
| 				__func__);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	ret = misc_register(&lightsensor_misc);
 | |
| 	if (ret < 0) {
 | |
| 		pr_err("%s: can not register misc device\n",
 | |
| 				__func__);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	li->ls_config = pdata->config;
 | |
| 	irq = pdata->irq;
 | |
| 	li->old_intr_cmd = pdata->old_intr_cmd;
 | |
| 	ret = request_irq(irq, lightsensor_irq_handler, IRQF_TRIGGER_NONE, "lightsensor_microp", li);
 | |
| 	if (ret < 0) {
 | |
| 		pr_err("%s: request_irq(%d) failed for (%d)\n",
 | |
| 			__func__, irq, ret);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	set_ls_kvalue(li);
 | |
| 	ret = upload_ls_table(li);
 | |
| 	if (ret < 0) {
 | |
| 		pr_err("%s: upload ls table fail\n",
 | |
| 				__func__);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	li->ls_wq = create_workqueue("ls_wq");
 | |
| 	if (li->ls_wq == NULL)
 | |
| 			return -ENOMEM;
 | |
| 
 | |
| 	if (li->ls_config->ls_gpio_on) {
 | |
| 		ret = gpio_request(li->ls_config->ls_gpio_on,
 | |
| 				"microp_i2c");
 | |
| 		if (ret < 0) {
 | |
| 			pr_err("request gpio ls failed\n");
 | |
| 			return ret;
 | |
| 		}
 | |
| 		ret = gpio_direction_output(li->ls_config->ls_gpio_on, 0);
 | |
| 		if (ret < 0) {
 | |
| 			pr_err("gpio_direction_output ls failed\n");
 | |
| 			return ret;
 | |
| 		}
 | |
| 	}
 | |
| 	ls_power(1);
 | |
| #ifdef CONFIG_HAS_EARLYSUSPEND
 | |
| 	li->early_suspend.level =
 | |
| 			EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
 | |
| 	li->early_suspend.suspend = light_sensor_suspend;
 | |
| 	li->early_suspend.resume = light_sensor_resume;
 | |
| 	register_early_suspend(&li->early_suspend);
 | |
| #endif
 | |
| 	ret = device_create_file(&li->client->dev, &dev_attr_ls_adc);
 | |
| 	ret = device_create_file(&li->client->dev, &dev_attr_ls_auto);
 | |
| 	ret = device_create_file(&li->client->dev, &dev_attr_ls_kadc);
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| }
 | |
| 
 | |
| static struct platform_driver lightsensor_driver = {
 | |
| 	.probe = lightsensor_probe,
 | |
| 	.driver = { .name = "lightsensor_microp", },
 | |
| };
 | |
| 
 | |
| static int __init light_sensor_init(void)
 | |
| {
 | |
| 	return platform_driver_register(&lightsensor_driver);
 | |
| }
 | |
| 
 | |
| static void __exit light_sensor_exit(void)
 | |
| {
 | |
| 	platform_driver_unregister(&lightsensor_driver);
 | |
| }
 | |
| 
 | |
| module_init(light_sensor_init);
 | |
| module_exit(light_sensor_exit);
 | |
| 
 | |
| MODULE_DESCRIPTION("HTC LIGHT SENSOR");
 | |
| MODULE_LICENSE("GPL");
 |