3539 lines
84 KiB
C
3539 lines
84 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/interrupt.h>
|
||
|
|
#include <linux/i2c.h>
|
||
|
|
#include <linux/slab.h>
|
||
|
|
#include <linux/irq.h>
|
||
|
|
#include <linux/miscdevice.h>
|
||
|
|
#include <linux/uaccess.h>
|
||
|
|
#include <linux/delay.h>
|
||
|
|
#include <linux/input.h>
|
||
|
|
#include <linux/workqueue.h>
|
||
|
|
#include <linux/kobject.h>
|
||
|
|
#include <linux/platform_device.h>
|
||
|
|
#include <linux/atomic.h>
|
||
|
|
#include <linux/version.h>
|
||
|
|
#include <linux/fs.h>
|
||
|
|
#include <linux/io.h>
|
||
|
|
#include <linux/module.h>
|
||
|
|
#include <linux/pm_wakeup.h>
|
||
|
|
#include <hwmsensor.h>
|
||
|
|
#include "cust_alsps.h"
|
||
|
|
#include "alsps.h"
|
||
|
|
#include "stk3x1x.h"
|
||
|
|
|
||
|
|
#define DRIVER_VERSION "3.9.2 20180403"
|
||
|
|
#define MTK_AUTO_DETECT_ALSPS
|
||
|
|
/*
|
||
|
|
* configuration
|
||
|
|
*/
|
||
|
|
|
||
|
|
#define stk3x1x_DEV_NAME "stk3x1x"
|
||
|
|
#define APS_TAG "[ALS/PS] "
|
||
|
|
#define APS_FUN(f) pr_debug(APS_TAG"%s\n", __func__)
|
||
|
|
#define APS_ERR(fmt, args...) pr_info(APS_TAG"%s %d : "fmt,\
|
||
|
|
__func__, __LINE__, ##args)
|
||
|
|
#define APS_DBG(fmt, args...) pr_debug(APS_TAG fmt, ##args)
|
||
|
|
#define APS_LOG(fmt, args...) pr_info(APS_TAG fmt, ##args)
|
||
|
|
/*
|
||
|
|
* extern functions
|
||
|
|
*/
|
||
|
|
#define STK_H_PS 400
|
||
|
|
#define STK_H_HT 300
|
||
|
|
#define STK_H_LT 200
|
||
|
|
|
||
|
|
#define STK_IRC_MAX_ALS_CODE 20000
|
||
|
|
#define STK_IRC_MIN_ALS_CODE 25
|
||
|
|
#define STK_IRC_MIN_IR_CODE 50
|
||
|
|
#define STK_IRC_ALS_DENOMI 2
|
||
|
|
#define STK_IRC_ALS_NUMERA 5
|
||
|
|
#define STK_IRC_ALS_CORREC 748
|
||
|
|
|
||
|
|
#define STK_IRS_IT_REDUCE 2
|
||
|
|
#define STK_ALS_READ_IRS_IT_REDUCE 5
|
||
|
|
#define STK_ALS_THRESHOLD 30
|
||
|
|
|
||
|
|
#define STK3310SA_PID 0x17
|
||
|
|
#define STK3311SA_PID 0x1E
|
||
|
|
#define STK3311WV_PID 0x1D
|
||
|
|
#define STK3311X_PID 0x12
|
||
|
|
#define STK33119_PID 0x11
|
||
|
|
|
||
|
|
static unsigned int als_value; /* lux value in test level*/
|
||
|
|
static unsigned int als_value_cali; /* lux value for ALS calibration */
|
||
|
|
static unsigned int transmittance_cali; /* transmittance for ALS calibration */
|
||
|
|
static unsigned int als_cal;
|
||
|
|
/*
|
||
|
|
* Since baro sensor driver in AP,
|
||
|
|
* it should register information to sensorlist.
|
||
|
|
*/
|
||
|
|
static struct sensorInfo_NonHub_t alsps_devinfo;
|
||
|
|
|
||
|
|
#define ALS_DEFAULT_TRANSMITTANCE 1553
|
||
|
|
#define ALS_CALIBRATION_LUX 400
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static struct i2c_client *stk3x1x_i2c_client;
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static const struct i2c_device_id stk3x1x_i2c_id[] = {
|
||
|
|
{stk3x1x_DEV_NAME, 0},
|
||
|
|
{}
|
||
|
|
};
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk3x1x_i2c_probe(struct i2c_client *client,
|
||
|
|
const struct i2c_device_id *id);
|
||
|
|
static int stk3x1x_i2c_remove(struct i2c_client *client);
|
||
|
|
|
||
|
|
static int stk3x1x_i2c_detect(struct i2c_client *client,
|
||
|
|
struct i2c_board_info *info);
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
#ifdef CONFIG_PM_SLEEP
|
||
|
|
static int stk3x1x_suspend(struct device *dev);
|
||
|
|
static int stk3x1x_resume(struct device *dev);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifndef C_I2C_FIFO_SIZE
|
||
|
|
#define C_I2C_FIFO_SIZE 8
|
||
|
|
#endif
|
||
|
|
static DEFINE_MUTEX(STK3X1X_i2c_mutex);
|
||
|
|
static int stk3x1x_init_flag = -1; /* 0 <==> OK -1 <==> fail */
|
||
|
|
static int stk3x1x_local_init(void);
|
||
|
|
static int stk3x1x_local_uninit(void);
|
||
|
|
static struct alsps_init_info stk3x1x_init_info = {
|
||
|
|
.name = "stk3x1x",
|
||
|
|
.init = stk3x1x_local_init,
|
||
|
|
.uninit = stk3x1x_local_uninit,
|
||
|
|
};
|
||
|
|
|
||
|
|
struct alsps_hw alsps_cust;
|
||
|
|
static struct alsps_hw *hw = &alsps_cust;
|
||
|
|
struct platform_device *alspsPltFmDev;
|
||
|
|
/* For alsp driver get cust info */
|
||
|
|
struct alsps_hw *get_cust_alsps(void)
|
||
|
|
{
|
||
|
|
return &alsps_cust;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
enum {
|
||
|
|
STK_TRC_ALS_DATA = 0x0001,
|
||
|
|
STK_TRC_PS_DATA = 0x0002,
|
||
|
|
STK_TRC_EINT = 0x0004,
|
||
|
|
STK_TRC_IOCTL = 0x0008,
|
||
|
|
STK_TRC_I2C = 0x0010,
|
||
|
|
STK_TRC_CVT_ALS = 0x0020,
|
||
|
|
STK_TRC_CVT_PS = 0x0040,
|
||
|
|
STK_TRC_DEBUG = 0x8000,
|
||
|
|
} STK_TRC;
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
enum {
|
||
|
|
STK_BIT_ALS = 1,
|
||
|
|
STK_BIT_PS = 2,
|
||
|
|
} STK_BIT;
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
struct stk3x1x_i2c_addr {
|
||
|
|
/*define a series of i2c slave address*/
|
||
|
|
u8 state; /* enable/disable state */
|
||
|
|
u8 psctrl; /* PS control */
|
||
|
|
u8 alsctrl; /* ALS control */
|
||
|
|
u8 ledctrl; /* LED control */
|
||
|
|
u8 intmode; /* INT mode */
|
||
|
|
u8 wait; /* wait time */
|
||
|
|
u8 thdh1_ps; /* PS INT threshold high 1 */
|
||
|
|
u8 thdh2_ps; /* PS INT threshold high 2 */
|
||
|
|
u8 thdl1_ps; /* PS INT threshold low 1 */
|
||
|
|
u8 thdl2_ps; /* PS INT threshold low 2 */
|
||
|
|
u8 thdh1_als; /* ALS INT threshold high 1 */
|
||
|
|
u8 thdh2_als; /* ALS INT threshold high 2 */
|
||
|
|
u8 thdl1_als; /* ALS INT threshold low 1 */
|
||
|
|
u8 thdl2_als; /* ALS INT threshold low 2 */
|
||
|
|
u8 flag; /* int flag */
|
||
|
|
u8 data1_ps; /* ps data1 */
|
||
|
|
u8 data2_ps; /* ps data2 */
|
||
|
|
u8 data1_als; /* als data1 */
|
||
|
|
u8 data2_als; /* als data2 */
|
||
|
|
u8 data1_offset; /* offset data1 */
|
||
|
|
u8 data2_offset; /* offset data2 */
|
||
|
|
u8 data1_ir; /* ir data1 */
|
||
|
|
u8 data2_ir; /* ir data2 */
|
||
|
|
u8 soft_reset; /* software reset */
|
||
|
|
};
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
struct stk3x1x_priv {
|
||
|
|
struct alsps_hw *hw;
|
||
|
|
struct i2c_client *client;
|
||
|
|
struct delayed_work eint_work;
|
||
|
|
|
||
|
|
/*i2c address group*/
|
||
|
|
struct stk3x1x_i2c_addr addr;
|
||
|
|
|
||
|
|
/*misc*/
|
||
|
|
atomic_t trace;
|
||
|
|
atomic_t i2c_retry;
|
||
|
|
atomic_t als_suspend;
|
||
|
|
atomic_t als_debounce; /*debounce time after enabling als*/
|
||
|
|
atomic_t als_deb_on; /*indicates if the debounce is on*/
|
||
|
|
atomic_t als_deb_end; /*the jiffies representing the end of debounce*/
|
||
|
|
atomic_t ps_mask; /*mask ps: always return far away*/
|
||
|
|
atomic_t ps_debounce; /*debounce time after enabling ps*/
|
||
|
|
atomic_t ps_deb_on; /*indicates if the debounce is on*/
|
||
|
|
atomic_t ps_deb_end; /*the jiffies representing the end of debounce*/
|
||
|
|
atomic_t ps_suspend;
|
||
|
|
atomic_t init_done;
|
||
|
|
struct device_node *irq_node;
|
||
|
|
int irq;
|
||
|
|
|
||
|
|
/*data*/
|
||
|
|
u16 als;
|
||
|
|
u16 ps;
|
||
|
|
u8 _align;
|
||
|
|
|
||
|
|
atomic_t state_val;
|
||
|
|
atomic_t psctrl_val;
|
||
|
|
atomic_t alsctrl_val;
|
||
|
|
u8 wait_val;
|
||
|
|
u8 ledctrl_val;
|
||
|
|
u8 int_val;
|
||
|
|
|
||
|
|
atomic_t ps_high_thd_val;
|
||
|
|
/*the cmd value can't be read, stored in ram*/
|
||
|
|
atomic_t ps_low_thd_val;
|
||
|
|
/*the cmd value can't be read, stored in ram*/
|
||
|
|
ulong enable;
|
||
|
|
/*enable mask*/
|
||
|
|
ulong pending_intr;
|
||
|
|
/*pending interrupt*/
|
||
|
|
atomic_t recv_reg;
|
||
|
|
bool first_boot;
|
||
|
|
uint16_t ir_code;
|
||
|
|
uint16_t als_correct_factor;
|
||
|
|
bool als_last;
|
||
|
|
bool re_enable_ps;
|
||
|
|
bool re_enable_als;
|
||
|
|
u16 ps_cali;
|
||
|
|
|
||
|
|
uint8_t pid;
|
||
|
|
uint8_t p_wv_r_bd_with_co;
|
||
|
|
uint32_t als_code_last;
|
||
|
|
uint16_t als_data_index;
|
||
|
|
uint8_t ps_distance_last;
|
||
|
|
uint8_t p_1x_r_bd_with_co;
|
||
|
|
uint8_t p_19_r_bc;
|
||
|
|
|
||
|
|
uint32_t als_transmittance;
|
||
|
|
};
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
#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 stk3x1x_pm_ops = {
|
||
|
|
SET_SYSTEM_SLEEP_PM_OPS(stk3x1x_suspend, stk3x1x_resume)
|
||
|
|
};
|
||
|
|
#endif
|
||
|
|
|
||
|
|
static struct i2c_driver stk3x1x_i2c_driver = {
|
||
|
|
.probe = stk3x1x_i2c_probe,
|
||
|
|
.remove = stk3x1x_i2c_remove,
|
||
|
|
.detect = stk3x1x_i2c_detect,
|
||
|
|
.id_table = stk3x1x_i2c_id,
|
||
|
|
.driver = {
|
||
|
|
.name = stk3x1x_DEV_NAME,
|
||
|
|
#ifdef CONFIG_PM_SLEEP
|
||
|
|
.pm = &stk3x1x_pm_ops,
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef CONFIG_OF
|
||
|
|
.of_match_table = alsps_of_match,
|
||
|
|
#endif
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
static struct stk3x1x_priv *stk3x1x_obj;
|
||
|
|
static int stk3x1x_get_ps_value(struct stk3x1x_priv *obj, u16 ps);
|
||
|
|
static int stk3x1x_get_ps_value_only(struct stk3x1x_priv *obj, u16 ps);
|
||
|
|
static uint32_t stk_alscode2lux(struct stk3x1x_priv *obj, uint32_t alscode);
|
||
|
|
static int stk3x1x_read_als(struct i2c_client *client, u16 *data);
|
||
|
|
static int stk3x1x_read_ps(struct i2c_client *client, u16 *data);
|
||
|
|
static int stk3x1x_set_als_int_thd(struct i2c_client *client, u16 als_data_reg);
|
||
|
|
static int32_t stk3x1x_get_ir_value(struct stk3x1x_priv *obj,
|
||
|
|
int32_t als_it_reduce);
|
||
|
|
#ifdef STK_CHK_REG
|
||
|
|
static int stk3x1x_validate_n_handle(struct i2c_client *client);
|
||
|
|
#endif
|
||
|
|
static int stk3x1x_init_client(struct i2c_client *client);
|
||
|
|
static struct wakeup_source mps_lock;
|
||
|
|
|
||
|
|
static DEFINE_MUTEX(run_cali_mutex);
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_get_addr(struct alsps_hw *hw, struct stk3x1x_i2c_addr *addr)
|
||
|
|
{
|
||
|
|
if (!hw || !addr)
|
||
|
|
return -EFAULT;
|
||
|
|
|
||
|
|
addr->state = STK_STATE_REG;
|
||
|
|
addr->psctrl = STK_PSCTRL_REG;
|
||
|
|
addr->alsctrl = STK_ALSCTRL_REG;
|
||
|
|
addr->ledctrl = STK_LEDCTRL_REG;
|
||
|
|
addr->intmode = STK_INT_REG;
|
||
|
|
addr->wait = STK_WAIT_REG;
|
||
|
|
addr->thdh1_ps = STK_THDH1_PS_REG;
|
||
|
|
addr->thdh2_ps = STK_THDH2_PS_REG;
|
||
|
|
addr->thdl1_ps = STK_THDL1_PS_REG;
|
||
|
|
addr->thdl2_ps = STK_THDL2_PS_REG;
|
||
|
|
addr->thdh1_als = STK_THDH1_ALS_REG;
|
||
|
|
addr->thdh2_als = STK_THDH2_ALS_REG;
|
||
|
|
addr->thdl1_als = STK_THDL1_ALS_REG;
|
||
|
|
addr->thdl2_als = STK_THDL2_ALS_REG;
|
||
|
|
addr->flag = STK_FLAG_REG;
|
||
|
|
addr->data1_ps = STK_DATA1_PS_REG;
|
||
|
|
addr->data2_ps = STK_DATA2_PS_REG;
|
||
|
|
addr->data1_als = STK_DATA1_ALS_REG;
|
||
|
|
addr->data2_als = STK_DATA2_ALS_REG;
|
||
|
|
addr->data1_offset = STK_DATA1_OFFSET_REG;
|
||
|
|
addr->data2_offset = STK_DATA2_OFFSET_REG;
|
||
|
|
addr->data1_ir = STK_DATA1_IR_REG;
|
||
|
|
addr->data2_ir = STK_DATA2_IR_REG;
|
||
|
|
addr->soft_reset = STK_SW_RESET_REG;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_hwmsen_read_block(struct i2c_client *client,
|
||
|
|
u8 addr, u8 *data, u8 len)
|
||
|
|
{
|
||
|
|
int err;
|
||
|
|
u8 beg = addr;
|
||
|
|
struct i2c_msg msgs[2] = {
|
||
|
|
{
|
||
|
|
.addr = client->addr,
|
||
|
|
.flags = 0,
|
||
|
|
.len = 1,
|
||
|
|
.buf = &beg
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.addr = client->addr,
|
||
|
|
.flags = I2C_M_RD,
|
||
|
|
.len = len,
|
||
|
|
.buf = data,
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
mutex_lock(&STK3X1X_i2c_mutex);
|
||
|
|
|
||
|
|
if (!client) {
|
||
|
|
mutex_unlock(&STK3X1X_i2c_mutex);
|
||
|
|
return -EINVAL;
|
||
|
|
} else if (len > C_I2C_FIFO_SIZE) {
|
||
|
|
mutex_unlock(&STK3X1X_i2c_mutex);
|
||
|
|
APS_ERR(" length %d exceeds %d\n", len, C_I2C_FIFO_SIZE);
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||
|
|
mutex_unlock(&STK3X1X_i2c_mutex);
|
||
|
|
|
||
|
|
if (err != 2) {
|
||
|
|
APS_ERR("i2c_transfer error: (%d %p %d) %d\n",
|
||
|
|
addr, data, len, err);
|
||
|
|
err = -EIO;
|
||
|
|
} else {
|
||
|
|
err = 0;/*no error*/
|
||
|
|
}
|
||
|
|
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_get_timing(void)
|
||
|
|
{
|
||
|
|
return 200;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_master_recv(struct i2c_client *client, u16 addr, u8 *buf, int count)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int ret = 0, retry = 0;
|
||
|
|
int trc = atomic_read(&obj->trace);
|
||
|
|
int max_try = atomic_read(&obj->i2c_retry);
|
||
|
|
|
||
|
|
while (retry++ < max_try) {
|
||
|
|
ret = stk3x1x_hwmsen_read_block(client, addr, buf, count);
|
||
|
|
|
||
|
|
if (ret == 0)
|
||
|
|
break;
|
||
|
|
|
||
|
|
udelay(100);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (unlikely(trc)) {
|
||
|
|
if ((retry != 1) && (trc & STK_TRC_DEBUG))
|
||
|
|
APS_DBG("(recv) %d/%d\n", retry - 1, max_try);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* If everything went ok (i.e. 1 msg transmitted), return #bytes
|
||
|
|
* transmitted, else error code.
|
||
|
|
*/
|
||
|
|
return (ret == 0) ? count : ret;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_master_send(struct i2c_client *client, u16 addr, u8 *buf, int count)
|
||
|
|
{
|
||
|
|
int ret = 0, retry = 0;
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int trc = atomic_read(&obj->trace);
|
||
|
|
int max_try = atomic_read(&obj->i2c_retry);
|
||
|
|
|
||
|
|
while (retry++ < max_try) {
|
||
|
|
ret = hwmsen_write_block(client, addr, buf, count);
|
||
|
|
|
||
|
|
if (ret == 0)
|
||
|
|
break;
|
||
|
|
|
||
|
|
udelay(100);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (unlikely(trc)) {
|
||
|
|
if ((retry != 1) && (trc & STK_TRC_DEBUG))
|
||
|
|
APS_DBG("(send) %d/%d\n", retry - 1, max_try);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* If everything went ok (i.e. 1 msg transmitted) */
|
||
|
|
/* return bytes transmitted, else error code. */
|
||
|
|
return (ret == 0) ? count : ret;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk3x1x_otp_read_byte_data(struct i2c_client *client, u8 command)
|
||
|
|
{
|
||
|
|
int ret;
|
||
|
|
int value;
|
||
|
|
u8 data;
|
||
|
|
|
||
|
|
data = 0x2;
|
||
|
|
ret = stk3x1x_master_send(client, 0x0, &data, 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("write 0x0 = %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
data = command;
|
||
|
|
ret = stk3x1x_master_send(client, 0x90, &data, 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("write 0x90 = %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
data = 0x82;
|
||
|
|
ret = stk3x1x_master_send(client, 0x92, &data, 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("write 0x0 = %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
usleep_range(2000, 4000);
|
||
|
|
ret = stk3x1x_master_recv(client, 0x91, &data, 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
value = data;
|
||
|
|
APS_DBG("%s: 0x%x=0x%x\n", __func__, command, value);
|
||
|
|
data = 0x0;
|
||
|
|
ret = stk3x1x_master_send(client, 0x0, &data, 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("write 0x0 = %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_write_led(struct i2c_client *client, u8 data)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
ret = stk3x1x_master_send(client, obj->addr.ledctrl, &data, 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("write led = %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk_als_ir_get_corr(struct stk3x1x_priv *obj, u32 als_data)
|
||
|
|
{
|
||
|
|
int32_t als_comperator;
|
||
|
|
|
||
|
|
if (obj->ir_code) {
|
||
|
|
obj->als_correct_factor = 1000;
|
||
|
|
if (als_data < STK_IRC_MAX_ALS_CODE
|
||
|
|
&& als_data > STK_IRC_MIN_ALS_CODE &&
|
||
|
|
obj->ir_code > STK_IRC_MIN_IR_CODE) {
|
||
|
|
als_comperator = als_data *
|
||
|
|
STK_IRC_ALS_NUMERA / STK_IRC_ALS_DENOMI;
|
||
|
|
|
||
|
|
if (obj->ir_code > als_comperator)
|
||
|
|
obj->als_correct_factor = STK_IRC_ALS_CORREC;
|
||
|
|
}
|
||
|
|
|
||
|
|
APS_DBG("%s: als=%d, ir=%d, als_correct_factor=%d",
|
||
|
|
__func__, als_data,
|
||
|
|
obj->ir_code, obj->als_correct_factor);
|
||
|
|
obj->ir_code = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
#ifdef STK_IRS
|
||
|
|
int stk_als_ir_skip_als(struct stk3x1x_priv *obj)
|
||
|
|
{
|
||
|
|
int ret;
|
||
|
|
u8 buf[2];
|
||
|
|
|
||
|
|
if (obj->als_data_index < 60000)
|
||
|
|
obj->als_data_index++;
|
||
|
|
else
|
||
|
|
obj->als_data_index = 0;
|
||
|
|
|
||
|
|
if (obj->als_data_index % 10 == 1) {
|
||
|
|
ret = stk3x1x_master_recv(obj->client,
|
||
|
|
obj->addr.data1_als, buf, 0x02);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk_als_ir_run(struct stk3x1x_priv *obj)
|
||
|
|
{
|
||
|
|
u32 ir_data;
|
||
|
|
|
||
|
|
if (obj->als_data_index % 10 == 0) {
|
||
|
|
if (obj->ps_distance_last != 0 && obj->ir_code == 0) {
|
||
|
|
ir_data = stk3x1x_get_ir_value(obj, STK_IRS_IT_REDUCE);
|
||
|
|
|
||
|
|
if (ir_data > 0)
|
||
|
|
obj->ir_code = ir_data;
|
||
|
|
}
|
||
|
|
|
||
|
|
return ir_data;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
#endif /* #ifdef STK_IRS */
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
|
||
|
|
#define STK_ALS_THRESHOLD 30
|
||
|
|
int stk3x1x_read_als(struct i2c_client *client, u16 *data)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int ret = 0;
|
||
|
|
u8 buf[2];
|
||
|
|
u32 als_data;
|
||
|
|
int32_t ir_data;
|
||
|
|
#ifdef STK_IRS
|
||
|
|
const int ir_enlarge =
|
||
|
|
1 << (STK_ALS_READ_IRS_IT_REDUCE - STK_IRS_IT_REDUCE);
|
||
|
|
#endif
|
||
|
|
if (!client)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
#ifdef STK_IRS
|
||
|
|
ret = stk_als_ir_skip_als(obj);
|
||
|
|
if (ret == 1)
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
#endif
|
||
|
|
if (atomic_read(&obj->als_suspend)) {
|
||
|
|
als_data = 0;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(client, obj->addr.data1_als, buf, 0x02);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
als_data = (buf[0] << 8) | (buf[1]);
|
||
|
|
|
||
|
|
if (obj->p_1x_r_bd_with_co == 0x07 || obj->p_19_r_bc == 0x03) {
|
||
|
|
als_data = als_data * 16 / 10;
|
||
|
|
|
||
|
|
if (als_data > 65535)
|
||
|
|
als_data = 65535;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (obj->p_wv_r_bd_with_co & 0x02) {
|
||
|
|
if (als_data < STK_ALS_THRESHOLD &&
|
||
|
|
obj->als_code_last > 10000) {
|
||
|
|
ir_data = stk3x1x_get_ir_value(obj,
|
||
|
|
STK_ALS_READ_IRS_IT_REDUCE);
|
||
|
|
#ifdef STK_IRS
|
||
|
|
|
||
|
|
if (ir_data > 0)
|
||
|
|
obj->ir_code = ir_data * ir_enlarge;
|
||
|
|
|
||
|
|
#endif
|
||
|
|
if (ir_data > (STK_ALS_THRESHOLD * 3))
|
||
|
|
als_data = obj->als_code_last;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef STK_IRS
|
||
|
|
else
|
||
|
|
obj->ir_code = 0;
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
obj->als_code_last = als_data;
|
||
|
|
#ifdef STK_IRS
|
||
|
|
stk_als_ir_get_corr(obj, als_data);
|
||
|
|
als_data = als_data * obj->als_correct_factor / 1000;
|
||
|
|
#endif
|
||
|
|
out:
|
||
|
|
*data = (u16)als_data;
|
||
|
|
#ifdef STK_IRS
|
||
|
|
stk_als_ir_run(obj);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
if (atomic_read(&obj->trace) & STK_TRC_ALS_DATA)
|
||
|
|
APS_DBG("ALS: 0x%04X\n", (u32)(*data));
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_write_als(struct i2c_client *client, u8 data)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
ret = stk3x1x_master_send(client, obj->addr.alsctrl, &data, 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("write als = %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_read_state(struct i2c_client *client, u8 *data)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
u8 buf;
|
||
|
|
|
||
|
|
if (!client)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(client, STK_STATE_REG, &buf, 0x01);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
*data = buf;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_read_flag(struct i2c_client *client, u8 *data)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int ret = 0;
|
||
|
|
u8 buf;
|
||
|
|
|
||
|
|
if (!client)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(client, obj->addr.flag, &buf, 0x01);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
*data = buf;
|
||
|
|
|
||
|
|
if (atomic_read(&obj->trace) & STK_TRC_ALS_DATA)
|
||
|
|
APS_DBG("PS NF flag: 0x%04X\n", (u32)(*data));
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_read_id(struct i2c_client *client)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int ret = 0;
|
||
|
|
u8 buf[2];
|
||
|
|
u8 pid_msb;
|
||
|
|
int otp25 = 0;
|
||
|
|
|
||
|
|
if (!client)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
obj->p_wv_r_bd_with_co = 0;
|
||
|
|
obj->p_1x_r_bd_with_co = 0;
|
||
|
|
obj->p_19_r_bc = 0;
|
||
|
|
ret = stk3x1x_master_recv(client, STK_PDT_ID_REG, buf, 0x02);
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
obj->pid = buf[0];
|
||
|
|
APS_DBG("%s: PID=0x%x, VID=0x%x\n", __func__, buf[0], buf[1]);
|
||
|
|
|
||
|
|
if (obj->pid == STK3310SA_PID || obj->pid == STK3311SA_PID)
|
||
|
|
obj->ledctrl_val &= 0x3F;
|
||
|
|
|
||
|
|
if (buf[0] == STK3311WV_PID)
|
||
|
|
obj->p_wv_r_bd_with_co |= 0x04;
|
||
|
|
else if (buf[0] == STK3311X_PID)
|
||
|
|
obj->p_1x_r_bd_with_co |= 0x04;
|
||
|
|
else if (buf[0] == STK33119_PID)
|
||
|
|
obj->p_19_r_bc |= 0x02;
|
||
|
|
|
||
|
|
if (buf[1] == 0xC3) {
|
||
|
|
obj->p_wv_r_bd_with_co |= 0x02;
|
||
|
|
obj->p_1x_r_bd_with_co |= 0x02;
|
||
|
|
} else if (buf[1] == 0xC2) {
|
||
|
|
obj->p_19_r_bc |= 0x01;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_otp_read_byte_data(client, 0x25);
|
||
|
|
|
||
|
|
if (ret < 0)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
otp25 = ret;
|
||
|
|
|
||
|
|
if (otp25 & 0x80)
|
||
|
|
obj->p_wv_r_bd_with_co |= 0x01;
|
||
|
|
|
||
|
|
APS_DBG("%s: p_wv_r_bd_with_co = 0x%x\n",
|
||
|
|
__func__, obj->p_wv_r_bd_with_co);
|
||
|
|
|
||
|
|
if (otp25 & 0x40)
|
||
|
|
obj->p_1x_r_bd_with_co |= 0x01;
|
||
|
|
|
||
|
|
APS_DBG("%s: p_1x_r_bd_with_co = 0x%x\n",
|
||
|
|
__func__, obj->p_1x_r_bd_with_co);
|
||
|
|
APS_DBG("%s: p_19_r_bc = 0x%x\n",
|
||
|
|
__func__, obj->p_19_r_bc);
|
||
|
|
|
||
|
|
if (buf[0] == 0) {
|
||
|
|
APS_ERR("PID=0x0, please make sure the chip is stk3x1x!\n");
|
||
|
|
return -2;
|
||
|
|
}
|
||
|
|
|
||
|
|
pid_msb = buf[0] & 0xF0;
|
||
|
|
|
||
|
|
switch (pid_msb) {
|
||
|
|
case 0x10:
|
||
|
|
case 0x20:
|
||
|
|
case 0x30:
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
default:
|
||
|
|
APS_ERR("invalid PID(%#x)\n", buf[0]);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_read_ps(struct i2c_client *client, u16 *data)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int ret = 0;
|
||
|
|
u8 buf[2];
|
||
|
|
|
||
|
|
if (!client) {
|
||
|
|
APS_ERR("i2c client is NULL\n");
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(client, obj->addr.data1_ps, buf, 0x02);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
*data = (buf[0] << 8) | (buf[1]);
|
||
|
|
|
||
|
|
if (atomic_read(&obj->trace) & STK_TRC_ALS_DATA)
|
||
|
|
APS_DBG("PS: 0x%04X\n", (u32)(*data));
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_write_ps(struct i2c_client *client, u8 data)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
ret = stk3x1x_master_send(client, obj->addr.psctrl, &data, 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("write ps = %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_write_wait(struct i2c_client *client, u8 data)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
ret = stk3x1x_master_send(client, obj->addr.wait, &data, 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("write wait = %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_write_int(struct i2c_client *client, u8 data)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
ret = stk3x1x_master_send(client, obj->addr.intmode, &data, 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("write intmode = %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_write_state(struct i2c_client *client, u8 data)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
ret = stk3x1x_master_send(client, obj->addr.state, &data, 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("write state = %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_write_flag(struct i2c_client *client, u8 data)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
ret = stk3x1x_master_send(client, obj->addr.flag, &data, 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("write ps = %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_write_sw_reset(struct i2c_client *client)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
u8 buf = 0, r_buf = 0;
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
APS_LOG("%s: In\n", __func__);
|
||
|
|
buf = 0x7F;
|
||
|
|
ret = stk3x1x_master_send(client, obj->addr.wait,
|
||
|
|
(char *)&buf, sizeof(buf));
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("i2c write test error = %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(client, obj->addr.wait, &r_buf, 1);
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("i2c read test error = %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (buf != r_buf) {
|
||
|
|
APS_ERR("read-back is not the same, write=0x%x, read=0x%x\n",
|
||
|
|
buf, r_buf);
|
||
|
|
return -EIO;
|
||
|
|
}
|
||
|
|
|
||
|
|
buf = 0;
|
||
|
|
ret = stk3x1x_master_send(client, obj->addr.soft_reset,
|
||
|
|
(char *)&buf, sizeof(buf));
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("write software reset error = %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
msleep(20);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_write_ps_high_thd(struct i2c_client *client, u16 thd)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
u8 buf[2];
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
buf[0] = (u8)((0xFF00 & thd) >> 8);
|
||
|
|
buf[1] = (u8)(0x00FF & thd);
|
||
|
|
ret = stk3x1x_master_send(client, obj->addr.thdh1_ps, &buf[0], 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("WARNING: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_send(client, obj->addr.thdh2_ps, &(buf[1]), 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("WARNING: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_write_ps_low_thd(struct i2c_client *client, u16 thd)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
u8 buf[2];
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
buf[0] = (u8)((0xFF00 & thd) >> 8);
|
||
|
|
buf[1] = (u8)(0x00FF & thd);
|
||
|
|
ret = stk3x1x_master_send(client, obj->addr.thdl1_ps, &buf[0], 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("WARNING: %s: %d\n", __func__, ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_send(client, obj->addr.thdl2_ps, &(buf[1]), 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("WARNING: %s: %d\n", __func__, ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_write_als_high_thd(struct i2c_client *client, u16 thd)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
u8 buf[2];
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
buf[0] = (u8)((0xFF00 & thd) >> 8);
|
||
|
|
buf[1] = (u8)(0x00FF & thd);
|
||
|
|
ret = stk3x1x_master_send(client, obj->addr.thdh1_als, &buf[0], 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("WARNING: %s: %d\n", __func__, ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_send(client, obj->addr.thdh2_als, &(buf[1]), 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("WARNING: %s: %d\n", __func__, ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_write_als_low_thd(struct i2c_client *client, u16 thd)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
u8 buf[2];
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
buf[0] = (u8)((0xFF00 & thd) >> 8);
|
||
|
|
buf[1] = (u8)(0x00FF & thd);
|
||
|
|
ret = stk3x1x_master_send(client, obj->addr.thdl1_als, &buf[0], 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("WARNING: %s: %d\n", __func__, ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_send(client, obj->addr.thdl2_als, &(buf[1]), 1);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("WARNING: %s: %d\n", __func__, ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
#ifdef STK_PS_DEBUG
|
||
|
|
static int show_allreg(void)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
u8 rbuf[0x22];
|
||
|
|
int cnt;
|
||
|
|
|
||
|
|
memset(rbuf, 0, sizeof(rbuf));
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client, 0, &rbuf[0], 7);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client, 7, &rbuf[7], 7);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client, 14, &rbuf[14], 7);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client, 21, &rbuf[21], 7);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client, 28, &rbuf[28], 4);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client,
|
||
|
|
STK_PDT_ID_REG, &rbuf[32], 2);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (cnt = 0; cnt < 0x20; cnt++)
|
||
|
|
APS_DBG("reg[0x%x]=0x%x\n", cnt, rbuf[cnt]);
|
||
|
|
|
||
|
|
APS_DBG("reg[0x3E]=0x%x\n", rbuf[cnt]);
|
||
|
|
APS_DBG("reg[0x3F]=0x%x\n", rbuf[cnt++]);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static void stk_ps_report(struct stk3x1x_priv *obj, int nf)
|
||
|
|
{
|
||
|
|
if (ps_report_interrupt_data(nf))
|
||
|
|
APS_ERR("call ps_report_interrupt_data fail\n");
|
||
|
|
|
||
|
|
APS_DBG("%s:ps raw 0x%x -> value 0x%x\n", __func__, obj->ps, nf);
|
||
|
|
obj->ps_distance_last = nf;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk3x1x_enable_als(struct i2c_client *client, int enable)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int err, cur = 0, old = atomic_read(&obj->state_val);
|
||
|
|
int trc = atomic_read(&obj->trace);
|
||
|
|
|
||
|
|
APS_LOG("%s: enable=%d\n", __func__, enable);
|
||
|
|
|
||
|
|
cur = old & (~(STK_STATE_EN_ALS_MASK | STK_STATE_EN_WAIT_MASK));
|
||
|
|
|
||
|
|
if (enable)
|
||
|
|
cur |= STK_STATE_EN_ALS_MASK;
|
||
|
|
else if (old & STK_STATE_EN_PS_MASK)
|
||
|
|
cur |= STK_STATE_EN_WAIT_MASK;
|
||
|
|
|
||
|
|
if (trc & STK_TRC_DEBUG)
|
||
|
|
APS_DBG("%s: %08X, %08X, %d\n", __func__, cur, old, enable);
|
||
|
|
|
||
|
|
if (0 == (cur ^ old))
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
#ifdef STK_IRS
|
||
|
|
|
||
|
|
if (enable && !(old & STK_STATE_EN_PS_MASK)) {
|
||
|
|
err = stk3x1x_get_ir_value(obj, STK_IRS_IT_REDUCE);
|
||
|
|
if (err > 0)
|
||
|
|
obj->ir_code = err;
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif
|
||
|
|
|
||
|
|
if (enable && obj->hw->polling_mode_als == 0) {
|
||
|
|
stk3x1x_write_als_high_thd(client, 0x0);
|
||
|
|
stk3x1x_write_als_low_thd(client, 0xFFFF);
|
||
|
|
}
|
||
|
|
|
||
|
|
err = stk3x1x_write_state(client, cur);
|
||
|
|
if (err < 0)
|
||
|
|
return err;
|
||
|
|
|
||
|
|
atomic_set(&obj->state_val, cur);
|
||
|
|
if (enable) {
|
||
|
|
obj->als_last = 0;
|
||
|
|
|
||
|
|
if (obj->hw->polling_mode_als) {
|
||
|
|
atomic_set(&obj->als_deb_on, 1);
|
||
|
|
atomic_set(&obj->als_deb_end, jiffies +
|
||
|
|
atomic_read(&obj->als_debounce)*HZ / 1000);
|
||
|
|
} else {
|
||
|
|
schedule_delayed_work(&obj->eint_work, 220 * HZ / 1000);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef STK_IRS
|
||
|
|
obj->als_data_index = 0;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
if (trc & STK_TRC_DEBUG)
|
||
|
|
APS_DBG("enable als (%d)\n", enable);
|
||
|
|
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk3x1x_enable_ps_set_thd(struct stk3x1x_priv *obj)
|
||
|
|
{
|
||
|
|
int err;
|
||
|
|
|
||
|
|
err = stk3x1x_write_ps_high_thd(obj->client,
|
||
|
|
atomic_read(&obj->ps_high_thd_val));
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("write high thd error: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = stk3x1x_write_ps_low_thd(obj->client,
|
||
|
|
atomic_read(&obj->ps_low_thd_val));
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("write low thd error: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
APS_DBG("%s:in reg, HT=%d, LT=%d\n", __func__,
|
||
|
|
atomic_read(&obj->ps_high_thd_val),
|
||
|
|
atomic_read(&obj->ps_low_thd_val));
|
||
|
|
APS_DBG("%s:in driver, HT=%d, LT=%d\n", __func__,
|
||
|
|
atomic_read(&obj->ps_high_thd_val),
|
||
|
|
atomic_read(&obj->ps_low_thd_val));
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk3x1x_enable_ps(struct i2c_client *client,
|
||
|
|
int enable, int validate_reg)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int err, cur = 0, old = atomic_read(&obj->state_val);
|
||
|
|
int trc = atomic_read(&obj->trace);
|
||
|
|
|
||
|
|
#ifdef STK_PS_DEBUG
|
||
|
|
if (enable) {
|
||
|
|
APS_LOG("%s: before enable ps, HT=%d, LT=%d\n", __func__,
|
||
|
|
atomic_read(&obj->ps_high_thd_val),
|
||
|
|
atomic_read(&obj->ps_low_thd_val));
|
||
|
|
APS_LOG("%s: before enable ps, show all reg\n", __func__);
|
||
|
|
show_allreg(); /*for debug*/
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif
|
||
|
|
pr_info("%s: enable=%d\n", __func__, enable);
|
||
|
|
cur = old;
|
||
|
|
cur &= (~(0x45));
|
||
|
|
|
||
|
|
if (enable) {
|
||
|
|
cur |= (STK_STATE_EN_PS_MASK);
|
||
|
|
|
||
|
|
if (!(old & STK_STATE_EN_ALS_MASK))
|
||
|
|
cur |= STK_STATE_EN_WAIT_MASK;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (0 == (cur ^ old)) {
|
||
|
|
pr_info("%s: repeat enable=%d, reg=0x%x\n",
|
||
|
|
__func__, enable, cur);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef STK_CHK_REG
|
||
|
|
|
||
|
|
if (validate_reg) {
|
||
|
|
err = stk3x1x_validate_n_handle(obj->client);
|
||
|
|
if (err < 0)
|
||
|
|
APS_ERR("stk3x1x_validate_n_handle fail: %d\n", err);
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif
|
||
|
|
if (obj->first_boot == true)
|
||
|
|
obj->first_boot = false;
|
||
|
|
|
||
|
|
if (enable) {
|
||
|
|
stk3x1x_enable_ps_set_thd(obj);
|
||
|
|
|
||
|
|
if (obj->hw->polling_mode_ps == 1)
|
||
|
|
__pm_stay_awake(&mps_lock);
|
||
|
|
} else {
|
||
|
|
if (obj->hw->polling_mode_ps == 1)
|
||
|
|
__pm_relax(&mps_lock);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (trc & STK_TRC_DEBUG)
|
||
|
|
APS_DBG("%s: %08X, %08X, %d\n", __func__, cur, old, enable);
|
||
|
|
|
||
|
|
err = stk3x1x_write_state(client, cur);
|
||
|
|
|
||
|
|
if (err < 0)
|
||
|
|
return err;
|
||
|
|
|
||
|
|
atomic_set(&obj->state_val, cur);
|
||
|
|
|
||
|
|
if (enable) {
|
||
|
|
if (obj->hw->polling_mode_ps) {
|
||
|
|
atomic_set(&obj->ps_deb_on, 1);
|
||
|
|
atomic_set(&obj->ps_deb_end,
|
||
|
|
jiffies + atomic_read(&obj->ps_debounce)*HZ / 1000);
|
||
|
|
} else {
|
||
|
|
#ifdef STK_CHK_REG
|
||
|
|
|
||
|
|
if (!validate_reg) {
|
||
|
|
stk_ps_report(obj, 1);
|
||
|
|
} else
|
||
|
|
#endif
|
||
|
|
{
|
||
|
|
msleep(20);
|
||
|
|
err = stk3x1x_read_ps(obj->client, &obj->ps);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("read ps data:%d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = stk3x1x_get_ps_value_only(obj, obj->ps);
|
||
|
|
|
||
|
|
if (err < 0) {
|
||
|
|
APS_ERR("get ps value: %d\n", err);
|
||
|
|
return err;
|
||
|
|
} else if (stk3x1x_obj->hw->polling_mode_ps
|
||
|
|
== 0)
|
||
|
|
stk_ps_report(obj, err);
|
||
|
|
|
||
|
|
#ifdef STK_PS_DEBUG
|
||
|
|
APS_LOG("%s: after stk_ps_report, show reg\n",
|
||
|
|
__func__);
|
||
|
|
show_allreg();
|
||
|
|
/*for debug*/
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (trc & STK_TRC_DEBUG)
|
||
|
|
APS_DBG("enable ps (%d)\n", enable);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk3x1x_ps_calibration(struct i2c_client *client)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
u8 reg;
|
||
|
|
int err = 0;
|
||
|
|
int org_state_reg = atomic_read(&obj->state_val);
|
||
|
|
bool re_en_ps = false, re_en_als = false;
|
||
|
|
int counter = 0;
|
||
|
|
uint32_t ps_sum = 0;
|
||
|
|
const int PS_CALI_COUNT = 5;
|
||
|
|
|
||
|
|
if (org_state_reg & STK_STATE_EN_PS_MASK) {
|
||
|
|
APS_LOG("%s: force disable PS\n", __func__);
|
||
|
|
stk3x1x_enable_ps(obj->client, 0, 1);
|
||
|
|
re_en_ps = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (org_state_reg & STK_STATE_EN_ALS_MASK) {
|
||
|
|
APS_LOG("%s: force disable ALS\n", __func__);
|
||
|
|
stk3x1x_enable_als(obj->client, 0);
|
||
|
|
re_en_als = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
#if defined(CONFIG_OF)
|
||
|
|
disable_irq(obj->irq);
|
||
|
|
#else
|
||
|
|
mt_eint_mask(CUST_EINT_ALS_NUM);
|
||
|
|
#endif
|
||
|
|
reg = STK_STATE_EN_WAIT_MASK | STK_STATE_EN_PS_MASK;
|
||
|
|
err = stk3x1x_write_state(client, reg);
|
||
|
|
|
||
|
|
if (err < 0)
|
||
|
|
goto err_out;
|
||
|
|
|
||
|
|
while (counter < PS_CALI_COUNT) {
|
||
|
|
msleep(60);
|
||
|
|
|
||
|
|
err = stk3x1x_read_ps(obj->client, &obj->ps);
|
||
|
|
if (err)
|
||
|
|
goto err_out;
|
||
|
|
|
||
|
|
APS_LOG("%s: ps=%d\n", __func__, obj->ps);
|
||
|
|
ps_sum += obj->ps;
|
||
|
|
counter++;
|
||
|
|
}
|
||
|
|
|
||
|
|
obj->ps_cali = (ps_sum / PS_CALI_COUNT);
|
||
|
|
|
||
|
|
err_out:
|
||
|
|
#if defined(CONFIG_OF)
|
||
|
|
disable_irq(obj->irq);
|
||
|
|
#else
|
||
|
|
mt_eint_mask(CUST_EINT_ALS_NUM);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
if (re_en_als) {
|
||
|
|
APS_LOG("%s: re-enable ALS\n", __func__);
|
||
|
|
stk3x1x_enable_als(obj->client, 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (re_en_ps) {
|
||
|
|
APS_LOG("%s: re-enable PS\n", __func__);
|
||
|
|
stk3x1x_enable_ps(obj->client, 1, 1);
|
||
|
|
}
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk3x1x_check_intr(struct i2c_client *client, u8 *status)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int err;
|
||
|
|
|
||
|
|
err = stk3x1x_read_flag(client, status);
|
||
|
|
if (err < 0) {
|
||
|
|
APS_ERR("WARNING: read flag reg error: %d\n", err);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
APS_DBG("%s: read status reg: 0x%x\n", __func__, *status);
|
||
|
|
|
||
|
|
if (*status & STK_FLG_ALSINT_MASK)
|
||
|
|
set_bit(STK_BIT_ALS, &obj->pending_intr);
|
||
|
|
else
|
||
|
|
clear_bit(STK_BIT_ALS, &obj->pending_intr);
|
||
|
|
|
||
|
|
if (*status & STK_FLG_PSINT_MASK)
|
||
|
|
set_bit(STK_BIT_PS, &obj->pending_intr);
|
||
|
|
else
|
||
|
|
clear_bit(STK_BIT_PS, &obj->pending_intr);
|
||
|
|
|
||
|
|
if (atomic_read(&obj->trace) & STK_TRC_DEBUG)
|
||
|
|
APS_DBG("check intr: 0x%02X => 0x%08lX\n",
|
||
|
|
*status, obj->pending_intr);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
static int stk3x1x_clear_intr(struct i2c_client *client,
|
||
|
|
u8 status, u8 disable_flag)
|
||
|
|
{
|
||
|
|
int err = 0;
|
||
|
|
|
||
|
|
status = status | (STK_FLG_ALSINT_MASK | STK_FLG_PSINT_MASK
|
||
|
|
| STK_FLG_OUI_MASK | STK_FLG_IR_RDY_MASK);
|
||
|
|
status &= (~disable_flag);
|
||
|
|
|
||
|
|
err = stk3x1x_write_flag(client, status);
|
||
|
|
if (err)
|
||
|
|
APS_ERR("stk3x1x_write_flag failed, err=%d\n", err);
|
||
|
|
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
#ifdef STK_CHK_REG
|
||
|
|
static int stk3x1x_chk_reg_valid(struct stk3x1x_priv *obj)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
u8 buf[9];
|
||
|
|
|
||
|
|
if (!obj)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
memset(buf, 0, sizeof(buf));
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client, 1, &buf[0], 7);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client, 8, &buf[7], 2);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (buf[0] != atomic_read(&obj->psctrl_val)) {
|
||
|
|
APS_ERR("%s: invalid reg 0x01=0x%2x\n", __func__, buf[0]);
|
||
|
|
return 0xFF;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef STK_IRS
|
||
|
|
|
||
|
|
if (buf[1] != atomic_read(&obj->alsctrl_val)
|
||
|
|
&& buf[1] != (atomic_read(&obj->alsctrl_val) -
|
||
|
|
STK_IRS_IT_REDUCE)
|
||
|
|
&& buf[1] != (atomic_read(&obj->alsctrl_val) -
|
||
|
|
STK_ALS_READ_IRS_IT_REDUCE))
|
||
|
|
#else
|
||
|
|
if (buf[1] != atomic_read(&obj->alsctrl_val)
|
||
|
|
&& buf[1] != (atomic_read(&obj->alsctrl_val) -
|
||
|
|
STK_ALS_READ_IRS_IT_REDUCE))
|
||
|
|
#endif
|
||
|
|
{
|
||
|
|
APS_ERR("%s: invalid reg 0x02=0x%2x\n", __func__, buf[1]);
|
||
|
|
return 0xFF;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (buf[2] != obj->ledctrl_val) {
|
||
|
|
APS_ERR("%s: invalid reg 0x03=0x%2x\n", __func__, buf[2]);
|
||
|
|
return 0xFF;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (buf[3] != obj->int_val) {
|
||
|
|
APS_ERR("%s: invalid reg 0x04=0x%2x\n", __func__, buf[3]);
|
||
|
|
return 0xFF;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (buf[4] != obj->wait_val) {
|
||
|
|
APS_ERR("%s: invalid reg 0x05=0x%2x\n", __func__, buf[4]);
|
||
|
|
return 0xFF;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (buf[5] != (atomic_read(&obj->ps_high_thd_val) & 0xFF00) >> 8) {
|
||
|
|
APS_ERR("%s: invalid reg 0x06=0x%2x\n", __func__, buf[5]);
|
||
|
|
return 0xFF;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (buf[6] != (atomic_read(&obj->ps_high_thd_val) & 0x00FF)) {
|
||
|
|
APS_ERR("%s: invalid reg 0x07=0x%2x\n", __func__, buf[6]);
|
||
|
|
return 0xFF;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (buf[7] != (atomic_read(&obj->ps_low_thd_val) & 0xFF00) >> 8) {
|
||
|
|
APS_ERR("%s: invalid reg 0x08=0x%2x\n", __func__, buf[7]);
|
||
|
|
return 0xFF;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (buf[8] != (atomic_read(&obj->ps_low_thd_val) & 0x00FF)) {
|
||
|
|
APS_ERR("%s: invalid reg 0x09=0x%2x\n", __func__, buf[8]);
|
||
|
|
return 0xFF;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int stk3x1x_validate_n_handle(struct i2c_client *client)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int err;
|
||
|
|
|
||
|
|
err = stk3x1x_chk_reg_valid(obj);
|
||
|
|
if (err < 0) {
|
||
|
|
APS_ERR("stk3x1x_chk_reg_valid fail: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (err == 0xFF) {
|
||
|
|
APS_ERR("%s: Re-init chip\n", __func__);
|
||
|
|
stk3x1x_init_client(obj->client);
|
||
|
|
|
||
|
|
err = stk3x1x_write_ps_high_thd(client,
|
||
|
|
atomic_read(&obj->ps_high_thd_val);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("write high thd error: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = stk3x1x_write_ps_low_thd(client,
|
||
|
|
atomic_read(&obj->ps_low_thd_val);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("write low thd error: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0xFF;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
#endif /* #ifdef STK_CHK_REG */
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk3x1x_set_als_int_thd(struct i2c_client *client, u16 als_data_reg)
|
||
|
|
{
|
||
|
|
s32 als_thd_h, als_thd_l;
|
||
|
|
|
||
|
|
als_thd_h = als_data_reg + STK_ALS_CODE_CHANGE_THD;
|
||
|
|
als_thd_l = als_data_reg - STK_ALS_CODE_CHANGE_THD;
|
||
|
|
|
||
|
|
if (als_thd_h >= (1 << 16))
|
||
|
|
als_thd_h = (1 << 16) - 1;
|
||
|
|
|
||
|
|
if (als_thd_l < 0)
|
||
|
|
als_thd_l = 0;
|
||
|
|
|
||
|
|
APS_DBG("%s:als_thd_h:%d,als_thd_l:%d\n",
|
||
|
|
__func__, als_thd_h, als_thd_l);
|
||
|
|
stk3x1x_write_als_high_thd(client, als_thd_h);
|
||
|
|
stk3x1x_write_als_low_thd(client, als_thd_l);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk_als_int_handle(struct stk3x1x_priv *obj, u16 als_reading)
|
||
|
|
{
|
||
|
|
struct hwm_sensor_data sensor_data;
|
||
|
|
|
||
|
|
memset(&sensor_data, 0, sizeof(sensor_data));
|
||
|
|
stk3x1x_set_als_int_thd(obj->client, obj->als);
|
||
|
|
sensor_data.values[0] = stk_alscode2lux(obj, obj->als);
|
||
|
|
sensor_data.value_divide = 1;
|
||
|
|
sensor_data.status = SENSOR_STATUS_ACCURACY_MEDIUM;
|
||
|
|
APS_DBG("%s:als raw 0x%x -> value 0x%x\n", __func__,
|
||
|
|
obj->als, sensor_data.values[0]);
|
||
|
|
/*let up layer to know*/
|
||
|
|
obj->als_code_last = als_reading;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
void stk3x1x_eint_func(void)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = stk3x1x_obj;
|
||
|
|
|
||
|
|
APS_DBG(" interrupt fuc\n");
|
||
|
|
if (!obj)
|
||
|
|
return;
|
||
|
|
|
||
|
|
if (obj->hw->polling_mode_ps == 0 || obj->hw->polling_mode_als == 0)
|
||
|
|
schedule_delayed_work(&obj->eint_work, 0);
|
||
|
|
|
||
|
|
if (atomic_read(&obj->trace) & STK_TRC_EINT)
|
||
|
|
APS_DBG("eint: als/ps intrs\n");
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static void stk3x1x_eint_work(struct work_struct *work)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = stk3x1x_obj;
|
||
|
|
int err;
|
||
|
|
u8 flag_reg, disable_flag = 0;
|
||
|
|
|
||
|
|
APS_DBG(" eint work\n");
|
||
|
|
|
||
|
|
err = stk3x1x_check_intr(obj->client, &flag_reg);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("stk3x1x_check_intr fail: %d\n", err);
|
||
|
|
goto err_i2c_rw;
|
||
|
|
}
|
||
|
|
|
||
|
|
APS_DBG(" &obj->pending_intr =%lx\n", obj->pending_intr);
|
||
|
|
|
||
|
|
if (((1 << STK_BIT_ALS) & obj->pending_intr)
|
||
|
|
&& (obj->hw->polling_mode_als == 0)) {
|
||
|
|
/*get raw data*/
|
||
|
|
APS_DBG("stk als change\n");
|
||
|
|
disable_flag |= STK_FLG_ALSINT_MASK;
|
||
|
|
|
||
|
|
err = stk3x1x_read_als(obj->client, &obj->als);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("stk3x1x_read_als failed %d\n", err);
|
||
|
|
goto err_i2c_rw;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = stk_als_int_handle(obj, obj->als);
|
||
|
|
|
||
|
|
if (err < 0)
|
||
|
|
goto err_i2c_rw;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (((1 << STK_BIT_PS) & obj->pending_intr)
|
||
|
|
&& (obj->hw->polling_mode_ps == 0)) {
|
||
|
|
APS_DBG("stk ps change\n");
|
||
|
|
disable_flag |= STK_FLG_PSINT_MASK;
|
||
|
|
|
||
|
|
err = stk3x1x_read_ps(obj->client, &obj->ps);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("stk3x1x read ps data: %d\n", err);
|
||
|
|
goto err_i2c_rw;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef STK_PS_DEBUG
|
||
|
|
APS_DBG("%s: ps interrupt, show all reg\n", __func__);
|
||
|
|
show_allreg(); /*for debug*/
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
if (disable_flag) {
|
||
|
|
err = stk3x1x_clear_intr(obj->client, flag_reg, disable_flag);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("fail: %d\n", err);
|
||
|
|
goto err_i2c_rw;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
msleep(20);
|
||
|
|
#if defined(CONFIG_OF)
|
||
|
|
enable_irq(obj->irq);
|
||
|
|
#endif
|
||
|
|
return;
|
||
|
|
err_i2c_rw:
|
||
|
|
msleep(30);
|
||
|
|
#if defined(CONFIG_OF)
|
||
|
|
enable_irq(obj->irq);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
#if defined(CONFIG_OF)
|
||
|
|
static irqreturn_t stk3x1x_eint_handler(int irq, void *desc)
|
||
|
|
{
|
||
|
|
pr_info("%s\n", __func__);
|
||
|
|
disable_irq_nosync(stk3x1x_obj->irq);
|
||
|
|
stk3x1x_eint_func();
|
||
|
|
return IRQ_HANDLED;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
int stk3x1x_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 };
|
||
|
|
|
||
|
|
/* gpio setting */
|
||
|
|
pinctrl = devm_pinctrl_get(&client->dev);
|
||
|
|
|
||
|
|
if (IS_ERR(pinctrl)) {
|
||
|
|
ret = PTR_ERR(pinctrl);
|
||
|
|
APS_ERR("Cannot find alsps pinctrl!\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
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");
|
||
|
|
}
|
||
|
|
|
||
|
|
/* eint request */
|
||
|
|
if (stk3x1x_obj->irq_node) {
|
||
|
|
of_property_read_u32_array(stk3x1x_obj->irq_node,
|
||
|
|
"debounce", ints, ARRAY_SIZE(ints));
|
||
|
|
gpio_request(ints[0], "p-sensor");
|
||
|
|
gpio_set_debounce(ints[0], ints[1]);
|
||
|
|
pinctrl_select_state(pinctrl, pins_cfg);
|
||
|
|
APS_DBG("ints[0] = %d, ints[1] = %d!!\n", ints[0], ints[1]);
|
||
|
|
stk3x1x_obj->irq =
|
||
|
|
irq_of_parse_and_map(stk3x1x_obj->irq_node, 0);
|
||
|
|
APS_DBG("stk3x1x_obj->irq = %d\n", stk3x1x_obj->irq);
|
||
|
|
|
||
|
|
if (!stk3x1x_obj->irq) {
|
||
|
|
APS_ERR("irq_of_parse_and_map fail!!\n");
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = request_irq(stk3x1x_obj->irq, stk3x1x_eint_handler,
|
||
|
|
IRQ_TYPE_LEVEL_LOW, "ALS-eint", NULL);
|
||
|
|
if (ret) {
|
||
|
|
APS_ERR("IRQ LINE NOT AVAILABLE!!\n");
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
APS_ERR("null irq node!!\n");
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk3x1x_init_client(struct i2c_client *client)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
int err;
|
||
|
|
int ps_ctrl;
|
||
|
|
|
||
|
|
APS_LOG("%s: In\n", __func__);
|
||
|
|
|
||
|
|
err = stk3x1x_write_sw_reset(client);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("software reset error, err=%d", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = stk3x1x_read_id(client);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("stk3x1x_read_id error, err=%d", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (obj->first_boot == true) {
|
||
|
|
if (obj->hw->polling_mode_ps == 0
|
||
|
|
|| obj->hw->polling_mode_als == 0) {
|
||
|
|
err = stk3x1x_setup_eint(client);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("setup eint error: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
err = stk3x1x_write_state(client, atomic_read(&obj->state_val));
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("write stete error: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
ps_ctrl = atomic_read(&obj->psctrl_val);
|
||
|
|
|
||
|
|
if (obj->hw->polling_mode_ps == 1)
|
||
|
|
ps_ctrl &= 0x3F;
|
||
|
|
|
||
|
|
err = stk3x1x_write_ps(client, ps_ctrl);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("write ps error: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = stk3x1x_write_als(client, atomic_read(&obj->alsctrl_val));
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("write als error: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = stk3x1x_write_led(client, obj->ledctrl_val);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("write led error: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = stk3x1x_write_wait(client, obj->wait_val);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("write wait error: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = stk3x1x_write_int(client, obj->int_val);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("write int mode error: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
obj->re_enable_ps = false;
|
||
|
|
obj->re_enable_als = false;
|
||
|
|
obj->als_code_last = 100;
|
||
|
|
|
||
|
|
obj->ps_distance_last = -1;
|
||
|
|
obj->als_code_last = 100;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static inline uint32_t stk3x1x_get_als_reading_avg(struct i2c_client *client,
|
||
|
|
int sSampleNo)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
uint16_t ALSData = 0;
|
||
|
|
uint16_t DataCount = 0;
|
||
|
|
uint32_t sAveAlsData = 0;
|
||
|
|
|
||
|
|
while (DataCount < sSampleNo) {
|
||
|
|
msleep(100);
|
||
|
|
|
||
|
|
res = stk3x1x_read_als(stk3x1x_obj->client, &stk3x1x_obj->als);
|
||
|
|
if (res)
|
||
|
|
APS_ERR("get_als_reading_avg: stk3x1x_read_als!!\n");
|
||
|
|
|
||
|
|
ALSData = stk3x1x_obj->als;
|
||
|
|
APS_DBG("%s: [STK]als code = %d\n", __func__, ALSData);
|
||
|
|
sAveAlsData += ALSData;
|
||
|
|
DataCount++;
|
||
|
|
}
|
||
|
|
sAveAlsData /= sSampleNo;
|
||
|
|
return sAveAlsData;
|
||
|
|
}
|
||
|
|
/*
|
||
|
|
* Sysfs attributes
|
||
|
|
*/
|
||
|
|
static ssize_t config_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
ssize_t res;
|
||
|
|
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
res = scnprintf(buf, PAGE_SIZE, "(%d %d %d %d %d %d)\n",
|
||
|
|
atomic_read(&stk3x1x_obj->i2c_retry),
|
||
|
|
atomic_read(&stk3x1x_obj->als_debounce),
|
||
|
|
atomic_read(&stk3x1x_obj->ps_mask),
|
||
|
|
atomic_read(&stk3x1x_obj->ps_high_thd_val),
|
||
|
|
atomic_read(&stk3x1x_obj->ps_low_thd_val),
|
||
|
|
atomic_read(&stk3x1x_obj->ps_debounce));
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t config_store(struct device_driver *ddri,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
int retry, als_deb, ps_deb, mask, hthres, lthres, err;
|
||
|
|
struct i2c_client *client;
|
||
|
|
|
||
|
|
client = stk3x1x_i2c_client;
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (sscanf(buf, "%d %d %d %d %d %d",
|
||
|
|
&retry, &als_deb, &mask, &hthres, <hres, &ps_deb) == 6) {
|
||
|
|
atomic_set(&stk3x1x_obj->i2c_retry, retry);
|
||
|
|
atomic_set(&stk3x1x_obj->als_debounce, als_deb);
|
||
|
|
atomic_set(&stk3x1x_obj->ps_mask, mask);
|
||
|
|
atomic_set(&stk3x1x_obj->ps_high_thd_val, hthres);
|
||
|
|
atomic_set(&stk3x1x_obj->ps_low_thd_val, lthres);
|
||
|
|
atomic_set(&stk3x1x_obj->ps_debounce, ps_deb);
|
||
|
|
|
||
|
|
err = stk3x1x_write_ps_high_thd(client,
|
||
|
|
atomic_read(&stk3x1x_obj->ps_high_thd_val));
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("write high thd error: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = stk3x1x_write_ps_low_thd(client,
|
||
|
|
atomic_read(&stk3x1x_obj->ps_low_thd_val));
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("write low thd error: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
APS_ERR("invalid content: '%s'\n", buf);
|
||
|
|
}
|
||
|
|
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t trace_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
ssize_t res;
|
||
|
|
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
res = scnprintf(buf, PAGE_SIZE, "0x%04X\n",
|
||
|
|
atomic_read(&stk3x1x_obj->trace));
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t trace_store(struct device_driver *ddri,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
int trace;
|
||
|
|
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (sscanf(buf, "0x%x", &trace) == 1)
|
||
|
|
atomic_set(&stk3x1x_obj->trace, trace);
|
||
|
|
else
|
||
|
|
APS_ERR("invalid content: '%s', count = %d\n", buf, (int)count);
|
||
|
|
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t ir_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
int32_t reading;
|
||
|
|
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
reading = stk3x1x_get_ir_value(stk3x1x_obj, STK_IRS_IT_REDUCE);
|
||
|
|
|
||
|
|
if (reading < 0)
|
||
|
|
return scnprintf(buf, PAGE_SIZE, "ERROR: %d\n", reading);
|
||
|
|
|
||
|
|
stk3x1x_obj->ir_code = reading;
|
||
|
|
return scnprintf(buf, PAGE_SIZE, "0x%04X\n", stk3x1x_obj->ir_code);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t als_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (stk3x1x_obj->hw->polling_mode_als == 1) {
|
||
|
|
res = stk3x1x_read_als(stk3x1x_obj->client, &stk3x1x_obj->als);
|
||
|
|
if (res)
|
||
|
|
return scnprintf(buf, PAGE_SIZE, "ERROR: %d\n", res);
|
||
|
|
else
|
||
|
|
return scnprintf(buf, PAGE_SIZE,
|
||
|
|
"%d\n", stk3x1x_obj->als);
|
||
|
|
}
|
||
|
|
|
||
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", stk3x1x_obj->als_code_last);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t ps_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
res = stk3x1x_read_ps(stk3x1x_obj->client, &stk3x1x_obj->ps);
|
||
|
|
if (res)
|
||
|
|
return scnprintf(buf, PAGE_SIZE, "ERROR: %d\n", res);
|
||
|
|
else
|
||
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", stk3x1x_obj->ps);
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t reg_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
u8 int_status;
|
||
|
|
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*read*/
|
||
|
|
stk3x1x_check_intr(stk3x1x_obj->client, &int_status);
|
||
|
|
stk3x1x_read_ps(stk3x1x_obj->client, &stk3x1x_obj->ps);
|
||
|
|
stk3x1x_read_als(stk3x1x_obj->client, &stk3x1x_obj->als);
|
||
|
|
/*write*/
|
||
|
|
stk3x1x_write_als(stk3x1x_obj->client,
|
||
|
|
atomic_read(&stk3x1x_obj->alsctrl_val));
|
||
|
|
stk3x1x_write_ps(stk3x1x_obj->client,
|
||
|
|
atomic_read(&stk3x1x_obj->psctrl_val));
|
||
|
|
stk3x1x_write_ps_high_thd(stk3x1x_obj->client,
|
||
|
|
atomic_read(&stk3x1x_obj->ps_high_thd_val));
|
||
|
|
stk3x1x_write_ps_low_thd(stk3x1x_obj->client,
|
||
|
|
atomic_read(&stk3x1x_obj->ps_low_thd_val));
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t send_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t send_store(struct device_driver *ddri,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
int addr, cmd;
|
||
|
|
u8 dat;
|
||
|
|
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
} else if (sscanf(buf, "%x %x", &addr, &cmd) != 2) {
|
||
|
|
APS_ERR("invalid format: '%s'\n", buf);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
dat = (u8)cmd;
|
||
|
|
APS_DBG("send(%02X, %02X) = %d\n", addr, cmd,
|
||
|
|
stk3x1x_master_send(stk3x1x_obj->client,
|
||
|
|
(u16)addr, &dat, sizeof(dat)));
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t recv_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
return scnprintf(buf, PAGE_SIZE, "0x%04X\n",
|
||
|
|
atomic_read(&stk3x1x_obj->recv_reg));
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t recv_store(struct device_driver *ddri,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
int addr;
|
||
|
|
u8 dat;
|
||
|
|
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
} else if (kstrtouint(buf, 16, &addr) != 0) {
|
||
|
|
APS_ERR("invalid format: '%s'\n", buf);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
APS_DBG("recv(%02X) = %d, 0x%02X\n", addr,
|
||
|
|
stk3x1x_master_recv(stk3x1x_obj->client,
|
||
|
|
(u16)addr, (char *)&dat, sizeof(dat)), dat);
|
||
|
|
atomic_set(&stk3x1x_obj->recv_reg, dat);
|
||
|
|
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
|
||
|
|
static ssize_t allreg_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
u8 rbuf[0x22];
|
||
|
|
int cnt;
|
||
|
|
int len = 0;
|
||
|
|
|
||
|
|
memset(rbuf, 0, sizeof(rbuf));
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client, 0, &rbuf[0], 7);
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client, 7, &rbuf[7], 7);
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client, 14, &rbuf[14], 7);
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client, 21, &rbuf[21], 7);
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client, 28, &rbuf[28], 4);
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client,
|
||
|
|
STK_PDT_ID_REG, &rbuf[32], 2);
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (cnt = 0; cnt < 0x20; cnt++) {
|
||
|
|
APS_LOG("reg[0x%x]=0x%x\n", cnt, rbuf[cnt]);
|
||
|
|
len += scnprintf(buf + len, PAGE_SIZE - len,
|
||
|
|
"[%2X]%2X,", cnt, rbuf[cnt]);
|
||
|
|
}
|
||
|
|
|
||
|
|
APS_DBG("reg[0x3E]=0x%x\n", rbuf[cnt]);
|
||
|
|
APS_DBG("reg[0x3F]=0x%x\n", rbuf[cnt++]);
|
||
|
|
len += scnprintf(buf + len, PAGE_SIZE - len, "[0x3E]%2X,[0x3F]%2X\n",
|
||
|
|
rbuf[cnt - 1], rbuf[cnt]);
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t status_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
ssize_t len = 0;
|
||
|
|
u8 rbuf[25];
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (stk3x1x_obj->hw) {
|
||
|
|
len += scnprintf(buf + len, PAGE_SIZE - len,
|
||
|
|
"CUST: %d, (%d %d) (%02X)(%02X %02X %02X) (%02X %02X %02X %02X)\n",
|
||
|
|
stk3x1x_obj->hw->i2c_num,
|
||
|
|
stk3x1x_obj->hw->power_id,
|
||
|
|
stk3x1x_obj->hw->power_vol,
|
||
|
|
stk3x1x_obj->addr.flag,
|
||
|
|
stk3x1x_obj->addr.alsctrl,
|
||
|
|
stk3x1x_obj->addr.data1_als,
|
||
|
|
stk3x1x_obj->addr.data2_als,
|
||
|
|
stk3x1x_obj->addr.psctrl,
|
||
|
|
stk3x1x_obj->addr.data1_ps,
|
||
|
|
stk3x1x_obj->addr.data2_ps,
|
||
|
|
stk3x1x_obj->addr.thdh1_ps);
|
||
|
|
} else {
|
||
|
|
len += scnprintf(buf + len, PAGE_SIZE - len, "CUST: NULL\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
len += scnprintf(buf + len, PAGE_SIZE - len,
|
||
|
|
"REGS: %02X %02X %02X %02X %02X %02X %02X %02X %02lX %02lX\n",
|
||
|
|
atomic_read(&stk3x1x_obj->state_val),
|
||
|
|
atomic_read(&stk3x1x_obj->psctrl_val),
|
||
|
|
atomic_read(&stk3x1x_obj->alsctrl_val),
|
||
|
|
stk3x1x_obj->ledctrl_val, stk3x1x_obj->int_val,
|
||
|
|
stk3x1x_obj->wait_val,
|
||
|
|
atomic_read(&stk3x1x_obj->ps_high_thd_val),
|
||
|
|
atomic_read(&stk3x1x_obj->ps_low_thd_val),
|
||
|
|
stk3x1x_obj->enable,
|
||
|
|
stk3x1x_obj->pending_intr);
|
||
|
|
#ifdef MT6516
|
||
|
|
len += scnprintf(buf + len, PAGE_SIZE - len, "EINT: %d (%d %d %d %d)\n",
|
||
|
|
mt_get_gpio_in(GPIO_ALS_EINT_PIN),
|
||
|
|
CUST_EINT_ALS_NUM,
|
||
|
|
CUST_EINT_ALS_POLARITY,
|
||
|
|
CUST_EINT_ALS_DEBOUNCE_EN,
|
||
|
|
CUST_EINT_ALS_DEBOUNCE_CN);
|
||
|
|
len += scnprintf(buf + len, PAGE_SIZE - len, "GPIO: %d (%d %d %d %d)\n",
|
||
|
|
GPIO_ALS_EINT_PIN,
|
||
|
|
mt_get_gpio_dir(GPIO_ALS_EINT_PIN),
|
||
|
|
mt_get_gpio_mode(GPIO_ALS_EINT_PIN),
|
||
|
|
mt_get_gpio_pull_enable(GPIO_ALS_EINT_PIN),
|
||
|
|
mt_get_gpio_pull_select(GPIO_ALS_EINT_PIN));
|
||
|
|
#endif
|
||
|
|
len += scnprintf(buf + len, PAGE_SIZE - len, "MISC: %d %d\n",
|
||
|
|
atomic_read(&stk3x1x_obj->als_suspend),
|
||
|
|
atomic_read(&stk3x1x_obj->ps_suspend));
|
||
|
|
len += scnprintf(buf + len, PAGE_SIZE - len, "VER.: %s\n",
|
||
|
|
DRIVER_VERSION);
|
||
|
|
memset(rbuf, 0, sizeof(rbuf));
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client, 0, &rbuf[0], 7);
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client, 7, &rbuf[7], 7);
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client, 14, &rbuf[14], 7);
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
len += scnprintf(buf + len, PAGE_SIZE - len,
|
||
|
|
"[PS=%2X] [ALS=%2X] [WAIT=%4Xms]\n",
|
||
|
|
rbuf[0] & 0x01, (rbuf[0] & 0x02) >> 1,
|
||
|
|
((rbuf[0] & 0x04) >> 2) * rbuf[5] * 6);
|
||
|
|
len += scnprintf(buf + len, PAGE_SIZE - len,
|
||
|
|
"[EN_ASO=%2X] [EN_AK=%2X] [NEAR/FAR=%2X]\n",
|
||
|
|
(rbuf[0] & 0x20) >> 5, (rbuf[0] & 0x40) >> 6,
|
||
|
|
rbuf[16] & 0x01);
|
||
|
|
len += scnprintf(buf + len, PAGE_SIZE - len,
|
||
|
|
"[FLAG_OUI=%2X] [FLAG_PSINT=%2X] [FLAG_ALSINT=%2X]\n",
|
||
|
|
(rbuf[16] & 0x04) >> 2,
|
||
|
|
(rbuf[16] & 0x10) >> 4, (rbuf[16] & 0x20) >> 5);
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
#define IS_SPACE(CH) (((CH) == ' ') || ((CH) == '\n'))
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t pscalibration_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t pscalibration_store(struct device_driver *ddri,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
ret = stk3x1x_ps_calibration(stk3x1x_obj->client);
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t pthredcalibration_show(struct device_driver *ddri,
|
||
|
|
char *buf)
|
||
|
|
{
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t ps_offset_store(struct device_driver *ddri,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t enable_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
int32_t enable;
|
||
|
|
u8 r_buf;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(stk3x1x_obj->client,
|
||
|
|
STK_STATE_REG, &r_buf, 0x01);
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("error: %d\n", ret);
|
||
|
|
return -EFAULT;
|
||
|
|
}
|
||
|
|
|
||
|
|
enable = (r_buf & STK_STATE_EN_ALS_MASK) ? 1 : 0;
|
||
|
|
|
||
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", enable);
|
||
|
|
}
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
APS_LOG("%s: Enable ALS : %d\n", __func__, en);
|
||
|
|
|
||
|
|
stk3x1x_enable_als(stk3x1x_obj->client, en);
|
||
|
|
return size;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t alstest_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
int32_t als_reading;
|
||
|
|
|
||
|
|
APS_LOG("%s: [STK]Start testing light...\n", __func__);
|
||
|
|
|
||
|
|
msleep(150);
|
||
|
|
als_reading = stk3x1x_get_als_reading_avg(stk3x1x_obj->client, 5);
|
||
|
|
APS_LOG("%s: [STK]als_reading = %d\n", __func__, als_reading);
|
||
|
|
|
||
|
|
als_value = stk_alscode2lux(stk3x1x_obj, als_reading);
|
||
|
|
|
||
|
|
APS_LOG("%s: [STK]Start testing light done!!! als_value = %d\n",
|
||
|
|
__func__, als_value);
|
||
|
|
APS_LOG("%s: [STK]Start testing light done!!! als_test_adc = %d\n",
|
||
|
|
__func__, als_reading);
|
||
|
|
return scnprintf(buf, PAGE_SIZE,
|
||
|
|
"als_value = %5d lux, cci_als_test_adc = %5d\n",
|
||
|
|
als_value, als_reading);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static ssize_t alscali_show(struct device_driver *ddri, char *buf)
|
||
|
|
{
|
||
|
|
int32_t als_reading;
|
||
|
|
unsigned int als_adc_cali;
|
||
|
|
bool result = true;
|
||
|
|
int als_calibration_lux;
|
||
|
|
|
||
|
|
APS_LOG("%s: [STK]Start Cali light...\n", __func__);
|
||
|
|
|
||
|
|
als_calibration_lux = ALS_CALIBRATION_LUX;
|
||
|
|
|
||
|
|
msleep(150);
|
||
|
|
als_reading = stk3x1x_get_als_reading_avg(stk3x1x_obj->client, 5);
|
||
|
|
als_adc_cali = als_reading;
|
||
|
|
|
||
|
|
als_value_cali = stk_alscode2lux(stk3x1x_obj, als_reading);
|
||
|
|
|
||
|
|
if (((als_value_cali * stk3x1x_obj->als_transmittance)
|
||
|
|
/ als_calibration_lux) > 0 && (als_adc_cali <= 65535)) {
|
||
|
|
|
||
|
|
transmittance_cali = (als_value_cali
|
||
|
|
* stk3x1x_obj->als_transmittance) / als_calibration_lux;
|
||
|
|
/* transmittance for cali */
|
||
|
|
|
||
|
|
stk3x1x_obj->als_transmittance = transmittance_cali;
|
||
|
|
|
||
|
|
/* calculate lux base on calibrated transmittance */
|
||
|
|
als_value_cali = stk_alscode2lux(stk3x1x_obj, als_reading);
|
||
|
|
|
||
|
|
APS_LOG("%s: [STK]cali light done!!! als_value_cali = %d lux\n",
|
||
|
|
__func__, als_value_cali);
|
||
|
|
APS_LOG("%s:[STK]cali light done!!! transmittance_cali = %d\n",
|
||
|
|
__func__, transmittance_cali);
|
||
|
|
APS_LOG("%s:[STK]cali light done!!! als_adc_cali = %d code\n",
|
||
|
|
__func__, als_adc_cali);
|
||
|
|
} else {
|
||
|
|
APS_LOG("%s: [STK]cali light fail!!! als_value_cali = %d lux\n",
|
||
|
|
__func__, als_value_cali);
|
||
|
|
APS_LOG("%s:[STK]cali light fail!!! transmittance_cali = %d\n",
|
||
|
|
__func__, transmittance_cali);
|
||
|
|
APS_LOG("%s:[STK]cali light fail!!! als_adc_cali = %d code\n",
|
||
|
|
__func__, als_adc_cali);
|
||
|
|
result = false;
|
||
|
|
}
|
||
|
|
/* compute transmittance for ALS calibration end */
|
||
|
|
|
||
|
|
return scnprintf(buf, PAGE_SIZE,
|
||
|
|
"%s: cali =%d lux, transmittance_cali= %d,adc_cali= %d code\n",
|
||
|
|
result ? "PASSED" : "FAIL", als_value_cali,
|
||
|
|
transmittance_cali, als_adc_cali);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static DRIVER_ATTR_RO(als);
|
||
|
|
static DRIVER_ATTR_RO(ps);
|
||
|
|
static DRIVER_ATTR_RO(ir);
|
||
|
|
static DRIVER_ATTR_RW(config);
|
||
|
|
static DRIVER_ATTR_RW(trace);
|
||
|
|
static DRIVER_ATTR_RO(status);
|
||
|
|
static DRIVER_ATTR_RW(send);
|
||
|
|
static DRIVER_ATTR_RW(recv);
|
||
|
|
static DRIVER_ATTR_RO(reg);
|
||
|
|
static DRIVER_ATTR_RO(allreg);
|
||
|
|
static DRIVER_ATTR_RW(pscalibration);
|
||
|
|
static DRIVER_ATTR_RO(pthredcalibration);
|
||
|
|
static DRIVER_ATTR_WO(ps_offset);
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static DRIVER_ATTR_RW(enable);
|
||
|
|
static DRIVER_ATTR_RO(alstest);
|
||
|
|
static DRIVER_ATTR_RO(alscali);
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static struct driver_attribute *stk3x1x_attr_list[] = {
|
||
|
|
&driver_attr_als,
|
||
|
|
&driver_attr_ps,
|
||
|
|
&driver_attr_ir,
|
||
|
|
&driver_attr_trace, /*trace log*/
|
||
|
|
&driver_attr_config,
|
||
|
|
&driver_attr_status,
|
||
|
|
&driver_attr_send,
|
||
|
|
&driver_attr_recv,
|
||
|
|
&driver_attr_allreg,
|
||
|
|
&driver_attr_reg,
|
||
|
|
&driver_attr_pscalibration,
|
||
|
|
&driver_attr_pthredcalibration,
|
||
|
|
&driver_attr_ps_offset,
|
||
|
|
|
||
|
|
&driver_attr_enable,
|
||
|
|
&driver_attr_alstest,
|
||
|
|
&driver_attr_alscali,
|
||
|
|
};
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk3x1x_create_attr(struct device_driver *driver)
|
||
|
|
{
|
||
|
|
int idx, err = 0;
|
||
|
|
int num = (int)(sizeof(stk3x1x_attr_list) /
|
||
|
|
sizeof(stk3x1x_attr_list[0]));
|
||
|
|
|
||
|
|
if (driver == NULL)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
for (idx = 0; idx < num; idx++) {
|
||
|
|
err = driver_create_file(driver, stk3x1x_attr_list[idx]);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("driver_create_file (%s) = %d\n",
|
||
|
|
stk3x1x_attr_list[idx]->attr.name, err);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk3x1x_delete_attr(struct device_driver *driver)
|
||
|
|
{
|
||
|
|
int idx, err = 0;
|
||
|
|
int num = (int)(sizeof(stk3x1x_attr_list) /
|
||
|
|
sizeof(stk3x1x_attr_list[0]));
|
||
|
|
|
||
|
|
if (!driver)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
for (idx = 0; idx < num; idx++)
|
||
|
|
driver_remove_file(driver, stk3x1x_attr_list[idx]);
|
||
|
|
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Function Configuration
|
||
|
|
*/
|
||
|
|
static uint32_t stk_alscode2lux(struct stk3x1x_priv *obj, uint32_t alscode)
|
||
|
|
{
|
||
|
|
/*
|
||
|
|
* Lux = ALS ADC data / Display Transmittance
|
||
|
|
* Formula for ADC to LUX
|
||
|
|
*/
|
||
|
|
alscode += ((alscode<<7)+(alscode<<3)+(alscode>>1));
|
||
|
|
alscode <<= 3;
|
||
|
|
alscode /= obj->als_transmittance;
|
||
|
|
return alscode;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk3x1x_get_ps_value_only(struct stk3x1x_priv *obj, u16 ps)
|
||
|
|
{
|
||
|
|
int mask = atomic_read(&obj->ps_mask);
|
||
|
|
int invalid = 0, val;
|
||
|
|
int err;
|
||
|
|
u8 flag;
|
||
|
|
|
||
|
|
err = stk3x1x_read_flag(obj->client, &flag);
|
||
|
|
if (err)
|
||
|
|
return err;
|
||
|
|
|
||
|
|
val = (flag & STK_FLG_NF_MASK) ? 1 : 0;
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!invalid) {
|
||
|
|
if (unlikely(atomic_read(&obj->trace) & STK_TRC_CVT_PS)) {
|
||
|
|
if (mask)
|
||
|
|
APS_DBG("PS: %05d => %05d [M]\n", ps, val);
|
||
|
|
else
|
||
|
|
APS_DBG("PS: %05d => %05d\n", ps, val);
|
||
|
|
}
|
||
|
|
|
||
|
|
return val;
|
||
|
|
}
|
||
|
|
APS_ERR(" ps value is invalid, PS: %05d => %05d\n", ps, val);
|
||
|
|
|
||
|
|
if (unlikely(atomic_read(&obj->trace) & STK_TRC_CVT_PS))
|
||
|
|
APS_ERR("PS: %05d => %05d (-1)\n", ps, val);
|
||
|
|
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk3x1x_get_ps_value(struct stk3x1x_priv *obj, u16 ps)
|
||
|
|
{
|
||
|
|
int mask = atomic_read(&obj->ps_mask);
|
||
|
|
int invalid = 0, val;
|
||
|
|
int err;
|
||
|
|
u8 flag;
|
||
|
|
|
||
|
|
err = stk3x1x_read_flag(obj->client, &flag);
|
||
|
|
if (err)
|
||
|
|
return err;
|
||
|
|
|
||
|
|
val = (flag & STK_FLG_NF_MASK) ? 1 : 0;
|
||
|
|
|
||
|
|
APS_DBG("%s: read_flag = 0x%x, val = %d\n", __func__, flag, val);
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!invalid) {
|
||
|
|
if (unlikely(atomic_read(&obj->trace) & STK_TRC_CVT_PS)) {
|
||
|
|
if (mask)
|
||
|
|
APS_DBG("PS: %05d => %05d [M]\n", ps, val);
|
||
|
|
else
|
||
|
|
APS_DBG("PS: %05d => %05d\n", ps, val);
|
||
|
|
}
|
||
|
|
|
||
|
|
return val;
|
||
|
|
}
|
||
|
|
APS_ERR("ps value is invalid, PS: %05d => %05d\n", ps, val);
|
||
|
|
|
||
|
|
if (unlikely(atomic_read(&obj->trace) & STK_TRC_CVT_PS))
|
||
|
|
APS_ERR("PS: %05d => %05d (-1)\n", ps, val);
|
||
|
|
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
|
||
|
|
static int32_t stk3x1x_set_irs_it_slp(struct stk3x1x_priv *obj,
|
||
|
|
uint16_t *slp_time, int32_t ials_it_reduce)
|
||
|
|
{
|
||
|
|
uint8_t irs_alsctrl;
|
||
|
|
int32_t ret;
|
||
|
|
|
||
|
|
irs_alsctrl = (atomic_read(&obj->alsctrl_val) & 0x0F) - ials_it_reduce;
|
||
|
|
switch (irs_alsctrl) {
|
||
|
|
case 2:
|
||
|
|
*slp_time = 1;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 3:
|
||
|
|
*slp_time = 2;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 4:
|
||
|
|
*slp_time = 3;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 5:
|
||
|
|
*slp_time = 6;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 6:
|
||
|
|
*slp_time = 12;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 7:
|
||
|
|
*slp_time = 24;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 8:
|
||
|
|
*slp_time = 48;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 9:
|
||
|
|
*slp_time = 96;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 10:
|
||
|
|
*slp_time = 192;
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
APS_ERR("%s: unknown ALS IT=0x%x\n", __func__, irs_alsctrl);
|
||
|
|
ret = -EINVAL;
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
irs_alsctrl |= (atomic_read(&obj->alsctrl_val) & 0xF0);
|
||
|
|
ret = i2c_smbus_write_byte_data(obj->client,
|
||
|
|
STK_ALSCTRL_REG, irs_alsctrl);
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("%s: write i2c error\n", __func__);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t stk3x1x_get_ir_value(struct stk3x1x_priv *obj,
|
||
|
|
int32_t als_it_reduce)
|
||
|
|
{
|
||
|
|
int32_t word_data, ret;
|
||
|
|
uint8_t w_reg, retry = 0;
|
||
|
|
uint16_t irs_slp_time = 100;
|
||
|
|
|
||
|
|
u8 flag;
|
||
|
|
u8 buf[2];
|
||
|
|
|
||
|
|
ret = stk3x1x_set_irs_it_slp(obj, &irs_slp_time, als_it_reduce);
|
||
|
|
|
||
|
|
if (ret < 0)
|
||
|
|
goto irs_err_i2c_rw;
|
||
|
|
|
||
|
|
w_reg = atomic_read(&obj->state_val) | STK_STATE_EN_IRS_MASK;
|
||
|
|
ret = i2c_smbus_write_byte_data(obj->client, STK_STATE_REG, w_reg);
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("%s: write i2c error\n", __func__);
|
||
|
|
goto irs_err_i2c_rw;
|
||
|
|
}
|
||
|
|
|
||
|
|
msleep(irs_slp_time);
|
||
|
|
|
||
|
|
do {
|
||
|
|
msleep(20);
|
||
|
|
ret = stk3x1x_read_flag(obj->client, &flag);
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("WARNING: read flag reg error: %d\n", ret);
|
||
|
|
goto irs_err_i2c_rw;
|
||
|
|
}
|
||
|
|
|
||
|
|
retry++;
|
||
|
|
} while (retry < 10 && ((flag & STK_FLG_IR_RDY_MASK) == 0));
|
||
|
|
|
||
|
|
if (retry == 10) {
|
||
|
|
APS_ERR("%s: ir data is not ready for a long time\n", __func__);
|
||
|
|
ret = -EINVAL;
|
||
|
|
goto irs_err_i2c_rw;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_clear_intr(obj->client, flag, STK_FLG_IR_RDY_MASK);
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("%s: write i2c error\n", __func__);
|
||
|
|
goto irs_err_i2c_rw;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = stk3x1x_master_recv(obj->client, STK_DATA1_IR_REG, buf, 2);
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("%s fail, ret=0x%x", __func__, ret);
|
||
|
|
goto irs_err_i2c_rw;
|
||
|
|
}
|
||
|
|
|
||
|
|
word_data = (buf[0] << 8) | buf[1];
|
||
|
|
ret = i2c_smbus_write_byte_data(obj->client, STK_ALSCTRL_REG,
|
||
|
|
atomic_read(&obj->alsctrl_val));
|
||
|
|
|
||
|
|
if (ret < 0) {
|
||
|
|
APS_ERR("%s: write i2c error\n", __func__);
|
||
|
|
goto irs_err_i2c_rw;
|
||
|
|
}
|
||
|
|
return word_data;
|
||
|
|
irs_err_i2c_rw:
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Function Configuration
|
||
|
|
*/
|
||
|
|
static int stk3x1x_open(struct inode *inode, struct file *file)
|
||
|
|
{
|
||
|
|
file->private_data = stk3x1x_i2c_client;
|
||
|
|
|
||
|
|
if (!file->private_data) {
|
||
|
|
APS_ERR("null pointer!!\n");
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
return nonseekable_open(inode, file);
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk3x1x_release(struct inode *inode, struct file *file)
|
||
|
|
{
|
||
|
|
file->private_data = NULL;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static long stk3x1x_unlocked_ioctl(struct file *file,
|
||
|
|
unsigned int cmd, unsigned long arg)
|
||
|
|
{
|
||
|
|
struct i2c_client *client = (struct i2c_client *)file->private_data;
|
||
|
|
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
|
||
|
|
long err = 0;
|
||
|
|
void __user *ptr = (void __user *) arg;
|
||
|
|
int dat;
|
||
|
|
uint32_t enable;
|
||
|
|
int ps_result, ps_cali_result;
|
||
|
|
int threshold[2];
|
||
|
|
|
||
|
|
switch (cmd) {
|
||
|
|
case ALSPS_SET_PS_MODE:
|
||
|
|
if (copy_from_user(&enable, ptr, sizeof(enable))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (enable) {
|
||
|
|
err = stk3x1x_enable_ps(obj->client, 1, 1);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("enable ps fail: %ld\n", err);
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
set_bit(STK_BIT_PS, &obj->enable);
|
||
|
|
} else {
|
||
|
|
err = stk3x1x_enable_ps(obj->client, 0, 1);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("disable ps fail: %ld\n", err);
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
clear_bit(STK_BIT_PS, &obj->enable);
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_GET_PS_MODE:
|
||
|
|
enable = test_bit(STK_BIT_PS, &obj->enable) ? (1) : (0);
|
||
|
|
|
||
|
|
if (copy_to_user(ptr, &enable, sizeof(enable))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_GET_PS_DATA:
|
||
|
|
err = stk3x1x_read_ps(obj->client, &obj->ps);
|
||
|
|
if (err)
|
||
|
|
goto err_out;
|
||
|
|
|
||
|
|
dat = stk3x1x_get_ps_value(obj, obj->ps);
|
||
|
|
if (dat < 0) {
|
||
|
|
err = dat;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (copy_to_user(ptr, &dat, sizeof(dat))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_GET_PS_RAW_DATA:
|
||
|
|
err = stk3x1x_read_ps(obj->client, &obj->ps);
|
||
|
|
if (err)
|
||
|
|
goto err_out;
|
||
|
|
|
||
|
|
dat = obj->ps;
|
||
|
|
|
||
|
|
if (copy_to_user(ptr, &dat, sizeof(dat))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_SET_ALS_MODE:
|
||
|
|
if (copy_from_user(&enable, ptr, sizeof(enable))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (enable) {
|
||
|
|
err = stk3x1x_enable_als(obj->client, 1);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("enable als fail: %ld\n", err);
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
set_bit(STK_BIT_ALS, &obj->enable);
|
||
|
|
} else {
|
||
|
|
err = stk3x1x_enable_als(obj->client, 0);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("disable als fail: %ld\n", err);
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
clear_bit(STK_BIT_ALS, &obj->enable);
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_GET_ALS_MODE:
|
||
|
|
enable = test_bit(STK_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 = stk3x1x_read_als(obj->client, &obj->als);
|
||
|
|
if (err)
|
||
|
|
goto err_out;
|
||
|
|
|
||
|
|
dat = stk_alscode2lux(obj, obj->als);
|
||
|
|
|
||
|
|
if (copy_to_user(ptr, &dat, sizeof(dat))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_GET_ALS_RAW_DATA:
|
||
|
|
err = stk3x1x_read_als(obj->client, &obj->als);
|
||
|
|
if (err)
|
||
|
|
goto err_out;
|
||
|
|
|
||
|
|
dat = obj->als;
|
||
|
|
|
||
|
|
if (copy_to_user(ptr, &dat, sizeof(dat))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_GET_PS_THRESHOLD_HIGH:
|
||
|
|
dat = atomic_read(&obj->ps_high_thd_val);
|
||
|
|
APS_LOG("%s:ps_high_thd_val:%d\n", __func__, dat);
|
||
|
|
|
||
|
|
if (copy_to_user(ptr, &dat, sizeof(dat))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_GET_PS_THRESHOLD_LOW:
|
||
|
|
dat = atomic_read(&obj->ps_low_thd_val);
|
||
|
|
APS_LOG("%s:ps_low_thd_val:%d\n", __func__, dat);
|
||
|
|
|
||
|
|
if (copy_to_user(ptr, &dat, sizeof(dat))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_GET_PS_TEST_RESULT:
|
||
|
|
err = stk3x1x_read_ps(obj->client, &obj->ps);
|
||
|
|
if (err)
|
||
|
|
goto err_out;
|
||
|
|
|
||
|
|
if (obj->ps > atomic_read(&obj->ps_high_thd_val))
|
||
|
|
ps_result = 0;
|
||
|
|
else
|
||
|
|
ps_result = 1;
|
||
|
|
|
||
|
|
APS_LOG("ALSPS_GET_PS_TEST: ps_result = %d\n", ps_result);
|
||
|
|
|
||
|
|
if (copy_to_user(ptr, &ps_result, sizeof(ps_result))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_IOCTL_CLR_CALI:
|
||
|
|
if (copy_from_user(&dat, ptr, sizeof(dat))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
obj->ps_cali = 0;
|
||
|
|
atomic_set(&obj->ps_high_thd_val, obj->hw->ps_threshold_high);
|
||
|
|
atomic_set(&obj->ps_low_thd_val, obj->hw->ps_threshold_low);
|
||
|
|
APS_LOG("ALSPS_IOCTL_CLR_CAL : ps_cali:%d high:%d low:%d\n",
|
||
|
|
obj->ps_cali,
|
||
|
|
atomic_read(&obj->ps_high_thd_val),
|
||
|
|
atomic_read(&obj->ps_low_thd_val));
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_IOCTL_GET_CALI:
|
||
|
|
stk3x1x_ps_calibration(obj->client);
|
||
|
|
ps_cali_result = obj->ps_cali;
|
||
|
|
APS_LOG("ALSPS_IOCTL_GET_CAL : ps_cali = %d\n", obj->ps_cali);
|
||
|
|
|
||
|
|
if (copy_to_user(ptr, &ps_cali_result,
|
||
|
|
sizeof(ps_cali_result))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_IOCTL_SET_CALI:
|
||
|
|
|
||
|
|
/*1. libhwm.so calc value store in ps_cali;*/
|
||
|
|
/*2. nvram_daemon update ps_cali in driver*/
|
||
|
|
if (copy_from_user(&ps_cali_result, ptr,
|
||
|
|
sizeof(ps_cali_result))) {
|
||
|
|
err = -EFAULT;
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
obj->ps_cali = ps_cali_result;
|
||
|
|
|
||
|
|
atomic_set(&obj->ps_high_thd_val, obj->ps_cali + 200);
|
||
|
|
atomic_set(&obj->ps_low_thd_val, obj->ps_cali + 150);
|
||
|
|
|
||
|
|
err = stk3x1x_write_ps_high_thd(obj->client,
|
||
|
|
atomic_read(&obj->ps_high_thd_val));
|
||
|
|
if (err)
|
||
|
|
goto err_out;
|
||
|
|
|
||
|
|
err = stk3x1x_write_ps_low_thd(obj->client,
|
||
|
|
atomic_read(&obj->ps_low_thd_val));
|
||
|
|
if (err)
|
||
|
|
goto err_out;
|
||
|
|
|
||
|
|
APS_LOG("ALSPS_IOCTL_SET_CAL :ps_cali_result = %d\n",
|
||
|
|
ps_cali_result);
|
||
|
|
APS_LOG("ALSPS_IOCTL_SET_CAL :obj->ps_cali:%d high:%d low:%d\n",
|
||
|
|
obj->ps_cali,
|
||
|
|
atomic_read(&obj->ps_high_thd_val),
|
||
|
|
atomic_read(&obj->ps_low_thd_val));
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ALSPS_SET_PS_THRESHOLD:
|
||
|
|
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_high_thd_val,
|
||
|
|
(threshold[0] + obj->ps_cali));
|
||
|
|
atomic_set(&obj->ps_low_thd_val,
|
||
|
|
(threshold[1] + obj->ps_cali));
|
||
|
|
|
||
|
|
err = stk3x1x_write_ps_high_thd(obj->client,
|
||
|
|
atomic_read(&obj->ps_high_thd_val));
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("write high thd error: %ld\n", err);
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = stk3x1x_write_ps_low_thd(obj->client,
|
||
|
|
atomic_read(&obj->ps_low_thd_val));
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("write low thd error: %ld\n", err);
|
||
|
|
goto err_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
APS_ERR("%s not supported = 0x%04x", __func__, cmd);
|
||
|
|
err = -ENOIOCTLCMD;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
err_out:
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static const struct file_operations stk3x1x_fops = {
|
||
|
|
.open = stk3x1x_open,
|
||
|
|
.release = stk3x1x_release,
|
||
|
|
.unlocked_ioctl = stk3x1x_unlocked_ioctl,
|
||
|
|
};
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static struct miscdevice stk3x1x_device = {
|
||
|
|
.minor = MISC_DYNAMIC_MINOR,
|
||
|
|
.name = "als_ps",
|
||
|
|
.fops = &stk3x1x_fops,
|
||
|
|
};
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
|
||
|
|
#ifdef CONFIG_PM_SLEEP
|
||
|
|
static int stk3x1x_suspend(struct device *dev)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = dev_get_drvdata(dev);
|
||
|
|
int err = 0;
|
||
|
|
|
||
|
|
APS_FUN();
|
||
|
|
|
||
|
|
if (!obj) {
|
||
|
|
APS_ERR("null pointer!!\n");
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
atomic_set(&obj->als_suspend, 1);
|
||
|
|
err = stk3x1x_enable_als(obj->client, 0);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("disable als fail: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
atomic_set(&obj->ps_suspend, 1);
|
||
|
|
err = stk3x1x_enable_ps(obj->client, 0, 1);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("disable ps fail: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int stk3x1x_resume(struct device *dev)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = dev_get_drvdata(dev);
|
||
|
|
int err = 0;
|
||
|
|
|
||
|
|
APS_FUN();
|
||
|
|
|
||
|
|
if (!obj) {
|
||
|
|
APS_ERR("null pointer!!\n");
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = stk3x1x_init_client(obj->client);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("initialize client fail!!\n");
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = stk3x1x_enable_als(obj->client, 1);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("enable als fail: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
atomic_set(&obj->als_suspend, 0);
|
||
|
|
err = stk3x1x_enable_ps(obj->client, 1, 1);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("enable ps fail: %d\n", err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
atomic_set(&obj->ps_suspend, 0);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk3x1x_i2c_detect(struct i2c_client *client,
|
||
|
|
struct i2c_board_info *info)
|
||
|
|
{
|
||
|
|
strcpy(info->type, stk3x1x_DEV_NAME);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int als_open_report_data(int open)
|
||
|
|
{
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int als_enable_nodata(int en)
|
||
|
|
{
|
||
|
|
int res = 0;
|
||
|
|
#ifdef CUSTOM_KERNEL_SENSORHUB
|
||
|
|
SCP_SENSOR_HUB_DATA req;
|
||
|
|
int len;
|
||
|
|
#endif /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
|
||
|
|
APS_LOG("stk3x1x_obj als enable value = %d\n", en);
|
||
|
|
#ifdef CUSTOM_KERNEL_SENSORHUB
|
||
|
|
req.activate_req.sensorType = ID_LIGHT;
|
||
|
|
req.activate_req.action = SENSOR_HUB_ACTIVATE;
|
||
|
|
req.activate_req.enable = en;
|
||
|
|
len = sizeof(req.activate_req);
|
||
|
|
res = SCP_sensorHub_req_send(&req, &len, 1);
|
||
|
|
#else /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
|
||
|
|
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
res = stk3x1x_enable_als(stk3x1x_obj->client, en);
|
||
|
|
#endif /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
|
||
|
|
|
||
|
|
if (res) {
|
||
|
|
APS_ERR("%s is failed!!\n", __func__);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int als_set_delay(u64 ns)
|
||
|
|
{
|
||
|
|
/* TODO */
|
||
|
|
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;
|
||
|
|
|
||
|
|
#ifdef CUSTOM_KERNEL_SENSORHUB
|
||
|
|
SCP_SENSOR_HUB_DATA req;
|
||
|
|
int len;
|
||
|
|
#else
|
||
|
|
struct stk3x1x_priv *obj = NULL;
|
||
|
|
#endif /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
|
||
|
|
|
||
|
|
#ifdef CUSTOM_KERNEL_SENSORHUB
|
||
|
|
req.get_data_req.sensorType = ID_LIGHT;
|
||
|
|
req.get_data_req.action = SENSOR_HUB_GET_DATA;
|
||
|
|
len = sizeof(req.get_data_req);
|
||
|
|
err = SCP_sensorHub_req_send(&req, &len, 1);
|
||
|
|
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("SCP_sensorHub_req_send fail!\n");
|
||
|
|
} else {
|
||
|
|
*value = req.get_data_rsp.int16_Data[0];
|
||
|
|
*status = SENSOR_STATUS_ACCURACY_MEDIUM;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (atomic_read(&stk3x1x_obj->trace) & CMC_TRC_PS_DATA) {
|
||
|
|
APS_DBG("value = %d\n", *value);
|
||
|
|
/*show data*/
|
||
|
|
}
|
||
|
|
|
||
|
|
#else /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
|
||
|
|
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
obj = stk3x1x_obj;
|
||
|
|
|
||
|
|
err = stk3x1x_read_als(obj->client, &obj->als);
|
||
|
|
if (err) {
|
||
|
|
err = -1;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
*value = stk_alscode2lux(obj, obj->als);
|
||
|
|
|
||
|
|
*status = SENSOR_STATUS_ACCURACY_MEDIUM;
|
||
|
|
|
||
|
|
#endif /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
|
||
|
|
|
||
|
|
out:
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int ps_open_report_data(int open)
|
||
|
|
{
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int ps_enable_nodata(int en)
|
||
|
|
{
|
||
|
|
int res = 0;
|
||
|
|
|
||
|
|
#ifdef CUSTOM_KERNEL_SENSORHUB
|
||
|
|
SCP_SENSOR_HUB_DATA req;
|
||
|
|
int len;
|
||
|
|
#endif /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
|
||
|
|
APS_LOG("stk3x1x_obj ps enable value = %d\n", en);
|
||
|
|
#ifdef CUSTOM_KERNEL_SENSORHUB
|
||
|
|
req.activate_req.sensorType = ID_PROXIMITY;
|
||
|
|
req.activate_req.action = SENSOR_HUB_ACTIVATE;
|
||
|
|
req.activate_req.enable = en;
|
||
|
|
len = sizeof(req.activate_req);
|
||
|
|
res = SCP_sensorHub_req_send(&req, &len, 1);
|
||
|
|
#else /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
|
||
|
|
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
res = stk3x1x_enable_ps(stk3x1x_obj->client, en, 1);
|
||
|
|
|
||
|
|
if (res) {
|
||
|
|
APS_ERR("als_enable_nodata is failed!!\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
|
||
|
|
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;
|
||
|
|
#ifdef CUSTOM_KERNEL_SENSORHUB
|
||
|
|
SCP_SENSOR_HUB_DATA req;
|
||
|
|
int len;
|
||
|
|
#endif /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
|
||
|
|
#ifdef CUSTOM_KERNEL_SENSORHUB
|
||
|
|
req.get_data_req.sensorType = ID_PROXIMITY;
|
||
|
|
req.get_data_req.action = SENSOR_HUB_GET_DATA;
|
||
|
|
len = sizeof(req.get_data_req);
|
||
|
|
err = SCP_sensorHub_req_send(&req, &len, 1);
|
||
|
|
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("SCP_sensorHub_req_send fail!\n");
|
||
|
|
} else {
|
||
|
|
*value = req.get_data_rsp.int16_Data[0];
|
||
|
|
*status = SENSOR_STATUS_ACCURACY_MEDIUM;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (atomic_read(&stk3x1x_obj->trace) & CMC_TRC_PS_DATA) {
|
||
|
|
APS_DBG("value = %d\n", *value);
|
||
|
|
/*show data*/
|
||
|
|
}
|
||
|
|
|
||
|
|
#else /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
|
||
|
|
|
||
|
|
if (!stk3x1x_obj) {
|
||
|
|
APS_ERR("stk3x1x_obj is null!!\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = stk3x1x_read_ps(stk3x1x_obj->client, &stk3x1x_obj->ps);
|
||
|
|
if (err) {
|
||
|
|
err = -1;
|
||
|
|
} else {
|
||
|
|
*value = stk3x1x_get_ps_value(stk3x1x_obj, stk3x1x_obj->ps);
|
||
|
|
*status = SENSOR_STATUS_ACCURACY_MEDIUM;
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk3x1x_i2c_probe(struct i2c_client *client,
|
||
|
|
const struct i2c_device_id *id)
|
||
|
|
{
|
||
|
|
struct stk3x1x_priv *obj = NULL;
|
||
|
|
int err = 0;
|
||
|
|
struct als_control_path als_ctl = { 0 };
|
||
|
|
struct als_data_path als_data = { 0 };
|
||
|
|
struct ps_control_path ps_ctl = { 0 };
|
||
|
|
struct ps_data_path ps_data = { 0 };
|
||
|
|
|
||
|
|
APS_LOG("%s: driver version: %s\n", __func__, DRIVER_VERSION);
|
||
|
|
err = get_alsps_dts_func(client->dev.of_node, hw);
|
||
|
|
if (err < 0) {
|
||
|
|
APS_ERR("get customization info from dts failed\n");
|
||
|
|
goto exit;
|
||
|
|
}
|
||
|
|
|
||
|
|
obj = kzalloc(sizeof(*obj), GFP_KERNEL);
|
||
|
|
if (!obj) {
|
||
|
|
err = -ENOMEM;
|
||
|
|
goto exit;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(obj, 0, sizeof(*obj));
|
||
|
|
stk3x1x_obj = obj;
|
||
|
|
obj->hw = hw;
|
||
|
|
stk3x1x_get_addr(obj->hw, &obj->addr);
|
||
|
|
INIT_DELAYED_WORK(&obj->eint_work, stk3x1x_eint_work);
|
||
|
|
client->addr = *hw->i2c_addr;
|
||
|
|
obj->client = client;
|
||
|
|
i2c_set_clientdata(client, obj);
|
||
|
|
atomic_set(&obj->als_debounce, 200);
|
||
|
|
atomic_set(&obj->als_deb_on, 0);
|
||
|
|
atomic_set(&obj->als_deb_end, 0);
|
||
|
|
atomic_set(&obj->ps_debounce, 10);
|
||
|
|
atomic_set(&obj->ps_deb_on, 0);
|
||
|
|
atomic_set(&obj->ps_deb_end, 0);
|
||
|
|
atomic_set(&obj->ps_mask, 0);
|
||
|
|
atomic_set(&obj->trace, 0x00);
|
||
|
|
atomic_set(&obj->als_suspend, 0);
|
||
|
|
atomic_set(&obj->init_done, 0);
|
||
|
|
obj->irq_node = client->dev.of_node;
|
||
|
|
atomic_set(&obj->state_val, 0);
|
||
|
|
atomic_set(&obj->psctrl_val, 0x30); /*0x31*/
|
||
|
|
atomic_set(&obj->alsctrl_val, 0x39);
|
||
|
|
obj->ledctrl_val = 0xFF;
|
||
|
|
obj->wait_val = 0xF;
|
||
|
|
obj->int_val = 0;
|
||
|
|
obj->first_boot = true;
|
||
|
|
obj->als_correct_factor = 1000;
|
||
|
|
|
||
|
|
atomic_set(&obj->ps_high_thd_val, obj->hw->ps_threshold_high);
|
||
|
|
atomic_set(&obj->ps_low_thd_val, obj->hw->ps_threshold_low);
|
||
|
|
atomic_set(&obj->recv_reg, 0);
|
||
|
|
|
||
|
|
if (obj->hw->polling_mode_ps == 0)
|
||
|
|
APS_LOG("%s: enable PS interrupt\n", __func__);
|
||
|
|
|
||
|
|
obj->int_val |= STK_INT_PS_MODE1;
|
||
|
|
|
||
|
|
if (obj->hw->polling_mode_als == 0) {
|
||
|
|
obj->int_val |= STK_INT_ALS;
|
||
|
|
APS_LOG("%s: enable ALS interrupt\n", __func__);
|
||
|
|
}
|
||
|
|
|
||
|
|
obj->enable = 0;
|
||
|
|
obj->pending_intr = 0;
|
||
|
|
atomic_set(&obj->i2c_retry, 3);
|
||
|
|
|
||
|
|
if (atomic_read(&obj->state_val) & STK_STATE_EN_ALS_MASK)
|
||
|
|
set_bit(STK_BIT_ALS, &obj->enable);
|
||
|
|
|
||
|
|
if (atomic_read(&obj->state_val) & STK_STATE_EN_PS_MASK)
|
||
|
|
set_bit(STK_BIT_PS, &obj->enable);
|
||
|
|
|
||
|
|
stk3x1x_i2c_client = client;
|
||
|
|
|
||
|
|
err = stk3x1x_init_client(client);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("stk3x1x init client failed\n");
|
||
|
|
goto exit_init_failed;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = misc_register(&stk3x1x_device);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("stk3x1x_device register failed\n");
|
||
|
|
goto exit_misc_device_register_failed;
|
||
|
|
}
|
||
|
|
|
||
|
|
err =
|
||
|
|
stk3x1x_create_attr(&(stk3x1x_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_use_common_factory = false;
|
||
|
|
|
||
|
|
if (obj->hw->polling_mode_als == 1)
|
||
|
|
als_ctl.is_polling_mode = true;
|
||
|
|
else
|
||
|
|
als_ctl.is_polling_mode = false;
|
||
|
|
|
||
|
|
#ifdef CUSTOM_KERNEL_SENSORHUB
|
||
|
|
als_ctl.is_support_batch = obj->hw->is_batch_supported_als;
|
||
|
|
#else
|
||
|
|
als_ctl.is_support_batch = false;
|
||
|
|
#endif
|
||
|
|
err = als_register_control_path(&als_ctl);
|
||
|
|
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("als_control 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("als_data register fail = %d\n", err);
|
||
|
|
goto exit_sensor_obj_attach_fail;
|
||
|
|
}
|
||
|
|
|
||
|
|
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.ps_calibration = NULL;
|
||
|
|
ps_ctl.ps_calibration = NULL;
|
||
|
|
ps_ctl.is_use_common_factory = false;
|
||
|
|
|
||
|
|
if (obj->hw->polling_mode_ps == 1) {
|
||
|
|
ps_ctl.is_polling_mode = true;
|
||
|
|
ps_ctl.is_report_input_direct = false;
|
||
|
|
wakeup_source_init(&mps_lock, "ps wakelock");
|
||
|
|
} else {
|
||
|
|
ps_ctl.is_polling_mode = false;
|
||
|
|
ps_ctl.is_report_input_direct = true;
|
||
|
|
}
|
||
|
|
#ifdef CUSTOM_KERNEL_SENSORHUB
|
||
|
|
ps_ctl.is_support_batch = obj->hw->is_batch_supported_ps;
|
||
|
|
#else
|
||
|
|
ps_ctl.is_support_batch = false;
|
||
|
|
#endif
|
||
|
|
err = ps_register_control_path(&ps_ctl);
|
||
|
|
if (err) {
|
||
|
|
APS_ERR("ps_control 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("ps_data register fail = %d\n", err);
|
||
|
|
goto exit_sensor_obj_attach_fail;
|
||
|
|
}
|
||
|
|
|
||
|
|
stk3x1x_init_flag = 0;
|
||
|
|
APS_LOG("%s: state_val=0x%x, psctrl_val=0x%x, alsctrl_val=0x%x\n",
|
||
|
|
__func__, atomic_read(&obj->state_val),
|
||
|
|
atomic_read(&obj->psctrl_val), atomic_read(&obj->alsctrl_val));
|
||
|
|
APS_LOG("ledctrl_val=0x%x, wait_val=0x%x, int_val=0x%x\n",
|
||
|
|
obj->ledctrl_val, obj->wait_val, obj->int_val);
|
||
|
|
/*
|
||
|
|
* Since alsps sensor driver in AP,
|
||
|
|
* it should register information to sensorlist.
|
||
|
|
*/
|
||
|
|
strncpy(alsps_devinfo.name, STK3x1x_DEV_NAME, sizeof(STK3x1x_DEV_NAME));
|
||
|
|
sensorlist_register_deviceinfo(ID_PRESSURE, &alsps_devinfo);
|
||
|
|
APS_LOG("%s: OK\n", __func__);
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
exit_sensor_obj_attach_fail:
|
||
|
|
exit_create_attr_failed:
|
||
|
|
misc_deregister(&stk3x1x_device);
|
||
|
|
exit_misc_device_register_failed:
|
||
|
|
exit_init_failed:
|
||
|
|
kfree(obj);
|
||
|
|
exit:
|
||
|
|
stk3x1x_i2c_client = NULL;
|
||
|
|
#ifdef MT6516
|
||
|
|
MT6516_EINTIRQMask(CUST_EINT_ALS_NUM); /*mask interrupt if fail*/
|
||
|
|
#endif
|
||
|
|
stk3x1x_init_flag = -1;
|
||
|
|
APS_ERR("%s: err = %d\n", __func__, err);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int stk3x1x_i2c_remove(struct i2c_client *client)
|
||
|
|
{
|
||
|
|
int err = 0;
|
||
|
|
|
||
|
|
err =
|
||
|
|
stk3x1x_delete_attr(&(stk3x1x_init_info.platform_diver_addr->driver));
|
||
|
|
|
||
|
|
if (err)
|
||
|
|
APS_ERR("stk3x1x_delete_attr fail: %d\n", err);
|
||
|
|
|
||
|
|
misc_deregister(&stk3x1x_device);
|
||
|
|
|
||
|
|
stk3x1x_i2c_client = NULL;
|
||
|
|
i2c_unregister_device(client);
|
||
|
|
kfree(i2c_get_clientdata(client));
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int stk3x1x_local_uninit(void)
|
||
|
|
{
|
||
|
|
APS_FUN();
|
||
|
|
|
||
|
|
i2c_del_driver(&stk3x1x_i2c_driver);
|
||
|
|
stk3x1x_i2c_client = NULL;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
|
||
|
|
static int stk3x1x_local_init(void)
|
||
|
|
{
|
||
|
|
struct stk3x1x_i2c_addr addr;
|
||
|
|
|
||
|
|
APS_FUN();
|
||
|
|
|
||
|
|
stk3x1x_get_addr(hw, &addr);
|
||
|
|
|
||
|
|
if (i2c_add_driver(&stk3x1x_i2c_driver)) {
|
||
|
|
APS_ERR("add driver error\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (-1 == stk3x1x_init_flag) {
|
||
|
|
APS_ERR("%s fail with stk3x1x_init_flag=%d\n",
|
||
|
|
__func__, stk3x1x_init_flag);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* wait for idme for calibration ready open it */
|
||
|
|
als_cal = 65536; //idme_get_alscal_value();
|
||
|
|
|
||
|
|
if (als_cal > 0 && als_cal <= 65535)
|
||
|
|
stk3x1x_obj->als_transmittance = als_cal;
|
||
|
|
else
|
||
|
|
stk3x1x_obj->als_transmittance = ALS_DEFAULT_TRANSMITTANCE;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static int __init stk3x1x_init(void)
|
||
|
|
{
|
||
|
|
alsps_driver_add(&stk3x1x_init_info);
|
||
|
|
/* hwmsen_alsps_add(&stk3x1x_init_info);*/
|
||
|
|
pr_info("%s done\n", __func__);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
static void __exit stk3x1x_exit(void)
|
||
|
|
{
|
||
|
|
APS_FUN();
|
||
|
|
}
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
module_init(stk3x1x_init);
|
||
|
|
module_exit(stk3x1x_exit);
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
MODULE_AUTHOR("MingHsien Hsieh");
|
||
|
|
MODULE_DESCRIPTION("SensorTek stk3x1x proximity and light sensor driver");
|
||
|
|
MODULE_LICENSE("GPL");
|