blob: 49489a61fb33efb5c7bd32984680c7dec236c1c5 [file] [log] [blame]
/*
* 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", &reg, &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");