diff options
Diffstat (limited to 'drivers/input/misc/cm3217.c')
-rw-r--r-- | drivers/input/misc/cm3217.c | 1025 |
1 files changed, 1025 insertions, 0 deletions
diff --git a/drivers/input/misc/cm3217.c b/drivers/input/misc/cm3217.c new file mode 100644 index 000000000000..eed3a6dd331e --- /dev/null +++ b/drivers/input/misc/cm3217.c @@ -0,0 +1,1025 @@ +/* drivers/input/misc/cm3217.c - cm3217 optical sensors driver + * + * Copyright (C) 2011 Capella Microsystems Inc. + * Author: Frank Hsieh <pengyueh@gmail.com> + * + * 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/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 <linux/slab.h> +#include <asm/uaccess.h> +#include <asm/mach-types.h> +#include <linux/cm3217.h> +#include <asm/setup.h> +#include <linux/wakelock.h> +#include <linux/jiffies.h> + +#define D(x...) pr_info(x) + +#define I2C_RETRY_COUNT 10 + +#define LS_POLLING_DELAY 500 + +static void report_do_work(struct work_struct *w); +static DECLARE_DELAYED_WORK(report_work, report_do_work); + +struct cm3217_info { + struct class *cm3217_class; + struct device *ls_dev; + struct input_dev *ls_input_dev; + + struct early_suspend early_suspend; + struct i2c_client *i2c_client; + struct workqueue_struct *lp_wq; + + int als_enable; + uint16_t *adc_table; + uint16_t cali_table[10]; + int irq; + int ls_calibrate; + int (*power)(int, uint8_t); /* power to the chip */ + + uint32_t als_kadc; + uint32_t als_gadc; + uint16_t golden_adc; + + int lightsensor_opened; + int current_level; + uint16_t current_adc; + int polling_delay; +}; +struct cm3217_info *lp_info; +int enable_log = 0; +int fLevel=-1; +static struct mutex als_enable_mutex, als_disable_mutex, als_get_adc_mutex; +static int lightsensor_enable(struct cm3217_info *lpi); +static int lightsensor_disable(struct cm3217_info *lpi); + +int32_t als_kadc; + +static int I2C_RxData(uint16_t slaveAddr, uint8_t *rxData, int length) +{ + uint8_t loop_i; + + struct i2c_msg msgs[] = { + { + .addr = slaveAddr, + .flags = I2C_M_RD, + .len = length, + .buf = rxData, + }, + }; + + for (loop_i = 0; loop_i < I2C_RETRY_COUNT; loop_i++) { + + if (i2c_transfer(lp_info->i2c_client->adapter, msgs, 1) > 0) + break; + + msleep(10); + } + if (loop_i >= I2C_RETRY_COUNT) { + printk(KERN_ERR "[ERR][CM3217 error] %s retry over %d\n", + __func__, I2C_RETRY_COUNT); + return -EIO; + } + + return 0; +} + +static int I2C_TxData(uint16_t slaveAddr, uint8_t *txData, int length) +{ + uint8_t loop_i; + + struct i2c_msg msg[] = { + { + .addr = slaveAddr, + .flags = 0, + .len = length, + .buf = txData, + }, + }; + + for (loop_i = 0; loop_i < I2C_RETRY_COUNT; loop_i++) { + if (i2c_transfer(lp_info->i2c_client->adapter, msg, 1) > 0) + break; + + msleep(10); + } + + if (loop_i >= I2C_RETRY_COUNT) { + printk(KERN_ERR "[ERR][CM3217 error] %s retry over %d\n", + __func__, I2C_RETRY_COUNT); + return -EIO; + } + + return 0; +} + +static int _cm3217_I2C_Read_Byte(uint16_t slaveAddr, uint8_t *pdata) +{ + uint8_t buffer = 0; + int ret = 0; + + if (pdata == NULL) + return -EFAULT; + + ret = I2C_RxData(slaveAddr, &buffer, 1); + if (ret < 0) { + pr_err( + "[ERR][CM3217 error]%s: I2C_RxData fail, slave addr: 0x%x\n", + __func__, slaveAddr); + return ret; + } + + *pdata = buffer; +#if 0 + /* Debug use */ + printk(KERN_DEBUG "[CM3217] %s: I2C_RxData[0x%x] = 0x%x\n", + __func__, slaveAddr, buffer); +#endif + return ret; +} + +static int _cm3217_I2C_Write_Byte(uint16_t SlaveAddress, + uint8_t data) +{ + char buffer[2]; + int ret = 0; +#if 0 + /* Debug use */ + printk(KERN_DEBUG + "[CM3217] %s: _cm3217_I2C_Write_Byte[0x%x, 0x%x, 0x%x]\n", + __func__, SlaveAddress, cmd, data); +#endif + buffer[0] = data; + + ret = I2C_TxData(SlaveAddress, buffer, 1); + if (ret < 0) { + pr_err("[ERR][CM3217 error]%s: I2C_TxData fail\n", __func__); + return -EIO; + } + + return ret; +} + +static int get_ls_adc_value(uint16_t *als_step, bool resume) +{ + uint8_t lsb, msb; + int ret = 0; + struct cm3217_info *lpi = lp_info; + + if (als_step == NULL) + return -EFAULT; + + /* Read ALS data: LSB */ + ret = _cm3217_I2C_Read_Byte(ALS_R_LSB_addr, &lsb); + if (ret < 0) { + pr_err( + "[LS][CM3217 error]%s: _cm3217_I2C_Read_Byte LSB fail\n", + __func__); + return -EIO; + } + + /* Read ALS data: MSB */ + ret = _cm3217_I2C_Read_Byte(ALS_R_MSB_addr, &msb); + if (ret < 0) { + pr_err( + "[LS][CM3217 error]%s: _cm3217_I2C_Read_Byte MSB fail\n", + __func__); + return -EIO; + } + + *als_step = (uint16_t)msb; + *als_step <<= 8; + *als_step |= (uint16_t)lsb; + + D("[LS][CM3217] %s: raw adc = 0x%X\n", + __func__, *als_step); + + if (!lpi->ls_calibrate) { + *als_step = (*als_step) * lpi->als_gadc / lpi->als_kadc; + if (*als_step > 0xFFFF) + *als_step = 0xFFFF; + } + + return ret; +} + +static void report_lsensor_input_event(struct cm3217_info *lpi, bool resume) +{/*when resume need report a data, so the paramerter need to quick reponse*/ + uint16_t adc_value = 0; + int level = 0, i, ret = 0; + + mutex_lock(&als_get_adc_mutex); + + ret = get_ls_adc_value(&adc_value, resume); + + if( lpi->ls_calibrate ) { + for (i = 0; i < 10; i++) { + if (adc_value <= (*(lpi->cali_table + i))) { + level = i; + if (*(lpi->cali_table + i)) + break; + } + if ( i == 9) {/*avoid i = 10, because 'cali_table' of size is 10 */ + level = i; + break; + } + } + } else { + for (i = 0; i < 10; i++) { + if (adc_value <= (*(lpi->adc_table + i))) { + level = i; + if (*(lpi->adc_table + i)) + break; + } + if ( i == 9) {/*avoid i = 10, because 'cali_table' of size is 10 */ + level = i; + break; + } + } + } + + if ((i == 0) || (adc_value == 0)) + D("[LS][CM3217] %s: ADC=0x%03X, Level=%d, l_thd equal 0, h_thd = 0x%x \n", + __func__, adc_value, level, *(lpi->cali_table + i)); + else + D("[LS][CM3217] %s: ADC=0x%03X, Level=%d, l_thd = 0x%x, h_thd = 0x%x \n", + __func__, adc_value, level, *(lpi->cali_table + (i - 1)) + 1, *(lpi->cali_table + i)); + + lpi->current_level = level; + lpi->current_adc = adc_value; + /*D("[CM3217] %s: *(lpi->cali_table + (i - 1)) + 1 = 0x%X, *(lpi->cali_table + i) = 0x%x \n", __func__, *(lpi->cali_table + (i - 1)) + 1, *(lpi->cali_table + i));*/ + if(fLevel>=0){ + D("[LS][CM3217] L-sensor force level enable level=%d fLevel=%d\n",level,fLevel); + level=fLevel; + } + input_report_abs(lpi->ls_input_dev, ABS_MISC, level); + input_sync(lpi->ls_input_dev); + + mutex_unlock(&als_get_adc_mutex); +} + +static void report_do_work(struct work_struct *work) +{ + struct cm3217_info *lpi = lp_info; + + if (enable_log) + D("[CM3217] %s\n", __func__); + + report_lsensor_input_event(lpi, 0); + + queue_delayed_work(lpi->lp_wq, &report_work, lpi->polling_delay); +} + +static int als_power(int enable) +{ + struct cm3217_info *lpi = lp_info; + + if (lpi->power) + lpi->power(LS_PWR_ON, 1); + + return 0; +} + +void lightsensor_set_kvalue(struct cm3217_info *lpi) +{ + if (!lpi) { + pr_err("[LS][CM3217 error]%s: ls_info is empty\n", __func__); + return; + } + + D("[LS][CM3217] %s: ALS calibrated als_kadc=0x%x\n", + __func__, als_kadc); + + if (als_kadc >> 16 == ALS_CALIBRATED) + lpi->als_kadc = als_kadc & 0xFFFF; + else { + lpi->als_kadc = 0; + D("[LS][CM3217] %s: no ALS calibrated\n", __func__); + } + + if (lpi->als_kadc && lpi->golden_adc > 0) { + lpi->als_kadc = (lpi->als_kadc > 0 && lpi->als_kadc < 0x1000) ? + lpi->als_kadc : lpi->golden_adc; + lpi->als_gadc = lpi->golden_adc; + } else { + lpi->als_kadc = 1; + lpi->als_gadc = 1; + } + D("[LS][CM3217] %s: als_kadc=0x%x, als_gadc=0x%x\n", + __func__, lpi->als_kadc, lpi->als_gadc); +} + +static int lightsensor_update_table(struct cm3217_info *lpi) +{ + uint32_t tmpData[10]; + int i; + for (i = 0; i < 10; i++) { + tmpData[i] = (uint32_t)(*(lpi->adc_table + i)) + * lpi->als_kadc / lpi->als_gadc ; + if( tmpData[i] <= 0xFFFF ){ + lpi->cali_table[i] = (uint16_t) tmpData[i]; + } else { + lpi->cali_table[i] = 0xFFFF; + } + D("[LS][CM3217] %s: Calibrated adc_table: data[%d], %x\n", + __func__, i, lpi->cali_table[i]); + } + + return 0; +} + +static int lightsensor_enable(struct cm3217_info *lpi) +{ + int ret = 0; + uint8_t cmd = 0; + + mutex_lock(&als_enable_mutex); + D("[LS][CM3217] %s\n", __func__); + + cmd = ( CM3217_ALS_IT_2_T | CM3217_ALS_BIT5_Default_1 | + CM3217_ALS_WDM_DEFAULT_1); + ret = _cm3217_I2C_Write_Byte(ALS_W_CMD1_addr, cmd); + if (ret < 0) + pr_err( + "[LS][CM3217 error]%s: set auto light sensor fail\n", + __func__); + else { + msleep(50);/*wait for 50 ms for the first report adc*/ + /* report an invalid value first to ensure we + * trigger an event when adc_level is zero. + */ + + input_report_abs(lpi->ls_input_dev, ABS_MISC, -1); + input_sync(lpi->ls_input_dev); + report_lsensor_input_event(lpi, 1);/*resume, IOCTL and DEVICE_ATTR*/ + lpi->als_enable = 1; + } + queue_delayed_work(lpi->lp_wq, &report_work, lpi->polling_delay); + lpi->als_enable=1; + mutex_unlock(&als_enable_mutex); + + return ret; +} + +static int lightsensor_disable(struct cm3217_info *lpi) +{ + int ret = 0; + char cmd = 0; + + mutex_lock(&als_disable_mutex); + + D("[LS][CM3217] %s\n", __func__); + + cmd = (CM3217_ALS_IT_2_T | CM3217_ALS_BIT5_Default_1 | + CM3217_ALS_WDM_DEFAULT_1 | CM3217_ALS_SD); + ret = _cm3217_I2C_Write_Byte(ALS_W_CMD1_addr, cmd); + if (ret < 0) + pr_err("[LS][CM3217 error]%s: disable auto light sensor fail\n", + __func__); + else { + lpi->als_enable = 0; + } + cancel_delayed_work(&report_work); + lpi->als_enable=0; + mutex_unlock(&als_disable_mutex); + + return ret; +} + +static int lightsensor_open(struct inode *inode, struct file *file) +{ + struct cm3217_info *lpi = lp_info; + int rc = 0; + + D("[LS][CM3217] %s\n", __func__); + if (lpi->lightsensor_opened) { + pr_err("[LS][CM3217 error]%s: already opened\n", __func__); + rc = -EBUSY; + } + lpi->lightsensor_opened = 1; + return rc; +} + +static int lightsensor_release(struct inode *inode, struct file *file) +{ + struct cm3217_info *lpi = lp_info; + + D("[LS][CM3217] %s\n", __func__); + lpi->lightsensor_opened = 0; + return 0; +} + +static long lightsensor_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int rc, val; + struct cm3217_info *lpi = lp_info; + + /*D("[CM3217] %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; + } + D("[LS][CM3217] %s LIGHTSENSOR_IOCTL_ENABLE, value = %d\n", + __func__, val); + rc = val ? lightsensor_enable(lpi) : lightsensor_disable(lpi); + break; + case LIGHTSENSOR_IOCTL_GET_ENABLED: + val = lpi->als_enable; + D("[LS][CM3217] %s LIGHTSENSOR_IOCTL_GET_ENABLED, enabled %d\n", + __func__, val); + rc = put_user(val, (unsigned long __user *)arg); + break; + default: + pr_err("[LS][CM3217 error]%s: invalid cmd %d\n", + __func__, _IOC_NR(cmd)); + rc = -EINVAL; + } + + return rc; +} + +static const 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) +{ + int ret; + struct cm3217_info *lpi = lp_info; + + D("[LS][CM3217] %s: ADC = 0x%04X, Level = %d \n", + __func__, lpi->current_adc, lpi->current_level); + ret = sprintf(buf, "ADC[0x%04X] => level %d\n", + lpi->current_adc, lpi->current_level); + + return ret; +} + +static DEVICE_ATTR(ls_adc, 0664, ls_adc_show, NULL); + +static ssize_t ls_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + struct cm3217_info *lpi = lp_info; + + ret = sprintf(buf, "Light sensor Auto Enable = %d\n", + lpi->als_enable); + + return ret; +} + +static ssize_t ls_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + int ls_auto; + struct cm3217_info *lpi = lp_info; + + ls_auto = -1; + sscanf(buf, "%d", &ls_auto); + + if (ls_auto != 0 && ls_auto != 1 ) + return -EINVAL; + + if (ls_auto) { + ret = lightsensor_enable(lpi); + } else { + ret = lightsensor_disable(lpi); + } + + D("[LS][CM3217] %s: lpi->als_enable = %d, lpi->ls_calibrate = %d, ls_auto=%d\n", + __func__, lpi->als_enable, lpi->ls_calibrate, ls_auto); + + if (ret < 0) + pr_err( + "[LS][CM3217 error]%s: set auto light sensor fail\n", + __func__); + + return count; +} + +static DEVICE_ATTR(ls_auto, 0664, + ls_enable_show, ls_enable_store); + +static ssize_t ls_kadc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cm3217_info *lpi = lp_info; + int ret; + + ret = sprintf(buf, "kadc = 0x%x", + lpi->als_kadc); + + return ret; +} + +static ssize_t ls_kadc_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cm3217_info *lpi = lp_info; + int kadc_temp = 0; + + sscanf(buf, "%d", &kadc_temp); + /* + if (kadc_temp <= 0 || lpi->golden_adc <= 0) { + printk(KERN_ERR "[LS][CM3217 error] %s: kadc_temp=0x%x, als_gadc=0x%x\n", + __func__, kadc_temp, lpi->golden_adc); + return -EINVAL; + }*/ + mutex_lock(&als_get_adc_mutex); + if(kadc_temp != 0) { + lpi->als_kadc = kadc_temp; + if( lpi->als_gadc != 0){ + if (lightsensor_update_table(lpi) < 0) + printk(KERN_ERR "[LS][CM3217 error] %s: update ls table fail\n", __func__); + } else { + printk(KERN_INFO "[LS]%s: als_gadc =0x%x wait to be set\n", + __func__, lpi->als_gadc); + } + } else { + printk(KERN_INFO "[LS]%s: als_kadc can't be set to zero\n", + __func__); + } + + mutex_unlock(&als_get_adc_mutex); + return count; +} + +static DEVICE_ATTR(ls_kadc, 0664, ls_kadc_show, ls_kadc_store); + +static ssize_t ls_gadc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cm3217_info *lpi = lp_info; + int ret; + + ret = sprintf(buf, "gadc = 0x%x\n", lpi->als_gadc); + + return ret; +} + +static ssize_t ls_gadc_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cm3217_info *lpi = lp_info; + int gadc_temp = 0; + + sscanf(buf, "%d", &gadc_temp); + /*if (gadc_temp <= 0 || lpi->golden_adc <= 0) { + printk(KERN_ERR "[LS][CM3217 error] %s: kadc_temp=0x%x, als_gadc=0x%x\n", + __func__, kadc_temp, lpi->golden_adc); + return -EINVAL; + }*/ + + mutex_lock(&als_get_adc_mutex); + if(gadc_temp != 0) { + lpi->als_gadc = gadc_temp; + if( lpi->als_kadc != 0){ + if (lightsensor_update_table(lpi) < 0) + printk(KERN_ERR "[LS][CM3217 error] %s: update ls table fail\n", __func__); + } else { + printk(KERN_INFO "[LS]%s: als_kadc =0x%x wait to be set\n", + __func__, lpi->als_kadc); + } + } else { + printk(KERN_INFO "[LS]%s: als_gadc can't be set to zero\n", + __func__); + } + mutex_unlock(&als_get_adc_mutex); + return count; +} + +static DEVICE_ATTR(ls_gadc, 0664, ls_gadc_show, ls_gadc_store); + +static ssize_t ls_adc_table_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned length = 0; + int i; + + for (i = 0; i < 10; i++) { + length += sprintf(buf + length, + "[CM3217]Get adc_table[%d] = 0x%x ; %d, Get cali_table[%d] = 0x%x ; %d, \n", + i, *(lp_info->adc_table + i), + *(lp_info->adc_table + i), + i, *(lp_info->cali_table + i), + *(lp_info->cali_table + i)); + } + return length; +} + +static ssize_t ls_adc_table_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + + struct cm3217_info *lpi = lp_info; + char *token[10]; + uint16_t tempdata[10]; + int i; + + printk(KERN_INFO "[LS][CM3217]%s\n", buf); + for (i = 0; i < 10; i++) { + token[i] = strsep((char **)&buf, " "); + tempdata[i] = simple_strtoul(token[i], NULL, 16); + if (tempdata[i] < 1 || tempdata[i] > 0xffff) { + printk(KERN_ERR + "[LS][CM3217 error] adc_table[%d] = 0x%x Err\n", + i, tempdata[i]); + return count; + } + } + mutex_lock(&als_get_adc_mutex); + for (i = 0; i < 10; i++) { + lpi->adc_table[i] = tempdata[i]; + printk(KERN_INFO + "[LS][CM3217]Set lpi->adc_table[%d] = 0x%x\n", + i, *(lp_info->adc_table + i)); + } + if (lightsensor_update_table(lpi) < 0) + printk(KERN_ERR "[LS][CM3217 error] %s: update ls table fail\n", + __func__); + mutex_unlock(&als_get_adc_mutex); + D("[LS][CM3217] %s\n", __func__); + + return count; +} + +static DEVICE_ATTR(ls_adc_table, 0664, + ls_adc_table_show, ls_adc_table_store); + +static uint8_t ALS_CONF1 = 0; +static ssize_t ls_conf1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "ALS_CONF1 = %x\n", ALS_CONF1); +} +static ssize_t ls_conf1_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int value = 0; + sscanf(buf, "0x%x", &value); + + ALS_CONF1 = value; + printk(KERN_INFO "[LS]set ALS_CONF1 = %x\n", ALS_CONF1); + _cm3217_I2C_Write_Byte(ALS_W_CMD1_addr, ALS_CONF1); + return count; +} +static DEVICE_ATTR(ls_conf1, 0664, ls_conf1_show, ls_conf1_store); + +static uint8_t ALS_CONF2 = 0; +static ssize_t ls_conf2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "ALS_CONF2 = %x\n", ALS_CONF2); +} +static ssize_t ls_conf2_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int value = 0; + sscanf(buf, "0x%x", &value); + + ALS_CONF2 = value; + printk(KERN_INFO "[LS]set ALS_CONF2 = %x\n", ALS_CONF2); + _cm3217_I2C_Write_Byte(ALS_W_CMD2_addr, ALS_CONF2); + return count; +} +static DEVICE_ATTR(ls_conf2, 0664, ls_conf2_show, ls_conf2_store); + +static ssize_t ls_fLevel_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "fLevel = %d\n", fLevel); +} +static ssize_t ls_fLevel_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cm3217_info *lpi = lp_info; + int value=0; + sscanf(buf, "%d", &value); + (value>=0)?(value=min(value,10)):(value=max(value,-1)); + fLevel=value; + input_report_abs(lpi->ls_input_dev, ABS_MISC, fLevel); + input_sync(lpi->ls_input_dev); + printk(KERN_INFO "[LS]set fLevel = %d\n", fLevel); + + msleep(1000); + fLevel=-1; + return count; +} +static DEVICE_ATTR(ls_flevel, 0664, ls_fLevel_show, ls_fLevel_store); + +static int lightsensor_setup(struct cm3217_info *lpi) +{ + int ret; + + lpi->ls_input_dev = input_allocate_device(); + if (!lpi->ls_input_dev) { + pr_err( + "[LS][CM3217 error]%s: could not allocate ls input device\n", + __func__); + return -ENOMEM; + } + lpi->ls_input_dev->name = "cm3217-ls"; + set_bit(EV_ABS, lpi->ls_input_dev->evbit); + input_set_abs_params(lpi->ls_input_dev, ABS_MISC, 0, 9, 0, 0); + + ret = input_register_device(lpi->ls_input_dev); + if (ret < 0) { + pr_err("[LS][CM3217 error]%s: can not register ls input device\n", + __func__); + goto err_free_ls_input_device; + } + + ret = misc_register(&lightsensor_misc); + if (ret < 0) { + pr_err("[LS][CM3217 error]%s: can not register ls misc device\n", + __func__); + goto err_unregister_ls_input_device; + } + + return ret; + +err_unregister_ls_input_device: + input_unregister_device(lpi->ls_input_dev); +err_free_ls_input_device: + input_free_device(lpi->ls_input_dev); + return ret; +} + +static int cm3217_setup(struct cm3217_info *lpi) +{ + int ret = 0; + + als_power(1); + msleep(5); + + ret = _cm3217_I2C_Write_Byte(ALS_W_CMD2_addr, CM3217_ALS_WDM_DEFAULT_1 |CM3217_ALS_IT_2_T|CM3217_ALS_BIT5_Default_1); + if(ret<0) + return ret; + + ret = _cm3217_I2C_Write_Byte(ALS_W_CMD2_addr, CM3217_ALS_IT_100ms); + msleep(10); + + return ret; +} + +static void cm3217_early_suspend(struct early_suspend *h) +{ + struct cm3217_info *lpi = lp_info; + + D("[LS][CM3217] %s\n", __func__); + + if (lpi->als_enable) + lightsensor_disable(lpi); +} + +static void cm3217_late_resume(struct early_suspend *h) +{ + struct cm3217_info *lpi = lp_info; + + D("[LS][CM3217] %s\n", __func__); + + if (!lpi->als_enable) + lightsensor_enable(lpi); +} + +static int cm3217_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct cm3217_info *lpi; + struct cm3217_platform_data *pdata; + + D("[CM3217] %s\n", __func__); + + + lpi = kzalloc(sizeof(struct cm3217_info), GFP_KERNEL); + if (!lpi) + return -ENOMEM; + + /*D("[CM3217] %s: client->irq = %d\n", __func__, client->irq);*/ + + lpi->i2c_client = client; + pdata = client->dev.platform_data; + if (!pdata) { + pr_err("[CM3217 error]%s: Assign platform_data error!!\n", + __func__); + ret = -EBUSY; + goto err_platform_data_null; + } + + lpi->irq = client->irq; + + i2c_set_clientdata(client, lpi); + lpi->adc_table = pdata->levels; + lpi->golden_adc = pdata->golden_adc; + lpi->power = pdata->power; + + lpi->polling_delay = msecs_to_jiffies(LS_POLLING_DELAY); + + lp_info = lpi; + + mutex_init(&als_enable_mutex); + mutex_init(&als_disable_mutex); + mutex_init(&als_get_adc_mutex); + + ret = lightsensor_setup(lpi); + if (ret < 0) { + pr_err("[LS][CM3217 error]%s: lightsensor_setup error!!\n", + __func__); + goto err_lightsensor_setup; + } + + //SET LUX STEP FACTOR HERE + // if adc raw value one step = 0.3 lux + // the following will set the factor 0.3 = 3/10 + // and lpi->golden_adc = 3; + // set als_kadc = (ALS_CALIBRATED <<16) | 10; + + als_kadc = (ALS_CALIBRATED <<16) | 10; + lpi->golden_adc = 3; + + //ls calibrate always set to 1 + lpi->ls_calibrate = 1; + + lightsensor_set_kvalue(lpi); + ret = lightsensor_update_table(lpi); + if (ret < 0) { + pr_err("[LS][CM3217 error]%s: update ls table fail\n", + __func__); + goto err_lightsensor_update_table; + } + + lpi->lp_wq = create_singlethread_workqueue("cm3217_wq"); + if (!lpi->lp_wq) { + pr_err("[CM3217 error]%s: can't create workqueue\n", __func__); + ret = -ENOMEM; + goto err_create_singlethread_workqueue; + } + + ret = cm3217_setup(lpi); + if (ret < 0) { + pr_err("[ERR][CM3217 error]%s: cm3217_setup error!\n", __func__); + goto err_cm3217_setup; + } + + lpi->cm3217_class = class_create(THIS_MODULE, "optical_sensors"); + if (IS_ERR(lpi->cm3217_class)) { + ret = PTR_ERR(lpi->cm3217_class); + lpi->cm3217_class = NULL; + goto err_create_class; + } + + lpi->ls_dev = device_create(lpi->cm3217_class, + NULL, 0, "%s", "lightsensor"); + if (unlikely(IS_ERR(lpi->ls_dev))) { + ret = PTR_ERR(lpi->ls_dev); + lpi->ls_dev = NULL; + goto err_create_ls_device; + } + + /* register the attributes */ + ret = device_create_file(lpi->ls_dev, &dev_attr_ls_adc); + if (ret) + goto err_create_ls_device_file; + + /* register the attributes */ + ret = device_create_file(lpi->ls_dev, &dev_attr_ls_auto); + if (ret) + goto err_create_ls_device_file; + + /* register the attributes */ + ret = device_create_file(lpi->ls_dev, &dev_attr_ls_kadc); + if (ret) + goto err_create_ls_device_file; + + ret = device_create_file(lpi->ls_dev, &dev_attr_ls_gadc); + if (ret) + goto err_create_ls_device_file; + + ret = device_create_file(lpi->ls_dev, &dev_attr_ls_adc_table); + if (ret) + goto err_create_ls_device_file; + + ret = device_create_file(lpi->ls_dev, &dev_attr_ls_conf1); + if (ret) + goto err_create_ls_device_file; + + ret = device_create_file(lpi->ls_dev, &dev_attr_ls_conf2); + if (ret) + goto err_create_ls_device_file; + + ret = device_create_file(lpi->ls_dev, &dev_attr_ls_flevel); + if (ret) + goto err_create_ls_device_file; + + lpi->early_suspend.level = + EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + lpi->early_suspend.suspend = cm3217_early_suspend; + lpi->early_suspend.resume = cm3217_late_resume; + register_early_suspend(&lpi->early_suspend); + + lpi->als_enable=0; + D("[CM3217] %s: Probe success!\n", __func__); + + return ret; + +err_create_ls_device_file: + device_unregister(lpi->ls_dev); +err_create_ls_device: + class_destroy(lpi->cm3217_class); +err_create_class: +err_cm3217_setup: + destroy_workqueue(lpi->lp_wq); + mutex_destroy(&als_enable_mutex); + mutex_destroy(&als_disable_mutex); + mutex_destroy(&als_get_adc_mutex); + input_unregister_device(lpi->ls_input_dev); + input_free_device(lpi->ls_input_dev); +err_create_singlethread_workqueue: +err_lightsensor_update_table: + misc_deregister(&lightsensor_misc); +err_lightsensor_setup: +err_platform_data_null: + kfree(lpi); + return ret; +} + +static const struct i2c_device_id cm3217_i2c_id[] = { + {CM3217_I2C_NAME, 0}, + {} +}; + +static struct i2c_driver cm3217_driver = { + .id_table = cm3217_i2c_id, + .probe = cm3217_probe, + .driver = { + .name = CM3217_I2C_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init cm3217_init(void) +{ + return i2c_add_driver(&cm3217_driver); +} + +static void __exit cm3217_exit(void) +{ + i2c_del_driver(&cm3217_driver); +} + +module_init(cm3217_init); +module_exit(cm3217_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CM3217 Driver"); +MODULE_AUTHOR("Frank Hsieh <pengyueh@gmail.com>"); + + |