1166 lines
31 KiB
C
1166 lines
31 KiB
C
/*
|
|
* Copyright by ams AG
|
|
* All rights are reserved.
|
|
*
|
|
* IMPORTANT - PLEASE READ CAREFULLY BEFORE COPYING, INSTALLING OR USING
|
|
* THE SOFTWARE.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED FOR USE ONLY IN CONJUNCTION WITH AMS PRODUCTS.
|
|
* USE OF THE SOFTWARE IN CONJUNCTION WITH NON-AMS-PRODUCTS IS EXPLICITLY
|
|
* EXCLUDED.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/unistd.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/input.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/module.h>
|
|
#include <linux/device.h>
|
|
#include <linux/string.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/freezer.h>
|
|
#include <linux/gpio/consumer.h>
|
|
#include <linux/sensor/sensors_core.h>
|
|
#include <linux/kfifo.h>
|
|
|
|
#include "ams_dax_reg.h"
|
|
#include "ams_dax.h"
|
|
#include "ams_i2c.h"
|
|
|
|
#ifdef CONFIG_OF
|
|
#include <linux/of_device.h>
|
|
#endif
|
|
|
|
#define VENDOR_NAME "AMS"
|
|
#define CHIP_NAME "TCS3701"
|
|
#define MODULE_NAME "light_sensor"
|
|
|
|
#define REL_DELTA REL_DIAL
|
|
#define REL_2ND_MIN REL_HWHEEL
|
|
#define REL_2ND_MAX REL_WHEEL
|
|
#define REL_LUX REL_MISC
|
|
|
|
#define LIGHT_LOG_TIME 40
|
|
|
|
#define HIGH_BRIGHTNESS 78
|
|
#define DEFAULT_JAMES_DELAY_MS 20
|
|
#define DEFAULT_RGBC_DELAY_MS 100
|
|
#define FIFO_OV 0x80
|
|
#define DEFAULT_BRIGHTNESS_LEVEL 110
|
|
#define ASCII_TO_DEC(x) (x - 48)
|
|
#define GAIN_FACTOR (512)
|
|
#define CMD_CAM_LUX_DISABLE 2
|
|
#define EVENT_CAM_LUX_DISABLE -2
|
|
#define EVENT_INIT_MOVING_AVERAGE -3
|
|
#define TCS3701_CHIP_ID 0x18
|
|
|
|
#define STANDARD_DEVIATION_HIGH_THRESHOLD 10000
|
|
#define STANDARD_DEVIATION_LOW_THRESHOLD 8100
|
|
|
|
u8 smux_data[20] = {
|
|
0x12, 0x10, 0x21, 0x21,
|
|
0x11, 0x20, 0x12, 0x22,
|
|
0x01, 0x21, 0x20, 0x12,
|
|
0x12, 0x22, 0x21, 0x12,
|
|
0x11, 0x02, 0x00, 0x76
|
|
};
|
|
|
|
u8 smux_default_data[20] = {
|
|
0x14, 0x25, 0x23, 0x41,
|
|
0x33, 0x12, 0x14, 0x24,
|
|
0x53, 0x23, 0x15, 0x14,
|
|
0x32, 0x44, 0x21, 0x23,
|
|
0x13, 0x54, 0x00, 0x76
|
|
};
|
|
|
|
const u16 alsGain_conversion[] = {
|
|
1,
|
|
1,
|
|
2,
|
|
4,
|
|
8,
|
|
16,
|
|
32,
|
|
64,
|
|
128,
|
|
256,
|
|
512,
|
|
1024,
|
|
2048
|
|
};
|
|
|
|
static u16 ams_alsTimeUsToReg(u32 x)
|
|
{
|
|
u16 regValue;
|
|
|
|
regValue = (x / 2780) - 1;
|
|
return regValue;
|
|
}
|
|
|
|
static u8 ams_alsGainToReg(u32 x)
|
|
{
|
|
int i;
|
|
|
|
for (i = sizeof(alsGain_conversion)/sizeof(u16)-1; i != 0; i--)
|
|
if (x >= alsGain_conversion[i])
|
|
break;
|
|
|
|
return (i << 0);
|
|
}
|
|
|
|
static ssize_t ams_light_name_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_NAME);
|
|
}
|
|
|
|
static ssize_t ams_light_reg_data_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct ams_chip *chip = dev_get_drvdata(dev);
|
|
u8 reg = 0;
|
|
int offset = 0;
|
|
u8 val = 0;
|
|
|
|
for (reg = 0x00; reg <= 0xFF; reg++) {
|
|
val = i2c_smbus_read_byte_data(chip->client, reg);
|
|
SENSOR_INFO("Read Reg: 0x%2x Value: 0x%2x\n", reg, val);
|
|
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
|
"Reg: 0x%2x Value: 0x%2x\n", reg, val);
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
static ssize_t ams_light_reg_data_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
struct ams_chip *chip = dev_get_drvdata(dev);
|
|
int reg, val, ret;
|
|
|
|
if (sscanf(buf, "%2x,%4x", ®, &val) != 2) {
|
|
SENSOR_ERR("invalid value\n");
|
|
return count;
|
|
}
|
|
|
|
ret = ams_i2c_write(chip->client, chip->shadow, reg, val);
|
|
if (!ret)
|
|
SENSOR_INFO("Register(0x%2x) data(0x%4x)\n", reg, val);
|
|
else
|
|
SENSOR_ERR("failed %d\n", ret);
|
|
|
|
return count;
|
|
}
|
|
|
|
static void ams_get_itime(struct ams_chip *chip)
|
|
{
|
|
if (chip->brightness_level <= BRIGHTNESS_CODE_LEVEL_1)
|
|
chip->itime = 2060; /* ((4.167/sample) -0.163)*1000 */
|
|
else if (chip->brightness_level <= BRIGHTNESS_CODE_LEVEL_2)
|
|
chip->itime = 1320;
|
|
else if (chip->brightness_level <= BRIGHTNESS_CODE_LEVEL_3)
|
|
chip->itime = 925;
|
|
else if (chip->brightness_level <= BRIGHTNESS_CODE_LEVEL_MAX)
|
|
chip->itime = 690;
|
|
else
|
|
chip->itime = 690;
|
|
}
|
|
|
|
static void ams_change_fifo_sample(struct ams_chip *chip)
|
|
{
|
|
int alg_current_itime;
|
|
|
|
if (chip->cur_algo_mode == ALS_ALGO_HIGH)
|
|
return;
|
|
|
|
alg_current_itime = chip->itime;
|
|
ams_get_itime(chip);
|
|
|
|
if (alg_current_itime != chip->itime) {
|
|
cancel_delayed_work_sync(&chip->main_work);
|
|
SENSOR_INFO("change itime : %d\n", chip->itime);
|
|
ams_i2c_write(chip->client, chip->shadow, AMS_REG_ENABLE, 0x01);
|
|
i2c_smbus_write_word_data(chip->client, AMS_REG_ASTEPL,
|
|
ams_alsTimeUsToReg(chip->itime * 1000));
|
|
ams_i2c_write(chip->client, chip->shadow, AMS_REG_ENABLE, 0x03);
|
|
ams_i2c_set_field(chip->client, chip->shadow,
|
|
AMS_REG_CONTROL, 0x1, 0x1, 0x1);
|
|
chip->fifoCnt = 0;
|
|
schedule_delayed_work(&chip->main_work,
|
|
nsecs_to_jiffies(atomic_read(&chip->delay)));
|
|
}
|
|
}
|
|
|
|
static void ams_change_algo_mode(struct ams_chip *chip, int algo_mode)
|
|
{
|
|
int alg_current_mode = chip->cur_algo_mode;
|
|
|
|
if (algo_mode == ALS_ALGO_HIGH || chip->brightness_level == 0) {
|
|
chip->itime = 16000;
|
|
chip->cur_algo_mode = ALS_ALGO_HIGH;
|
|
} else {
|
|
chip->cur_algo_mode = ALS_ALGO_MID;
|
|
}
|
|
|
|
if (alg_current_mode != chip->cur_algo_mode) {
|
|
cancel_delayed_work_sync(&chip->main_work);
|
|
|
|
switch (chip->cur_algo_mode) {
|
|
case ALS_ALGO_MID:
|
|
SENSOR_INFO("START MID ALGORITHM\n");
|
|
ams_i2c_write(chip->client,
|
|
chip->shadow, AMS_REG_ENABLE, 0x01);
|
|
/* SMUX write command */
|
|
ams_i2c_write(chip->client,
|
|
chip->shadow, AMS_REG_CFG6, 0x10);
|
|
/* Send smux remap sequence */
|
|
ams_i2c_reg_blk_write(chip->client,
|
|
AMS_REG_RAM_START, smux_data, 20);
|
|
/* execute the smux command */
|
|
ams_i2c_write(chip->client,
|
|
chip->shadow, AMS_REG_ENABLE, 0x11);
|
|
ams_i2c_write(chip->client,
|
|
chip->shadow, AMS_REG_CFG6, 0x00);
|
|
ams_i2c_write(chip->client,
|
|
chip->shadow, AMS_REG_ALS_CHANNEL_CTRL, 0x3c);
|
|
ams_i2c_write(chip->client,
|
|
chip->shadow, AMS_REG_ATIME, 0x00);
|
|
i2c_smbus_write_byte_data(chip->client,
|
|
AMS_REG_WTIME, 0x00);
|
|
chip->again = 1024;
|
|
i2c_smbus_write_byte_data(chip->client,
|
|
AMS_REG_CFG1, ams_alsGainToReg(chip->again));
|
|
ams_get_itime(chip);
|
|
i2c_smbus_write_word_data(chip->client, AMS_REG_ASTEPL,
|
|
ams_alsTimeUsToReg(chip->itime * 1000));
|
|
i2c_smbus_write_byte_data(chip->client,
|
|
AMS_REG_FIFO_MAP, 0x06);
|
|
i2c_smbus_write_byte_data(chip->client,
|
|
AMS_REG_CFG8, 0x18);
|
|
ams_i2c_set_field(chip->client,
|
|
chip->shadow, AMS_REG_PCFG1, 0x3, 0x1, 0x1);
|
|
ams_i2c_set_field(chip->client,
|
|
chip->shadow, AMS_REG_CONTROL, 0x1, 0x1, 0x1);
|
|
chip->fifoCnt = 0;
|
|
ams_i2c_write(chip->client,
|
|
chip->shadow, AMS_REG_ENABLE, 0x03);
|
|
atomic_set(&chip->delay,
|
|
DEFAULT_JAMES_DELAY_MS * NSEC_PER_MSEC);
|
|
break;
|
|
case ALS_ALGO_HIGH:
|
|
SENSOR_INFO("START HIGH ALGORITHM\n");
|
|
ams_i2c_write(chip->client,
|
|
chip->shadow, AMS_REG_ENABLE, 0x01);
|
|
/* SMUX write command */
|
|
ams_i2c_write(chip->client,
|
|
chip->shadow, AMS_REG_CFG6, 0x10);
|
|
/* Send smux remap sequence */
|
|
ams_i2c_reg_blk_write(chip->client,
|
|
AMS_REG_RAM_START, smux_default_data, 20);
|
|
/* execute the smux command */
|
|
ams_i2c_write(chip->client,
|
|
chip->shadow, AMS_REG_ENABLE, 0x11);
|
|
ams_i2c_write(chip->client,
|
|
chip->shadow, AMS_REG_CFG6, 0x00);
|
|
ams_i2c_write(chip->client,
|
|
chip->shadow, AMS_REG_ALS_CHANNEL_CTRL, 0x00);
|
|
ams_i2c_write(chip->client,
|
|
chip->shadow, AMS_REG_ATIME, 0x00);
|
|
i2c_smbus_write_byte_data(chip->client,
|
|
AMS_REG_WTIME, 0x00);
|
|
i2c_smbus_write_byte_data(chip->client,
|
|
AMS_REG_FIFO_MAP, 0x00);
|
|
ams_i2c_set_field(chip->client,
|
|
chip->shadow, AMS_REG_CFG8, 0x2, 0x1, 0x1);
|
|
|
|
/* 16msec integration time for HIGH BRIGHTNESS ALG */
|
|
i2c_smbus_write_word_data(chip->client, AMS_REG_ASTEPL,
|
|
ams_alsTimeUsToReg(chip->itime * 1000));
|
|
ams_i2c_write(chip->client,
|
|
chip->shadow, AMS_REG_ENABLE, 0x03);
|
|
atomic_set(&chip->delay,
|
|
DEFAULT_RGBC_DELAY_MS * NSEC_PER_MSEC);
|
|
chip->previousGain = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
schedule_delayed_work(&chip->main_work,
|
|
nsecs_to_jiffies(atomic_read(&chip->delay)));
|
|
}
|
|
}
|
|
|
|
static void ams_report_events(struct ams_chip *chip)
|
|
{
|
|
if (chip->lux < 0)
|
|
chip->lux = 0;
|
|
|
|
input_report_rel(chip->input_dev, REL_LUX, chip->lux + 1);
|
|
input_sync(chip->input_dev);
|
|
}
|
|
|
|
static void ams_report_high_brightness_events(struct ams_chip *chip)
|
|
{
|
|
input_report_rel(chip->input_dev, REL_DELTA, chip->ch0_delta + 1);
|
|
input_report_rel(chip->input_dev, REL_2ND_MIN, chip->ch0_2nd_min + 1);
|
|
input_report_rel(chip->input_dev, REL_2ND_MAX, chip->ch0_2nd_max + 1);
|
|
input_sync(chip->input_dev);
|
|
}
|
|
|
|
static bool ams_highBrightness_alsCalcLux(struct ams_chip *chip,
|
|
u32 (*min_buf)[JAMES_FIFO_MAX_CNT],
|
|
u32 (*max_buf)[JAMES_FIFO_MAX_CNT])
|
|
{
|
|
int i;
|
|
u32 ch0_min = 0xfffff;
|
|
u32 ch0_max = 0;
|
|
|
|
chip->ch0_2nd_max = 0;
|
|
chip->ch0_2nd_min = 0xfffff;
|
|
|
|
for (i = 0; i < JAMES_FIFO_MAX_CNT; i++) {
|
|
if (max_buf[CH0][i] > ch0_max) {
|
|
chip->ch0_2nd_max = ch0_max;
|
|
ch0_max = max_buf[CH0][i];
|
|
} else if (max_buf[CH0][i] > chip->ch0_2nd_max) {
|
|
chip->ch0_2nd_max = max_buf[CH0][i];
|
|
}
|
|
|
|
if (min_buf[CH0][i] < ch0_min) {
|
|
chip->ch0_2nd_min = ch0_min;
|
|
ch0_min = min_buf[CH0][i];
|
|
} else if (min_buf[CH0][i] < chip->ch0_2nd_min) {
|
|
chip->ch0_2nd_min = min_buf[CH0][i];
|
|
}
|
|
}
|
|
|
|
chip->ch0_2nd_max = chip->ch0_2nd_max * GAIN_FACTOR / chip->again;
|
|
chip->ch0_2nd_min = chip->ch0_2nd_min * GAIN_FACTOR / chip->again;
|
|
|
|
chip->ch0_delta = (4 * chip->ch0_2nd_max) - (4 * chip->ch0_2nd_min);
|
|
|
|
if (!chip->ch0_delta)
|
|
chip->ch0_delta = 1;
|
|
|
|
if (chip->count_log_time >= LIGHT_LOG_TIME) {
|
|
SENSOR_INFO("[MAX-MIN] max: %d min: %d Br: %d\n",
|
|
chip->ch0_2nd_max, chip->ch0_2nd_min,
|
|
chip->brightness_level);
|
|
chip->count_log_time = 0;
|
|
} else {
|
|
chip->count_log_time++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool ams_alsCalcLux(struct ams_chip *chip,
|
|
u32 (*buf)[JAMES_FIFO_MAX_CNT])
|
|
{
|
|
int i;
|
|
u32 ch0_min = 0xffff, ch1_min = 0xffff;
|
|
int64_t ch0_sum = 0, ch1_sum = 0;
|
|
|
|
for (i = 0; i < JAMES_FIFO_MAX_CNT; i++) {
|
|
ch0_sum += buf[CH0][i];
|
|
if (ch0_min > buf[CH0][i])
|
|
ch0_min = buf[CH0][i];
|
|
ch1_sum += buf[CH1][i];
|
|
if (ch1_min > buf[CH1][i])
|
|
ch1_min = buf[CH1][i];
|
|
}
|
|
|
|
ch0_sum = ch0_sum - ch0_min;
|
|
ch1_sum = ch1_sum - ch1_min;
|
|
|
|
if (((ch0_sum == ch1_sum) && (ch0_sum != 0)) || (ch0_sum < ch1_sum)) {
|
|
chip->lux = 0;
|
|
} else {
|
|
ch0_sum = ch0_sum * AGAIN_FACTOR / chip->again;
|
|
ch1_sum = ch1_sum * AGAIN_FACTOR / chip->again;
|
|
|
|
chip->lux = JAMES_DGF * (CH0_COEF * ch0_sum - CH1_COEF * ch1_sum) / 1000;
|
|
chip->lux = chip->lux * ATIME_FACTOR / (chip->itime * (JAMES_FIFO_MAX_CNT - 1));
|
|
}
|
|
|
|
if (chip->count_log_time >= LIGHT_LOG_TIME) {
|
|
SENSOR_INFO("[JAMES] lux: %d, Br: %d\n",
|
|
chip->lux, chip->brightness_level);
|
|
chip->count_log_time = 0;
|
|
} else {
|
|
chip->count_log_time++;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool ams_acLight_alsCalcLux(struct ams_chip *chip,
|
|
u32 (*buf)[JAMES_FIFO_MAX_CNT])
|
|
{
|
|
int i;
|
|
u32 ch0_min = 0xffff, ch1_min = 0xffff;
|
|
int64_t ch0_sum = 0, ch1_sum = 0;
|
|
|
|
for (i = 0; i < JAMES_FIFO_MAX_CNT; i++) {
|
|
ch0_sum += buf[CH0][i];
|
|
if (ch0_min > buf[CH0][i])
|
|
ch0_min = buf[CH0][i];
|
|
ch1_sum += buf[CH1][i];
|
|
if (ch1_min > buf[CH1][i])
|
|
ch1_min = buf[CH1][i];
|
|
}
|
|
|
|
ch0_sum = ch0_sum - ch0_min;
|
|
ch1_sum = ch1_sum - ch1_min;
|
|
|
|
if (ch0_sum < ch1_sum) {
|
|
chip->lux = 0;
|
|
} else {
|
|
ch0_sum = ch0_sum * AGAIN_FACTOR / chip->again;
|
|
ch1_sum = ch1_sum * AGAIN_FACTOR / chip->again;
|
|
|
|
chip->lux = JAMES_DGF * (CH0_COEF * ch0_sum) / 1000 * 3 / 10;
|
|
chip->lux = chip->lux * ATIME_FACTOR / (chip->itime * (JAMES_FIFO_MAX_CNT - 1));
|
|
}
|
|
|
|
if (chip->count_log_time >= LIGHT_LOG_TIME) {
|
|
SENSOR_INFO("[AC-MODE] lux: %d, Br: %d\n",
|
|
chip->lux, chip->brightness_level);
|
|
chip->count_log_time = 0;
|
|
} else {
|
|
chip->count_log_time++;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void ams_process_mid(struct ams_chip *chip)
|
|
{
|
|
int j = 0;
|
|
int i = 0;
|
|
bool isHbMode;
|
|
u16 fifo_size;
|
|
uint8_t fifo_lvl;
|
|
uint8_t status6;
|
|
u16 sample_cnt = 0;
|
|
u16 quotient = 0, remainder = 0;
|
|
uint64_t sum[CH_MAX_CNT] = {0, 0};
|
|
u32 tmp;
|
|
uint64_t temp;
|
|
u32 recommendedGain;
|
|
u32 max_count;
|
|
u32 adcObjective;
|
|
u32 stddev_ch0, stddev_ch1;
|
|
|
|
ams_i2c_read(chip->client, AMS_REG_STATUS6, &status6);
|
|
ams_i2c_read(chip->client, AMS_REG_FIFO_LVL, &fifo_lvl);
|
|
|
|
if (fifo_lvl < 1) {
|
|
SENSOR_INFO("FIFO EMPTY\n");
|
|
return;
|
|
}
|
|
|
|
if (status6 & FIFO_OV) {
|
|
ams_i2c_set_field(chip->client, chip->shadow,
|
|
AMS_REG_CONTROL, 0x1, 0x1, 0x1);
|
|
SENSOR_INFO("FIFO OVERFLOW\n");
|
|
return;
|
|
}
|
|
|
|
fifo_size = (u16)fifo_lvl * 2;
|
|
sample_cnt = fifo_size / 4; /* ch0 2byte + ch1 2byte = 4byte */
|
|
|
|
if (sample_cnt < 1) {
|
|
SENSOR_INFO("FIFO Not enough for ALG\n");
|
|
return;
|
|
}
|
|
|
|
quotient = fifo_size / 32;
|
|
remainder = fifo_size % 32;
|
|
|
|
memset(&chip->fifodata[0], 0x00, sizeof(uint8_t) * 256);
|
|
if (quotient == 0) { /* fifo size is less than 32 , reading remainder */
|
|
i2c_smbus_read_i2c_block_data(chip->client,
|
|
AMS_REG_FDATAL, remainder, (u8 *)&chip->fifodata[0]);
|
|
} else {
|
|
for (i = 0; i < quotient; i++)
|
|
i2c_smbus_read_i2c_block_data(chip->client,
|
|
AMS_REG_FDATAL, 32,
|
|
(u8 *)&chip->fifodata[i * 32]);
|
|
|
|
if (remainder != 0)
|
|
i2c_smbus_read_i2c_block_data(chip->client,
|
|
AMS_REG_FDATAL, remainder,
|
|
(u8 *)&chip->fifodata[i * 32]);
|
|
}
|
|
|
|
i = i * 32 + remainder;
|
|
|
|
chip->chMinBuf[CH0][chip->fifoCnt] = 0xffff;
|
|
chip->chMinBuf[CH1][chip->fifoCnt] = 0xffff;
|
|
chip->chMaxBuf[CH0][chip->fifoCnt] = 0x0;
|
|
chip->chMaxBuf[CH1][chip->fifoCnt] = 0x0;
|
|
|
|
for (j = 0; j < sample_cnt; j++) {
|
|
chip->data_buf[CH0][j] = (u16)((chip->fifodata[j * 4] << 0) | (chip->fifodata[j * 4 + 1] << 8));
|
|
chip->data_buf[CH1][j] = (u16)((chip->fifodata[j * 4 + 2] << 0) | (chip->fifodata[j * 4 + 2 + 1] << 8));
|
|
if (chip->data_buf[CH0][j] < chip->data_buf[CH1][j]) {
|
|
tmp = chip->data_buf[CH0][j];
|
|
chip->data_buf[CH0][j] = chip->data_buf[CH1][j];
|
|
chip->data_buf[CH1][j] = tmp;
|
|
}
|
|
|
|
if (chip->chMinBuf[CH0][chip->fifoCnt] > chip->data_buf[CH0][j])
|
|
chip->chMinBuf[CH0][chip->fifoCnt] = chip->data_buf[CH0][j];
|
|
if (chip->chMinBuf[CH1][chip->fifoCnt] > chip->data_buf[CH1][j])
|
|
chip->chMinBuf[CH1][chip->fifoCnt] = chip->data_buf[CH1][j];
|
|
|
|
if (chip->chMaxBuf[CH0][chip->fifoCnt] < chip->data_buf[CH0][j])
|
|
chip->chMaxBuf[CH0][chip->fifoCnt] = chip->data_buf[CH0][j];
|
|
if (chip->chMaxBuf[CH1][chip->fifoCnt] < chip->data_buf[CH1][j])
|
|
chip->chMaxBuf[CH1][chip->fifoCnt] = chip->data_buf[CH1][j];
|
|
|
|
sum[CH0] += chip->data_buf[CH0][j];
|
|
sum[CH1] += chip->data_buf[CH1][j];
|
|
#if 0
|
|
SENSOR_INFO("[CH_RAW] %u, %u\n", chip->data_buf[0][j], chip->data_buf[1][j]);
|
|
#endif
|
|
}
|
|
|
|
chip->chAvg[CH0][chip->fifoCnt] = sum[CH0] / sample_cnt;
|
|
chip->chAvg[CH1][chip->fifoCnt] = sum[CH1] / sample_cnt;
|
|
|
|
sum[CH0] = 0;
|
|
sum[CH1] = 0;
|
|
|
|
for (j = 0; j < sample_cnt; j++) {
|
|
if (chip->chAvg[CH0][chip->fifoCnt] > chip->data_buf[CH0][j])
|
|
tmp = (int32_t)chip->chAvg[CH0][chip->fifoCnt] - chip->data_buf[CH0][j];
|
|
else
|
|
tmp = (int32_t)chip->data_buf[CH0][j] - chip->chAvg[CH0][chip->fifoCnt];
|
|
sum[CH0] += (tmp * tmp);
|
|
|
|
if (chip->chAvg[CH1][chip->fifoCnt] > chip->data_buf[CH1][j])
|
|
tmp = (int32_t)chip->chAvg[CH1][chip->fifoCnt] - chip->data_buf[CH1][j];
|
|
else
|
|
tmp = (int32_t)chip->data_buf[CH1][j] - chip->chAvg[CH1][chip->fifoCnt];
|
|
sum[CH1] += (tmp * tmp);
|
|
}
|
|
sum[CH0] = sum[CH0] / sample_cnt;
|
|
sum[CH1] = sum[CH1] / sample_cnt;
|
|
|
|
if (!((chip->itime == 0) || (chip->again == 0)
|
|
|| (chip->again > 2048))) {
|
|
tmp = (2048 * 1000) / chip->again;
|
|
stddev_ch0 = (u32)(sum[CH0] * tmp * tmp / (chip->itime * chip->itime));
|
|
stddev_ch1 = (u32)(sum[CH1] * tmp * tmp / (chip->itime * chip->itime));
|
|
} else {
|
|
stddev_ch0 = 0;
|
|
stddev_ch1 = 0;
|
|
}
|
|
|
|
chip->fifoCnt++;
|
|
|
|
max_count = (1000 * chip->itime) / 2780;
|
|
adcObjective = max_count * 128;
|
|
adcObjective /= 160; /* about 80% (128 / 160) */
|
|
temp = (uint64_t)adcObjective * 2048;
|
|
if (chip->chMaxBuf[CH0][chip->fifoCnt - 1] != 0)
|
|
temp /= chip->chMaxBuf[CH0][chip->fifoCnt - 1];
|
|
temp *= chip->again;
|
|
temp /= 2048;
|
|
recommendedGain = temp & 0xffffffff;
|
|
recommendedGain = ams_alsGainToReg(recommendedGain);
|
|
recommendedGain = alsGain_conversion[recommendedGain];
|
|
|
|
i2c_smbus_write_byte_data(chip->client, AMS_REG_FIFO_MAP, 0x06);
|
|
if (recommendedGain != chip->again) {
|
|
SENSOR_INFO("gain chg to: %u, (ch0: %u)\n",
|
|
recommendedGain,
|
|
chip->chMaxBuf[CH0][chip->fifoCnt - 1]);
|
|
chip->again = recommendedGain;
|
|
ams_i2c_modify(chip->client, chip->shadow,
|
|
AMS_REG_CFG1, AMS_MASK_AGAIN,
|
|
ams_alsGainToReg(recommendedGain));
|
|
chip->fifoCnt = 0;
|
|
chip->acLightDefenceOnCnt = 0;
|
|
chip->acLightDefenceOffCnt = 0;
|
|
ams_i2c_set_field(chip->client, chip->shadow,
|
|
AMS_REG_CONTROL, 0x1, 0x1, 0x1);
|
|
return;
|
|
}
|
|
|
|
ams_i2c_set_field(chip->client, chip->shadow,
|
|
AMS_REG_CONTROL, 0x1, 0x1, 0x1);
|
|
|
|
if ((stddev_ch0 > STANDARD_DEVIATION_HIGH_THRESHOLD)
|
|
|| (stddev_ch1 > STANDARD_DEVIATION_HIGH_THRESHOLD))
|
|
chip->acLightDefenceOnCnt++;
|
|
else
|
|
chip->acLightDefenceOnCnt = 0;
|
|
if ((stddev_ch0 < STANDARD_DEVIATION_LOW_THRESHOLD)
|
|
&& (stddev_ch1 < STANDARD_DEVIATION_LOW_THRESHOLD))
|
|
chip->acLightDefenceOffCnt++;
|
|
else
|
|
chip->acLightDefenceOffCnt = 0;
|
|
|
|
chip->rawdata.red = chip->chMinBuf[CH0][chip->fifoCnt - 1];
|
|
chip->rawdata.green = chip->chMinBuf[CH1][chip->fifoCnt - 1];
|
|
chip->rawdata.blue = chip->chMaxBuf[CH0][chip->fifoCnt - 1];
|
|
chip->rawdata.clear = chip->chMaxBuf[CH1][chip->fifoCnt - 1];
|
|
if (chip->fifoCnt >= JAMES_FIFO_MAX_CNT) {
|
|
if (chip->acLightDefenceOnCnt >= JAMES_FIFO_MAX_CNT)
|
|
chip->isAcLightDefenceMode = true;
|
|
else if (chip->acLightDefenceOffCnt >= JAMES_FIFO_MAX_CNT)
|
|
chip->isAcLightDefenceMode = false;
|
|
|
|
if (chip->brightness_level >= HIGH_BRIGHTNESS_CODE) {
|
|
isHbMode = ams_highBrightness_alsCalcLux(chip,
|
|
chip->chMinBuf, chip->chMaxBuf);
|
|
} else if (chip->isAcLightDefenceMode) {
|
|
isHbMode = ams_acLight_alsCalcLux(chip, chip->chAvg);
|
|
} else {
|
|
isHbMode = ams_alsCalcLux(chip, chip->chMinBuf);
|
|
}
|
|
chip->acLightDefenceOnCnt = 0;
|
|
chip->acLightDefenceOffCnt = 0;
|
|
|
|
chip->fifoCnt = 0;
|
|
if (isHbMode)
|
|
ams_report_high_brightness_events(chip);
|
|
else
|
|
ams_report_events(chip);
|
|
}
|
|
}
|
|
|
|
static void ams_als_compute_data(struct ams_chip *chip)
|
|
{
|
|
long long lux = 0;
|
|
|
|
if (!(chip->rawdata.clear <= 1 &&
|
|
(chip->rawdata.clear + chip->rawdata.red
|
|
+ chip->rawdata.green + chip->rawdata.blue) <= 4)) {
|
|
lux = (long long)DEFAULT_C_COEF * chip->rawdata.clear
|
|
+ (long long)DEFAULT_R_COEF * chip->rawdata.red
|
|
+ (long long)DEFAULT_G_COEF * chip->rawdata.green
|
|
+ (long long)DEFAULT_B_COEF * chip->rawdata.blue;
|
|
|
|
if (chip->cpl != 0)
|
|
lux = lux / chip->cpl;
|
|
}
|
|
|
|
chip->lux = (int)lux;
|
|
if (chip->count_log_time >= LIGHT_LOG_TIME) {
|
|
SENSOR_INFO("r:%d, g:%d, b:%d, c:%d, cpl:%d, LUX:%d\n",
|
|
chip->rawdata.red, chip->rawdata.green,
|
|
chip->rawdata.blue, chip->rawdata.clear,
|
|
chip->cpl, chip->lux);
|
|
chip->count_log_time = 0;
|
|
} else {
|
|
chip->count_log_time++;
|
|
}
|
|
|
|
ams_report_events(chip);
|
|
}
|
|
|
|
static void ams_process_high(struct ams_chip *chip)
|
|
{
|
|
u8 gain_reg;
|
|
|
|
ams_i2c_read(chip->client, AMS_REG_ASTATUS, &gain_reg);
|
|
chip->again = alsGain_conversion[gain_reg & 0x0f];
|
|
ams_i2c_blk_read_direct(chip->client, AMS_REG_ADATAL0,
|
|
(uint8_t *)(&chip->rawdata), 8);
|
|
|
|
if (chip->previousGain != chip->again) {
|
|
chip->cpl = chip->itime * chip->again / DEFAULT_DFG;
|
|
chip->previousGain = chip->again;
|
|
SENSOR_INFO("change gain %d\n", chip->again);
|
|
} else {
|
|
ams_als_compute_data(chip);
|
|
}
|
|
}
|
|
|
|
static void ams_work_func_light(struct work_struct *work)
|
|
{
|
|
struct ams_chip *chip = container_of((struct delayed_work *)work,
|
|
struct ams_chip, main_work);
|
|
|
|
if (chip->cur_algo_mode == ALS_ALGO_MID)
|
|
ams_process_mid(chip);
|
|
else
|
|
ams_process_high(chip);
|
|
|
|
schedule_delayed_work(&chip->main_work,
|
|
nsecs_to_jiffies(atomic_read(&chip->delay)));
|
|
}
|
|
|
|
static ssize_t ams_light_brightness_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct ams_chip *chip = dev_get_drvdata(dev);
|
|
|
|
chip->brightness_level = ASCII_TO_DEC(buf[0]) * 100
|
|
+ ASCII_TO_DEC(buf[1]) * 10 + ASCII_TO_DEC(buf[2]);
|
|
|
|
if (chip->als_enabled) {
|
|
if (chip->cur_algo_mode == ALS_ALGO_MID
|
|
&& chip->brightness_level == 0)
|
|
ams_change_algo_mode(chip, ALS_ALGO_HIGH);
|
|
else if (chip->cur_algo_mode == ALS_ALGO_HIGH
|
|
&& chip->algo_mode == ALS_ALGO_MID
|
|
&& chip->brightness_level != 0)
|
|
ams_change_algo_mode(chip, ALS_ALGO_MID);
|
|
else if (chip->cur_algo_mode == ALS_ALGO_MID)
|
|
ams_change_fifo_sample(chip);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t ams_light_algo_mode_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
u8 algo_mode;
|
|
int ret;
|
|
struct ams_chip *chip = dev_get_drvdata(dev);
|
|
|
|
ret = kstrtou8(buf, 2, &algo_mode);
|
|
if (ret || algo_mode > 1) {
|
|
SENSOR_ERR("Invalid Argument %d, %d\n", ret, algo_mode);
|
|
return ret;
|
|
}
|
|
|
|
if (chip->als_enabled)
|
|
ams_change_algo_mode(chip, algo_mode + 1);
|
|
|
|
chip->algo_mode = algo_mode + 1;
|
|
|
|
return size;
|
|
}
|
|
|
|
static void ams_light_enable(struct ams_chip *chip)
|
|
{
|
|
SENSOR_INFO("\n");
|
|
|
|
chip->algo_mode = ALS_ALGO_MID;
|
|
chip->cur_algo_mode = ALS_ALGO_NONE;
|
|
chip->acLightDefenceOnCnt = 0;
|
|
chip->acLightDefenceOffCnt = 0;
|
|
chip->isAcLightDefenceMode = false;
|
|
chip->count_log_time = LIGHT_LOG_TIME;
|
|
|
|
ams_change_algo_mode(chip, chip->algo_mode);
|
|
}
|
|
|
|
static void ams_light_disable(struct ams_chip *chip)
|
|
{
|
|
SENSOR_INFO("\n");
|
|
cancel_delayed_work_sync(&chip->main_work);
|
|
ams_i2c_write(chip->client, chip->shadow, AMS_REG_ENABLE, 0x01);
|
|
}
|
|
|
|
static ssize_t ams_light_enable_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
u8 enable;
|
|
int ret;
|
|
struct ams_chip *chip = dev_get_drvdata(dev);
|
|
|
|
ret = kstrtou8(buf, 0, &enable);
|
|
if (ret) {
|
|
SENSOR_ERR("Invalid Argument %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (enable == CMD_CAM_LUX_DISABLE) {
|
|
input_report_rel(chip->input_dev,
|
|
REL_LUX, EVENT_CAM_LUX_DISABLE + 1);
|
|
input_sync(chip->input_dev);
|
|
return size;
|
|
}
|
|
|
|
SENSOR_INFO("new_value = %u\n", enable);
|
|
|
|
if (enable && !chip->als_enabled)
|
|
ams_light_enable(chip);
|
|
else if (!enable && chip->als_enabled)
|
|
ams_light_disable(chip);
|
|
|
|
chip->als_enabled = enable;
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t ams_light_enable_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct ams_chip *chip = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", chip->als_enabled);
|
|
}
|
|
|
|
static ssize_t ams_light_poll_delay_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t ams_light_poll_delay_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
return count;
|
|
}
|
|
|
|
static ssize_t ams_light_vendor_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR_NAME);
|
|
}
|
|
|
|
static ssize_t ams_light_lux_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct ams_chip *chip = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d,%d\n",
|
|
chip->rawdata.red, chip->rawdata.green,
|
|
chip->rawdata.blue, chip->rawdata.clear,
|
|
chip->itime, chip->again);
|
|
}
|
|
|
|
static ssize_t ams_light_data_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct ams_chip *chip = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d,%d\n",
|
|
chip->rawdata.red, chip->rawdata.green,
|
|
chip->rawdata.blue, chip->rawdata.clear,
|
|
chip->itime, chip->again);
|
|
}
|
|
|
|
static ssize_t ams_light_brightness_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct ams_chip *chip = dev_get_drvdata(dev);
|
|
|
|
SENSOR_INFO("%d\n", chip->brightness_level);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", chip->brightness_level);
|
|
}
|
|
|
|
static ssize_t ams_light_circle_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct ams_chip *chip = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d.%2.2d %d.%2.2d %d.%2.2d\n",
|
|
chip->light_position[0], chip->light_position[1],
|
|
chip->light_position[2], chip->light_position[3],
|
|
chip->light_position[4], chip->light_position[5]);
|
|
}
|
|
|
|
static DEVICE_ATTR(name, S_IRUGO, ams_light_name_show, NULL);
|
|
static DEVICE_ATTR(reg_data, S_IRUGO,
|
|
ams_light_reg_data_show, ams_light_reg_data_store);
|
|
static DEVICE_ATTR(vendor, S_IRUGO, ams_light_vendor_show, NULL);
|
|
static DEVICE_ATTR(lux, S_IRUGO, ams_light_lux_show, NULL);
|
|
static DEVICE_ATTR(raw_data, S_IRUGO, ams_light_data_show, NULL);
|
|
static DEVICE_ATTR(brightness, 0664,
|
|
ams_light_brightness_show, ams_light_brightness_store);
|
|
static DEVICE_ATTR(light_circle, 0440, ams_light_circle_show, NULL);
|
|
static DEVICE_ATTR(algo_mode, 0220, NULL, ams_light_algo_mode_store);
|
|
|
|
static struct device_attribute *sensor_attrs[] = {
|
|
&dev_attr_name,
|
|
&dev_attr_reg_data,
|
|
&dev_attr_vendor,
|
|
&dev_attr_lux,
|
|
&dev_attr_raw_data,
|
|
&dev_attr_brightness,
|
|
&dev_attr_light_circle,
|
|
&dev_attr_algo_mode,
|
|
NULL,
|
|
};
|
|
|
|
static DEVICE_ATTR(poll_delay, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
ams_light_poll_delay_show, ams_light_poll_delay_store);
|
|
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
ams_light_enable_show, ams_light_enable_store);
|
|
|
|
static struct attribute *light_sysfs_attrs[] = {
|
|
&dev_attr_enable.attr,
|
|
&dev_attr_poll_delay.attr,
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute_group light_attribute_group = {
|
|
.attrs = light_sysfs_attrs,
|
|
};
|
|
|
|
static int ams_parse_dt(struct device *dev, struct ams_chip *chip)
|
|
{
|
|
struct device_node *np = dev->of_node;
|
|
|
|
if (of_property_read_u32_array(np, "ams,light_position",
|
|
chip->light_position,
|
|
sizeof(chip->light_position) / sizeof(chip->light_position[0])) < 0) {
|
|
SENSOR_ERR("no ams light_position, set as 0\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
SENSOR_INFO("light-position - %u.%u %u.%u %u.%u\n",
|
|
chip->light_position[0], chip->light_position[1],
|
|
chip->light_position[2], chip->light_position[3],
|
|
chip->light_position[4], chip->light_position[5]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ams_init_input_device(struct ams_chip *chip)
|
|
{
|
|
int ret = 0;
|
|
struct input_dev *dev;
|
|
|
|
/* allocate lightsensor input_device */
|
|
dev = input_allocate_device();
|
|
if (!dev)
|
|
return -ENOMEM;
|
|
|
|
dev->name = MODULE_NAME;
|
|
dev->id.bustype = BUS_I2C;
|
|
|
|
input_set_capability(dev, EV_REL, REL_DELTA);
|
|
input_set_capability(dev, EV_REL, REL_2ND_MIN);
|
|
input_set_capability(dev, EV_REL, REL_2ND_MAX);
|
|
input_set_capability(dev, EV_REL, REL_LUX);
|
|
|
|
input_set_drvdata(dev, chip);
|
|
|
|
ret = input_register_device(dev);
|
|
if (ret < 0) {
|
|
input_free_device(dev);
|
|
goto err_register_input_dev;
|
|
}
|
|
|
|
ret = sensors_create_symlink(&dev->dev.kobj, dev->name);
|
|
if (ret < 0)
|
|
goto err_create_sensor_symlink;
|
|
|
|
ret = sysfs_create_group(&dev->dev.kobj, &light_attribute_group);
|
|
if (ret < 0)
|
|
goto err_create_sysfs_group;
|
|
|
|
chip->input_dev = dev;
|
|
|
|
return 0;
|
|
|
|
err_create_sysfs_group:
|
|
sensors_remove_symlink(&dev->dev.kobj, dev->name);
|
|
err_create_sensor_symlink:
|
|
input_unregister_device(dev);
|
|
err_register_input_dev:
|
|
dev = NULL;
|
|
return ret;
|
|
}
|
|
|
|
static int ams_get_id(struct ams_chip *chip)
|
|
{
|
|
int ret;
|
|
u8 id, rev, aux;
|
|
|
|
ret = ams_i2c_read(chip->client, AMS_REG_AUXID, &aux);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = ams_i2c_read(chip->client, AMS_REG_REVID, &rev);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = ams_i2c_read(chip->client, AMS_REG_ID, &id);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (id != TCS3701_CHIP_ID) {
|
|
SENSOR_ERR("device id:%02x fail!\n", id);
|
|
return -1;
|
|
}
|
|
|
|
SENSOR_INFO("device id:%02x device revid:%02x device aux:%02x\n",
|
|
id, rev, aux);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ams_probe(struct i2c_client *client, const struct i2c_device_id *idp)
|
|
{
|
|
int ret;
|
|
struct ams_chip *chip;
|
|
|
|
if (!i2c_check_functionality(client->adapter,
|
|
I2C_FUNC_SMBUS_BYTE_DATA)) {
|
|
SENSOR_ERR("i2c smbus byte data unsupported\n");
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
chip = kzalloc(sizeof(struct ams_chip), GFP_KERNEL);
|
|
if (!chip)
|
|
return -ENOMEM;
|
|
|
|
chip->client = client;
|
|
i2c_set_clientdata(client, chip);
|
|
|
|
ret = ams_get_id(chip);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("i2c failed, ret=%d\n", ret);
|
|
goto err_i2c_fail;
|
|
}
|
|
|
|
ret = ams_parse_dt(&client->dev, chip);
|
|
if (ret)
|
|
SENSOR_ERR("ams_parse_dt failed, ret=%d\n", ret);
|
|
|
|
ams_i2c_write(chip->client, chip->shadow, AMS_REG_ENABLE, 0x00);
|
|
ams_i2c_write(chip->client, chip->shadow, AMS_REG_ENABLE, 0x01);
|
|
ams_i2c_write(chip->client, chip->shadow, AMS_REG_AILTL, 0x00);
|
|
ams_i2c_write(chip->client, chip->shadow, AMS_REG_AILTH, 0x00);
|
|
ams_i2c_write(chip->client, chip->shadow, AMS_REG_AIHTL, 0xFF);
|
|
ams_i2c_write(chip->client, chip->shadow, AMS_REG_AIHTH, 0xFF);
|
|
ams_i2c_write(chip->client, chip->shadow, AMS_REG_PERS, 0x00);
|
|
i2c_smbus_write_byte_data(chip->client, AMS_REG_INTENAB, 0x04);
|
|
|
|
ret = ams_init_input_device(chip);
|
|
if (ret) {
|
|
SENSOR_ERR("Input device init failed, ret=%d\n", ret);
|
|
goto err_init_input_device;
|
|
}
|
|
|
|
INIT_DELAYED_WORK(&chip->main_work, ams_work_func_light);
|
|
atomic_set(&chip->delay, DEFAULT_RGBC_DELAY_MS * NSEC_PER_MSEC);
|
|
chip->brightness_level = DEFAULT_BRIGHTNESS_LEVEL;
|
|
|
|
/* set sysfs for light sensor */
|
|
ret = sensors_register(&chip->ls_dev, chip, sensor_attrs, MODULE_NAME);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("Sensor registration failed, ret=%d\n", ret);
|
|
goto err_sensors_register;
|
|
}
|
|
|
|
SENSOR_INFO("Probe ok.\n");
|
|
|
|
return 0;
|
|
|
|
err_sensors_register:
|
|
sensors_remove_symlink(&chip->input_dev->dev.kobj,
|
|
chip->input_dev->name);
|
|
sysfs_remove_group(&chip->input_dev->dev.kobj, &light_attribute_group);
|
|
input_unregister_device(chip->input_dev);
|
|
err_init_input_device:
|
|
err_i2c_fail:
|
|
kfree(chip);
|
|
SENSOR_ERR("Probe failed.\n");
|
|
return ret;
|
|
}
|
|
|
|
static int ams_suspend(struct device *dev)
|
|
{
|
|
struct ams_chip *chip = dev_get_drvdata(dev);
|
|
|
|
SENSOR_INFO("\n");
|
|
|
|
if (chip->als_enabled)
|
|
ams_light_disable(chip);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ams_resume(struct device *dev)
|
|
{
|
|
struct ams_chip *chip = dev_get_drvdata(dev);
|
|
|
|
SENSOR_INFO("\n");
|
|
|
|
if (chip->als_enabled) {
|
|
ams_light_enable(chip);
|
|
input_report_rel(chip->input_dev, REL_LUX,
|
|
EVENT_INIT_MOVING_AVERAGE + 1);
|
|
input_sync(chip->input_dev);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int ams_remove(struct i2c_client *client)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void ams_shutdown(struct i2c_client *client)
|
|
{
|
|
struct ams_chip *chip = i2c_get_clientdata(client);
|
|
|
|
SENSOR_INFO("\n");
|
|
|
|
if (chip->als_enabled)
|
|
ams_light_disable(chip);
|
|
}
|
|
|
|
static struct i2c_device_id ams_idtable[] = {
|
|
{ CHIP_NAME, 0},
|
|
{}
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(i2c, ams_idtable);
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id ams_of_match[] = {
|
|
{ .compatible = "ams,tcs3701",},
|
|
};
|
|
#else
|
|
#define ams_of_match NULL
|
|
#endif
|
|
|
|
static const struct dev_pm_ops ams_pm_ops = {
|
|
.suspend = ams_suspend,
|
|
.resume = ams_resume,
|
|
};
|
|
|
|
static struct i2c_driver ams_driver = {
|
|
.driver = {
|
|
.name = CHIP_NAME,
|
|
.pm = &ams_pm_ops,
|
|
.of_match_table = ams_of_match,
|
|
},
|
|
.id_table = ams_idtable,
|
|
.probe = ams_probe,
|
|
.remove = ams_remove,
|
|
.shutdown = ams_shutdown,
|
|
};
|
|
|
|
module_i2c_driver(ams_driver);
|
|
|
|
MODULE_DESCRIPTION("AMS Dvice Driver");
|
|
MODULE_AUTHOR("Samsung Electronics");
|
|
MODULE_LICENSE("GPL");
|