1814 lines
48 KiB
C
Executable File
1814 lines
48 KiB
C
Executable File
/*
|
|
* 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>
|
|
#include "tmd3725.h"
|
|
|
|
#define MODULE_NAME_PROX "proximity_sensor"
|
|
#define MODULE_NAME_LIGHT "light_sensor"
|
|
#define VENDOR_NAME "TAOS"
|
|
#define CHIP_NAME "TMD3725"
|
|
|
|
#define DEFAULT_COEF_R (-220) /* -0.22 */
|
|
#define DEFAULT_COEF_G 110 /* 0.11 */
|
|
#define DEFAULT_COEF_B (-1120) /* -1.12 */
|
|
#define DEFAULT_COEF_C (1000) /* 1 */
|
|
#define DEFAULT_DGF 831 /* 831.54 */
|
|
#define DEFAULT_CCT_COEF 4091 /* 4091 */
|
|
#define DEFAULT_CCT_OFFSET 227 /* 227 */
|
|
#define DEFAULT_LUX_MULTIPLE 10 /* lux x 1 */
|
|
#define DEFAULT_THRESHOLD_DET_HI 55 /* high threshold */
|
|
#define DEFAULT_THRESHOLD_STILL_DET_LOW 40 /* low threshold */
|
|
#define DEFAULT_THRESHOLD_STILL_DET_HI 250 /* 2nd high threshold */
|
|
#define DEFAULT_THRESHOLD_REL_LOW 130 /* 2nd low threshold */
|
|
#define DEFAULT_ATIME 0x11 /* 50.4 msec */
|
|
#define DEFAULT_PRATE 0x38 /* 5ms */
|
|
#define DEFAULT_WTIME 0x00 /* 2.8 msec */
|
|
#ifdef CONFIG_SENSORS_TMD3725_RF_NOISE_DEFENCE_CODE
|
|
#define DEFAULT_WTIME_FOR_PROX_ONLY 0x12 /* 53.2 msec */
|
|
#endif
|
|
#define DEFAULT_PPULSE 0x96 /* ppulse_len: 16us, ppluse: 23 */
|
|
#define DEFAULT_PGCFG1 0x08 /* pagin 1x, pgldrive 54mA */
|
|
#define DEFAULT_AGAIN 0x02
|
|
|
|
#define TMD3725_CHIP_ID 0xE4
|
|
#define LIGHT_LOG_TIME 30 /* 200m X 30 */
|
|
#define PROX_WAKE_LOCK_TIME (3 * HZ) /* 3 Sec */
|
|
#define PROX_AVG_COUNT 40
|
|
#define PROX_MAX_DATA 0xff
|
|
#define PROX_MIN_DATA 0
|
|
#define PROX_DETECT_OFFSET 250
|
|
#define PROX_COMPENSATION_OFFSET 40
|
|
|
|
#define ATIME_INTERVAL 28 /* 2.81ms */
|
|
#define LIMIT_GAIN_1_CLEAR_DATA 25
|
|
#define LIMIT_GAIN_16_CLEAR_DATA 15000
|
|
#define LIMIT_MAX_CLEAR_DATA 18500
|
|
#define LIGHT_MAX_LUX 150000
|
|
|
|
#define AZ_CONFIG_SET 0x7f /* one-shot autozero */
|
|
#define PERSIST_TIME_SET 0x30
|
|
#define BINARY_SEARCH_SET (0x03 << 5)
|
|
#define CALIBRATION_SET 0x01
|
|
#define DEFAULT_LIGHT_POLL_DELAY 100 /* 100 ms */
|
|
#define DEFAULT_PROX_AVG_POLL_DELAY (2000 * NSEC_PER_MSEC) /* 2 sec */
|
|
|
|
/* driver data */
|
|
struct tmd3725_data {
|
|
struct i2c_client *i2c_client;
|
|
struct input_dev *light_input_dev;
|
|
struct input_dev *prox_input_dev;
|
|
struct device *light_dev;
|
|
struct device *prox_dev;
|
|
struct work_struct work_prox;
|
|
struct work_struct work_prox_avg;
|
|
struct mutex mode_lock;
|
|
struct mutex prox_mutex;
|
|
struct mutex enable_lock;
|
|
struct wake_lock prx_wake_lock;
|
|
struct hrtimer prox_avg_timer;
|
|
struct delayed_work work_light;
|
|
struct workqueue_struct *wq;
|
|
struct workqueue_struct *prox_avg_wq;
|
|
struct regulator *prox_vled;
|
|
int64_t light_poll_delay;
|
|
ktime_t prox_avg_poll_delay;
|
|
|
|
u8 power_state;
|
|
u16 op_mode_state;
|
|
s32 clear;
|
|
s32 red;
|
|
s32 green;
|
|
s32 blue;
|
|
int lux;
|
|
int count_log_time;
|
|
int dgf;
|
|
int cct_coef;
|
|
int cct_offset;
|
|
int coef_r;
|
|
int coef_g;
|
|
int coef_b;
|
|
int coef_c;
|
|
int als_time;
|
|
int prate;
|
|
int wtime;
|
|
int ppulse;
|
|
int pgcfg1;
|
|
int als_gain;
|
|
int lux_mul;
|
|
|
|
int prox_irq;
|
|
int prox_avg[3];
|
|
int prox_avg_enable;
|
|
int prox_offset;
|
|
int prox_level_state;
|
|
int prox_irq_gpio;
|
|
int prox_thd_det_hi;
|
|
int prox_thd_still_det_low;
|
|
int prox_thd_still_det_hi;
|
|
int prox_thd_rel_low;
|
|
int prox_vled_ldo_pin;
|
|
bool prox_cal_complete;
|
|
};
|
|
|
|
static int tmd3725_prox_vled_onoff(struct tmd3725_data *taos, int onoff)
|
|
{
|
|
int err;
|
|
|
|
SENSOR_INFO("%s, ldo:%d\n", (onoff) ? "on" : "off",
|
|
taos->prox_vled_ldo_pin);
|
|
|
|
/* ldo control */
|
|
if (taos->prox_vled_ldo_pin) {
|
|
gpio_set_value(taos->prox_vled_ldo_pin, onoff);
|
|
if (onoff)
|
|
msleep(20);
|
|
return 0;
|
|
}
|
|
|
|
/* regulator(PMIC) control */
|
|
if (!taos->prox_vled) {
|
|
SENSOR_INFO("VLED get regulator\n");
|
|
taos->prox_vled =
|
|
regulator_get(&taos->i2c_client->dev, "taos,vled");
|
|
if (IS_ERR(taos->prox_vled)) {
|
|
SENSOR_ERR("regulator_get fail\n");
|
|
taos->prox_vled = NULL;
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
if (onoff) {
|
|
if (regulator_is_enabled(taos->prox_vled)) {
|
|
SENSOR_INFO("Regulator already enabled\n");
|
|
return 0;
|
|
}
|
|
|
|
err = regulator_enable(taos->prox_vled);
|
|
if (err)
|
|
SENSOR_ERR("Failed to enable vled.\n");
|
|
|
|
usleep_range(10000, 11000);
|
|
} else {
|
|
err = regulator_disable(taos->prox_vled);
|
|
if (err)
|
|
SENSOR_ERR("Failed to disable vled.\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tmd3725_i2c_read_data(struct tmd3725_data *taos, u8 reg)
|
|
{
|
|
int ret;
|
|
|
|
ret = i2c_smbus_read_byte_data(taos->i2c_client, reg);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tmd3725_i2c_write_data(struct tmd3725_data *taos, u8 reg, u8 val)
|
|
{
|
|
int ret;
|
|
|
|
ret = i2c_smbus_write_byte_data(taos->i2c_client,
|
|
(CMD_REG | reg), val);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tmd3725_i2c_modify_write(struct tmd3725_data *taos,
|
|
u8 reg, u8 mask, u8 val)
|
|
{
|
|
int ret;
|
|
|
|
ret = i2c_smbus_read_byte_data(taos->i2c_client, reg);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("read failed %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = (ret & (~mask)) | val;
|
|
ret = i2c_smbus_write_byte_data(taos->i2c_client,
|
|
(CMD_REG | reg), ret);
|
|
if (ret < 0)
|
|
SENSOR_ERR("write failed %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void tmd3725_clear_interrupt(struct tmd3725_data *taos)
|
|
{
|
|
int ret;
|
|
|
|
SENSOR_INFO("called\n");
|
|
|
|
ret = tmd3725_i2c_read_data(taos, STATUS);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed %d\n", ret);
|
|
}
|
|
|
|
static int tmd3725_light_get_cct(struct tmd3725_data *taos)
|
|
{
|
|
int cct = taos->cct_coef;
|
|
|
|
if (taos->red != 0)
|
|
cct = (cct * taos->blue) / taos->red;
|
|
|
|
cct += taos->cct_offset;
|
|
return cct;
|
|
}
|
|
|
|
static int tmd3725_light_get_lux(struct tmd3725_data *taos)
|
|
{
|
|
u8 reg_gain;
|
|
s32 rp1, gp1, bp1, cp1;
|
|
s32 calculated_lux;
|
|
int gain;
|
|
int ret;
|
|
|
|
ret = i2c_smbus_read_word_data(taos->i2c_client, CFG1);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("failed %d\n", ret);
|
|
return taos->lux;
|
|
}
|
|
reg_gain = (u16)ret & 0xff;
|
|
|
|
taos->clear = i2c_smbus_read_word_data(taos->i2c_client, CLR_CHAN0LO);
|
|
taos->red = i2c_smbus_read_word_data(taos->i2c_client, RED_CHAN1LO);
|
|
taos->green = i2c_smbus_read_word_data(taos->i2c_client, GRN_CHAN1LO);
|
|
taos->blue = i2c_smbus_read_word_data(taos->i2c_client, BLU_CHAN1LO);
|
|
|
|
if ((taos->clear < 0) || (taos->red < 0) ||
|
|
(taos->green < 0) || (taos->blue < 0)) {
|
|
SENSOR_ERR("rgb read failed\n");
|
|
|
|
return taos->lux;
|
|
}
|
|
|
|
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:
|
|
gain = 1;
|
|
break;
|
|
}
|
|
|
|
if (gain == 1 && taos->clear < LIMIT_GAIN_1_CLEAR_DATA) {
|
|
reg_gain = 0x02; /* Gain 16x */
|
|
|
|
ret = tmd3725_i2c_write_data(taos, CFG1, reg_gain);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed %d\n", ret);
|
|
|
|
return -1;
|
|
} else if (gain == 16 && taos->clear > LIMIT_GAIN_16_CLEAR_DATA) {
|
|
reg_gain = 0x00; /* Gain 1x */
|
|
|
|
ret = tmd3725_i2c_write_data(taos, CFG1, reg_gain);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed %d\n", ret);
|
|
|
|
return -1;
|
|
}
|
|
|
|
if ((taos->clear >= LIMIT_MAX_CLEAR_DATA) && (gain == 1))
|
|
return LIGHT_MAX_LUX;
|
|
|
|
/* calculate lux */
|
|
rp1 = taos->red * taos->coef_r;
|
|
gp1 = taos->green * taos->coef_g;
|
|
bp1 = taos->blue * taos->coef_b;
|
|
cp1 = taos->clear * taos->coef_c;
|
|
|
|
calculated_lux = (rp1 + gp1 + bp1 + cp1) / 1000;
|
|
if (calculated_lux < 0)
|
|
calculated_lux = 0;
|
|
else {
|
|
/* divide by CPL, CPL = (ATIME_MS * ALS_GAIN / DGF); */
|
|
calculated_lux = calculated_lux * taos->dgf;
|
|
calculated_lux *= taos->lux_mul;
|
|
calculated_lux /= ((taos->als_time + 1) * ATIME_INTERVAL);
|
|
calculated_lux /= gain;
|
|
}
|
|
|
|
taos->lux = (int)calculated_lux;
|
|
taos->als_gain = gain;
|
|
|
|
return taos->lux;
|
|
}
|
|
|
|
static int tmd3725_initialize_chip(struct tmd3725_data *taos)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = tmd3725_i2c_write_data(taos, CFG3, INT_READ_CLEAR);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed to write cfg3 reg %d\n", ret);
|
|
|
|
ret = tmd3725_i2c_write_data(taos, AZ_CONFIG, AZ_CONFIG_SET);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed to write auto zero cfg reg %d\n", ret);
|
|
|
|
ret = tmd3725_i2c_write_data(taos, CFG1, taos->als_gain);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed to write als gain ctrl reg %d\n", ret);
|
|
|
|
ret = tmd3725_i2c_write_data(taos, ALS_TIME, taos->als_time);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed to write als time reg %d\n", ret);
|
|
|
|
ret = tmd3725_i2c_write_data(taos, PPERS, PERSIST_TIME_SET);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed to write proximity persistence %d\n", ret);
|
|
|
|
ret = tmd3725_i2c_write_data(taos, PGCFG0, taos->ppulse);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed to write proximity pulse %d\n", ret);
|
|
|
|
ret = tmd3725_i2c_write_data(taos, PRX_RATE, taos->prate);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed to write proximity sample rate %d\n", ret);
|
|
|
|
ret = tmd3725_i2c_write_data(taos, WAIT_TIME, taos->wtime);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed to write als wait time reg %d\n", ret);
|
|
|
|
ret = tmd3725_i2c_write_data(taos, PGCFG1, taos->pgcfg1);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed to write prox pulse reg %d\n", ret);
|
|
|
|
ret = tmd3725_i2c_modify_write(taos, CALIBCFG,
|
|
AUTO_OFFSET_ADJ, AUTO_OFFSET_ADJ);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed to write enable state reg %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tmd3725_set_op_mode(struct tmd3725_data *taos, u16 op_mode, u8 state)
|
|
{
|
|
u8 intenab_val, enable_val;
|
|
int ret = -1;
|
|
|
|
mutex_lock(&taos->mode_lock);
|
|
|
|
if (state)
|
|
taos->op_mode_state |= (op_mode << 0);
|
|
else
|
|
taos->op_mode_state &= ~(op_mode << 0);
|
|
|
|
if (op_mode == MODE_PROX)
|
|
tmd3725_clear_interrupt(taos);
|
|
|
|
switch (taos->op_mode_state) {
|
|
case MODE_OFF:
|
|
intenab_val = CNTL_REG_CLEAR;
|
|
enable_val = CNTL_PWRON;
|
|
break;
|
|
case MODE_ALS:
|
|
intenab_val = CNTL_REG_CLEAR;
|
|
enable_val = PON | AEN;
|
|
break;
|
|
case MODE_PROX:
|
|
if (taos->prox_cal_complete == false) {
|
|
SENSOR_INFO("returned in MODE_PROX\n");
|
|
mutex_unlock(&taos->mode_lock);
|
|
return 0;
|
|
}
|
|
intenab_val = PIEN | ZIEN;
|
|
enable_val = PEN | PON | WEN;
|
|
break;
|
|
case MODE_ALS_PROX:
|
|
if (taos->prox_cal_complete == false) {
|
|
SENSOR_INFO("returned in MODE_ALS_PROX\n");
|
|
mutex_unlock(&taos->mode_lock);
|
|
return 0;
|
|
}
|
|
intenab_val = PIEN | ZIEN;
|
|
enable_val = PEN | PON | AEN | WEN;
|
|
break;
|
|
default:
|
|
mutex_unlock(&taos->mode_lock);
|
|
return ret;
|
|
}
|
|
|
|
ret = tmd3725_i2c_write_data(taos, INTENAB, intenab_val);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("INTENAB failed %d\n", ret);
|
|
mutex_unlock(&taos->mode_lock);
|
|
return ret;
|
|
}
|
|
ret = tmd3725_i2c_write_data(taos, CMD_REG, enable_val);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("CMD_REG failed %d\n", ret);
|
|
mutex_unlock(&taos->mode_lock);
|
|
return ret;
|
|
}
|
|
|
|
mutex_unlock(&taos->mode_lock);
|
|
return 0;
|
|
}
|
|
|
|
static void tmd3725_light_enable(struct tmd3725_data *taos)
|
|
{
|
|
SENSOR_INFO("start poll timer\n");
|
|
taos->count_log_time = LIGHT_LOG_TIME;
|
|
schedule_delayed_work(&taos->work_light,
|
|
msecs_to_jiffies(DEFAULT_LIGHT_POLL_DELAY));
|
|
}
|
|
|
|
static void tmd3725_light_disable(struct tmd3725_data *taos)
|
|
{
|
|
SENSOR_INFO("cancelling poll timer\n");
|
|
cancel_delayed_work_sync(&taos->work_light);
|
|
}
|
|
|
|
static void tmd3725_work_func_light(struct work_struct *work)
|
|
{
|
|
struct tmd3725_data *taos = container_of((struct delayed_work *)work,
|
|
struct tmd3725_data, work_light);
|
|
|
|
int lux = tmd3725_light_get_lux(taos);
|
|
int cct = tmd3725_light_get_cct(taos);
|
|
|
|
if (lux < 0) {
|
|
schedule_delayed_work(&taos->work_light,
|
|
msecs_to_jiffies(DEFAULT_LIGHT_POLL_DELAY));
|
|
return;
|
|
}
|
|
|
|
if (taos->count_log_time >= LIGHT_LOG_TIME) {
|
|
SENSOR_INFO("R:%d G:%d B:%d C:%d lux:%d again:%d\n",
|
|
taos->red, taos->green, taos->blue,
|
|
taos->clear, lux, taos->als_gain);
|
|
taos->count_log_time = 0;
|
|
} else {
|
|
taos->count_log_time++;
|
|
}
|
|
|
|
input_report_rel(taos->light_input_dev, REL_MISC, lux + 1);
|
|
input_report_rel(taos->light_input_dev, REL_WHEEL, cct);
|
|
input_sync(taos->light_input_dev);
|
|
|
|
schedule_delayed_work(&taos->work_light,
|
|
msecs_to_jiffies(taos->light_poll_delay));
|
|
}
|
|
|
|
static int tmd3725_prox_get_adc(struct tmd3725_data *taos)
|
|
{
|
|
int adc;
|
|
|
|
adc = tmd3725_i2c_read_data(taos, PRX_DATA_HIGH);
|
|
if (adc < 0)
|
|
return PROX_MIN_DATA;
|
|
else if (adc > PROX_MAX_DATA)
|
|
return PROX_MAX_DATA;
|
|
|
|
return adc;
|
|
}
|
|
|
|
static int tmd3725_prox_get_threshold(struct tmd3725_data *taos, u8 buf)
|
|
{
|
|
int threshold;
|
|
|
|
threshold = tmd3725_i2c_read_data(taos, buf);
|
|
if (threshold < 0)
|
|
SENSOR_ERR("failed %d\n", threshold);
|
|
|
|
return threshold;
|
|
}
|
|
|
|
static void tmd3725_prox_set_threshold(struct tmd3725_data *taos)
|
|
{
|
|
u8 prox_int_thresh[2];
|
|
int ret;
|
|
|
|
switch (taos->prox_level_state) {
|
|
case STATE_INIT:
|
|
prox_int_thresh[0] = PROX_MAX_DATA;
|
|
prox_int_thresh[1] = PROX_MIN_DATA;
|
|
break;
|
|
case STATE_DETECTION:
|
|
prox_int_thresh[0] = PROX_MIN_DATA;
|
|
prox_int_thresh[1] = taos->prox_thd_det_hi;
|
|
break;
|
|
case STATE_STILL_DETECTION:
|
|
prox_int_thresh[0] = taos->prox_thd_still_det_low;
|
|
prox_int_thresh[1] = taos->prox_thd_still_det_hi;
|
|
break;
|
|
case STATE_RELEASE:
|
|
prox_int_thresh[0] = taos->prox_thd_rel_low;
|
|
prox_int_thresh[1] = PROX_MAX_DATA;
|
|
break;
|
|
case STATE_HIGH_OFFSET:
|
|
prox_int_thresh[0] = taos->prox_thd_still_det_low;
|
|
prox_int_thresh[1] = PROX_MAX_DATA;
|
|
break;
|
|
default:
|
|
SENSOR_ERR("Unknown state err = %d\n", taos->prox_level_state);
|
|
return;
|
|
}
|
|
|
|
ret = tmd3725_i2c_write_data(taos, PRX_MINTHRESH, prox_int_thresh[0]);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed %d\n", ret);
|
|
|
|
ret = tmd3725_i2c_write_data(taos, PRX_MAXTHRESH, prox_int_thresh[1]);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed %d\n", ret);
|
|
}
|
|
|
|
static int tmd3725_prox_get_offset(struct tmd3725_data *taos)
|
|
{
|
|
int ret;
|
|
u8 offset_l;
|
|
u8 offset_h;
|
|
|
|
ret = tmd3725_i2c_read_data(taos, POFFSET_L);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("POFFSET_L failed, err = %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
offset_l = (u8)ret;
|
|
ret = tmd3725_i2c_read_data(taos, POFFSET_H);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("POFFSET_H failed, err = %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
offset_h = (u8)ret;
|
|
if ((char)offset_h < 0)
|
|
taos->prox_offset = offset_l * (-1);
|
|
else
|
|
taos->prox_offset = offset_l;
|
|
|
|
return taos->prox_offset;
|
|
}
|
|
|
|
static void tmd3725_prox_set_offset(struct tmd3725_data *taos, u8 offset)
|
|
{
|
|
int ret;
|
|
|
|
ret = tmd3725_i2c_write_data(taos, POFFSET_L, offset);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("POFFSET_L failed, err = %d\n", ret);
|
|
return;
|
|
}
|
|
|
|
ret = tmd3725_i2c_write_data(taos, POFFSET_H, PROX_MIN_DATA);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("POFFSET_H failed, err = %d\n", ret);
|
|
return;
|
|
}
|
|
|
|
taos->prox_offset = offset;
|
|
}
|
|
|
|
static void tmd3725_prox_initialize_target(struct tmd3725_data *taos)
|
|
{
|
|
int ret;
|
|
|
|
mutex_lock(&taos->mode_lock);
|
|
|
|
SENSOR_INFO("Calibration Start !!!\n");
|
|
|
|
ret = tmd3725_i2c_write_data(taos, ENABLE, PON);
|
|
if (ret < 0)
|
|
SENSOR_ERR("ENABLE failed %d\n", ret);
|
|
|
|
ret = tmd3725_i2c_write_data(taos, INTENAB, CIEN);
|
|
if (ret < 0)
|
|
SENSOR_ERR("INTENAB failed %d\n", ret);
|
|
|
|
/* < BINARY SEARCH TARGET > */
|
|
/*
|
|
value : TARGET
|
|
0 : 0
|
|
1 : 1
|
|
2 : 3
|
|
3 : 7
|
|
4 : 15
|
|
5 : 31
|
|
6 : 63
|
|
7 : 127
|
|
*/
|
|
ret = tmd3725_i2c_modify_write(taos, CALIBCFG,
|
|
BINSRCH_TARGET, BINARY_SEARCH_SET);
|
|
if (ret < 0)
|
|
SENSOR_ERR("CALIBCFG failed %d\n", ret);
|
|
|
|
ret = tmd3725_i2c_write_data(taos, CALIB , CALIBRATION_SET);
|
|
if (ret < 0)
|
|
SENSOR_ERR("CALIB failed %d\n", ret);
|
|
|
|
taos->prox_cal_complete = false;
|
|
taos->op_mode_state |= (MODE_PROX << 0);
|
|
|
|
mutex_unlock(&taos->mode_lock);
|
|
}
|
|
|
|
static void tmd3725_prox_send_event(struct tmd3725_data *taos, int val)
|
|
{
|
|
input_report_abs(taos->prox_input_dev, ABS_DISTANCE, val);
|
|
input_sync(taos->prox_input_dev);
|
|
|
|
SENSOR_INFO("prox value = %d\n", val);
|
|
}
|
|
|
|
#ifdef CONFIG_SENSORS_TMD3725_RF_NOISE_DEFENCE_CODE
|
|
static void tmd3725_change_wtime(struct tmd3725_data *taos, int val)
|
|
{
|
|
int ret;
|
|
|
|
taos->wtime = val;
|
|
|
|
ret = tmd3725_i2c_write_data(taos, WAIT_TIME, taos->wtime);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed to write als wait time reg %d\n", ret);
|
|
else
|
|
SENSOR_INFO("change wait time reg %d\n", taos->wtime);
|
|
}
|
|
#endif
|
|
|
|
static void tmd3725_prox_process_state(struct tmd3725_data *taos)
|
|
{
|
|
int adc_data;
|
|
int thresh_hi;
|
|
int thresh_low;
|
|
int prox_state;
|
|
|
|
mutex_lock(&taos->prox_mutex);
|
|
adc_data = tmd3725_prox_get_adc(taos);
|
|
mutex_unlock(&taos->prox_mutex);
|
|
|
|
thresh_hi = tmd3725_prox_get_threshold(taos, PRX_MAXTHRESH);
|
|
thresh_low = tmd3725_prox_get_threshold(taos, PRX_MINTHRESH);
|
|
SENSOR_INFO("hi = %d, low = %d, adc_data = %d\n",
|
|
thresh_hi, thresh_low, adc_data);
|
|
|
|
if ((taos->prox_level_state < STATE_INIT) ||
|
|
((taos->prox_level_state > STATE_RELEASE))) {
|
|
SENSOR_ERR("Unavailable STATE\n");
|
|
return;
|
|
}
|
|
|
|
switch (taos->prox_level_state) {
|
|
case STATE_INIT:
|
|
tmd3725_prox_get_offset(taos);
|
|
SENSOR_INFO("STATE_INIT - offset: %d\n", taos->prox_offset);
|
|
|
|
if (taos->prox_offset > PROX_DETECT_OFFSET) {
|
|
tmd3725_prox_set_offset(taos,
|
|
taos->prox_offset - PROX_COMPENSATION_OFFSET);
|
|
taos->prox_level_state = STATE_RELEASE;
|
|
prox_state = PROX_CLOSE;
|
|
} else {
|
|
if (adc_data >= taos->prox_thd_still_det_hi) {
|
|
taos->prox_level_state = STATE_RELEASE;
|
|
prox_state = PROX_CLOSE;
|
|
} else if (adc_data >= taos->prox_thd_det_hi) {
|
|
taos->prox_level_state = STATE_STILL_DETECTION;
|
|
prox_state = PROX_CLOSE;
|
|
} else {
|
|
taos->prox_level_state = STATE_DETECTION;
|
|
prox_state = PROX_FAR;
|
|
}
|
|
}
|
|
input_report_abs(taos->prox_input_dev,
|
|
ABS_DISTANCE, !prox_state);
|
|
tmd3725_prox_send_event(taos, prox_state);
|
|
|
|
break;
|
|
case STATE_DETECTION:
|
|
SENSOR_INFO("STATE_DETECTION\n");
|
|
taos->prox_level_state = STATE_STILL_DETECTION;
|
|
tmd3725_prox_send_event(taos, PROX_CLOSE);
|
|
break;
|
|
case STATE_STILL_DETECTION:
|
|
SENSOR_INFO("STATE_STILL_DETECTION\n");
|
|
if (adc_data >= taos->prox_thd_still_det_hi) {
|
|
taos->prox_level_state = STATE_RELEASE;
|
|
} else if (adc_data <= taos->prox_thd_still_det_low) {
|
|
taos->prox_level_state = STATE_DETECTION;
|
|
tmd3725_prox_send_event(taos, PROX_FAR);
|
|
}
|
|
break;
|
|
case STATE_RELEASE:
|
|
SENSOR_INFO("STATE_RELEASE\n");
|
|
tmd3725_prox_initialize_target(taos);
|
|
taos->prox_level_state = STATE_DETECTION;
|
|
tmd3725_prox_send_event(taos, PROX_FAR);
|
|
break;
|
|
case STATE_HIGH_OFFSET:
|
|
SENSOR_INFO("STATE_HIGH_OFFSET\n");
|
|
break;
|
|
default:
|
|
SENSOR_INFO("NONE State or Unknown state\n");
|
|
break;
|
|
}
|
|
|
|
tmd3725_prox_set_threshold(taos);
|
|
}
|
|
|
|
static void tmd3725_work_func_prox_avg(struct work_struct *work)
|
|
{
|
|
struct tmd3725_data *taos = container_of(work, struct tmd3725_data,
|
|
work_prox_avg);
|
|
int prox_level_state;
|
|
int min = 0, max = 0, avg = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < PROX_AVG_COUNT; i++) {
|
|
mutex_lock(&taos->prox_mutex);
|
|
prox_level_state = tmd3725_prox_get_adc(taos);
|
|
mutex_unlock(&taos->prox_mutex);
|
|
if (prox_level_state > PROX_MIN_DATA) {
|
|
avg += prox_level_state;
|
|
if (i == 0)
|
|
min = prox_level_state;
|
|
if (prox_level_state < min)
|
|
min = prox_level_state;
|
|
if (prox_level_state > max)
|
|
max = prox_level_state;
|
|
} else {
|
|
prox_level_state = PROX_MIN_DATA;
|
|
}
|
|
msleep(40);
|
|
}
|
|
avg /= i;
|
|
taos->prox_avg[0] = min;
|
|
taos->prox_avg[1] = avg;
|
|
taos->prox_avg[2] = max;
|
|
}
|
|
|
|
static void tmd3725_work_func_prox(struct work_struct *work)
|
|
{
|
|
u8 status;
|
|
int ret;
|
|
struct tmd3725_data *taos =
|
|
container_of(work, struct tmd3725_data, work_prox);
|
|
|
|
/* disable INT */
|
|
disable_irq_nosync(taos->prox_irq);
|
|
|
|
/* Calibration Handle Event */
|
|
ret = tmd3725_i2c_read_data(taos, STATUS);
|
|
if (ret < 0)
|
|
SENSOR_ERR("STATUS failed %d\n", ret);
|
|
|
|
tmd3725_clear_interrupt(taos);
|
|
enable_irq(taos->prox_irq);
|
|
|
|
status = (u8)ret;
|
|
if (status & CINT) {
|
|
taos->prox_cal_complete = true;
|
|
ret = tmd3725_i2c_modify_write(taos,
|
|
CALIBSTAT, CALIB_FINISHED, CALIB_FINISHED);
|
|
if (ret < 0)
|
|
SENSOR_ERR("CALIBSTAT failed %d\n", ret);
|
|
|
|
tmd3725_set_op_mode(taos, MODE_PROX, ON);
|
|
tmd3725_prox_get_offset(taos);
|
|
SENSOR_INFO("CINT - status: 0x%x, offset: %d\n",
|
|
status, taos->prox_offset);
|
|
return;
|
|
}
|
|
|
|
if (status & ZINT) {
|
|
tmd3725_prox_get_offset(taos);
|
|
SENSOR_INFO("ZINT - status: 0x%x, offset: %d\n",
|
|
status, taos->prox_offset);
|
|
tmd3725_prox_initialize_target(taos);
|
|
return;
|
|
}
|
|
|
|
if (status & PINT) {
|
|
if (taos->prox_cal_complete == true)
|
|
tmd3725_prox_process_state(taos);
|
|
else
|
|
SENSOR_INFO("prox cal is not completed!!\n");
|
|
}
|
|
}
|
|
|
|
irqreturn_t tmd3725_prox_irq_handler(int irq, void *data)
|
|
{
|
|
struct tmd3725_data *taos = data;
|
|
|
|
if (taos->prox_irq != -1) {
|
|
wake_lock_timeout(&taos->prx_wake_lock, PROX_WAKE_LOCK_TIME);
|
|
queue_work(taos->wq, &taos->work_prox);
|
|
}
|
|
|
|
SENSOR_INFO("taos interrupt handler is called\n");
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static enum hrtimer_restart tmd3725_prox_avg_timer_func(struct hrtimer *timer)
|
|
{
|
|
struct tmd3725_data *taos = container_of(timer, struct tmd3725_data,
|
|
prox_avg_timer);
|
|
queue_work(taos->prox_avg_wq, &taos->work_prox_avg);
|
|
hrtimer_forward_now(&taos->prox_avg_timer, taos->prox_avg_poll_delay);
|
|
|
|
return HRTIMER_RESTART;
|
|
}
|
|
|
|
static ssize_t tmd3725_light_poll_delay_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%lld\n", taos->light_poll_delay);
|
|
}
|
|
|
|
static ssize_t tmd3725_light_poll_delay_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
int64_t new_delay;
|
|
int err;
|
|
|
|
err = kstrtoll(buf, 10, &new_delay);
|
|
if (err < 0) {
|
|
SENSOR_ERR("invalid value %d\n", err);
|
|
return size;
|
|
}
|
|
|
|
SENSOR_INFO("new delay = %lldns, old delay = %lldms\n",
|
|
new_delay, taos->light_poll_delay);
|
|
|
|
mutex_lock(&taos->enable_lock);
|
|
if ((new_delay / NSEC_PER_MSEC) != (taos->light_poll_delay)) {
|
|
taos->light_poll_delay = new_delay / NSEC_PER_MSEC;
|
|
if (taos->power_state & LIGHT_ENABLED) {
|
|
tmd3725_light_disable(taos);
|
|
tmd3725_light_enable(taos);
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&taos->enable_lock);
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t tmd3725_light_enable_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n",
|
|
(taos->power_state & LIGHT_ENABLED) ? 1 : 0);
|
|
}
|
|
|
|
static ssize_t tmd3725_light_enable_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct tmd3725_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\n");
|
|
return size;
|
|
}
|
|
|
|
SENSOR_INFO("new_value = %d, old state = %d\n",
|
|
new_value, (taos->power_state & LIGHT_ENABLED) ? 1 : 0);
|
|
|
|
mutex_lock(&taos->enable_lock);
|
|
if (new_value && !(taos->power_state & LIGHT_ENABLED)) {
|
|
taos->power_state |= LIGHT_ENABLED;
|
|
#ifdef CONFIG_SENSORS_TMD3725_RF_NOISE_DEFENCE_CODE
|
|
tmd3725_change_wtime(taos, DEFAULT_WTIME);
|
|
#endif
|
|
tmd3725_set_op_mode(taos, MODE_ALS, ON);
|
|
tmd3725_light_enable(taos);
|
|
} else if (!new_value && (taos->power_state & LIGHT_ENABLED)) {
|
|
tmd3725_light_disable(taos);
|
|
tmd3725_set_op_mode(taos, MODE_ALS, OFF);
|
|
#ifdef CONFIG_SENSORS_TMD3725_RF_NOISE_DEFENCE_CODE
|
|
tmd3725_change_wtime(taos, DEFAULT_WTIME_FOR_PROX_ONLY);
|
|
#endif
|
|
taos->power_state &= ~LIGHT_ENABLED;
|
|
}
|
|
mutex_unlock(&taos->enable_lock);
|
|
return size;
|
|
}
|
|
|
|
static ssize_t tmd3725_prox_enable_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n",
|
|
(taos->power_state & PROXIMITY_ENABLED) ? 1 : 0);
|
|
}
|
|
|
|
static ssize_t tmd3725_prox_enable_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct tmd3725_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\n");
|
|
return size;
|
|
}
|
|
|
|
SENSOR_INFO("new_value = %d, old state = %d\n",
|
|
new_value, (taos->power_state & PROXIMITY_ENABLED) ? 1 : 0);
|
|
|
|
mutex_lock(&taos->enable_lock);
|
|
if (new_value && !(taos->power_state & PROXIMITY_ENABLED)) {
|
|
tmd3725_prox_vled_onoff(taos, ON);
|
|
tmd3725_prox_initialize_target(taos);
|
|
taos->prox_level_state = STATE_INIT;
|
|
tmd3725_prox_set_threshold(taos);
|
|
|
|
taos->power_state |= PROXIMITY_ENABLED;
|
|
|
|
enable_irq(taos->prox_irq);
|
|
enable_irq_wake(taos->prox_irq);
|
|
} else if (!new_value && (taos->power_state & PROXIMITY_ENABLED)) {
|
|
disable_irq_wake(taos->prox_irq);
|
|
disable_irq(taos->prox_irq);
|
|
|
|
tmd3725_set_op_mode(taos, MODE_PROX, OFF);
|
|
tmd3725_prox_vled_onoff(taos, OFF);
|
|
taos->power_state &= ~PROXIMITY_ENABLED;
|
|
}
|
|
mutex_unlock(&taos->enable_lock);
|
|
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR(poll_delay, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
tmd3725_light_poll_delay_show,
|
|
tmd3725_light_poll_delay_store);
|
|
|
|
static struct device_attribute dev_attr_light_enable =
|
|
__ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
tmd3725_light_enable_show, tmd3725_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 dev_attr_proximity_enable =
|
|
__ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
tmd3725_prox_enable_show, tmd3725_prox_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 ssize_t tmd3725_get_vendor_name(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR_NAME);
|
|
}
|
|
|
|
static ssize_t tmd3725_get_chip_name(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_NAME);
|
|
}
|
|
|
|
static ssize_t tmd3725_prox_level_state_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", tmd3725_prox_get_adc(taos));
|
|
}
|
|
|
|
static ssize_t tmd3725_prox_avg_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n",
|
|
taos->prox_avg[0], taos->prox_avg[1], taos->prox_avg[2]);
|
|
}
|
|
|
|
static ssize_t tmd3725_prox_avg_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct tmd3725_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\n");
|
|
return size;
|
|
}
|
|
|
|
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_avg_poll_delay));
|
|
hrtimer_start(&taos->prox_avg_timer,
|
|
taos->prox_avg_poll_delay, 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 size;
|
|
}
|
|
|
|
static ssize_t tmd3725_prox_thd_det_high_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", taos->prox_thd_det_hi);
|
|
}
|
|
|
|
static ssize_t tmd3725_prox_thd_det_high_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
int thresh_value, ret;
|
|
|
|
ret = kstrtoint(buf, 10, &thresh_value);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("kstrtoint failed. %d", ret);
|
|
return size;
|
|
}
|
|
|
|
SENSOR_INFO("thresh_hi = %d\n", thresh_value);
|
|
taos->prox_thd_det_hi = thresh_value;
|
|
tmd3725_prox_set_threshold(taos);
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t tmd3725_prox_thd_still_det_hi_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", taos->prox_thd_still_det_hi);
|
|
}
|
|
|
|
static ssize_t tmd3725_prox_thd_still_det_hi_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
int thresh_value, ret;
|
|
|
|
ret = kstrtoint(buf, 10, &thresh_value);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("kstrtoint failed. %d", ret);
|
|
return size;
|
|
}
|
|
|
|
SENSOR_INFO("thresh_low = %d\n", thresh_value);
|
|
taos->prox_thd_still_det_hi = thresh_value;
|
|
tmd3725_prox_set_threshold(taos);
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t tmd3725_prox_thd_still_det_low_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", taos->prox_thd_still_det_low);
|
|
}
|
|
|
|
static ssize_t tmd3725_prox_thd_still_det_low_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
int thresh_value, ret;
|
|
|
|
ret = kstrtoint(buf, 10, &thresh_value);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("kstrtoint failed. %d", ret);
|
|
return size;
|
|
}
|
|
|
|
SENSOR_INFO("thresh_low = %d\n", thresh_value);
|
|
taos->prox_thd_still_det_low = thresh_value;
|
|
tmd3725_prox_set_threshold(taos);
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t tmd3725_prox_thd_rel_low_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", taos->prox_thd_rel_low);
|
|
}
|
|
|
|
static ssize_t tmd3725_prox_thd_rel_low_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
int thresh_value, ret;
|
|
|
|
ret = kstrtoint(buf, 10, &thresh_value);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("kstrtoint failed. %d", ret);
|
|
return size;
|
|
}
|
|
|
|
SENSOR_INFO("thresh_low = %d\n", thresh_value);
|
|
taos->prox_thd_rel_low = thresh_value;
|
|
tmd3725_prox_set_threshold(taos);
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t tmd3725_prox_trim_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
|
|
return sprintf(buf, "%d\n", taos->prox_offset);
|
|
}
|
|
|
|
static ssize_t tmd3725_prox_trim_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
int trim_value, ret;
|
|
|
|
ret = kstrtoint(buf, 10, &trim_value);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("kstrtoint failed. %d", ret);
|
|
return size;
|
|
}
|
|
|
|
SENSOR_INFO("prox_trim = %d\n", trim_value);
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t tmd3725_write_register_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
int reg, val, ret;
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
|
|
if (sscanf(buf, "%2x,%2x", ®, &val) != 2) {
|
|
SENSOR_ERR("invalid value\n");
|
|
return count;
|
|
}
|
|
|
|
ret = tmd3725_i2c_write_data(taos, reg, val);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed %d\n", ret);
|
|
else
|
|
SENSOR_INFO("Register(0x%2x) data(0x%2x)\n", reg, val);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t tmd3725_read_register_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
u8 reg;
|
|
int offset = 0, val = 0;
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
|
|
for (reg = ALS_TIME; reg <= CFG1; reg++) {
|
|
val = tmd3725_i2c_read_data(taos, reg);
|
|
SENSOR_INFO("Read Reg: 0x%2x Value: 0x%2x\n", reg, val);
|
|
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
|
"Reg: 0x%2x Value: 0x%2x\n", reg, val);
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
static ssize_t tmd3725_dhr_sensor_info_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
uint8_t pgcfg1 = 0, pgcfg0 = 0;
|
|
uint8_t p_gain = 0, p_drive_cur = 0;
|
|
uint8_t per_time = 0;
|
|
uint8_t p_pulse = 0, p_pulse_len = 0;
|
|
uint8_t p_time = 0, l_atime = 0;
|
|
|
|
pgcfg1 = tmd3725_i2c_read_data(taos, PGCFG1);
|
|
p_gain = (pgcfg1 & 0XC0) >> 6;
|
|
p_drive_cur = pgcfg1 & 0x1F;
|
|
|
|
per_time = tmd3725_i2c_read_data(taos, PPERS);
|
|
pgcfg0 = tmd3725_i2c_read_data(taos, PGCFG0);
|
|
p_pulse_len = (pgcfg0 & 0XC0) >> 6;
|
|
p_pulse = pgcfg0 & 0X3F;
|
|
|
|
p_time = tmd3725_i2c_read_data(taos, PRX_RATE);
|
|
l_atime = tmd3725_i2c_read_data(taos, ALS_TIME);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "\"THD\":\"%d %d %d %d\","\
|
|
"\"PDRIVE_CURRENT\":\"%02x\","\
|
|
"\"PERSIST_TIME\":\"%02x\","\
|
|
"\"PPULSE\":\"%02x\","\
|
|
"\"PGAIN\":\"%02x\","\
|
|
"\"PTIME\":\"%02x\","\
|
|
"\"PPLUSE_LEN\":\"%02x\","\
|
|
"\"ATIME\":\"%02x\","\
|
|
"\"POFFSET\":\"%d\"\n",
|
|
taos->prox_thd_det_hi, taos->prox_thd_still_det_low,
|
|
taos->prox_thd_still_det_hi, taos->prox_thd_rel_low,
|
|
p_drive_cur, per_time, p_pulse,
|
|
p_gain, p_time, p_pulse_len, l_atime, taos->prox_offset);
|
|
}
|
|
|
|
static DEVICE_ATTR(vendor, S_IRUGO, tmd3725_get_vendor_name, NULL);
|
|
static DEVICE_ATTR(name, S_IRUGO, tmd3725_get_chip_name, NULL);
|
|
static struct device_attribute dev_attr_proximity_raw_data =
|
|
__ATTR(raw_data, S_IRUGO, tmd3725_prox_level_state_show, NULL);
|
|
static DEVICE_ATTR(prox_avg, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
tmd3725_prox_avg_show, tmd3725_prox_avg_store);
|
|
static DEVICE_ATTR(state, S_IRUGO, tmd3725_prox_level_state_show, NULL);
|
|
static DEVICE_ATTR(thresh_high, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
tmd3725_prox_thd_det_high_show,
|
|
tmd3725_prox_thd_det_high_store);
|
|
static DEVICE_ATTR(thresh_low, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
tmd3725_prox_thd_still_det_low_show,
|
|
tmd3725_prox_thd_still_det_low_store);
|
|
static DEVICE_ATTR(thresh_detect_high, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
tmd3725_prox_thd_still_det_hi_show,
|
|
tmd3725_prox_thd_still_det_hi_store);
|
|
static DEVICE_ATTR(thresh_detect_low, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
tmd3725_prox_thd_rel_low_show,
|
|
tmd3725_prox_thd_rel_low_store);
|
|
static DEVICE_ATTR(prox_trim, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
tmd3725_prox_trim_show, tmd3725_prox_trim_store);
|
|
static DEVICE_ATTR(register, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
tmd3725_read_register_show, tmd3725_write_register_store);
|
|
static DEVICE_ATTR(dhr_sensor_info, S_IRUSR | S_IRGRP,
|
|
tmd3725_dhr_sensor_info_show, NULL);
|
|
|
|
static struct device_attribute *prox_sensor_attrs[] = {
|
|
&dev_attr_vendor,
|
|
&dev_attr_name,
|
|
&dev_attr_state,
|
|
&dev_attr_prox_avg,
|
|
&dev_attr_proximity_raw_data,
|
|
&dev_attr_thresh_high,
|
|
&dev_attr_thresh_low,
|
|
&dev_attr_thresh_detect_high,
|
|
&dev_attr_thresh_detect_low,
|
|
&dev_attr_prox_trim,
|
|
&dev_attr_register,
|
|
&dev_attr_dhr_sensor_info,
|
|
NULL
|
|
};
|
|
|
|
static ssize_t tmd3725_light_get_lux_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", tmd3725_light_get_lux(taos));
|
|
}
|
|
|
|
static ssize_t tmd3725_light_get_raw_data_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct tmd3725_data *taos = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%u,%u,%u,%u,%u,%u\n",
|
|
taos->red, taos->green, taos->blue, taos->clear,
|
|
taos->als_time, taos->als_gain);
|
|
}
|
|
|
|
static DEVICE_ATTR(adc, S_IRUGO, tmd3725_light_get_lux_show, NULL);
|
|
static DEVICE_ATTR(lux, S_IRUGO, tmd3725_light_get_lux_show, NULL);
|
|
static struct device_attribute dev_attr_light_raw_data =
|
|
__ATTR(raw_data, S_IRUGO, tmd3725_light_get_raw_data_show, NULL);
|
|
|
|
static struct device_attribute *lightsensor_additional_attributes[] = {
|
|
&dev_attr_adc,
|
|
&dev_attr_lux,
|
|
&dev_attr_vendor,
|
|
&dev_attr_name,
|
|
&dev_attr_light_raw_data,
|
|
NULL
|
|
};
|
|
|
|
static int tmd3725_setup_irq(struct tmd3725_data *taos)
|
|
{
|
|
int ret = -EIO;
|
|
|
|
ret = gpio_request(taos->prox_irq_gpio, "gpio_proximity_irq");
|
|
if (ret < 0) {
|
|
SENSOR_ERR("gpio %d request failed %d\n",
|
|
taos->prox_irq_gpio, ret);
|
|
goto err_exit;
|
|
}
|
|
|
|
ret = gpio_direction_input(taos->prox_irq_gpio);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("failed to set gpio %d as input %d\n",
|
|
taos->prox_irq_gpio, ret);
|
|
goto err_gpio_direction_input;
|
|
}
|
|
|
|
taos->prox_irq = gpio_to_irq(taos->prox_irq_gpio);
|
|
|
|
ret = request_threaded_irq(taos->prox_irq, NULL,
|
|
tmd3725_prox_irq_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
|
"proximity_irq", taos);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("request_irq %d failed for gpio %d %d\n",
|
|
taos->prox_irq, taos->prox_irq_gpio, ret);
|
|
goto err_request_irq;
|
|
}
|
|
|
|
/* start with interrupts disabled */
|
|
disable_irq(taos->prox_irq);
|
|
return 0;
|
|
|
|
err_request_irq:
|
|
err_gpio_direction_input:
|
|
gpio_free(taos->prox_irq_gpio);
|
|
err_exit:
|
|
return ret;
|
|
}
|
|
|
|
static int tmd3725_parse_dt(struct tmd3725_data *taos, struct device *dev)
|
|
{
|
|
int ret = 0;
|
|
enum of_gpio_flags flags;
|
|
struct device_node *np = dev->of_node;
|
|
|
|
|
|
taos->prox_irq_gpio = of_get_named_gpio_flags(np,
|
|
"taos,irq_gpio", 0, &flags);
|
|
if (taos->prox_irq_gpio < 0) {
|
|
SENSOR_ERR("fail to get prox_irq_gpio\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
taos->prox_vled_ldo_pin = of_get_named_gpio_flags(np,
|
|
"taos,vled_ldo_pin", 0, &flags);
|
|
if (taos->prox_vled_ldo_pin < 0) {
|
|
SENSOR_ERR("fail to get vled_ldo_pin\n");
|
|
taos->prox_vled_ldo_pin = 0;
|
|
} else {
|
|
ret = gpio_request(taos->prox_vled_ldo_pin, "prox_vled_en");
|
|
if (ret < 0)
|
|
SENSOR_ERR("gpio %d request failed %d\n",
|
|
taos->prox_vled_ldo_pin, ret);
|
|
else
|
|
gpio_direction_output(taos->prox_vled_ldo_pin, 0);
|
|
}
|
|
|
|
if (of_property_read_u32(np, "taos,prox_thd_det_hi",
|
|
&taos->prox_thd_det_hi) < 0)
|
|
taos->prox_thd_det_hi = DEFAULT_THRESHOLD_DET_HI;
|
|
if (of_property_read_u32(np, "taos,prox_thd_still_det_low",
|
|
&taos->prox_thd_still_det_low) < 0)
|
|
taos->prox_thd_still_det_low = DEFAULT_THRESHOLD_STILL_DET_LOW;
|
|
SENSOR_INFO("th_det_hi:%d, th_still_det_low:%d\n",
|
|
taos->prox_thd_det_hi, taos->prox_thd_still_det_low);
|
|
|
|
if (of_property_read_u32(np, "taos,prox_thd_still_det_hi",
|
|
&taos->prox_thd_still_det_hi) < 0)
|
|
taos->prox_thd_still_det_hi = DEFAULT_THRESHOLD_STILL_DET_HI;
|
|
if (of_property_read_u32(np, "taos,prox_thd_rel_low",
|
|
&taos->prox_thd_rel_low) < 0)
|
|
taos->prox_thd_rel_low = DEFAULT_THRESHOLD_REL_LOW;
|
|
SENSOR_INFO("th_still_det_hi:%d, th_rel_low:%d\n",
|
|
taos->prox_thd_still_det_hi, taos->prox_thd_rel_low);
|
|
|
|
if (of_property_read_u32(np, "taos,coef_r", &taos->coef_r) < 0)
|
|
taos->coef_r = DEFAULT_COEF_R;
|
|
if (of_property_read_u32(np, "taos,coef_g", &taos->coef_g) < 0)
|
|
taos->coef_g = DEFAULT_COEF_G;
|
|
if (of_property_read_u32(np, "taos,coef_b", &taos->coef_b) < 0)
|
|
taos->coef_b = DEFAULT_COEF_B;
|
|
if (of_property_read_u32(np, "taos,coef_c", &taos->coef_c) < 0)
|
|
taos->coef_c = DEFAULT_COEF_C;
|
|
if (of_property_read_u32(np, "taos,dgf", &taos->dgf) < 0)
|
|
taos->dgf = DEFAULT_DGF;
|
|
if (of_property_read_u32(np, "taos,cct_coef", &taos->cct_coef) < 0)
|
|
taos->cct_coef = DEFAULT_CCT_COEF;
|
|
if (of_property_read_u32(np, "taos,cct_offset", &taos->cct_offset) < 0)
|
|
taos->cct_offset = DEFAULT_CCT_OFFSET;
|
|
SENSOR_INFO("c_r:%d c_g:%d c_b:%d dgf:%d cct_coef:%d cct_offset:%d\n",
|
|
taos->coef_r, taos->coef_g, taos->coef_b,
|
|
taos->dgf, taos->cct_coef, taos->cct_offset);
|
|
|
|
if (of_property_read_u32(np, "taos,lux_mul", &taos->lux_mul) < 0)
|
|
taos->lux_mul = DEFAULT_LUX_MULTIPLE;
|
|
|
|
taos->ppulse = DEFAULT_PPULSE;
|
|
taos->pgcfg1 = DEFAULT_PGCFG1;
|
|
taos->als_time = DEFAULT_ATIME;
|
|
taos->prate = DEFAULT_PRATE;
|
|
#ifdef CONFIG_SENSORS_TMD3725_RF_NOISE_DEFENCE_CODE
|
|
taos->wtime = DEFAULT_WTIME_FOR_PROX_ONLY;
|
|
#else
|
|
taos->wtime = DEFAULT_WTIME;
|
|
#endif
|
|
taos->als_gain = DEFAULT_AGAIN;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tmd3725_i2c_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
int ret = -ENODEV;
|
|
struct tmd3725_data *taos;
|
|
|
|
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 tmd3725_data), GFP_KERNEL);
|
|
if (!taos) {
|
|
SENSOR_ERR("failed to alloc memory for module data\n");
|
|
ret = -ENOMEM;
|
|
goto err_exit;
|
|
}
|
|
|
|
if (client->dev.of_node) {
|
|
ret = tmd3725_parse_dt(taos, &client->dev);
|
|
if (ret < 0)
|
|
goto err_tmd3725_data_free;
|
|
}
|
|
|
|
taos->i2c_client = client;
|
|
i2c_set_clientdata(client, taos);
|
|
|
|
/* ID Check */
|
|
ret = i2c_smbus_read_byte_data(client, CMD_REG | CHIPID);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("i2c read error %d\n", ret);
|
|
goto err_chip_id_or_i2c_error;
|
|
} else if (ret != TMD3725_CHIP_ID) {
|
|
SENSOR_ERR("chip id error 0x[%X]\n", ret);
|
|
ret = -ENXIO;
|
|
goto err_chip_id_or_i2c_error;
|
|
}
|
|
|
|
/* wake lock init */
|
|
wake_lock_init(&taos->prx_wake_lock, WAKE_LOCK_SUSPEND,
|
|
"prx_wake_lock");
|
|
mutex_init(&taos->prox_mutex);
|
|
mutex_init(&taos->enable_lock);
|
|
mutex_init(&taos->mode_lock);
|
|
|
|
/* allocate proximity input_device */
|
|
taos->prox_input_dev = input_allocate_device();
|
|
if (!taos->prox_input_dev) {
|
|
ret = -ENOMEM;
|
|
SENSOR_ERR("could not allocate input device\n");
|
|
goto err_input_allocate_device_proximity;
|
|
}
|
|
|
|
input_set_drvdata(taos->prox_input_dev, taos);
|
|
taos->prox_input_dev->name = MODULE_NAME_PROX;
|
|
input_set_capability(taos->prox_input_dev, EV_ABS, ABS_DISTANCE);
|
|
input_set_abs_params(taos->prox_input_dev, ABS_DISTANCE, 0, 1, 0, 0);
|
|
|
|
ret = input_register_device(taos->prox_input_dev);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("could not register input device\n");
|
|
input_free_device(taos->prox_input_dev);
|
|
goto err_input_register_device_proximity;
|
|
}
|
|
ret = sensors_register(&taos->prox_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->prox_input_dev->dev.kobj,
|
|
taos->prox_input_dev->name);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("fail: sensors_create_symlink\n");
|
|
goto err_symlink_device_proximity;
|
|
}
|
|
ret = sysfs_create_group(&taos->prox_input_dev->dev.kobj,
|
|
&proximity_attribute_group);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("could not create sysfs group\n");
|
|
goto err_create_sensorgoup_proximity;
|
|
}
|
|
/* 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("tmd3725_wq");
|
|
if (!taos->wq) {
|
|
ret = -ENOMEM;
|
|
SENSOR_ERR("could not create workqueue\n");
|
|
goto err_create_workqueue;
|
|
}
|
|
|
|
taos->prox_avg_wq =
|
|
create_singlethread_workqueue("tmd3725_wq_prox_avg_wq");
|
|
if (!taos->prox_avg_wq) {
|
|
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_DELAYED_WORK(&taos->work_light, tmd3725_work_func_light);
|
|
taos->light_poll_delay = DEFAULT_LIGHT_POLL_DELAY;
|
|
INIT_WORK(&taos->work_prox, tmd3725_work_func_prox);
|
|
INIT_WORK(&taos->work_prox_avg, tmd3725_work_func_prox_avg);
|
|
|
|
hrtimer_init(&taos->prox_avg_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
|
taos->prox_avg_poll_delay = ns_to_ktime(DEFAULT_PROX_AVG_POLL_DELAY);
|
|
taos->prox_avg_timer.function = tmd3725_prox_avg_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 = MODULE_NAME_LIGHT;
|
|
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");
|
|
input_free_device(taos->light_input_dev);
|
|
goto err_input_register_device_light;
|
|
}
|
|
ret = sensors_register(&taos->light_dev, taos,
|
|
lightsensor_additional_attributes, MODULE_NAME_LIGHT);
|
|
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 = tmd3725_setup_irq(taos);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("could not setup irq\n");
|
|
goto err_setup_irq;
|
|
}
|
|
|
|
tmd3725_initialize_chip(taos);
|
|
|
|
SENSOR_INFO("success\n");
|
|
return 0;
|
|
|
|
err_setup_irq:
|
|
sysfs_remove_group(&taos->light_input_dev->dev.kobj,
|
|
&light_attribute_group);
|
|
err_create_sensorgoup_light:
|
|
sensors_remove_symlink(&taos->light_input_dev->dev.kobj,
|
|
taos->prox_input_dev->name);
|
|
err_symlink_device_light:
|
|
err_sensor_register_device_light:
|
|
input_unregister_device(taos->light_input_dev);
|
|
err_input_register_device_light:
|
|
err_input_allocate_device_light:
|
|
destroy_workqueue(taos->prox_avg_wq);
|
|
err_create_avg_workqueue:
|
|
destroy_workqueue(taos->wq);
|
|
err_create_workqueue:
|
|
sysfs_remove_group(&taos->prox_input_dev->dev.kobj,
|
|
&proximity_attribute_group);
|
|
err_create_sensorgoup_proximity:
|
|
sensors_remove_symlink(&taos->prox_input_dev->dev.kobj,
|
|
taos->prox_input_dev->name);
|
|
err_symlink_device_proximity:
|
|
err_sensor_register_device_proximity:
|
|
input_unregister_device(taos->prox_input_dev);
|
|
err_input_register_device_proximity:
|
|
err_input_allocate_device_proximity:
|
|
mutex_destroy(&taos->mode_lock);
|
|
mutex_destroy(&taos->enable_lock);
|
|
mutex_destroy(&taos->prox_mutex);
|
|
wake_lock_destroy(&taos->prx_wake_lock);
|
|
err_chip_id_or_i2c_error:
|
|
if (taos->prox_vled_ldo_pin)
|
|
gpio_free(taos->prox_vled_ldo_pin);
|
|
err_tmd3725_data_free:
|
|
kfree(taos);
|
|
err_exit:
|
|
SENSOR_ERR("failed %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static void tmd3725_i2c_shutdown(struct i2c_client *client)
|
|
{
|
|
struct tmd3725_data *taos = i2c_get_clientdata(client);
|
|
|
|
if (taos->power_state & LIGHT_ENABLED) {
|
|
tmd3725_light_disable(taos);
|
|
tmd3725_set_op_mode(taos, MODE_ALS, OFF);
|
|
taos->power_state &= ~LIGHT_ENABLED;
|
|
}
|
|
|
|
if (taos->power_state & PROXIMITY_ENABLED) {
|
|
disable_irq_wake(taos->prox_irq);
|
|
disable_irq(taos->prox_irq);
|
|
|
|
tmd3725_set_op_mode(taos, MODE_PROX, OFF);
|
|
tmd3725_prox_vled_onoff(taos, OFF);
|
|
taos->power_state &= ~PROXIMITY_ENABLED;
|
|
}
|
|
|
|
SENSOR_INFO("is called\n");
|
|
}
|
|
|
|
static int tmd3725_suspend(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
struct tmd3725_data *taos = i2c_get_clientdata(client);
|
|
|
|
if (taos->power_state & LIGHT_ENABLED) {
|
|
SENSOR_INFO("is called\n");
|
|
tmd3725_light_disable(taos);
|
|
tmd3725_set_op_mode(taos, MODE_ALS, OFF);
|
|
}
|
|
|
|
if (taos->power_state & PROXIMITY_ENABLED) {
|
|
SENSOR_INFO("is called\n");
|
|
disable_irq(taos->prox_irq);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tmd3725_resume(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
struct tmd3725_data *taos = i2c_get_clientdata(client);
|
|
|
|
if (taos->power_state & LIGHT_ENABLED) {
|
|
SENSOR_INFO("is called\n");
|
|
tmd3725_light_enable(taos);
|
|
tmd3725_set_op_mode(taos, MODE_ALS, ON);
|
|
}
|
|
|
|
if (taos->power_state & PROXIMITY_ENABLED) {
|
|
SENSOR_INFO("is called\n");
|
|
enable_irq(taos->prox_irq);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int tmd3725_i2c_remove(struct i2c_client *client)
|
|
{
|
|
SENSOR_INFO("\n");
|
|
return 0;
|
|
}
|
|
|
|
static const struct i2c_device_id tmd3725_device_id[] = {
|
|
{ CHIP_NAME, 0},
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, tmd3725_device_id);
|
|
|
|
static const struct dev_pm_ops tmd3725_pm_ops = {
|
|
.suspend = tmd3725_suspend,
|
|
.resume = tmd3725_resume
|
|
};
|
|
|
|
#ifdef CONFIG_OF
|
|
static struct of_device_id tm3725_match_table[] = {
|
|
{ .compatible = "taos,tmd3725",},
|
|
};
|
|
#else
|
|
#define tm3725_match_table NULL
|
|
#endif
|
|
|
|
static struct i2c_driver tmd3725_i2c_driver = {
|
|
.driver = {
|
|
.name = CHIP_NAME,
|
|
.owner = THIS_MODULE,
|
|
.pm = &tmd3725_pm_ops,
|
|
.of_match_table = tm3725_match_table,
|
|
},
|
|
.probe = tmd3725_i2c_probe,
|
|
.remove = tmd3725_i2c_remove,
|
|
.shutdown = tmd3725_i2c_shutdown,
|
|
.id_table = tmd3725_device_id,
|
|
};
|
|
|
|
|
|
static int __init tmd3725_init(void)
|
|
{
|
|
return i2c_add_driver(&tmd3725_i2c_driver);
|
|
}
|
|
|
|
static void __exit tmd3725_exit(void)
|
|
{
|
|
i2c_del_driver(&tmd3725_i2c_driver);
|
|
}
|
|
|
|
module_init(tmd3725_init);
|
|
module_exit(tmd3725_exit);
|
|
|
|
MODULE_AUTHOR("SAMSUNG");
|
|
MODULE_DESCRIPTION("Optical Sensor driver for tmd3725");
|
|
MODULE_LICENSE("GPL");
|