android_kernel_samsung_msm8976/drivers/sensors/tmd37xx.c

2561 lines
68 KiB
C

/*
* Copyright (c) 2010 SAMSUNG
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/gpio.h>
#include <linux/wakelock.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/workqueue.h>
#include <linux/uaccess.h>
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/sensor/sensors_core.h>
#ifdef CONFIG_SENSORS_TMD3782
#include "tmd3782.h"
#endif
#ifdef CONFIG_SENSORS_TMD3700
#include "tmd37xx.h"
#endif
/* Note about power vs enable/disable:
* The chip has two functions, proximity and ambient light sensing.
* There is no separate power enablement to the two functions (unlike
* the Capella CM3602/3623).
* This module implements two drivers: /dev/proximity and /dev/light.
* When either driver is enabled (via sysfs attributes), we give power
* to the chip. When both are disabled, we remove power from the chip.
* In suspend, we remove power if light is disabled but not if proximity is
* enabled (proximity is allowed to wakeup from suspend).
*
* There are no ioctls for either driver interfaces. Output is via
* input device framework and control via sysfs attributes.
*/
/* taos debug */
#define MODULE_NAME_PROX "proximity_sensor"
#define VENDOR_NAME "TAOS"
#ifdef CONFIG_SENSORS_TMD3700
#define CHIP_NAME "TMD3700"
#define CHIP_ID 0xC0
#endif
#ifdef CONFIG_SENSORS_TMD3782
#define CHIP_NAME "TMD3782"
#define CHIP_ID 0x69
#endif
#define RETRY_REBOOT 3
/*lightsnesor log time 6SEC 200mec X 30*/
#define LIGHT_LOG_TIME 30
/* sensor type */
#define LIGHT 0
#define PROXIMITY 1
#define ALL 2
enum {
LIGHT_ENABLED = BIT(0),
PROXIMITY_ENABLED = BIT(1),
};
enum {
STATE_CLOSE = 0,
STATE_FAR = 1,
};
enum {
OFF = 0,
ON = 1,
};
#if defined(CONFIG_MACH_A7LTE)
#define Atime_ms 504 //50.4 ms
#define DGF 1575
#define R_Coef1 (80)
#define G_Coef1 (1000)
#define B_Coef1 (-380)
#define CT_Coef1 (3541)
#define CT_Offset1 (2035)
#define IR_R_Coef1 (-1)
#define IR_G_Coef1 (109)
#define IR_B_Coef1 (-29)
#define IR_C_Coef1 (57)
#define IR_Coef1 (38)
#define INTEGRATION_CYCLE 240
#define CAL_SKIP_ADC 204
#define CAL_FAIL_ADC 480
#elif defined(CONFIG_SENSORS_TMD3782)
#define Atime_ms 504 //50.4 ms
#define DGF 642
#define R_Coef1 (330)
#define G_Coef1 (1000)
#define B_Coef1 (150)
#define CT_Coef1 (3210)
#define CT_Offset1 (1788)
#define IR_R_Coef1 (-1)
#define IR_G_Coef1 (109)
#define IR_B_Coef1 (-29)
#define IR_C_Coef1 (57)
#define IR_Coef1 (38)
#define INTEGRATION_CYCLE 240
#define CAL_SKIP_ADC 325
#define CAL_FAIL_ADC 440
#elif defined(CONFIG_SENSORS_TMD3700)
#define Atime_ms 1596 /* 159.6ms */
#define DGF 1200 //1200
#define R_Coef1 (270) // 0.27
#define G_Coef1 (1000)// 1
#define B_Coef1 (90) // 0.09
#define IR_R_Coef1 (-1)
#define IR_G_Coef1 (109)
#define IR_B_Coef1 (-29)
#define IR_C_Coef1 (57)
#define IR_Coef1 (38)
#define CT_Coef1 (3193) //3193
#define CT_Offset1 (1950) //1950
#define INTEGRATION_CYCLE 240
#define CAL_SKIP_ADC 42
#define CAL_FAIL_ADC 110
#define POFFSET_TRIM 20
#endif
#define ADC_BUFFER_NUM 6
#define PROX_AVG_COUNT 40
#define MAX_LUX 150000
#define TAOS_PROX_MAX 1023
#define TAOS_PROX_MIN 0
#define OFFSET_ARRAY_LENGTH 10
#define OFFSET_FILE_PATH "/efs/FactoryApp/prox_cal"
#ifdef CONFIG_PROX_WINDOW_TYPE
#define WINDOW_TYPE_FILE_PATH "/sys/class/sec/sec_touch_ic/window_type"
#endif
/* driver data */
struct taos_data {
struct i2c_client *i2c_client;
struct taos_platform_data *pdata;
struct input_dev *proximity_input_dev;
struct input_dev *light_input_dev;
struct device *light_dev;
struct device *proximity_dev;
struct work_struct work_light;
struct work_struct work_prox;
struct work_struct work_prox_avg;
struct mutex prox_mutex;
struct mutex power_lock;
struct wake_lock prx_wake_lock;
struct hrtimer timer;
struct hrtimer prox_avg_timer;
struct workqueue_struct *wq;
struct workqueue_struct *wq_avg;
ktime_t light_poll_delay;
ktime_t prox_polling_time;
u8 power_state;
int irq;
bool adc_buf_initialized;
int adc_value_buf[ADC_BUFFER_NUM];
int adc_index_count;
int avg[3];
int prox_avg_enable;
s32 clrdata;
s32 reddata;
s32 grndata;
s32 bludata;
s32 irdata;
int lux;
/* Auto Calibration */
u16 offset_value;
int cal_result;
int threshold_high;
int threshold_low;
int proximity_value;
bool set_manual_thd;
int count_log_time;
#ifdef CONFIG_SENSORS_TMD3700_RESET_DEFENCE_CODE
u8 vdd_reset;
#endif
#ifdef CONFIG_PROX_WINDOW_TYPE
char windowtype[2];
#endif
struct regulator *leda_3p3;
};
static void taos_thresh_set(struct taos_data *taos);
static int proximity_get_adc(struct taos_data *taos);
static int lightsensor_get_adcvalue(struct taos_data *taos);
static int proximity_open_offset(struct taos_data *data);
static int proximity_adc_read(struct taos_data *taos);
#ifdef CONFIG_PROX_WINDOW_TYPE
static int proximity_open_window_type(struct taos_data *data);
#endif
#ifdef CONFIG_SENSORS_TMD3700
static int tmd3700_interrupt_clear(struct taos_data *taos);
#endif
static int leda_regulator_onoff(struct taos_data *data, int onoff)
{
int err;
if(data->pdata->vled_ldo)
{
gpio_direction_output(data->pdata->vled_ldo, onoff);
return 0;
} else {
if(!data->leda_3p3)
{
#ifdef CONFIG_SENSORS_TMD3700
data->leda_3p3 = regulator_get(&data->i2c_client->dev, "reg_vio");
#endif
#ifdef CONFIG_SENSORS_TMD3782
data->leda_3p3 = regulator_get(NULL, "GES_LED_3P3");
#endif
if (IS_ERR(data->leda_3p3)) {
SENSOR_ERR("regulator_get fail\n");
goto err_3p3;
}
}
SENSOR_INFO("onoff = %d\n", onoff);
if(IS_ERR(data->leda_3p3)) {
SENSOR_ERR("regulator is null\n");
goto err_3p3;
}
if (onoff) {
err = regulator_enable(data->leda_3p3);
if (err < 0) {
SENSOR_ERR("enable vdd_1p8 fail\n");
}
} else {
if (regulator_is_enabled(data->leda_3p3)) {
err = regulator_disable(data->leda_3p3);
if (err)
SENSOR_ERR("error lvs1_1p8 disabling regulator\n");
}
}
return 0;
err_3p3:
return -ENODEV;
}
}
#ifdef CONFIG_SENSORS_TMD3782
static int opt_i2c_read(struct taos_data *taos, u8 reg , u8 *val)
{
int ret;
i2c_smbus_write_byte(taos->i2c_client, (CMD_REG | reg));
ret = i2c_smbus_read_byte(taos->i2c_client);
*val = ret;
return ret;
}
static int opt_i2c_write_command(struct taos_data *taos, u8 val)
{
int ret;
ret = i2c_smbus_write_byte(taos->i2c_client, val);
SENSOR_INFO("[TAOS Command] val=[0x%x] - ret=[0x%x]\n", val, ret);
return ret;
}
#else
#ifdef CONFIG_SEC_FACTORY
static int opt_i2c_read(struct taos_data *taos, u8 reg , u8 *val)
{
int ret;
i2c_smbus_write_byte(taos->i2c_client, (CMD_REG | reg));
ret = i2c_smbus_read_byte(taos->i2c_client);
*val = ret;
return ret;
}
#endif //CONFIG_SEC_FACTORY
#endif //CONFIG_SENSORS_TMD3782
static int opt_i2c_write(struct taos_data *taos, u8 reg, u8 *val)
{
int ret;
int retry = 3;
do {
ret = i2c_smbus_write_byte_data(taos->i2c_client,
(CMD_REG | reg), *val);
if (ret < 0) {
SENSOR_ERR("%d\n", ret);
usleep_range(1000, 1100);
} else
return ret;
} while (retry--);
SENSOR_ERR("i2c write failed, ret = %d\n", ret);
return ret;
}
static int proximity_get_adc(struct taos_data *taos)
{
int adc = 0;
#ifdef CONFIG_SENSORS_TMD3700_RESET_DEFENCE_CODE
if(taos->vdd_reset)
{
SENSOR_INFO("vdd: turned off.\n");
return -1;
}
#endif
#ifdef CONFIG_SENSORS_TMD3700
adc = i2c_smbus_read_byte_data(taos->i2c_client, CMD_REG |PRX_DATA_HIGH );
#else
adc = i2c_smbus_read_word_data(taos->i2c_client, CMD_REG | PRX_LO );
#endif
if (adc < taos->pdata->prox_rawdata_trim)
return TAOS_PROX_MIN;
if (adc > TAOS_PROX_MAX)
adc = TAOS_PROX_MAX;
return adc - taos->pdata->prox_rawdata_trim;
}
static int taos_proximity_get_threshold(struct taos_data *taos, u8 buf)
{
#ifdef CONFIG_SENSORS_TMD3700
u16 threshold;
threshold = i2c_smbus_read_byte_data(taos->i2c_client, (CMD_REG | buf));
SENSOR_INFO("threshold = %d,trim = %d,buf=%x\n",threshold,taos->pdata->prox_rawdata_trim,buf);
if ((threshold == 0xFF) || (threshold == 0))
return threshold;
#endif //CONFIG_SENSORS_TMD3700
#ifdef CONFIG_SENSORS_TMD3782
int threshold;
threshold = i2c_smbus_read_word_data(taos->i2c_client, (CMD_REG | buf));
SENSOR_INFO("threshold = %d,trim = %d,buf=%x\n",threshold,taos->pdata->prox_rawdata_trim,buf);
if ((threshold == 0xFFFF) || (threshold == 0))
return (int)threshold;
#endif //CONFIG_SENSORS_TMD3782
return (int)threshold - taos->pdata->prox_rawdata_trim;
}
static void taos_thresh_set(struct taos_data *taos)
{
int ret = 0;
u16 trim = (u16)taos->pdata->prox_rawdata_trim;
/* Setting for proximity interrupt */
#ifdef CONFIG_SENSORS_TMD3700
u8 prox_int_thresh[2] = {0,};
if (taos->proximity_value == STATE_CLOSE) {
// Low -> threshod_low , High -> 0xff
prox_int_thresh[0] = taos->threshold_low+trim;
prox_int_thresh[1] = 0xFF;
} else {
// Low -> 0, Hight -> threshod_high
prox_int_thresh[0] = 0;
prox_int_thresh[1] = taos->threshold_high+trim;
}
ret = opt_i2c_write(taos, (CMD_REG|PRX_MINTHRESH),
&prox_int_thresh[0]);
if (ret < 0)
SENSOR_ERR("opt_i2c_write failed, err = %d\n", ret);
ret = opt_i2c_write(taos, (CMD_REG|(PRX_MAXTHRESH)),
&prox_int_thresh[1]);
if (ret < 0)
SENSOR_ERR("opt_i2c_write failed, err = %d\n", ret);
#endif //CONFIG_SENSORS_TMD3700
#ifdef CONFIG_SENSORS_TMD3782
u8 i, prox_int_thresh[4] = {0,};
if (taos->proximity_value == STATE_CLOSE) {
prox_int_thresh[0] = ((u16)taos->threshold_low+trim) & 0xFF;
prox_int_thresh[1] = ((taos->threshold_low+trim) >> 8) & 0xFF;
prox_int_thresh[2] = (0xFFFF) & 0xFF;
prox_int_thresh[3] = (0xFFFF >> 8) & 0xFF;
} else {
prox_int_thresh[0] = (0x0000) & 0xFF;
prox_int_thresh[1] = (0x0000 >> 8) & 0xFF;
prox_int_thresh[2] = ((u16)taos->threshold_high+trim) & 0xFF;
prox_int_thresh[3] = (((u16)taos->threshold_high+trim) >> 8) & 0xFF;
}
for (i = 0; i < 4; i++) {
ret = opt_i2c_write(taos,
(CMD_REG|(PRX_MINTHRESHLO + i)),
&prox_int_thresh[i]);
if (ret < 0)
SENSOR_ERR("opt_i2c_write failed, err = %d\n", ret);
}
#endif //CONFIG_SENSORS_TMD3782
}
#ifdef CONFIG_SENSORS_TMD3700
static int taos_chip_enable(struct taos_data *taos)
{
u8 temp_val;
int ret = 0;
temp_val = taos->pdata->prox_thresh_low;
ret = opt_i2c_write(taos, (CMD_REG|PRX_MINTHRESH), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to prox gain reg failed\n");
temp_val = taos->pdata->prox_thresh_hi;
ret = opt_i2c_write(taos, (CMD_REG|PRX_MAXTHRESH), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to prox gain reg failed\n");
temp_val = taos->pdata->als_gain;//AGAIN Default 2(x16)
ret = opt_i2c_write(taos, (CMD_REG|CFG1), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to prox gain reg failed\n");
temp_val = 0x20; //asein|psien|pien|aien|cien|0|0|0
ret = opt_i2c_write(taos, (CMD_REG |INTENAB), &temp_val);
temp_val = CMD_pen | CMD_pon |CMD_aen |CMD_wen; // wen|pen|aen|pon, Proximity , als , poweron enable
ret = opt_i2c_write(taos, (CMD_REG), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to clr ctrl reg failed\n");
SENSOR_INFO("\n");
return ret;
}
#endif
static int taos_chip_on(struct taos_data *taos)
{
#ifdef CONFIG_SENSORS_TMD3700
int ret = 0;
u8 temp_val;
/********************/
/* Apply factory trim **/
/*********************/
u8 ppulse_len;
u8 ppulse;
u8 pgain;
u8 pldrive;
u8 pre_offset;
int poffset;
char pre_offset_sign;
/*********************/
/* END **/
/*********************/
SENSOR_INFO("dgf:%d, cctcoef:%d, cctoff:%d, r:%d, g:%d, b:%d\n",
taos->pdata->dgf, taos->pdata->cct_coef, taos->pdata->cct_offset,
taos->pdata->coef_r, taos->pdata->coef_g, taos->pdata->coef_b);
tmd3700_interrupt_clear(taos);
temp_val = taos->pdata->prox_thresh_low;
ret = opt_i2c_write(taos, (CMD_REG|PRX_MINTHRESH), &temp_val);
temp_val = taos->pdata->prox_thresh_hi;
ret = opt_i2c_write(taos, (CMD_REG|PRX_MAXTHRESH), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to interrupt reg failed\n");
temp_val = 0x10;// proximity upper 4bit | als lower 4bit : proximity cycle -any, als cycle 0 every time
ret = opt_i2c_write(taos, (CMD_REG|PPERS), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to interrupt reg failed\n");
temp_val = 0x8c;//ppulse_len: 16us , ppluse : 13
ppulse_len = (temp_val >> 6) & 0x03;
ppulse = (temp_val) & 0x3F;
ret = opt_i2c_write(taos, (CMD_REG|PGCFG0), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to prox cfg reg failed\n");
temp_val = 0x90;//pagin 4x , pgldrive 102mA
pgain = (temp_val >> 6) & 0x03;
pldrive = (temp_val) & 0x1F;
ret = opt_i2c_write(taos, (CMD_REG|PGCFG1), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to prox cfg reg failed\n");
temp_val = P_TIME_US(20000);//4//20mSec , (pTime +1) * 88us
ret = opt_i2c_write(taos, (CMD_REG|PRX_RATE), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to prox cfg reg failed\n");
temp_val = 0x7f;//// one-shot autozero
ret = opt_i2c_write(taos, (CMD_REG|AZ_CONFIG), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to prox cfg reg failed\n");
temp_val = taos->pdata->als_gain;//AGAIN Default 2(x16)
ret = opt_i2c_write(taos, (CMD_REG|CFG1), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to prox gain reg failed\n");
temp_val = taos->pdata->als_time;
ret = opt_i2c_write(taos, (CMD_REG|ALS_TIME), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to als time reg failed\n");
temp_val = 0x20; //asein|psien|pien|aien|cien|0|0|0
ret = opt_i2c_write(taos, (CMD_REG |INTENAB), &temp_val);
temp_val = 0x00; //wtime 2.8 msec
ret = opt_i2c_write(taos, (CMD_REG |WAIT_TIME), &temp_val);
/*********************/
/* Apply factory trim **/
/*********************/
// 1 . Read Factory trim Mag
pre_offset = i2c_smbus_read_byte_data(taos->i2c_client, 0xE6);
// 2 . Read Factory trim Sign
pre_offset_sign = i2c_smbus_read_byte_data(taos->i2c_client, 0xE7);
// 3 . Calculate poffset
if(pre_offset_sign > 0)
poffset = (-1)*(pre_offset * (( (1<<ppulse_len) * (ppulse + 1) * (1<<pgain) * (pldrive + 1) ) / 3264));
else
poffset = pre_offset * (( (1<<ppulse_len) * (ppulse + 1) * (1<<pgain) * (pldrive + 1) ) / 3264);
poffset = POFFSET_TRIM + poffset;
if(poffset > 0)
pre_offset_sign = 0x00;
else {
poffset = (-1) * poffset;
pre_offset_sign = 0x01;
}
SENSOR_INFO("poffset= %d, pre_poffset= %d, ppulse= %d, ppulse_len= %d\n",poffset,pre_offset,ppulse,ppulse_len);
SENSOR_INFO("pre_offset_sign= %d, pgain= %d, pldrive= %d, 2<<pgain= %d\n",pre_offset_sign,pgain,pldrive,2<<(pgain-1));
temp_val = (u8)poffset;
ret = opt_i2c_write(taos,POFFSET_L, &temp_val);
temp_val = pre_offset_sign;
ret = opt_i2c_write(taos,POFFSET_H, &temp_val);
/*********************/
/* END **/
/*********************/
temp_val = CMD_pen | CMD_pon |CMD_aen |CMD_wen; // wen|pen|aen|pon, Proximity , als , poweron enable
ret = opt_i2c_write(taos, (CMD_REG), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to clr ctrl reg failed\n");
SENSOR_INFO("th_min: %d , th_max: %d, als_gain: %d\n", taos->pdata->prox_thresh_low,taos->pdata->prox_thresh_hi,taos->pdata->als_gain );
return ret;
#else
int ret = 0;
u8 temp_val;
u8 reg_cntrl;
leda_regulator_onoff(taos,ON);
temp_val = CNTL_PWRON;
ret = opt_i2c_write(taos, (CMD_REG|CNTRL), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to clr ctrl reg failed\n");
usleep_range(3000, 3100); // A minimum interval of 2.4ms must pass after PON is enabled.
temp_val = taos->pdata->als_time;
ret = opt_i2c_write(taos, (CMD_REG|ALS_TIME), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to als time reg failed\n");
temp_val = 0xff;
ret = opt_i2c_write(taos, (CMD_REG|WAIT_TIME), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to wait time reg failed\n");
temp_val = taos->pdata->intr_filter;
ret = opt_i2c_write(taos, (CMD_REG|INTERRUPT), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to interrupt reg failed\n");
temp_val = 0x0;
ret = opt_i2c_write(taos, (CMD_REG|PRX_CFG), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to prox cfg reg failed\n");
temp_val = taos->pdata->prox_pulsecnt;
ret = opt_i2c_write(taos, (CMD_REG|PRX_COUNT), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to prox cnt reg failed\n");
temp_val = taos->pdata->als_gain;
ret = opt_i2c_write(taos, (CMD_REG|GAIN), &temp_val);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to prox gain reg failed\n");
reg_cntrl = CNTL_INTPROXPON_ENBL;
ret = opt_i2c_write(taos, (CMD_REG|CNTRL), &reg_cntrl);
if (ret < 0)
SENSOR_ERR("opt_i2c_write to ctrl reg failed\n");
return ret;
#endif
}
static int taos_chip_off(struct taos_data *taos)
{
#ifdef CONFIG_SENSORS_TMD3700
int ret = 0;
u8 reg_cntrl;
SENSOR_INFO("\n");
tmd3700_interrupt_clear(taos);
reg_cntrl = CNTL_REG_CLEAR; //asein|psien|pien|aien|cien|0|0|0
ret = opt_i2c_write(taos, (CMD_REG |INTENAB), &reg_cntrl);
if (ret < 0) {
SENSOR_ERR("opt_i2c_write to ctrl reg failed\n");
return ret;
}
//ret = i2c_smbus_read_byte_data(taos->i2c_client, CMD_REG | STATUS);//read status
//SENSOR_ERR(" status [%x] \n", ret);
reg_cntrl = CNTL_PWRON;
ret = opt_i2c_write(taos, (CMD_REG), &reg_cntrl);
if (ret < 0) {
SENSOR_ERR("opt_i2c_write to ctrl reg failed\n");
return ret;
}
#else
int ret = 0;
u8 reg_cntrl;
reg_cntrl = CNTL_REG_CLEAR;
ret = opt_i2c_write(taos, (CMD_REG | CNTRL), &reg_cntrl);
if (ret < 0) {
SENSOR_ERR("opt_i2c_write to ctrl reg failed\n");
return ret;
}
leda_regulator_onoff(taos,OFF);
#endif
return ret;
}
static int taos_get_cct(struct taos_data *taos)
{
int bp1 = taos->bludata - taos->irdata;
int rp1 = taos->reddata - taos->irdata;
int cct = 0;
if (rp1 != 0)
cct = taos->pdata->cct_coef * bp1 / rp1 + taos->pdata->cct_offset;
return cct;
}
static int taos_get_lux(struct taos_data *taos)
{
s32 rp1, gp1, bp1;
s32 clrdata = 0;
s32 reddata = 0;
s32 grndata = 0;
s32 bludata = 0;
s32 calculated_lux = 0;
u8 reg_gain = 0x0;
u16 temp_gain = 0x0;
int gain = 1;
int ret = 0;
u8 get_reg = 0;
#ifdef CONFIG_SENSORS_TMD3700
get_reg = CFG1;
#endif
#ifdef CONFIG_SENSORS_TMD3782
get_reg = GAIN;
#endif
temp_gain = i2c_smbus_read_word_data(taos->i2c_client,
(CMD_REG | get_reg));
reg_gain = temp_gain & 0xff;
clrdata = i2c_smbus_read_word_data(taos->i2c_client,
(CMD_REG | CLR_CHAN0LO));
reddata = i2c_smbus_read_word_data(taos->i2c_client,
(CMD_REG | RED_CHAN1LO));
grndata = i2c_smbus_read_word_data(taos->i2c_client,
(CMD_REG | GRN_CHAN1LO));
bludata = i2c_smbus_read_word_data(taos->i2c_client,
(CMD_REG | BLU_CHAN1LO));
taos->clrdata = clrdata;
taos->reddata = reddata;
taos->grndata = grndata;
taos->bludata = bludata;
switch (reg_gain & 0x03) {
case 0x00:
gain = 1;
break;
case 0x01:
gain = 4;
break;
case 0x02:
gain = 16;
break;
/* case 0x03:
gain = 64;
break;
*/
default:
break;
}
if (gain == 1 && clrdata < 25) {
#ifdef CONFIG_SENSORS_TMD3782
reg_gain = 0x22;
#endif
#ifdef CONFIG_SENSORS_TMD3700
reg_gain = 0x02;//Gain 16x
#endif
ret = opt_i2c_write(taos, (CMD_REG | get_reg), &reg_gain);
if (ret < 0)
SENSOR_ERR("opt_i2c_write failed, err = %d\n", ret);
return taos->lux;
} else if (gain == 16 && clrdata > 15000) {
#ifdef CONFIG_SENSORS_TMD3782
reg_gain = 0x20;
#endif
#ifdef CONFIG_SENSORS_TMD3700
reg_gain = 0x00;//Gain 1x
#endif
ret = opt_i2c_write(taos, (CMD_REG | get_reg), &reg_gain);
if (ret < 0)
SENSOR_ERR("opt_i2c_write failed, err = %d\n", ret);
return taos->lux;
}
if ((clrdata >= 18500) && (gain == 1)) {
calculated_lux = MAX_LUX;
return calculated_lux;
}
/* calculate lux */
taos->irdata = (reddata + grndata + bludata - clrdata) /2;
if(taos->irdata < 0)
{
taos->irdata=0;
}
/* remove ir from counts*/
rp1 = taos->reddata - taos->irdata;
gp1 = taos->grndata - taos->irdata;
bp1 = taos->bludata - taos->irdata;
calculated_lux = (rp1 * taos->pdata->coef_r + gp1 * taos->pdata->coef_g + bp1 * taos->pdata->coef_b) / 1000;
if (calculated_lux < 0)
calculated_lux = 0;
else {
/* divide by CPL, CPL = (Atime_ms * ALS_GAIN / DGF); */
calculated_lux = calculated_lux* taos->pdata->dgf;
calculated_lux *= taos->pdata->lux_multiple; /* Atime_ms */
calculated_lux /= Atime_ms;
calculated_lux /= gain;
}
taos->lux = (int)((calculated_lux * 117) / 100);
//SENSOR_ERR("Lux = %d\n",taos->lux);
return taos->lux;
}
static void taos_light_enable(struct taos_data *taos)
{
taos->count_log_time = LIGHT_LOG_TIME;
#ifdef CONFIG_SENSORS_TMD3782
int cct = 0;
int adc = 0;
SENSOR_INFO("starting poll timer, delay %lldns\n",
ktime_to_ns(taos->light_poll_delay));
taos_get_lux(taos);
msleep(60);/*first lux value need not update to hal*/
adc = taos_get_lux(taos);
cct = taos_get_cct(taos);
input_report_rel(taos->light_input_dev, REL_MISC, adc + 1);
input_report_rel(taos->light_input_dev, REL_WHEEL, cct);
input_sync(taos->light_input_dev);
SENSOR_INFO("light_enable, adc: %d, cct: %d\n",adc,cct);
#endif
hrtimer_start(&taos->timer, taos->light_poll_delay, HRTIMER_MODE_REL);
}
static void taos_light_disable(struct taos_data *taos)
{
SENSOR_INFO("cancelling poll timer\n");
hrtimer_cancel(&taos->timer);
cancel_work_sync(&taos->work_light);
}
static ssize_t poll_delay_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct taos_data *taos = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%lld\n",
ktime_to_ns(taos->light_poll_delay));
}
static ssize_t poll_delay_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct taos_data *taos = dev_get_drvdata(dev);
int64_t new_delay;
int err;
err = kstrtoll(buf, 10, &new_delay);
if (err < 0)
return err;
SENSOR_INFO("new delay = %lldns, old delay = %lldns\n",
new_delay, ktime_to_ns(taos->light_poll_delay));
#ifdef CONFIG_SENSORS_TMD3700_RESET_DEFENCE_CODE
if(taos->vdd_reset)
{
taos->light_poll_delay = ns_to_ktime(new_delay);
return size;
}
#endif
mutex_lock(&taos->power_lock);
if (new_delay != ktime_to_ns(taos->light_poll_delay)) {
taos->light_poll_delay = ns_to_ktime(new_delay);
if (taos->power_state & LIGHT_ENABLED) {
taos_light_disable(taos);
taos_light_enable(taos);
}
}
mutex_unlock(&taos->power_lock);
return size;
}
static ssize_t light_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct taos_data *taos = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n",
(taos->power_state & LIGHT_ENABLED) ? 1 : 0);
}
static ssize_t proximity_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct taos_data *taos = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n",
(taos->power_state & PROXIMITY_ENABLED) ? 1 : 0);
}
static ssize_t light_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct taos_data *taos = dev_get_drvdata(dev);
bool new_value;
if (sysfs_streq(buf, "1")) {
new_value = true;
} else if (sysfs_streq(buf, "0")) {
new_value = false;
} else {
SENSOR_ERR("invalid value %d\n", *buf);
return -EINVAL;
}
#ifdef CONFIG_SENSORS_TMD3700_RESET_DEFENCE_CODE
if(taos->vdd_reset)
{
if(new_value) taos->power_state |= LIGHT_ENABLED;
else taos->power_state &= ~LIGHT_ENABLED;
return size;
}
#endif
mutex_lock(&taos->power_lock);
SENSOR_INFO("new_value = %d, old state = %d\n",
new_value, (taos->power_state & LIGHT_ENABLED) ? 1 : 0);
if (new_value && !(taos->power_state & LIGHT_ENABLED)) {
if (!taos->power_state) {
#ifdef CONFIG_SENSORS_TMD3700
taos_chip_enable(taos);
#else
taos_chip_on(taos);
#endif
}
taos->power_state |= LIGHT_ENABLED;
taos_light_enable(taos);
} else if (!new_value && (taos->power_state & LIGHT_ENABLED)) {
taos_light_disable(taos);
taos->power_state &= ~LIGHT_ENABLED;
if (!taos->power_state)
taos_chip_off(taos);
}
mutex_unlock(&taos->power_lock);
return size;
}
#ifdef CONFIG_SENSORS_TMD3700
static int tmd3700_interrupt_clear(struct taos_data *taos)
{
int ret;
int val =0 ;
val = 0xfa;//asein|psien|pien|aien|cien|0|0|0
ret = i2c_smbus_write_byte_data(taos->i2c_client, CMD_REG | STATUS, val);
if(ret)
SENSOR_ERR("%d\n", ret);
return 0;
}
#endif
static ssize_t proximity_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct taos_data *taos = dev_get_drvdata(dev);
bool new_value;
int ret = 0;
if (sysfs_streq(buf, "1")) {
new_value = true;
} else if (sysfs_streq(buf, "0")) {
new_value = false;
} else {
SENSOR_ERR("invalid value %d\n", *buf);
return -EINVAL;
}
#ifdef CONFIG_SENSORS_TMD3700_RESET_DEFENCE_CODE
if(taos->vdd_reset)
{
if(new_value) taos->power_state |= PROXIMITY_ENABLED;
else taos->power_state &= ~PROXIMITY_ENABLED;
return size;
}
#endif
mutex_lock(&taos->power_lock);
SENSOR_INFO("new_value = %d, old state = %d\n",
new_value, (taos->power_state & PROXIMITY_ENABLED) ? 1 : 0);
if (new_value && !(taos->power_state & PROXIMITY_ENABLED)) {
if (taos->set_manual_thd == false) {
ret = proximity_open_offset(taos);
if (ret < 0 && ret != -ENOENT)
SENSOR_ERR("proximity_open_offset() failed\n");
#ifdef CONFIG_PROX_WINDOW_TYPE
ret = proximity_open_window_type(taos);
#endif
taos->threshold_high = taos->pdata->prox_thresh_hi
+ taos->offset_value;
taos->threshold_low = taos->pdata->prox_thresh_low
+ taos->offset_value;
SENSOR_INFO("th_hi = %d, th_low = %d\n",
taos->threshold_high, taos->threshold_low);
}
if (!taos->power_state)
taos_chip_on(taos);
if (!(taos->power_state & PROXIMITY_ENABLED))
leda_regulator_onoff(taos,ON);
taos->power_state |= PROXIMITY_ENABLED;
taos->proximity_value = STATE_FAR;
taos_thresh_set(taos);
mdelay(10);
#ifdef CONFIG_SENSORS_TMD3782
SENSOR_INFO("Thes_high = %d,Thes_low = %d\n",
taos_proximity_get_threshold(taos, PRX_MAXTHRESHLO),
taos_proximity_get_threshold(taos, PRX_MINTHRESHLO));
/* interrupt clearing */
ret = opt_i2c_write_command(taos, (CMD_REG|CMD_SPL_FN|CMD_PROXALS_INTCLR));
#endif
#ifdef CONFIG_SENSORS_TMD3700
ret= tmd3700_interrupt_clear(taos);
#endif
if (ret < 0)
SENSOR_ERR("opt_i2c_write failed, err = %d\n", ret);
#if defined(CONFIG_SENSORS_TMD3782_PROX_ABS) || defined(CONFIG_SENSORS_TMD3700)
input_report_abs(taos->proximity_input_dev, ABS_DISTANCE, 1);
#else
input_report_rel(taos->proximity_input_dev, REL_MISC, 1+1);
#endif
input_sync(taos->proximity_input_dev);
enable_irq(taos->irq);
enable_irq_wake(taos->irq);
} else if (!new_value && (taos->power_state & PROXIMITY_ENABLED)) {
disable_irq_wake(taos->irq);
disable_irq(taos->irq);
#ifdef CONFIG_SENSORS_TMD3700
leda_regulator_onoff(taos,OFF);
#endif
taos->power_state &= ~PROXIMITY_ENABLED;
if (!taos->power_state)
taos_chip_off(taos);
}
mutex_unlock(&taos->power_lock);
return size;
}
static ssize_t proximity_state_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct taos_data *taos = dev_get_drvdata(dev);
int adc = 0;
adc = proximity_get_adc(taos);
return snprintf(buf, PAGE_SIZE, "%d\n", adc);
}
#ifdef CONFIG_PROX_WINDOW_TYPE
static void change_proximity_default_threshold(struct taos_data *data)
{
int trim = data->pdata->prox_rawdata_trim;
switch (data->windowtype[1]) {
case WINTYPE_WHITE:
data->pdata->prox_thresh_hi = WHITEWINDOW_HI_THRESHOLD-trim;
data->pdata->prox_thresh_low = WHITEWINDOW_LOW_THRESHOLD-trim;
break;
case WINTYPE_OTHERS:
data->pdata->prox_thresh_hi = BLACKWINDOW_HI_THRESHOLD-trim;
data->pdata->prox_thresh_low = BLACKWINDOW_LOW_THRESHOLD-trim;
break;
default:
data->pdata->prox_thresh_hi = data->pdata->prox_thresh_hi;
data->pdata->prox_thresh_low = data->pdata->prox_thresh_low;
break;
}
}
static int proximity_open_window_type(struct taos_data *data)
{
struct file *wintype_filp = NULL;
int err = 0;
mm_segment_t old_fs;
old_fs = get_fs();
set_fs(KERNEL_DS);
wintype_filp = filp_open(WINDOW_TYPE_FILE_PATH, O_RDONLY, 0);
if (IS_ERR(wintype_filp)) {
SENSOR_INFO("no window_type file\n");
err = PTR_ERR(wintype_filp);
if (err != -ENOENT)
SENSOR_ERR("Can't open window_type file\n");
set_fs(old_fs);
data->windowtype[0] = 0;
data->windowtype[1] = 0;
goto exit;
}
err = wintype_filp->f_op->read(wintype_filp,
(u8 *)&data->windowtype, sizeof(u8) * 2, &wintype_filp->f_pos);
if (err != sizeof(u8) * 2) {
SENSOR_ERR("Can't read the window_type data from file\n");
err = -EIO;
}
SENSOR_INFO("%c%c\n", data->windowtype[0], data->windowtype[1]);
filp_close(wintype_filp, current->files);
set_fs(old_fs);
exit:
change_proximity_default_threshold(data);
return err;
}
#endif
static int proximity_open_offset(struct taos_data *data)
{
struct file *offset_filp = NULL;
int err = 0;
mm_segment_t old_fs;
old_fs = get_fs();
set_fs(KERNEL_DS);
offset_filp = filp_open(OFFSET_FILE_PATH, O_RDONLY, 0);
if (IS_ERR(offset_filp)) {
SENSOR_ERR("no offset file\n");
err = PTR_ERR(offset_filp);
if (err != -ENOENT)
SENSOR_ERR("Can't open offset file\n");
set_fs(old_fs);
return err;
}
err = offset_filp->f_op->read(offset_filp,
(char *)&data->offset_value, sizeof(u16), &offset_filp->f_pos);
if (err != sizeof(u16)) {
SENSOR_ERR("Can't read the offset data from file\n");
err = -EIO;
}
SENSOR_INFO("data->offset_value = %d\n", data->offset_value);
if(data->offset_value < CAL_SKIP_ADC/2 || data->offset_value > CAL_FAIL_ADC/2)
data->offset_value = 0 ;
filp_close(offset_filp, current->files);
set_fs(old_fs);
return err;
}
static int proximity_adc_read(struct taos_data *taos)
{
int sum[OFFSET_ARRAY_LENGTH];
int i = 0;
int avg = 0;
int min = 0;
int max = 0;
int total = 0;
mutex_lock(&taos->prox_mutex);
for (i = 0; i < OFFSET_ARRAY_LENGTH; i++) {
usleep_range(10000, 11000);
sum[i] = proximity_get_adc(taos);
if (i == 0) {
min = sum[i];
max = sum[i];
} else {
if (sum[i] < min)
min = sum[i];
else if (sum[i] > max)
max = sum[i];
}
total += sum[i];
}
mutex_unlock(&taos->prox_mutex);
total -= (min + max);
avg = (int)(total / (OFFSET_ARRAY_LENGTH - 2));
return avg;
}
static int proximity_store_offset(struct device *dev, bool do_calib)
{
struct taos_data *taos = dev_get_drvdata(dev);
struct file *offset_filp = NULL;
mm_segment_t old_fs;
int err = 0;
u16 abnormal_ct = proximity_adc_read(taos);
u16 offset = 0;
if (do_calib) {
/* tap offset button */
SENSOR_INFO("calibration start\n");
if (abnormal_ct < CAL_SKIP_ADC) {
taos->offset_value = 0;
taos->threshold_high = taos->pdata->prox_thresh_hi;
taos->threshold_low = taos->pdata->prox_thresh_low;
taos_thresh_set(taos);
taos->set_manual_thd = false;
taos->cal_result = 2;
SENSOR_INFO(" crosstalk < %d, skip calibration\n", CAL_SKIP_ADC);
} else if ((abnormal_ct >= CAL_SKIP_ADC)
&& (abnormal_ct <= CAL_FAIL_ADC)) {
offset = abnormal_ct / 2;
taos->offset_value = offset;
taos->threshold_high = taos->pdata->prox_thresh_hi
+ offset;
taos->threshold_low = taos->pdata->prox_thresh_low
+ offset;
taos_thresh_set(taos);
taos->set_manual_thd = false;
taos->cal_result = 1;
} else {
taos->offset_value = 0;
taos->threshold_high = taos->pdata->prox_thresh_hi;
taos->threshold_low = taos->pdata->prox_thresh_low;
taos_thresh_set(taos);
taos->set_manual_thd = false;
taos->cal_result = 0;
SENSOR_INFO("crosstalk > %d, calibration failed\n", CAL_FAIL_ADC);
}
} else {
/* tap reset button */
SENSOR_INFO("reset\n");
taos->threshold_high = taos->pdata->prox_thresh_hi;
taos->threshold_low = taos->pdata->prox_thresh_low;
taos_thresh_set(taos);
taos->offset_value = 0;
taos->cal_result = 2;
taos->set_manual_thd = false;
}
SENSOR_INFO("abnormal_ct : %d, offset : %d\n", abnormal_ct, taos->offset_value);
/* store offset in file */
old_fs = get_fs();
set_fs(KERNEL_DS);
offset_filp = filp_open(OFFSET_FILE_PATH,
O_CREAT | O_TRUNC | O_WRONLY, 0660);
if (IS_ERR(offset_filp)) {
SENSOR_ERR("Can't open prox_offset file\n");
set_fs(old_fs);
err = PTR_ERR(offset_filp);
return err;
}
err = offset_filp->f_op->write(offset_filp,
(char *)&taos->offset_value, sizeof(u16), &offset_filp->f_pos);
if (err != sizeof(u16))
SENSOR_ERR("Can't write the offset data to file\n");
filp_close(offset_filp, current->files);
set_fs(old_fs);
return 1;
}
static ssize_t proximity_cal_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
bool do_calib;
int err;
if (sysfs_streq(buf, "1")) { /* calibrate cancelation value */
do_calib = true;
} else if (sysfs_streq(buf, "0")) { /* reset cancelation value */
do_calib = false;
} else {
SENSOR_ERR("invalid value %d\n", *buf);
return -EINVAL;
}
err = proximity_store_offset(dev, do_calib);
if (err < 0) {
SENSOR_ERR("proximity_store_offset() failed\n");
return err;
}
return size;
}
static ssize_t proximity_cal_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct taos_data *taos = dev_get_drvdata(dev);
proximity_open_offset(taos);
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n",
taos->offset_value, taos->threshold_high, taos->threshold_low);
}
static ssize_t prox_offset_pass_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct taos_data *taos = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", taos->cal_result);
}
static ssize_t proximity_avg_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct taos_data *taos = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n",
taos->avg[0], taos->avg[1], taos->avg[2]);
}
static ssize_t proximity_avg_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct taos_data *taos = dev_get_drvdata(dev);
int new_value = 0;
if (sysfs_streq(buf, "1")) {
new_value = true;
} else if (sysfs_streq(buf, "0")) {
new_value = false;
} else {
SENSOR_ERR("invalid value %d\n", *buf);
return -EINVAL;
}
if (taos->prox_avg_enable == new_value)
SENSOR_INFO(" same status\n");
else if (new_value == 1) {
SENSOR_INFO("starting poll timer, delay %lldns\n",
ktime_to_ns(taos->prox_polling_time));
hrtimer_start(&taos->prox_avg_timer,
taos->prox_polling_time, HRTIMER_MODE_REL);
taos->prox_avg_enable = 1;
} else {
SENSOR_INFO("cancelling prox avg poll timer\n");
hrtimer_cancel(&taos->prox_avg_timer);
cancel_work_sync(&taos->work_prox_avg);
taos->prox_avg_enable = 0;
}
return 1;
}
static ssize_t proximity_thresh_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct taos_data *taos = dev_get_drvdata(dev);
int thresh_hi = 0;
msleep(20);
#ifdef CONFIG_SENSORS_TMD3782
thresh_hi = taos_proximity_get_threshold(taos, PRX_MAXTHRESHLO);
#endif
#ifdef CONFIG_SENSORS_TMD3700
thresh_hi = taos_proximity_get_threshold(taos, PRX_MAXTHRESH);
#endif
SENSOR_INFO("THRESHOLD = %d\n", thresh_hi);
return snprintf(buf, PAGE_SIZE, "prox_threshold = %d\n", thresh_hi);
}
static ssize_t proximity_thresh_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct taos_data *taos = dev_get_drvdata(dev);
int thresh_value = (u8)(taos->pdata->prox_thresh_hi);
int err = 0;
err = kstrtoint(buf, 10, &thresh_value);
SENSOR_INFO("value = %d\n", thresh_value);
if (err < 0)
SENSOR_ERR("kstrtoint failed.");
taos->threshold_high = thresh_value;
taos_thresh_set(taos);
msleep(20);
return size;
}
static ssize_t thresh_high_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct taos_data *taos = dev_get_drvdata(dev);
int thresh_hi = 0, thresh_low = 0;
msleep(20);
#ifdef CONFIG_SENSORS_TMD3782
thresh_low = taos_proximity_get_threshold(taos, PRX_MINTHRESHLO);
thresh_hi = taos_proximity_get_threshold(taos, PRX_MAXTHRESHLO);
#endif
#ifdef CONFIG_SENSORS_TMD3700
thresh_low = taos_proximity_get_threshold(taos, PRX_MINTHRESH);
thresh_hi = taos_proximity_get_threshold(taos, PRX_MAXTHRESH);
#endif
SENSOR_INFO("thresh_hi = %d, thresh_low = %d\n", thresh_hi,thresh_low);
return snprintf(buf, PAGE_SIZE, "%d,%d\n", thresh_hi, thresh_low);
}
static ssize_t thresh_high_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct taos_data *taos = dev_get_drvdata(dev);
int thresh_value = (u8)(taos->pdata->prox_thresh_hi);
int err = 0;
err = kstrtoint(buf, 10, &thresh_value);
SENSOR_INFO("thresh_value = %d\n", thresh_value);
if (err < 0)
SENSOR_ERR("kstrtoint failed.");
taos->threshold_high = thresh_value;
taos_thresh_set(taos);
msleep(20);
taos->set_manual_thd = true;
return size;
}
static ssize_t thresh_low_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct taos_data *taos = dev_get_drvdata(dev);
int thresh_hi = 0, thresh_low = 0;
msleep(20);
#ifdef CONFIG_SENSORS_TMD3782
thresh_hi = taos_proximity_get_threshold(taos, PRX_MAXTHRESHLO);
thresh_low = taos_proximity_get_threshold(taos, PRX_MINTHRESHLO);
#endif
#ifdef CONFIG_SENSORS_TMD3700
thresh_hi = taos_proximity_get_threshold(taos, PRX_MAXTHRESH);
thresh_low = taos_proximity_get_threshold(taos, PRX_MINTHRESH);
#endif
SENSOR_INFO("thresh_hi = %d, thresh_low = %d\n", thresh_hi,thresh_low);
return snprintf(buf, PAGE_SIZE, "%d,%d\n", thresh_hi, thresh_low);
}
static ssize_t thresh_low_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct taos_data *taos = dev_get_drvdata(dev);
int thresh_value = (u8)(taos->pdata->prox_thresh_low);
int err = 0;
err = kstrtoint(buf, 10, &thresh_value);
SENSOR_INFO("thresh_value = %d\n", thresh_value);
if (err < 0)
SENSOR_ERR("kstrtoint failed.");
taos->threshold_low = thresh_value;
taos_thresh_set(taos);
msleep(20);
taos->set_manual_thd = true;
return size;
}
static ssize_t prox_trim_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct taos_data *taos = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", taos->pdata->prox_rawdata_trim);
}
static ssize_t prox_trim_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct taos_data *taos = dev_get_drvdata(dev);
int trim_value = (u8)(taos->pdata->prox_rawdata_trim);
int err = 0;
err = kstrtoint(buf, 10, &trim_value);
SENSOR_INFO(" trim_value = %d\n", trim_value);
if (err < 0)
SENSOR_ERR(" kstrtoint failed.");
taos->pdata->prox_rawdata_trim = trim_value;
return size;
}
#ifdef CONFIG_SEC_FACTORY
static ssize_t proximity_register_write_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned int regist = 0, val = 0;
struct taos_data *taos = dev_get_drvdata(dev);
if (sscanf(buf, "%x,%x", &regist, &val) != 2) {
SENSOR_ERR("The number of data are wrong\n");
return count;
}
opt_i2c_write(taos, regist, (u8 *)&val);
SENSOR_INFO("Register(0x%2x) 16:data(0x%4x) 10:%d \n", regist, val, val);
return count;
}
static ssize_t proximity_register_read_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
u8 val, i;
int offset = 0;
struct taos_data *taos = dev_get_drvdata(dev);
for (i = 0; i < 90; i++) {
val = 0;
opt_i2c_read(taos, i + 128, &val);
offset += snprintf(buf + offset, PAGE_SIZE - offset,
"Register(0x%2x), data(0x%2x)\n", i + 128, val);
SENSOR_INFO("Register(0x%2x) data(0x%2x)\n", i, val);
}
//e6
val = 0;
opt_i2c_read(taos,0xE6, &val);
offset += snprintf(buf + offset, PAGE_SIZE - offset,
"Register(0xE6), data(0x%2x)\n", val);
//e7
val = 0;
opt_i2c_read(taos,0xE7, &val);
offset += snprintf(buf + offset, PAGE_SIZE - offset,
"Register(0xE7), data(0x%2x)\n", val);
return offset;
}
#endif //CONFIG_SEC_FACTORY
#ifdef CONFIG_SENSORS_TMD3700_RESET_DEFENCE_CODE
static ssize_t taos_power_reset_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct taos_data *taos = dev_get_drvdata(dev);
SENSOR_INFO("\n");
taos->vdd_reset = 1;
mutex_lock(&taos->power_lock);
if(taos->power_state & PROXIMITY_ENABLED)
{
disable_irq_wake(taos->irq);
disable_irq(taos->irq);
#ifdef CONFIG_SENSORS_TMD3700
leda_regulator_onoff(taos,OFF);
#endif
}
if(taos->power_state & LIGHT_ENABLED)
taos_light_disable(taos);
taos_chip_off(taos);
mutex_unlock(&taos->power_lock);
return snprintf(buf, PAGE_SIZE, "%d\n",1);
}
static ssize_t taos_sw_reset_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct taos_data *taos = dev_get_drvdata(dev);
SENSOR_INFO("\n");
mutex_lock(&taos->power_lock);
taos_chip_on(taos);
if (!taos->power_state)
taos_chip_off(taos);
if(taos->power_state & LIGHT_ENABLED)
{
taos_light_enable(taos);
}
if (taos->power_state & PROXIMITY_ENABLED) {
if (taos->set_manual_thd == false) {
proximity_open_offset(taos);
#ifdef CONFIG_PROX_WINDOW_TYPE
proximity_open_window_type(taos);
#endif
taos->threshold_high = taos->pdata->prox_thresh_hi
+ taos->offset_value;
taos->threshold_low = taos->pdata->prox_thresh_low
+ taos->offset_value;
SENSOR_INFO("th_hi = %d, th_low = %d\n",
taos->threshold_high, taos->threshold_low);
}
leda_regulator_onoff(taos,ON);
taos->proximity_value = STATE_FAR;
taos_thresh_set(taos);
mdelay(10);
#ifdef CONFIG_SENSORS_TMD3782
SENSOR_INFO("Thes_high = %d,Thes_low = %d\n",
taos_proximity_get_threshold(taos, PRX_MAXTHRESHLO),
taos_proximity_get_threshold(taos, PRX_MINTHRESHLO));
/* interrupt clearing */
opt_i2c_write_command(taos, (CMD_REG|CMD_SPL_FN|CMD_PROXALS_INTCLR));
#endif
#ifdef CONFIG_SENSORS_TMD3700
tmd3700_interrupt_clear(taos);
#endif
enable_irq(taos->irq);
enable_irq_wake(taos->irq);
}
mutex_unlock(&taos->power_lock);
taos->vdd_reset = 0;
return snprintf(buf, PAGE_SIZE, "%d\n",1);
}
#endif
static ssize_t get_vendor_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR_NAME);
}
static ssize_t get_chip_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_NAME);
}
static DEVICE_ATTR(vendor, S_IRUGO, get_vendor_name, NULL);
static DEVICE_ATTR(name, S_IRUGO, get_chip_name, NULL);
static ssize_t lightsensor_file_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct taos_data *taos = dev_get_drvdata(dev);
int adc = 0;
adc = lightsensor_get_adcvalue(taos);
return snprintf(buf, PAGE_SIZE, "%d\n", adc);
}
static ssize_t lightsensor_raw_data_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct taos_data *taos = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%u,%u,%u,%u\n",
taos->reddata, taos->grndata, taos->bludata, taos->clrdata);
}
static struct device_attribute dev_attr_light_raw_data =
__ATTR(raw_data, S_IRUGO, lightsensor_raw_data_show, NULL);
static DEVICE_ATTR(adc, S_IRUGO, lightsensor_file_state_show, NULL);
static DEVICE_ATTR(lux, S_IRUGO, lightsensor_file_state_show, NULL);
static DEVICE_ATTR(poll_delay, S_IRUGO | S_IWUSR | S_IWGRP,
poll_delay_show, poll_delay_store);
#ifdef CONFIG_SENSORS_TMD3700_RESET_DEFENCE_CODE
static DEVICE_ATTR(power_reset, S_IRUSR | S_IRGRP,
taos_power_reset_show, NULL);
static DEVICE_ATTR(sw_reset, S_IRUSR | S_IRGRP,
taos_sw_reset_show, NULL);
#endif
static struct device_attribute dev_attr_light_enable =
__ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP,
light_enable_show, light_enable_store);
static struct attribute *light_sysfs_attrs[] = {
&dev_attr_light_enable.attr,
&dev_attr_poll_delay.attr,
NULL
};
static struct attribute_group light_attribute_group = {
.attrs = light_sysfs_attrs,
};
static struct device_attribute *lightsensor_additional_attributes[] = {
&dev_attr_adc,
&dev_attr_lux,
&dev_attr_vendor,
&dev_attr_name,
&dev_attr_light_raw_data,
#ifdef CONFIG_SENSORS_TMD3700_RESET_DEFENCE_CODE
&dev_attr_power_reset,
&dev_attr_sw_reset,
#endif
NULL
};
static struct device_attribute dev_attr_proximity_enable =
__ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP,
proximity_enable_show, proximity_enable_store);
static struct attribute *proximity_sysfs_attrs[] = {
&dev_attr_proximity_enable.attr,
NULL
};
static struct attribute_group proximity_attribute_group = {
.attrs = proximity_sysfs_attrs,
};
static struct device_attribute dev_attr_proximity_raw_data =
__ATTR(raw_data, S_IRUGO, proximity_state_show, NULL);
static DEVICE_ATTR(prox_cal, S_IRUGO | S_IWUSR, proximity_cal_show,
proximity_cal_store);
static DEVICE_ATTR(prox_avg, S_IRUGO|S_IWUSR, proximity_avg_show,
proximity_avg_store);
static DEVICE_ATTR(state, S_IRUGO, proximity_state_show, NULL);
static DEVICE_ATTR(prox_offset_pass, S_IRUGO,
prox_offset_pass_show, NULL);
static DEVICE_ATTR(prox_thresh, 0644, proximity_thresh_show,
proximity_thresh_store);
static DEVICE_ATTR(thresh_high, 0644, thresh_high_show,
thresh_high_store);
static DEVICE_ATTR(thresh_low, 0644, thresh_low_show,
thresh_low_store);
static DEVICE_ATTR(prox_trim, S_IRUGO| S_IWUSR | S_IWGRP,
prox_trim_show, prox_trim_store);
#ifdef CONFIG_SEC_FACTORY
static DEVICE_ATTR(prox_register, S_IRUGO | S_IWUSR | S_IWGRP,
proximity_register_read_show, proximity_register_write_store);
#endif //CONFIG_SEC_FACTORY
static struct device_attribute *prox_sensor_attrs[] = {
&dev_attr_state,
&dev_attr_prox_avg,
&dev_attr_vendor,
&dev_attr_name,
&dev_attr_proximity_raw_data,
&dev_attr_prox_cal,
&dev_attr_prox_offset_pass,
&dev_attr_prox_thresh,
&dev_attr_thresh_high,
&dev_attr_thresh_low,
&dev_attr_prox_trim,
#ifdef CONFIG_SEC_FACTORY
&dev_attr_prox_register,
#endif //CONFIG_SEC_FACTORY
NULL
};
static int lightsensor_get_adcvalue(struct taos_data *taos)
{
int i = 0;
int j = 0;
unsigned int adc_total = 0;
int adc_avr_value;
unsigned int adc_index = 0;
unsigned int adc_max = 0;
unsigned int adc_min = 0;
int value = 0;
#ifdef CONFIG_SENSORS_TMD3700_RESET_DEFENCE_CODE
if(taos->vdd_reset)
{
SENSOR_INFO("vdd: turned off.\n");
return -1;
}
#endif
/* get ADC */
value = taos_get_lux(taos);
adc_index = (taos->adc_index_count++) % ADC_BUFFER_NUM;
/*ADC buffer initialize (light sensor off ---> light sensor on) */
if (!taos->adc_buf_initialized) {
taos->adc_buf_initialized = true;
for (j = 0; j < ADC_BUFFER_NUM; j++)
taos->adc_value_buf[j] = value;
} else
taos->adc_value_buf[adc_index] = value;
adc_max = taos->adc_value_buf[0];
adc_min = taos->adc_value_buf[0];
for (i = 0; i < ADC_BUFFER_NUM; i++) {
adc_total += taos->adc_value_buf[i];
if (adc_max < taos->adc_value_buf[i])
adc_max = taos->adc_value_buf[i];
if (adc_min > taos->adc_value_buf[i])
adc_min = taos->adc_value_buf[i];
}
adc_avr_value = (adc_total-(adc_max+adc_min))/(ADC_BUFFER_NUM - 2);
if (taos->adc_index_count == ADC_BUFFER_NUM)
taos->adc_index_count = 0;
return adc_avr_value;
}
static void taos_work_func_light(struct work_struct *work)
{
struct taos_data *taos = container_of(work, struct taos_data,
work_light);
int adc = taos_get_lux(taos);
int cct = taos_get_cct(taos);
input_report_rel(taos->light_input_dev, REL_MISC, adc + 1);
input_report_rel(taos->light_input_dev, REL_WHEEL, cct);
input_sync(taos->light_input_dev);
if (taos->count_log_time >= LIGHT_LOG_TIME) {
SENSOR_INFO("R = %d, G = %d, B = %d, C = %d, ir = %d, lux = %d, cct = %d\n",
taos->reddata, taos->grndata, taos->bludata, taos->clrdata, taos->irdata, adc, cct);
taos->count_log_time = 0;
} else
taos->count_log_time++;
}
static void taos_work_func_prox(struct work_struct *work)
{
struct taos_data *taos =
container_of(work, struct taos_data, work_prox);
int adc_data;
int threshold_high;
int threshold_low;
int proximity_value = 0;
#ifdef CONFIG_SENSORS_TMD3782
u8 chipid = 0x69;
int ret = 0;
int i = 0;
#endif
/* disable INT */
disable_irq_nosync(taos->irq);
#ifdef CONFIG_SENSORS_TMD3782
while (chipid != 0x69 && i < 10) {
msleep(20);
ret = opt_i2c_read(taos, CHIPID, &chipid);
i++;
}
if (ret < 0)
SENSOR_ERR("opt_i2c_read failed, err = %d\n", ret);
#endif
/* change Threshold */
mutex_lock(&taos->prox_mutex);
adc_data = proximity_get_adc(taos);
mutex_unlock(&taos->prox_mutex);
#ifdef CONFIG_SENSORS_TMD3782
threshold_high = taos_proximity_get_threshold(taos, PRX_MAXTHRESHLO);
threshold_low = taos_proximity_get_threshold(taos, PRX_MINTHRESHLO);
SENSOR_INFO("hi = %d, low = %d, adc_data = %d\n",
taos->threshold_high, taos->threshold_low, adc_data);
if ((threshold_high == (taos->threshold_high)) &&
(adc_data >= (taos->threshold_high))) {
proximity_value = STATE_CLOSE;
#if defined(CONFIG_SENSORS_TMD3782_PROX_ABS)
input_report_abs(taos->proximity_input_dev,
ABS_DISTANCE, proximity_value);
#else
input_report_rel(taos->proximity_input_dev,
REL_MISC, proximity_value+1);
#endif
input_sync(taos->proximity_input_dev);
SENSOR_INFO("prox value = %d\n", proximity_value);
} else if ((threshold_high == (0xFFFF)) &&
(adc_data <= (taos->threshold_low))) {
proximity_value = STATE_FAR;
#if defined(CONFIG_SENSORS_TMD3782_PROX_ABS)
input_report_abs(taos->proximity_input_dev,
ABS_DISTANCE, proximity_value);
#else
input_report_rel(taos->proximity_input_dev,
REL_MISC, proximity_value+1);
#endif
input_sync(taos->proximity_input_dev);
SENSOR_INFO("prox value = %d\n", proximity_value);
} else {
SENSOR_ERR("Error Case!adc=[%X], th_high=[%d], th_min=[%d]\n",
adc_data, threshold_high, threshold_low);
goto exit;
}
#endif //CONFIG_SENSORS_TMD3782
#ifdef CONFIG_SENSORS_TMD3700
threshold_high = taos_proximity_get_threshold(taos, PRX_MAXTHRESH);
threshold_low = taos_proximity_get_threshold(taos, PRX_MINTHRESH);
SENSOR_INFO("hi = %d, low = %d, adc_data = %d\n",
taos->threshold_high, taos->threshold_low, adc_data);
if ((taos->proximity_value == STATE_FAR) &&
(adc_data >= (taos->threshold_high))) {
proximity_value = STATE_CLOSE;
input_report_abs(taos->proximity_input_dev,
ABS_DISTANCE, proximity_value);
input_sync(taos->proximity_input_dev);
SENSOR_INFO("prox value = %d\n", proximity_value);
}
else if ((taos->proximity_value == STATE_CLOSE) &&
(adc_data <= (taos->threshold_low)))
{
proximity_value = STATE_FAR;
input_report_abs(taos->proximity_input_dev,
ABS_DISTANCE, proximity_value);
input_sync(taos->proximity_input_dev);
SENSOR_INFO("prox value = %d\n", proximity_value);
} else {
SENSOR_ERR("Error Case!adc=[%X], th_high=[%d], th_min=[%d]\n",
adc_data, threshold_high, threshold_low);
goto exit;
}
if(proximity_value == STATE_CLOSE)
SENSOR_INFO("[PROX] CLOSE pdata=[%d] \n", adc_data);
else if (proximity_value == STATE_FAR)
SENSOR_INFO("[PROX] FAR pdata=[%d] \n", adc_data);
else
SENSOR_INFO("[PROX] Unknown pdata=[%d] \n", adc_data);
#endif
taos->proximity_value = proximity_value;
taos_thresh_set(taos);
/* reset Interrupt pin */
/* to active Interrupt, TMD2771x Interuupt pin shoud be reset. */
exit:
#ifdef CONFIG_SENSORS_TMD3782
i2c_smbus_write_byte(taos->i2c_client,
(CMD_REG|CMD_SPL_FN|CMD_PROXALS_INTCLR));
#endif
#ifdef CONFIG_SENSORS_TMD3700
tmd3700_interrupt_clear(taos);
#endif
/* enable INT */
enable_irq(taos->irq);
}
static void taos_work_func_prox_avg(struct work_struct *work)
{
struct taos_data *taos = container_of(work, struct taos_data,
work_prox_avg);
int proximity_value = 0;
int min = 0, max = 0, avg = 0;
int i = 0;
for (i = 0; i < PROX_AVG_COUNT; i++) {
mutex_lock(&taos->prox_mutex);
proximity_value = proximity_get_adc(taos);
mutex_unlock(&taos->prox_mutex);
if (proximity_value > TAOS_PROX_MIN) {
avg += proximity_value;
if (!i)
min = proximity_value;
if (proximity_value < min)
min = proximity_value;
if (proximity_value > max)
max = proximity_value;
} else {
proximity_value = TAOS_PROX_MIN;
}
msleep(40);
}
avg /= i;
taos->avg[0] = min;
taos->avg[1] = avg;
taos->avg[2] = max;
}
/* This function is for light sensor. It operates every a few seconds.
* It asks for work to be done on a thread because i2c needs a thread
* context (slow and blocking) and then reschedules the timer to run again.
*/
static enum hrtimer_restart taos_timer_func(struct hrtimer *timer)
{
struct taos_data *taos = container_of(timer, struct taos_data, timer);
queue_work(taos->wq, &taos->work_light);
hrtimer_forward_now(&taos->timer, taos->light_poll_delay);
return HRTIMER_RESTART;
}
static enum hrtimer_restart taos_prox_timer_func(struct hrtimer *timer)
{
struct taos_data *taos = container_of(timer, struct taos_data,
prox_avg_timer);
queue_work(taos->wq_avg, &taos->work_prox_avg);
hrtimer_forward_now(&taos->prox_avg_timer, taos->prox_polling_time);
return HRTIMER_RESTART;
}
/* interrupt happened due to transition/change of near/far proximity state */
irqreturn_t taos_irq_handler(int irq, void *data)
{
struct taos_data *ip = data;
if (ip->irq != -1) {
wake_lock_timeout(&ip->prx_wake_lock, 3*HZ);
queue_work(ip->wq, &ip->work_prox);
}
SENSOR_INFO("taos interrupt handler is called\n");
return IRQ_HANDLED;
}
static int taos_setup_irq(struct taos_data *taos)
{
int rc = -EIO;
struct taos_platform_data *pdata = taos->pdata;
int irq;
SENSOR_INFO("start\n");
rc = gpio_request(pdata->als_int, "gpio_proximity_out");
if (rc < 0) {
SENSOR_ERR("gpio %d request failed (%d)\n", pdata->als_int, rc);
return rc;
}
rc = gpio_direction_input(pdata->als_int);
if (rc < 0) {
SENSOR_ERR("failed to set gpio %d as input (%d)\n", pdata->als_int, rc);
goto err_gpio_direction_input;
}
irq = gpio_to_irq(pdata->als_int);
rc = request_threaded_irq(irq, NULL, taos_irq_handler,
IRQF_TRIGGER_FALLING |IRQF_ONESHOT,
"proximity_int", taos);
if (rc < 0) {
SENSOR_ERR("request_irq(%d) failed for gpio %d (%d)\n", irq, pdata->als_int, rc);
goto err_request_irq;
}
/* start with interrupts disabled */
disable_irq(irq);
taos->irq = irq;
SENSOR_INFO("success\n");
goto done;
err_request_irq:
err_gpio_direction_input:
gpio_free(pdata->als_int);
done:
return rc;
}
static int taos_get_initial_offset(struct taos_data *taos)
{
int ret = 0;
u8 p_offset = 0;
ret = proximity_open_offset(taos);
if (ret < 0) {
p_offset = 0;
taos->offset_value = 0;
} else
p_offset = taos->offset_value;
SENSOR_ERR("initial offset = %d\n", p_offset);
return p_offset;
}
#ifdef CONFIG_OF
/* device tree parsing function */
static int taos_parse_dt(struct device *dev, struct taos_platform_data *pdata)
{
int ret = 0;
u32 sign[6] = {2,2,2,2,2,2};
struct device_node *np = dev->of_node;
pdata->als_int = of_get_named_gpio_flags(np, "taos,irq_gpio",
0, &pdata->als_int_flags);
pdata->vled_ldo = of_get_named_gpio_flags(np, "taos,vled_ldo", 0,
&pdata->als_en_flags);
if (pdata->vled_ldo < 0) {
SENSOR_ERR("fail to get vled_ldo: means to use regulator as vLED\n");
pdata->vled_ldo = 0;
}
ret = of_property_read_u32(np, "taos,prox_rawdata_trim",
&pdata->prox_rawdata_trim);
ret = of_property_read_u32(np, "taos,prox_thresh_hi",
&pdata->prox_thresh_hi);
ret = of_property_read_u32(np, "taos,prox_thresh_low",
&pdata->prox_thresh_low);
ret = of_property_read_u32(np, "taos,als_time",
&pdata->als_time);
ret = of_property_read_u32(np, "taos,intr_filter",
&pdata->intr_filter);
ret = of_property_read_u32(np, "taos,prox_pulsecnt",
&pdata->prox_pulsecnt);
ret = of_property_read_u32(np, "taos,als_gain",
&pdata->als_gain);
pdata->dgf = DGF;
pdata->cct_coef = CT_Coef1;
pdata->cct_offset = CT_Offset1;
pdata->coef_r = R_Coef1;
pdata->coef_g = G_Coef1;
pdata->coef_b = B_Coef1;
pdata->lux_multiple = 10; // lux * 1
ret = of_property_read_u32(np, "taos,lux_multiple",
&pdata->lux_multiple);
if (of_property_read_u32_array(np, "taos,coef_sign", sign, 6) >= 0)
{
if (of_property_read_u32(np, "taos,dgf",&pdata->dgf) >= 0)
pdata->dgf *= (sign[0]-1);
if (of_property_read_u32(np, "taos,cct_coef",&pdata->cct_coef) >= 0)
pdata->cct_coef *= (sign[1]-1);
if (of_property_read_u32(np, "taos,cct_offset",&pdata->cct_offset) >= 0)
pdata->cct_offset *= (sign[2]-1);
if (of_property_read_u32(np, "taos,coef_r",&pdata->coef_r) >= 0)
pdata->coef_r *= (sign[3]-1);
if (of_property_read_u32(np, "taos,coef_g",&pdata->coef_g) >= 0)
pdata->coef_g *= (sign[4]-1);
if (of_property_read_u32(np, "taos,coef_b",&pdata->coef_b) >= 0)
pdata->coef_b *= (sign[5]-1);
}
SENSOR_INFO("irq_gpio:%d, dgf: %d, cct_coef:%d, cct_offset:%d, r:%d, g:%d, b:%d\n",
pdata->als_int, pdata->dgf, pdata->cct_coef, pdata->cct_offset,
pdata->coef_r, pdata->coef_g, pdata->coef_b);
return 0;
}
#else
static int taos_parse_dt(struct device *dev,struct taos_platform_data *pdata)
{
return -ENODEV;
}
#endif
static int taos_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret = -ENODEV;
int chipid = 0;
int retry = 0;
struct taos_data *taos;
struct taos_platform_data *pdata = NULL;
SENSOR_INFO("START\n");
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
SENSOR_ERR("i2c functionality check failed!\n");
return ret;
}
taos = kzalloc(sizeof(struct taos_data), GFP_KERNEL);
if (!taos) {
SENSOR_ERR("failed to alloc memory for module data\n");
ret = -ENOMEM;
goto done;
}
/* parsing data */
if(client->dev.of_node) {
pdata = devm_kzalloc (&client->dev ,
sizeof(struct taos_platform_data ), GFP_KERNEL);
if(!pdata) {
dev_err(&client->dev, "Failed to allocate memory\n");
ret = -ENOMEM;
goto err_taos_data_free;
}
ret = taos_parse_dt(&client->dev, pdata);
if(ret)
goto err_devicetree;
} else
pdata = client->dev.platform_data;
if (!pdata) {
SENSOR_ERR("missing pdata!\n");
goto err_taos_data_free;
}
taos->i2c_client = client;
sreboot:
#ifdef CONFIG_SENSORS_TMD3782
leda_regulator_onoff(taos,1);
usleep_range(9000, 10000);
#endif
/* ID Check */
chipid = i2c_smbus_read_byte_data(client, CMD_REG | CHIPID);
if (chipid != CHIP_ID) {
SENSOR_ERR("i2c read error 0x[%X]\n", chipid);
#ifdef CONFIG_SENSORS_TMD3782
leda_regulator_onoff(taos,0);
#endif
usleep_range(9000, 10000);
if(retry < RETRY_REBOOT){
retry++;
goto sreboot;
}
ret = -ENXIO;
goto err_chip_id_or_i2c_error;
}
#ifdef CONFIG_SENSORS_TMD3700_RESET_DEFENCE_CODE
taos->vdd_reset = 0;
#endif
taos->pdata = pdata;
i2c_set_clientdata(client, taos);
taos->lux = 0;
taos->offset_value = taos_get_initial_offset(taos);
#ifdef CONFIG_PROX_WINDOW_TYPE
proximity_open_window_type(taos);
#endif
taos->set_manual_thd = false;
taos->threshold_high = taos->pdata->prox_thresh_hi + taos->offset_value;
taos->threshold_low = taos->pdata->prox_thresh_low + taos->offset_value;
/* wake lock init */
wake_lock_init(&taos->prx_wake_lock, WAKE_LOCK_SUSPEND,
"prx_wake_lock");
mutex_init(&taos->prox_mutex);
mutex_init(&taos->power_lock);
/* allocate proximity input_device */
taos->proximity_input_dev = input_allocate_device();
if (!taos->proximity_input_dev) {
SENSOR_ERR("could not allocate input device\n");
goto err_input_allocate_device_proximity;
}
input_set_drvdata(taos->proximity_input_dev, taos);
taos->proximity_input_dev->name = "proximity_sensor";
#if defined(CONFIG_SENSORS_TMD3700) || defined(CONFIG_SENSORS_TMD3782_PROX_ABS)
input_set_capability(taos->proximity_input_dev, EV_ABS, ABS_DISTANCE);
input_set_abs_params(taos->proximity_input_dev, ABS_DISTANCE, 0, 1, 0, 0);
#else
input_set_capability(taos->proximity_input_dev, EV_REL, REL_MISC);
#endif
SENSOR_INFO("registering proximity input device\n");
ret = input_register_device(taos->proximity_input_dev);
if (ret < 0) {
SENSOR_ERR("could not register input device\n");
goto err_input_register_device_proximity;
}
ret = sensors_register(taos->proximity_dev, taos, prox_sensor_attrs,
MODULE_NAME_PROX); /* factory attributs */
if (ret < 0) {
SENSOR_ERR(" could not registersensors_register\n");
goto err_sensor_register_device_proximity;
}
ret = sensors_create_symlink(&taos->proximity_input_dev->dev.kobj, taos->proximity_input_dev->name);
if (ret < 0) {
SENSOR_ERR("fail: sensors_create_symlink\n");
goto err_symlink_device_proximity;
}
ret = sysfs_create_group(&taos->proximity_input_dev->dev.kobj, &proximity_attribute_group);
if (ret < 0) {
SENSOR_ERR("could not create sysfs group\n");
goto err_create_sensorgoup_proximity;
}
/* hrtimer settings. we poll for light values using a timer. */
hrtimer_init(&taos->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
taos->light_poll_delay = ns_to_ktime(200 * NSEC_PER_MSEC);
taos->timer.function = taos_timer_func;
/* the timer just fires off a work queue request. we need a thread
to read the i2c (can be slow and blocking). */
taos->wq = create_singlethread_workqueue("taos_wq");
if (!taos->wq) {
ret = -ENOMEM;
SENSOR_ERR("could not create workqueue\n");
goto err_create_workqueue;
}
taos->wq_avg = create_singlethread_workqueue("taos_wq_avg");
if (!taos->wq_avg) {
ret = -ENOMEM;
SENSOR_ERR("could not create workqueue\n");
goto err_create_avg_workqueue;
}
/* this is the thread function we run on the work queue */
INIT_WORK(&taos->work_light, taos_work_func_light);
INIT_WORK(&taos->work_prox, taos_work_func_prox);
INIT_WORK(&taos->work_prox_avg, taos_work_func_prox_avg);
taos->prox_avg_enable = 0;
hrtimer_init(&taos->prox_avg_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
taos->prox_polling_time = ns_to_ktime(2000 * NSEC_PER_MSEC);
taos->prox_avg_timer.function = taos_prox_timer_func;
/* allocate lightsensor-level input_device */
taos->light_input_dev = input_allocate_device();
if (!taos->light_input_dev) {
SENSOR_ERR("could not allocate input device\n");
ret = -ENOMEM;
goto err_input_allocate_device_light;
}
input_set_drvdata(taos->light_input_dev, taos);
taos->light_input_dev->name = "light_sensor";
input_set_capability(taos->light_input_dev, EV_REL, REL_MISC);
input_set_capability(taos->light_input_dev, EV_REL, REL_WHEEL);
SENSOR_INFO("registering lightsensor-level input device\n");
ret = input_register_device(taos->light_input_dev);
if (ret < 0) {
SENSOR_ERR("could not register input device\n");
goto err_input_register_device_light;
}
ret = sensors_register(taos->light_dev,taos, lightsensor_additional_attributes,"light_sensor");
if (ret < 0) {
SENSOR_ERR("cound not register light sensor device(%d).\n",ret);
goto err_sensor_register_device_light;
}
ret = sensors_create_symlink(&taos->light_input_dev->dev.kobj, taos->light_input_dev->name);
if (ret < 0) {
SENSOR_ERR("cound not sensors_create_symlink(%d).\n",ret);
goto err_symlink_device_light;
}
ret = sysfs_create_group(&taos->light_input_dev->dev.kobj, &light_attribute_group);
if (ret < 0) {
SENSOR_ERR("could not create sysfs group\n");
goto err_create_sensorgoup_light;
}
/* setup irq */
ret = taos_setup_irq(taos);
if (ret < 0) {
SENSOR_ERR("could not setup irq\n");
goto err_setup_irq;
}
#ifdef CONFIG_SENSORS_TMD3700
taos_chip_on(taos);
taos_chip_off(taos);
#endif
SENSOR_INFO("PROBE DONE\n");
goto done;
/* error, unwind it all */
err_setup_irq:
err_create_sensorgoup_light:
sensors_remove_symlink(&taos->light_input_dev->dev.kobj,taos->proximity_input_dev->name);
err_symlink_device_light:
sensors_unregister(taos->light_dev, lightsensor_additional_attributes);
err_sensor_register_device_light:
input_unregister_device(taos->light_input_dev);
err_input_register_device_light:
input_free_device(taos->light_input_dev);
err_input_allocate_device_light:
destroy_workqueue(taos->wq_avg);
err_create_avg_workqueue:
destroy_workqueue(taos->wq);
err_create_workqueue:
sysfs_remove_group(&taos->proximity_input_dev->dev.kobj,
&proximity_attribute_group);
err_create_sensorgoup_proximity:
sensors_remove_symlink(&taos->proximity_input_dev->dev.kobj,taos->proximity_input_dev->name);
err_symlink_device_proximity:
sensors_unregister(taos->proximity_dev, prox_sensor_attrs);
err_sensor_register_device_proximity:
input_unregister_device(taos->proximity_input_dev);
err_input_register_device_proximity:
input_free_device(taos->proximity_input_dev);
err_input_allocate_device_proximity:
mutex_destroy(&taos->power_lock);
mutex_destroy(&taos->prox_mutex);
wake_lock_destroy(&taos->prx_wake_lock);
err_devicetree:
SENSOR_ERR("error in device tree\n");
err_taos_data_free:
err_chip_id_or_i2c_error:
kfree(taos);
done:
return ret;
}
static int taos_suspend(struct device *dev)
{
/* We disable power only if proximity is disabled. If proximity
is enabled, we leave power on because proximity is allowed
to wake up device. We remove power without changing
taos->power_state because we use that state in resume.
*/
struct i2c_client *client = to_i2c_client(dev);
struct taos_data *taos = i2c_get_clientdata(client);
if (taos->power_state & LIGHT_ENABLED)
taos_light_disable(taos);
if (taos->power_state == LIGHT_ENABLED)
taos_chip_off(taos);
if (taos->power_state & PROXIMITY_ENABLED)
disable_irq(taos->irq);
return 0;
}
static int taos_resume(struct device *dev)
{
/* Turn power back on if we were before suspend. */
struct i2c_client *client = to_i2c_client(dev);
struct taos_data *taos = i2c_get_clientdata(client);
#ifdef CONFIG_SENSORS_TMD3700
if (taos->power_state == LIGHT_ENABLED)
taos_chip_enable(taos);
#else
if (taos->power_state == LIGHT_ENABLED)
taos_chip_on(taos);
#endif
if (taos->power_state & LIGHT_ENABLED)
taos_light_enable(taos);
if (taos->power_state & PROXIMITY_ENABLED)
enable_irq(taos->irq);
SENSOR_INFO(" is called\n");
return 0;
}
static int taos_i2c_remove(struct i2c_client *client)
{
struct taos_data *taos = i2c_get_clientdata(client);
if (taos->power_state) {
if (taos->power_state & LIGHT_ENABLED)
taos_light_disable(taos);
}
/* free irq */
free_irq(taos->irq, NULL);
gpio_free(taos->pdata->als_int);
/* remove proxmity sensor */
sysfs_remove_group(&taos->proximity_input_dev->dev.kobj, &proximity_attribute_group);
sensors_remove_symlink(&taos->proximity_input_dev->dev.kobj,taos->proximity_input_dev->name);
sensors_unregister(taos->proximity_dev, prox_sensor_attrs);
input_unregister_device(taos->proximity_input_dev);
input_free_device(taos->proximity_input_dev);
/* remove light sensor */
sysfs_remove_group(&taos->light_input_dev->dev.kobj, &light_attribute_group);
sensors_remove_symlink(&taos->light_input_dev->dev.kobj,taos->light_input_dev->name);
sensors_unregister(taos->light_dev, lightsensor_additional_attributes);
input_unregister_device(taos->light_input_dev);
input_free_device(taos->light_input_dev);
/* destroy workqueue */
destroy_workqueue(taos->wq);
destroy_workqueue(taos->wq_avg);
mutex_destroy(&taos->power_lock);
mutex_destroy(&taos->prox_mutex);
wake_lock_destroy(&taos->prx_wake_lock);
kfree(taos);
return 0;
}
static void taos_i2c_shutdown(struct i2c_client *client)
{
struct taos_data *taos = i2c_get_clientdata(client);
SENSOR_INFO("taos_i2c_shutdown\n");
if (taos->power_state & LIGHT_ENABLED)
taos_light_disable(taos);
if (taos->power_state == LIGHT_ENABLED)
taos_chip_off(taos);
if (taos->power_state & PROXIMITY_ENABLED)
disable_irq(taos->irq);
SENSOR_INFO("taos_i2c_shutdown done\n");
}
static const struct i2c_device_id taos_device_id[] = {
{"taos", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, taos_device_id);
static const struct dev_pm_ops taos_pm_ops = {
.suspend = taos_suspend,
.resume = taos_resume
};
#ifdef CONFIG_OF
static struct of_device_id tm37xx_match_table[] = {
{ .compatible = "taos,tmd3700",},
{ .compatible = "taos,tmd3782",},
};
#else
#define tm37xx_match_table NULL
#endif
static struct i2c_driver taos_i2c_driver = {
.driver = {
.name = "taos",
.owner = THIS_MODULE,
.pm = &taos_pm_ops,
.of_match_table = tm37xx_match_table,
},
.probe = taos_i2c_probe,
.remove = taos_i2c_remove,
.shutdown = taos_i2c_shutdown,
.id_table = taos_device_id,
};
static int __init taos_init(void)
{
printk("[SENSOR] taos_init\n");
return i2c_add_driver(&taos_i2c_driver);
}
static void __exit taos_exit(void)
{
i2c_del_driver(&taos_i2c_driver);
}
module_init(taos_init);
module_exit(taos_exit);
MODULE_AUTHOR("SAMSUNG");
MODULE_DESCRIPTION("Optical Sensor driver for taos");
MODULE_LICENSE("GPL");