Merge "input: sensor: add cm36283 sensor polling support"
diff --git a/drivers/input/misc/cm36283.c b/drivers/input/misc/cm36283.c
index 3dba330..75d67a0 100644
--- a/drivers/input/misc/cm36283.c
+++ b/drivers/input/misc/cm36283.c
@@ -56,7 +56,31 @@
 #define CM36283_VI2C_MIN_UV	1750000
 #define CM36283_VI2C_MAX_UV	1950000
 
+/* cm36283 polling rate in ms */
+#define CM36283_LS_MIN_POLL_DELAY	1
+#define CM36283_LS_MAX_POLL_DELAY	1000
+#define CM36283_LS_DEFAULT_POLL_DELAY	100
+
+#define CM36283_PS_MIN_POLL_DELAY	1
+#define CM36283_PS_MAX_POLL_DELAY	1000
+#define CM36283_PS_DEFAULT_POLL_DELAY	100
+
 static int record_init_fail = 0;
+
+static const int als_range[] = {
+	[CM36283_ALS_IT0] = 6554,
+	[CM36283_ALS_IT1] = 3277,
+	[CM36283_ALS_IT2] = 1638,
+	[CM36283_ALS_IT3] = 819,
+};
+
+static const int als_sense[] = {
+	[CM36283_ALS_IT0] = 10,
+	[CM36283_ALS_IT1] = 20,
+	[CM36283_ALS_IT2] = 40,
+	[CM36283_ALS_IT3] = 80,
+};
+
 static void sensor_irq_do_work(struct work_struct *work);
 static DECLARE_WORK(sensor_irq_work, sensor_irq_do_work);
 
@@ -104,15 +128,20 @@
 
 	uint16_t ls_cmd;
 	uint8_t record_clear_int_fail;
-
+	bool polling;
+	atomic_t ls_poll_delay;
+	atomic_t ps_poll_delay;
 	struct regulator *vdd;
 	struct regulator *vio;
+	struct delayed_work ldwork;
+	struct delayed_work pdwork;
 };
 struct cm36283_info *lp_info;
 int fLevel=-1;
 static struct mutex als_enable_mutex, als_disable_mutex, als_get_adc_mutex;
 static struct mutex ps_enable_mutex, ps_disable_mutex, ps_get_adc_mutex;
 static struct mutex CM36283_control_mutex;
+static struct mutex wq_lock;
 static int lightsensor_enable(struct cm36283_info *lpi);
 static int lightsensor_disable(struct cm36283_info *lpi);
 static int initial_cm36283(struct cm36283_info *lpi);
@@ -120,7 +149,8 @@
 
 int32_t als_kadc;
 
-static int control_and_report(struct cm36283_info *lpi, uint8_t mode, uint16_t param);
+static int control_and_report(struct cm36283_info *lpi, uint8_t mode,
+		uint16_t param, int report);
 
 static int I2C_RxData(uint16_t slaveAddr, uint8_t cmd, uint8_t *rxData, int length)
 {
@@ -385,12 +415,91 @@
 {
 	struct cm36283_info *lpi = lp_info;
 	uint16_t intFlag;
-  _cm36283_I2C_Read_Word(lpi->slave_addr, INT_FLAG, &intFlag);
-	control_and_report(lpi, CONTROL_INT_ISR_REPORT, intFlag);  
-	  
+	_cm36283_I2C_Read_Word(lpi->slave_addr, INT_FLAG, &intFlag);
+	control_and_report(lpi, CONTROL_INT_ISR_REPORT, intFlag, 1);
+
 	enable_irq(lpi->irq);
 }
 
+static int get_als_range(void)
+{
+	uint16_t ls_conf;
+	int ret = 0;
+	int index = 0;
+	struct cm36283_info *lpi = lp_info;
+
+	ret = _cm36283_I2C_Read_Word(lpi->slave_addr, ALS_CONF, &ls_conf);
+	if (ret) {
+		dev_err(&lpi->i2c_client->dev, "read ALS_CONF from i2c error. %d\n",
+				ret);
+		return -EIO;
+	}
+
+	index = (ls_conf & 0xC0) >> 0x06;
+	return  als_range[index];
+}
+
+static int get_als_sense(void)
+{
+	uint16_t ls_conf;
+	int ret = 0;
+	int index = 0;
+	struct cm36283_info *lpi = lp_info;
+
+	ret = _cm36283_I2C_Read_Word(lpi->slave_addr, ALS_CONF, &ls_conf);
+	if (ret) {
+		dev_err(&lpi->i2c_client->dev, "read ALS_CONF from i2c error. %d\n",
+				ret);
+		return -EIO;
+	}
+
+	index = (ls_conf & 0xC0) >> 0x06;
+	return  als_sense[index];
+}
+
+static void psensor_delay_work_handler(struct work_struct *work)
+{
+	struct cm36283_info *lpi = lp_info;
+	uint16_t adc_value = 0;
+	int ret;
+
+	mutex_lock(&wq_lock);
+
+	ret = get_ps_adc_value(&adc_value);
+
+	mutex_unlock(&wq_lock);
+
+	if (ret >= 0) {
+		input_report_abs(lpi->ps_input_dev, ABS_DISTANCE,
+				adc_value > lpi->ps_close_thd_set ? 0 : 1);
+		input_sync(lpi->ps_input_dev);
+	}
+	schedule_delayed_work(&lpi->pdwork,
+			msecs_to_jiffies(atomic_read(&lpi->ps_poll_delay)));
+}
+
+static void lsensor_delay_work_handler(struct work_struct *work)
+{
+	struct cm36283_info *lpi = lp_info;
+	uint16_t adc_value = 0;
+	int sense;
+
+	mutex_lock(&wq_lock);
+
+	get_ls_adc_value(&adc_value, 0);
+	sense = get_als_sense();
+
+	mutex_unlock(&wq_lock);
+
+	if (sense > 0) {
+		lpi->current_adc = adc_value;
+		input_report_abs(lpi->ls_input_dev, ABS_MISC, adc_value/sense);
+		input_sync(lpi->ls_input_dev);
+	}
+	schedule_delayed_work(&lpi->ldwork,
+			msecs_to_jiffies(atomic_read(&lpi->ls_poll_delay)));
+}
+
 static irqreturn_t cm36283_irq_handler(int irq, void *data)
 {
 	struct cm36283_info *lpi = data;
@@ -434,33 +543,44 @@
 static int psensor_enable(struct cm36283_info *lpi)
 {
 	int ret = -EIO;
+	unsigned int delay;
 	
 	mutex_lock(&ps_enable_mutex);
 	D("[PS][CM36283] %s\n", __func__);
 
-	if ( lpi->ps_enable ) {
+	if (lpi->ps_enable) {
 		D("[PS][CM36283] %s: already enabled\n", __func__);
 		ret = 0;
-	} else
-  	ret = control_and_report(lpi, CONTROL_PS, 1);
-	
+	} else {
+		ret = control_and_report(lpi, CONTROL_PS, 1, 0);
+	}
+
 	mutex_unlock(&ps_enable_mutex);
+
+	delay = atomic_read(&lpi->ps_poll_delay);
+	if (lpi->polling)
+		schedule_delayed_work(&lpi->pdwork, msecs_to_jiffies(delay));
+
 	return ret;
 }
 
 static int psensor_disable(struct cm36283_info *lpi)
 {
 	int ret = -EIO;
-	
+
+	if (lpi->polling)
+		cancel_delayed_work_sync(&lpi->pdwork);
+
 	mutex_lock(&ps_disable_mutex);
 	D("[PS][CM36283] %s\n", __func__);
 
-	if ( lpi->ps_enable == 0 ) {
+	if (lpi->ps_enable == 0) {
 		D("[PS][CM36283] %s: already disabled\n", __func__);
 		ret = 0;
-	} else
-  	ret = control_and_report(lpi, CONTROL_PS,0);
-	
+	} else {
+		ret = control_and_report(lpi, CONTROL_PS, 0, 0);
+	}
+
 	mutex_unlock(&ps_disable_mutex);
 	return ret;
 }
@@ -584,6 +704,7 @@
 static int lightsensor_enable(struct cm36283_info *lpi)
 {
 	int ret = -EIO;
+	unsigned int delay;
 	
 	mutex_lock(&als_enable_mutex);
 	D("[LS][CM36283] %s\n", __func__);
@@ -591,10 +712,17 @@
 	if (lpi->als_enable) {
 		D("[LS][CM36283] %s: already enabled\n", __func__);
 		ret = 0;
-	} else
-  	ret = control_and_report(lpi, CONTROL_ALS, 1);
+	} else {
+		ret = control_and_report(lpi, CONTROL_ALS, 1, 0);
+	}
 	
 	mutex_unlock(&als_enable_mutex);
+
+	delay = atomic_read(&lpi->ls_poll_delay);
+	if (lpi->polling)
+		schedule_delayed_work(&lpi->ldwork,
+				msecs_to_jiffies(delay));
+
 	return ret;
 }
 
@@ -604,11 +732,15 @@
 	mutex_lock(&als_disable_mutex);
 	D("[LS][CM36283] %s\n", __func__);
 
+	if (lpi->polling)
+		cancel_delayed_work_sync(&lpi->ldwork);
+
 	if ( lpi->als_enable == 0 ) {
 		D("[LS][CM36283] %s: already disabled\n", __func__);
 		ret = 0;
-	} else
-    ret = control_and_report(lpi, CONTROL_ALS, 0);
+	} else {
+		ret = control_and_report(lpi, CONTROL_ALS, 0, 0);
+	}
 	
 	mutex_unlock(&als_disable_mutex);
 	return ret;
@@ -691,13 +823,14 @@
 	uint16_t value;
 	int ret;
 	struct cm36283_info *lpi = lp_info;
-	int intr_val;
-
-	intr_val = gpio_get_value(lpi->intr_pin);
+	int intr_val = -1;
 
 	get_ps_adc_value(&value);
+	if (gpio_is_valid(lpi->intr_pin))
+		intr_val = gpio_get_value(lpi->intr_pin);
 
-	ret = sprintf(buf, "ADC[0x%04X], ENABLE = %d, intr_pin = %d\n", value, lpi->ps_enable, intr_val);
+	ret = snprintf(buf, PAGE_SIZE, "ADC[0x%04X], ENABLE=%d intr_pin=%d\n",
+			value, lpi->ps_enable, intr_val);
 
 	return ret;
 }
@@ -1079,6 +1212,62 @@
 }
 static DEVICE_ATTR(ls_conf, 0664, ls_conf_show, ls_conf_store);
 
+static ssize_t ls_poll_delay_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct cm36283_info *lpi = lp_info;
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			atomic_read(&lpi->ls_poll_delay));
+}
+
+static ssize_t ls_poll_delay_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct cm36283_info *lpi = lp_info;
+	unsigned long interval_ms;
+
+	if (kstrtoul(buf, 10, &interval_ms))
+		return -EINVAL;
+
+	if ((interval_ms < CM36283_LS_MIN_POLL_DELAY) ||
+			(interval_ms > CM36283_LS_MAX_POLL_DELAY))
+		return -EINVAL;
+
+	atomic_set(&lpi->ls_poll_delay, (unsigned int) interval_ms);
+	return count;
+}
+
+static DEVICE_ATTR(ls_poll_delay, 0664, ls_poll_delay_show,
+		ls_poll_delay_store);
+
+static ssize_t ps_poll_delay_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct cm36283_info *lpi = lp_info;
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			atomic_read(&lpi->ps_poll_delay));
+}
+
+static ssize_t ps_poll_delay_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct cm36283_info *lpi = lp_info;
+	unsigned long interval_ms;
+
+	if (kstrtoul(buf, 10, &interval_ms))
+		return -EINVAL;
+
+	if ((interval_ms < CM36283_PS_MIN_POLL_DELAY) ||
+			(interval_ms > CM36283_PS_MAX_POLL_DELAY))
+		return -EINVAL;
+
+	atomic_set(&lpi->ps_poll_delay, (unsigned int) interval_ms);
+	return count;
+}
+
+static DEVICE_ATTR(ps_poll_delay, 0664, ps_poll_delay_show,
+		ps_poll_delay_store);
+
 static ssize_t ls_fLevel_show(struct device *dev,
 				  struct device_attribute *attr, char *buf)
 {
@@ -1106,6 +1295,7 @@
 static int lightsensor_setup(struct cm36283_info *lpi)
 {
 	int ret;
+	int range;
 
 	lpi->ls_input_dev = input_allocate_device();
 	if (!lpi->ls_input_dev) {
@@ -1116,7 +1306,9 @@
 	}
 	lpi->ls_input_dev->name = "cm36283-ls";
 	set_bit(EV_ABS, lpi->ls_input_dev->evbit);
-	input_set_abs_params(lpi->ls_input_dev, ABS_MISC, 0, 9, 0, 0);
+
+	range = get_als_range();
+	input_set_abs_params(lpi->ls_input_dev, ABS_MISC, 0, range, 0, 0);
 
 	ret = input_register_device(lpi->ls_input_dev);
 	if (ret < 0) {
@@ -1191,13 +1383,8 @@
 	D("[PS][CM36283] %s, INTERRUPT GPIO val = %d\n", __func__, val);
 
 	ret = _cm36283_I2C_Read_Word(lpi->slave_addr, ID_REG, &idReg);
-	if ((ret < 0) || (idReg != 0xC082)) {
-  		if (record_init_fail == 0)
-  			record_init_fail = 1;
-  		return -ENOMEM;/*If devices without cm36283 chip and did not probe driver*/	
-  }
-  
-	return 0;
+
+	return ret;
 }
 
 static int cm36283_setup(struct cm36283_info *lpi)
@@ -1231,14 +1418,15 @@
 	}
 	
 	/*Default disable P sensor and L sensor*/
-  ls_initial_cmd(lpi);
+	ls_initial_cmd(lpi);
 	psensor_initial_cmd(lpi);
 
-	ret = request_any_context_irq(lpi->irq,
-			cm36283_irq_handler,
-			IRQF_TRIGGER_LOW,
-			"cm36283",
-			lpi);
+	if (!lpi->polling)
+		ret = request_any_context_irq(lpi->irq,
+				cm36283_irq_handler,
+				IRQF_TRIGGER_LOW,
+				"cm36283",
+				lpi);
 	if (ret < 0) {
 		pr_err(
 			"[PS][CM36283 error]%s: req_irq(%d) fail for gpio %d (%d)\n",
@@ -1405,6 +1593,12 @@
 	lpi->ps_close_thd_set = pdata->ps_close_thd_set;	
 	lpi->ps_conf1_val = pdata->ps_conf1_val;
 	lpi->ps_conf3_val = pdata->ps_conf3_val;
+	lpi->polling = pdata->polling;
+	atomic_set(&lpi->ls_poll_delay,
+			(unsigned int) CM36283_LS_DEFAULT_POLL_DELAY);
+	atomic_set(&lpi->ps_poll_delay,
+			(unsigned int) CM36283_PS_DEFAULT_POLL_DELAY);
+
 	
 	lpi->ls_cmd  = pdata->ls_cmd;
 	
@@ -1522,6 +1716,10 @@
 	if (ret)
 		goto err_create_ls_device_file;
 
+	ret = device_create_file(lpi->ls_dev, &dev_attr_ls_poll_delay);
+	if (ret)
+		goto err_create_ls_device_file;
+
 	lpi->ps_dev = device_create(lpi->cm36283_class,
 				NULL, 0, "%s", "proximity");
 	if (unlikely(IS_ERR(lpi->ps_dev))) {
@@ -1554,6 +1752,10 @@
 	if (ret)
 		goto err_create_ps_device;
 
+	ret = device_create_file(lpi->ps_dev, &dev_attr_ps_poll_delay);
+	if (ret)
+		goto err_create_ps_device;
+
 #ifdef CONFIG_HAS_EARLYSUSPEND
 	lpi->early_suspend.level =
 			EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
@@ -1562,6 +1764,9 @@
 	register_early_suspend(&lpi->early_suspend);
 #endif
 
+	mutex_init(&wq_lock);
+	INIT_DELAYED_WORK(&lpi->ldwork, lsensor_delay_work_handler);
+	INIT_DELAYED_WORK(&lpi->pdwork, psensor_delay_work_handler);
 	D("[PS][CM36283] %s: Probe success!\n", __func__);
 
 	return ret;
@@ -1601,11 +1806,13 @@
 	kfree(lpi);
 	return ret;
 }
-   
-static int control_and_report( struct cm36283_info *lpi, uint8_t mode, uint16_t param ) {
-  int ret=0;
+
+static int control_and_report(struct cm36283_info *lpi, uint8_t mode,
+	uint16_t param, int report)
+{
+	int ret = 0;
 	uint16_t adc_value = 0;
-	uint16_t ps_data = 0;	
+	uint16_t ps_data = 0;
 	int level = 0, i, val;
 	
   mutex_lock(&CM36283_control_mutex);
@@ -1670,63 +1877,62 @@
       		  }
       	  }
     	  }
-    
-    	  ret = set_lsensor_range(((i == 0) || (adc_value == 0)) ? 0 :
-    		   	*(lpi->cali_table + (i - 1)) + 1,
-    		    *(lpi->cali_table + i));
-    	  
-        lpi->ls_cmd |= CM36283_ALS_INT_EN;
-    	  
-        ret = _cm36283_I2C_Write_Word(lpi->slave_addr, ALS_CONF, lpi->ls_cmd);  
-    	  
-    		if ((i == 0) || (adc_value == 0))
-    			D("[LS][CM3628] %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][CM3628] %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;    
-        input_report_abs(lpi->ls_input_dev, ABS_MISC, level);
-        input_sync(lpi->ls_input_dev);
+	if (!lpi->polling) {
+		ret = set_lsensor_range(((i == 0) ||
+					(adc_value == 0)) ? 0 :
+				*(lpi->cali_table + (i - 1)) + 1,
+				*(lpi->cali_table + i));
+
+		lpi->ls_cmd |= CM36283_ALS_INT_EN;
+	}
+
+	ret = _cm36283_I2C_Write_Word(lpi->slave_addr, ALS_CONF,
+			lpi->ls_cmd);
+
+	if (report) {
+		lpi->current_level = level;
+		lpi->current_adc = adc_value;
+		input_report_abs(lpi->ls_input_dev, ABS_MISC, level);
+		input_sync(lpi->ls_input_dev);
+	}
     }
   }
 
 #define PS_CLOSE 1
 #define PS_AWAY  (1<<1)
 #define PS_CLOSE_AND_AWAY PS_CLOSE+PS_AWAY
+	if (report && (lpi->ps_enable)) {
+		int ps_status = 0;
+		if (mode == CONTROL_PS)
+			ps_status = PS_CLOSE_AND_AWAY;
+		else if (mode == CONTROL_INT_ISR_REPORT) {
+			if (param & INT_FLAG_PS_IF_CLOSE)
+				ps_status |= PS_CLOSE;
+			if (param & INT_FLAG_PS_IF_AWAY)
+				ps_status |= PS_AWAY;
+		}
 
-  if(lpi->ps_enable){
-    int ps_status = 0;
-    if( mode == CONTROL_PS )
-      ps_status = PS_CLOSE_AND_AWAY;   
-    else if(mode == CONTROL_INT_ISR_REPORT ){  
-      if ( param & INT_FLAG_PS_IF_CLOSE )
-        ps_status |= PS_CLOSE;      
-      if ( param & INT_FLAG_PS_IF_AWAY )
-        ps_status |= PS_AWAY;
-    }
-      
-    if (ps_status!=0){
-      switch(ps_status){
-        case PS_CLOSE_AND_AWAY:
-          get_stable_ps_adc_value(&ps_data);
-          val = (ps_data >= lpi->ps_close_thd_set) ? 0 : 1;
-          break;
-        case PS_AWAY:
-          val = 1;
-          break;
-        case PS_CLOSE:
-          val = 0;
-          break;
-        };
-      input_report_abs(lpi->ps_input_dev, ABS_DISTANCE, val);      
-      input_sync(lpi->ps_input_dev);        
-    }
-  }
+		if (ps_status != 0) {
+			switch (ps_status) {
+			case PS_CLOSE_AND_AWAY:
+				get_stable_ps_adc_value(&ps_data);
+				val = (ps_data >= lpi->ps_close_thd_set)
+					? 0 : 1;
+				break;
+			case PS_AWAY:
+				val = 1;
+				break;
+			case PS_CLOSE:
+				val = 0;
+				break;
+			};
+			input_report_abs(lpi->ps_input_dev, ABS_DISTANCE, val);
+			input_sync(lpi->ps_input_dev);
+		}
+	}
 
-  mutex_unlock(&CM36283_control_mutex);
-  return ret;
+	mutex_unlock(&CM36283_control_mutex);
+	return ret;
 }
 
 static int cm36283_power_set(struct cm36283_info *info, bool on)
diff --git a/include/linux/cm36283.h b/include/linux/cm36283.h
index 58abbe1..2872d04 100644
--- a/include/linux/cm36283.h
+++ b/include/linux/cm36283.h
@@ -122,6 +122,13 @@
 
 #define CM36283_LEVELS_SIZE		10
 
+enum {
+	CM36283_ALS_IT0 = 0,
+	CM36283_ALS_IT1,
+	CM36283_ALS_IT2,
+	CM36283_ALS_IT3,
+};
+
 struct cm36283_platform_data {
 	int intr;
 	uint16_t levels[10];