2403 lines
55 KiB
C
2403 lines
55 KiB
C
|
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
|
/*
|
||
|
|
* Copyright (c) 2020 MediaTek Inc.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include <linux/of.h>
|
||
|
|
#include <linux/of_address.h>
|
||
|
|
#include <linux/of_irq.h>
|
||
|
|
#include <linux/gpio.h>
|
||
|
|
#include <linux/regulator/consumer.h>
|
||
|
|
#include <linux/sched/clock.h>
|
||
|
|
|
||
|
|
#include "cust_alsps.h"
|
||
|
|
#include "ltr559.h"
|
||
|
|
#include "alsps.h"
|
||
|
|
|
||
|
|
/******************************************************************************
|
||
|
|
* configuration
|
||
|
|
******************************************************************************/
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
|
||
|
|
#define LTR559_DEV_NAME "ltr559"
|
||
|
|
|
||
|
|
#define SUPPORT_PSENSOR
|
||
|
|
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
#define GN_MTK_BSP_PS_DYNAMIC_CALI
|
||
|
|
#endif
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
#define APS_TAG "[ltr559] "
|
||
|
|
#define APS_FUN(f) pr_debug(APS_TAG "%s\n", __func__)
|
||
|
|
|
||
|
|
#define APS_ERR(fmt, args...) pr_info(APS_TAG fmt, ##args)
|
||
|
|
|
||
|
|
#define APS_DBG(fmt, args...)
|
||
|
|
#define APS_LOG(fmt, args...)
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
|
||
|
|
static struct i2c_client *ltr559_i2c_client;
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static const struct i2c_device_id ltr559_i2c_id[] = {
|
||
|
|
{LTR559_DEV_NAME, 0},
|
||
|
|
{}
|
||
|
|
};
|
||
|
|
static unsigned long long int_top_time;
|
||
|
|
struct alsps_hw alsps_cust;
|
||
|
|
static struct alsps_hw *hw = &alsps_cust;
|
||
|
|
struct platform_device *alspsPltFmDev;
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int ltr559_i2c_probe(struct i2c_client *client,
|
||
|
|
const struct i2c_device_id *id);
|
||
|
|
static int ltr559_i2c_remove(struct i2c_client *client);
|
||
|
|
static int ltr559_i2c_detect(struct i2c_client *client,
|
||
|
|
struct i2c_board_info *info);
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
#ifdef CONFIG_PM_SLEEP
|
||
|
|
static int ltr559_suspend(struct device *dev);
|
||
|
|
static int ltr559_resume(struct device *dev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
//static int ps_gainrange;
|
||
|
|
static int als_gainrange;
|
||
|
|
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
static int final_prox_val;
|
||
|
|
#endif
|
||
|
|
static int final_lux_val;
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int ltr559_als_read(struct i2c_client *client, u16 *data);
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
|
||
|
|
enum {
|
||
|
|
CMC_BIT_ALS = 1,
|
||
|
|
CMC_BIT_PS = 2,
|
||
|
|
};
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
struct ltr559_i2c_addr { /*define a series of i2c slave address */
|
||
|
|
u8 write_addr;
|
||
|
|
u8 ps_thd; /*PS INT threshold */
|
||
|
|
};
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
|
||
|
|
struct ltr559_priv {
|
||
|
|
struct alsps_hw *hw;
|
||
|
|
struct i2c_client *client;
|
||
|
|
struct work_struct eint_work;
|
||
|
|
struct mutex lock;
|
||
|
|
/*i2c address group */
|
||
|
|
struct ltr559_i2c_addr addr;
|
||
|
|
|
||
|
|
/*misc */
|
||
|
|
u16 als_modulus;
|
||
|
|
atomic_t i2c_retry;
|
||
|
|
/*debounce time after enabling als */
|
||
|
|
atomic_t als_debounce;
|
||
|
|
/*indicates if the debounce is on */
|
||
|
|
atomic_t als_deb_on;
|
||
|
|
/*the jiffies representing the end of debounce */
|
||
|
|
atomic_t als_deb_end;
|
||
|
|
/*mask ps: always return far away */
|
||
|
|
atomic_t ps_mask;
|
||
|
|
/*debounce time after enabling ps */
|
||
|
|
atomic_t ps_debounce;
|
||
|
|
/*indicates if the debounce is on */
|
||
|
|
atomic_t ps_deb_on;
|
||
|
|
/*the jiffies representing the end of debounce */
|
||
|
|
atomic_t ps_deb_end;
|
||
|
|
atomic_t ps_suspend;
|
||
|
|
atomic_t als_suspend;
|
||
|
|
atomic_t init_done;
|
||
|
|
struct device_node *irq_node;
|
||
|
|
int irq;
|
||
|
|
|
||
|
|
/*data */
|
||
|
|
u16 als;
|
||
|
|
u16 ps;
|
||
|
|
u8 _align;
|
||
|
|
u16 als_level_num;
|
||
|
|
u16 als_value_num;
|
||
|
|
u32 als_level[C_CUST_ALS_LEVEL - 1];
|
||
|
|
u32 als_value[C_CUST_ALS_LEVEL];
|
||
|
|
u16 ps_cali;
|
||
|
|
|
||
|
|
atomic_t als_cmd_val;
|
||
|
|
atomic_t ps_cmd_val;
|
||
|
|
atomic_t ps_thd_val;
|
||
|
|
atomic_t ps_thd_val_high;
|
||
|
|
atomic_t ps_thd_val_low;
|
||
|
|
ulong enable;
|
||
|
|
ulong pending_intr;
|
||
|
|
|
||
|
|
/*early suspend */
|
||
|
|
#if defined(CONFIG_HAS_EARLYSUSPEND)
|
||
|
|
struct early_suspend early_drv;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* The ALS calibration threshold for Diag . Default Value 400. */
|
||
|
|
u32 lux_threshold;
|
||
|
|
u32 transmittance;
|
||
|
|
};
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
#define DEF_LUX_THRESHOLD (400)
|
||
|
|
#define DEF_TRANSMITTANCE (1092)
|
||
|
|
|
||
|
|
#define ALS_CAL_FILE "/data/als_cal_data.bin"
|
||
|
|
#define LTR_DATA_BUF_NUM 1
|
||
|
|
|
||
|
|
unsigned int als_cal;
|
||
|
|
static int als_enable_nodata(int en);
|
||
|
|
static int als_get_data(int *value, int *status);
|
||
|
|
static int ltr559_get_als_value(struct ltr559_priv *obj, u16 als);
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
struct PS_CALI_DATA_STRUCT {
|
||
|
|
int close;
|
||
|
|
int far_away;
|
||
|
|
int valid;
|
||
|
|
};
|
||
|
|
static struct PS_CALI_DATA_STRUCT ps_cali = { 0, 0, 0 };
|
||
|
|
static int intr_flag_value;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
static struct ltr559_priv *ltr559_obj;
|
||
|
|
|
||
|
|
static struct i2c_client *ltr559_i2c_client;
|
||
|
|
|
||
|
|
static DEFINE_MUTEX(ltr559_i2c_mutex);
|
||
|
|
static DEFINE_MUTEX(ltr559_mutex);
|
||
|
|
static DEFINE_MUTEX(ltrinterrupt_mutex);
|
||
|
|
|
||
|
|
static int ltr559_local_init(void);
|
||
|
|
static int ltr559_remove(void);
|
||
|
|
#ifdef GN_MTK_BSP_PS_DYNAMIC_CALI
|
||
|
|
static int last_min_value = 2047;
|
||
|
|
static int ltr559_dynamic_calibrate(void);
|
||
|
|
#endif
|
||
|
|
static int ltr559_init_flag = -1;
|
||
|
|
static struct alsps_init_info ltr559_init_info = {
|
||
|
|
.name = "ltr559",
|
||
|
|
.init = ltr559_local_init,
|
||
|
|
.uninit = ltr559_remove,
|
||
|
|
|
||
|
|
};
|
||
|
|
|
||
|
|
#ifdef CONFIG_OF
|
||
|
|
static const struct of_device_id alsps_of_match[] = {
|
||
|
|
{.compatible = "mediatek,alsps"},
|
||
|
|
{},
|
||
|
|
};
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef CONFIG_PM_SLEEP
|
||
|
|
static const struct dev_pm_ops ltr559_pm_ops = {
|
||
|
|
SET_SYSTEM_SLEEP_PM_OPS(ltr559_suspend, ltr559_resume)
|
||
|
|
};
|
||
|
|
#endif
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static struct i2c_driver ltr559_i2c_driver = {
|
||
|
|
.probe = ltr559_i2c_probe,
|
||
|
|
.remove = ltr559_i2c_remove,
|
||
|
|
.detect = ltr559_i2c_detect,
|
||
|
|
.id_table = ltr559_i2c_id,
|
||
|
|
.driver = {
|
||
|
|
.name = LTR559_DEV_NAME,
|
||
|
|
#ifdef CONFIG_PM_SLEEP
|
||
|
|
.pm = <r559_pm_ops,
|
||
|
|
#endif
|
||
|
|
#ifdef CONFIG_OF
|
||
|
|
.of_match_table = alsps_of_match,
|
||
|
|
#endif
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
/*
|
||
|
|
* #########
|
||
|
|
* ## I2C ##
|
||
|
|
* #########
|
||
|
|
*/
|
||
|
|
|
||
|
|
// I2C Read
|
||
|
|
static int ltr559_i2c_read_reg(u8 regnum)
|
||
|
|
{
|
||
|
|
u8 buffer[1], reg_value[1];
|
||
|
|
int res = 0;
|
||
|
|
|
||
|
|
mutex_lock(<r559_i2c_mutex);
|
||
|
|
|
||
|
|
buffer[0] = regnum;
|
||
|
|
res = i2c_master_send(ltr559_obj->client, buffer, 0x1);
|
||
|
|
if (res <= 0) {
|
||
|
|
|
||
|
|
APS_ERR("read reg send res = %d\n", res);
|
||
|
|
mutex_unlock(<r559_i2c_mutex);
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
res = i2c_master_recv(ltr559_obj->client, reg_value, 0x1);
|
||
|
|
if (res <= 0) {
|
||
|
|
APS_ERR("read reg recv res = %d\n", res);
|
||
|
|
mutex_unlock(<r559_i2c_mutex);
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_unlock(<r559_i2c_mutex);
|
||
|
|
|
||
|
|
return reg_value[0];
|
||
|
|
}
|
||
|
|
|
||
|
|
// I2C Write
|
||
|
|
static int ltr559_i2c_write_reg(u8 regnum, u8 value)
|
||
|
|
{
|
||
|
|
u8 databuf[2];
|
||
|
|
int res = 0;
|
||
|
|
|
||
|
|
mutex_lock(<r559_i2c_mutex);
|
||
|
|
databuf[0] = regnum;
|
||
|
|
databuf[1] = value;
|
||
|
|
res = i2c_master_send(ltr559_obj->client, databuf, 0x2);
|
||
|
|
mutex_unlock(<r559_i2c_mutex);
|
||
|
|
if (res < 0) {
|
||
|
|
APS_ERR("write reg send res = %d\n", res);
|
||
|
|
return res;
|
||
|
|
} else
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
static int ltr559_ps_set_thres(void)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
u8 databuf[2];
|
||
|
|
|
||
|
|
struct i2c_client *client = ltr559_obj->client;
|
||
|
|
struct ltr559_priv *obj = ltr559_obj;
|
||
|
|
|
||
|
|
APS_FUN();
|
||
|
|
|
||
|
|
//BUILD_BUG_ON_ZERO(0>1);
|
||
|
|
|
||
|
|
APS_DBG("ps_cali.valid: %d\n", ps_cali.valid);
|
||
|
|
if (ps_cali.valid == 1) {
|
||
|
|
databuf[0] = LTR559_PS_THRES_LOW_0;
|
||
|
|
databuf[1] = (u8) (ps_cali.far_away & 0x00FF);
|
||
|
|
res = i2c_master_send(client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto EXIT_ERR;
|
||
|
|
return ltr559_ERR_I2C;
|
||
|
|
}
|
||
|
|
databuf[0] = LTR559_PS_THRES_LOW_1;
|
||
|
|
databuf[1] = (u8) ((ps_cali.far_away & 0xFF00) >> 8);
|
||
|
|
res = i2c_master_send(client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto EXIT_ERR;
|
||
|
|
return ltr559_ERR_I2C;
|
||
|
|
}
|
||
|
|
databuf[0] = LTR559_PS_THRES_UP_0;
|
||
|
|
databuf[1] = (u8) (ps_cali.close & 0x00FF);
|
||
|
|
res = i2c_master_send(client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto EXIT_ERR;
|
||
|
|
return ltr559_ERR_I2C;
|
||
|
|
}
|
||
|
|
databuf[0] = LTR559_PS_THRES_UP_1;
|
||
|
|
databuf[1] = (u8) ((ps_cali.close & 0xFF00) >> 8);
|
||
|
|
res = i2c_master_send(client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto EXIT_ERR;
|
||
|
|
return ltr559_ERR_I2C;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
databuf[0] = LTR559_PS_THRES_LOW_0;
|
||
|
|
databuf[1] =
|
||
|
|
(u8) ((atomic_read(&obj->ps_thd_val_low)) & 0x00FF);
|
||
|
|
res = i2c_master_send(client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto EXIT_ERR;
|
||
|
|
return ltr559_ERR_I2C;
|
||
|
|
}
|
||
|
|
databuf[0] = LTR559_PS_THRES_LOW_1;
|
||
|
|
databuf[1] =
|
||
|
|
(u8) ((atomic_read(&obj->ps_thd_val_low) >> 8) & 0x00FF);
|
||
|
|
|
||
|
|
res = i2c_master_send(client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto EXIT_ERR;
|
||
|
|
return ltr559_ERR_I2C;
|
||
|
|
}
|
||
|
|
databuf[0] = LTR559_PS_THRES_UP_0;
|
||
|
|
databuf[1] =
|
||
|
|
(u8) ((atomic_read(&obj->ps_thd_val_high)) & 0x00FF);
|
||
|
|
res = i2c_master_send(client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto EXIT_ERR;
|
||
|
|
return ltr559_ERR_I2C;
|
||
|
|
}
|
||
|
|
databuf[0] = LTR559_PS_THRES_UP_1;
|
||
|
|
databuf[1] =
|
||
|
|
(u8) ((atomic_read(&obj->ps_thd_val_high) >> 8) & 0x00FF);
|
||
|
|
res = i2c_master_send(client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto EXIT_ERR;
|
||
|
|
return ltr559_ERR_I2C;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
APS_DBG("ps low: %d high: %d\n", atomic_read(&obj->ps_thd_val_low),
|
||
|
|
atomic_read(&obj->ps_thd_val_high));
|
||
|
|
|
||
|
|
res = 0;
|
||
|
|
return res;
|
||
|
|
|
||
|
|
EXIT_ERR:
|
||
|
|
APS_ERR("set thres: %d\n", res);
|
||
|
|
return res;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
//static int ltr559_ps_enable(int gainrange)
|
||
|
|
static int ltr559_ps_enable(struct i2c_client *client, int enable)
|
||
|
|
{
|
||
|
|
//struct ltr559_priv *obj = ltr559_obj;
|
||
|
|
u8 regdata;
|
||
|
|
int err;
|
||
|
|
|
||
|
|
//int setgain;
|
||
|
|
APS_LOG("%s ...start!\n", __func__);
|
||
|
|
|
||
|
|
err = ltr559_i2c_write_reg(LTR559_PS_LED, 0x7F);
|
||
|
|
if (err < 0) {
|
||
|
|
APS_LOG("ltr559 set ps pulse error\n");
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = ltr559_i2c_write_reg(LTR559_PS_N_PULSES, 0x08);
|
||
|
|
if (err < 0) {
|
||
|
|
APS_LOG("ltr559 set ps pulse error\n");
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = ltr559_i2c_write_reg(0x84, 0x00);
|
||
|
|
if (err < 0) {
|
||
|
|
APS_LOG("ltr559 set ps meas error\n");
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = ltr559_i2c_write_reg(LTR559_INTERRUPT, 0x01);
|
||
|
|
if (err < 0) {
|
||
|
|
APS_LOG("ltr559 set ps pulse error\n");
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = ltr559_i2c_write_reg(LTR559_INTERRUPT_PERSIST, 0x10);
|
||
|
|
if (err < 0) {
|
||
|
|
APS_LOG("ltr559 set ps pulse error\n");
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
regdata = ltr559_i2c_read_reg(LTR559_PS_CONTR);
|
||
|
|
|
||
|
|
if (enable == 1) {
|
||
|
|
APS_LOG("PS: enable ps only\n");
|
||
|
|
|
||
|
|
regdata |= 0x03;
|
||
|
|
} else {
|
||
|
|
APS_LOG("PS: disable ps only\n");
|
||
|
|
|
||
|
|
regdata &= 0xfc;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = ltr559_i2c_write_reg(LTR559_PS_CONTR, regdata);
|
||
|
|
if (err < 0) {
|
||
|
|
APS_ERR("PS: enable ps err: %d en: %d\n", err, enable);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
msleep(WAKEUP_DELAY);
|
||
|
|
regdata = ltr559_i2c_read_reg(LTR559_PS_CONTR);
|
||
|
|
|
||
|
|
#ifdef GN_MTK_BSP_PS_DYNAMIC_CALI
|
||
|
|
|
||
|
|
if (regdata & 0x02) {
|
||
|
|
if (ltr559_dynamic_calibrate() < 0)
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
ltr559_ps_set_thres();
|
||
|
|
|
||
|
|
#endif
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static int ltr559_ps_read(struct i2c_client *client, u16 *data)
|
||
|
|
{
|
||
|
|
int psval_lo, psval_hi, psdata;
|
||
|
|
|
||
|
|
psval_lo = ltr559_i2c_read_reg(LTR559_PS_DATA_0);
|
||
|
|
APS_DBG("ps_rawdata_psval_lo = %d\n", psval_lo);
|
||
|
|
if (psval_lo < 0) {
|
||
|
|
|
||
|
|
APS_DBG("psval_lo error\n");
|
||
|
|
psdata = psval_lo;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
psval_hi = ltr559_i2c_read_reg(LTR559_PS_DATA_1);
|
||
|
|
APS_DBG("ps_rawdata_psval_hi = %d\n", psval_hi);
|
||
|
|
|
||
|
|
if (psval_hi < 0) {
|
||
|
|
APS_DBG("psval_hi error\n");
|
||
|
|
psdata = psval_hi;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
psdata = ((psval_hi & 7) * 256) + psval_lo;
|
||
|
|
APS_DBG("ps_rawdata = %d\n", psdata);
|
||
|
|
|
||
|
|
*data = psdata;
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
out:
|
||
|
|
final_prox_val = psdata;
|
||
|
|
|
||
|
|
return psdata;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t als_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
|
||
|
|
if (!ltr559_obj) {
|
||
|
|
APS_ERR("ltr559_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
res = ltr559_als_read(ltr559_obj->client, <r559_obj->als);
|
||
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", ltr559_obj->als);
|
||
|
|
|
||
|
|
}
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t ps_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
|
||
|
|
if (!ltr559_obj) {
|
||
|
|
APS_ERR("ltr559_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
res = ltr559_ps_read(ltr559_obj->client, <r559_obj->ps);
|
||
|
|
return snprintf(buf, PAGE_SIZE, "0x%04X\n", ltr559_obj->ps);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t status_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
ssize_t len = 0;
|
||
|
|
|
||
|
|
if (!ltr559_obj) {
|
||
|
|
APS_ERR("ltr559_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ltr559_obj->hw) {
|
||
|
|
|
||
|
|
len +=
|
||
|
|
snprintf(buf + len, PAGE_SIZE - len, "CUST: %d, (%d %d)\n",
|
||
|
|
ltr559_obj->hw->i2c_num, ltr559_obj->hw->power_id,
|
||
|
|
ltr559_obj->hw->power_vol);
|
||
|
|
|
||
|
|
} else {
|
||
|
|
len += snprintf(buf + len, PAGE_SIZE - len, "CUST: NULL\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
len +=
|
||
|
|
snprintf(buf + len, PAGE_SIZE - len, "MISC: %d %d\n",
|
||
|
|
atomic_read(<r559_obj->als_suspend),
|
||
|
|
atomic_read(<r559_obj->ps_suspend));
|
||
|
|
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t reg_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
int i, len = 0;
|
||
|
|
int reg[] = { 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
|
||
|
|
0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91,
|
||
|
|
0x92, 0x93, 0x94, 0x95, 0x97, 0x98, 0x99, 0x9a, 0x9e
|
||
|
|
};
|
||
|
|
|
||
|
|
for (i = 0; i < 27; i++) {
|
||
|
|
len +=
|
||
|
|
snprintf(buf + len, PAGE_SIZE - len,
|
||
|
|
"reg:0x%04X value: 0x%04X\n", reg[i],
|
||
|
|
ltr559_i2c_read_reg(reg[i]));
|
||
|
|
|
||
|
|
}
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t reg_store(struct device_driver *ddri, const char *buf,
|
||
|
|
size_t count)
|
||
|
|
{
|
||
|
|
int ret, value;
|
||
|
|
u8 reg;
|
||
|
|
|
||
|
|
if (!ltr559_obj) {
|
||
|
|
APS_ERR("ltr559_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (sscanf(buf, "%hhx %x ", ®, &value) == 2) {
|
||
|
|
APS_DBG
|
||
|
|
("before write reg: %x, reg_value = %x write value=%x\n",
|
||
|
|
reg, ltr559_i2c_read_reg(reg), value);
|
||
|
|
ret = ltr559_i2c_write_reg(reg, value);
|
||
|
|
APS_DBG("after write reg: %x, reg_value = %x\n", reg,
|
||
|
|
ltr559_i2c_read_reg(reg));
|
||
|
|
} else {
|
||
|
|
APS_DBG("invalid content: '%s', length = %zu\n", buf, count);
|
||
|
|
}
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
/******************************************************************************
|
||
|
|
* Sysfs attributes for Diag kaka
|
||
|
|
******************************************************************************/
|
||
|
|
|
||
|
|
inline uint32_t ltr_alscode2lux(uint32_t alscode)
|
||
|
|
{
|
||
|
|
alscode += ((alscode << 7) + (alscode << 3) + (alscode >> 1));
|
||
|
|
alscode <<= 3;
|
||
|
|
if (ltr559_obj->transmittance > 0)
|
||
|
|
alscode /= ltr559_obj->transmittance;
|
||
|
|
else
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
return alscode;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int32_t ltr559_als_get_data_avg(int sSampleNo)
|
||
|
|
{
|
||
|
|
int32_t DataCount = 0;
|
||
|
|
int32_t sAveAlsData = 0;
|
||
|
|
|
||
|
|
u16 als_reading = 0;
|
||
|
|
int result = 0;
|
||
|
|
|
||
|
|
struct ltr559_priv *obj = NULL;
|
||
|
|
|
||
|
|
if (!ltr559_obj) {
|
||
|
|
APS_ERR("ltr559_obj is null!!\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
obj = ltr559_obj;
|
||
|
|
|
||
|
|
result = ltr559_als_read(obj->client, &als_reading);
|
||
|
|
APS_DBG("[%s]: Ignore first als value:%d\n", __func__, als_reading);
|
||
|
|
|
||
|
|
while (DataCount < sSampleNo) {
|
||
|
|
msleep(50);
|
||
|
|
result = ltr559_als_read(obj->client, &als_reading);
|
||
|
|
APS_ERR("%s: [#23][LTR]als code = %d\n", __func__,
|
||
|
|
als_reading);
|
||
|
|
sAveAlsData += als_reading;
|
||
|
|
DataCount++;
|
||
|
|
}
|
||
|
|
sAveAlsData /= sSampleNo;
|
||
|
|
return sAveAlsData;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool als_store_cali_transmittance_in_file(const char *filename,
|
||
|
|
unsigned int value)
|
||
|
|
{
|
||
|
|
struct file *cali_file;
|
||
|
|
mm_segment_t fs;
|
||
|
|
char w_buf[LTR_DATA_BUF_NUM * sizeof(unsigned int) * 2 + 1] = { 0 };
|
||
|
|
char r_buf[LTR_DATA_BUF_NUM * sizeof(unsigned int) * 2 + 1] = { 0 };
|
||
|
|
int i;
|
||
|
|
char *dest = w_buf;
|
||
|
|
|
||
|
|
APS_ERR("%s enter", __func__);
|
||
|
|
cali_file = filp_open(filename, O_CREAT | O_RDWR, 0777);
|
||
|
|
|
||
|
|
if (IS_ERR(cali_file)) {
|
||
|
|
APS_ERR("%s open error exit!\n", __func__);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
fs = get_fs();
|
||
|
|
set_fs(get_ds());
|
||
|
|
|
||
|
|
for (i = 0; i < LTR_DATA_BUF_NUM; i++) {
|
||
|
|
sprintf(dest, "%02X", value & 0x000000FF);
|
||
|
|
dest += 2;
|
||
|
|
sprintf(dest, "%02X", (value >> 8) & 0x000000FF);
|
||
|
|
dest += 2;
|
||
|
|
sprintf(dest, "%02X", (value >> 16) & 0x000000FF);
|
||
|
|
dest += 2;
|
||
|
|
sprintf(dest, "%02X", (value >> 24) & 0x000000FF);
|
||
|
|
dest += 2;
|
||
|
|
}
|
||
|
|
|
||
|
|
APS_ERR("w_buf: %s\n", w_buf);
|
||
|
|
cali_file->f_op->write(cali_file, (void *)w_buf,
|
||
|
|
LTR_DATA_BUF_NUM * sizeof(unsigned int) *
|
||
|
|
2 + 1, &cali_file->f_pos);
|
||
|
|
cali_file->f_pos = 0x00;
|
||
|
|
cali_file->f_op->read(cali_file, (void *)r_buf,
|
||
|
|
LTR_DATA_BUF_NUM * sizeof(unsigned int) *
|
||
|
|
2 + 1, &cali_file->f_pos);
|
||
|
|
|
||
|
|
for (i = 0; i < LTR_DATA_BUF_NUM * sizeof(unsigned int) * 2 + 1;
|
||
|
|
i++) {
|
||
|
|
if (r_buf[i] != w_buf[i]) {
|
||
|
|
filp_close(cali_file, NULL);
|
||
|
|
APS_ERR("%s read back error! exit!\n",
|
||
|
|
__func__);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
set_fs(fs);
|
||
|
|
|
||
|
|
filp_close(cali_file, NULL);
|
||
|
|
APS_ERR("pass\n");
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t enable_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
int32_t enabled = 0;
|
||
|
|
int32_t ret = 0;
|
||
|
|
u8 regdata = 0;
|
||
|
|
|
||
|
|
if (test_bit(CMC_BIT_ALS, <r559_obj->enable))
|
||
|
|
enabled = 1;
|
||
|
|
else
|
||
|
|
enabled = 0;
|
||
|
|
|
||
|
|
regdata = ltr559_i2c_read_reg(LTR559_ALS_CONTR);
|
||
|
|
|
||
|
|
if (regdata & 0x01) {
|
||
|
|
APS_LOG("ALS Enabled\n");
|
||
|
|
ret = 1;
|
||
|
|
} else {
|
||
|
|
APS_LOG("ALS Disabled\n");
|
||
|
|
ret = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (enabled != ret)
|
||
|
|
APS_ERR(
|
||
|
|
"%s: driver and sensor mismatch! driver_enable=0x%x, sensor_enable=%x\n",
|
||
|
|
__func__, enabled, ret);
|
||
|
|
|
||
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t enable_store(struct device_driver *ddri,
|
||
|
|
const char *buf, size_t size)
|
||
|
|
{
|
||
|
|
uint8_t en;
|
||
|
|
|
||
|
|
if (sysfs_streq(buf, "1"))
|
||
|
|
en = 1;
|
||
|
|
else if (sysfs_streq(buf, "0"))
|
||
|
|
en = 0;
|
||
|
|
else {
|
||
|
|
APS_ERR("%s, invalid value %d\n", __func__, *buf);
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
als_enable_nodata(en);
|
||
|
|
|
||
|
|
APS_DBG("%s: Enable ALS : %d\n", __func__, en);
|
||
|
|
|
||
|
|
return size;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t lux_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
int32_t als_reading = 0;
|
||
|
|
|
||
|
|
als_reading = ltr559_als_get_data_avg(5);
|
||
|
|
|
||
|
|
als_reading = ltr_alscode2lux(als_reading);
|
||
|
|
|
||
|
|
return scnprintf(buf, PAGE_SIZE, "%d lux\n", als_reading);
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t lux_threshold_show(struct device_driver *ddri,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
int32_t lux_threshold;
|
||
|
|
|
||
|
|
lux_threshold = ltr559_obj->lux_threshold;
|
||
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", lux_threshold);
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t lux_threshold_store(struct device_driver *ddri,
|
||
|
|
const char *buf, size_t size)
|
||
|
|
{
|
||
|
|
unsigned long value = 0;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
ret = kstrtoul(buf, 10, &value);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("%s:strict_strtoul failed, ret=0x%x\n",
|
||
|
|
__func__, ret);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
ltr559_obj->lux_threshold = value;
|
||
|
|
return size;
|
||
|
|
}
|
||
|
|
|
||
|
|
//kaka
|
||
|
|
static ssize_t transmittance_show(struct device_driver *ddri,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
int32_t transmittance;
|
||
|
|
|
||
|
|
transmittance = ltr559_obj->transmittance;
|
||
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", transmittance);
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t transmittance_store(struct device_driver *ddri,
|
||
|
|
const char *buf, size_t size)
|
||
|
|
{
|
||
|
|
unsigned long value = 0;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
ret = kstrtoul(buf, 10, &value);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("%s:strict_strtoul failed, ret=0x%x\n",
|
||
|
|
__func__, ret);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
ltr559_obj->transmittance = value;
|
||
|
|
return size;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t cali_Light_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
int32_t als_reading;
|
||
|
|
int32_t als_value_cali_adc;
|
||
|
|
int32_t als_value_cali;
|
||
|
|
bool result = false;
|
||
|
|
|
||
|
|
APS_ERR("%s:[#23][LTR]Start Cali light...\n", __func__);
|
||
|
|
|
||
|
|
msleep(150);
|
||
|
|
als_reading = ltr559_als_get_data_avg(5);
|
||
|
|
|
||
|
|
als_value_cali_adc = als_reading;
|
||
|
|
als_value_cali = ltr_alscode2lux(als_reading);
|
||
|
|
|
||
|
|
if (((als_value_cali * ltr559_obj->transmittance) /
|
||
|
|
(ltr559_obj->lux_threshold)) > 0
|
||
|
|
&& (als_value_cali_adc <= 65535)) {
|
||
|
|
|
||
|
|
/* transmittance for cali */
|
||
|
|
ltr559_obj->transmittance =
|
||
|
|
(als_value_cali * ltr559_obj->transmittance) /
|
||
|
|
(ltr559_obj->lux_threshold);
|
||
|
|
|
||
|
|
result =
|
||
|
|
als_store_cali_transmittance_in_file(ALS_CAL_FILE,
|
||
|
|
ltr559_obj->transmittance);
|
||
|
|
APS_ERR("%s: result:=%d\n", __func__, result);
|
||
|
|
//calculate lux base on calibrated transmittance
|
||
|
|
als_value_cali = ltr_alscode2lux(als_reading);
|
||
|
|
APS_ERR(
|
||
|
|
"%s:[#23][LTR]cali light done!!! als_value_cali = %d lux, ltr559_obj->transmittance = %d, als_value_cali_adc = %d code\n",
|
||
|
|
__func__, als_value_cali, ltr559_obj->transmittance,
|
||
|
|
als_value_cali_adc);
|
||
|
|
|
||
|
|
} else {
|
||
|
|
APS_ERR(
|
||
|
|
"%s:[#23][LTR]cali light fail!!! cci_als_value_cali = %d lux, ltr559_obj->transmittance = %d, als_value_cali_adc = %d code\n",
|
||
|
|
__func__, als_value_cali, ltr559_obj->transmittance,
|
||
|
|
als_value_cali_adc);
|
||
|
|
result = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return scnprintf(buf, PAGE_SIZE,
|
||
|
|
"%s: als_value_cali = %d lux, ltr559_obj->transmittance = %d, als_value_cali_adc = %d code\n",
|
||
|
|
result ? "PASSED" : "FAIL", als_value_cali,
|
||
|
|
ltr559_obj->transmittance, als_value_cali_adc);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static DRIVER_ATTR_RO(als);
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
static DRIVER_ATTR_RO(ps);
|
||
|
|
#endif
|
||
|
|
static DRIVER_ATTR_RO(status);
|
||
|
|
static DRIVER_ATTR_RW(reg);
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
//For Diag to Calibrate the ALS
|
||
|
|
static DRIVER_ATTR_RW(enable);
|
||
|
|
static DRIVER_ATTR_RO(lux);
|
||
|
|
static DRIVER_ATTR_RW(lux_threshold);
|
||
|
|
static DRIVER_ATTR_RW(transmittance);
|
||
|
|
static DRIVER_ATTR_RO(cali_Light);
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static struct driver_attribute *ltr559_attr_list[] = {
|
||
|
|
&driver_attr_als,
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
&driver_attr_ps,
|
||
|
|
#endif
|
||
|
|
&driver_attr_status,
|
||
|
|
&driver_attr_reg,
|
||
|
|
&driver_attr_enable,
|
||
|
|
&driver_attr_lux,
|
||
|
|
&driver_attr_lux_threshold,
|
||
|
|
&driver_attr_transmittance,
|
||
|
|
&driver_attr_cali_Light,
|
||
|
|
};
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int ltr559_create_attr(struct device_driver *driver)
|
||
|
|
{
|
||
|
|
int idx, err = 0;
|
||
|
|
int num = (int)ARRAY_SIZE(ltr559_attr_list);
|
||
|
|
|
||
|
|
if (driver == NULL)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
for (idx = 0; idx < num; idx++) {
|
||
|
|
err = driver_create_file(driver, ltr559_attr_list[idx]);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("driver_create_file (%s) = %d\n",
|
||
|
|
ltr559_attr_list[idx]->attr.name, err);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int ltr559_delete_attr(struct device_driver *driver)
|
||
|
|
{
|
||
|
|
int idx, err = 0;
|
||
|
|
int num = (int)ARRAY_SIZE(ltr559_attr_list);
|
||
|
|
|
||
|
|
if (!driver)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
for (idx = 0; idx < num; idx++)
|
||
|
|
driver_remove_file(driver, ltr559_attr_list[idx]);
|
||
|
|
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
/************************
|
||
|
|
* ALS CONFIG
|
||
|
|
************************/
|
||
|
|
|
||
|
|
#ifdef GN_MTK_BSP_PS_DYNAMIC_CALI
|
||
|
|
static int ltr559_dynamic_calibrate(void)
|
||
|
|
{
|
||
|
|
//int ret = 0;
|
||
|
|
int i = 0;
|
||
|
|
int j = 0;
|
||
|
|
int data = 0;
|
||
|
|
int noise = 0;
|
||
|
|
//int len = 0;
|
||
|
|
//int err = 0;
|
||
|
|
int max = 0;
|
||
|
|
//int idx_table = 0;
|
||
|
|
unsigned long data_total = 0;
|
||
|
|
struct ltr559_priv *obj = ltr559_obj;
|
||
|
|
|
||
|
|
APS_FUN(f);
|
||
|
|
if (!obj)
|
||
|
|
goto err;
|
||
|
|
|
||
|
|
msleep(20);
|
||
|
|
for (i = 0; i < 5; i++) {
|
||
|
|
if (max++ > 5)
|
||
|
|
goto err;
|
||
|
|
|
||
|
|
msleep(20);
|
||
|
|
|
||
|
|
ltr559_ps_read(obj->client, &obj->ps);
|
||
|
|
data = obj->ps;
|
||
|
|
|
||
|
|
if (data == 0)
|
||
|
|
j++;
|
||
|
|
|
||
|
|
data_total += data;
|
||
|
|
}
|
||
|
|
noise = data_total / (5 - j);
|
||
|
|
//isadjust = 1;
|
||
|
|
if ((noise < last_min_value + 100)) {
|
||
|
|
last_min_value = noise;
|
||
|
|
if (noise < 50) {
|
||
|
|
atomic_set(&obj->ps_thd_val_high, noise + 50); //15
|
||
|
|
atomic_set(&obj->ps_thd_val_low, noise + 20); //14
|
||
|
|
|
||
|
|
} else if (noise < 100) {
|
||
|
|
atomic_set(&obj->ps_thd_val_high, noise + 60); //15
|
||
|
|
atomic_set(&obj->ps_thd_val_low, noise + 30); //14
|
||
|
|
} else if (noise < 200) {
|
||
|
|
atomic_set(&obj->ps_thd_val_high, noise + 90); //15
|
||
|
|
atomic_set(&obj->ps_thd_val_low, noise + 60); //14
|
||
|
|
} else if (noise < 300) {
|
||
|
|
atomic_set(&obj->ps_thd_val_high, noise + 130); //15
|
||
|
|
atomic_set(&obj->ps_thd_val_low, noise + 90); //14
|
||
|
|
} else if (noise < 400) {
|
||
|
|
atomic_set(&obj->ps_thd_val_high, noise + 150); //15
|
||
|
|
atomic_set(&obj->ps_thd_val_low, noise + 120); //14
|
||
|
|
} else if (noise < 600) {
|
||
|
|
atomic_set(&obj->ps_thd_val_high, noise + 220); //15
|
||
|
|
atomic_set(&obj->ps_thd_val_low, noise + 180); //14
|
||
|
|
} else if (noise < 800) {
|
||
|
|
atomic_set(&obj->ps_thd_val_high, noise + 280); //15
|
||
|
|
atomic_set(&obj->ps_thd_val_low, noise + 240); //14
|
||
|
|
} else {
|
||
|
|
atomic_set(&obj->ps_thd_val_high, 1000);
|
||
|
|
atomic_set(&obj->ps_thd_val_low, 880);
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
APS_DBG(" calibrate:noise=%d, thdlow= %d , thdhigh = %d\n", noise,
|
||
|
|
atomic_read(&obj->ps_thd_val_low),
|
||
|
|
atomic_read(&obj->ps_thd_val_high));
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
err:
|
||
|
|
APS_ERR("%s fail!!!\n", __func__);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
static int ltr559_als_enable(struct i2c_client *client, int enable)
|
||
|
|
{
|
||
|
|
//struct ltr559_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int err = 0;
|
||
|
|
u8 regdata = 0;
|
||
|
|
//if (enable == obj->als_enable)
|
||
|
|
// return 0;
|
||
|
|
|
||
|
|
regdata = ltr559_i2c_read_reg(LTR559_ALS_CONTR);
|
||
|
|
|
||
|
|
if (enable == 1) {
|
||
|
|
APS_LOG("ALS(1): enable als only\n");
|
||
|
|
regdata |= 0x01;
|
||
|
|
} else {
|
||
|
|
APS_LOG("ALS(1): disable als only\n");
|
||
|
|
regdata &= 0xfe;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = ltr559_i2c_write_reg(LTR559_ALS_CONTR, regdata);
|
||
|
|
if (err < 0) {
|
||
|
|
APS_ERR("ALS: enable als err: %d en: %d\n", err, enable);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
//obj->als_enable = enable;
|
||
|
|
|
||
|
|
mdelay(WAKEUP_DELAY);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static int ltr559_als_read(struct i2c_client *client, u16 *data)
|
||
|
|
{
|
||
|
|
struct ltr559_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int alsval_ch0_lo, alsval_ch0_hi, alsval_ch0;
|
||
|
|
int alsval_ch1_lo, alsval_ch1_hi, alsval_ch1;
|
||
|
|
int luxdata_int;
|
||
|
|
int ratio;
|
||
|
|
|
||
|
|
if (atomic_read(&obj->als_suspend)) {
|
||
|
|
luxdata_int = 0;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
alsval_ch1_lo = ltr559_i2c_read_reg(LTR559_ALS_DATA_CH1_0);
|
||
|
|
alsval_ch1_hi = ltr559_i2c_read_reg(LTR559_ALS_DATA_CH1_1);
|
||
|
|
alsval_ch1 = (alsval_ch1_hi * 256) + alsval_ch1_lo;
|
||
|
|
APS_DBG("alsval_ch1_lo = %d,alsval_ch1_hi=%d,alsval_ch1=%d\n",
|
||
|
|
alsval_ch1_lo, alsval_ch1_hi, alsval_ch1);
|
||
|
|
|
||
|
|
alsval_ch0_lo = ltr559_i2c_read_reg(LTR559_ALS_DATA_CH0_0);
|
||
|
|
alsval_ch0_hi = ltr559_i2c_read_reg(LTR559_ALS_DATA_CH0_1);
|
||
|
|
alsval_ch0 = (alsval_ch0_hi * 256) + alsval_ch0_lo;
|
||
|
|
APS_DBG("alsval_ch0_lo = %d,alsval_ch0_hi=%d,alsval_ch0=%d\n",
|
||
|
|
alsval_ch0_lo, alsval_ch0_hi, alsval_ch0);
|
||
|
|
|
||
|
|
if ((alsval_ch1 == 0) || (alsval_ch0 == 0))
|
||
|
|
ratio = 0;
|
||
|
|
else
|
||
|
|
ratio = (alsval_ch1 * 100) / (alsval_ch0 + alsval_ch1);
|
||
|
|
|
||
|
|
APS_DBG("ratio = %d gainrange = %d\n", ratio, als_gainrange);
|
||
|
|
/*CWF light*/
|
||
|
|
if (ratio < 50) {
|
||
|
|
luxdata_int =
|
||
|
|
(((17743 * alsval_ch0) +
|
||
|
|
(11059 * alsval_ch1)) / als_gainrange) / 1040;
|
||
|
|
} /*D65 light*/
|
||
|
|
else if ((ratio < 66) && (ratio >= 50)) {
|
||
|
|
luxdata_int =
|
||
|
|
(((5926 * alsval_ch0) +
|
||
|
|
(1185 * alsval_ch1)) / als_gainrange) / 884;
|
||
|
|
} /* A light */
|
||
|
|
else if ((ratio < 99) && (ratio >= 66)) {
|
||
|
|
luxdata_int =
|
||
|
|
(((5926 * alsval_ch0) +
|
||
|
|
(1185 * alsval_ch1)) / als_gainrange) / 1030;
|
||
|
|
} else {
|
||
|
|
luxdata_int = 0;
|
||
|
|
}
|
||
|
|
APS_DBG("als_ratio = %d\n", ratio);
|
||
|
|
APS_DBG("als_org_value_lux = %d\n", luxdata_int);
|
||
|
|
|
||
|
|
out:
|
||
|
|
*data = luxdata_int;
|
||
|
|
final_lux_val = luxdata_int;
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
void ltr559_eint_func(void)
|
||
|
|
{
|
||
|
|
struct ltr559_priv *obj = ltr559_obj;
|
||
|
|
|
||
|
|
APS_FUN();
|
||
|
|
|
||
|
|
if (!obj)
|
||
|
|
return;
|
||
|
|
|
||
|
|
int_top_time = sched_clock();
|
||
|
|
schedule_work(&obj->eint_work);
|
||
|
|
//schedule_delayed_work(&obj->eint_work);
|
||
|
|
}
|
||
|
|
|
||
|
|
#if defined(CONFIG_OF)
|
||
|
|
static irqreturn_t ltr559_eint_handler(int irq, void *desc)
|
||
|
|
{
|
||
|
|
disable_irq_nosync(ltr559_obj->irq);
|
||
|
|
ltr559_eint_func();
|
||
|
|
|
||
|
|
return IRQ_HANDLED;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int ltr559_setup_eint(struct i2c_client *client)
|
||
|
|
{
|
||
|
|
int ret;
|
||
|
|
struct pinctrl *pinctrl;
|
||
|
|
struct pinctrl_state *pins_default;
|
||
|
|
struct pinctrl_state *pins_cfg;
|
||
|
|
u32 ints[2] = { 0, 0 };
|
||
|
|
|
||
|
|
APS_FUN();
|
||
|
|
|
||
|
|
/* gpio setting */
|
||
|
|
pinctrl = devm_pinctrl_get(&client->dev);
|
||
|
|
if (IS_ERR(pinctrl)) {
|
||
|
|
ret = PTR_ERR(pinctrl);
|
||
|
|
APS_ERR("Cannot find alsps pinctrl!\n");
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
pins_default = pinctrl_lookup_state(pinctrl, "pin_default");
|
||
|
|
if (IS_ERR(pins_default)) {
|
||
|
|
ret = PTR_ERR(pins_default);
|
||
|
|
APS_ERR("Cannot find alsps pinctrl default!\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
pins_cfg = pinctrl_lookup_state(pinctrl, "pin_cfg");
|
||
|
|
if (IS_ERR(pins_cfg)) {
|
||
|
|
ret = PTR_ERR(pins_cfg);
|
||
|
|
APS_ERR("Cannot find alsps pinctrl pin_cfg!\n");
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
pinctrl_select_state(pinctrl, pins_cfg);
|
||
|
|
|
||
|
|
/* eint request */
|
||
|
|
if (ltr559_obj->irq_node) {
|
||
|
|
of_property_read_u32_array(ltr559_obj->irq_node, "debounce",
|
||
|
|
ints, ARRAY_SIZE(ints));
|
||
|
|
gpio_set_debounce(ints[0], ints[1]);
|
||
|
|
APS_LOG("ints[0] = %d, ints[1] = %d!!\n", ints[0], ints[1]);
|
||
|
|
ltr559_obj->irq = irq_of_parse_and_map(ltr559_obj->irq_node, 0);
|
||
|
|
APS_LOG("ltr559_obj->irq = %d\n", ltr559_obj->irq);
|
||
|
|
if (!ltr559_obj->irq) {
|
||
|
|
APS_ERR("irq_of_parse_and_map fail!!\n");
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
if (request_irq(ltr559_obj->irq, ltr559_eint_handler,
|
||
|
|
IRQF_TRIGGER_FALLING, "PS-eint", NULL)) {
|
||
|
|
APS_ERR("IRQ LINE NOT AVAILABLE!!\n");
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
APS_ERR("null irq node!!\n");
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int ltr559_check_and_clear_intr(struct i2c_client *client)
|
||
|
|
{
|
||
|
|
int res, intp, intl;
|
||
|
|
u8 buffer[2];
|
||
|
|
u8 temp;
|
||
|
|
|
||
|
|
APS_FUN();
|
||
|
|
|
||
|
|
buffer[0] = LTR559_ALS_PS_STATUS;
|
||
|
|
res = i2c_master_send(client, buffer, 0x1);
|
||
|
|
if (res <= 0)
|
||
|
|
goto EXIT_ERR;
|
||
|
|
|
||
|
|
res = i2c_master_recv(client, buffer, 0x1);
|
||
|
|
if (res <= 0)
|
||
|
|
goto EXIT_ERR;
|
||
|
|
|
||
|
|
temp = buffer[0];
|
||
|
|
res = 1;
|
||
|
|
intp = 0;
|
||
|
|
intl = 0;
|
||
|
|
if (0 != (buffer[0] & 0x02)) {
|
||
|
|
res = 0;
|
||
|
|
intp = 1;
|
||
|
|
}
|
||
|
|
if (0 != (buffer[0] & 0x08)) {
|
||
|
|
res = 0;
|
||
|
|
intl = 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (res == 0) {
|
||
|
|
if ((intp == 1) && (intl == 0))
|
||
|
|
buffer[1] = buffer[0] & 0xfD;
|
||
|
|
else if ((intp == 0) && (intl == 1))
|
||
|
|
buffer[1] = buffer[0] & 0xf7;
|
||
|
|
else
|
||
|
|
buffer[1] = buffer[0] & 0xf5;
|
||
|
|
|
||
|
|
buffer[0] = LTR559_ALS_PS_STATUS;
|
||
|
|
res = i2c_master_send(client, buffer, 0x2);
|
||
|
|
if (res <= 0)
|
||
|
|
goto EXIT_ERR;
|
||
|
|
else
|
||
|
|
res = 0;
|
||
|
|
} else
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
EXIT_ERR:
|
||
|
|
APS_ERR("%s fail\n", __func__);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
static int ltr559_check_intr(struct i2c_client *client)
|
||
|
|
{
|
||
|
|
|
||
|
|
int res, intp, intl;
|
||
|
|
u8 buffer[2];
|
||
|
|
|
||
|
|
APS_FUN();
|
||
|
|
|
||
|
|
buffer[0] = ltr559_i2c_read_reg(LTR559_ALS_PS_STATUS);
|
||
|
|
|
||
|
|
APS_LOG("status = %x\n", buffer[0]);
|
||
|
|
|
||
|
|
res = 1;
|
||
|
|
intp = 0;
|
||
|
|
intl = 0;
|
||
|
|
if (0 != (buffer[0] & 0x02)) {
|
||
|
|
|
||
|
|
res = 0;
|
||
|
|
intp = 1;
|
||
|
|
}
|
||
|
|
if (0 != (buffer[0] & 0x08)) {
|
||
|
|
res = 0;
|
||
|
|
intl = 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (res == 0) {
|
||
|
|
if ((intp == 1) && (intl == 0)) {
|
||
|
|
APS_LOG("PS interrupt\n");
|
||
|
|
buffer[1] = buffer[0] & 0xfD;
|
||
|
|
|
||
|
|
} else if ((intp == 0) && (intl == 1)) {
|
||
|
|
APS_LOG("ALS interrupt\n");
|
||
|
|
buffer[1] = buffer[0] & 0xf7;
|
||
|
|
} else {
|
||
|
|
APS_LOG("Check ALS/PS interrupt error\n");
|
||
|
|
buffer[1] = buffer[0] & 0xf5;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int ltr559_clear_intr(struct i2c_client *client)
|
||
|
|
{
|
||
|
|
u8 buffer[2];
|
||
|
|
|
||
|
|
APS_FUN();
|
||
|
|
|
||
|
|
//APS_DBG("buffer[0] = %d\n",buffer[0]);
|
||
|
|
buffer[0] = ltr559_i2c_read_reg(LTR559_ALS_PS_STATUS);
|
||
|
|
APS_LOG("status = %x\n", buffer[0]);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
static int ltr559_devinit(void)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
int init_ps_gain;
|
||
|
|
int init_als_gain;
|
||
|
|
u8 databuf[2];
|
||
|
|
|
||
|
|
struct i2c_client *client = ltr559_obj->client;
|
||
|
|
|
||
|
|
struct ltr559_priv *obj = ltr559_obj;
|
||
|
|
|
||
|
|
mdelay(PON_DELAY);
|
||
|
|
|
||
|
|
init_ps_gain = MODE_PS_Gain16;
|
||
|
|
|
||
|
|
APS_LOG("LTR559_PS setgain = %d!\n", init_ps_gain);
|
||
|
|
res = ltr559_i2c_write_reg(LTR559_PS_CONTR, init_ps_gain);
|
||
|
|
if (res < 0) {
|
||
|
|
APS_LOG("ltr559 set ps gain error\n");
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
res = ltr559_i2c_write_reg(LTR559_ALS_MEAS_RATE, 0x01);
|
||
|
|
if (res < 0) {
|
||
|
|
APS_LOG("ltr559 set als meas rate error\n");
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
mdelay(WAKEUP_DELAY);
|
||
|
|
|
||
|
|
res = ltr559_i2c_write_reg(LTR559_PS_LED, 0x7F);
|
||
|
|
if (res < 0) {
|
||
|
|
APS_LOG("ltr559 set ps pulse error\n");
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
res = ltr559_i2c_write_reg(LTR559_PS_N_PULSES, 0x08);
|
||
|
|
if (res < 0) {
|
||
|
|
APS_LOG("ltr559 set ps pulse error\n");
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
// Enable ALS to Full Range at startup
|
||
|
|
als_gainrange = ALS_RANGE_8K;
|
||
|
|
|
||
|
|
init_als_gain = als_gainrange;
|
||
|
|
|
||
|
|
switch (init_als_gain) {
|
||
|
|
case ALS_RANGE_64K:
|
||
|
|
res = ltr559_i2c_write_reg(LTR559_ALS_CONTR, MODE_ALS_Range1);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALS_RANGE_32K:
|
||
|
|
res = ltr559_i2c_write_reg(LTR559_ALS_CONTR, MODE_ALS_Range2);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALS_RANGE_16K:
|
||
|
|
res = ltr559_i2c_write_reg(LTR559_ALS_CONTR, MODE_ALS_Range3);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALS_RANGE_8K:
|
||
|
|
res = ltr559_i2c_write_reg(LTR559_ALS_CONTR, MODE_ALS_Range4);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALS_RANGE_1300:
|
||
|
|
res = ltr559_i2c_write_reg(LTR559_ALS_CONTR, MODE_ALS_Range5);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALS_RANGE_600:
|
||
|
|
res = ltr559_i2c_write_reg(LTR559_ALS_CONTR, MODE_ALS_Range6);
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
res = ltr559_i2c_write_reg(LTR559_ALS_CONTR, MODE_ALS_Range1);
|
||
|
|
APS_ERR("proxmy sensor gainrange %d!\n", init_als_gain);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*for interrupt work mode support */
|
||
|
|
if (obj->hw->polling_mode_ps == 0) {
|
||
|
|
APS_LOG("eint enable");
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
ltr559_ps_set_thres();
|
||
|
|
#endif
|
||
|
|
databuf[0] = LTR559_INTERRUPT;
|
||
|
|
databuf[1] = 0x01;
|
||
|
|
res = i2c_master_send(client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto EXIT_ERR;
|
||
|
|
return ltr559_ERR_I2C;
|
||
|
|
}
|
||
|
|
|
||
|
|
databuf[0] = LTR559_INTERRUPT_PERSIST;
|
||
|
|
databuf[1] = 0x20;
|
||
|
|
res = i2c_master_send(client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto EXIT_ERR;
|
||
|
|
return ltr559_ERR_I2C;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
res = ltr559_check_and_clear_intr(client);
|
||
|
|
if (res) {
|
||
|
|
APS_ERR("check/clear intr: %d\n", res);
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
res = 0;
|
||
|
|
|
||
|
|
EXIT_ERR:
|
||
|
|
APS_ERR("init dev: %d\n", res);
|
||
|
|
return res;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
|
||
|
|
static int ltr559_get_als_value(struct ltr559_priv *obj, u16 als)
|
||
|
|
{
|
||
|
|
int idx;
|
||
|
|
int invalid = 0;
|
||
|
|
|
||
|
|
APS_DBG("als = %d\n", als);
|
||
|
|
for (idx = 0; idx < obj->als_level_num; idx++) {
|
||
|
|
if (als < obj->hw->als_level[idx])
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (idx >= obj->als_value_num) {
|
||
|
|
APS_ERR("exceed range\n");
|
||
|
|
idx = obj->als_value_num - 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (atomic_read(&obj->als_deb_on) == 1) {
|
||
|
|
unsigned long endt = atomic_read(&obj->als_deb_end);
|
||
|
|
|
||
|
|
if (time_after(jiffies, endt))
|
||
|
|
atomic_set(&obj->als_deb_on, 0);
|
||
|
|
|
||
|
|
if (atomic_read(&obj->als_deb_on) == 1)
|
||
|
|
invalid = 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!invalid) {
|
||
|
|
APS_DBG("ALS: %05d => %05d\n", als, obj->hw->als_value[idx]);
|
||
|
|
return obj->hw->als_value[idx];
|
||
|
|
}
|
||
|
|
|
||
|
|
APS_ERR("ALS: %05d => %05d (-1)\n", als,
|
||
|
|
obj->hw->als_value[idx]);
|
||
|
|
return -1;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int ltr559_get_ps_value(struct ltr559_priv *obj, u16 ps)
|
||
|
|
{
|
||
|
|
int val, invalid = 0;
|
||
|
|
static int val_temp = 1;
|
||
|
|
|
||
|
|
if (ps > atomic_read(&obj->ps_thd_val_high)) {
|
||
|
|
val = 0; /*close */
|
||
|
|
val_temp = 0;
|
||
|
|
intr_flag_value = 1;
|
||
|
|
} else if (ps < atomic_read(&obj->ps_thd_val_low)) {
|
||
|
|
val = 1; /*far away */
|
||
|
|
val_temp = 1;
|
||
|
|
intr_flag_value = 0;
|
||
|
|
} else
|
||
|
|
val = val_temp;
|
||
|
|
|
||
|
|
if (atomic_read(&obj->ps_suspend)) {
|
||
|
|
invalid = 1;
|
||
|
|
} else if (atomic_read(&obj->ps_deb_on) == 1) {
|
||
|
|
unsigned long endt = atomic_read(&obj->ps_deb_end);
|
||
|
|
|
||
|
|
if (time_after(jiffies, endt))
|
||
|
|
atomic_set(&obj->ps_deb_on, 0);
|
||
|
|
|
||
|
|
if (atomic_read(&obj->ps_deb_on) == 1)
|
||
|
|
invalid = 1;
|
||
|
|
} else if (obj->als > 50000) {
|
||
|
|
//invalid = 1;
|
||
|
|
APS_DBG("ligh too high will result to failt proximiy\n");
|
||
|
|
return 1; /*far away */
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!invalid) {
|
||
|
|
APS_DBG("PS: %05d => %05d\n", ps, val);
|
||
|
|
return val;
|
||
|
|
} else {
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int als_open_report_data(int open)
|
||
|
|
{
|
||
|
|
/* should queuq work to report event if is_report_input_direct=true */
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int als_enable_nodata(int en)
|
||
|
|
{
|
||
|
|
int res = 0;
|
||
|
|
|
||
|
|
APS_LOG("ltr559_obj als enable value = %d\n", en);
|
||
|
|
|
||
|
|
mutex_lock(<r559_mutex);
|
||
|
|
if (en)
|
||
|
|
set_bit(CMC_BIT_ALS, <r559_obj->enable);
|
||
|
|
else
|
||
|
|
clear_bit(CMC_BIT_ALS, <r559_obj->enable);
|
||
|
|
mutex_unlock(<r559_mutex);
|
||
|
|
if (!ltr559_obj) {
|
||
|
|
APS_ERR("ltr559_obj is null!!\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
res = ltr559_als_enable(ltr559_obj->client, en);
|
||
|
|
if (res) {
|
||
|
|
APS_ERR("%s is failed!!\n", __func__);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int als_set_delay(u64 ns)
|
||
|
|
{
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int als_batch(int flag, int64_t samplingPeriodNs,
|
||
|
|
int64_t maxBatchReportLatencyNs)
|
||
|
|
{
|
||
|
|
return als_set_delay(samplingPeriodNs);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int als_flush(void)
|
||
|
|
{
|
||
|
|
return als_flush_report();
|
||
|
|
}
|
||
|
|
|
||
|
|
static int als_get_data(int *value, int *status)
|
||
|
|
{
|
||
|
|
int err = 0;
|
||
|
|
|
||
|
|
struct ltr559_priv *obj = NULL;
|
||
|
|
int cali_lux = 0;
|
||
|
|
|
||
|
|
if (!ltr559_obj) {
|
||
|
|
APS_ERR("ltr559_obj is null!!\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
obj = ltr559_obj;
|
||
|
|
err = ltr559_als_read(obj->client, &obj->als);
|
||
|
|
if (err)
|
||
|
|
err = -1;
|
||
|
|
else {
|
||
|
|
|
||
|
|
cali_lux = ltr_alscode2lux(obj->als);
|
||
|
|
/* *value = ltr559_get_als_value(obj, cali_lux); */
|
||
|
|
/* APS_ERR("als: %d\n", obj->als); */
|
||
|
|
/* *value = obj->als; */
|
||
|
|
*value = cali_lux;
|
||
|
|
APS_DBG("als after cali value: %d\n", cali_lux);
|
||
|
|
if (*value < 0)
|
||
|
|
err = -1;
|
||
|
|
*status = SENSOR_STATUS_ACCURACY_MEDIUM;
|
||
|
|
}
|
||
|
|
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
static int ps_open_report_data(int open)
|
||
|
|
{
|
||
|
|
/* should queuq work to report event if is_report_input_direct=true */
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int ps_enable_nodata(int en)
|
||
|
|
{
|
||
|
|
int res = 0;
|
||
|
|
|
||
|
|
APS_LOG("ltr559_obj als enable value = %d\n", en);
|
||
|
|
|
||
|
|
mutex_lock(<r559_mutex);
|
||
|
|
if (en)
|
||
|
|
set_bit(CMC_BIT_PS, <r559_obj->enable);
|
||
|
|
|
||
|
|
else
|
||
|
|
clear_bit(CMC_BIT_PS, <r559_obj->enable);
|
||
|
|
|
||
|
|
mutex_unlock(<r559_mutex);
|
||
|
|
if (!ltr559_obj) {
|
||
|
|
APS_ERR("ltr559_obj is null!!\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
res = ltr559_ps_enable(ltr559_obj->client, en);
|
||
|
|
if (res) {
|
||
|
|
APS_ERR("als_enable_nodata is failed!!\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static int ps_set_delay(u64 ns)
|
||
|
|
{
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int ps_batch(int flag, int64_t samplingPeriodNs,
|
||
|
|
int64_t maxBatchReportLatencyNs)
|
||
|
|
{
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int ps_flush(void)
|
||
|
|
{
|
||
|
|
return ps_flush_report();
|
||
|
|
}
|
||
|
|
|
||
|
|
static int ps_get_data(int *value, int *status)
|
||
|
|
{
|
||
|
|
int err = 0;
|
||
|
|
|
||
|
|
if (!ltr559_obj) {
|
||
|
|
APS_ERR("ltr559_obj is null!!\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = ltr559_ps_read(ltr559_obj->client, <r559_obj->ps);
|
||
|
|
if (err)
|
||
|
|
err = -1;
|
||
|
|
else {
|
||
|
|
*value = ltr559_get_ps_value(ltr559_obj, ltr559_obj->ps);
|
||
|
|
if (*value < 0)
|
||
|
|
err = -1;
|
||
|
|
*status = SENSOR_STATUS_ACCURACY_MEDIUM;
|
||
|
|
}
|
||
|
|
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
/*for interrupt work mode support */
|
||
|
|
static void ltr559_eint_work(struct work_struct *work)
|
||
|
|
{
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
struct ltr559_priv *obj =
|
||
|
|
(struct ltr559_priv *)container_of(work, struct ltr559_priv,
|
||
|
|
eint_work);
|
||
|
|
int err;
|
||
|
|
u8 databuf[2];
|
||
|
|
int res = 0;
|
||
|
|
int value = 1;
|
||
|
|
int i;
|
||
|
|
int reg[] = { 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
|
||
|
|
0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91,
|
||
|
|
0x92, 0x93, 0x94, 0x95, 0x97, 0x98, 0x99, 0x9a, 0x9e
|
||
|
|
};
|
||
|
|
|
||
|
|
APS_FUN();
|
||
|
|
|
||
|
|
mutex_lock(<rinterrupt_mutex);
|
||
|
|
|
||
|
|
err = ltr559_check_intr(obj->client);
|
||
|
|
if (err < 0) {
|
||
|
|
APS_ERR("%s check intrs: %d\n", __func__, err);
|
||
|
|
} else {
|
||
|
|
//get raw data
|
||
|
|
ltr559_ps_read(obj->client, &obj->ps);
|
||
|
|
if (obj->ps < 0) {
|
||
|
|
err = -1;
|
||
|
|
goto REPORT;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
APS_DBG("%s rawdata ps=%d als_ch0=%d!\n", __func__,
|
||
|
|
obj->ps, obj->als);
|
||
|
|
value = ltr559_get_ps_value(obj, obj->ps);
|
||
|
|
APS_DBG("intr_flag_value=%d\n", intr_flag_value);
|
||
|
|
if (intr_flag_value) {
|
||
|
|
APS_DBG(" interrupt value ps will < ps_threshold_low");
|
||
|
|
|
||
|
|
databuf[0] = LTR559_PS_THRES_LOW_0;
|
||
|
|
databuf[1] =
|
||
|
|
(u8) ((atomic_read(&obj->ps_thd_val_low)) & 0x00FF);
|
||
|
|
res = i2c_master_send(obj->client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto REPORT;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
databuf[0] = LTR559_PS_THRES_LOW_1;
|
||
|
|
databuf[1] =
|
||
|
|
(u8) (((atomic_read(&obj->ps_thd_val_low)) & 0xFF00)
|
||
|
|
>> 8);
|
||
|
|
res = i2c_master_send(obj->client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto REPORT;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
databuf[0] = LTR559_PS_THRES_UP_0;
|
||
|
|
databuf[1] = (u8) (0x00FF);
|
||
|
|
res = i2c_master_send(obj->client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto REPORT;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
databuf[0] = LTR559_PS_THRES_UP_1;
|
||
|
|
databuf[1] = (u8) ((0xFF00) >> 8);
|
||
|
|
res = i2c_master_send(obj->client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto REPORT;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
//ble start
|
||
|
|
|
||
|
|
for (i = 0; i < 27; i++) {
|
||
|
|
APS_DBG("reg:0x%04X value: 0x%04X\n", reg[i],
|
||
|
|
ltr559_i2c_read_reg(reg[i]));
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
//ble end
|
||
|
|
#ifdef GN_MTK_BSP_PS_DYNAMIC_CALI
|
||
|
|
if (obj->ps < (last_min_value - 100)) {
|
||
|
|
last_min_value = obj->ps;
|
||
|
|
APS_DBG(" last_min_value is %d,noise is %d\n",
|
||
|
|
last_min_value, obj->ps);
|
||
|
|
if (obj->ps < 50) {
|
||
|
|
atomic_set(&obj->ps_thd_val_high,
|
||
|
|
obj->ps + 50);
|
||
|
|
atomic_set(&obj->ps_thd_val_low,
|
||
|
|
obj->ps + 20);
|
||
|
|
} else if (obj->ps < 100) {
|
||
|
|
atomic_set(&obj->ps_thd_val_high,
|
||
|
|
obj->ps + 60);
|
||
|
|
atomic_set(&obj->ps_thd_val_low,
|
||
|
|
obj->ps + 30);
|
||
|
|
} else if (obj->ps < 200) {
|
||
|
|
atomic_set(&obj->ps_thd_val_high,
|
||
|
|
obj->ps + 90);
|
||
|
|
atomic_set(&obj->ps_thd_val_low,
|
||
|
|
obj->ps + 60);
|
||
|
|
} else if (obj->ps < 300) {
|
||
|
|
atomic_set(&obj->ps_thd_val_high,
|
||
|
|
obj->ps + 130);
|
||
|
|
atomic_set(&obj->ps_thd_val_low,
|
||
|
|
obj->ps + 90);
|
||
|
|
} else if (obj->ps < 400) {
|
||
|
|
atomic_set(&obj->ps_thd_val_high,
|
||
|
|
obj->ps + 150);
|
||
|
|
atomic_set(&obj->ps_thd_val_low,
|
||
|
|
obj->ps + 120);
|
||
|
|
} else if (obj->ps < 600) {
|
||
|
|
atomic_set(&obj->ps_thd_val_high,
|
||
|
|
obj->ps + 220);
|
||
|
|
atomic_set(&obj->ps_thd_val_low,
|
||
|
|
obj->ps + 180);
|
||
|
|
} else if (obj->ps < 800) {
|
||
|
|
atomic_set(&obj->ps_thd_val_high,
|
||
|
|
obj->ps + 280);
|
||
|
|
atomic_set(&obj->ps_thd_val_low,
|
||
|
|
obj->ps + 240);
|
||
|
|
} else {
|
||
|
|
atomic_set(&obj->ps_thd_val_high, 1000);
|
||
|
|
atomic_set(&obj->ps_thd_val_low, 880);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
APS_DBG
|
||
|
|
(" interrupt value ps will > ps_threshold_high\n");
|
||
|
|
databuf[0] = LTR559_PS_THRES_LOW_0;
|
||
|
|
databuf[1] = (u8) (0 & 0x00FF);
|
||
|
|
res = i2c_master_send(obj->client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto REPORT;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
databuf[0] = LTR559_PS_THRES_LOW_1;
|
||
|
|
databuf[1] = (u8) ((0 & 0xFF00) >> 8);
|
||
|
|
res = i2c_master_send(obj->client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto REPORT;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
databuf[0] = LTR559_PS_THRES_UP_0;
|
||
|
|
databuf[1] =
|
||
|
|
(u8) ((atomic_read(&obj->ps_thd_val_high)) &
|
||
|
|
0x00FF);
|
||
|
|
res = i2c_master_send(obj->client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto REPORT;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
databuf[0] = LTR559_PS_THRES_UP_1;
|
||
|
|
databuf[1] =
|
||
|
|
(u8) (((atomic_read(&obj->ps_thd_val_high)) &
|
||
|
|
0xFF00) >> 8);
|
||
|
|
res = i2c_master_send(obj->client, databuf, 0x2);
|
||
|
|
if (res <= 0) {
|
||
|
|
goto REPORT;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
ltr559_clear_intr(obj->client);
|
||
|
|
|
||
|
|
REPORT:
|
||
|
|
enable_irq(ltr559_obj->irq);
|
||
|
|
mutex_unlock(<rinterrupt_mutex);
|
||
|
|
ps_report_interrupt_data(value);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
/******************************************************************************
|
||
|
|
* Function Configuration
|
||
|
|
*****************************************************************************/
|
||
|
|
static int ltr559_open(struct inode *inode, struct file *file)
|
||
|
|
{
|
||
|
|
file->private_data = ltr559_i2c_client;
|
||
|
|
|
||
|
|
if (!file->private_data) {
|
||
|
|
APS_ERR("null pointer!!\n");
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
return nonseekable_open(inode, file);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int ltr559_release(struct inode *inode, struct file *file)
|
||
|
|
{
|
||
|
|
file->private_data = NULL;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
|
||
|
|
static long ltr559_unlocked_ioctl(struct file *file, unsigned int cmd,
|
||
|
|
unsigned long arg)
|
||
|
|
{
|
||
|
|
struct i2c_client *client = (struct i2c_client *)file->private_data;
|
||
|
|
struct ltr559_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int err = 0;
|
||
|
|
void __user *ptr = (void __user *)arg;
|
||
|
|
int dat;
|
||
|
|
uint32_t enable;
|
||
|
|
int ps_cali;
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
int ps_result;
|
||
|
|
int threshold[2];
|
||
|
|
#endif
|
||
|
|
APS_DBG("cmd= %d\n", cmd);
|
||
|
|
switch (cmd) {
|
||
|
|
case ALSPS_SET_PS_MODE:
|
||
|
|
if (copy_from_user(&enable, ptr, sizeof(enable))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
err = ltr559_ps_enable(obj->client, enable);
|
||
|
|
if (err < 0) {
|
||
|
|
APS_ERR("enable ps fail: %d en: %d\n", err, enable);
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
set_bit(CMC_BIT_PS, &obj->enable);
|
||
|
|
#endif
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_GET_PS_MODE:
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
enable = test_bit(CMC_BIT_PS, &obj->enable) ? (1) : (0);
|
||
|
|
if (copy_to_user(ptr, &enable, sizeof(enable))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_GET_PS_DATA:
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
APS_DBG("ALSPS_GET_PS_DATA\n");
|
||
|
|
err = ltr559_ps_read(obj->client, &obj->ps);
|
||
|
|
if (err < 0)
|
||
|
|
goto err_out;
|
||
|
|
|
||
|
|
dat = ltr559_get_ps_value(obj, obj->ps);
|
||
|
|
if (copy_to_user(ptr, &dat, sizeof(dat))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_GET_PS_RAW_DATA:
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
err = ltr559_ps_read(obj->client, &obj->ps);
|
||
|
|
if (err < 0)
|
||
|
|
goto err_out;
|
||
|
|
|
||
|
|
dat = obj->ps;
|
||
|
|
if (copy_to_user(ptr, &dat, sizeof(dat))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_SET_ALS_MODE:
|
||
|
|
if (copy_from_user(&enable, ptr, sizeof(enable))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
//if(enable)
|
||
|
|
//{
|
||
|
|
err = ltr559_als_enable(obj->client, enable);
|
||
|
|
if (err < 0) {
|
||
|
|
APS_ERR("enable als fail: %d en: %d\n", err, enable);
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
set_bit(CMC_BIT_ALS, &obj->enable);
|
||
|
|
//}
|
||
|
|
//else
|
||
|
|
//{
|
||
|
|
// err = ltr559_als_disable();
|
||
|
|
// if(err < 0)
|
||
|
|
// {
|
||
|
|
// APS_ERR("disable als fail: %d\n", err);
|
||
|
|
// goto err_out;
|
||
|
|
// }
|
||
|
|
// clear_bit(CMC_BIT_ALS, &obj->enable);
|
||
|
|
//}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_GET_ALS_MODE:
|
||
|
|
enable = test_bit(CMC_BIT_ALS, &obj->enable) ? (1) : (0);
|
||
|
|
if (copy_to_user(ptr, &enable, sizeof(enable))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_GET_ALS_DATA:
|
||
|
|
err = ltr559_als_read(obj->client, &obj->als);
|
||
|
|
if (err < 0)
|
||
|
|
goto err_out;
|
||
|
|
|
||
|
|
dat = ltr559_get_als_value(obj, obj->als);
|
||
|
|
if (copy_to_user(ptr, &dat, sizeof(dat))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_GET_ALS_RAW_DATA:
|
||
|
|
err = ltr559_als_read(obj->client, &obj->als);
|
||
|
|
if (err < 0)
|
||
|
|
goto err_out;
|
||
|
|
|
||
|
|
dat = obj->als;
|
||
|
|
if (copy_to_user(ptr, &dat, sizeof(dat))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
/*---------------------for factory mode test--------------------*/
|
||
|
|
case ALSPS_GET_PS_TEST_RESULT:
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
err = ltr559_ps_read(obj->client, &obj->ps);
|
||
|
|
if (err)
|
||
|
|
goto err_out;
|
||
|
|
|
||
|
|
if (obj->ps > atomic_read(&obj->ps_thd_val_high))
|
||
|
|
ps_result = 0;
|
||
|
|
else
|
||
|
|
ps_result = 1;
|
||
|
|
|
||
|
|
if (copy_to_user(ptr, &ps_result, sizeof(ps_result))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_IOCTL_CLR_CALI:
|
||
|
|
if (copy_from_user(&dat, ptr, sizeof(dat))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
if (dat == 0)
|
||
|
|
obj->ps_cali = 0;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_IOCTL_GET_CALI:
|
||
|
|
ps_cali = obj->ps_cali;
|
||
|
|
if (copy_to_user(ptr, &ps_cali, sizeof(ps_cali))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_IOCTL_SET_CALI:
|
||
|
|
if (copy_from_user(&ps_cali, ptr, sizeof(ps_cali))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
obj->ps_cali = ps_cali;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_SET_PS_THRESHOLD:
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
if (copy_from_user(threshold, ptr, sizeof(threshold))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
APS_ERR("%s set threshold high: 0x%x, low: 0x%x\n", __func__,
|
||
|
|
threshold[0], threshold[1]);
|
||
|
|
atomic_set(&obj->ps_thd_val_high,
|
||
|
|
(threshold[0] + obj->ps_cali));
|
||
|
|
atomic_set(&obj->ps_thd_val_low, (threshold[1] + obj->ps_cali));
|
||
|
|
|
||
|
|
//set_psensor_threshold(obj->client);
|
||
|
|
#endif
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_GET_PS_THRESHOLD_HIGH:
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
threshold[0] =
|
||
|
|
atomic_read(&obj->ps_thd_val_high) - obj->ps_cali;
|
||
|
|
APS_ERR("%s get threshold high: 0x%x\n", __func__,
|
||
|
|
threshold[0]);
|
||
|
|
if (copy_to_user(ptr, &threshold[0], sizeof(threshold[0]))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_GET_PS_THRESHOLD_LOW:
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
threshold[0] = atomic_read(&obj->ps_thd_val_low) - obj->ps_cali;
|
||
|
|
APS_ERR("%s get threshold low: 0x%x\n", __func__, threshold[0]);
|
||
|
|
if (copy_to_user(ptr, &threshold[0], sizeof(threshold[0]))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
APS_ERR("%s not supported = 0x%04x", __func__, cmd);
|
||
|
|
err = -ENOIOCTLCMD;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
err_out:
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static const struct file_operations ltr559_fops = {
|
||
|
|
//.owner = THIS_MODULE,
|
||
|
|
.open = ltr559_open,
|
||
|
|
.release = ltr559_release,
|
||
|
|
.unlocked_ioctl = ltr559_unlocked_ioctl,
|
||
|
|
};
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static struct miscdevice ltr559_device = {
|
||
|
|
.minor = MISC_DYNAMIC_MINOR,
|
||
|
|
.name = "als_ps",
|
||
|
|
.fops = <r559_fops,
|
||
|
|
};
|
||
|
|
|
||
|
|
#ifdef CONFIG_PM_SLEEP
|
||
|
|
static int ltr559_suspend(struct device *dev)
|
||
|
|
{
|
||
|
|
struct i2c_client *client = to_i2c_client(dev);
|
||
|
|
struct ltr559_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int err;
|
||
|
|
|
||
|
|
APS_FUN();
|
||
|
|
|
||
|
|
if (!obj) {
|
||
|
|
APS_ERR("null pointer!!\n");
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
atomic_set(&obj->als_suspend, 1);
|
||
|
|
err = ltr559_als_enable(obj->client, 0);
|
||
|
|
if (err < 0) {
|
||
|
|
APS_ERR("disable als: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
atomic_set(&obj->ps_suspend, 1);
|
||
|
|
err = ltr559_ps_enable(obj->client, 0);
|
||
|
|
if (err < 0) {
|
||
|
|
APS_ERR("disable ps: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int ltr559_resume(struct device *dev)
|
||
|
|
{
|
||
|
|
struct i2c_client *client = to_i2c_client(dev);
|
||
|
|
struct ltr559_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int err;
|
||
|
|
|
||
|
|
APS_FUN();
|
||
|
|
|
||
|
|
if (!obj) {
|
||
|
|
APS_ERR("null pointer!!\n");
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
atomic_set(&obj->als_suspend, 0);
|
||
|
|
if (test_bit(CMC_BIT_ALS, &obj->enable)) {
|
||
|
|
err = ltr559_als_enable(obj->client, 1);
|
||
|
|
if (err < 0)
|
||
|
|
APS_ERR("enable als fail: %d\n", err);
|
||
|
|
}
|
||
|
|
atomic_set(&obj->ps_suspend, 0);
|
||
|
|
if (test_bit(CMC_BIT_PS, &obj->enable)) {
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
err = ltr559_ps_enable(obj->client, 1);
|
||
|
|
if (err < 0)
|
||
|
|
APS_ERR("enable ps fail: %d\n", err);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if defined(CONFIG_HAS_EARLYSUSPEND)
|
||
|
|
static void ltr559_early_suspend(struct early_suspend *h)
|
||
|
|
{ /*early_suspend is only applied for ALS */
|
||
|
|
struct ltr559_priv *obj =
|
||
|
|
container_of(h, struct ltr559_priv, early_drv);
|
||
|
|
int err;
|
||
|
|
|
||
|
|
APS_FUN();
|
||
|
|
|
||
|
|
if (!obj) {
|
||
|
|
APS_ERR("null pointer!!\n");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
atomic_set(&obj->als_suspend, 1);
|
||
|
|
err = ltr559_als_enable(obj->client, 0);
|
||
|
|
if (err < 0)
|
||
|
|
APS_ERR("disable als fail: %d\n", err);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void ltr559_late_resume(struct early_suspend *h)
|
||
|
|
{ /*early_suspend is only applied for ALS */
|
||
|
|
struct ltr559_priv *obj =
|
||
|
|
container_of(h, struct ltr559_priv, early_drv);
|
||
|
|
int err;
|
||
|
|
|
||
|
|
APS_FUN();
|
||
|
|
|
||
|
|
if (!obj) {
|
||
|
|
APS_ERR("null pointer!!\n");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
atomic_set(&obj->als_suspend, 0);
|
||
|
|
if (test_bit(CMC_BIT_ALS, &obj->enable)) {
|
||
|
|
err = ltr559_als_enable(obj->client, 1);
|
||
|
|
if (err < 0)
|
||
|
|
APS_ERR("enable als fail: %d\n", err);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int ltr559_i2c_detect(struct i2c_client *client,
|
||
|
|
struct i2c_board_info *info)
|
||
|
|
{
|
||
|
|
strcpy(info->type, LTR559_DEV_NAME);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int ltr559_i2c_probe(struct i2c_client *client,
|
||
|
|
const struct i2c_device_id *id)
|
||
|
|
{
|
||
|
|
struct ltr559_priv *obj;
|
||
|
|
struct als_control_path als_ctl = { 0 };
|
||
|
|
struct als_data_path als_data = { 0 };
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
struct ps_control_path ps_ctl = { 0 };
|
||
|
|
struct ps_data_path ps_data = { 0 };
|
||
|
|
#endif
|
||
|
|
int err = 0;
|
||
|
|
|
||
|
|
APS_LOG("%s\n", __func__);
|
||
|
|
|
||
|
|
err = get_alsps_dts_func(client->dev.of_node, hw);
|
||
|
|
if (err < 0) {
|
||
|
|
APS_ERR("get customization info from dts failed\n");
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
obj = kzalloc(sizeof(*obj), GFP_KERNEL);
|
||
|
|
if (!obj) {
|
||
|
|
err = -ENOMEM;
|
||
|
|
goto exit;
|
||
|
|
}
|
||
|
|
memset(obj, 0, sizeof(*obj));
|
||
|
|
ltr559_obj = obj;
|
||
|
|
|
||
|
|
obj->hw = hw;
|
||
|
|
|
||
|
|
INIT_WORK(&obj->eint_work, ltr559_eint_work);
|
||
|
|
obj->client = client;
|
||
|
|
i2c_set_clientdata(client, obj);
|
||
|
|
atomic_set(&obj->als_debounce, 300);
|
||
|
|
atomic_set(&obj->als_deb_on, 0);
|
||
|
|
atomic_set(&obj->als_deb_end, 0);
|
||
|
|
atomic_set(&obj->ps_debounce, 300);
|
||
|
|
atomic_set(&obj->ps_deb_on, 0);
|
||
|
|
atomic_set(&obj->ps_deb_end, 0);
|
||
|
|
atomic_set(&obj->ps_mask, 0);
|
||
|
|
atomic_set(&obj->als_suspend, 0);
|
||
|
|
atomic_set(&obj->ps_thd_val_high, obj->hw->ps_threshold_high);
|
||
|
|
atomic_set(&obj->ps_thd_val_low, obj->hw->ps_threshold_low);
|
||
|
|
atomic_set(&obj->ps_thd_val, obj->hw->ps_threshold);
|
||
|
|
|
||
|
|
ltr559_obj = obj;
|
||
|
|
obj->irq_node = client->dev.of_node;
|
||
|
|
|
||
|
|
obj->enable = 0;
|
||
|
|
obj->pending_intr = 0;
|
||
|
|
obj->als_level_num =
|
||
|
|
ARRAY_SIZE(obj->hw->als_level);
|
||
|
|
obj->als_value_num =
|
||
|
|
ARRAY_SIZE(obj->hw->als_value);
|
||
|
|
obj->als_modulus = (400 * 100) / (16 * 150);
|
||
|
|
//(400)/16*2.72 here is amplify *100
|
||
|
|
WARN_ON(sizeof(obj->als_level) != sizeof(obj->hw->als_level));
|
||
|
|
memcpy(obj->als_level, obj->hw->als_level, sizeof(obj->als_level));
|
||
|
|
WARN_ON(sizeof(obj->als_value) != sizeof(obj->hw->als_value));
|
||
|
|
memcpy(obj->als_value, obj->hw->als_value, sizeof(obj->als_value));
|
||
|
|
atomic_set(&obj->i2c_retry, 3);
|
||
|
|
set_bit(CMC_BIT_ALS, &obj->enable);
|
||
|
|
set_bit(CMC_BIT_PS, &obj->enable);
|
||
|
|
|
||
|
|
APS_LOG("ltr559_devinit() start...!\n");
|
||
|
|
ltr559_i2c_client = client;
|
||
|
|
ltr559_i2c_client->addr = 0x23;
|
||
|
|
err = ltr559_devinit();
|
||
|
|
if (err)
|
||
|
|
goto exit_init_failed;
|
||
|
|
|
||
|
|
APS_LOG("ltr559_devinit() ...OK!\n");
|
||
|
|
|
||
|
|
err = misc_register(<r559_device);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("ltr559_device register failed\n");
|
||
|
|
goto exit_misc_device_register_failed;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Register sysfs attribute */
|
||
|
|
err =
|
||
|
|
ltr559_create_attr(&(ltr559_init_info.platform_diver_addr->driver));
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("create attribute err = %d\n", err);
|
||
|
|
goto exit_create_attr_failed;
|
||
|
|
}
|
||
|
|
|
||
|
|
als_ctl.open_report_data = als_open_report_data;
|
||
|
|
als_ctl.enable_nodata = als_enable_nodata;
|
||
|
|
als_ctl.set_delay = als_set_delay;
|
||
|
|
als_ctl.batch = als_batch;
|
||
|
|
als_ctl.flush = als_flush;
|
||
|
|
als_ctl.is_report_input_direct = false;
|
||
|
|
als_ctl.is_support_batch = false;
|
||
|
|
|
||
|
|
err = als_register_control_path(&als_ctl);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("register fail = %d\n", err);
|
||
|
|
goto exit_sensor_obj_attach_fail;
|
||
|
|
}
|
||
|
|
|
||
|
|
als_data.get_data = als_get_data;
|
||
|
|
als_data.vender_div = 100;
|
||
|
|
err = als_register_data_path(&als_data);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("tregister fail = %d\n", err);
|
||
|
|
goto exit_sensor_obj_attach_fail;
|
||
|
|
}
|
||
|
|
#ifdef SUPPORT_PSENSOR
|
||
|
|
ps_ctl.open_report_data = ps_open_report_data;
|
||
|
|
ps_ctl.enable_nodata = ps_enable_nodata;
|
||
|
|
ps_ctl.set_delay = ps_set_delay;
|
||
|
|
ps_ctl.batch = ps_batch;
|
||
|
|
ps_ctl.flush = ps_flush;
|
||
|
|
ps_ctl.is_report_input_direct = false;
|
||
|
|
ps_ctl.is_support_batch = false;
|
||
|
|
err = ps_register_control_path(&ps_ctl);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("register fail = %d\n", err);
|
||
|
|
goto exit_sensor_obj_attach_fail;
|
||
|
|
}
|
||
|
|
|
||
|
|
ps_data.get_data = ps_get_data;
|
||
|
|
ps_data.vender_div = 100;
|
||
|
|
err = ps_register_data_path(&ps_data);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("tregister fail = %d\n", err);
|
||
|
|
goto exit_sensor_obj_attach_fail;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = ltr559_setup_eint(client);
|
||
|
|
if (err != 0) {
|
||
|
|
APS_ERR("setup eint: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if defined(CONFIG_HAS_EARLYSUSPEND)
|
||
|
|
obj->early_drv.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 1,
|
||
|
|
obj->early_drv.suspend = ltr559_early_suspend,
|
||
|
|
obj->early_drv.resume = ltr559_late_resume,
|
||
|
|
register_early_suspend(&obj->early_drv);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
ltr559_init_flag = 0;
|
||
|
|
APS_LOG("%s: OK\n", __func__);
|
||
|
|
|
||
|
|
|
||
|
|
ltr559_obj->lux_threshold = DEF_LUX_THRESHOLD;
|
||
|
|
ltr559_obj->transmittance = DEF_TRANSMITTANCE;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
exit_create_attr_failed:
|
||
|
|
exit_sensor_obj_attach_fail:
|
||
|
|
exit_misc_device_register_failed:
|
||
|
|
misc_deregister(<r559_device);
|
||
|
|
exit_init_failed:
|
||
|
|
kfree(obj);
|
||
|
|
exit:
|
||
|
|
ltr559_i2c_client = NULL;
|
||
|
|
APS_ERR("%s: err = %d\n", __func__, err);
|
||
|
|
ltr559_init_flag = -1;
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
|
||
|
|
static int ltr559_i2c_remove(struct i2c_client *client)
|
||
|
|
{
|
||
|
|
int err;
|
||
|
|
|
||
|
|
err = ltr559_delete_attr(<r559_i2c_driver.driver);
|
||
|
|
if (err)
|
||
|
|
APS_ERR("ltr559_delete_attr fail: %d\n", err);
|
||
|
|
|
||
|
|
misc_deregister(<r559_device);
|
||
|
|
|
||
|
|
ltr559_i2c_client = NULL;
|
||
|
|
i2c_unregister_device(client);
|
||
|
|
kfree(i2c_get_clientdata(client));
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int ltr559_remove(void)
|
||
|
|
{
|
||
|
|
//struct alsps_hw *hw = get_cust_alsps_hw();
|
||
|
|
APS_FUN();
|
||
|
|
|
||
|
|
i2c_del_driver(<r559_i2c_driver);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int ltr559_local_init(void)
|
||
|
|
{
|
||
|
|
if (i2c_add_driver(<r559_i2c_driver)) {
|
||
|
|
APS_ERR("add driver error\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
if (-1 == ltr559_init_flag)
|
||
|
|
return -1;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int __init ltr559_init(void)
|
||
|
|
{
|
||
|
|
APS_FUN();
|
||
|
|
|
||
|
|
alsps_driver_add(<r559_init_info);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static void __exit ltr559_exit(void)
|
||
|
|
{
|
||
|
|
APS_FUN();
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
module_init(ltr559_init);
|
||
|
|
module_exit(ltr559_exit);
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
|
||
|
|
MODULE_AUTHOR("MingHsien Hsieh");
|
||
|
|
MODULE_DESCRIPTION("LTR-559ALS Driver");
|
||
|
|
MODULE_LICENSE("GPL v2");
|