summaryrefslogtreecommitdiff
path: root/drivers/input/misc/cm3217.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/misc/cm3217.c')
-rw-r--r--drivers/input/misc/cm3217.c1025
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>");
+
+