| /* |
| * Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved. |
| * |
| * This software is licensed under the terms of the GNU General Public |
| * License, as published by the Free Software Foundation, and |
| * may be copied, distributed, and modified under those terms. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #define VENDOR "AMS" |
| |
| #define TCS3407_CHIP_NAME "TCS3407" |
| #define TCS3408_CHIP_NAME "TCS3408" |
| |
| #define VERSION "2" |
| #define SUB_VERSION "10" |
| #define VENDOR_VERSION "a" |
| |
| #define MODULE_NAME_ALS "als_rear" |
| |
| #define TCS3407_SLAVE_I2C_ADDR_REVID_V0 0x39 |
| #define TCS3407_SLAVE_I2C_ADDR_REVID_V1 0x29 |
| |
| #define AMSDRIVER_I2C_RETRY_DELAY 10 |
| #define AMSDRIVER_I2C_MAX_RETRIES 5 |
| |
| //#define CONFIG_AMS_OPTICAL_SENSOR_FIFO |
| #define TCS3408_USE_SMUX |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FIFO |
| /* AWB/Flicker Definition */ |
| #define ALS_AUTOGAIN |
| #define BYTE 2 |
| #define AWB_INTERVAL 20 /* 20 sample(from 17 to 28) */ |
| |
| #define CONFIG_SKIP_CNT 8 |
| #define FLICKER_FIFO_THR 16 |
| #define FLICKER_DATA_CNT 200 |
| #define FLICKER_FIFO_READ -2 |
| |
| #define TCS3407_IOCTL_MAGIC 0xFD |
| #define TCS3407_IOCTL_READ_FLICKER _IOR(TCS3407_IOCTL_MAGIC, 0x01, int *) |
| #endif |
| |
| #if defined(CONFIG_LEDS_S2MPB02) && !defined(CONFIG_AMS_OPTICAL_SENSOR_FIFO) |
| #define CONFIG_AMS_OPTICAL_SENSOR_EOL_MODE |
| #endif |
| |
| #include "tcs3407.h" |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_EOL_MODE |
| #include <linux/leds-s2mpb02.h> |
| #include <linux/pwm.h> |
| |
| #define DEFAULT_DUTY_50HZ 5000 |
| #define DEFAULT_DUTY_60HZ 4166 |
| |
| #define MAX_TEST_RESULT 256 |
| #define EOL_COUNT 20 |
| #define EOL_SKIP_COUNT 5 |
| #define EOL_GAIN 500 |
| |
| #define DEFAULT_IR_SPEC_MIN 0 |
| #define DEFAULT_IR_SPEC_MAX 500000 |
| #define DEFAULT_IC_SPEC_MIN 0 |
| #define DEFAULT_IC_SPEC_MAX 20000 |
| |
| static u32 gSpec_ir_min = DEFAULT_IR_SPEC_MIN; |
| static u32 gSpec_ir_max = DEFAULT_IR_SPEC_MAX; |
| static u32 gSpec_clear_min = DEFAULT_IR_SPEC_MIN; |
| static u32 gSpec_clear_max = DEFAULT_IR_SPEC_MAX; |
| static u32 gSpec_icratio_min = DEFAULT_IC_SPEC_MIN; |
| static u32 gSpec_icratio_max = DEFAULT_IC_SPEC_MAX; |
| static u32 debug_pwm_duty = 0; |
| |
| #define FREQ100_SPEC_IN(X) ((X == 100)?"PASS":"FAIL") |
| #define FREQ120_SPEC_IN(X) ((X == 120)?"PASS":"FAIL") |
| |
| #define IR_SPEC_IN(X) ((X >= gSpec_ir_min && X <= gSpec_ir_max)?"PASS":"FAIL") |
| #define CLEAR_SPEC_IN(X) ((X >= gSpec_clear_min && X <= gSpec_clear_max)?"PASS":"FAIL") |
| #define ICRATIO_SPEC_IN(X) ((X >= gSpec_icratio_min && X <= gSpec_icratio_max)?"PASS":"FAIL") |
| #endif |
| |
| static int als_debug = 1; |
| static int als_info; |
| |
| module_param(als_debug, int, S_IRUGO | S_IWUSR); |
| module_param(als_info, int, S_IRUGO | S_IWUSR); |
| |
| static struct tcs3407_device_data *tcs3407_data; |
| |
| #define AMS_ROUND_SHFT_VAL 4 |
| #define AMS_ROUND_ADD_VAL (1 << (AMS_ROUND_SHFT_VAL - 1)) |
| #define AMS_ALS_GAIN_FACTOR 1000 |
| #define CPU_FRIENDLY_FACTOR_1024 1 |
| #define AMS_ALS_Cc (118 * CPU_FRIENDLY_FACTOR_1024) |
| #define AMS_ALS_Rc (112 * CPU_FRIENDLY_FACTOR_1024) |
| #define AMS_ALS_Gc (172 * CPU_FRIENDLY_FACTOR_1024) |
| #define AMS_ALS_Bc (180 * CPU_FRIENDLY_FACTOR_1024) |
| #define AMS_ALS_Wbc (111 * CPU_FRIENDLY_FACTOR_1024) |
| |
| #define AMS_ALS_FACTOR 1000 |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_259x |
| #define AMS_ALS_TIMEBASE (100000) /* in uSec, see data sheet */ |
| #define AMS_ALS_ADC_MAX_COUNT (37888) /* see data sheet */ |
| #else |
| #define AMS_ALS_TIMEBASE (2780) /* in uSec, see data sheet */ |
| #define AMS_ALS_ADC_MAX_COUNT (1024) /* see data sheet */ |
| #endif |
| #define AMS_ALS_THRESHOLD_LOW (5) /* in % */ |
| #define AMS_ALS_THRESHOLD_HIGH (5) /* in % */ |
| |
| #define AMS_ALS_ATIME (50000) |
| |
| #define WIDEBAND_CONST 4 |
| #define CLEAR_CONST 3 |
| |
| /* REENABLE only enables those that were on record as being enabled */ |
| #define AMS_REENABLE(ret) {ret = ams_setByte(ctx->portHndl, DEVREG_ENABLE, ctx->shadowEnableReg); } |
| /* DISABLE_ALS disables ALS w/o recording that as its new state */ |
| #define AMS_DISABLE_ALS(ret) {ret = ams_setField(ctx->portHndl, DEVREG_ENABLE, LOW, (MASK_AEN)); } |
| #define AMS_REENABLE_ALS(ret) {ret = ams_setField(ctx->portHndl, DEVREG_ENABLE, HIGH, (MASK_AEN)); } |
| |
| #define AMS_SET_ALS_TIME(uSec, ret) {ret = ams_setByte(ctx->portHndl, DEVREG_ATIME, alsTimeUsToReg(uSec)); } |
| #define AMS_GET_ALS_TIME(uSec, ret) {ret = ams_getByte(ctx->portHndl, DEVREG_ATIME, alsTimeUsToReg(uSec)); } |
| |
| #define AMS_GET_ALS_GAIN(scaledGain, gain, ret) {ret = ams_getByte(ctx->portHndl, DEVREG_ASTATUS, &(gain)); \ |
| scaledGain = alsGain_conversion[(gain) & 0x0f]; } |
| |
| #define AMS_SET_ALS_STEP_TIME(uSec, ret) {ret = ams_setWord(ctx->portHndl, DEVREG_ASTEPL, alsTimeUsToReg(uSec * 1000)); } |
| |
| #define AMS_SET_ALS_GAIN(mGain, ret) {ret = ams_setField(ctx->portHndl, DEVREG_CFG1, alsGainToReg(mGain), MASK_AGAIN); } |
| #define AMS_SET_ALS_PERS(persCode, ret) {ret = ams_setField(ctx->portHndl, DEVREG_PERS, (persCode), MASK_APERS); } |
| #define AMS_CLR_ALS_INT(ret) {ret = ams_setByte(ctx->portHndl, DEVREG_STATUS, (AINT | ASAT_FDSAT)); } |
| #define AMS_SET_ALS_THRS_LOW(x, ret) {ret = ams_setWord(ctx->portHndl, DEVREG_AILTL, (x)); } |
| #define AMS_SET_ALS_THRS_HIGH(x, ret) {ret = ams_setWord(ctx->portHndl, DEVREG_AIHTL, (x)); } |
| #define AMS_SET_ALS_AUTOGAIN(x, ret) {ret = ams_setField(ctx->portHndl, DEVREG_CFG8, (x), MASK_AUTOGAIN); } |
| #define AMS_SET_ALS_AGC_LOW_HYST(x) {ams_setField(ctx->portHndl, DEVREG_CFG10, ((x)<<4), MASK_AGC_LOW_HYST); } |
| #define AMS_SET_ALS_AGC_HIGH_HYST(x) {ams_setField(ctx->portHndl, DEVREG_CFG10, ((x)<<6), MASK_AGC_HIGH_HYST); } |
| |
| /* Get CRGB and whatever Wideband it may have */ |
| #define AMS_ALS_GET_CRGB_W(x, ret) {ret = ams_getBuf(ctx->portHndl, DEVREG_ADATA0L, (uint8_t *) (x), 10); } |
| |
| #if 0 //def TCS3408_USE_SMUX |
| static uint8_t smux_tcs3408_data[] = { |
| 0x14, 0x25, 0x23, 0x41, |
| 0x33, 0x12, 0x14, 0x24, |
| 0x53, 0x23, 0x15, 0x14, |
| 0x32, 0x44, 0x21, 0x23, |
| 0x13, 0x54, 0x00, 0x66 |
| }; |
| #endif |
| |
| // SMUX Default |
| //14 25 23 41 33 12 14 24 53 23 15 14 32 44 21 23 13 54 00 76 |
| /* |
| static uint8_t smux_tcs3407_data[] = { |
| 0x14, 0x25, 0x23, 0x41, |
| 0x33, 0x12, 0x14, 0x24, |
| 0x53, 0x23, 0x15, 0x14, |
| 0x32, 0x44, 0x21, 0x23, |
| 0x13, 0x54, 0x00, 0x77 |
| }; |
| */ |
| |
| #define AMS_READ_S_MUX() {ret = ams_setField(ctx->portHndl, DEVREG_CFG6, ((1)<<3), MASK_SMUX_CMD); } |
| #define AMS_WRITE_S_MUX() {ret = ams_setField(ctx->portHndl, DEVREG_CFG6, ((2)<<3), MASK_SMUX_CMD); } |
| #define AMS_CLOSE_S_MUX() {ret = ams_setField(ctx->portHndl, DEVREG_CFG6, 0x00, MASK_SMUX_CMD); } |
| |
| |
| typedef struct{ |
| uint8_t deviceId; |
| uint8_t deviceIdMask; |
| uint8_t deviceRef; |
| uint8_t deviceRefMask; |
| ams_deviceIdentifier_e device; |
| } ams_deviceIdentifier_t; |
| |
| typedef struct _fifo { |
| uint16_t AdcClear; |
| uint16_t AdcRed; |
| uint16_t AdcGreen; |
| uint16_t AdcBlue; |
| uint16_t AdcWb; |
| } adcDataSet_t; |
| |
| #define AMS_PORT_LOG_CRGB_W(dataset) \ |
| ALS_info("%s - C, R,G,B = %u, %u,%u,%u; WB = %u\n", __func__ \ |
| , dataset.AdcClear \ |
| , dataset.AdcRed \ |
| , dataset.AdcGreen \ |
| , dataset.AdcBlue \ |
| , dataset.AdcWb \ |
| ) |
| |
| static ams_deviceIdentifier_t deviceIdentifier[] = { |
| {AMS_DEVICE_ID, AMS_DEVICE_ID_MASK, AMS_REV_ID, AMS_REV_ID_MASK, AMS_TCS3407}, |
| {AMS_DEVICE_ID, AMS_DEVICE_ID_MASK, AMS_REV_ID_UNTRIM, AMS_REV_ID_MASK, AMS_TCS3407_UNTRIM}, |
| {AMS_DEVICE_ID2, AMS_DEVICE_ID2_MASK, AMS_REV_ID2, AMS_REV_ID2_MASK, AMS_TCS3408}, |
| {AMS_DEVICE_ID2, AMS_DEVICE_ID2_MASK, AMS_REV_ID2_UNTRIM, AMS_REV_ID2_MASK, AMS_TCS3408_UNTRIM}, |
| {0, 0, 0, 0, AMS_LAST_DEVICE} |
| }; |
| |
| deviceRegisterTable_t deviceRegisterDefinition[DEVREG_REG_MAX] = { |
| { 0x00, 0x00 }, /* DEVREG_RAM_START */ |
| { 0x13, 0x00 }, /* DEVREG_SMUX13_PRX_TO_FLICKER */ |
| |
| { 0x80, 0x00 }, /* DEVREG_ENABLE */ |
| { 0x81, 0x00 }, /* DEVREG_ATIME */ |
| { 0x82, 0x00 }, /* DEVREG_PTIME */ |
| { 0x83, 0x00 }, /* DEVREG_WTIME */ |
| { 0x84, 0x00 }, /* DEVREG_AILTL */ |
| { 0x85, 0x00 }, /* DEVREG_AILTH */ |
| { 0x86, 0x00 }, /* DEVREG_AIHTL */ |
| { 0x87, 0x00 }, /* DEVREG_AIHTH */ |
| |
| { 0x90, 0x00 }, /* DEVREG_AUXID */ |
| { 0x91, AMS_REV_ID }, /* DEVREG_REVID */ |
| { 0x92, AMS_DEVICE_ID }, /* DEVREG_ID */ |
| { 0x93, 0x00 }, /* DEVREG_STATUS */ |
| { 0x94, 0x00 }, /* DEVREG_ASTATUS */ |
| { 0x95, 0x00 }, /* DEVREG_ADATAOL */ |
| { 0x96, 0x00 }, /* DEVREG_ADATAOH */ |
| { 0x97, 0x00 }, /* DEVREG_ADATA1L */ |
| { 0x98, 0x00 }, /* DEVREG_ADATA1H */ |
| { 0x99, 0x00 }, /* DEVREG_ADATA2L */ |
| { 0x9A, 0x00 }, /* DEVREG_ADATA2H */ |
| { 0x9B, 0x00 }, /* DEVREG_ADATA3L */ |
| { 0x9C, 0x00 }, /* DEVREG_ADATA3H */ |
| { 0x9D, 0x00 }, /* DEVREG_ADATA4L */ |
| { 0x9E, 0x00 }, /* DEVREG_ADATA4H */ |
| { 0x9F, 0x00 }, /* DEVREG_ADATA5L */ |
| |
| { 0xA0, 0x00 }, /* DEVREG_ADATA5H */ |
| { 0xA3, 0x00 }, /* DEVREG_STATUS2 */ |
| { 0xA4, 0x00 }, /* DEVREG_STATUS3 */ |
| { 0xA6, 0x00 }, /* DEVREG_STATUS5 */ |
| { 0xA7, 0x00 }, /* DEVREG_STATUS4 */ |
| /* 0xA8 Reserved */ |
| { 0xA9, 0x40 }, /* DEVREG_CFG0 */ |
| { 0xAA, 0x09 }, /* DEVREG_CFG1 */ |
| { 0xAC, 0x0C }, /* DEVREG_CFG3 */ |
| { 0xAD, 0x00 }, /* DEVREG_CFG4 */ |
| { 0xAF, 0x00 }, /* DEVREG_CFG6 */ |
| |
| { 0xB1, 0x98 }, /* DEVREG_CFG8 */ |
| { 0xB2, 0x00 }, /* DEVREG_CFG9 */ |
| { 0xB3, 0xF2 }, /* DEVREG_CFG10 */ |
| { 0xB4, 0x4D }, /* DEVREG_CFG11 */ |
| { 0xB5, 0x00 }, /* DEVREG_CFG12 */ |
| { 0xBD, 0x00 }, /* DEVREG_PERS */ |
| { 0xBE, 0x02 }, /* DEVREG_GPIO */ |
| |
| { 0xCA, 0xE7 }, /* DEVREG_ASTEPL */ |
| { 0xCB, 0x03 }, /* DEVREG_ASTEPH */ |
| { 0xCF, 0X97 }, /* DEVREG_AGC_GAIN_MAX */ |
| /*0x97, extended max fd_gain to 1024x as 0xA7, again to 2048x onTCS3408 0xBC*/ |
| |
| { 0xD6, 0xFf }, /* DEVREG_AZ_CONFIG */ |
| { 0xD7, 0x21}, /*DEVREG_FD_CFG0*/ |
| { 0xD8, 0x68}, /*DEVREG_FD_CFG1*/ |
| { 0xD9, 0x64}, /*DEVREG_FD_CFG2*/ |
| { 0xDA, 0x91 }, /*DEVREG_FD_CFG3*/ |
| { 0xDB, 0x00 }, /* DEVREG_FD_STATUS */ |
| /* 0xEF-0xF8 Reserved */ |
| { 0xF9, 0x00 }, /* DEVREG_INTENAB */ |
| { 0xFA, 0x00 }, /* DEVREG_CONTROL */ |
| { 0xFC, 0x00 }, /* DEVREG_FIFO_MAP */ |
| { 0xFD, 0x00 }, /* DEVREG_FIFO_STATUS */ |
| { 0xFE, 0x00 }, /* DEVREG_FDATAL */ |
| { 0xFF, 0x00 }, /* DEVREG_FDATAH */ |
| { 0x6f, 0x00 }, /* DEVREG_FLKR_WA_RAMLOC_1 */ |
| { 0x71, 0x00 }, /* DEVREG_FLKR_WA_RAMLOC_2 */ |
| { 0xF3, 0x00 }, /* DEVREG_SOFT_RESET */ |
| }; |
| |
| uint32_t alsGain_conversion[] = { |
| 1000 / 2, |
| 1 * 1000, |
| 2 * 1000, |
| 4 * 1000, |
| 8 * 1000, |
| 16 * 1000, |
| 32 * 1000, |
| 64 * 1000, |
| 128 * 1000, |
| 256 * 1000, |
| 512 * 1000, |
| 1024 * 1000, |
| 2048 * 1000, |
| }; |
| |
| static const struct of_device_id tcs3407_match_table[] = { |
| { .compatible = "ams,tcs3407",}, |
| {}, |
| }; |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FIFO |
| static long tcs3407_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
| { |
| void __user *argp = (void __user *)arg; |
| int ret = 0; |
| |
| struct tcs3407_device_data *data = container_of(file->private_data, |
| struct tcs3407_device_data, miscdev); |
| |
| ALS_dbg("%s - ioctl start, %d\n", __func__, cmd); |
| mutex_lock(&data->flickerdatalock); |
| |
| switch (cmd) { |
| case TCS3407_IOCTL_READ_FLICKER: |
| ALS_dbg("%s - TCS3407_IOCTL_READ_FLICKER = %d\n", __func__, data->flicker_data[0]); |
| |
| ret = copy_to_user(argp, |
| data->flicker_data, |
| sizeof(int)*FLICKER_DATA_CNT); |
| |
| if (unlikely(ret)) |
| goto ioctl_error; |
| |
| break; |
| |
| default: |
| ALS_err("%s - invalid cmd\n", __func__); |
| break; |
| } |
| |
| mutex_unlock(&data->flickerdatalock); |
| return ret; |
| |
| ioctl_error: |
| mutex_unlock(&data->flickerdatalock); |
| ALS_err("%s - read flicker data err(%d)\n", __func__, ret); |
| return -ret; |
| } |
| |
| static const struct file_operations tcs3407_fops = { |
| .owner = THIS_MODULE, |
| .open = nonseekable_open, |
| .unlocked_ioctl = tcs3407_ioctl, |
| }; |
| #endif |
| |
| static uint8_t alsGainToReg(uint32_t x) |
| { |
| int i; |
| |
| for (i = sizeof(alsGain_conversion)/sizeof(uint32_t)-1; i != 0; i--) { |
| if (x >= alsGain_conversion[i]) |
| break; |
| } |
| return (i << 0); |
| } |
| |
| static uint16_t alsTimeUsToReg(uint32_t x) |
| { |
| uint16_t regValue; |
| |
| regValue = (x / 2816); |
| |
| return regValue; |
| } |
| |
| static void tcs3407_debug_var(struct tcs3407_device_data *data) |
| { |
| ALS_dbg("===== %s =====\n", __func__); |
| ALS_dbg("%s client %p slave_addr 0x%x\n", __func__, |
| data->client, data->client->addr); |
| ALS_dbg("%s dev %p\n", __func__, data->dev); |
| ALS_dbg("%s als_input_dev %p\n", __func__, data->als_input_dev); |
| ALS_dbg("%s als_pinctrl %p\n", __func__, data->als_pinctrl); |
| ALS_dbg("%s pins_sleep %p\n", __func__, data->pins_sleep); |
| ALS_dbg("%s pins_idle %p\n", __func__, data->pins_idle); |
| ALS_dbg("%s vdd_1p8 %s\n", __func__, data->vdd_1p8); |
| ALS_dbg("%s i2c_1p8 %s\n", __func__, data->i2c_1p8); |
| ALS_dbg("%s als_enabled %d\n", __func__, data->enabled); |
| ALS_dbg("%s als_sampling_rate %d\n", __func__, data->sampling_period_ns); |
| ALS_dbg("%s regulator_state %d\n", __func__, data->regulator_state); |
| ALS_dbg("%s als_int %d\n", __func__, data->pin_als_int); |
| ALS_dbg("%s als_en %d\n", __func__, data->pin_als_en); |
| ALS_dbg("%s als_irq %d\n", __func__, data->dev_irq); |
| ALS_dbg("%s irq_state %d\n", __func__, data->irq_state); |
| ALS_dbg("===== %s =====\n", __func__); |
| } |
| |
| static int tcs3407_write_reg(struct tcs3407_device_data *device, |
| u8 reg_addr, u8 data) |
| { |
| int err = -1; |
| int tries = 0; |
| int num = 1; |
| u8 buffer[2] = { reg_addr, data }; |
| struct i2c_msg msgs[] = { |
| { |
| .addr = device->client->addr, |
| .flags = device->client->flags & I2C_M_TEN, |
| .len = 2, |
| .buf = buffer, |
| }, |
| }; |
| |
| if (!device->pm_state || device->regulator_state == 0) { |
| ALS_err("%s - write error, pm suspend or reg_state %d\n", |
| __func__, device->regulator_state); |
| err = -EFAULT; |
| return err; |
| } |
| mutex_lock(&device->suspendlock); |
| |
| do { |
| err = i2c_transfer(device->client->adapter, msgs, num); |
| if (err != num) |
| msleep_interruptible(AMSDRIVER_I2C_RETRY_DELAY); |
| if (err < 0) |
| ALS_err("%s - i2c_transfer error = %d\n", __func__, err); |
| } while ((err != num) && (++tries < AMSDRIVER_I2C_MAX_RETRIES)); |
| |
| mutex_unlock(&device->suspendlock); |
| |
| if (err != num) { |
| ALS_err("%s -write transfer error:%d\n", __func__, err); |
| err = -EIO; |
| device->i2c_err_cnt++; |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| static int tcs3407_read_reg(struct tcs3407_device_data *device, |
| u8 reg_addr, u8 *buffer, int length) |
| { |
| int err = -1; |
| int tries = 0; /* # of attempts to read the device */ |
| int num = 2; |
| struct i2c_msg msgs[] = { |
| { |
| .addr = device->client->addr, |
| .flags = device->client->flags & I2C_M_TEN, |
| .len = 1, |
| .buf = buffer, |
| }, |
| { |
| .addr = device->client->addr, |
| .flags = (device->client->flags & I2C_M_TEN) | I2C_M_RD, |
| .len = length, |
| .buf = buffer, |
| }, |
| }; |
| |
| if (!device->pm_state || device->regulator_state == 0) { |
| ALS_err("%s - read error, pm suspend or reg_state %d\n", |
| __func__, device->regulator_state); |
| err = -EFAULT; |
| return err; |
| } |
| mutex_lock(&device->suspendlock); |
| |
| do { |
| buffer[0] = reg_addr; |
| err = i2c_transfer(device->client->adapter, msgs, num); |
| if (err != num) |
| msleep_interruptible(AMSDRIVER_I2C_RETRY_DELAY); |
| if (err < 0) |
| ALS_err("%s - i2c_transfer error = %d\n", __func__, err); |
| } while ((err != num) && (++tries < AMSDRIVER_I2C_MAX_RETRIES)); |
| |
| mutex_unlock(&device->suspendlock); |
| |
| if (err != num) { |
| ALS_err("%s -read transfer error:%d\n", __func__, err); |
| err = -EIO; |
| device->i2c_err_cnt++; |
| } else |
| err = 0; |
| |
| return err; |
| } |
| |
| static int ams_getByte(AMS_PORT_portHndl *portHndl, ams_deviceRegister_t reg, uint8_t *readData) |
| { |
| struct tcs3407_device_data *data = i2c_get_clientdata(portHndl); |
| int err = 0; |
| uint8_t length = 1; |
| |
| /* Sanity check input param */ |
| if (reg >= DEVREG_REG_MAX) |
| return 0; |
| |
| err = tcs3407_read_reg(data, deviceRegisterDefinition[reg].address, readData, length); |
| |
| return err; |
| } |
| |
| static int ams_setByte(AMS_PORT_portHndl *portHndl, ams_deviceRegister_t reg, uint8_t setData) |
| { |
| struct tcs3407_device_data *data = i2c_get_clientdata(portHndl); |
| int err = 0; |
| |
| /* Sanity check input param */ |
| if (reg >= DEVREG_REG_MAX) |
| return 0; |
| |
| err = tcs3407_write_reg(data, deviceRegisterDefinition[reg].address, setData); |
| |
| return err; |
| } |
| |
| static int ams_getBuf(AMS_PORT_portHndl *portHndl, ams_deviceRegister_t reg, uint8_t *readData, uint8_t length) |
| { |
| struct tcs3407_device_data *data = i2c_get_clientdata(portHndl); |
| int err = 0; |
| |
| /* Sanity check input param */ |
| if (reg >= DEVREG_REG_MAX) |
| return 0; |
| |
| err = tcs3407_read_reg(data, deviceRegisterDefinition[reg].address, readData, length); |
| |
| return err; |
| } |
| |
| int ams_setBuf(AMS_PORT_portHndl *portHndl, ams_deviceRegister_t reg, uint8_t *setData, uint8_t length) |
| { |
| struct tcs3407_device_data *data = i2c_get_clientdata(portHndl); |
| int err = 0; |
| |
| /* Sanity check input param */ |
| if (reg >= DEVREG_REG_MAX) |
| return 0; |
| |
| err = tcs3407_write_reg(data, deviceRegisterDefinition[reg].address, *setData); |
| |
| return err; |
| } |
| |
| int ams_getWord(AMS_PORT_portHndl *portHndl, ams_deviceRegister_t reg, uint16_t *readData) |
| { |
| struct tcs3407_device_data *data = i2c_get_clientdata(portHndl); |
| int err = 0; |
| uint8_t length = sizeof(uint16_t); |
| uint8_t buffer[sizeof(uint16_t)]; |
| |
| /* Sanity check input param */ |
| if (reg >= DEVREG_REG_MAX) |
| return 0; |
| |
| err = tcs3407_read_reg(data, deviceRegisterDefinition[reg].address, buffer, length); |
| |
| *readData = ((buffer[0] << AMS_ENDIAN_1) + (buffer[1] << AMS_ENDIAN_2)); |
| |
| return err; |
| } |
| |
| static int ams_setWord(AMS_PORT_portHndl *portHndl, ams_deviceRegister_t reg, uint16_t setData) |
| { |
| struct tcs3407_device_data *data = i2c_get_clientdata(portHndl); |
| int err = 0; |
| uint8_t buffer[sizeof(uint16_t)]; |
| |
| /* Sanity check input param */ |
| if (reg >= (DEVREG_REG_MAX - 1)) |
| return 0; |
| |
| buffer[0] = ((setData >> AMS_ENDIAN_1) & 0xff); |
| buffer[1] = ((setData >> AMS_ENDIAN_2) & 0xff); |
| |
| err = tcs3407_write_reg(data, deviceRegisterDefinition[reg].address, buffer[0]); |
| err = tcs3407_write_reg(data, deviceRegisterDefinition[reg + 1].address, buffer[1]); |
| |
| return err; |
| } |
| |
| int ams_getField(AMS_PORT_portHndl *portHndl, ams_deviceRegister_t reg, uint8_t *setData, ams_regMask_t mask) |
| { |
| struct tcs3407_device_data *data = i2c_get_clientdata(portHndl); |
| int err = 0; |
| uint8_t length = 1; |
| |
| /* Sanity check input param */ |
| if (reg >= DEVREG_REG_MAX) |
| return 0; |
| |
| err = tcs3407_read_reg(data, deviceRegisterDefinition[reg].address, setData, length); |
| |
| *setData &= mask; |
| |
| return err; |
| } |
| |
| static int ams_setField(AMS_PORT_portHndl *portHndl, ams_deviceRegister_t reg, uint8_t setData, ams_regMask_t mask) |
| { |
| struct tcs3407_device_data *data = i2c_get_clientdata(portHndl); |
| int err = 1; |
| uint8_t length = 1; |
| uint8_t original_data; |
| uint8_t new_data; |
| |
| /* Sanity check input param */ |
| if (reg >= DEVREG_REG_MAX) |
| return 0; |
| |
| err = tcs3407_read_reg(data, deviceRegisterDefinition[reg].address, &original_data, length); |
| if (err < 0) |
| return err; |
| |
| new_data = original_data & ~mask; |
| new_data |= (setData & mask); |
| |
| if (new_data != original_data) |
| err = tcs3407_write_reg(data, deviceRegisterDefinition[reg].address, new_data); |
| |
| return err; |
| } |
| |
| static void als_getDefaultCalibrationData(ams_ccb_als_calibration_t *data) |
| { |
| if (data != NULL) { |
| data->Time_base = AMS_ALS_TIMEBASE; |
| data->thresholdLow = AMS_ALS_THRESHOLD_LOW; |
| data->thresholdHigh = AMS_ALS_THRESHOLD_HIGH; |
| data->calibrationFactor = 1000; |
| } |
| } |
| |
| static void als_update_statics(amsAlsContext_t *ctx); |
| |
| static int amsAlg_als_processData(amsAlsContext_t *ctx, amsAlsDataSet_t *inputData) |
| { |
| uint32_t tempIr = 0, tempWb = 0, tempClear = 0; |
| uint32_t UVIR_Clear = 0; |
| uint32_t UVIR_wideband = 0; |
| ams_deviceCtx_t *deviceCtx = tcs3407_data->deviceCtx; |
| |
| ALS_info("%s - raw: %d, %d, %d\n", __func__, ctx->uvir_cpl, ctx->gain, ctx->time_us); |
| ALS_info("%s - raw: %d, %d, %d, %d, %d\n", __func__, inputData->datasetArray->clearADC, |
| inputData->datasetArray->redADC, inputData->datasetArray->greenADC, |
| inputData->datasetArray->blueADC, inputData->datasetArray->widebandADC); |
| |
| if (inputData->status & ALS_STATUS_RDY) { |
| if (ctx->previousGain != ctx->gain) |
| als_update_statics(ctx); |
| |
| ctx->results.rawClear = inputData->datasetArray->clearADC; |
| ctx->results.rawRed = inputData->datasetArray->redADC; |
| ctx->results.rawGreen = inputData->datasetArray->greenADC; |
| ctx->results.rawBlue = inputData->datasetArray->blueADC; |
| ctx->results.rawWideband = inputData->datasetArray->widebandADC; |
| |
| /* Calculate IR */ |
| tempIr = inputData->datasetArray->redADC + |
| inputData->datasetArray->greenADC + |
| inputData->datasetArray->blueADC; |
| if (tempIr > inputData->datasetArray->clearADC) |
| ctx->results.IR = (tempIr - inputData->datasetArray->clearADC) / 2; |
| else |
| ctx->results.IR = 0; |
| |
| /*Calculate UV+IR*/ |
| ctx->results.irrRed = ((inputData->datasetArray->redADC * (AMS_ALS_Rc / CPU_FRIENDLY_FACTOR_1024))) / ctx->uvir_cpl; |
| ctx->results.irrClear = ((inputData->datasetArray->clearADC * (AMS_ALS_Cc / CPU_FRIENDLY_FACTOR_1024))) / ctx->uvir_cpl; |
| ctx->results.irrBlue = ((inputData->datasetArray->blueADC * (AMS_ALS_Bc / CPU_FRIENDLY_FACTOR_1024))) / ctx->uvir_cpl; |
| ctx->results.irrGreen = ((inputData->datasetArray->greenADC * (AMS_ALS_Gc / CPU_FRIENDLY_FACTOR_1024))) / ctx->uvir_cpl; |
| |
| UVIR_Clear = (inputData->datasetArray->clearADC * (AMS_ALS_Cc / CPU_FRIENDLY_FACTOR_1024)) / ctx->uvir_cpl; |
| UVIR_wideband = (inputData->datasetArray->widebandADC * (AMS_ALS_Wbc / CPU_FRIENDLY_FACTOR_1024)) / ctx->uvir_cpl; |
| |
| if (deviceCtx->deviceId < AMS_TCS3408) { |
| tempWb = WIDEBAND_CONST * UVIR_wideband; |
| tempClear = (CLEAR_CONST * UVIR_Clear) >> 1; |
| |
| if (tempWb < tempClear) |
| ctx->results.irrWideband = 0; |
| else |
| ctx->results.irrWideband = tempWb - tempClear; |
| } else { |
| tempWb = (WIDEBAND_CONST * UVIR_wideband) * AMS_ALS_FACTOR; |
| tempClear = (1400 * UVIR_Clear); |
| |
| if (tempWb < tempClear) |
| ctx->results.irrWideband = 0; |
| else |
| ctx->results.irrWideband = (tempWb - tempClear) / AMS_ALS_FACTOR; |
| } |
| } |
| |
| ALS_info("%s - cal: %d, %d, %d, %d, %d, %d, %d\n", __func__, |
| ctx->results.irrClear, ctx->results.irrRed, ctx->results.irrGreen, |
| ctx->results.irrBlue, ctx->results.irrWideband, UVIR_Clear, UVIR_wideband); |
| |
| return 0; |
| } |
| |
| static bool ams_getMode(ams_deviceCtx_t *ctx, ams_mode_t *mode) |
| { |
| *mode = ctx->mode; |
| |
| return false; |
| } |
| |
| uint32_t ams_getResult(ams_deviceCtx_t *ctx) |
| { |
| uint32_t returnValue = ctx->updateAvailable; |
| |
| ctx->updateAvailable = 0; |
| |
| return returnValue; |
| } |
| |
| static int amsAlg_als_initAlg(amsAlsContext_t *ctx, amsAlsInitData_t *initData); |
| static int amsAlg_als_getAlgInfo(amsAlsAlgoInfo_t *info); |
| static int amsAlg_als_processData(amsAlsContext_t *ctx, amsAlsDataSet_t *inputData); |
| #ifdef TCS3408_USE_SMUX |
| static int ams_smux_set(ams_deviceCtx_t *ctx); |
| #endif |
| |
| static int ccb_alsInit(void *dcbCtx, ams_ccb_als_init_t *initData) |
| { |
| ams_deviceCtx_t *ctx = (ams_deviceCtx_t *)dcbCtx; |
| ams_ccb_als_ctx_t *ccbCtx = &ctx->ccbAlsCtx; |
| amsAlsInitData_t initAlsData; |
| amsAlsAlgoInfo_t infoAls; |
| int ret = 0; |
| |
| ALS_dbg("%s - ccb_proximityInit\n", __func__); |
| |
| if (initData) |
| memcpy(&ccbCtx->initData, initData, sizeof(ams_ccb_als_init_t)); |
| else |
| ccbCtx->initData.calibrate = false; |
| |
| initAlsData.adaptive = false; |
| initAlsData.irRejection = false; |
| initAlsData.gain = ccbCtx->initData.configData.gain; |
| #ifdef AMS_ALS_GAIN_V2 |
| ctx->alsGain = initAlsData.gain; |
| #endif |
| initAlsData.time_us = ccbCtx->initData.configData.uSecTime; |
| initAlsData.calibration.adcMaxCount = ccbCtx->initData.calibrationData.adcMaxCount; |
| initAlsData.calibration.calibrationFactor = ccbCtx->initData.calibrationData.calibrationFactor; |
| initAlsData.calibration.Time_base = ccbCtx->initData.calibrationData.Time_base; |
| initAlsData.calibration.thresholdLow = ccbCtx->initData.calibrationData.thresholdLow; |
| initAlsData.calibration.thresholdHigh = ccbCtx->initData.calibrationData.thresholdHigh; |
| //initAlsData.calibration.calibrationFactor = ccbCtx->initData.calibrationData.calibrationFactor; |
| amsAlg_als_getAlgInfo(&infoAls); |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_3407 |
| /* get gain from HW register if so configured */ |
| if (ctx->ccbAlsCtx.initData.autoGain) { |
| uint32_t scaledGain; |
| uint8_t gain; |
| |
| AMS_GET_ALS_GAIN(scaledGain, gain, ret); |
| if (ret < 0) { |
| ALS_err("%s - failed to AMS_GET_ALS_GAIN\n", __func__); |
| return ret; |
| } |
| ctx->ccbAlsCtx.ctxAlgAls.gain = scaledGain; |
| } |
| #endif |
| amsAlg_als_initAlg(&ccbCtx->ctxAlgAls, &initAlsData); |
| |
| AMS_SET_ALS_TIME(ccbCtx->initData.configData.uSecTime, ret); |
| if (ret < 0) { |
| ALS_err("%s - failed to AMS_SET_ALS_TIME\n", __func__); |
| return ret; |
| } |
| AMS_SET_ALS_PERS(0x01, ret); |
| if (ret < 0) { |
| ALS_err("%s - failed to AMS_SET_ALS_PERS\n", __func__); |
| return ret; |
| } |
| AMS_SET_ALS_GAIN(ctx->ccbAlsCtx.initData.configData.gain, ret); |
| if (ret < 0) { |
| ALS_err("%s - failed to AMS_SET_ALS_GAIN\n", __func__); |
| return ret; |
| } |
| |
| ccbCtx->shadowAiltReg = 0xffff; |
| ccbCtx->shadowAihtReg = 0; |
| AMS_SET_ALS_THRS_LOW(ccbCtx->shadowAiltReg, ret); |
| if (ret < 0) { |
| ALS_err("%s - failed to AMS_SET_ALS_THRS_LOW\n", __func__); |
| return ret; |
| } |
| AMS_SET_ALS_THRS_HIGH(ccbCtx->shadowAihtReg, ret); |
| if (ret < 0) { |
| ALS_err("%s - failed to AMS_SET_ALS_THRS_HIGH\n", __func__); |
| return ret; |
| } |
| |
| ccbCtx->state = AMS_CCB_ALS_RGB; |
| |
| return ret; |
| } |
| |
| void ccb_alsInit_FIFO(void *dcbCtx, ams_ccb_als_init_t *initData) |
| { |
| ams_deviceCtx_t *ctx = (ams_deviceCtx_t *)dcbCtx; |
| ams_ccb_als_ctx_t *ccbCtx = &ctx->ccbAlsCtx; |
| amsAlsInitData_t initAlsData; |
| int ret = 0; |
| |
| if (initData) |
| memcpy(&ccbCtx->initData, initData, sizeof(ams_ccb_als_init_t)); |
| else |
| ccbCtx->initData.calibrate = false; |
| |
| initAlsData.gain = ccbCtx->initData.configData.gain; |
| #ifdef AMS_ALS_GAIN_V2 |
| ctx->alsGain = initAlsData.gain; |
| #endif |
| initAlsData.time_us = ccbCtx->initData.configData.uSecTime; |
| initAlsData.calibration.adcMaxCount = ccbCtx->initData.calibrationData.adcMaxCount; |
| initAlsData.calibration.calibrationFactor = ccbCtx->initData.calibrationData.calibrationFactor; |
| initAlsData.calibration.Time_base = ccbCtx->initData.calibrationData.Time_base; |
| initAlsData.calibration.thresholdLow = ccbCtx->initData.calibrationData.thresholdLow; |
| initAlsData.calibration.thresholdHigh = ccbCtx->initData.calibrationData.thresholdHigh; |
| initAlsData.calibration.Wbc = ccbCtx->initData.calibrationData.Wbc; |
| |
| ALS_dbg("%s - ccb_alsInit time %d, gain value %d , autogain %d\n", |
| __func__, initAlsData.time_us, initAlsData.gain, ctx->ccbAlsCtx.initData.autoGain); |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FIFO |
| ALS_dbg("%s - !!!!start %s!!!!!", __func__, __func__); |
| |
| //ams_setByte (ctx->portHndl, DEVREG_CFG6, 0x10); |
| //ams_setByte(ctx->portHndl, DEVREG_ALS_CHANNEL_CTRL, 0x1e); |
| /* SMUX write command */ |
| ams_setByte(ctx->portHndl, DEVREG_CONTROL, 0x02); //FIFO Buffer , FINT, FIFO_OV, FIFO_LVL all clear |
| |
| AMS_SET_ALS_GAIN(1000, ret); |
| |
| //ams_setByte(ctx->portHndl, DEVREG_CFG4, 0x80); |
| ams_setByte(ctx->portHndl, DEVREG_FD_CFG0, 0x80); |
| ams_setByte(ctx->portHndl, DEVREG_AZ_CONFIG, 0x00); |
| |
| //ams_setByte(ctx->portHndl, DEVREG_ATIME, 0x00); |
| ams_setByte(ctx->portHndl, DEVREG_WTIME, 0x00); |
| ams_setByte(ctx->portHndl, DEVREG_FIFO_MAP, 0x40); // ADATA5, flikcer |
| ams_setByte(ctx->portHndl, DEVREG_CFG8, 0xC8); //FIFO_THR:16, FD_AGC_DISABLE:YES |
| //ams_setField(ctx->portHndl, DEVREG_PCFG1, 0x08, 0x08); |
| |
| // set 150us |
| //ams_setField(ctx->portHndl, DEVREG_PCFG1, 0x08, 0x08); |
| |
| AMS_SET_ALS_TIME(0, ret); |
| ccbCtx->initData.configData.uSecTime = 2532; |
| AMS_SET_ALS_STEP_TIME(ccbCtx->initData.configData.uSecTime, ret); /*2.78msec*/ |
| |
| AMS_SET_ALS_PERS(0x00, ret); |
| /* force interrupt */ |
| ccbCtx->shadowAiltReg = 0xffff; |
| ccbCtx->shadowAihtReg = 0; |
| AMS_SET_ALS_THRS_LOW(ccbCtx->shadowAiltReg, ret); |
| AMS_SET_ALS_THRS_HIGH(ccbCtx->shadowAihtReg, ret); |
| |
| //ccbCtx->state = AMS_CCB_ALS_INIT; |
| #else |
| AMS_SET_ALS_TIME(ccbCtx->initData.configData.uSecTime, ret); |
| AMS_SET_ALS_PERS(0x01, ret); |
| AMS_SET_ALS_GAIN(ctx->ccbAlsCtx.initData.configData.gain, ret); |
| |
| ccbCtx->shadowAiltReg = 0xffff; |
| ccbCtx->shadowAihtReg = 0; |
| AMS_SET_ALS_THRS_LOW(ccbCtx->shadowAiltReg, ret); |
| AMS_SET_ALS_THRS_HIGH(ccbCtx->shadowAihtReg, ret); |
| |
| ccbCtx->state = AMS_CCB_ALS_RGB; |
| #endif |
| } |
| |
| static void ccb_alsInfo(ams_ccb_als_info_t *infoData) |
| { |
| if (infoData != NULL) { |
| infoData->algName = "ALS"; |
| infoData->contextMemSize = sizeof(ams_ccb_als_ctx_t); |
| infoData->scratchMemSize = 0; |
| infoData->defaultCalibrationData.calibrationFactor = 1000; |
| //infoData->defaultCalibrationData.luxTarget = CONFIG_ALS_CAL_TARGET; |
| //infoData->defaultCalibrationData.luxTargetError = CONFIG_ALS_CAL_TARGET_TOLERANCE; |
| als_getDefaultCalibrationData(&infoData->defaultCalibrationData); |
| } |
| } |
| |
| static void ccb_alsSetConfig(void *dcbCtx, ams_ccb_als_config_t *configData) |
| { |
| ams_ccb_als_ctx_t *ccbCtx = &((ams_deviceCtx_t *)dcbCtx)->ccbAlsCtx; |
| |
| ccbCtx->initData.configData.threshold = configData->threshold; |
| } |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FIFO |
| static bool ccb_FlickerFIFOEvent(void *dcbCtx, ams_ccb_als_dataSet_t *alsData) |
| { |
| ams_deviceCtx_t *ctx = (ams_deviceCtx_t *)dcbCtx; |
| ams_ccb_als_ctx_t *ccbCtx = &((ams_deviceCtx_t *)dcbCtx)->ccbAlsCtx; |
| |
| //ams_flicker_ctx_t *flickerCtx = (ams_flicker_ctx_t *)&ctx->flickerCtx; |
| int i = 0; |
| uint8_t fifo_lvl; |
| uint8_t fifo_ov; |
| uint16_t fifo_size, quotient, remainder; |
| uint16_t data = 0; |
| static adcDataSet_t adcData; |
| int ret = 0; |
| |
| //static uint8_t fifo_buffer[256]={0,}; |
| |
| ams_getByte(ctx->portHndl, DEVREG_FIFO_STATUS, &fifo_lvl); //current fifo count |
| ams_getByte(ctx->portHndl, DEVREG_STATUS4, &fifo_ov); |
| |
| #ifdef FLICKER_FIFO_THR |
| fifo_size = FLICKER_FIFO_THR * BYTE; |
| #else |
| fifo_size = fifo_lvl * BYTE; |
| #endif |
| quotient = fifo_size / 32; |
| remainder = fifo_size % 32; |
| |
| ALS_dbg("%s - FIFO LVL = %d, FIFO_OV = %d\n", __func__, fifo_lvl, fifo_ov); |
| |
| if (fifo_lvl >= FLICKER_FIFO_THR) { //>128 |
| if (quotient == 0) {// fifo size is less than 32 , reading remainder |
| ams_getBuf(ctx->portHndl, DEVREG_FDATAL, (uint8_t *)&tcs3407_data->fifodata[0], remainder); |
| } else { |
| for (i = 0; i < quotient; i++) |
| ams_getBuf(ctx->portHndl, DEVREG_FDATAL, (uint8_t *)&tcs3407_data->fifodata[i * 32], 32); |
| |
| if (remainder != 0) |
| ams_getBuf(ctx->portHndl, DEVREG_FDATAL, (uint8_t *)&tcs3407_data->fifodata[i * 32], remainder); |
| } |
| ALS_dbg("%s - ~~~~FIFO full~~~~level %d overflow %d size %d\n", __func__, fifo_lvl, fifo_ov, fifo_size); |
| |
| for (i = 0; i < fifo_size; i += 2) { // read 256 byte |
| data = (u16)((tcs3407_data->fifodata[i] << 0) | (tcs3407_data->fifodata[i + 1] << 8)); |
| tcs3407_data->flicker_data[tcs3407_data->flicker_data_cnt++] = data; |
| |
| ALS_dbg("%s - data[%d] = %u, data_cnt = %d\n", __func__, i, data, tcs3407_data->flicker_data_cnt); |
| |
| if (tcs3407_data->flicker_data_cnt >= 200) { |
| tcs3407_data->flicker_data_cnt = 0; |
| input_report_rel(tcs3407_data->als_input_dev, REL_RZ, (FLICKER_FIFO_READ + 1)); |
| input_sync(tcs3407_data->als_input_dev); |
| break; |
| } |
| } |
| |
| input_report_rel(tcs3407_data->als_input_dev, REL_RY, data + 1); |
| input_sync(tcs3407_data->als_input_dev); |
| |
| ams_setByte(ctx->portHndl, DEVREG_CONTROL, 0x02); //FIFO Buffer , FINT, FIFO_OV, FIFO_LVL all clear |
| } |
| |
| #ifdef ALS_AUTOGAIN |
| // Do S/W AGC |
| AMS_ALS_GET_CRGB_W(&adcData, ret); |
| { |
| uint64_t temp; |
| uint32_t recommendedGain; |
| uint32_t max_count; |
| uint32_t adcObjective; |
| |
| max_count = (1024 * ccbCtx->initData.configData.uSecTime) / 2780; |
| //uint32_t adcObjective = ctx->ccbAlsCtx.ctxAlgAls.saturation * 128; |
| adcObjective = max_count * 128; |
| //adcObjective /= 160; /* about 80% (128 / 160) */ |
| adcObjective /= 400; /* about 80% (128 / 160) */ |
| |
| if (adcData.AdcClear == 0) { |
| /* to avoid divide by zero */ |
| adcData.AdcClear = 1; |
| } |
| temp = adcObjective * 2048; /* 2048 to avoid floating point operation later on */ |
| do_div(temp, adcData.AdcClear); |
| |
| temp *= ctx->ccbAlsCtx.ctxAlgAls.gain; |
| do_div(temp, 2048); |
| |
| recommendedGain = temp & 0xffffffff; |
| |
| ALS_dbg("%s - Clear ADC : %d, Red ADC : %d, Green ADC : %d, Blue ADC : %d\n", |
| __func__, adcData.AdcClear, adcData.AdcRed, adcData.AdcGreen, adcData.AdcBlue); |
| |
| ALS_dbg("%s - AMS_CCB_ALS_AUTOGAIN: sat=%u, objctv=%u, cur=%u, rec=%u", |
| __func__, |
| ctx->ccbAlsCtx.ctxAlgAls.saturation, |
| adcObjective, |
| ctx->ccbAlsCtx.ctxAlgAls.gain, |
| recommendedGain); |
| |
| recommendedGain = alsGainToReg(recommendedGain); |
| recommendedGain = alsGain_conversion[recommendedGain]; |
| if (recommendedGain != ctx->ccbAlsCtx.ctxAlgAls.gain) { |
| ALS_dbg("%s - gain chg to: %u\n", __func__, recommendedGain); |
| ctx->ccbAlsCtx.ctxAlgAls.gain = recommendedGain; |
| //ccbCtx->alg_config.gain = recommendedGain/1000; |
| AMS_DISABLE_ALS(ret); |
| |
| ctx->shadowIntenabReg &= ~(FIEN|ASIEN_FDSIEN); |
| ams_setField(ctx->portHndl, DEVREG_INTENAB, HIGH, ctx->shadowIntenabReg);/*disable*/ |
| |
| AMS_SET_ALS_GAIN(ctx->ccbAlsCtx.ctxAlgAls.gain, ret); |
| |
| ams_setByte(ctx->portHndl, DEVREG_CONTROL, 0x02); //FIFO Buffer , FINT, FIFO_OV, FIFO_LVL all clear |
| |
| ctx->shadowIntenabReg = (FIEN|ASIEN_FDSIEN); |
| ams_setField(ctx->portHndl, DEVREG_INTENAB, HIGH, ctx->shadowIntenabReg);/*enable*/ |
| |
| AMS_REENABLE_ALS(ret); |
| } else |
| ALS_dbg("%s - no chg, gain %u\n", __func__, ctx->ccbAlsCtx.ctxAlgAls.gain); |
| } |
| #endif |
| |
| //AMS_PORT_log_1("Last ccb_alsHd: buffer count %d\n" , buffer_count); |
| return false; |
| } |
| #endif |
| |
| static int ccb_alsHandle(void *dcbCtx, ams_ccb_als_dataSet_t *alsData) |
| { |
| ams_deviceCtx_t *ctx = (ams_deviceCtx_t *)dcbCtx; |
| ams_ccb_als_ctx_t *ccbCtx = &((ams_deviceCtx_t *)dcbCtx)->ccbAlsCtx; |
| amsAlsDataSet_t inputDataAls; |
| static adcDataSet_t adcData; /* QC - is this really needed? */ |
| int ret = 0; |
| /* |
| * uint64_t temp; |
| * uint32_t recommendedGain; |
| * uint32_t adcObjective; |
| * |
| * adcDataSet_t tmpAdcData; |
| * amsAlsContext_t tmp; |
| * amsAlsDataSet_t tmpInputDataAls; |
| */ |
| //ALS_info(AMS_DEBUG, "ccb_alsHandle: case = %d\n", ccbCtx->state); |
| #if 0 |
| AMS_ALS_GET_CRGB_W((uint8_t *)&adcData); |
| AMS_PORT_LOG_CRGB_W(adcData); |
| #endif |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_3407 |
| /* get gain from HW register if so configured */ |
| if (ctx->ccbAlsCtx.initData.autoGain) { |
| uint32_t scaledGain; |
| #ifdef AMS_ALS_GAIN_V2 |
| uint8_t gainLevel; |
| |
| AMS_GET_ALS_GAIN(scaledGain, gainLevel, ret); |
| if (ret < 0) { |
| ALS_err("%s - failed to AMS_GET_ALS_GAIN\n", __func__); |
| return ret; |
| } |
| |
| #else |
| uint8_t gain; |
| |
| AMS_GET_ALS_GAIN(scaledGain, gain, ret); |
| if (ret < 0) { |
| ALS_err("%s - failed to AMS_GET_ALS_GAIN\n", __func__); |
| return ret; |
| } |
| |
| #endif |
| ctx->ccbAlsCtx.ctxAlgAls.gain = scaledGain; |
| } |
| #endif |
| |
| switch (ccbCtx->state) { |
| case AMS_CCB_ALS_INIT: |
| AMS_DISABLE_ALS(ret); |
| if (ret < 0) { |
| ALS_err("%s - failed to AMS_DISABLE_ALS\n", __func__); |
| return ret; |
| } |
| AMS_SET_ALS_TIME(ccbCtx->initData.configData.uSecTime, ret); |
| if (ret < 0) { |
| ALS_err("%s - failed to AMS_SET_ALS_TIME\n", __func__); |
| return ret; |
| } |
| AMS_SET_ALS_PERS(0x01, ret); |
| if (ret < 0) { |
| ALS_err("%s - failed to AMS_SET_ALS_PERS\n", __func__); |
| return ret; |
| } |
| |
| AMS_SET_ALS_GAIN(ctx->ccbAlsCtx.initData.configData.gain, ret); |
| if (ret < 0) { |
| ALS_err("%s - failed to AMS_SET_ALS_GAIN\n", __func__); |
| return ret; |
| } |
| /* force interrupt */ |
| ccbCtx->shadowAiltReg = 0xffff; |
| ccbCtx->shadowAihtReg = 0; |
| AMS_SET_ALS_THRS_LOW(ccbCtx->shadowAiltReg, ret); |
| if (ret < 0) { |
| ALS_err("%s - failed to AMS_SET_ALS_THRS_LOW\n", __func__); |
| return ret; |
| } |
| AMS_SET_ALS_THRS_HIGH(ccbCtx->shadowAihtReg, ret); |
| if (ret < 0) { |
| ALS_err("%s - failed to AMS_SET_ALS_THRS_HIGH\n", __func__); |
| return ret; |
| } |
| ccbCtx->state = AMS_CCB_ALS_RGB; |
| AMS_REENABLE_ALS(ret); |
| if (ret < 0) { |
| ALS_err("%s - failed to AMS_REENABLE_ALS\n", __func__); |
| return ret; |
| } |
| break; |
| case AMS_CCB_ALS_RGB: /* state to measure RGB */ |
| #ifdef HAVE_OPTION__ALWAYS_READ |
| if ((alsData->statusReg & (AINT)) || ctx->alwaysReadAls) |
| #else |
| if (alsData->statusReg & (AINT)) |
| #endif |
| { |
| AMS_ALS_GET_CRGB_W(&adcData, ret); |
| if (ret < 0) { |
| ALS_err("%s - failed to AMS_ALS_GET_CRGB_W\n", __func__); |
| return ret; |
| } |
| inputDataAls.status = ALS_STATUS_RDY; |
| inputDataAls.datasetArray = (alsData_t *)&adcData; |
| AMS_PORT_LOG_CRGB_W(adcData); |
| |
| /* if (ctx->ccbAlsCtx.ctxAlgAls.previousGain != |
| * ctx->ccbAlsCtx.ctxAlgAls.gain) { |
| * AMS_DISABLE_ALS(); |
| * ALS_info(AMS_DEBUG, "ccb_alsHandle: ALS Disalbe to Enable\n"); |
| * AMS_REENABLE_ALS(); |
| * } |
| */ |
| |
| amsAlg_als_processData(&ctx->ccbAlsCtx.ctxAlgAls, &inputDataAls); |
| |
| if (ctx->mode & MODE_ALS_LUX) |
| ctx->updateAvailable |= (1 << AMS_AMBIENT_SENSOR); |
| |
| ccbCtx->state = AMS_CCB_ALS_RGB; |
| } |
| break; |
| |
| default: |
| ccbCtx->state = AMS_CCB_ALS_RGB; |
| break; |
| } |
| return false; |
| } |
| |
| static void ccb_alsGetResult(void *dcbCtx, ams_ccb_als_result_t *exportData) |
| { |
| ams_ccb_als_ctx_t *ccbCtx = &((ams_deviceCtx_t *)dcbCtx)->ccbAlsCtx; |
| |
| /* export data */ |
| exportData->clear = ccbCtx->ctxAlgAls.results.irrClear; |
| exportData->red = ccbCtx->ctxAlgAls.results.irrRed; |
| exportData->green = ccbCtx->ctxAlgAls.results.irrGreen; |
| exportData->blue = ccbCtx->ctxAlgAls.results.irrBlue; |
| exportData->ir = ccbCtx->ctxAlgAls.results.irrWideband; |
| exportData->time_us = ccbCtx->ctxAlgAls.time_us; |
| exportData->gain = ccbCtx->ctxAlgAls.gain; |
| exportData->rawClear = ccbCtx->ctxAlgAls.results.rawClear; |
| exportData->rawRed = ccbCtx->ctxAlgAls.results.rawRed; |
| exportData->rawGreen = ccbCtx->ctxAlgAls.results.rawGreen; |
| exportData->rawBlue = ccbCtx->ctxAlgAls.results.rawBlue; |
| exportData->rawWideband = ccbCtx->ctxAlgAls.results.rawWideband; |
| } |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_ALS_CCB |
| static bool _3407_alsSetThreshold(ams_deviceCtx_t *ctx, int32_t threshold) |
| { |
| ams_ccb_als_config_t configData; |
| |
| configData.threshold = threshold; |
| ccb_alsSetConfig(ctx, &configData); |
| |
| return false; |
| } |
| #endif |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FLICKER |
| #ifndef CONFIG_AMS_OPTICAL_SENSOR_FIFO |
| static int _3407_flickerInit(ams_deviceCtx_t *ctx); |
| #endif |
| #endif |
| |
| static int ams_deviceSetConfig(ams_deviceCtx_t *ctx, ams_configureFeature_t feature, deviceConfigOptions_t option, uint32_t data) |
| { |
| int ret = 0; |
| |
| if (feature == AMS_CONFIG_ALS_LUX) { |
| ALS_dbg("%s - ams_configureFeature_t AMS_CONFIG_ALS_LUX\n", __func__); |
| switch (option) { |
| case AMS_CONFIG_ENABLE: /* ON / OFF */ |
| ALS_info("%s - deviceConfigOptions_t AMS_CONFIG_ENABLE(%u)\n", __func__, data); |
| ALS_info("%s - current mode %d\n", __func__, ctx->mode); |
| if (data == 0) { |
| if (ctx->mode == MODE_ALS_LUX) { |
| /* if no other active features, turn off device */ |
| ctx->shadowEnableReg = 0; |
| ctx->shadowIntenabReg = 0; |
| ctx->mode = MODE_OFF; |
| } else { |
| if ((ctx->mode & MODE_ALS_ALL) == MODE_ALS_LUX) { |
| ctx->shadowEnableReg &= ~MASK_AEN; |
| ctx->shadowIntenabReg &= ~MASK_ALS_INT_ALL; |
| } |
| ctx->mode &= ~(MODE_ALS_LUX); |
| } |
| } else { |
| if ((ctx->mode & MODE_ALS_ALL) == 0) { |
| ret = ccb_alsInit(ctx, &ctx->ccbAlsCtx.initData); |
| if (ret < 0) { |
| ALS_err("%s - failed to ccb_alsInit\n", __func__); |
| return ret; |
| } |
| ctx->shadowEnableReg |= (AEN | PON); |
| ctx->shadowIntenabReg |= AIEN; |
| } else { |
| /* force interrupt */ |
| ret = ams_setWord(ctx->portHndl, DEVREG_AIHTL, 0x00); |
| if (ret < 0) { |
| ALS_err("%s - failed to set DEVREG_AIHTL\n", __func__); |
| return ret; |
| } |
| } |
| ctx->mode |= MODE_ALS_LUX; |
| } |
| break; |
| case AMS_CONFIG_THRESHOLD: /* set threshold */ |
| ALS_info("%s - deviceConfigOptions_t AMS_CONFIG_THRESHOLD\n", __func__); |
| ALS_info("%s - data %d\n", __func__, data); |
| _3407_alsSetThreshold(ctx, data); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| if (feature == AMS_CONFIG_FLICKER) { |
| ALS_dbg("%s - ams_configureFeature_t AMS_CONFIG_FLICKER\n", __func__); |
| switch (option) { |
| case AMS_CONFIG_ENABLE: /* power on */ |
| ALS_info("%s - deviceConfigOptions_t AMS_CONFIG_ENABLE(%u)\n", __func__, data); |
| ALS_info("%s - current mode %d\n", __func__, ctx->mode); |
| if (data == 0) { |
| ret = ams_setField(ctx->portHndl, DEVREG_CFG9, LOW, MASK_SIEN_FD); |
| if (ret < 0) { |
| ALS_err("%s - failed to set DEVREG_CFG9\n", __func__); |
| return ret; |
| } |
| if (ctx->mode == MODE_FLICKER) { |
| /* if no other active features, turn off device */ |
| ctx->shadowEnableReg = 0; |
| ctx->shadowIntenabReg = 0; |
| ctx->mode = MODE_OFF; |
| ams_setByte(ctx->portHndl, DEVREG_CONTROL, 0x02); //20180828 FIFO Buffer , FINT, FIFO_OV, FIFO_LVL all clear |
| } else { |
| ctx->mode &= ~MODE_FLICKER; |
| ctx->shadowEnableReg &= ~(FDEN); |
| |
| if (!(ctx->mode & MODE_IRBEAM)) |
| ctx->shadowIntenabReg &= ~(SIEN); |
| } |
| } else { |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FIFO |
| ctx->shadowEnableReg |= (AEN | PON); |
| ctx->shadowIntenabReg |= FIEN; |
| #else |
| ctx->shadowEnableReg |= (FDEN | PON); |
| ctx->shadowIntenabReg |= SIEN; |
| ret = ams_setField(ctx->portHndl, DEVREG_CFG9, SIEN_FD, MASK_SIEN_FD); |
| if (ret < 0) { |
| ALS_err("%s - failed to set DEVREG_CFG9\n", __func__); |
| return ret; |
| } |
| #endif |
| ctx->mode |= MODE_FLICKER; |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FIFO |
| ccb_alsInit_FIFO(ctx, &ctx->ccbAlsCtx.initData); |
| #else |
| ret = _3407_flickerInit(ctx); |
| if (ret < 0) { |
| ALS_err("%s - failed to _3407_flickerInit\n", __func__); |
| return ret; |
| } |
| //_3407_flickerEnableWAPre(ctx,ctx->shadowEnableReg,ctx->shadowIntenabReg); |
| #endif |
| } |
| break; |
| case AMS_CONFIG_THRESHOLD: /* set threshold */ |
| ALS_info("%s - deviceConfigOptions_t AMS_CONFIG_THRESHOLD\n", __func__); |
| /* TODO?: set FD_COMPARE value? */ |
| break; |
| default: |
| break; |
| } |
| } |
| ret = ams_setByte(ctx->portHndl, DEVREG_ENABLE, ctx->shadowEnableReg); |
| if (ret < 0) { |
| ALS_err("%s - failed to set DEVREG_ENABLE\n", __func__); |
| return ret; |
| } |
| ret = ams_setByte(ctx->portHndl, DEVREG_INTENAB, ctx->shadowIntenabReg); |
| if (ret < 0) { |
| ALS_err("%s - failed to set DEVREG_INTENAB\n", __func__); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| #define STAR_ATIME 50 //50 msec |
| #define STAR_D_FACTOR 2266 |
| |
| static void als_update_statics(amsAlsContext_t *ctx) |
| { |
| uint64_t tempCpl; |
| uint64_t tempTime_us = ctx->time_us; |
| uint64_t tempGain = ctx->gain; |
| |
| /* test for the potential of overflowing */ |
| uint32_t maxOverFlow; |
| #ifdef __KERNEL__ |
| u64 tmpTerm1; |
| u64 tmpTerm2; |
| #endif |
| #ifdef __KERNEL__ |
| u64 tmp = ULLONG_MAX; |
| |
| do_div(tmp, ctx->time_us); |
| |
| maxOverFlow = (uint32_t)tmp; |
| #else |
| maxOverFlow = (uint64_t)ULLONG_MAX / ctx->time_us; |
| #endif |
| |
| if (maxOverFlow < ctx->gain) { |
| /* TODO: need to find use-case to test */ |
| #ifdef __KERNEL__ |
| tmpTerm1 = tempTime_us; |
| do_div(tmpTerm1, 2); |
| tmpTerm2 = tempGain; |
| do_div(tmpTerm2, 2); |
| tempCpl = tmpTerm1 * tmpTerm2; |
| do_div(tempCpl, (AMS_ALS_GAIN_FACTOR/4)); |
| #else |
| tempCpl = ((tempTime_us / 2) * (tempGain / 2)) / (AMS_ALS_GAIN_FACTOR/4); |
| #endif |
| |
| } else { |
| #ifdef __KERNEL__ |
| tempCpl = (tempTime_us * tempGain); |
| do_div(tempCpl, AMS_ALS_GAIN_FACTOR); |
| #else |
| tempCpl = (tempTime_us * tempGain) / AMS_ALS_GAIN_FACTOR; |
| #endif |
| } |
| if (tempCpl > (uint32_t)ULONG_MAX) { |
| /* if we get here, we have a problem */ |
| //AMS_PORT_log_Msg_1(AMS_ERROR, "als_update_statics: overflow, setting cpl=%u\n", (uint32_t)ULONG_MAX); |
| tempCpl = (uint32_t)ULONG_MAX; |
| } |
| |
| #if 0//def __KERNEL__ |
| /*UVIR CPL refer as a const value with STAR Proejct */ |
| ctx->uvir_cpl = (tempTime_us * tempGain); |
| do_div(ctx->uvir_cpl, AMS_ALS_GAIN_FACTOR); |
| do_div(ctx->uvir_cpl, STAR_D_FACTOR); |
| #else |
| /*UVIR CPL refer as a const value with STAR Proejct */ |
| ctx->uvir_cpl = (tempTime_us * tempGain) / AMS_ALS_GAIN_FACTOR; |
| ctx->uvir_cpl = ctx->uvir_cpl / STAR_D_FACTOR; |
| #endif |
| ctx->previousGain = ctx->gain; |
| |
| // AMS_PORT_log_Msg_4(AMS_DEBUG, "als_update_statics: time=%d, gain=%d, dFactor=%d => cpl=%u\n", ctx->time_us, ctx->gain, ctx->calibration.D_factor, ctx->cpl); |
| } |
| |
| static int amsAlg_als_setConfig(amsAlsContext_t *ctx, amsAlsConf_t *inputData) |
| { |
| int ret = 0; |
| |
| if (inputData != NULL) { |
| ctx->gain = inputData->gain; |
| ctx->time_us = inputData->time_us; |
| } |
| //als_update_statics(ctx); |
| |
| return ret; |
| } |
| |
| /* |
| * getConfig: is used to quarry the algorithm's configuration |
| */ |
| static int amsAlg_als_getConfig(amsAlsContext_t *ctx, amsAlsConf_t *outputData) |
| { |
| int ret = 0; |
| |
| outputData->gain = ctx->gain; |
| outputData->time_us = ctx->time_us; |
| |
| return ret; |
| } |
| |
| static int amsAlg_als_getResult(amsAlsContext_t *ctx, amsAlsResult_t *outData) |
| { |
| int ret = 0; |
| |
| outData->rawClear = ctx->results.rawClear; |
| outData->rawRed = ctx->results.rawRed; |
| outData->rawGreen = ctx->results.rawGreen; |
| outData->rawBlue = ctx->results.rawBlue; |
| outData->irrBlue = ctx->results.irrBlue; |
| outData->irrClear = ctx->results.irrClear; |
| outData->irrGreen = ctx->results.irrGreen; |
| outData->irrRed = ctx->results.irrRed; |
| outData->irrWideband = ctx->results.irrWideband; |
| outData->mLux_ave = ctx->results.mLux_ave / AMS_LUX_AVERAGE_COUNT; |
| outData->IR = ctx->results.IR; |
| outData->CCT = ctx->results.CCT; |
| outData->adaptive = ctx->results.adaptive; |
| |
| if (ctx->notStableMeasurement) |
| ctx->notStableMeasurement = false; |
| |
| outData->mLux = ctx->results.mLux; |
| |
| return ret; |
| } |
| |
| static int amsAlg_als_initAlg(amsAlsContext_t *ctx, amsAlsInitData_t *initData) |
| { |
| int ret = 0; |
| |
| memset(ctx, 0, sizeof(amsAlsContext_t)); |
| |
| if (initData != NULL) { |
| ctx->calibration.Time_base = initData->calibration.Time_base; |
| ctx->calibration.thresholdLow = initData->calibration.thresholdLow; |
| ctx->calibration.thresholdHigh = initData->calibration.thresholdHigh; |
| ctx->calibration.calibrationFactor = initData->calibration.calibrationFactor; |
| } |
| |
| if (initData != NULL) { |
| ctx->gain = initData->gain; |
| ctx->time_us = initData->time_us; |
| ctx->adaptive = initData->adaptive; |
| } else { |
| ALS_dbg("error: initData == NULL\n"); |
| } |
| |
| als_update_statics(ctx); |
| return ret; |
| } |
| |
| static int amsAlg_als_getAlgInfo(amsAlsAlgoInfo_t *info) |
| { |
| int ret = 0; |
| |
| info->algName = "AMS_ALS"; |
| info->contextMemSize = sizeof(amsAlsContext_t); |
| info->scratchMemSize = 0; |
| |
| info->initAlg = &amsAlg_als_initAlg; |
| info->processData = &amsAlg_als_processData; |
| info->getResult = &amsAlg_als_getResult; |
| info->setConfig = &amsAlg_als_setConfig; |
| info->getConfig = &amsAlg_als_getConfig; |
| |
| return ret; |
| } |
| |
| #ifdef TCS3408_USE_SMUX |
| static int ams_smux_set(ams_deviceCtx_t *ctx) |
| { |
| int ret = 0; |
| |
| if ((ctx->deviceId == AMS_TCS3408) || (ctx->deviceId == AMS_TCS3408_UNTRIM)) { |
| ams_setByte(ctx->portHndl, DEVREG_ENABLE, 0x00); //sensor off |
| ams_setByte(ctx->portHndl, DEVREG_ENABLE, 0x01); //only PON |
| |
| /* SMUX read command from ram*/ |
| AMS_READ_S_MUX(); |
| ams_setByte(ctx->portHndl, DEVREG_ENABLE, 0x11);//PON + SMUXEN excute |
| udelay(1000); //Now 0x80 needs to be read back until SMUXEN has been cleared , wait 1msec |
| ams_setByte(ctx->portHndl,DEVREG_SMUX13_PRX_TO_FLICKER,0x66); // 0x66 : ficker+flicker, 0x76: only one flikcer, 0x00 flicker off |
| /* SMUX write command */ |
| AMS_WRITE_S_MUX();//SMUX Write from RAM to chain |
| ams_setByte(ctx->portHndl, DEVREG_ENABLE, 0x11);//PON + SMUXEN |
| udelay(1000); //Now 0x80 needs to be read back until SMUXEN has been cleared , wait 1msec |
| //ams_setByte(ctx->portHndl, DEVREG_ENABLE, 0x11); //PON + SMUXEN |
| } |
| |
| return ret; |
| } |
| #endif |
| |
| static int tcs3407_print_reg_status(void) |
| { |
| int reg, err; |
| u8 recvData; |
| |
| for (reg = 0; reg < DEVREG_REG_MAX; reg++) { |
| err = tcs3407_read_reg(tcs3407_data, deviceRegisterDefinition[reg].address, &recvData, 1); |
| if (err != 0) { |
| ALS_err("%s - error reading 0x%02x err:%d\n", |
| __func__, reg, err); |
| } else { |
| ALS_dbg("%s - 0x%02x = 0x%02x\n", |
| __func__, deviceRegisterDefinition[reg].address, recvData); |
| } |
| } |
| return 0; |
| } |
| |
| static int tcs3407_set_sampling_rate(u32 sampling_period_ns) |
| { |
| ALS_dbg("%s - sensor_info_data not support\n", __func__); |
| |
| return 0; |
| } |
| |
| static void tcs3407_irq_set_state(struct tcs3407_device_data *data, int irq_enable) |
| { |
| ALS_dbg("%s - irq_enable : %d, irq_state : %d\n", |
| __func__, irq_enable, data->irq_state); |
| |
| if (irq_enable) { |
| if (data->irq_state++ == 0) |
| enable_irq(data->dev_irq); |
| } else { |
| if (data->irq_state == 0) |
| return; |
| if (--data->irq_state <= 0) { |
| disable_irq(data->dev_irq); |
| data->irq_state = 0; |
| } |
| } |
| } |
| |
| static int tcs3407_power_ctrl(struct tcs3407_device_data *data, int onoff) |
| { |
| int rc = 0; |
| static int i2c_1p8_enable; |
| |
| struct regulator *regulator_vdd_1p8 = NULL; |
| struct regulator *regulator_i2c_1p8 = NULL; |
| |
| ALS_dbg("%s - onoff : %d, state : %d\n", |
| __func__, onoff, data->regulator_state); |
| |
| if (onoff == PWR_ON) { |
| if (data->regulator_state != 0) { |
| ALS_dbg("%s - duplicate regulator\n", __func__); |
| data->regulator_state++; |
| return 0; |
| } |
| data->regulator_state++; |
| data->pm_state = PM_RESUME; |
| } else { |
| if (data->regulator_state == 0) { |
| ALS_dbg("%s - already off the regulator\n", __func__); |
| return 0; |
| } else if (data->regulator_state != 1) { |
| ALS_dbg("%s - duplicate regulator\n", __func__); |
| data->regulator_state--; |
| return 0; |
| } |
| data->regulator_state--; |
| } |
| |
| if (data->i2c_1p8 != NULL) { |
| regulator_i2c_1p8 = regulator_get(NULL, data->i2c_1p8); |
| if (IS_ERR(regulator_i2c_1p8) || regulator_i2c_1p8 == NULL) { |
| ALS_err("%s - get i2c_1p8 regulator failed\n", __func__); |
| rc = PTR_ERR(regulator_i2c_1p8); |
| regulator_i2c_1p8 = NULL; |
| goto get_i2c_1p8_failed; |
| } |
| } |
| |
| regulator_vdd_1p8 = |
| regulator_get(&data->client->dev, data->vdd_1p8); |
| if (IS_ERR(regulator_vdd_1p8) || regulator_vdd_1p8 == NULL) { |
| ALS_dbg("%s - get vdd_1p8 regulator failed\n", __func__); |
| rc = PTR_ERR(regulator_vdd_1p8); |
| regulator_vdd_1p8 = NULL; |
| } |
| |
| if (onoff == PWR_ON) { |
| if (data->i2c_1p8 != NULL && i2c_1p8_enable == 0) { |
| rc = regulator_enable(regulator_i2c_1p8); |
| i2c_1p8_enable = 1; |
| if (rc) { |
| ALS_err("%s - enable i2c_1p8 failed, rc=%d\n", __func__, rc); |
| goto enable_i2c_1p8_failed; |
| } |
| } |
| if (regulator_vdd_1p8 != NULL) { |
| rc = regulator_enable(regulator_vdd_1p8); |
| if (rc) { |
| ALS_err("%s - enable vdd_1p8 failed, rc=%d\n", |
| __func__, rc); |
| goto enable_vdd_1p8_failed; |
| } |
| } |
| if (data->pin_als_en >= 0) { |
| rc = gpio_direction_output(data->pin_als_en, 1); |
| if (rc) { |
| ALS_err("%s - gpio direction output failed, rc=%d\n", |
| __func__, rc); |
| goto gpio_direction_output_failed; |
| } |
| } |
| |
| usleep_range(1000, 1100); |
| } else { |
| if (regulator_vdd_1p8 != NULL) { |
| rc = regulator_disable(regulator_vdd_1p8); |
| if (rc) { |
| ALS_err("%s - disable vdd_1p8 failed, rc=%d\n", |
| __func__, rc); |
| goto done; |
| } |
| } |
| |
| if (data->pin_als_en >= 0) { |
| gpio_set_value(data->pin_als_en, 0); |
| rc = gpio_direction_input(data->pin_als_en); |
| if (rc) { |
| ALS_err("%s - gpio direction input failed, rc=%d\n", |
| __func__, rc); |
| } |
| } |
| |
| #ifdef I2C_1P8_DISABLE |
| if (data->i2c_1p8 != NULL) { |
| rc = regulator_disable(regulator_i2c_1p8); |
| i2c_1p8_enable = 0; |
| if (rc) { |
| ALS_err("%s - disable i2c_1p8 failed, rc=%d\n", __func__, rc); |
| goto done; |
| } |
| } |
| #endif |
| } |
| |
| goto done; |
| |
| gpio_direction_output_failed: |
| if (regulator_vdd_1p8 != NULL) |
| regulator_disable(regulator_vdd_1p8); |
| enable_vdd_1p8_failed: |
| #ifdef I2C_1P8_DISABLE |
| if (data->i2c_1p8 != NULL) { |
| regulator_disable(regulator_i2c_1p8); |
| i2c_1p8_enable = 0; |
| } |
| #endif |
| enable_i2c_1p8_failed: |
| done: |
| regulator_put(regulator_vdd_1p8); |
| if (data->i2c_1p8 != NULL) |
| regulator_put(regulator_i2c_1p8); |
| get_i2c_1p8_failed: |
| return rc; |
| } |
| |
| static bool ams_deviceGetAls(ams_deviceCtx_t *ctx, ams_apiAls_t *exportData); |
| static bool ams_deviceGetFlicker(ams_deviceCtx_t *ctx, ams_apiAlsFlicker_t *exportData); |
| |
| static void report_als(struct tcs3407_device_data *chip) |
| { |
| ams_apiAls_t outData; |
| static unsigned int als_cnt; |
| |
| if (chip->als_input_dev) { |
| ams_deviceGetAls(chip->deviceCtx, &outData); |
| input_report_rel(chip->als_input_dev, REL_X, outData.ir + 1); |
| input_report_rel(chip->als_input_dev, REL_Y, outData.red + 1); |
| input_report_rel(chip->als_input_dev, REL_Z, outData.green + 1); |
| input_report_rel(chip->als_input_dev, REL_RX, outData.blue + 1); |
| input_report_rel(chip->als_input_dev, REL_RY, outData.clear + 1); |
| input_report_abs(chip->als_input_dev, ABS_X, outData.time_us + 1); |
| input_report_abs(chip->als_input_dev, ABS_Y, outData.gain + 1); |
| input_sync(chip->als_input_dev); |
| |
| if (als_cnt++ > 10) { |
| ALS_dbg("%s - I:%d, R:%d, G:%d, B:%d, C:%d TIME:%d, GAIN:%d\n", __func__, |
| outData.ir, outData.red, outData.green, outData.blue, outData.clear, outData.time_us, outData.gain); |
| als_cnt = 0; |
| } else { |
| ALS_info("%s - I:%d, R:%d, G:%d, B:%d, C:%d TIME:%d, GAIN:%d\n", __func__, |
| outData.ir, outData.red, outData.green, outData.blue, outData.clear, outData.time_us, outData.gain); |
| } |
| |
| chip->user_ir_data = outData.ir; |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_EOL_MODE |
| if (chip->eol_enable && chip->eol_count >= EOL_SKIP_COUNT) { |
| chip->eol_awb += outData.ir; |
| chip->eol_clear += outData.clear; |
| } |
| #endif |
| } |
| } |
| |
| static void report_flicker(struct tcs3407_device_data *chip) |
| { |
| ams_apiAlsFlicker_t outData; |
| uint flicker = 0; |
| static unsigned int flicker_cnt; |
| |
| if (chip->als_input_dev) { |
| ams_deviceGetFlicker |
| (chip->deviceCtx, &outData); |
| |
| if (outData.mHz == 100000 || outData.mHz == 120000) |
| flicker = outData.mHz / 1000; |
| |
| input_report_rel(chip->als_input_dev, REL_RZ, flicker + 1); |
| input_sync(chip->als_input_dev); |
| |
| if (flicker_cnt++ > 10) { |
| ALS_dbg("%s - flicker = %d\n", __func__, flicker); |
| flicker_cnt = 0; |
| } else { |
| ALS_info("%s - flicker = %d, %d\n", __func__, flicker, outData.mHz); |
| } |
| |
| chip->user_flicker_data = outData.mHz; |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_EOL_MODE |
| if (chip->eol_enable && flicker != 0 && chip->eol_count >= EOL_SKIP_COUNT) { |
| chip->eol_flicker += flicker; |
| chip->eol_flicker_count++; |
| } |
| #endif |
| |
| } |
| } |
| |
| static ssize_t als_ir_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ams_apiAls_t outData; |
| struct tcs3407_device_data *chip = dev_get_drvdata(dev); |
| |
| ams_deviceGetAls(chip->deviceCtx, &outData); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", outData.ir); |
| } |
| |
| static ssize_t als_red_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ams_apiAls_t outData; |
| struct tcs3407_device_data *chip = dev_get_drvdata(dev); |
| |
| ams_deviceGetAls(chip->deviceCtx, &outData); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", outData.red); |
| } |
| |
| static ssize_t als_green_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ams_apiAls_t outData; |
| struct tcs3407_device_data *chip = dev_get_drvdata(dev); |
| |
| ams_deviceGetAls(chip->deviceCtx, &outData); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", outData.green); |
| } |
| |
| static ssize_t als_blue_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ams_apiAls_t outData; |
| struct tcs3407_device_data *chip = dev_get_drvdata(dev); |
| |
| ams_deviceGetAls(chip->deviceCtx, &outData); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", outData.blue); |
| } |
| |
| static ssize_t als_clear_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ams_apiAls_t outData; |
| struct tcs3407_device_data *chip = dev_get_drvdata(dev); |
| |
| ams_deviceGetAls(chip->deviceCtx, &outData); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", outData.clear); |
| } |
| |
| static ssize_t als_raw_data_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ams_apiAls_t outData; |
| struct tcs3407_device_data *chip = dev_get_drvdata(dev); |
| |
| ams_deviceGetAls(chip->deviceCtx, &outData); |
| |
| return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d\n", outData.rawWideband, |
| outData.rawRed, outData.rawGreen, outData.rawBlue, outData.rawClear); |
| } |
| |
| static size_t als_enable_set(struct tcs3407_device_data *chip, uint8_t valueToSet) |
| { |
| int rc = 0; |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_ALS_CCB |
| rc = ams_deviceSetConfig(chip->deviceCtx, AMS_CONFIG_ALS_LUX, AMS_CONFIG_ENABLE, valueToSet); |
| if (rc < 0) { |
| ALS_err("%s - ams_deviceSetConfig ALS_LUX fail, rc=%d\n", __func__, rc); |
| return rc; |
| } |
| #endif |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FLICKER |
| |
| rc = ams_deviceSetConfig(chip->deviceCtx, AMS_CONFIG_FLICKER, AMS_CONFIG_ENABLE, valueToSet); |
| if (rc < 0) { |
| ALS_err("%s - ams_deviceSetConfig FLICKER fail, rc=%d\n", __func__, rc); |
| return rc; |
| } |
| |
| #endif |
| return 0; |
| } |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FLICKER |
| static ssize_t flicker_data_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ams_apiAlsFlicker_t outData; |
| struct tcs3407_device_data *chip = dev_get_drvdata(dev); |
| |
| ams_deviceGetFlicker(chip->deviceCtx, &outData); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", outData.mHz); |
| } |
| #endif |
| |
| /* als input enable/disable sysfs */ |
| static ssize_t tcs3407_enable_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| ams_mode_t mode; |
| |
| ams_getMode(data->deviceCtx, &mode); |
| |
| if (mode & MODE_ALS_ALL) |
| return snprintf(buf, PAGE_SIZE, "%d\n", 1); |
| else |
| return snprintf(buf, PAGE_SIZE, "%d\n", 0); |
| } |
| |
| static int ams_deviceInit(ams_deviceCtx_t *ctx, AMS_PORT_portHndl *portHndl, ams_calibrationData_t *calibrationData); |
| |
| static ssize_t tcs3407_enable_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| bool value; |
| int err = 0; |
| |
| if (strtobool(buf, &value)) |
| return -EINVAL; |
| |
| ALS_dbg("%s - en : %d, c : %d\n", __func__, value, data->enabled); |
| |
| mutex_lock(&data->activelock); |
| |
| if (value) { |
| tcs3407_irq_set_state(data, PWR_ON); |
| |
| err = tcs3407_power_ctrl(data, PWR_ON); |
| if (err < 0) |
| ALS_err("%s - als_regulator_on fail err = %d\n", |
| __func__, err); |
| |
| err = ams_deviceInit(data->deviceCtx, data->client, NULL); |
| if (err < 0) { |
| ALS_err("%s - ams_deviceInit failed.\n", __func__); |
| goto err_device_init; |
| } else { |
| ALS_dbg("%s - ams_amsDeviceInit ok\n", __func__); |
| } |
| |
| err = als_enable_set(data, AMSDRIVER_ALS_ENABLE); |
| |
| if (err == 0) |
| data->enabled = 1; |
| |
| if (err < 0) { |
| input_report_rel(data->als_input_dev, |
| REL_RZ, -5 + 1); /* F_ERR_I2C -5 detected i2c error */ |
| input_sync(data->als_input_dev); |
| ALS_err("%s - enable error %d\n", __func__, err); |
| } |
| |
| data->mode_cnt.amb_cnt++; |
| } else { |
| if (data->regulator_state == 0) { |
| ALS_dbg("%s - already power off - disable skip\n", |
| __func__); |
| goto err_already_off; |
| } |
| |
| err = als_enable_set(data, AMSDRIVER_ALS_DISABLE); |
| if (err != 0) |
| ALS_err("%s - disable err : %d\n", __func__, err); |
| |
| data->enabled = 0; |
| |
| err = tcs3407_power_ctrl(data, PWR_OFF); |
| if (err < 0) |
| ALS_err("%s - als_regulator_off fail err = %d\n", |
| __func__, err); |
| tcs3407_irq_set_state(data, PWR_OFF); |
| } |
| |
| err_device_init: |
| err_already_off: |
| mutex_unlock(&data->activelock); |
| |
| return count; |
| } |
| |
| static ssize_t tcs3407_poll_delay_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", data->sampling_period_ns); |
| } |
| |
| static ssize_t tcs3407_poll_delay_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t size) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| u32 sampling_period_ns = 0; |
| int err = 0; |
| |
| mutex_lock(&data->activelock); |
| |
| err = kstrtoint(buf, 10, &sampling_period_ns); |
| |
| if (err < 0) { |
| ALS_err("%s - kstrtoint failed.(%d)\n", __func__, err); |
| mutex_unlock(&data->activelock); |
| return err; |
| } |
| |
| err = tcs3407_set_sampling_rate(sampling_period_ns); |
| |
| if (err > 0) |
| data->sampling_period_ns = err; |
| |
| ALS_dbg("%s - als sensor sampling rate is setted as %dns\n", __func__, sampling_period_ns); |
| |
| mutex_unlock(&data->activelock); |
| |
| return size; |
| } |
| |
| static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR|S_IWGRP, |
| tcs3407_enable_show, tcs3407_enable_store); |
| static DEVICE_ATTR(poll_delay, S_IRUGO|S_IWUSR|S_IWGRP, |
| tcs3407_poll_delay_show, tcs3407_poll_delay_store); |
| |
| static struct attribute *als_sysfs_attrs[] = { |
| &dev_attr_enable.attr, |
| &dev_attr_poll_delay.attr, |
| NULL |
| }; |
| |
| static struct attribute_group als_attribute_group = { |
| .attrs = als_sysfs_attrs, |
| }; |
| |
| /* als_sensor sysfs */ |
| static ssize_t tcs3407_name_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| ams_deviceCtx_t *ctx = data->deviceCtx; |
| char chip_name[NAME_LEN]; |
| |
| switch (ctx->deviceId) { |
| case AMS_TCS3407: |
| case AMS_TCS3407_UNTRIM: |
| strlcpy(chip_name, TCS3407_CHIP_NAME, sizeof(chip_name)); |
| break; |
| case AMS_TCS3408: |
| case AMS_TCS3408_UNTRIM: |
| strlcpy(chip_name, TCS3408_CHIP_NAME, sizeof(chip_name)); |
| break; |
| default: |
| strlcpy(chip_name, TCS3407_CHIP_NAME, sizeof(chip_name)); |
| break; |
| } |
| |
| return snprintf(buf, PAGE_SIZE, "%s\n", chip_name); |
| } |
| |
| static ssize_t tcs3407_vendor_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR); |
| } |
| |
| static ssize_t tcs3407_flush_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| int ret = 0; |
| u8 handle = 0; |
| |
| mutex_lock(&data->activelock); |
| ret = kstrtou8(buf, 10, &handle); |
| if (ret < 0) { |
| ALS_err("%s - kstrtou8 failed.(%d)\n", __func__, ret); |
| mutex_unlock(&data->activelock); |
| return ret; |
| } |
| ALS_dbg("%s - handle = %d\n", __func__, handle); |
| mutex_unlock(&data->activelock); |
| |
| input_report_rel(data->als_input_dev, REL_MISC, handle); |
| |
| return size; |
| } |
| |
| static ssize_t tcs3407_int_pin_check_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| /* need to check if this should be implemented */ |
| ALS_dbg("%s - not implement\n", __func__); |
| return snprintf(buf, PAGE_SIZE, "%d\n", 0); |
| } |
| |
| static ssize_t tcs3407_read_reg_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| |
| ALS_info("%s - val=0x%06x\n", __func__, data->reg_read_buf); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", data->reg_read_buf); |
| } |
| |
| static ssize_t tcs3407_read_reg_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| |
| int err = -1; |
| unsigned int cmd = 0; |
| u8 val = 0; |
| |
| mutex_lock(&data->i2clock); |
| if (data->regulator_state == 0) { |
| ALS_dbg("%s - need to power on\n", __func__); |
| mutex_unlock(&data->i2clock); |
| return size; |
| } |
| err = sscanf(buf, "%8x", &cmd); |
| if (err == 0) { |
| ALS_err("%s - sscanf fail\n", __func__); |
| mutex_unlock(&data->i2clock); |
| return size; |
| } |
| |
| err = tcs3407_read_reg(data, (u8)cmd, &val, 1); |
| if (err != 0) { |
| ALS_err("%s - err=%d, val=0x%06x\n", |
| __func__, err, val); |
| mutex_unlock(&data->i2clock); |
| return size; |
| } |
| data->reg_read_buf = (u32)val; |
| mutex_unlock(&data->i2clock); |
| |
| return size; |
| } |
| static ssize_t tcs3407_write_reg_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| |
| int err = -1; |
| unsigned int cmd = 0; |
| unsigned int val = 0; |
| |
| mutex_lock(&data->i2clock); |
| if (data->regulator_state == 0) { |
| ALS_dbg("%s - need to power on.\n", __func__); |
| mutex_unlock(&data->i2clock); |
| return size; |
| } |
| err = sscanf(buf, "%8x, %8x", &cmd, &val); |
| if (err == 0) { |
| ALS_err("%s - sscanf fail %s\n", __func__, buf); |
| mutex_unlock(&data->i2clock); |
| return size; |
| } |
| |
| err = tcs3407_write_reg(data, (u8)cmd, (u8)val); |
| if (err < 0) { |
| ALS_err("%s - fail err = %d\n", __func__, err); |
| mutex_unlock(&data->i2clock); |
| return err; |
| } |
| mutex_unlock(&data->i2clock); |
| |
| return size; |
| } |
| |
| static ssize_t tcs3407_debug_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| |
| ALS_info("%s - debug mode = %u\n", __func__, data->debug_mode); |
| |
| return snprintf(buf, PAGE_SIZE, "%u\n", data->debug_mode); |
| } |
| |
| static ssize_t tcs3407_debug_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| int err; |
| s32 mode; |
| struct pwm_state state; |
| int period = 10000000; /* nano secs */ |
| int period2 = 8333333; /* nano secs */ |
| int duty_cycle = 5000000; |
| |
| if(data->pwm) |
| pwm_get_state(data->pwm, &state); |
| |
| state.enabled = 1; |
| state.period = period; /* nano secs */ |
| if (debug_pwm_duty) |
| duty_cycle = debug_pwm_duty; |
| |
| state.duty_cycle = duty_cycle; |
| state.polarity = PWM_POLARITY_NORMAL; // should be default low |
| |
| mutex_lock(&data->activelock); |
| err = kstrtoint(buf, 10, &mode); |
| if (err < 0) { |
| ALS_err("%s - kstrtoint failed.(%d)\n", __func__, err); |
| mutex_unlock(&data->activelock); |
| return err; |
| } |
| data->debug_mode = mode; |
| ALS_info("%s - mode = %d\n", __func__, mode); |
| |
| switch (data->debug_mode) { |
| case DEBUG_REG_STATUS: |
| tcs3407_print_reg_status(); |
| break; |
| case DEBUG_VAR: |
| tcs3407_debug_var(data); |
| break; |
| case 3: |
| pinctrl_select_state(data->als_pinctrl, data->pinctrl_pwm); |
| pwm_apply_state(data->pwm, &state); |
| break; |
| case 4: |
| state.period = period2; |
| pwm_apply_state(data->pwm, &state); |
| break; |
| default: |
| debug_pwm_duty = data->debug_mode; |
| break; |
| } |
| mutex_unlock(&data->activelock); |
| |
| return size; |
| } |
| |
| static ssize_t tcs3407_device_id_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ALS_dbg("%s - device_id not support\n", __func__); |
| |
| return snprintf(buf, PAGE_SIZE, "NOT SUPPORT\n"); |
| } |
| |
| static ssize_t tcs3407_part_type_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| ams_deviceCtx_t *ctx = data->deviceCtx; |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", ctx->deviceId); |
| } |
| |
| static ssize_t tcs3407_i2c_err_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| u32 err_cnt = 0; |
| |
| err_cnt = data->i2c_err_cnt; |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", err_cnt); |
| } |
| |
| static ssize_t tcs3407_i2c_err_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| |
| data->i2c_err_cnt = 0; |
| |
| return size; |
| } |
| |
| static ssize_t tcs3407_curr_adc_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, |
| "\"HRIC\":\"%d\",\"HRRC\":\"%d\",\"HRIA\":\"%d\",\"HRRA\":\"%d\"\n", |
| 0, 0, data->user_ir_data, data->user_flicker_data); |
| } |
| |
| static ssize_t tcs3407_curr_adc_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| |
| data->user_ir_data = 0; |
| data->user_flicker_data = 0; |
| |
| return size; |
| } |
| |
| static ssize_t tcs3407_mode_cnt_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, |
| "\"CNT_HRM\":\"%d\",\"CNT_AMB\":\"%d\",\"CNT_PROX\":\"%d\",\"CNT_SDK\":\"%d\",\"CNT_CGM\":\"%d\",\"CNT_UNKN\":\"%d\"\n", |
| data->mode_cnt.hrm_cnt, data->mode_cnt.amb_cnt, data->mode_cnt.prox_cnt, |
| data->mode_cnt.sdk_cnt, data->mode_cnt.cgm_cnt, data->mode_cnt.unkn_cnt); |
| } |
| |
| static ssize_t tcs3407_mode_cnt_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| |
| data->mode_cnt.hrm_cnt = 0; |
| data->mode_cnt.amb_cnt = 0; |
| data->mode_cnt.prox_cnt = 0; |
| data->mode_cnt.sdk_cnt = 0; |
| data->mode_cnt.cgm_cnt = 0; |
| data->mode_cnt.unkn_cnt = 0; |
| |
| return size; |
| } |
| |
| static ssize_t tcs3407_factory_cmd_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| static int cmd_result; |
| |
| mutex_lock(&data->activelock); |
| |
| if (data->isTrimmed) |
| cmd_result = 1; |
| else |
| cmd_result = 0; |
| |
| ALS_dbg("%s - cmd_result = %d\n", __func__, cmd_result); |
| |
| mutex_unlock(&data->activelock); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", cmd_result); |
| } |
| |
| static ssize_t tcs3407_version_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ALS_info("%s - cmd_result = %s.%s.%s%s\n", __func__, |
| VERSION, SUB_VERSION, HEADER_VERSION, VENDOR_VERSION); |
| |
| return snprintf(buf, PAGE_SIZE, "%s.%s.%s%s\n", |
| VERSION, SUB_VERSION, HEADER_VERSION, VENDOR_VERSION); |
| } |
| |
| static ssize_t tcs3407_sensor_info_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ALS_dbg("%s - sensor_info_data not support\n", __func__); |
| |
| return snprintf(buf, PAGE_SIZE, "NOT SUPPORT\n"); |
| } |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_EOL_MODE |
| |
| static int tcs3407_eol_mode(struct tcs3407_device_data *data) |
| { |
| int led_curr = 0; |
| int pulse_duty = 0; |
| int curr_state = EOL_STATE_INIT; |
| int ret = 0; |
| ams_deviceCtx_t *ctx = data->deviceCtx; |
| int icRatio100 = 0; |
| int icRatio120 = 0; |
| struct pwm_state state; |
| int period_100 = 10000000; /* nano secs */ |
| int period_120 = 8333333; /* nano secs */ |
| int duty = 20; |
| |
| data->eol_state = EOL_STATE_INIT; |
| data->eol_enable = 1; |
| data->eol_result_status = 1; |
| |
| if (data->pwm == NULL || debug_pwm_duty > period_100) { |
| ret = gpio_request(data->pin_led_en, NULL); |
| if (ret < 0) |
| return ret; |
| |
| s2mpb02_led_en(S2MPB02_TORCH_LED_1, led_curr, S2MPB02_LED_TURN_WAY_GPIO); |
| |
| switch (ctx->deviceId) { |
| case AMS_TCS3407: |
| case AMS_TCS3407_UNTRIM: |
| led_curr = S2MPB02_TORCH_OUT_I_100MA; |
| break; |
| case AMS_TCS3408: |
| case AMS_TCS3408_UNTRIM: |
| led_curr = S2MPB02_TORCH_OUT_I_20MA; |
| break; |
| default: |
| led_curr = S2MPB02_TORCH_OUT_I_20MA; |
| break; |
| } |
| |
| while (data->eol_state < EOL_STATE_DONE) { |
| switch (data->eol_state) { |
| case EOL_STATE_INIT: |
| break; |
| case EOL_STATE_100: |
| pulse_duty = data->eol_pulse_duty[0]; |
| break; |
| case EOL_STATE_120: |
| pulse_duty = data->eol_pulse_duty[1]; |
| break; |
| default: |
| break; |
| } |
| |
| if (data->eol_state >= EOL_STATE_100) { |
| if (curr_state != data->eol_state) { |
| s2mpb02_led_en(S2MPB02_TORCH_LED_1, led_curr, S2MPB02_LED_TURN_WAY_GPIO); |
| curr_state = data->eol_state; |
| } else |
| gpio_direction_output(data->pin_led_en, 1); |
| |
| udelay(pulse_duty); |
| |
| gpio_direction_output(data->pin_led_en, 0); |
| |
| data->eol_pulse_count++; |
| } |
| udelay(pulse_duty); |
| } |
| s2mpb02_led_en(S2MPB02_TORCH_LED_1, 0, S2MPB02_LED_TURN_WAY_GPIO); |
| gpio_free(data->pin_led_en); |
| } else { |
| ALS_dbg("%s - PWM torch set 0x%x 0x%x\n", __func__, data->pinctrl_pwm, data->pinctrl_out); |
| pinctrl_select_state(data->als_pinctrl, data->pinctrl_pwm); |
| |
| pwm_get_state(data->pwm, &state); |
| |
| ALS_dbg("%s - debug duty = %d\n", __func__, debug_pwm_duty); |
| |
| while (data->eol_state < EOL_STATE_DONE) { |
| switch (data->eol_state) { |
| case EOL_STATE_INIT: |
| break; |
| case EOL_STATE_100: |
| state.period = period_100; /* nano secs */ |
| break; |
| case EOL_STATE_120: |
| state.period = period_120; /* nano secs */ |
| break; |
| default: |
| break; |
| } |
| |
| if (data->eol_state >= EOL_STATE_100) { |
| if (curr_state != data->eol_state) { |
| |
| state.enabled = 1; |
| if (debug_pwm_duty) |
| state.duty_cycle = debug_pwm_duty; |
| else |
| state.duty_cycle = state.period * duty / 100; |
| |
| state.polarity = PWM_POLARITY_NORMAL; |
| |
| ALS_dbg("%s - pwm state en = %d, pe = %d, du = %d, po = %d\n", __func__, |
| state.enabled, state.period, state.duty_cycle, state.polarity); |
| |
| pwm_apply_state(data->pwm, &state); |
| |
| curr_state = data->eol_state; |
| } |
| |
| data->eol_pulse_count++; |
| } |
| udelay(1000); |
| } |
| state.enabled = 0; |
| |
| pwm_apply_state(data->pwm, &state); |
| |
| ALS_dbg("%s - pinctrl out = 0x%x\n", __func__, data->pinctrl_out); |
| pinctrl_select_state(data->als_pinctrl, data->pinctrl_out); |
| } |
| |
| data->eol_enable = 0; |
| |
| if (data->eol_state >= EOL_STATE_DONE) { |
| icRatio100 = data->eol_flicker_awb[EOL_STATE_100][1] * 100 / data->eol_flicker_awb[EOL_STATE_100][2]; |
| icRatio120 = data->eol_flicker_awb[EOL_STATE_120][1] * 100 / data->eol_flicker_awb[EOL_STATE_120][2]; |
| |
| snprintf(data->eol_result, MAX_TEST_RESULT, |
| "%d, %s, %d, %s, %d, %s, %d, %s, %d, %s, %d, %s, %d, %s, %d, %s\n", |
| data->eol_flicker_awb[EOL_STATE_100][0], FREQ100_SPEC_IN(data->eol_flicker_awb[EOL_STATE_100][0]), |
| data->eol_flicker_awb[EOL_STATE_120][0], FREQ120_SPEC_IN(data->eol_flicker_awb[EOL_STATE_120][0]), |
| data->eol_flicker_awb[EOL_STATE_100][1], IR_SPEC_IN(data->eol_flicker_awb[EOL_STATE_100][1]), |
| data->eol_flicker_awb[EOL_STATE_120][1], IR_SPEC_IN(data->eol_flicker_awb[EOL_STATE_120][1]), |
| data->eol_flicker_awb[EOL_STATE_100][2], CLEAR_SPEC_IN(data->eol_flicker_awb[EOL_STATE_100][2]), |
| data->eol_flicker_awb[EOL_STATE_120][2], CLEAR_SPEC_IN(data->eol_flicker_awb[EOL_STATE_120][2]), |
| icRatio100, ICRATIO_SPEC_IN(icRatio100), |
| icRatio120, ICRATIO_SPEC_IN(icRatio120)); |
| } else |
| ALS_err("%s - abnormal termination\n", __func__); |
| |
| ALS_dbg("%s - %s", __func__, data->eol_result); |
| |
| return 0; |
| } |
| |
| static ssize_t tcs3407_eol_mode_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| |
| mutex_lock(&data->activelock); |
| |
| if (data->eol_result == NULL) { |
| ALS_err("%s - data->eol_result is NULL\n", __func__); |
| mutex_unlock(&data->activelock); |
| return snprintf(buf, PAGE_SIZE, "%s\n", "NO_EOL_TEST"); |
| } |
| if (data->eol_enable == 1) { |
| mutex_unlock(&data->activelock); |
| return snprintf(buf, PAGE_SIZE, "%s\n", "EOL_RUNNING"); |
| } else if (data->eol_enable == 0 && data->eol_result_status == 0) { |
| mutex_unlock(&data->activelock); |
| return snprintf(buf, PAGE_SIZE, "%s\n", "NO_EOL_TEST"); |
| } |
| mutex_unlock(&data->activelock); |
| |
| data->eol_result_status = 0; |
| return snprintf(buf, PAGE_SIZE, "%s\n", data->eol_result); |
| } |
| |
| static ssize_t tcs3407_eol_mode_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| ams_deviceCtx_t *ctx = data->deviceCtx; |
| |
| int err = 0; |
| int mode = 0; |
| u8 preEnalble = data->enabled; |
| |
| err = kstrtoint(buf, 10, &mode); |
| if (err < 0) { |
| ALS_err("%s - kstrtoint failed.(%d)\n", __func__, err); |
| mutex_unlock(&data->activelock); |
| return err; |
| } |
| |
| switch (mode) { |
| case 1: |
| gSpec_ir_min = data->eol_ir_spec[0]; |
| gSpec_ir_max = data->eol_ir_spec[1]; |
| gSpec_clear_min = data->eol_clear_spec[0]; |
| gSpec_clear_max = data->eol_clear_spec[1]; |
| gSpec_icratio_min = data->eol_icratio_spec[0]; |
| gSpec_icratio_max = data->eol_icratio_spec[1]; |
| break; |
| |
| case 2: |
| gSpec_ir_min = data->eol_ir_spec[2]; |
| gSpec_ir_max = data->eol_ir_spec[3]; |
| gSpec_clear_min = data->eol_clear_spec[2]; |
| gSpec_clear_max = data->eol_clear_spec[3]; |
| gSpec_icratio_min = data->eol_icratio_spec[2]; |
| gSpec_icratio_max = data->eol_icratio_spec[3]; |
| break; |
| |
| default: |
| break; |
| } |
| |
| ALS_dbg("%s - mode = %d-%d, gSpec_ir = %d - %d, gSpec_clear = %d - %d, gSpec_icratio = %d - %d\n", __func__, mode, |
| preEnalble, gSpec_ir_min, gSpec_ir_max, gSpec_clear_min, gSpec_clear_max, gSpec_icratio_min, gSpec_icratio_max); |
| |
| mutex_lock(&data->activelock); |
| |
| if (!preEnalble) { |
| tcs3407_irq_set_state(data, PWR_ON); |
| |
| err = tcs3407_power_ctrl(data, PWR_ON); |
| if (err < 0) |
| ALS_err("%s - als_regulator_on fail err = %d\n", |
| __func__, err); |
| |
| err = ams_deviceInit(data->deviceCtx, data->client, NULL); |
| if (err < 0) { |
| ALS_err("%s - ams_deviceInit failed.\n", __func__); |
| goto err_device_init; |
| } else { |
| ALS_dbg("%s - ams_amsDeviceInit ok\n", __func__); |
| } |
| |
| err = als_enable_set(data, AMSDRIVER_ALS_ENABLE); |
| if (err == 0) { |
| data->enabled = 1; |
| } else if (err < 0) { |
| input_report_rel(data->als_input_dev, |
| REL_Y, -5 + 1); /* F_ERR_I2C -5 detected i2c error */ |
| input_sync(data->als_input_dev); |
| ALS_err("%s - enable error %d\n", __func__, err); |
| } |
| } |
| AMS_SET_ALS_AUTOGAIN(LOW, err); |
| AMS_SET_ALS_GAIN(EOL_GAIN, err); |
| ALS_dbg("%s - fixed ALS GAIN : %d\n", __func__, EOL_GAIN); |
| |
| mutex_unlock(&data->activelock); |
| |
| tcs3407_eol_mode(data); |
| |
| mutex_lock(&data->activelock); |
| |
| if (data->regulator_state == 0) { |
| ALS_dbg("%s - already power off - disable skip\n", |
| __func__); |
| goto err_already_off; |
| } |
| |
| err = als_enable_set(data, AMSDRIVER_ALS_DISABLE); |
| if (err != 0) |
| ALS_err("%s - disable err : %d\n", __func__, err); |
| |
| if (preEnalble) { |
| err = ams_deviceInit(data->deviceCtx, data->client, NULL); |
| if (err < 0) { |
| ALS_err("%s - ams_deviceInit failed.\n", __func__); |
| goto err_device_init; |
| } else { |
| ALS_dbg("%s - ams_amsDeviceInit ok\n", __func__); |
| } |
| |
| err = als_enable_set(data, AMSDRIVER_ALS_ENABLE); |
| if (err == 0) { |
| data->enabled = 1; |
| } else if (err < 0) { |
| input_report_rel(data->als_input_dev, |
| REL_Y, -5 + 1); /* F_ERR_I2C -5 detected i2c error */ |
| input_sync(data->als_input_dev); |
| ALS_err("%s - enable error %d\n", __func__, err); |
| } |
| } else { |
| data->enabled = 0; |
| |
| err = tcs3407_power_ctrl(data, PWR_OFF); |
| if (err < 0) |
| ALS_err("%s - als_regulator_off fail err = %d\n", |
| __func__, err); |
| tcs3407_irq_set_state(data, PWR_OFF); |
| } |
| err_device_init: |
| err_already_off: |
| mutex_unlock(&data->activelock); |
| |
| return size; |
| } |
| |
| static ssize_t tcs3407_eol_spec_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| |
| ALS_dbg("%s - eol_spec = %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n", __func__, |
| data->eol_ir_spec[0], data->eol_ir_spec[1], data->eol_ir_spec[2], data->eol_ir_spec[3], |
| data->eol_clear_spec[0], data->eol_clear_spec[1], data->eol_clear_spec[2], data->eol_clear_spec[3], |
| data->eol_icratio_spec[0], data->eol_icratio_spec[1], data->eol_icratio_spec[2], data->eol_icratio_spec[3]); |
| |
| return snprintf(buf, PAGE_SIZE, "%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n", |
| data->eol_ir_spec[0], data->eol_ir_spec[1], data->eol_ir_spec[2], data->eol_ir_spec[3], |
| data->eol_clear_spec[0], data->eol_clear_spec[1], data->eol_clear_spec[2], data->eol_clear_spec[3], |
| data->eol_icratio_spec[0], data->eol_icratio_spec[1], data->eol_icratio_spec[2], data->eol_icratio_spec[3]); |
| } |
| #endif |
| |
| static DEVICE_ATTR(name, S_IRUGO, tcs3407_name_show, NULL); |
| static DEVICE_ATTR(vendor, S_IRUGO, tcs3407_vendor_show, NULL); |
| static DEVICE_ATTR(als_flush, S_IWUSR | S_IWGRP, NULL, tcs3407_flush_store); |
| static DEVICE_ATTR(int_pin_check, S_IRUGO, tcs3407_int_pin_check_show, NULL); |
| static DEVICE_ATTR(read_reg, S_IRUGO | S_IWUSR | S_IWGRP, |
| tcs3407_read_reg_show, tcs3407_read_reg_store); |
| static DEVICE_ATTR(write_reg, S_IWUSR | S_IWGRP, NULL, tcs3407_write_reg_store); |
| static DEVICE_ATTR(als_debug, S_IRUGO | S_IWUSR | S_IWGRP, |
| tcs3407_debug_show, tcs3407_debug_store); |
| static DEVICE_ATTR(device_id, S_IRUGO, tcs3407_device_id_show, NULL); |
| static DEVICE_ATTR(part_type, S_IRUGO, tcs3407_part_type_show, NULL); |
| static DEVICE_ATTR(i2c_err_cnt, S_IRUGO | S_IWUSR | S_IWGRP, tcs3407_i2c_err_show, tcs3407_i2c_err_store); |
| static DEVICE_ATTR(curr_adc, S_IRUGO | S_IWUSR | S_IWGRP, tcs3407_curr_adc_show, tcs3407_curr_adc_store); |
| static DEVICE_ATTR(mode_cnt, S_IRUGO | S_IWUSR | S_IWGRP, tcs3407_mode_cnt_show, tcs3407_mode_cnt_store); |
| static DEVICE_ATTR(als_factory_cmd, S_IRUGO, tcs3407_factory_cmd_show, NULL); |
| static DEVICE_ATTR(als_version, S_IRUGO, tcs3407_version_show, NULL); |
| static DEVICE_ATTR(sensor_info, S_IRUGO, tcs3407_sensor_info_show, NULL); |
| static DEVICE_ATTR(als_ir, S_IRUGO, als_ir_show, NULL); |
| static DEVICE_ATTR(als_red, S_IRUGO, als_red_show, NULL); |
| static DEVICE_ATTR(als_green, S_IRUGO, als_green_show, NULL); |
| static DEVICE_ATTR(als_blue, S_IRUGO, als_blue_show, NULL); |
| static DEVICE_ATTR(als_clear, S_IRUGO, als_clear_show, NULL); |
| static DEVICE_ATTR(als_raw_data, S_IRUGO, als_raw_data_show, NULL); |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FLICKER |
| static DEVICE_ATTR(flicker_data, S_IRUGO, flicker_data_show, NULL); |
| #endif |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_EOL_MODE |
| static DEVICE_ATTR(eol_mode, S_IRUGO | S_IWUSR | S_IWGRP, tcs3407_eol_mode_show, tcs3407_eol_mode_store); |
| static DEVICE_ATTR(eol_spec, S_IRUGO, tcs3407_eol_spec_show, NULL); |
| #endif |
| |
| static struct device_attribute *tcs3407_sensor_attrs[] = { |
| &dev_attr_name, |
| &dev_attr_vendor, |
| &dev_attr_als_flush, |
| &dev_attr_int_pin_check, |
| &dev_attr_read_reg, |
| &dev_attr_write_reg, |
| &dev_attr_als_debug, |
| &dev_attr_device_id, |
| &dev_attr_part_type, |
| &dev_attr_i2c_err_cnt, |
| &dev_attr_curr_adc, |
| &dev_attr_mode_cnt, |
| &dev_attr_als_factory_cmd, |
| &dev_attr_als_version, |
| &dev_attr_sensor_info, |
| &dev_attr_als_ir, |
| &dev_attr_als_red, |
| &dev_attr_als_green, |
| &dev_attr_als_blue, |
| &dev_attr_als_clear, |
| &dev_attr_als_raw_data, |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FLICKER |
| &dev_attr_flicker_data, |
| #endif |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_EOL_MODE |
| &dev_attr_eol_mode, |
| &dev_attr_eol_spec, |
| #endif |
| NULL, |
| }; |
| |
| static int _3407_handleAlsEvent(ams_deviceCtx_t *ctx); |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FIFO |
| static void _3407_handleFlickerFIFOEvent(ams_deviceCtx_t *ctx); |
| #else |
| static int _3407_handleFlickerEvent(ams_deviceCtx_t *ctx); |
| #endif |
| |
| static int ams_deviceEventHandler(ams_deviceCtx_t *ctx) |
| { |
| int ret = 0; |
| uint8_t status5 = 0; |
| |
| ret = ams_getByte(ctx->portHndl, DEVREG_STATUS, &ctx->shadowStatus1Reg); |
| if (ret < 0) { |
| ALS_err("%s - failed to get DEVREG_STATUS\n", __func__); |
| return ret; |
| } |
| |
| if (ctx->shadowStatus1Reg & SINT) { |
| ret = ams_getByte(ctx->portHndl, DEVREG_STATUS5, &status5); |
| if (ret < 0) { |
| ALS_err("%s - failed to get DEVREG_STATUS5\n", __func__); |
| return ret; |
| } |
| ALS_info("%s - ctx->shadowStatus1Reg %x, status5 %x, mode %x", __func__, ctx->shadowStatus1Reg, status5, ctx->mode); |
| } |
| |
| if (ctx->shadowStatus1Reg != 0) { |
| /* this clears interrupt(s) and STATUS5 */ |
| ret = ams_setByte(ctx->portHndl, DEVREG_STATUS, ctx->shadowStatus1Reg); |
| if (ret < 0) { |
| ALS_err("%s - failed to set DEVREG_STATUS\n", __func__); |
| return ret; |
| } |
| } else { |
| ALS_err("%s - ams_devEventHd Error Case!!!!\n", __func__); |
| //ams_setByte(ctx->portHndl, DEVREG_STATUS, 0xff); |
| return ret; |
| } |
| |
| loop: |
| ALS_info("%s - loop: DCB 0x%02x, STATUS 0x%02x, STATUS5 0x%02x\n", __func__, ctx->mode, ctx->shadowStatus1Reg, status5); |
| |
| if ((ctx->shadowStatus1Reg & ALS_INT_ALL) /*|| ctx->alwaysReadAls*/) { |
| if ((ctx->mode & MODE_ALS_ALL) && (!(ctx->mode & MODE_IRBEAM))) { |
| ALS_info("%s - AlsEvent INT:%d alwaysReadAls = %d\n", __func__, (ctx->shadowStatus1Reg & ALS_INT_ALL), ctx->alwaysReadAls); |
| ret = _3407_handleAlsEvent(ctx); |
| if (ret < 0) { |
| ALS_err("%s - failed to _3407_handleAlsEvent\n", __func__); |
| return ret; |
| } |
| } |
| } |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FIFO |
| if ((ctx->shadowStatus1Reg & FIFOINT) || ((status5 & SINT_FD))) { |
| if (ctx->mode & MODE_FLICKER) { |
| //AMS_PORT_log("_3407_handleFlickerEvent\n"); |
| _3407_handleFlickerFIFOEvent(ctx); |
| } |
| } |
| #else |
| if ((status5 & SINT_FD)) { |
| if (ctx->mode & MODE_FLICKER) { |
| ALS_info("%s - FlickerEvent status5:0x%02x alwaysReadFlicker = %d\n", __func__, (status5 & SINT_FD), ctx->alwaysReadFlicker); |
| ret = _3407_handleFlickerEvent(ctx); |
| if (ret < 0) { |
| ALS_err("%s - failed to _3407_handleFlickerEvent\n", __func__); |
| return ret; |
| } |
| } |
| } |
| #endif |
| |
| ret = ams_getByte(ctx->portHndl, DEVREG_STATUS, &ctx->shadowStatus1Reg); |
| if (ret < 0) { |
| ALS_err("%s - failed to get DEVREG_STATUS\n", __func__); |
| return ret; |
| } |
| |
| if (ctx->shadowStatus1Reg & SINT) { |
| ret = ams_getByte(ctx->portHndl, DEVREG_STATUS5, &status5); |
| if (ret < 0) { |
| ALS_err("%s - failed to get DEVREG_STATUS5\n", __func__); |
| return ret; |
| } |
| } else { |
| status5 = 0; |
| } |
| |
| if (ctx->shadowStatus1Reg != 0) { |
| /* this clears interrupt(s) and STATUS5 */ |
| ret = ams_setByte(ctx->portHndl, DEVREG_STATUS, ctx->shadowStatus1Reg); |
| if (ret < 0) { |
| ALS_err("%s - failed to set DEVREG_STATUS\n", __func__); |
| return ret; |
| } |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FIFO |
| if (!(ctx->shadowStatus1Reg & (PSAT)))/*changed to update event data during saturation*/ |
| #else |
| if (!(ctx->shadowStatus1Reg & (ASAT_FDSAT | PSAT)))/*changed to update event data during saturation*/ |
| #endif |
| { |
| // AMS_PORT_log_1( "ams_devEventHd loop:go loop shadowStatus1Reg %x!!!!!!!!!!\n",ctx->shadowStatus1Reg); |
| ALS_dbg("%s - go_loop DCB 0x%02x, STATUS 0x%02x, STATUS2 0x%02x\n", |
| __func__, ctx->mode, ctx->shadowStatus1Reg, ctx->shadowStatus2Reg); |
| |
| goto loop; |
| } |
| } |
| |
| /* |
| * the individual handlers may have temporarily disabled things |
| * AMS_REENABLE(ret); |
| * if (ret < 0) { |
| * ALS_err("%s - failed to AMS_REENABLE\n", __func__); |
| * return ret; |
| * } |
| */ |
| return ret; |
| } |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_EOL_MODE |
| static int tcs3407_eol_mode_handler(struct tcs3407_device_data *data) |
| { |
| int i; |
| ams_deviceCtx_t *ctx = data->deviceCtx; |
| |
| switch (data->eol_state) { |
| case EOL_STATE_INIT: |
| if (data->eol_result == NULL) |
| data->eol_result = devm_kzalloc(&data->client->dev, MAX_TEST_RESULT, GFP_KERNEL); |
| |
| for (i = 0; i < EOL_STATE_DONE; i++) { |
| data->eol_flicker_awb[i][0] = 0; |
| data->eol_flicker_awb[i][1] = 0; |
| data->eol_flicker_awb[i][2] = 0; |
| } |
| data->eol_count = 0; |
| data->eol_awb = 0; |
| data->eol_clear = 0; |
| data->eol_flicker = 0; |
| data->eol_flicker_count = 0; |
| data->eol_state = EOL_STATE_100; |
| data->eol_pulse_count = 0; |
| break; |
| default: |
| data->eol_count++; |
| ALS_dbg("%s - %d, %d, %d, %d, %d\n", __func__, |
| data->eol_state, data->eol_count, data->eol_flicker, data->eol_awb, data->eol_clear); |
| ALS_dbg("%s - raw: %d, %d, %d, %d, %d\n", __func__, ctx->ccbAlsCtx.ctxAlgAls.uvir_cpl, |
| ctx->ccbAlsCtx.ctxAlgAls.results.rawClear, ctx->ccbAlsCtx.ctxAlgAls.results.rawWideband, |
| ctx->ccbAlsCtx.ctxAlgAls.results.irrClear, ctx->ccbAlsCtx.ctxAlgAls.results.irrWideband); |
| |
| if (data->eol_count >= (EOL_COUNT + EOL_SKIP_COUNT)) { |
| data->eol_flicker_awb[data->eol_state][0] = data->eol_flicker / data->eol_flicker_count; |
| data->eol_flicker_awb[data->eol_state][1] = data->eol_awb / EOL_COUNT; |
| data->eol_flicker_awb[data->eol_state][2] = data->eol_clear / EOL_COUNT; |
| |
| ALS_dbg("%s - eol_state = %d, pulse_duty = %d %d, pulse_count = %d\n", |
| __func__, data->eol_state, data->eol_pulse_duty[0], data->eol_pulse_duty[1], data->eol_pulse_count); |
| |
| data->eol_count = 0; |
| data->eol_awb = 0; |
| data->eol_clear = 0; |
| data->eol_flicker = 0; |
| data->eol_flicker_count = 0; |
| data->eol_pulse_count = 0; |
| data->eol_state++; |
| } |
| break; |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| irqreturn_t tcs3407_irq_handler(int dev_irq, void *device) |
| { |
| int err; |
| struct tcs3407_device_data *data = device; |
| int interruptsHandled = 0; |
| |
| ALS_info("%s - als_irq = %d\n", __func__, dev_irq); |
| |
| if (data->regulator_state == 0 || data->enabled == 0) { |
| ALS_dbg("%s - stop irq handler (reg_state : %d, enabled : %d)\n", |
| __func__, data->regulator_state, data->enabled); |
| |
| ams_setByte(data->client, DEVREG_STATUS, (AINT | ASAT_FDSAT)); |
| return IRQ_HANDLED; |
| } |
| |
| #ifdef CONFIG_ARCH_QCOM |
| pm_qos_add_request(&data->pm_qos_req_fpm, PM_QOS_CPU_DMA_LATENCY, |
| PM_QOS_DEFAULT_VALUE); |
| #endif |
| err = ams_deviceEventHandler(data->deviceCtx); |
| interruptsHandled = ams_getResult(data->deviceCtx); |
| |
| if (err == 0) { |
| if (data->als_input_dev == NULL) { |
| ALS_err("%s - als_input_dev is NULL\n", __func__); |
| } else { |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_ALS |
| if (interruptsHandled & (1 << AMS_AMBIENT_SENSOR)) { |
| report_als(data); |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_EOL_MODE |
| if (data->eol_enable) |
| tcs3407_eol_mode_handler(data); |
| #endif |
| } |
| #endif |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FLICKER |
| if (interruptsHandled & (1 << AMS_FLICKER_SENSOR)) |
| report_flicker(data); |
| #endif |
| } |
| } else { |
| ALS_err("%s - ams_deviceEventHandler failed\n", __func__); |
| } |
| #ifdef CONFIG_ARCH_QCOM |
| pm_qos_remove_request(&data->pm_qos_req_fpm); |
| #endif |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int tcs3407_setup_irq(struct tcs3407_device_data *data) |
| { |
| int errorno = -EIO; |
| |
| errorno = request_threaded_irq(data->dev_irq, NULL, |
| tcs3407_irq_handler, IRQF_TRIGGER_FALLING|IRQF_ONESHOT, |
| "als_rear_sensor_irq", data); |
| |
| if (errorno < 0) { |
| ALS_err("%s - failed for setup dev_irq errono= %d\n", |
| __func__, errorno); |
| errorno = -ENODEV; |
| return errorno; |
| } |
| |
| disable_irq(data->dev_irq); |
| |
| return errorno; |
| } |
| |
| static void tcs3407_init_var(struct tcs3407_device_data *data) |
| { |
| data->client = NULL; |
| data->dev = NULL; |
| data->als_input_dev = NULL; |
| data->als_pinctrl = NULL; |
| data->pins_sleep = NULL; |
| data->pins_idle = NULL; |
| data->vdd_1p8 = NULL; |
| data->i2c_1p8 = NULL; |
| data->enabled = 0; |
| data->sampling_period_ns = 0; |
| data->regulator_state = 0; |
| data->irq_state = 0; |
| data->reg_read_buf = 0; |
| data->pm_state = PM_RESUME; |
| data->i2c_err_cnt = 0; |
| data->user_ir_data = 0; |
| data->user_flicker_data = 0; |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_EOL_MODE |
| data->eol_pulse_duty[0] = DEFAULT_DUTY_50HZ; |
| data->eol_pulse_duty[1] = DEFAULT_DUTY_60HZ; |
| data->eol_pulse_count = 0; |
| data->pinctrl_out = NULL; |
| data->pinctrl_pwm = NULL; |
| data->pwm = NULL; |
| #endif |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FIFO |
| data->awb_sample_cnt = 0; |
| data->flicker_data_cnt = 0; |
| #endif |
| } |
| |
| static int tcs3407_parse_dt(struct tcs3407_device_data *data) |
| { |
| struct device *dev = &data->client->dev; |
| struct device_node *dNode = dev->of_node; |
| enum of_gpio_flags flags; |
| u32 gain_max = 0; |
| |
| if (dNode == NULL) |
| return -ENODEV; |
| |
| data->pin_als_int = of_get_named_gpio_flags(dNode, |
| "als_rear,int-gpio", 0, &flags); |
| if (data->pin_als_int < 0) { |
| ALS_err("%s - get als_rear_int error\n", __func__); |
| return -ENODEV; |
| } |
| |
| data->pin_als_en = of_get_named_gpio_flags(dNode, |
| "als_rear,als_en-gpio", 0, &flags); |
| if (data->pin_als_en < 0) |
| ALS_dbg("%s - get als_en failed\n", __func__); |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_EOL_MODE |
| data->pin_led_en = of_get_named_gpio_flags(dNode, |
| "als_rear,led_en-gpio", 0, &flags); |
| if (data->pin_led_en < 0) { |
| ALS_err("%s - get pin_led_en error\n", __func__); |
| return -ENODEV; |
| } |
| #endif |
| |
| if (of_property_read_string(dNode, "als_rear,vdd_1p8", |
| (char const **)&data->vdd_1p8) < 0) |
| ALS_dbg("%s - vdd_1p8 doesn`t exist\n", __func__); |
| |
| if (of_property_read_string(dNode, "als_rear,i2c_1p8", |
| (char const **)&data->i2c_1p8) < 0) |
| ALS_dbg("%s - i2c_1p8 doesn`t exist\n", __func__); |
| |
| data->als_pinctrl = devm_pinctrl_get(dev); |
| |
| if (IS_ERR_OR_NULL(data->als_pinctrl)) { |
| ALS_err("%s - get pinctrl(%li) error\n", |
| __func__, PTR_ERR(data->als_pinctrl)); |
| data->als_pinctrl = NULL; |
| return -EINVAL; |
| } |
| |
| data->pins_sleep = |
| pinctrl_lookup_state(data->als_pinctrl, "sleep"); |
| if (IS_ERR_OR_NULL(data->pins_sleep)) { |
| ALS_err("%s - get pins_sleep(%li) error\n", |
| __func__, PTR_ERR(data->pins_sleep)); |
| devm_pinctrl_put(data->als_pinctrl); |
| data->pins_sleep = NULL; |
| return -EINVAL; |
| } |
| |
| data->pins_idle = |
| pinctrl_lookup_state(data->als_pinctrl, "idle"); |
| if (IS_ERR_OR_NULL(data->pins_idle)) { |
| ALS_err("%s - get pins_idle(%li) error\n", |
| __func__, PTR_ERR(data->pins_idle)); |
| |
| devm_pinctrl_put(data->als_pinctrl); |
| data->pins_idle = NULL; |
| return -EINVAL; |
| } |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_EOL_MODE |
| data->pinctrl_pwm = pinctrl_lookup_state(data->als_pinctrl, "torch_pwm"); |
| data->pinctrl_out = pinctrl_lookup_state(data->als_pinctrl, "torch_out"); |
| |
| if (IS_ERR(data->pinctrl_pwm) || IS_ERR(data->pinctrl_out)) { |
| ALS_err("%s - Failed to get pinctrl for pwm, %d %d\n", |
| __func__, PTR_ERR(data->pinctrl_pwm), PTR_ERR(data->pinctrl_out)); |
| data->pinctrl_pwm = NULL; |
| data->pinctrl_out = NULL; |
| } else { |
| data->pwm = devm_of_pwm_get(dev, dNode, NULL); |
| if (IS_ERR(data->pwm)) { |
| ALS_err("%s - unable to request PWM %d\n", __func__, PTR_ERR(data->pwm)); |
| data->pwm = NULL; |
| } |
| } |
| #endif |
| |
| if (of_property_read_u32(dNode, "als_rear,gain_max", &gain_max) == 0) { |
| deviceRegisterDefinition[DEVREG_AGC_GAIN_MAX].resetValue = gain_max; |
| |
| ALS_dbg("%s - DEVREG_AGC_GAIN_MAX = 0x%x\n", __func__, gain_max); |
| } |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_EOL_MODE |
| if (of_property_read_u32_array(dNode, "als_rear,ir_spec", |
| data->eol_ir_spec, ARRAY_SIZE(data->eol_ir_spec)) < 0) { |
| ALS_err("%s - get ir_spec error\n", __func__); |
| |
| data->eol_ir_spec[0] = DEFAULT_IR_SPEC_MIN; |
| data->eol_ir_spec[1] = DEFAULT_IR_SPEC_MAX; |
| data->eol_ir_spec[2] = DEFAULT_IR_SPEC_MIN; |
| data->eol_ir_spec[3] = DEFAULT_IR_SPEC_MAX; |
| } |
| ALS_dbg("%s - ir_spec = %d, %d, %d, %d\n", __func__, |
| data->eol_ir_spec[0], data->eol_ir_spec[1], data->eol_ir_spec[2], data->eol_ir_spec[3]); |
| |
| if (of_property_read_u32_array(dNode, "als_rear,clear_spec", |
| data->eol_clear_spec, ARRAY_SIZE(data->eol_clear_spec)) < 0) { |
| ALS_err("%s - get clear_spec error\n", __func__); |
| |
| data->eol_clear_spec[0] = DEFAULT_IR_SPEC_MIN; |
| data->eol_clear_spec[1] = DEFAULT_IR_SPEC_MAX; |
| data->eol_clear_spec[2] = DEFAULT_IR_SPEC_MIN; |
| data->eol_clear_spec[3] = DEFAULT_IR_SPEC_MAX; |
| } |
| ALS_dbg("%s - clear_spec = %d, %d, %d, %d\n", __func__, |
| data->eol_clear_spec[0], data->eol_clear_spec[1], data->eol_clear_spec[2], data->eol_clear_spec[3]); |
| |
| if (of_property_read_u32_array(dNode, "als_rear,icratio_spec", |
| data->eol_icratio_spec, ARRAY_SIZE(data->eol_icratio_spec)) < 0) { |
| ALS_err("%s - get icratio_spec error\n", __func__); |
| |
| data->eol_icratio_spec[0] = DEFAULT_IC_SPEC_MIN; |
| data->eol_icratio_spec[1] = DEFAULT_IC_SPEC_MIN; |
| data->eol_icratio_spec[2] = DEFAULT_IC_SPEC_MIN; |
| data->eol_icratio_spec[3] = DEFAULT_IC_SPEC_MIN; |
| } |
| ALS_dbg("%s - icratio_spec = %d, %d, %d, %d\n", __func__, |
| data->eol_icratio_spec[0], data->eol_icratio_spec[1], data->eol_icratio_spec[2], data->eol_icratio_spec[3]); |
| #endif |
| |
| ALS_dbg("%s - done.\n", __func__); |
| |
| return 0; |
| } |
| |
| static void tcs3407_pin_control(struct tcs3407_device_data *data, bool pin_set) |
| { |
| int status = 0; |
| |
| if (!data->als_pinctrl) { |
| ALS_err("%s - als_pinctrl is null\n", __func__); |
| return; |
| } |
| if (pin_set) { |
| if (!IS_ERR_OR_NULL(data->pins_idle)) { |
| status = pinctrl_select_state(data->als_pinctrl, |
| data->pins_idle); |
| if (status) |
| ALS_err("%s - can't set pin default state\n", |
| __func__); |
| ALS_info("%s idle\n", __func__); |
| } |
| } else { |
| if (!IS_ERR_OR_NULL(data->pins_sleep)) { |
| status = pinctrl_select_state(data->als_pinctrl, |
| data->pins_sleep); |
| if (status) |
| ALS_err("%s - can't set pin sleep state\n", |
| __func__); |
| ALS_info("%s sleep\n", __func__); |
| } |
| } |
| } |
| |
| static int tcs3407_setup_gpio(struct tcs3407_device_data *data) |
| { |
| int errorno = -EIO; |
| |
| tcs3407_pin_control(data, true); |
| |
| errorno = gpio_request(data->pin_als_int, "als_rear_int"); |
| if (errorno) { |
| ALS_err("%s - failed to request als_int\n", __func__); |
| return errorno; |
| } |
| |
| errorno = gpio_direction_input(data->pin_als_int); |
| if (errorno) { |
| ALS_err("%s - failed to set als_int as input\n", __func__); |
| goto err_gpio_direction_input; |
| } |
| data->dev_irq = gpio_to_irq(data->pin_als_int); |
| |
| if (data->pin_als_en >= 0) { |
| errorno = gpio_request(data->pin_als_en, "als_rear_en"); |
| if (errorno) { |
| ALS_err("%s - failed to request als_en\n", __func__); |
| goto err_gpio_direction_input; |
| } |
| } |
| goto done; |
| |
| err_gpio_direction_input: |
| gpio_free(data->pin_als_int); |
| done: |
| return errorno; |
| } |
| |
| static int _3407_resetAllRegisters(AMS_PORT_portHndl *portHndl) |
| { |
| int err = 0; |
| /* |
| * ams_deviceRegister_t i; |
| * |
| * for (i = DEVREG_ENABLE; i <= DEVREG_CFG1; i++) { |
| * ams_setByte(portHndl, i, deviceRegisterDefinition[i].resetValue); |
| * } |
| * for (i = DEVREG_STATUS; i < DEVREG_REG_MAX; i++) { |
| * ams_setByte(portHndl, i, deviceRegisterDefinition[i].resetValue); |
| * } |
| */ |
| // To prevent SIDE EFFECT , below register should be written |
| err = ams_setByte(portHndl, DEVREG_CFG6, deviceRegisterDefinition[DEVREG_CFG6].resetValue); |
| if (err < 0) { |
| ALS_err("%s - failed to set DEVREG_CFG6\n", __func__); |
| return err; |
| } |
| err = ams_setByte(portHndl, DEVREG_AGC_GAIN_MAX, deviceRegisterDefinition[DEVREG_AGC_GAIN_MAX].resetValue); |
| if (err < 0) { |
| ALS_err("%s - failed to set DEVREG_AGC_GAIN_MAX\n", __func__); |
| return err; |
| } |
| |
| err = ams_setByte(portHndl, DEVREG_FD_CFG3, deviceRegisterDefinition[DEVREG_FD_CFG3].resetValue); |
| if (err < 0) { |
| ALS_err("%s - failed to set DEVREG_FD_CFG3\n", __func__); |
| return err; |
| } |
| |
| return err; |
| } |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_ALS_CCB |
| static int _3407_alsInit(ams_deviceCtx_t *ctx, ams_calibrationData_t *calibrationData) |
| { |
| int ret = 0; |
| |
| if (calibrationData == NULL) { |
| ams_ccb_als_info_t infoData; |
| |
| ALS_info("%s - calibrationData is null\n", __func__); |
| ccb_alsInfo(&infoData); |
| // ctx->ccbAlsCtx.initData.calibrationData.luxTarget = infoData.defaultCalibrationData.luxTarget; |
| // ctx->ccbAlsCtx.initData.calibrationData.luxTargetError = infoData.defaultCalibrationData.luxTargetError; |
| ctx->ccbAlsCtx.initData.calibrationData.calibrationFactor = infoData.defaultCalibrationData.calibrationFactor; |
| ctx->ccbAlsCtx.initData.calibrationData.Time_base = infoData.defaultCalibrationData.Time_base; |
| ctx->ccbAlsCtx.initData.calibrationData.thresholdLow = infoData.defaultCalibrationData.thresholdLow; |
| ctx->ccbAlsCtx.initData.calibrationData.thresholdHigh = infoData.defaultCalibrationData.thresholdHigh; |
| ctx->ccbAlsCtx.initData.calibrationData.calibrationFactor = infoData.defaultCalibrationData.calibrationFactor; |
| } else { |
| ALS_info("%s - calibrationData is non-null\n", __func__); |
| //ctx->ccbAlsCtx.initData.calibrationData.luxTarget = calibrationData->alsCalibrationLuxTarget; |
| //ctx->ccbAlsCtx.initData.calibrationData.luxTargetError = calibrationData->alsCalibrationLuxTargetError; |
| ctx->ccbAlsCtx.initData.calibrationData.calibrationFactor = calibrationData->alsCalibrationFactor; |
| ctx->ccbAlsCtx.initData.calibrationData.Time_base = calibrationData->timeBase_us; |
| ctx->ccbAlsCtx.initData.calibrationData.thresholdLow = calibrationData->alsThresholdLow; |
| ctx->ccbAlsCtx.initData.calibrationData.thresholdHigh = calibrationData->alsThresholdHigh; |
| //ctx->ccbAlsCtx.initData.calibrationData.calibrationFactor = calibrationData->alsCalibrationFactor; |
| } |
| ctx->ccbAlsCtx.initData.calibrate = false; |
| ctx->ccbAlsCtx.initData.configData.gain = 64000;//AGAIN |
| ctx->ccbAlsCtx.initData.configData.uSecTime = AMS_ALS_ATIME; /*ALS Inegration time 50msec*/ |
| |
| ctx->alwaysReadAls = false; |
| ctx->alwaysReadFlicker = false; |
| ctx->ccbAlsCtx.initData.autoGain = true; //AutoGainCtrol on |
| ctx->ccbAlsCtx.initData.hysteresis = 0x02; /*Lower threshold for adata in AGC */ |
| if (ctx->ccbAlsCtx.initData.autoGain) { |
| AMS_SET_ALS_AUTOGAIN(HIGH, ret); |
| if (ret < 0) { |
| ALS_err("%s - failed to AMS_SET_ALS_AUTOGAIN\n", __func__); |
| return ret; |
| } |
| /*******************************/ |
| /* |
| * - ALS_AGC_LOW_HYST - |
| * 0 -> 12.5 % |
| * 1 -> 25 % |
| * 2 -> 37.5 % |
| * 3 -> 50 % |
| * |
| * - ALS_AGC_HIGH_HYST - |
| * 0 -> 50 % |
| * 1 -> 62.5 % |
| * 2 -> 75 % |
| * 3 -> 87.5 % |
| */ |
| /*******************************/ |
| AMS_SET_ALS_AGC_LOW_HYST(0); // Low HYST -> 12.5 % |
| AMS_SET_ALS_AGC_HIGH_HYST(3); // High HYST -> 87.5 % |
| /* |
| * AMS_SET_ALS_AGC_HYST(ctx->ccbAlsCtx.initData.hysteresis, ret); |
| * if (ret < 0) { |
| * ALS_err("%s - failed to AMS_SET_ALS_AGC_HYST\n", __func__); |
| * return ret; |
| * } |
| */ |
| } |
| return ret; |
| } |
| |
| static bool ams_deviceGetAls(ams_deviceCtx_t *ctx, ams_apiAls_t *exportData) |
| { |
| ams_ccb_als_result_t result; |
| |
| ccb_alsGetResult(ctx, &result); |
| exportData->clear = result.clear; |
| exportData->red = result.red; |
| exportData->green = result.green; |
| exportData->blue = result.blue; |
| exportData->ir = result.ir; |
| exportData->time_us = result.time_us; |
| exportData->gain = result.gain; |
| // exportData->wideband = result.wideband; |
| exportData->rawClear = result.rawClear; |
| exportData->rawRed = result.rawRed; |
| exportData->rawGreen = result.rawGreen; |
| exportData->rawBlue = result.rawBlue; |
| exportData->rawWideband = result.rawWideband; |
| return false; |
| } |
| |
| static int _3407_handleAlsEvent(ams_deviceCtx_t *ctx) |
| { |
| int ret = 0; |
| ams_ccb_als_dataSet_t ccbAlsData; |
| |
| ccbAlsData.statusReg = ctx->shadowStatus1Reg; |
| ret = ccb_alsHandle(ctx, &ccbAlsData); |
| |
| return ret; |
| } |
| |
| #endif |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FLICKER |
| #ifndef CONFIG_AMS_OPTICAL_SENSOR_FIFO |
| static int _3407_flickerInit(ams_deviceCtx_t *ctx) |
| { |
| int err = 0; |
| ams_flicker_ctx_t *flickerCtx = (ams_flicker_ctx_t *)&ctx->flickerCtx; |
| |
| flickerCtx->lastValid.freq100Hz = NOT_VALID; |
| flickerCtx->lastValid.freq120Hz = NOT_VALID; |
| flickerCtx->lastValid.mHz = 0; |
| |
| err = ams_setByte(ctx->portHndl, DEVREG_FD_CFG0, ((FD_SAMPLES_128) | (FD_COMPARE_8_32NDS))); |
| if (err < 0) { |
| ALS_err("%s - failed to set DEVREG_FD_CFG0\n", __func__); |
| return err; |
| } |
| err = ams_setByte(ctx->portHndl, DEVREG_FD_CFG3, (FD_GAIN_128 | (1 & MASK_FD_TIME_MSBits))); |
| if (err < 0) { |
| ALS_err("%s - failed to set DEVREG_FD_CFG3\n", __func__); |
| return err; |
| } |
| err = ams_setField(ctx->portHndl, DEVREG_CFG10, FD_PERS_ALWAYS, MASK_FD_PERS); |
| if (err < 0) { |
| ALS_err("%s - failed to set DEVREG_CFG10\n", __func__); |
| return err; |
| } |
| |
| return err; |
| } |
| #endif |
| |
| static bool ams_deviceGetFlicker(ams_deviceCtx_t *ctx, ams_apiAlsFlicker_t *exportData) |
| { |
| ams_flicker_ctx_t *flickerCtx = (ams_flicker_ctx_t *)&ctx->flickerCtx; |
| |
| if (flickerCtx->statusReg & FD_100HZ_VALID) |
| exportData->freq100Hz = flickerCtx->lastValid.freq100Hz = |
| (flickerCtx->statusReg & FD_100HZ_FLICKER) ? PRESENT : ABSENT; |
| else |
| exportData->freq100Hz = flickerCtx->lastValid.freq100Hz; |
| |
| if (flickerCtx->statusReg & FD_120HZ_VALID) |
| exportData->freq120Hz = flickerCtx->lastValid.freq120Hz = |
| (flickerCtx->statusReg & FD_120HZ_FLICKER) ? PRESENT : ABSENT; |
| else |
| exportData->freq120Hz = flickerCtx->lastValid.freq120Hz; |
| |
| if ((exportData->freq100Hz == PRESENT) && (exportData->freq120Hz == PRESENT)) |
| exportData->mHz = flickerCtx->lastValid.mHz = (uint32_t)(ULONG_MAX); |
| else if (exportData->freq100Hz == PRESENT) |
| exportData->mHz = flickerCtx->lastValid.mHz = 100000; |
| else if (exportData->freq120Hz == PRESENT) |
| exportData->mHz = flickerCtx->lastValid.mHz = 120000; |
| else |
| exportData->mHz = 0; |
| return false; |
| } |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FIFO |
| static void _3407_handleFlickerFIFOEvent(ams_deviceCtx_t *ctx) |
| { |
| ams_ccb_als_dataSet_t ccbAlsData; |
| |
| ccb_FlickerFIFOEvent(ctx, &ccbAlsData); |
| } |
| #else |
| static int _3407_handleFlickerEvent(ams_deviceCtx_t *ctx) |
| { |
| int ret = 0; |
| ams_flicker_ctx_t *flickerCtx = (ams_flicker_ctx_t *)&ctx->flickerCtx; |
| |
| ret = ams_getByte(ctx->portHndl, DEVREG_FD_STATUS, &flickerCtx->statusReg); |
| if (ret < 0) { |
| ALS_err("%s - failed to get DEVREG_FD_STATUS\n", __func__); |
| return ret; |
| } |
| ret = ams_setByte(ctx->portHndl, DEVREG_FD_STATUS, MASK_CLEAR_FLICKER_STATUS); |
| if (ret < 0) { |
| ALS_err("%s - failed to set DEVREG_FD_STATUS\n", __func__); |
| return ret; |
| } |
| // ams_setByte(ctx->portHndl, DEVREG_FD_STATUS, flickerCtx->statusReg); |
| |
| if (flickerCtx->statusReg & MASK_FLICKER_VALID) |
| ctx->updateAvailable |= (1 << AMS_FLICKER_SENSOR); |
| ALS_info("%s - FD status = 0x%02x\n", __func__, flickerCtx->statusReg); |
| |
| return ret; |
| } |
| #endif |
| #endif |
| static int ams_deviceSoftReset(ams_deviceCtx_t *ctx) |
| { |
| int err = 0; |
| |
| ALS_dbg("%s - Start\n", __func__); |
| |
| // Before S/W reset, the PON has to be asserted |
| err = ams_setByte(ctx->portHndl, DEVREG_ENABLE, PON); |
| if (err < 0) { |
| ALS_err("%s - failed to set DEVREG_ENABLE\n", __func__); |
| return err; |
| } |
| |
| err = ams_setField(ctx->portHndl, DEVREG_SOFT_RESET, HIGH, MASK_SOFT_RESET); |
| if (err < 0) { |
| ALS_err("%s - failed to set DEVREG_SOFT_RESET\n", __func__); |
| return err; |
| } |
| // Need 1 msec delay |
| usleep_range(1000, 1100); |
| |
| // Recover the previous enable setting |
| err = ams_setByte(ctx->portHndl, DEVREG_ENABLE, ctx->shadowEnableReg); |
| if (err < 0) { |
| ALS_err("%s - failed to set DEVREG_ENABLE\n", __func__); |
| return err; |
| } |
| |
| return err; |
| } |
| |
| static ams_deviceIdentifier_e ams_validateDevice(AMS_PORT_portHndl *portHndl) |
| { |
| uint8_t chipId; |
| uint8_t revId; |
| uint8_t auxId; |
| uint8_t i = 0; |
| int err = 0; |
| |
| struct tcs3407_device_data *data = i2c_get_clientdata(portHndl); |
| |
| err = ams_getByte(portHndl, DEVREG_ID, &chipId); |
| if (err < 0) { |
| ALS_err("%s - failed to get DEVREG_ID\n", __func__); |
| return AMS_UNKNOWN_DEVICE; |
| } |
| err = ams_getByte(portHndl, DEVREG_REVID, &revId); |
| if (err < 0) { |
| ALS_err("%s - failed to get DEVREG_REVID\n", __func__); |
| return AMS_UNKNOWN_DEVICE; |
| } |
| |
| do { |
| if (((chipId & deviceIdentifier[i].deviceIdMask) == |
| (deviceIdentifier[i].deviceId & deviceIdentifier[i].deviceIdMask)) && |
| ((revId & deviceIdentifier[i].deviceRefMask) == |
| (deviceIdentifier[i].deviceRef & deviceIdentifier[i].deviceRefMask))) { |
| |
| err = ams_getByte(portHndl, DEVREG_AUXID, &auxId); |
| if (err < 0) { |
| ALS_err("%s - failed to get DEVREG_ID\n", __func__); |
| return AMS_UNKNOWN_DEVICE; |
| } |
| |
| if (auxId) |
| data->isTrimmed = 1; |
| else |
| data->isTrimmed = 0; |
| |
| ALS_dbg("%s - ID:0x%02x, revID:0x%02x, auxID:0x%02x\n", __func__, chipId, revId, auxId); |
| |
| return deviceIdentifier[i].device; |
| } |
| i++; |
| } while (deviceIdentifier[i].device != AMS_LAST_DEVICE); |
| |
| return AMS_UNKNOWN_DEVICE; |
| } |
| |
| static int ams_deviceInit(ams_deviceCtx_t *ctx, AMS_PORT_portHndl *portHndl, ams_calibrationData_t *calibrationData) |
| { |
| int ret = 0; |
| |
| ctx->portHndl = portHndl; |
| ctx->mode = MODE_OFF; |
| ctx->systemCalibrationData = calibrationData; |
| ctx->deviceId = ams_validateDevice(ctx->portHndl); |
| ctx->shadowEnableReg = deviceRegisterDefinition[DEVREG_ENABLE].resetValue; |
| ret = ams_deviceSoftReset(ctx); |
| if (ret < 0) { |
| ALS_err("%s - failed to ams_deviceSoftReset\n", __func__); |
| return ret; |
| } |
| |
| ret = _3407_resetAllRegisters(ctx->portHndl); |
| if (ret < 0) { |
| ALS_err("%s - failed to _3407_resetAllRegisters\n", __func__); |
| return ret; |
| } |
| |
| #ifdef TCS3408_USE_SMUX |
| /* |
| S-MUX Read/Write |
| 1 read configuration to ram Read smux configuration to RAM from smux chain |
| 2 write configuration from ram Write smux configuration from RAM to smux chain |
| */ |
| |
| ams_smux_set(ctx); |
| #endif |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_ALS_CCB |
| ret = _3407_alsInit(ctx, calibrationData); |
| if (ret < 0) { |
| ALS_err("%s - failed to _3407_alsInit\n", __func__); |
| return ret; |
| } |
| #endif |
| /* |
| * ret = ams_setByte(ctx->portHndl, DEVREG_ENABLE, ctx->shadowEnableReg); |
| * if (ret < 0) { |
| * ALS_err("%s - failed to set DEVREG_ENABLE\n", __func__); |
| * return ret; |
| * } |
| */ |
| return ret; |
| } |
| |
| static bool ams_getDeviceInfo(ams_deviceInfo_t *info, ams_deviceIdentifier_e deviceId) |
| { |
| memset(info, 0, sizeof(ams_deviceInfo_t)); |
| |
| info->defaultCalibrationData.timeBase_us = AMS_USEC_PER_TICK; |
| info->numberOfSubSensors = 0; |
| info->memorySize = sizeof(ams_deviceCtx_t); |
| |
| switch (deviceId) { |
| case AMS_TCS3407: |
| case AMS_TCS3407_UNTRIM: |
| info->deviceModel = "TCS3407"; |
| break; |
| case AMS_TCS3408: |
| case AMS_TCS3408_UNTRIM: |
| info->deviceModel = "TCS3408"; |
| break; |
| default: |
| info->deviceModel = "UNKNOWN"; |
| break; |
| } |
| |
| memcpy(info->defaultCalibrationData.deviceName, info->deviceModel, sizeof(info->defaultCalibrationData.deviceName)); |
| info->deviceName = "ALS/PRX/FLKR"; |
| info->driverVersion = "Alpha"; |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_ALS_CCB |
| { |
| /* TODO */ |
| ams_ccb_als_info_t infoData; |
| |
| ccb_alsInfo(&infoData); |
| info->tableSubSensors[info->numberOfSubSensors] = AMS_AMBIENT_SENSOR; |
| info->numberOfSubSensors++; |
| |
| info->alsSensor.driverName = infoData.algName; |
| info->alsSensor.adcBits = 8; |
| info->alsSensor.maxPolRate = 50; |
| info->alsSensor.activeCurrent_uA = 100; |
| info->alsSensor.standbyCurrent_uA = 5; |
| info->alsSensor.rangeMax = 1; |
| info->alsSensor.rangeMin = 0; |
| |
| info->defaultCalibrationData.alsCalibrationFactor = infoData.defaultCalibrationData.calibrationFactor; |
| // info->defaultCalibrationData.alsCalibrationLuxTarget = infoData.defaultCalibrationData.luxTarget; |
| // info->defaultCalibrationData.alsCalibrationLuxTargetError = infoData.defaultCalibrationData.luxTargetError; |
| #if defined(CONFIG_AMS_ALS_CRWBI) || defined(CONFIG_AMS_ALS_CRGBW) |
| info->tableSubSensors[info->numberOfSubSensors] = AMS_WIDEBAND_ALS_SENSOR; |
| info->numberOfSubSensors++; |
| #endif |
| } |
| #endif |
| return false; |
| } |
| |
| int tcs3407_probe(struct i2c_client *client, const struct i2c_device_id *id) |
| { |
| int err = -ENODEV; |
| struct device *dev = &client->dev; |
| static struct tcs3407_device_data *data; |
| struct amsdriver_i2c_platform_data *pdata = dev->platform_data; |
| ams_deviceInfo_t amsDeviceInfo; |
| ams_deviceIdentifier_e deviceId; |
| |
| ALS_dbg("%s - start\n", __func__); |
| /* check to make sure that the adapter supports I2C */ |
| if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { |
| ALS_err("%s - I2C_FUNC_I2C not supported\n", __func__); |
| return -ENODEV; |
| } |
| /* allocate some memory for the device */ |
| data = devm_kzalloc(dev, sizeof(struct tcs3407_device_data), GFP_KERNEL); |
| if (data == NULL) { |
| ALS_err("%s - couldn't allocate device data memory\n", __func__); |
| return -ENOMEM; |
| } |
| |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FIFO |
| data->flicker_data = devm_kzalloc(dev, sizeof(int)*FLICKER_DATA_CNT, GFP_KERNEL); |
| if (data == NULL) { |
| ALS_err("%s - couldn't allocate device flicker_data memory\n", __func__); |
| return -ENOMEM; |
| } |
| #endif |
| |
| tcs3407_data = data; |
| tcs3407_init_var(data); |
| |
| if (!pdata) { |
| pdata = devm_kzalloc(dev, sizeof(struct amsdriver_i2c_platform_data), |
| GFP_KERNEL); |
| if (pdata == NULL) { |
| ALS_err("%s - couldn't allocate device pdata memory\n", __func__); |
| goto err_malloc_pdata; |
| } |
| if (of_match_device(tcs3407_match_table, &client->dev)) |
| pdata->of_node = client->dev.of_node; |
| } |
| |
| data->client = client; |
| data->miscdev.minor = MISC_DYNAMIC_MINOR; |
| data->miscdev.name = MODULE_NAME_ALS; |
| #ifdef CONFIG_AMS_OPTICAL_SENSOR_FIFO |
| data->miscdev.fops = &tcs3407_fops; |
| #endif |
| data->miscdev.mode = S_IRUGO; |
| data->pdata = pdata; |
| i2c_set_clientdata(client, data); |
| ALS_info("%s client = %p\n", __func__, client); |
| |
| err = misc_register(&data->miscdev); |
| if (err < 0) { |
| ALS_err("%s - failed to misc device register\n", __func__); |
| goto err_misc_register; |
| } |
| mutex_init(&data->i2clock); |
| mutex_init(&data->activelock); |
| mutex_init(&data->suspendlock); |
| mutex_init(&data->flickerdatalock); |
| |
| err = tcs3407_parse_dt(data); |
| if (err < 0) { |
| ALS_err("%s - failed to parse dt\n", __func__); |
| err = -ENODEV; |
| goto err_parse_dt; |
| } |
| err = tcs3407_setup_gpio(data); |
| if (err) { |
| ALS_err("%s - failed to setup gpio\n", __func__); |
| goto err_setup_gpio; |
| } |
| err = tcs3407_power_ctrl(data, PWR_ON); |
| if (err < 0) { |
| ALS_err("%s - failed to power on ctrl\n", __func__); |
| goto err_power_on; |
| } |
| if (data->client->addr == TCS3407_SLAVE_I2C_ADDR_REVID_V0) { |
| ALS_dbg("%s - slave address is REVID_V0\n", __func__); |
| } else if (data->client->addr == TCS3407_SLAVE_I2C_ADDR_REVID_V1) { |
| ALS_dbg("%s - slave address is REVID_V1\n", __func__); |
| } else { |
| err = -EIO; |
| ALS_err("%s - slave address error, 0x%02x\n", __func__, data->client->addr); |
| goto err_init_fail; |
| } |
| |
| /********************************************************************/ |
| /* Validate the appropriate ams device is available for this driver */ |
| /********************************************************************/ |
| deviceId = ams_validateDevice(data->client); |
| |
| if (deviceId == AMS_UNKNOWN_DEVICE) { |
| ALS_err("%s - ams_validateDevice failed: AMS_UNKNOWN_DEVICE\n", __func__); |
| err = -EIO; |
| goto err_id_failed; |
| } |
| ALS_dbg("%s - deviceId: %d\n", __func__, deviceId); |
| |
| ams_getDeviceInfo(&amsDeviceInfo, deviceId); |
| ALS_dbg("%s - name: %s, model: %s, driver ver:%s\n", __func__, |
| amsDeviceInfo.deviceName, amsDeviceInfo.deviceModel, amsDeviceInfo.driverVersion); |
| |
| data->deviceCtx = devm_kzalloc(dev, amsDeviceInfo.memorySize, GFP_KERNEL); |
| if (data->deviceCtx == NULL) { |
| ALS_err("%s - couldn't allocate device deviceCtx memory\n", __func__); |
| err = -ENOMEM; |
| goto err_malloc_deviceCtx; |
| } |
| |
| err = ams_deviceInit(data->deviceCtx, data->client, NULL); |
| if (err < 0) { |
| ALS_err("%s - ams_deviceInit failed.\n", __func__); |
| goto err_id_failed; |
| } else { |
| ALS_dbg("%s - ams_amsDeviceInit ok\n", __func__); |
| } |
| |
| data->als_input_dev = input_allocate_device(); |
| if (!data->als_input_dev) { |
| ALS_err("%s - could not allocate input device\n", __func__); |
| err = -EIO; |
| goto err_input_allocate_device; |
| } |
| data->als_input_dev->name = MODULE_NAME_ALS; |
| data->als_input_dev->id.bustype = BUS_I2C; |
| input_set_drvdata(data->als_input_dev, data); |
| input_set_capability(data->als_input_dev, EV_REL, REL_X); |
| input_set_capability(data->als_input_dev, EV_REL, REL_Y); |
| input_set_capability(data->als_input_dev, EV_REL, REL_Z); |
| input_set_capability(data->als_input_dev, EV_REL, REL_RX); |
| input_set_capability(data->als_input_dev, EV_REL, REL_RY); |
| input_set_capability(data->als_input_dev, EV_REL, REL_RZ); |
| input_set_capability(data->als_input_dev, EV_REL, REL_MISC); |
| input_set_capability(data->als_input_dev, EV_ABS, ABS_X); |
| input_set_capability(data->als_input_dev, EV_ABS, ABS_Y); |
| input_set_capability(data->als_input_dev, EV_ABS, ABS_Z); |
| |
| err = input_register_device(data->als_input_dev); |
| if (err < 0) { |
| input_free_device(data->als_input_dev); |
| ALS_err("%s - could not register input device\n", __func__); |
| goto err_input_register_device; |
| } |
| #ifdef CONFIG_ARCH_QCOM |
| err = sensors_create_symlink(&data->als_input_dev->dev.kobj, |
| data->als_input_dev->name); |
| #else |
| err = sensors_create_symlink(data->als_input_dev); |
| #endif |
| if (err < 0) { |
| ALS_err("%s - could not create_symlink\n", __func__); |
| goto err_sensors_create_symlink; |
| } |
| err = sysfs_create_group(&data->als_input_dev->dev.kobj, |
| &als_attribute_group); |
| if (err) { |
| ALS_err("%s - could not create sysfs group\n", __func__); |
| goto err_sysfs_create_group; |
| } |
| #ifdef CONFIG_ARCH_QCOM |
| err = sensors_register(&data->dev, data, tcs3407_sensor_attrs, |
| MODULE_NAME_ALS); |
| #else |
| err = sensors_register(data->dev, data, tcs3407_sensor_attrs, |
| MODULE_NAME_ALS); |
| #endif |
| if (err) { |
| ALS_err("%s - cound not register als_sensor(%d).\n", __func__, err); |
| goto als_sensor_register_failed; |
| } |
| |
| err = tcs3407_setup_irq(data); |
| if (err) { |
| ALS_err("%s - could not setup dev_irq\n", __func__); |
| goto err_setup_irq; |
| } |
| |
| err = tcs3407_power_ctrl(data, PWR_OFF); |
| if (err < 0) { |
| ALS_err("%s - failed to power off ctrl\n", __func__); |
| goto dev_set_drvdata_failed; |
| } |
| ALS_dbg("%s - success\n", __func__); |
| goto done; |
| |
| dev_set_drvdata_failed: |
| free_irq(data->dev_irq, data); |
| err_setup_irq: |
| sensors_unregister(data->dev, tcs3407_sensor_attrs); |
| als_sensor_register_failed: |
| sysfs_remove_group(&data->als_input_dev->dev.kobj, |
| &als_attribute_group); |
| err_sysfs_create_group: |
| #ifdef CONFIG_ARCH_QCOM |
| sensors_remove_symlink(&data->als_input_dev->dev.kobj, |
| data->als_input_dev->name); |
| #else |
| sensors_remove_symlink(data->als_input_dev); |
| #endif |
| err_sensors_create_symlink: |
| input_unregister_device(data->als_input_dev); |
| err_input_register_device: |
| err_input_allocate_device: |
| err_id_failed: |
| // devm_kfree(data->deviceCtx); |
| err_malloc_deviceCtx: |
| err_init_fail: |
| tcs3407_power_ctrl(data, PWR_OFF); |
| err_power_on: |
| gpio_free(data->pin_als_int); |
| if (data->pin_als_en >= 0) |
| gpio_free(data->pin_als_en); |
| err_setup_gpio: |
| err_parse_dt: |
| // devm_kfree(pdata); |
| err_malloc_pdata: |
| if (data->als_pinctrl) { |
| devm_pinctrl_put(data->als_pinctrl); |
| data->als_pinctrl = NULL; |
| } |
| if (data->pins_idle) |
| data->pins_idle = NULL; |
| if (data->pins_sleep) |
| data->pins_sleep = NULL; |
| |
| mutex_destroy(&data->i2clock); |
| mutex_destroy(&data->activelock); |
| mutex_destroy(&data->suspendlock); |
| mutex_destroy(&data->flickerdatalock); |
| misc_deregister(&data->miscdev); |
| err_misc_register: |
| // devm_kfree(data); |
| ALS_err("%s failed\n", __func__); |
| done: |
| return err; |
| } |
| |
| int tcs3407_remove(struct i2c_client *client) |
| { |
| struct tcs3407_device_data *data = i2c_get_clientdata(client); |
| |
| ALS_dbg("%s - start\n", __func__); |
| tcs3407_power_ctrl(data, PWR_OFF); |
| |
| sensors_unregister(data->dev, tcs3407_sensor_attrs); |
| sysfs_remove_group(&data->als_input_dev->dev.kobj, |
| &als_attribute_group); |
| #ifdef CONFIG_ARCH_QCOM |
| sensors_remove_symlink(&data->als_input_dev->dev.kobj, |
| data->als_input_dev->name); |
| #else |
| sensors_remove_symlink(data->als_input_dev); |
| #endif |
| input_unregister_device(data->als_input_dev); |
| |
| if (data->als_pinctrl) { |
| devm_pinctrl_put(data->als_pinctrl); |
| data->als_pinctrl = NULL; |
| } |
| if (data->pins_idle) |
| data->pins_idle = NULL; |
| if (data->pins_sleep) |
| data->pins_sleep = NULL; |
| disable_irq(data->dev_irq); |
| free_irq(data->dev_irq, data); |
| gpio_free(data->pin_als_int); |
| if (data->pin_als_en >= 0) |
| gpio_free(data->pin_als_en); |
| mutex_destroy(&data->i2clock); |
| mutex_destroy(&data->activelock); |
| mutex_destroy(&data->suspendlock); |
| mutex_destroy(&data->flickerdatalock); |
| misc_deregister(&data->miscdev); |
| |
| // devm_kfree(data->deviceCtx); |
| // devm_kfree(data->pdata); |
| // devm_kfree(data); |
| i2c_set_clientdata(client, NULL); |
| |
| data = NULL; |
| return 0; |
| } |
| |
| static void tcs3407_shutdown(struct i2c_client *client) |
| { |
| ALS_dbg("%s - start\n", __func__); |
| } |
| |
| #ifdef CONFIG_PM |
| static int tcs3407_suspend(struct device *dev) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| int err = 0; |
| |
| ALS_dbg("%s - %d\n", __func__, data->enabled); |
| |
| if (data->enabled != 0 || data->regulator_state != 0) { |
| mutex_lock(&data->activelock); |
| |
| als_enable_set(data, AMSDRIVER_ALS_DISABLE); |
| |
| err = tcs3407_power_ctrl(data, PWR_OFF); |
| if (err < 0) |
| ALS_err("%s - als_regulator_off fail err = %d\n", |
| __func__, err); |
| tcs3407_irq_set_state(data, PWR_OFF); |
| |
| mutex_unlock(&data->activelock); |
| } |
| mutex_lock(&data->suspendlock); |
| |
| data->pm_state = PM_SUSPEND; |
| tcs3407_pin_control(data, false); |
| |
| mutex_unlock(&data->suspendlock); |
| |
| return err; |
| } |
| |
| static int tcs3407_resume(struct device *dev) |
| { |
| struct tcs3407_device_data *data = dev_get_drvdata(dev); |
| int err = 0; |
| |
| ALS_dbg("%s - %d\n", __func__, data->enabled); |
| |
| mutex_lock(&data->suspendlock); |
| |
| tcs3407_pin_control(data, true); |
| |
| data->pm_state = PM_RESUME; |
| |
| mutex_unlock(&data->suspendlock); |
| |
| if (data->enabled != 0) { |
| mutex_lock(&data->activelock); |
| |
| tcs3407_irq_set_state(data, PWR_ON); |
| |
| err = tcs3407_power_ctrl(data, PWR_ON); |
| if (err < 0) |
| ALS_err("%s - als_regulator_on fail err = %d\n", |
| __func__, err); |
| |
| als_enable_set(data, AMSDRIVER_ALS_ENABLE); |
| |
| if (err < 0) { |
| input_report_rel(data->als_input_dev, |
| REL_RZ, -5 + 1); /* F_ERR_I2C -5 detected i2c error */ |
| input_sync(data->als_input_dev); |
| ALS_err("%s - awb mode enable error : %d\n", __func__, err); |
| } |
| |
| mutex_unlock(&data->activelock); |
| } |
| return err; |
| } |
| |
| static const struct dev_pm_ops tcs3407_pm_ops = { |
| .suspend = tcs3407_suspend, |
| .resume = tcs3407_resume |
| }; |
| #endif |
| |
| static const struct i2c_device_id tcs3407_idtable[] = { |
| { "tcs3407", 0 }, |
| { } |
| }; |
| /* descriptor of the tcs3407 I2C driver */ |
| static struct i2c_driver tcs3407_driver = { |
| .driver = { |
| .name = "tcs3407", |
| .owner = THIS_MODULE, |
| #if defined(CONFIG_PM) |
| .pm = &tcs3407_pm_ops, |
| #endif |
| .of_match_table = tcs3407_match_table, |
| }, |
| .probe = tcs3407_probe, |
| .remove = tcs3407_remove, |
| .shutdown = tcs3407_shutdown, |
| .id_table = tcs3407_idtable, |
| }; |
| |
| /* initialization and exit functions */ |
| static int __init tcs3407_init(void) |
| { |
| if (!lpcharge) |
| return i2c_add_driver(&tcs3407_driver); |
| else |
| return 0; |
| } |
| |
| static void __exit tcs3407_exit(void) |
| { |
| i2c_del_driver(&tcs3407_driver); |
| } |
| |
| module_init(tcs3407_init); |
| module_exit(tcs3407_exit); |
| |
| MODULE_AUTHOR("Samsung Electronics"); |
| MODULE_DESCRIPTION("TCS3407 ALS Driver"); |
| MODULE_LICENSE("GPL"); |