blob: e21eced687f403420ab7abcddd4c33ff68efcd67 [file] [log] [blame]
/*
* Copyright (c) 2014 JY Kim, jy.kim@maximintegrated.com
* Copyright (c) 2013 Maxim Integrated Products, Inc.
*
* 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/>.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "hrmsensor.h"
#include "hrm_max86915.h"
extern int hrm_debug;
extern int hrm_info;
#define VENDOR "MAXIM"
#define MAX86915_CHIP_NAME "MAX86915"
#define VENDOR_VERISON "1"
#define VERSION "39"
#define MAX86915_I2C_RETRY_DELAY 10
#define MAX86915_I2C_MAX_RETRIES 5
/*MAX86915 Register Map*/
#define MAX86915_INTERRUPT_STATUS 0x00
#define PWR_RDY_MASK 0x01
#define PROX_INT_MASK 0x10
#define ALC_OVF_MASK 0x20
#define PPG_RDY_MASK 0x40
#define A_FULL_MASK 0x80
#define MAX86915_INTERRUPT_STATUS_2 0x01
#define MAX86915_INTERRUPT_ENABLE 0x02
#define MAX86915_INTERRUPT_ENABLE_2 0x03
#define MAX86915_FIFO_WRITE_POINTER 0x04
#define MAX86915_OVF_COUNTER 0x05
#define MAX86915_FIFO_READ_POINTER 0x06
#define MAX86915_FIFO_DATA 0x07
#define MAX86915_FIFO_CONFIG 0x08
#define MAX86915_FIFO_A_FULL_MASK 0x0F
#define MAX86915_FIFO_A_FULL_OFFSET 0x00
#define MAX86915_FIFO_ROLLS_ON_MASK 0x10
#define MAX86915_FIFO_ROLLS_ON_OFFSET 0x04
#define MAX86915_SMP_AVE_MASK 0xE0
#define MAX86915_SMP_AVE_OFFSET 0x05
#define MAX86915_MODE_CONFIGURATION 0x09
#define MAX86915_MODE_MASK 0x03
#define MAX86915_MODE_OFFSET 0x00
#define MAX86915_AMB_LIGHT_DET_MASK 0x04
#define MAX86915_AMB_LIGHT_DET_OFFSET 0x02
#define MAX86915_RESET_MASK 0x40
#define MAX86915_RESET_OFFSET 0x06
#define MAX86915_SHDN_MASK 0x80
#define MAX86915_SHDN_OFFSET 0x07
#define MAX86915_MODE_CONFIGURATION_2 0x0A
#define MAX86915_LED_PW_MASK 0x03
#define MAX86915_LED_PW_OFFSET 0x00
#define MAX86915_SR_MASK 0x1C
#define MAX86915_SR_OFFSET 0x02
#define MAX86915_ADC_RGE_MASK 0x60
#define MAX86915_ADC_RGE_OFFSET 0x05
#define MAX86915_LED1_PA 0x0C
#define MAX86915_LED2_PA 0x0D
#define MAX86915_LED3_PA 0x0E
#define MAX86915_LED4_PA 0x0F
#define MAX86915_LED_RANGE 0x11
#define MAX86915_LED1_RGE_MASK 0x03
#define MAX86915_LED1_RGE_OFFSET 0x00
#define MAX86915_LED2_RGE_MASK 0x0C
#define MAX86915_LED2_RGE_OFFSET 0x02
#define MAX86915_LED3_RGE_MASK 0x30
#define MAX86915_LED3_RGE_OFFSET 0x04
#define MAX86915_LED4_RGE_MASK 0xC0
#define MAX86915_LED4_RGE_OFFSET 0x06
#define MAX86915_PILOT_PA 0x12
#define MAX86915_LED_SEQ_REG_1 0x13
#define MAX86915_LED1_MASK 0x0F
#define MAX86915_LED1_OFFSET 0x00
#define MAX86915_LED2_MASK 0xF0
#define MAX86915_LED2_OFFSET 0x04
#define MAX86915_LED_SEQ_REG_2 0x14
#define MAX86915_LED3_MASK 0x0F
#define MAX86915_LED3_OFFSET 0x00
#define MAX86915_LED4_MASK 0xF0
#define MAX86915_LED4_OFFSET 0x04
#define MAX86915_DAC1_XTALK_CODE 0x26
#define MAX86915_DAC2_XTALK_CODE 0x27
#define MAX86915_DAC3_XTALK_CODE 0x28
#define MAX86915_DAC4_XTALK_CODE 0x29
#define MAX86915_REV_ID_REG 0xFE
#define MAX86915_PART_ID_REG 0xFF
/* Default Register Value */
#define MAX86915_PART_ID 0x2B
#define MAX86915_REV_ID 0x01
#define MAX86915_HRM_DEFAULT_CURRENT1 0x46 /* IR */
#define MAX86915_HRM_DEFAULT_CURRENT2 0x00 /* RED */
#define MAX86915_HRM_DEFAULT_CURRENT3 0x00 /* GREEN */
#define MAX86915_HRM_DEFAULT_CURRENT4 0x00 /* BLUE */
#define MAX86915_HRM_DEFAULT_XTALK_CODE1 0x00 /* IR */
#define MAX86915_HRM_DEFAULT_XTALK_CODE2 0x00 /* RED */
#define MAX86915_HRM_DEFAULT_XTALK_CODE3 0x00 /* GREEN */
#define MAX86915_HRM_DEFAULT_XTALK_CODE4 0x00 /* BLUE */
#define MAX86915_4CH_DEFAULT_CURRENT1 0xFF /* IR */
#define MAX86915_4CH_DEFAULT_CURRENT2 0xFF /* RED */
#define MAX86915_4CH_DEFAULT_CURRENT3 0x87 /* GREEN */
#define MAX86915_4CH_DEFAULT_CURRENT4 0xFF /* BLUE */
#define MAX86915_DEFAULT_XTALK_CODE1 0x00 /* IR */
#define MAX86915_DEFAULT_XTALK_CODE2 0x00 /* RED */
#define MAX86915_DEFAULT_XTALK_CODE3 0x00 /* GREEN */
#define MAX86915_DEFAULT_XTALK_CODE4 0x00 /* BLUE */
#define MAX86915_IR_INIT_CURRENT 0x46 /* IR */
#define MAX86915_RED_INIT_CURRENT 0x41 /* RED */
#define MAX86915_GREEN_INIT_CURRENT 0x28 /* GREEN */
#define MAX86915_BLUE_INIT_CURRENT 0x3C /* BLUE */
#define MAX86915_IR_CURRENT_STEP 0x04 /* IR */
#define MAX86915_RED_CURRENT_STEP 0x04 /* RED */
#define MAX86915_GREEN_CURRENT_STEP 0x03 /* GREEN */
#define MAX86915_BLUE_CURRENT_STEP 0x05 /* BLUE */
#define MAX86915_DEFAULT_LED_RGE 0x02 /* 150mA AGCCONF*/
/* AWB/Flicker Definition */
#define AWB_INTERVAL 20 /* 20 sample(from 17 to 28) */
#define CONFIG_SKIP_CNT 8
#define FLICKER_DATA_CNT 200
#define MAX86915_IOCTL_MAGIC 0xFD
#define MAX86915_IOCTL_READ_FLICKER _IOR(MAX86915_IOCTL_MAGIC, 0x01, int *)
#define AWB_MAX86915_CONFIG_TH2 480000
#define AWB_MAX86915_CONFIG_TH3 40000
/* Configuration Paramter */
#define AGC_LED_SKIP_CNT 16
/* AGC Configuration */
#define AGC_IR 0
#define AGC_RED 1
#define AGC_GREEN 2
#define AGC_BLUE 3
#define AGC_SKIP_CNT 5
#define MAX86915_MIN_CURRENT 0
#define MAX86915_MAX_CURRENT ((MAX86915_DEFAULT_LED_RGE+1)*51000)
#define MAX86915_CURRENT_FULL_SCALE \
(MAX86915_MAX_CURRENT - MAX86915_MIN_CURRENT)
#define MAX86915_MIN_DIODE_VAL 0
#define MAX86915_MAX_DIODE_VAL ((1 << 19) - 1)
#define MAX86915_CURRENT_PER_STEP ((MAX86915_DEFAULT_LED_RGE+1) * 200)
#define MAX86915_AGC_DEFAULT_LED_OUT_RANGE 70
#define MAX86915_AGC_DEFAULT_CORRECTION_COEFF 50
#define MAX86915_AGC_DEFAULT_SENSITIVITY_PERCENT 14
#define MAX86915_AGC_DEFAULT_MIN_NUM_PERCENT (10)
/* Linear AGC */
#define MAX86915_AGC_ERROR_RANGE1 8
#define MAX86915_AGC_ERROR_RANGE2 28
#define ILLEGAL_OUTPUT_POINTER -1
#define CONSTRAINT_VIOLATION -2
/* Fixed Value Definition */
#define IR_LED_CH 1
#define RED_LED_CH 2
#define GREEN_LED_CH 3
#define BLUE_LED_CH 4
#define NUM_BYTES_PER_SAMPLE 3
#define MAX_LED_NUM 4
#define MAX86915_COUNT_MAX 65532
#define MAX86915_FIFO_SIZE 32
#define MAX86915_READ_REGFILE_PATH "/data/HRM/MAX86915_READ_REG.txt"
#define MAX86915_WRITE_REGFILE_PATH "/data/HRM/MAX86915_WRITE_REG.txt"
#define HRM_LDO_ON 1
#define HRM_LDO_OFF 0
#define I2C_WRITE_ENABLE 0x00
#define I2C_READ_ENABLE 0x01
#define MAX86915_THRESHOLD_DEFAULT 100000
#define DEFAULT_FIFO_CNT 1
#define CONFIG_4CH_INPUT
#define CONFIG_4CH_800HZ
/* #define CONFIG_HRM_GREEN_BLUE */
#define MAX_EOL_RESULT 256
/* End of Line Test */
#define SELF_IR_CH 0
#define SELF_RED_CH 1
#define SELF_GREEN_CH 2
#define SELF_BLUE_CH 3
#define SELF_MAX_CH_NUM 4
#define CONVER_FLOAT 65536
#define INTEGER_LEN 16
#define DECIMAL_LEN 10
#define SELF_SQ1_DEFAULT_SPO2 0x07
#define MAX_I2C_BLOCK_SIZE 252
#define SELF_MODE_400HZ 0
#define SELF_MODE_100HZ 1
#define SELF_DIVID_400HZ 1
#define SELF_DIVID_100HZ 4
enum _PART_TYPE {
PART_TYPE_NONE = 0,
PART_TYPE_MAX86915_ES = 30,
PART_TYPE_MAX86915_CS_15 = 31,
PART_TYPE_MAX86915_CS_21 = 32,
PART_TYPE_MAX86915_CS_22 = 33,
PART_TYPE_MAX86915_CS_211 = 34,
PART_TYPE_MAX86915_CS_221 = 35,
} PART_TYPE;
enum _AWB_CONFIG {
AWB_CONFIG0 = 0,
AWB_CONFIG1,
AWB_CONFIG2,
} AWB_CONFIG;
enum {
LED1_UP = 1 << 0,
LED1_DOWN = 1 << 1,
LED2_UP = 1 << 2,
LED2_DOWN = 1 << 3,
LED1_SET = 1 << 4,
LED2_SET = 1 << 5,
} LED_CON;
static enum {
S_INIT = 0,
S_F_A,
S_CAL,
S_F_D,
S_ERR,
S_UNKNOWN
} agc_pre_s, agc_cur_s;
enum {
DEBUG_WRITE_REG_TO_FILE = 1,
DEBUG_WRITE_FILE_TO_REG = 2,
DEBUG_SHOW_DEBUG_INFO = 3,
DEBUG_ENABLE_AGC = 4,
DEBUG_DISABLE_AGC = 5,
};
enum {
M_HRM,
M_HRMLED_IR,
M_HRMLED_RED,
M_HRMLED_BOTH,
M_SDK,
M_NONE
};
/* to protect the system performance issue. */
union int_status {
struct {
struct {
unsigned char pwr_rdy:1;
unsigned char:1;
unsigned char:1;
unsigned char uv_rdy:1;
unsigned char prox_int:1;
unsigned char alc_ovf:1;
unsigned char ppg_rdy:1;
unsigned char a_full:1;
};
struct {
unsigned char:1;
unsigned char die_temp_rdy:1;
unsigned char ecg_imp_rdy:1;
unsigned char:3;
unsigned char blue_rdy:1;
unsigned char vdd_oor:1;
};
};
u8 val[2];
};
struct max86915_div_data {
char left_integer[INTEGER_LEN];
char left_decimal[DECIMAL_LEN];
char right_integer[INTEGER_LEN];
char right_decimal[DECIMAL_LEN];
char result_integer[INTEGER_LEN];
char result_decimal[DECIMAL_LEN];
};
enum _EOL_STATE_TYPE_NEW {
_EOL_STATE_TYPE_NEW_INIT = 0,
_EOL_STATE_TYPE_NEW_FLICKER_INIT, /* step 1. flicker_step. */
_EOL_STATE_TYPE_NEW_FLICKER_MODE,
_EOL_STATE_TYPE_NEW_DC_INIT, /* step 2. dc_step. */
_EOL_STATE_TYPE_NEW_DC_MODE_LOW,
_EOL_STATE_TYPE_NEW_DC_MODE_MID,
_EOL_STATE_TYPE_NEW_DC_MODE_HIGH,
_EOL_STATE_TYPE_NEW_DC_XTALK,
_EOL_STATE_TYPE_NEW_FREQ_INIT, /* step 3. freq_step. */
_EOL_STATE_TYPE_NEW_FREQ_MODE,
_EOL_STATE_TYPE_NEW_END,
_EOL_STATE_TYPE_NEW_STOP,
} _EOL_STATE_TYPE_NEW;
/* turnning parameter of dc_step. */
#define EOL_NEW_HRM_ARRY_SIZE 4
#define EOL_NEW_MODE_DC_LOW 0
#define EOL_NEW_MODE_DC_MID 1
#define EOL_NEW_MODE_DC_HIGH 2
#define EOL_NEW_MODE_DC_XTALK 3
/* turnning parameter of skip count */
#define EOL_NEW_START_SKIP_CNT 134
/* turnning parameter of flicker_step. */
#define EOL_NEW_FLICKER_RETRY_CNT 48
#define EOL_NEW_FLICKER_SKIP_CNT 34
#define EOL_NEW_FLICKER_THRESH 8000
#define EOL_NEW_DC_MODE_SKIP_CNT 134
#define EOL_NEW_DC_FREQ_SKIP_CNT 134
#define EOL_NEW_DC_LOW_SKIP_CNT 134
#define EOL_NEW_DC_MIDDLE_SKIP_CNT 134
#define EOL_NEW_DC_HIGH_SKIP_CNT 134
#define EOL_NEW_DC_XTALK_SKIP_CNT 134
/* turnning parameter of frep_step. */
#define EOL_NEW_FREQ_MIN 385
#define EOL_NEW_FREQ_RETRY_CNT 5
#define EOL_NEW_FREQ_SKEW_RETRY_CNT 5
#define EOL_NEW_FREQ_SKIP_CNT 10
/* total buffer size. */
#define EOL_NEW_HRM_SIZE 400 /* <=this size should be lager than below size. */
#define EOL_NEW_FLICKER_SIZE 256
#define EOL_NEW_DC_LOW_SIZE 256
#define EOL_NEW_DC_MIDDLE_SIZE 256
#define EOL_NEW_DC_HIGH_SIZE 256
#define EOL_NEW_DC_XTALK_SIZE 256
/* the led current of DC step */
#define EOL_NEW_DC_LOW_LED_IR_CURRENT 0x53
#define EOL_NEW_DC_LOW_LED_RED_CURRENT 0x53
#define EOL_NEW_DC_LOW_LED_GREEN_CURRENT 0x53
#define EOL_NEW_DC_LOW_LED_BLUE_CURRENT 0x53
#define EOL_NEW_DC_MID_LED_IR_CURRENT 0xA6
#define EOL_NEW_DC_MID_LED_RED_CURRENT 0xA6
#define EOL_NEW_DC_MID_LED_GREEN_CURRENT 0xA6
#define EOL_NEW_DC_MID_LED_BLUE_CURRENT 0xA6
#define EOL_NEW_DC_HIGH_LED_IR_CURRENT 0xFA
#define EOL_NEW_DC_HIGH_LED_RED_CURRENT 0xFA
#define EOL_NEW_DC_HIGH_LED_GREEN_CURRENT 0xE9
#define EOL_NEW_DC_HIGH_LED_BLUE_CURRENT 0xE9
/* the led current of Frequency step */
#define EOL_NEW_FREQUENCY_LED_IR_CURRENT 0xFA
#define EOL_NEW_FREQUENCY_LED_RED_CURRENT 0xFA
#define EOL_NEW_FREQUENCY_LED_GREEN_CURRENT 0xFA
#define EOL_NEW_FREQUENCY_LED_BLUE_CURRENT 0xFA
#define EOL_NEW_HRM_COMPLETE_ALL_STEP 0x0F
#define EOL_SUCCESS 0x1
#define EOL_NEW_TYPE 0x1
/* step flicker mode */
struct max86915_eol_flicker_data {
int count;
int state;
int retry;
int index;
u64 max;
u64 buf[SELF_RED_CH][EOL_NEW_FLICKER_SIZE];
u64 sum[SELF_RED_CH];
u64 average[SELF_RED_CH];
u64 std_sum[SELF_RED_CH];
int done;
};
/* step dc level */
struct max86915_eol_hrm_data {
int count;
int index;
int ir_current;
int red_current;
int green_current;
int blue_current;
u64 buf[SELF_MAX_CH_NUM][EOL_NEW_HRM_SIZE];
u64 sum[SELF_MAX_CH_NUM];
u64 average[SELF_MAX_CH_NUM];
u64 std_sum[SELF_MAX_CH_NUM];
int done;
};
/* step freq_step */
struct max86915_eol_freq_data {
int state;
int retry;
int skew_retry;
unsigned long end_time;
unsigned long start_time;
struct timeval start_time_t;
struct timeval work_time_t;
unsigned long prev_time;
int count;
int skip_count;
int done;
};
struct max86915_eol_data {
int eol_count;
u8 state;
int eol_hrm_flag;
struct timeval start_time;
struct timeval end_time;
struct max86915_eol_flicker_data eol_flicker_data;
struct max86915_eol_hrm_data eol_hrm_data[EOL_NEW_HRM_ARRY_SIZE];
struct max86915_eol_freq_data eol_freq_data; /* test the ir routine. */
};
/* max86915 Data Structure */
struct max869_device_data {
struct i2c_client *client;
struct mutex flickerdatalock;
struct miscdevice miscdev;
s32 hrm_mode;
u8 agc_mode;
u8 eol_test_status;
char *eol_test_result;
u8 part_type;
u8 flex_mode;
s32 num_samples;
u16 sample_cnt;
u8 awb_flicker_status;
u8 eol_test_is_enable;
u8 pre_eol_test_is_enable;
u8 led_current1;
u8 led_current2;
u8 led_current3;
u8 led_current4;
u8 default_current1;
u8 default_current2;
u8 default_current3;
u8 default_current4;
u8 init_current[4];
u8 xtalk_code1;
u8 xtalk_code2;
u8 xtalk_code3;
u8 xtalk_code4;
u8 default_xtalk_code1;
u8 default_xtalk_code2;
u8 default_xtalk_code3;
u8 default_xtalk_code4;
u8 mode_sdk_enabled;
u16 awb_sample_cnt;
int *flicker_data;
int flicker_data_cnt;
/* AGC routine start */
u8 ir_led;
u8 red_led;
u8 green_led;
u8 blue_led;
bool agc_is_enable;
int agc_sum[4];
u32 agc_current[4];
u8 agc_led_set;
s32 agc_led_out_percent;
s32 agc_corr_coeff;
s32 agc_min_num_samples;
s32 agc_sensitivity_percent;
s32 change_by_percent_of_range[4];
s32 change_by_percent_of_current_setting[4];
s32 change_led_by_absolute_count[4];
s32 reached_thresh[4];
s32 threshold_default;
int (*update_led)(struct max869_device_data*, int, int);
/* AGC routine end */
struct max86915_eol_data eol_data;
int isTrimmed;
u32 i2c_err_cnt;
u16 ir_curr;
u16 red_curr;
u16 green_curr;
u16 blue_curr;
u32 ir_adc;
u32 red_adc;
u32 green_adc;
u32 blue_adc;
u32 agc_ch_adc[4];
int agc_enabled;
int fifo_data[MAX_LED_NUM][MAX86915_FIFO_SIZE];
int fifo_samples;
int vdd_oor_cnt;
u16 agc_sample_cnt[4];
int prev_ppg[MAX_LED_NUM];
int prev_current[MAX_LED_NUM];
int prev_agc_current[MAX_LED_NUM];
};
static void __max869_init_eol_data(struct max86915_eol_data *eol_data);
static void __max869_eol_flicker_data(int ir_data, struct max86915_eol_data *eol_data);
static void __max869_eol_hrm_data(int ir_data, int red_data, int green_data, int blue_data,
struct max86915_eol_data *eol_data, u32 array_pos, u32 eol_skip_cnt, u32 eol_step_size);
static void __max869_eol_freq_data(struct max86915_eol_data *eol_data, u8 divid);
static int __max869_eol_check_done(struct max86915_eol_data *eol_data, u8 mode, struct max869_device_data *device);
static void __max869_div_float(struct max86915_div_data *div_data, u64 left_operand, u64 right_operand);
static void __max869_eol_conv2float(struct max86915_eol_data *eol_data, u8 mode, struct max869_device_data *device);
static void __max869_div_str(char *left_integer, char *left_decimal, char *right_integer,
char *right_decimal, char *result_integer, char *result_decimal);
static void __max869_float_sqrt(char *integer_operand, char *decimal_operand);
static void __max869_plus_str(char *integer_operand, char *decimal_operand, char *result_integer, char *result_decimal);
static struct max869_device_data *max869_data;
static struct hrm_device_data *hrm_data;
static u8 agc_is_enabled = 1;
static u8 fifo_full_cnt = DEFAULT_FIFO_CNT;
static int __max869_write_default_regs(void)
{
int err = 0;
return err;
}
static int __max869_write_reg(struct max869_device_data *device,
u8 reg_addr, u8 data)
{
int err;
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 (hrm_data == NULL) {
HRM_dbg("%s - write error, hrm_data is null\n", __func__);
err = -EFAULT;
return err;
}
mutex_lock(&hrm_data->suspendlock);
if (!hrm_data->pm_state) {
HRM_dbg("%s - write error, pm suspend\n", __func__);
err = -EFAULT;
mutex_unlock(&hrm_data->suspendlock);
return err;
}
do {
err = i2c_transfer(device->client->adapter, msgs, num);
if (err != num)
msleep_interruptible(MAX86915_I2C_RETRY_DELAY);
if (err < 0)
HRM_dbg("%s - i2c_transfer error = %d\n", __func__, err);
} while ((err != num) && (++tries < MAX86915_I2C_MAX_RETRIES));
mutex_unlock(&hrm_data->suspendlock);
if (err != num) {
HRM_dbg("%s -write transfer error:%d\n", __func__, err);
err = -EIO;
device->i2c_err_cnt++;
return err;
}
return 0;
}
static int __max869_read_reg(struct max869_device_data *data,
u8 *buffer, int length)
{
int err = -1;
int tries = 0; /* # of attempts to read the device */
u8 addr = buffer[0];
int num = 2;
struct i2c_msg msgs[] = {
{
.addr = data->client->addr,
.flags = data->client->flags & I2C_M_TEN,
.len = 1,
.buf = buffer,
},
{
.addr = data->client->addr,
.flags = (data->client->flags & I2C_M_TEN) | I2C_M_RD,
.len = length,
.buf = buffer,
},
};
if (hrm_data == NULL) {
HRM_dbg("%s - read error, hrm_data is null\n", __func__);
err = -EFAULT;
return err;
}
mutex_lock(&hrm_data->suspendlock);
if (!hrm_data->pm_state) {
HRM_dbg("%s - read error, pm suspend\n", __func__);
err = -EFAULT;
mutex_unlock(&hrm_data->suspendlock);
return err;
}
do {
buffer[0] = addr;
err = i2c_transfer(data->client->adapter, msgs, num);
if (err != num)
msleep_interruptible(MAX86915_I2C_RETRY_DELAY);
if (err < 0)
HRM_dbg("%s - i2c_transfer error = %d\n", __func__, err);
} while ((err != num) && (++tries < MAX86915_I2C_MAX_RETRIES));
mutex_unlock(&hrm_data->suspendlock);
if (err != num) {
HRM_dbg("%s -read transfer error:%d\n", __func__, err);
err = -EIO;
data->i2c_err_cnt++;
} else
err = 0;
return err;
}
static int __max86915_hrm_enable(struct max869_device_data *data)
{
int err, i;
u8 flex_config[2] = {0, };
data->sample_cnt = 0;
data->agc_led_set = 0;
data->agc_ch_adc[0] = 0;
data->agc_ch_adc[1] = 0;
data->agc_ch_adc[2] = 0;
data->agc_ch_adc[3] = 0;
flex_config[0] = (RED_LED_CH << MAX86915_LED2_OFFSET) | IR_LED_CH;
flex_config[1] = 0x00;
#ifdef CONFIG_HRM_GREEN_BLUE
flex_config[1] = (BLUE_LED_CH << MAX86915_LED4_OFFSET) | GREEN_LED_CH;
#endif
data->num_samples = 2;
data->flex_mode = 3;
#ifdef CONFIG_HRM_GREEN_BLUE
data->num_samples = 4;
data->flex_mode = 0xf;
#endif
data->led_current1 = data->init_current[AGC_IR];
data->led_current2 = MAX86915_HRM_DEFAULT_CURRENT2;
#ifdef CONFIG_HRM_GREEN_BLUE
data->led_current3 = MAX86915_HRM_DEFAULT_CURRENT3;
data->led_current4 = MAX86915_HRM_DEFAULT_CURRENT4;
#endif
data->xtalk_code1 = MAX86915_HRM_DEFAULT_XTALK_CODE1;
data->xtalk_code2 = MAX86915_HRM_DEFAULT_XTALK_CODE2;
#ifdef CONFIG_HRM_GREEN_BLUE
data->xtalk_code3 = MAX86915_HRM_DEFAULT_XTALK_CODE3;
data->xtalk_code4 = MAX86915_HRM_DEFAULT_XTALK_CODE4;
#endif
HRM_info("%s - flexmode : 0x%02x, num_samples : %d\n", __func__,
data->flex_mode, data->num_samples);
/*write LED currents ir=1, red=2, violet=4*/
err = __max869_write_reg(data, MAX86915_LED1_PA,
data->led_current1);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED1_PA!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED2_PA,
data->led_current2);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED2_PA!\n",
__func__);
return -EIO;
}
#ifdef CONFIG_HRM_GREEN_BLUE
err = __max869_write_reg(data, MAX86915_LED3_PA,
data->led_current3);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED3_PA!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED4_PA,
data->led_current4);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED4_PA!\n",
__func__);
return -EIO;
}
#endif
/* LED Range */
err = __max869_write_reg(data, MAX86915_LED_RANGE,
(MAX86915_DEFAULT_LED_RGE << MAX86915_LED1_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE << MAX86915_LED2_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE << MAX86915_LED3_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE << MAX86915_LED4_RGE_OFFSET));
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED_RANGE!\n",
__func__);
return -EIO;
}
/* XTALK */
err = __max869_write_reg(data, MAX86915_DAC1_XTALK_CODE,
data->xtalk_code1);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC1_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_DAC2_XTALK_CODE,
data->xtalk_code2);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC2_XTALK_CODE!\n",
__func__);
return -EIO;
}
#ifdef CONFIG_HRM_GREEN_BLUE
err = __max869_write_reg(data, MAX86915_DAC3_XTALK_CODE,
data->xtalk_code3);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC3_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_DAC4_XTALK_CODE,
data->xtalk_code4);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC4_XTALK_CODE!\n",
__func__);
return -EIO;
}
#endif
err = __max869_write_reg(data, MAX86915_INTERRUPT_ENABLE, PPG_RDY_MASK);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_INTERRUPT_ENABLE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED_SEQ_REG_1,
flex_config[0]);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED_SEQ_REG_1!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED_SEQ_REG_2,
flex_config[1]);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED_SEQ_REG_2!\n",
__func__);
return -EIO;
}
/* 400 Hz Sampling Rate */
err = __max869_write_reg(data, MAX86915_MODE_CONFIGURATION_2,
0x0D | (0x03 << MAX86915_ADC_RGE_OFFSET));
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_FIFO_CONFIG,
(0x02 << MAX86915_SMP_AVE_OFFSET) & MAX86915_SMP_AVE_MASK);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_MODE_CONFIGURATION, 0x03);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
/* AGC control */
data->agc_current[AGC_IR] =
(data->led_current1 * MAX86915_CURRENT_PER_STEP);
data->agc_current[AGC_RED] =
(data->led_current2 * MAX86915_CURRENT_PER_STEP);
#ifdef CONFIG_HRM_GREEN_BLUE
data->agc_current[AGC_GREEN] =
(data->led_current3 * MAX86915_CURRENT_PER_STEP);
data->agc_current[AGC_BLUE] =
(data->led_current4 * MAX86915_CURRENT_PER_STEP);
#endif
for (i = 0; i < data->num_samples; i++) {
data->reached_thresh[i] = 0;
data->agc_sum[i] = 0;
data->prev_current[i] = 0;
data->prev_agc_current[i] = MAX86915_MAX_CURRENT;
data->prev_ppg[i] = 0;
}
return 0;
}
static int __max86915_sdk_enable(struct max869_device_data *data)
{
int err, i;
u8 flex_config[2] = {0, };
data->sample_cnt = 0;
data->agc_led_set = 0;
data->agc_ch_adc[0] = 0;
data->agc_ch_adc[1] = 0;
data->agc_ch_adc[2] = 0;
data->agc_ch_adc[3] = 0;
data->num_samples = 4;
data->flex_mode = 0xf;
flex_config[0] = (RED_LED_CH << MAX86915_LED2_OFFSET) | IR_LED_CH;
flex_config[1] = (BLUE_LED_CH << MAX86915_LED4_OFFSET) | GREEN_LED_CH;
data->led_current1 = MAX86915_MIN_CURRENT;
data->led_current2 = MAX86915_MIN_CURRENT;
data->led_current3 = MAX86915_MIN_CURRENT;
data->led_current4 = MAX86915_MIN_CURRENT;
/*
* data->led_current1 = (data->mode_sdk_enabled & (1<<0)) ? 0xff : 0x00;
* data->led_current2 = (data->mode_sdk_enabled & (1<<1)) ? 0xff : 0x00;
* data->led_current3 = (data->mode_sdk_enabled & (1<<2)) ? 0xff : 0x00;
* data->led_current4 = (data->mode_sdk_enabled & (1<<3)) ? 0xff : 0x00;
*
* data->xtalk_code1 = MAX86915_DEFAULT_XTALK_CODE1;
* data->xtalk_code2 = MAX86915_DEFAULT_XTALK_CODE2;
* data->xtalk_code3 = MAX86915_DEFAULT_XTALK_CODE3;
* data->xtalk_code4 = MAX86915_DEFAULT_XTALK_CODE4;
*/
HRM_info("%s - flexmode : 0x%02x, num_samples : %d\n", __func__,
data->flex_mode, data->num_samples);
/* write LED currents ir=1, red=2, violet=4 */
err = __max869_write_reg(data, MAX86915_LED1_PA,
data->led_current1);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED1_PA!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED2_PA,
data->led_current2);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED2_PA!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED3_PA,
data->led_current3);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED3_PA!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED4_PA,
data->led_current4);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED4_PA!\n",
__func__);
return -EIO;
}
/* LED Range */
err = __max869_write_reg(data, MAX86915_LED_RANGE,
(MAX86915_DEFAULT_LED_RGE << MAX86915_LED1_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE << MAX86915_LED2_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE << MAX86915_LED3_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE << MAX86915_LED4_RGE_OFFSET));
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED_RANGE!\n",
__func__);
return -EIO;
}
/* XTALK */
err = __max869_write_reg(data, MAX86915_DAC1_XTALK_CODE,
data->xtalk_code1);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC1_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_DAC2_XTALK_CODE,
data->xtalk_code2);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC2_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_DAC3_XTALK_CODE,
data->xtalk_code3);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC3_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_DAC4_XTALK_CODE,
data->xtalk_code4);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC4_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_INTERRUPT_ENABLE, PPG_RDY_MASK);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_INTERRUPT_ENABLE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED_SEQ_REG_1,
flex_config[0]);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED_SEQ_REG_1!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED_SEQ_REG_2,
flex_config[1]);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED_SEQ_REG_2!\n",
__func__);
return -EIO;
}
#ifdef ENABLE_POLL_DELAY
if (fifo_full_cnt == 8) {
/* 16uA, 800Hz, 70us */
err = __max869_write_reg(max869_data, MAX86915_MODE_CONFIGURATION_2,
0x50);
/* 1 Samples avg */
err = __max869_write_reg(max869_data, MAX86915_FIFO_CONFIG,
0x1f);
} else if (fifo_full_cnt == 4) {
/* 16uA, 400Hz, 120us */
err = __max869_write_reg(max869_data, MAX86915_MODE_CONFIGURATION_2,
0x50);
/* 2 Samples avg */
err = __max869_write_reg(max869_data, MAX86915_FIFO_CONFIG,
0x3f);
} else if (fifo_full_cnt == 2) {
/* 16uA, 200Hz, 120us */
err = __max869_write_reg(max869_data, MAX86915_MODE_CONFIGURATION_2,
0x50);
/* 4 Samples avg */
err = __max869_write_reg(max869_data, MAX86915_FIFO_CONFIG,
0x5f);
} else if (fifo_full_cnt == 1) {
/* 16uA, 400Hz, 120us */
err = __max869_write_reg(max869_data, MAX86915_MODE_CONFIGURATION_2,
0x4D);
/* 4 Samples avg */
err = __max869_write_reg(max869_data, MAX86915_FIFO_CONFIG,
0x5f);
}
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
HRM_dbg("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
#else
/* 400 Hz Sampling Rate & 120us Pulse Width */
err = __max869_write_reg(data, MAX86915_MODE_CONFIGURATION_2,
0x0D | (0x03 << MAX86915_ADC_RGE_OFFSET));
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_FIFO_CONFIG,
(0x02 << MAX86915_SMP_AVE_OFFSET) & MAX86915_SMP_AVE_MASK);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
#endif
err = __max869_write_reg(data, MAX86915_MODE_CONFIGURATION, 0x03);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
/* AGC control */
data->agc_current[AGC_IR] =
(data->led_current1 * MAX86915_CURRENT_PER_STEP);
data->agc_current[AGC_RED] =
(data->led_current2 * MAX86915_CURRENT_PER_STEP);
data->agc_current[AGC_GREEN] =
(data->led_current3 * MAX86915_CURRENT_PER_STEP);
data->agc_current[AGC_BLUE] =
(data->led_current4 * MAX86915_CURRENT_PER_STEP);
for (i = 0; i < data->num_samples; i++) {
data->reached_thresh[i] = 0;
data->agc_sum[i] = 0;
data->prev_current[i] = 0;
data->prev_agc_current[i] = MAX86915_MAX_CURRENT;
data->prev_ppg[i] = 0;
}
return 0;
}
static int __max86915_disable(struct max869_device_data *data)
{
int err;
err = __max869_write_reg(data, MAX86915_MODE_CONFIGURATION, MAX86915_RESET_MASK);
if (err != 0) {
HRM_dbg("%s - error init MAX86915_MODE_CONFIGURATION!\n",
__func__);
goto i2c_err;
}
err = __max869_write_reg(data, MAX86915_MODE_CONFIGURATION, MAX86915_SHDN_MASK);
if (err != 0) {
HRM_dbg("%s - error init MAX86915_MODE_CONFIGURATION!\n",
__func__);
goto i2c_err;
}
data->awb_sample_cnt = 0;
data->flicker_data_cnt = 0;
data->mode_sdk_enabled = 0;
data->led_current1 = 0;
data->led_current2 = 0;
data->led_current3 = 0;
data->led_current4 = 0;
return 0;
i2c_err:
return -EIO;
}
static int __max86915_init_device(struct max869_device_data *data)
{
int err = 0;
u8 recvData;
err = __max869_write_reg(data, MAX86915_MODE_CONFIGURATION, MAX86915_RESET_MASK);
if (err != 0) {
HRM_dbg("%s - error sw shutdown device!\n", __func__);
return -EIO;
}
/* Interrupt Clear */
recvData = MAX86915_INTERRUPT_STATUS;
err = __max869_read_reg(data, &recvData, 1);
if (err != 0) {
HRM_dbg("%s - __max869_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
/* Interrupt2 Clear */
recvData = MAX86915_INTERRUPT_STATUS_2;
err = __max869_read_reg(data, &recvData, 1);
if (err != 0) {
HRM_dbg("%s - __max869_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
HRM_info("%s done, part_type = %u\n", __func__, data->part_type);
return err;
}
static int __max869_init(struct max869_device_data *data)
{
int err = 0;
__max86915_init_device(data);
if (err < 0)
HRM_dbg("%s init fail, err = %d\n", __func__, err);
err = __max869_write_default_regs();
HRM_info("%s init done, err = %d\n", __func__, err);
data->agc_enabled = 1;
return err;
}
static int __max869_enable(struct max869_device_data *data)
{
int err = 0;
if (data->hrm_mode == MODE_SDK_IR)
err = __max86915_sdk_enable(data);
else
err = __max86915_hrm_enable(data);
if (err < 0)
HRM_dbg("%s enable fail, err = %d\n", __func__, err);
HRM_info("%s enable done, err = %d\n", __func__, err);
return err;
}
static int __max869_disable(struct max869_device_data *data)
{
int err = 0;
err = __max86915_disable(data);
if (err < 0)
HRM_dbg("%s enable fail, err = %d\n", __func__, err);
HRM_info("%s enable done, err = %d\n", __func__, err);
return err;
}
static int __max869_set_reg_hrm(struct max869_device_data *data)
{
int err = 0;
err = __max869_init(data);
err = __max869_enable(data);
data->agc_mode = M_HRM;
data->agc_enabled = 0;
return err;
}
static int __max869_set_reg_sdk(struct max869_device_data *data)
{
int err = 0;
err = __max869_init(data);
err = __max869_enable(data);
data->agc_mode = M_SDK;
/* data->agc_enabled = 0; */
return err;
}
static int __max869_set_reg_ambient(struct max869_device_data *data)
{
int err = 0;
u8 recvData = 0;
err = __max869_init(data);
err = __max869_enable(data);
/* Mode change to AWB */
err = __max869_write_reg(data,
MAX86915_MODE_CONFIGURATION, MAX86915_RESET_MASK);
if (err != 0) {
HRM_dbg("%s - error sw shutdown data!\n", __func__);
return -EIO;
}
/* Interrupt Clear */
recvData = MAX86915_INTERRUPT_STATUS;
err = __max869_read_reg(data, &recvData, 1);
if (err != 0) {
HRM_dbg("%s __max869_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
/* Interrupt2 Clear */
recvData = MAX86915_INTERRUPT_STATUS_2;
err = __max869_read_reg(data, &recvData, 1);
if (err != 0) {
HRM_dbg("%s __max869_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
/* 400Hz, LED_PW=400us, SPO2_ADC_RANGE=4096nA */
err = __max869_write_reg(data,
MAX86915_MODE_CONFIGURATION_2, 0x0F);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data,
MAX86915_FIFO_CONFIG, ((32 - AWB_INTERVAL) & MAX86915_FIFO_A_FULL_MASK));
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data,
MAX86915_INTERRUPT_ENABLE, A_FULL_MASK);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_INTERRUPT_ENABLE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED_SEQ_REG_1,
0x01);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED_SEQ_REG_1!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data,
MAX86915_MODE_CONFIGURATION, 0x07);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
data->awb_sample_cnt = 0;
data->flicker_data_cnt = 0;
data->awb_flicker_status = AWB_CONFIG1;
data->agc_mode = M_NONE;
return err;
}
static int __max869_set_reg_prox(struct max869_device_data *data)
{
int err = 0;
err = __max869_init(data);
err = __max869_enable(data);
max869_set_current(0xff, 0, 0, 0);
data->agc_mode = M_NONE;
return err;
}
static int max86915_update_led_current(struct max869_device_data *data,
int new_led_uA,
int led_num)
{
int err;
u8 led_reg_val;
int led_reg_addr;
u8 recvData;
if (new_led_uA > MAX86915_MAX_CURRENT) {
HRM_dbg("%s - Tried to set LED%d to %duA. Max is %duA\n",
__func__, led_num, new_led_uA, MAX86915_MAX_CURRENT);
new_led_uA = MAX86915_MAX_CURRENT;
} else if (new_led_uA < MAX86915_MIN_CURRENT) {
HRM_dbg("%s - Tried to set LED%d to %duA. Min is %duA\n",
__func__, led_num, new_led_uA, MAX86915_MIN_CURRENT);
new_led_uA = MAX86915_MIN_CURRENT;
}
led_reg_val = new_led_uA / MAX86915_CURRENT_PER_STEP;
led_reg_addr = MAX86915_LED1_PA + led_num;
HRM_dbg("%s - Setting ADD 0x%x, LED%d to 0x%.2X (%duA)\n",
__func__, led_reg_addr, led_num, led_reg_val, new_led_uA);
if (max869_data->part_type < PART_TYPE_MAX86915_CS_211) {
err = __max869_write_reg(data, MAX86915_MODE_CONFIGURATION, 0x00);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION!\n", __func__);
return -EIO;
}
}
err = __max869_write_reg(data, led_reg_addr, led_reg_val);
if (max869_data->part_type < PART_TYPE_MAX86915_CS_211) {
recvData = MAX86915_FIFO_CONFIG;
err = __max869_read_reg(data, &recvData, 1);
data->agc_led_set = AGC_LED_SKIP_CNT >> ((recvData & MAX86915_SMP_AVE_MASK) >> MAX86915_SMP_AVE_OFFSET);
err = __max869_write_reg(data, MAX86915_MODE_CONFIGURATION, 0x03);
}
if (err != 0) {
HRM_dbg("%s - error initializing register 0x%.2X!\n",
__func__, led_reg_addr);
return -EIO;
}
switch (led_num) {
case AGC_IR:
data->led_current1 = led_reg_val; /* set ir */
break;
case AGC_RED:
data->led_current2 = led_reg_val; /* set red */
break;
case AGC_GREEN:
data->led_current3 = led_reg_val; /* set green */
break;
case AGC_BLUE:
data->led_current4 = led_reg_val; /* set blue */
break;
}
return 0;
}
#ifdef MAXIM_AGC
static int agc_adj_calculator(struct max869_device_data *data,
s32 *change_by_percent_of_range,
s32 *change_by_percent_of_current_setting,
s32 *change_led_by_absolute_count,
s32 *set_led_to_absolute_count,
s32 target_percent_of_range,
s32 correction_coefficient,
s32 allowed_error_in_percentage,
s32 *reached_thresh,
s32 current_average,
s32 number_of_samples_averaged,
s32 led_drive_current_value)
{
s32 diode_min_val;
s32 diode_max_val;
s32 diode_min_current;
s32 diode_fs_current;
s32 current_percent_of_range = 0;
s32 delta = 0;
s32 desired_delta = 0;
s32 current_power_percent = 0;
if (change_by_percent_of_range == 0
|| change_by_percent_of_current_setting == 0
|| change_led_by_absolute_count == 0
|| set_led_to_absolute_count == 0)
return ILLEGAL_OUTPUT_POINTER;
if (target_percent_of_range > 90 || target_percent_of_range < 10)
return CONSTRAINT_VIOLATION;
if (correction_coefficient > 100 || correction_coefficient < 0)
return CONSTRAINT_VIOLATION;
if (allowed_error_in_percentage > 100
|| allowed_error_in_percentage < 0)
return CONSTRAINT_VIOLATION;
#if ((MAX86915_MAX_DIODE_VAL-MAX86915_MIN_DIODE_VAL) <= 0 \
|| (MAX86915_MAX_DIODE_VAL < 0) || (MAX86915_MIN_DIODE_VAL < 0))
#error "Illegal max86915 diode Min/Max Pair"
#endif
#if ((MAX86915_CURRENT_FULL_SCALE) <= 0 \
|| (MAX86915_MAX_CURRENT < 0) || (MAX86915_MIN_CURRENT < 0))
#error "Illegal max86915 LED Min/Max current Pair"
#endif
diode_min_val = MAX86915_MIN_DIODE_VAL;
diode_max_val = MAX86915_MAX_DIODE_VAL;
diode_min_current = MAX86915_MIN_CURRENT;
diode_fs_current = MAX86915_CURRENT_FULL_SCALE;
if (led_drive_current_value > MAX86915_MAX_CURRENT
|| led_drive_current_value < MAX86915_MIN_CURRENT)
return CONSTRAINT_VIOLATION;
if (current_average < MAX86915_MIN_DIODE_VAL
|| current_average > MAX86915_MAX_DIODE_VAL)
return CONSTRAINT_VIOLATION;
current_percent_of_range = 100 *
(current_average - diode_min_val) /
(diode_max_val - diode_min_val);
delta = current_percent_of_range - target_percent_of_range;
delta = delta * correction_coefficient / 100;
if (!(*reached_thresh))
allowed_error_in_percentage =
allowed_error_in_percentage * 2 / 7;
if (delta > -allowed_error_in_percentage
&& delta < allowed_error_in_percentage) {
*change_by_percent_of_range = 0;
*change_by_percent_of_current_setting = 0;
*change_led_by_absolute_count = 0;
*set_led_to_absolute_count = led_drive_current_value;
*reached_thresh = 1;
return 0;
}
current_power_percent = 100 *
(led_drive_current_value - diode_min_current) /
diode_fs_current;
if (delta < 0) {
if (current_power_percent > 97
&& (current_percent_of_range
< (data->threshold_default * 100) / (diode_max_val - diode_min_val))) {
desired_delta = -delta * (102 - current_power_percent) /
(100 - current_percent_of_range);
} else
desired_delta = -delta * (100 - current_power_percent) /
(100 - current_percent_of_range);
}
if (delta > 0)
desired_delta = -delta * (current_power_percent)
/ (current_percent_of_range);
*change_by_percent_of_range = desired_delta;
*change_led_by_absolute_count = (desired_delta
* diode_fs_current / 100);
*change_by_percent_of_current_setting =
(*change_led_by_absolute_count * 100)
/ (led_drive_current_value);
*set_led_to_absolute_count = led_drive_current_value
+ *change_led_by_absolute_count;
if (*set_led_to_absolute_count > MAX86915_MAX_CURRENT)
*set_led_to_absolute_count = MAX86915_MAX_CURRENT;
return 0;
}
#else
int __linear_search_agc(struct max869_device_data *data, int ppg_average, int led_num)
{
int xtalk_adc = 0;
int target_adc = MAX86915_MAX_DIODE_VAL * data->agc_led_out_percent / 100;
long long target_current;
int agc_range = MAX86915_AGC_ERROR_RANGE1;
if (ppg_average < MAX86915_MIN_DIODE_VAL
|| ppg_average > MAX86915_MAX_DIODE_VAL)
return CONSTRAINT_VIOLATION;
HRM_info("%s, (%d) ppg_average : %d\n", __func__, led_num, ppg_average);
if (ppg_average == MAX86915_MAX_DIODE_VAL) {
if (data->prev_agc_current[led_num] >= data->agc_current[led_num])
target_current = data->agc_current[led_num] / 2;
else
target_current = (data->agc_current[led_num] + data->prev_agc_current[led_num]) / 2;
if (target_current > MAX86915_MAX_CURRENT)
target_current = MAX86915_MAX_CURRENT;
else if (target_current < MAX86915_MIN_CURRENT)
target_current = MAX86915_MIN_CURRENT;
data->prev_agc_current[led_num] = data->agc_current[led_num];
data->change_led_by_absolute_count[led_num] = target_current - data->agc_current[led_num];
data->agc_current[led_num] = target_current;
return 0;
} else if (ppg_average == MAX86915_MIN_DIODE_VAL) {
if (data->prev_agc_current[led_num] < data->agc_current[led_num])
data->prev_agc_current[led_num] = MAX86915_MAX_CURRENT;
target_current = (data->agc_current[led_num]
+ data->prev_agc_current[led_num] + MAX86915_CURRENT_PER_STEP) / 2;
if (target_current > MAX86915_MAX_CURRENT)
target_current = MAX86915_MAX_CURRENT;
else if (target_current < MAX86915_MIN_CURRENT)
target_current = MAX86915_MIN_CURRENT;
data->prev_agc_current[led_num] = data->agc_current[led_num];
data->change_led_by_absolute_count[led_num] = target_current - data->agc_current[led_num];
data->agc_current[led_num] = target_current;
return 0;
}
if (data->reached_thresh[led_num])
agc_range = MAX86915_AGC_ERROR_RANGE2;
HRM_info("%s, (%d) target : %d, ppg_average : %d\n", __func__, led_num, target_adc, ppg_average);
if (ppg_average < MAX86915_MAX_DIODE_VAL * (data->agc_led_out_percent + agc_range) / 100
&& ppg_average > MAX86915_MAX_DIODE_VAL * (data->agc_led_out_percent - agc_range) / 100) {
data->reached_thresh[led_num] = 1;
data->change_led_by_absolute_count[led_num] = 0;
return 0;
}
switch (led_num) {
case AGC_IR:
xtalk_adc = MAX86915_MAX_DIODE_VAL / 2 * data->xtalk_code1 / 31;
target_adc += xtalk_adc;
break;
case AGC_RED:
xtalk_adc = MAX86915_MAX_DIODE_VAL / 2 * data->xtalk_code2 / 31;
target_adc += xtalk_adc;
break;
case AGC_GREEN:
xtalk_adc = MAX86915_MAX_DIODE_VAL / 2 * data->xtalk_code3 / 31;
target_adc += xtalk_adc;
break;
case AGC_BLUE:
xtalk_adc = MAX86915_MAX_DIODE_VAL / 2 * data->xtalk_code4 / 31;
target_adc += xtalk_adc;
break;
default:
return -EIO;
}
HRM_info("%s, (%d) target2 : %d\n", __func__, led_num, target_adc);
target_current = target_adc - (ppg_average + xtalk_adc);
if (data->agc_current[led_num] > data->prev_current[led_num])
target_current *= (data->agc_current[led_num] - data->prev_current[led_num]);
else if (data->agc_current[led_num] < data->prev_current[led_num])
target_current *= (data->prev_current[led_num] - data->agc_current[led_num]);
if (ppg_average + xtalk_adc > data->prev_ppg[led_num])
target_current /= (ppg_average + xtalk_adc - data->prev_ppg[led_num]);
else if (ppg_average + xtalk_adc < data->prev_ppg[led_num])
target_current /= (data->prev_ppg[led_num] - (ppg_average + xtalk_adc));
target_current += data->agc_current[led_num];
if (target_current > MAX86915_MAX_CURRENT)
target_current = MAX86915_MAX_CURRENT;
else if (target_current < MAX86915_MIN_CURRENT)
target_current = MAX86915_MIN_CURRENT;
data->change_led_by_absolute_count[led_num] = target_current - data->agc_current[led_num];
data->prev_current[led_num] = data->agc_current[led_num];
data->agc_current[led_num] = target_current;
data->prev_ppg[led_num] = ppg_average + xtalk_adc;
HRM_info("%s, data->agc_current[%d] : %u, %lld\n",
__func__, led_num, data->agc_current[led_num], target_current);
return 0;
}
#endif
int __max869_hrm_agc(struct max869_device_data *data, int counts, int led_num)
{
int err = 0;
int avg = 0;
/* HRM_info("%s - led_num(%d) = %d, %d\n", __func__, led_num, counts, data->agc_sample_cnt[led_num]); */
if (led_num < AGC_IR || led_num > AGC_BLUE)
return -EIO;
data->agc_sum[led_num] += counts;
data->agc_sample_cnt[led_num]++;
if (data->agc_sample_cnt[led_num] < data->agc_min_num_samples)
return 0;
data->agc_sample_cnt[led_num] = 0;
avg = data->agc_sum[led_num] / data->agc_min_num_samples;
data->agc_sum[led_num] = 0;
/* HRM_info("%s - led_num(%d) = %d, %d\n", __func__, led_num, counts, avg); */
#ifdef MAXIM_AGC
err = agc_adj_calculator(data,
&data->change_by_percent_of_range[led_num],
&data->change_by_percent_of_current_setting[led_num],
&data->change_led_by_absolute_count[led_num],
&data->agc_current[led_num],
data->agc_led_out_percent,
data->agc_corr_coeff,
data->agc_sensitivity_percent,
&data->reached_thresh[led_num],
avg,
data->agc_min_num_samples,
data->agc_current[led_num]);
#else
err = __linear_search_agc(data, avg, led_num);
#endif
if (err)
return err;
/* HRM_info("%s - %d-a:%d\n", __func__, led_num, data->change_led_by_absolute_count[led_num]); */
if (data->change_led_by_absolute_count[led_num] == 0) {
if (led_num == AGC_IR) {
data->ir_adc = counts;
data->ir_curr = data->led_current1;
} else if (led_num == AGC_RED) {
data->red_adc = counts;
data->red_curr = data->led_current2;
} else if (led_num == AGC_GREEN) {
data->green_adc = counts;
data->green_curr = data->led_current3;
} else if (led_num == AGC_BLUE) {
data->blue_adc = counts;
data->blue_curr = data->led_current4;
}
/*
* HRM_info("%s - ir_curr = 0x%2x, red_curr = 0x%2x, ir_adc = %d, red_adc = %d\n",
* __func__, data->ir_curr, data->red_curr, data->ir_adc, data->red_adc);
*/
return 0;
}
err = data->update_led(data, data->agc_current[led_num], led_num);
/* skip 2 sample after changing current */
max869_data->agc_enabled = 0;
max869_data->sample_cnt = AGC_SKIP_CNT - 2;
if (err)
HRM_dbg("%s failed\n", __func__);
return err;
}
static int __max869_cal_agc(int ir, int red, int green, int blue)
{
int err = 0;
int led_num0, led_num1, led_num2, led_num3;
led_num0 = AGC_IR;
led_num1 = AGC_RED;
led_num2 = AGC_GREEN;
led_num3 = AGC_BLUE;
if (max869_data->agc_mode == M_HRM) {
if (ir >= max869_data->threshold_default) { /* Detect */
err |= __max869_hrm_agc(max869_data, ir, led_num0); /* IR */
if (max869_data->agc_current[led_num1] != MAX86915_MIN_CURRENT) {
err |= __max869_hrm_agc(max869_data, red, led_num1); /* RED */
} else {
/* init */
max869_data->agc_current[led_num1]
= max869_data->init_current[led_num1] * MAX86915_CURRENT_PER_STEP;
max869_data->agc_enabled = 0;
max869_data->sample_cnt = 0;
max869_data->reached_thresh[led_num1] = 0;
max869_data->prev_current[led_num1] = 0;
max869_data->prev_agc_current[led_num1] = MAX86915_MAX_CURRENT;
max869_data->prev_ppg[led_num1] = 0;
max869_data->update_led(max869_data,
max869_data->agc_current[led_num1], led_num1);
}
#ifdef CONFIG_HRM_GREEN_BLUE
if (max869_data->agc_current[led_num2] != MAX86915_MIN_CURRENT) {
err |= __max869_hrm_agc(max869_data, green, led_num2); /* GREEN */
} else {
/* init */
max869_data->agc_current[led_num2]
= max869_data->init_current[led_num2] * MAX86915_CURRENT_PER_STEP;
max869_data->agc_enabled = 0;
max869_data->sample_cnt = 0;
max869_data->reached_thresh[led_num2] = 0;
max869_data->prev_current[led_num2] = 0;
max869_data->prev_agc_current[led_num2] = MAX86915_MAX_CURRENT;
max869_data->prev_ppg[led_num2] = 0;
max869_data->update_led(max869_data,
max869_data->agc_current[led_num2], led_num2);
}
if (max869_data->agc_current[led_num3] != MAX86915_MIN_CURRENT) {
err |= __max869_hrm_agc(max869_data, blue, led_num3); /* BLUE */
} else {
/* init */
max869_data->agc_current[led_num3]
= max869_data->init_current[led_num3] * MAX86915_CURRENT_PER_STEP;
max869_data->agc_enabled = 0;
max869_data->sample_cnt = 0;
max869_data->reached_thresh[led_num3] = 0;
max869_data->prev_current[led_num3] = 0;
max869_data->prev_agc_current[led_num3] = MAX86915_MAX_CURRENT;
max869_data->prev_ppg[led_num3] = 0;
max869_data->update_led(max869_data,
max869_data->agc_current[led_num3], led_num3);
}
#endif
} else {
if (max869_data->agc_current[led_num1] != MAX86915_MIN_CURRENT) { /* Disable led_num1 */
/* init */
max869_data->agc_current[led_num1] = MAX86915_MIN_CURRENT;
max869_data->reached_thresh[led_num1] = 0;
max869_data->prev_current[led_num1] = 0;
max869_data->prev_agc_current[led_num1] = MAX86915_MAX_CURRENT;
max869_data->prev_ppg[led_num1] = 0;
max869_data->update_led(max869_data,
max869_data->agc_current[led_num1], led_num1);
}
#ifdef CONFIG_HRM_GREEN_BLUE
if (max869_data->agc_current[led_num2] != MAX86915_MIN_CURRENT) { /* Disable led_num2 */
/* init */
max869_data->agc_current[led_num2] = MAX86915_MIN_CURRENT;
max869_data->reached_thresh[led_num2] = 0;
max869_data->prev_current[led_num2] = 0;
max869_data->prev_agc_current[led_num2] = MAX86915_MAX_CURRENT;
max869_data->prev_ppg[led_num2] = 0;
max869_data->update_led(max869_data,
max869_data->agc_current[led_num2], led_num2);
}
if (max869_data->agc_current[led_num3] != MAX86915_MIN_CURRENT) { /* Disable led_num3 */
/* init */
max869_data->agc_current[led_num3] = MAX86915_MIN_CURRENT;
max869_data->reached_thresh[led_num3] = 0;
max869_data->prev_current[led_num3] = 0;
max869_data->prev_agc_current[led_num3] = MAX86915_MAX_CURRENT;
max869_data->prev_ppg[led_num3] = 0;
max869_data->update_led(max869_data,
max869_data->agc_current[led_num3], led_num3);
}
#endif
if (max869_data->agc_current[led_num0]
!= max869_data->init_current[led_num0] * MAX86915_CURRENT_PER_STEP) {
/* init */
max869_data->agc_current[led_num0]
= max869_data->init_current[led_num0] * MAX86915_CURRENT_PER_STEP;
max869_data->agc_enabled = 0;
max869_data->sample_cnt = 0;
max869_data->reached_thresh[led_num0] = 0;
max869_data->prev_current[led_num0] = 0;
max869_data->prev_agc_current[led_num0] = MAX86915_MAX_CURRENT;
max869_data->prev_ppg[led_num0] = 0;
max869_data->update_led(max869_data,
max869_data->agc_current[led_num0], led_num0);
}
}
} else if (max869_data->agc_mode == M_HRMLED_IR) {
if (max869_data->agc_current[led_num0] != MAX86915_MIN_CURRENT) {
err |= __max869_hrm_agc(max869_data, ir, led_num0); /* IR */
} else {
max869_data->agc_current[led_num0] = MAX86915_MAX_CURRENT;
max869_data->agc_enabled = 0;
max869_data->sample_cnt = 0;
max869_data->reached_thresh[led_num0] = 0;
max869_data->prev_current[led_num0] = 0;
max869_data->prev_agc_current[led_num0] = MAX86915_MAX_CURRENT;
max869_data->prev_ppg[led_num0] = 0;
max869_data->update_led(max869_data,
max869_data->agc_current[led_num0], led_num0);
}
if (max869_data->agc_current[led_num1] != 0) { /* Disable led_num1 */
/* init */
max869_data->agc_current[led_num1] = 0;
max869_data->reached_thresh[led_num1] = 0;
max869_data->prev_current[led_num1] = 0;
max869_data->prev_agc_current[led_num1] = MAX86915_MAX_CURRENT;
max869_data->prev_ppg[led_num1] = 0;
max869_data->update_led(max869_data,
max869_data->agc_current[led_num1], led_num1);
}
} else if (max869_data->agc_mode == M_HRMLED_RED) {
if (max869_data->agc_current[led_num1] != MAX86915_MIN_CURRENT) {
err |= __max869_hrm_agc(max869_data, red, led_num1); /* RED */
} else {
max869_data->agc_current[led_num1] = MAX86915_MAX_CURRENT;
max869_data->agc_enabled = 0;
max869_data->sample_cnt = 0;
max869_data->reached_thresh[led_num1] = 0;
max869_data->prev_current[led_num1] = 0;
max869_data->prev_agc_current[led_num1] = MAX86915_MAX_CURRENT;
max869_data->prev_ppg[led_num1] = 0;
max869_data->update_led(max869_data,
max869_data->agc_current[led_num1], led_num1);
}
if (max869_data->agc_current[led_num0] != 0) { /* Disable led_num0 */
/* init */
max869_data->agc_current[led_num0] = 0;
max869_data->reached_thresh[led_num0] = 0;
max869_data->prev_current[led_num0] = 0;
max869_data->prev_agc_current[led_num0] = MAX86915_MAX_CURRENT;
max869_data->prev_ppg[led_num0] = 0;
max869_data->update_led(max869_data,
max869_data->agc_current[led_num0], led_num0);
}
} else if (max869_data->agc_mode == M_HRMLED_BOTH) {
if (max869_data->agc_current[led_num0] != MAX86915_MIN_CURRENT) {
err |= __max869_hrm_agc(max869_data, ir, led_num0); /* IR */
} else {
max869_data->agc_current[led_num0] = MAX86915_MAX_CURRENT;
max869_data->agc_enabled = 0;
max869_data->sample_cnt = 0;
max869_data->reached_thresh[led_num0] = 0;
max869_data->prev_current[led_num0] = 0;
max869_data->prev_agc_current[led_num0] = MAX86915_MAX_CURRENT;
max869_data->prev_ppg[led_num0] = 0;
max869_data->update_led(max869_data,
max869_data->agc_current[led_num0], led_num0);
}
if (max869_data->agc_current[led_num1] != MAX86915_MIN_CURRENT) {
err |= __max869_hrm_agc(max869_data, red, led_num1); /* RED */
} else {
max869_data->agc_current[led_num1] = MAX86915_MAX_CURRENT;
max869_data->agc_enabled = 0;
max869_data->sample_cnt = 0;
max869_data->reached_thresh[led_num1] = 0;
max869_data->prev_current[led_num1] = 0;
max869_data->prev_agc_current[led_num1] = MAX86915_MAX_CURRENT;
max869_data->prev_ppg[led_num1] = 0;
max869_data->update_led(max869_data,
max869_data->agc_current[led_num1], led_num1);
}
} else if (max869_data->agc_mode == M_SDK) {
if (max869_data->mode_sdk_enabled & 0x01) { /* IR channel */
if (max869_data->agc_current[led_num0] != MAX86915_MIN_CURRENT) {
err |= __max869_hrm_agc(max869_data, ir, led_num0);
} else {
max869_data->agc_current[led_num0]
= max869_data->default_current1 * MAX86915_CURRENT_PER_STEP;
max869_data->agc_enabled = 0;
max869_data->sample_cnt = 0;
max869_data->reached_thresh[led_num0] = 0;
max869_data->prev_current[led_num0] = 0;
max869_data->prev_agc_current[led_num0] = MAX86915_MAX_CURRENT;
max869_data->prev_ppg[led_num0] = 0;
max869_data->update_led(max869_data,
max869_data->agc_current[led_num0], led_num0);
}
}
if (max869_data->mode_sdk_enabled & 0x02) { /* RED channel */
if (max869_data->agc_current[led_num1] != MAX86915_MIN_CURRENT) {
err |= __max869_hrm_agc(max869_data, red, led_num1);
} else {
max869_data->agc_current[led_num1]
= max869_data->default_current2 * MAX86915_CURRENT_PER_STEP;
max869_data->agc_enabled = 0;
max869_data->sample_cnt = 0;
max869_data->reached_thresh[led_num1] = 0;
max869_data->prev_current[led_num1] = 0;
max869_data->prev_agc_current[led_num1] = MAX86915_MAX_CURRENT;
max869_data->prev_ppg[led_num1] = 0;
max869_data->update_led(max869_data,
max869_data->agc_current[led_num1], led_num1);
}
}
if (max869_data->mode_sdk_enabled & 0x04) { /* Green channel */
if (max869_data->agc_current[led_num2] != MAX86915_MIN_CURRENT) {
err |= __max869_hrm_agc(max869_data, green, led_num2);
} else {
max869_data->agc_current[led_num2]
= max869_data->default_current3 * MAX86915_CURRENT_PER_STEP;
max869_data->agc_enabled = 0;
max869_data->sample_cnt = 0;
max869_data->reached_thresh[led_num2] = 0;
max869_data->prev_current[led_num2] = 0;
max869_data->prev_agc_current[led_num2] = MAX86915_MAX_CURRENT;
max869_data->prev_ppg[led_num2] = 0;
max869_data->update_led(max869_data,
max869_data->agc_current[led_num2], led_num2);
}
}
if (max869_data->mode_sdk_enabled & 0x08) { /* BLUE channel */
if (max869_data->agc_current[led_num3] != MAX86915_MIN_CURRENT) {
err |= __max869_hrm_agc(max869_data, blue, led_num3);
} else {
max869_data->agc_current[led_num3]
= max869_data->default_current4 * MAX86915_CURRENT_PER_STEP;
max869_data->agc_enabled = 0;
max869_data->sample_cnt = 0;
max869_data->reached_thresh[led_num3] = 0;
max869_data->prev_current[led_num3] = 0;
max869_data->prev_agc_current[led_num3] = MAX86915_MAX_CURRENT;
max869_data->prev_ppg[led_num3] = 0;
max869_data->update_led(max869_data,
max869_data->agc_current[led_num3], led_num3);
}
}
}
return err;
}
static int __max869_write_reg_to_file(void)
{
int reg, err;
u8 recvData;
for (reg = 0; reg < 0x55; reg++) {
recvData = reg;
err = __max869_read_reg(max869_data, &recvData, 1);
if (err != 0) {
HRM_dbg("%s - error reading 0x%02x err:%d\n",
__func__, reg, err);
} else {
HRM_dbg("%s - 0x%02x = 0x%02x\n",
__func__, reg, recvData);
}
}
return 0;
}
static int __max869_write_file_to_reg(void)
{
return -EIO;
}
static void __max869_print_mode(int mode)
{
HRM_info("%s - ===== mode(%d) =====\n", __func__, mode);
if (mode == MODE_HRM)
HRM_info("%s - %s(%d)\n", __func__, "HRM", MODE_HRM);
if (mode == MODE_AMBIENT)
HRM_info("%s - %s(%d)\n", __func__, "AMBIENT", MODE_AMBIENT);
if (mode == MODE_PROX)
HRM_info("%s - %s(%d)\n", __func__, "PROX", MODE_PROX);
if (mode == MODE_SDK_IR)
HRM_info("%s - %s(%d)\n", __func__, "SDK", MODE_SDK_IR);
if (mode == MODE_UNKNOWN)
HRM_info("%s - %s(%d)\n", __func__, "UNKNOWN", MODE_UNKNOWN);
HRM_info("%s - ====================\n", __func__);
}
static int __max869_get_part_id(struct max869_device_data *data)
{
u8 recvData;
u8 recvDataSub;
int err;
err = __max869_write_reg(data, 0xFF, 0x54);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_TEST0!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, 0xFF, 0x4d);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_TEST1!\n",
__func__);
return -EIO;
}
recvData = 0xC1;
err = __max869_read_reg(data, &recvData, 1);
if (err != 0) {
HRM_dbg("%s - __max869_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
err = __max869_write_reg(data, 0xFF, 0x00);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86900_MODE_TEST0!\n",
__func__);
return -EIO;
}
HRM_dbg("%s - 0xC1 :%x\n", __func__, recvData);
if (recvData == 0x83)
return PART_TYPE_MAX86915_ES;
else if (recvData == 0x86)
return PART_TYPE_MAX86915_CS_15;
else if (recvData == 0x88) {
recvDataSub = 0xFE;
err = __max869_read_reg(data, &recvDataSub, 1);
if (err != 0) {
HRM_dbg("%s - __max869_read_reg err:%d, address:0x%02x\n",
__func__, err, recvDataSub);
return -EIO;
}
if (recvDataSub == 0x41)
return PART_TYPE_MAX86915_CS_21;
else if (recvDataSub == 0xC1)
return PART_TYPE_MAX86915_CS_22;
else if (recvDataSub == 0x42)
return PART_TYPE_MAX86915_CS_211;
else if (recvDataSub == 0xC2)
return PART_TYPE_MAX86915_CS_221;
else {
HRM_info("%s - recvDataSub:0x%02x\n",
__func__, recvDataSub);
return PART_TYPE_NONE;
}
} else {
HRM_info("%s - recvData:0x%02x\n",
__func__, recvData);
recvDataSub = 0xFE;
err = __max869_read_reg(data, &recvDataSub, 1);
if (err != 0) {
HRM_dbg("%s - __max869_read_reg err:%d, address:0x%02x\n",
__func__, err, recvDataSub);
return -EIO;
}
if (recvDataSub == 0x41)
return PART_TYPE_MAX86915_CS_21;
else if (recvDataSub == 0xC1)
return PART_TYPE_MAX86915_CS_22;
else if (recvDataSub == 0x42)
return PART_TYPE_MAX86915_CS_211;
else if (recvDataSub == 0xC2)
return PART_TYPE_MAX86915_CS_221;
else {
HRM_info("%s - recvDataSub:0x%02x\n",
__func__, recvDataSub);
return PART_TYPE_NONE;
}
}
}
int __max869_get_num_samples_in_fifo(struct max869_device_data *data)
{
int fifo_rd_ptr;
int fifo_wr_ptr;
int fifo_ovf_cnt;
char fifo_data[3];
int num_samples = 0;
int ret;
fifo_data[0] = MAX86915_FIFO_WRITE_POINTER;
ret = __max869_read_reg(data, fifo_data, 3);
if (ret < 0) {
HRM_dbg("%s failed. ret: %d\n", __func__, ret);
return ret;
}
fifo_wr_ptr = fifo_data[0] & 0x1F;
fifo_ovf_cnt = fifo_data[1] & 0x1F;
fifo_rd_ptr = fifo_data[2] & 0x1F;
if (fifo_ovf_cnt || fifo_rd_ptr == fifo_wr_ptr) {
if (fifo_ovf_cnt > 0)
HRM_dbg("###FALTAL### FIFO is Full. OVF: %d RD:%d WR:%d\n",
fifo_ovf_cnt, fifo_rd_ptr, fifo_wr_ptr);
num_samples = MAX86915_FIFO_SIZE;
} else {
if (fifo_wr_ptr > fifo_rd_ptr)
num_samples = fifo_wr_ptr - fifo_rd_ptr;
else if (fifo_wr_ptr < fifo_rd_ptr)
num_samples = MAX86915_FIFO_SIZE +
fifo_wr_ptr - fifo_rd_ptr;
}
return num_samples;
}
int __max869_get_fifo_settings(struct max869_device_data *data, u16 *ch)
{
int num_item = 0;
int i;
u16 tmp;
int ret;
u8 buf[2];
/*
* TODO: Somehow when the data corrupted in the bus,
* and i2c functions don't return error messages.
*/
buf[0] = MAX86915_LED_SEQ_REG_1;
ret = __max869_read_reg(data, buf, 2);
if (ret < 0)
return ret;
tmp = ((u16)buf[1] << 8) | (u16)buf[0];
*ch = tmp;
for (i = 0; i < 4; i++) {
if (tmp & 0x000F)
num_item++;
tmp >>= 4;
}
return num_item;
}
int __max869_read_fifo(struct max869_device_data *sd, char *fifo_buf, int num_bytes)
{
int ret = 0;
int data_offset = 0;
int to_read = 0;
while (num_bytes > 0) {
if (num_bytes > MAX_I2C_BLOCK_SIZE) {
to_read = MAX_I2C_BLOCK_SIZE;
num_bytes -= to_read;
} else {
to_read = num_bytes;
num_bytes = 0;
}
fifo_buf[data_offset] = MAX86915_FIFO_DATA;
ret = __max869_read_reg(sd, &fifo_buf[data_offset], to_read);
if (ret < 0)
break;
data_offset += to_read;
}
return ret;
}
int __max869_fifo_read_data(struct max869_device_data *data)
{
int ret = 0;
int num_samples;
int num_bytes;
int num_channel = 0;
u16 fd_settings = 0;
int i;
int j;
int offset1;
int offset2;
int index;
int samples[16] = {0, };
u8 fifo_buf[NUM_BYTES_PER_SAMPLE*32*MAX_LED_NUM];
int fifo_mode = -1;
if (data->hrm_mode == MODE_SDK_IR) {
num_samples = fifo_full_cnt;
num_channel = 4;
} else if (data->eol_test_is_enable == 1) {
num_samples = 20;
num_channel = data->num_samples;
} else {
num_samples = __max869_get_num_samples_in_fifo(data);
if (num_samples <= 0 || num_samples > MAX86915_FIFO_SIZE) {
ret = num_samples;
goto fail;
}
num_channel = __max869_get_fifo_settings(data, &fd_settings);
if (num_channel < 0) {
ret = num_channel;
goto fail;
}
}
num_bytes = num_channel * num_samples * NUM_BYTES_PER_SAMPLE;
ret = __max869_read_fifo(data, fifo_buf, num_bytes);
if (ret < 0)
goto fail;
/* clear the fifo_data buffer */
for (i = 0; i < MAX86915_FIFO_SIZE; i++)
for (j = 0; j < MAX_LED_NUM; j++)
data->fifo_data[j][i] = 0;
data->fifo_samples = 0;
for (i = 0; i < num_samples; i++) {
offset1 = i * NUM_BYTES_PER_SAMPLE * num_channel;
offset2 = 0;
for (j = 0; j < MAX_LED_NUM; j++) {
if (data->flex_mode & (1 << j)) {
index = offset1 + offset2;
samples[j] = ((int)fifo_buf[index + 0] << 16)
| ((int)fifo_buf[index + 1] << 8)
| ((int)fifo_buf[index + 2]);
samples[j] &= 0x7ffff;
data->fifo_data[j][i] = samples[j];
offset2 += NUM_BYTES_PER_SAMPLE;
}
}
}
data->fifo_samples = num_samples;
return ret;
fail:
HRM_dbg("%s failed. ret: %d, fifo_mode: %d\n", __func__, ret, fifo_mode);
return ret;
}
int __max869_fifo_irq_handler(struct max869_device_data *data)
{
int ret;
ret = __max869_fifo_read_data(data);
if (ret < 0) {
HRM_dbg("%s fifo. ret: %d\n", __func__, ret);
return ret;
}
return ret;
}
static void __max869_div_float(struct max86915_div_data *div_data, u64 left_operand, u64 right_operand)
{
snprintf(div_data->left_integer, INTEGER_LEN, "%llu", left_operand);
memset(div_data->left_decimal, '0', sizeof(div_data->left_decimal));
div_data->left_decimal[DECIMAL_LEN - 1] = 0;
snprintf(div_data->right_integer, INTEGER_LEN, "%llu", right_operand);
memset(div_data->right_decimal, '0', sizeof(div_data->right_decimal));
div_data->right_decimal[DECIMAL_LEN - 1] = 0;
__max869_div_str(div_data->left_integer, div_data->left_decimal, div_data->right_integer,
div_data->right_decimal, div_data->result_integer, div_data->result_decimal);
}
static void __max869_plus_str(char *integer_operand, char *decimal_operand, char *result_integer, char *result_decimal)
{
unsigned long long a, b;
int i;
for (i = 0; i < DECIMAL_LEN - 1; i++) {
decimal_operand[i] -= '0';
result_decimal[i] -= '0';
}
for (i = 0; i < DECIMAL_LEN - 1; i++)
result_decimal[i] += decimal_operand[i];
sscanf(integer_operand, "%19llu", &a);
sscanf(result_integer, "%19llu", &b);
for (i = DECIMAL_LEN - 1; i >= 0; i--) {
if (result_decimal[i] >= 10) {
if (i > 0)
result_decimal[i - 1] += 1;
else
a += 1;
result_decimal[i] -= 10;
}
}
a += b;
for (i = 0; i < DECIMAL_LEN - 1; i++)
result_decimal[i] += '0';
snprintf(result_integer, INTEGER_LEN, "%llu", a);
}
static void __max869_float_sqrt(char *integer_operand, char *decimal_operand)
{
int i;
char left_integer[INTEGER_LEN] = { '\0', }, left_decimal[DECIMAL_LEN] = { '\0', };
char result_integer[INTEGER_LEN] = { '\0', }, result_decimal[DECIMAL_LEN] = { '\0', };
char temp_integer[INTEGER_LEN] = { '\0', }, temp_decimal[DECIMAL_LEN] = { '\0', };
strncpy(left_integer, integer_operand, INTEGER_LEN - 1);
strncpy(left_decimal, decimal_operand, DECIMAL_LEN - 1);
for (i = 0; i < 100; i++) {
__max869_div_str(left_integer, left_decimal, integer_operand,
decimal_operand, result_integer, result_decimal);
__max869_plus_str(integer_operand, decimal_operand, result_integer, result_decimal);
snprintf(temp_integer, INTEGER_LEN, "%d", 2);
memset(temp_decimal, '0', sizeof(temp_decimal));
temp_decimal[DECIMAL_LEN - 1] = 0;
__max869_div_str(result_integer, result_decimal, temp_integer,
temp_decimal, integer_operand, decimal_operand);
}
}
static void __max869_div_str(char *left_integer, char *left_decimal, char *right_integer,
char *right_decimal, char *result_integer, char *result_decimal)
{
int i;
unsigned long long j;
unsigned long long loperand, roperand;
unsigned long long ldigit, rdigit, temp;
char str[10] = { 0, };
ldigit = 0;
rdigit = 0;
sscanf(left_integer, "%19llu", &loperand);
for (i = DECIMAL_LEN - 2; i >= 0; i--) {
if (left_decimal[i] != '0') {
ldigit = (unsigned long long)i + 1;
break;
}
}
sscanf(right_integer, "%19llu", &roperand);
for (i = DECIMAL_LEN - 2; i >= 0; i--) {
if (right_decimal[i] != '0') {
rdigit = (unsigned long long)i + 1;
break;
}
}
if (ldigit < rdigit)
ldigit = rdigit;
for (j = 0; j < ldigit; j++) {
loperand *= 10;
roperand *= 10;
}
if (ldigit != 0) {
snprintf(str, sizeof(str), "%%%llullu", ldigit);
sscanf(left_decimal, str, &temp);
loperand += temp;
snprintf(str, sizeof(str), "%%%llullu", ldigit);
sscanf(right_decimal, str, &temp);
roperand += temp;
}
if (roperand == 0)
roperand = 1;
temp = loperand / roperand;
snprintf(result_integer, INTEGER_LEN, "%llu", temp);
loperand -= roperand * temp;
loperand *= 10;
for (i = 0; i < DECIMAL_LEN; i++) {
temp = loperand / roperand;
if (temp != 0) {
loperand -= roperand * temp;
loperand *= 10;
} else
loperand *= 10;
result_decimal[i] = '0' + temp;
}
result_decimal[DECIMAL_LEN - 1] = 0;
}
static void __max869_init_eol_data(struct max86915_eol_data *eol_data)
{
memset(eol_data, 0, sizeof(struct max86915_eol_data));
do_gettimeofday(&eol_data->start_time);
eol_data->state = _EOL_STATE_TYPE_NEW_INIT;
}
static void __max869_eol_flicker_data(int ir_data, struct max86915_eol_data *eol_data)
{
int i = 0;
int eol_flicker_retry = eol_data->eol_flicker_data.retry;
int eol_flicker_count = eol_data->eol_flicker_data.count;
int eol_flicker_index = eol_data->eol_flicker_data.index;
u64 data[2];
u64 average[2];
if (!eol_flicker_count)
HRM_dbg("%s - EOL FLICKER STEP 1 STARTED !!\n", __func__);
if (eol_flicker_count < EOL_NEW_FLICKER_SKIP_CNT)
goto eol_flicker_exit;
if (!eol_data->eol_flicker_data.state) {
if (ir_data < EOL_NEW_FLICKER_THRESH)
eol_data->eol_flicker_data.state = 1;
else {
if (eol_flicker_retry < EOL_NEW_FLICKER_RETRY_CNT) {
HRM_dbg("%s - EOL FLICKER STEP 1 Retry : %d\n",
__func__, eol_data->eol_flicker_data.retry);
eol_data->eol_flicker_data.retry++;
goto eol_flicker_exit;
} else
eol_data->eol_flicker_data.state = 1;
}
}
if (eol_flicker_index < EOL_NEW_FLICKER_SIZE) {
eol_data->eol_flicker_data.buf[SELF_IR_CH][eol_flicker_index] = (u64)ir_data * CONVER_FLOAT;
eol_data->eol_flicker_data.sum[SELF_IR_CH]
+= eol_data->eol_flicker_data.buf[SELF_IR_CH][eol_flicker_index];
if (eol_data->eol_flicker_data.max
< eol_data->eol_flicker_data.buf[SELF_IR_CH][eol_flicker_index])
eol_data->eol_flicker_data.max
= eol_data->eol_flicker_data.buf[SELF_IR_CH][eol_flicker_index];
eol_data->eol_flicker_data.index++;
} else if (eol_flicker_index == EOL_NEW_FLICKER_SIZE && eol_data->eol_flicker_data.done != 1) {
do_div(eol_data->eol_flicker_data.sum[SELF_IR_CH], EOL_NEW_FLICKER_SIZE);
eol_data->eol_flicker_data.average[SELF_IR_CH] = eol_data->eol_flicker_data.sum[SELF_IR_CH];
average[SELF_IR_CH] = eol_data->eol_flicker_data.average[SELF_IR_CH];
do_div(average[SELF_IR_CH], CONVER_FLOAT);
for (i = 0; i < EOL_NEW_FLICKER_SIZE; i++) {
do_div(eol_data->eol_flicker_data.buf[SELF_IR_CH][i], CONVER_FLOAT);
/*
* HRM_dbg("EOL FLICKER STEP 1 ir_data %d, %llu",
* i, eol_data->eol_flicker_data.buf[SELF_IR_CH][i]);
*/
data[SELF_IR_CH] = eol_data->eol_flicker_data.buf[SELF_IR_CH][i];
eol_data->eol_flicker_data.std_sum[SELF_IR_CH]
+= (data[SELF_IR_CH] - average[SELF_IR_CH])
* (data[SELF_IR_CH] - average[SELF_IR_CH]);
}
eol_data->eol_flicker_data.done = 1;
HRM_dbg("%s - EOL FLICKER STEP 1 Done\n", __func__);
}
eol_flicker_exit:
eol_data->eol_flicker_data.count++;
}
static void __max869_eol_hrm_data(int ir_data, int red_data, int green_data, int blue_data,
struct max86915_eol_data *eol_data, u32 array_pos, u32 eol_skip_cnt, u32 eol_step_size)
{
int i = 0;
int hrm_eol_count = eol_data->eol_hrm_data[array_pos].count;
int hrm_eol_index = eol_data->eol_hrm_data[array_pos].index;
int ir_current = eol_data->eol_hrm_data[array_pos].ir_current;
int red_current = eol_data->eol_hrm_data[array_pos].red_current;
int green_current = eol_data->eol_hrm_data[array_pos].green_current;
int blue_current = eol_data->eol_hrm_data[array_pos].blue_current;
u64 data[4];
u64 average[4];
u32 hrm_eol_skip_cnt = eol_skip_cnt;
u32 hrm_eol_size = eol_step_size;
if (!hrm_eol_count) {
HRM_dbg("%s - STEP [%d], [%d] started IR : %d , RED : %d, GREEN : %d, BLUE : %d,eol_skip_cnt : %d, eol_step_size : %d\n",
__func__, array_pos, array_pos + 2, ir_current, red_current,
green_current, blue_current, eol_skip_cnt, eol_step_size);
}
if (eol_step_size > EOL_NEW_HRM_SIZE) {
HRM_dbg("%s - FATAL ERROR !!!! the buffer size should be smaller than EOL_NEW_HRM_SIZE\n", __func__);
goto hrm_eol_exit;
}
if (hrm_eol_count < hrm_eol_skip_cnt)
goto hrm_eol_exit;
if (hrm_eol_index < hrm_eol_size) {
eol_data->eol_hrm_data[array_pos].buf[SELF_IR_CH][hrm_eol_index] = (u64)ir_data * CONVER_FLOAT;
eol_data->eol_hrm_data[array_pos].buf[SELF_RED_CH][hrm_eol_index] = (u64)red_data * CONVER_FLOAT;
eol_data->eol_hrm_data[array_pos].buf[SELF_GREEN_CH][hrm_eol_index] = (u64)green_data * CONVER_FLOAT;
eol_data->eol_hrm_data[array_pos].buf[SELF_BLUE_CH][hrm_eol_index] = (u64)blue_data * CONVER_FLOAT;
eol_data->eol_hrm_data[array_pos].sum[SELF_IR_CH]
+= eol_data->eol_hrm_data[array_pos].buf[SELF_IR_CH][hrm_eol_index];
eol_data->eol_hrm_data[array_pos].sum[SELF_RED_CH]
+= eol_data->eol_hrm_data[array_pos].buf[SELF_RED_CH][hrm_eol_index];
eol_data->eol_hrm_data[array_pos].sum[SELF_GREEN_CH]
+= eol_data->eol_hrm_data[array_pos].buf[SELF_GREEN_CH][hrm_eol_index];
eol_data->eol_hrm_data[array_pos].sum[SELF_BLUE_CH]
+= eol_data->eol_hrm_data[array_pos].buf[SELF_BLUE_CH][hrm_eol_index];
eol_data->eol_hrm_data[array_pos].index++;
} else if (hrm_eol_index == hrm_eol_size && eol_data->eol_hrm_data[array_pos].done != 1) {
do_div(eol_data->eol_hrm_data[array_pos].sum[SELF_IR_CH], hrm_eol_size);
eol_data->eol_hrm_data[array_pos].average[SELF_IR_CH]
= eol_data->eol_hrm_data[array_pos].sum[SELF_IR_CH];
do_div(eol_data->eol_hrm_data[array_pos].sum[SELF_RED_CH], hrm_eol_size);
eol_data->eol_hrm_data[array_pos].average[SELF_RED_CH]
= eol_data->eol_hrm_data[array_pos].sum[SELF_RED_CH];
do_div(eol_data->eol_hrm_data[array_pos].sum[SELF_GREEN_CH], hrm_eol_size);
eol_data->eol_hrm_data[array_pos].average[SELF_GREEN_CH]
= eol_data->eol_hrm_data[array_pos].sum[SELF_GREEN_CH];
do_div(eol_data->eol_hrm_data[array_pos].sum[SELF_BLUE_CH], hrm_eol_size);
eol_data->eol_hrm_data[array_pos].average[SELF_BLUE_CH]
= eol_data->eol_hrm_data[array_pos].sum[SELF_BLUE_CH];
average[SELF_IR_CH] = eol_data->eol_hrm_data[array_pos].average[SELF_IR_CH];
do_div(average[SELF_IR_CH], CONVER_FLOAT);
average[SELF_RED_CH] = eol_data->eol_hrm_data[array_pos].average[SELF_RED_CH];
do_div(average[SELF_RED_CH], CONVER_FLOAT);
average[SELF_GREEN_CH] = eol_data->eol_hrm_data[array_pos].average[SELF_GREEN_CH];
do_div(average[SELF_GREEN_CH], CONVER_FLOAT);
average[SELF_BLUE_CH] = eol_data->eol_hrm_data[array_pos].average[SELF_BLUE_CH];
do_div(average[SELF_BLUE_CH], CONVER_FLOAT);
for (i = 0; i < hrm_eol_size; i++) {
do_div(eol_data->eol_hrm_data[array_pos].buf[SELF_IR_CH][i], CONVER_FLOAT);
data[SELF_IR_CH] = eol_data->eol_hrm_data[array_pos].buf[SELF_IR_CH][i];
do_div(eol_data->eol_hrm_data[array_pos].buf[SELF_RED_CH][i], CONVER_FLOAT);
data[SELF_RED_CH] = eol_data->eol_hrm_data[array_pos].buf[SELF_RED_CH][i];
do_div(eol_data->eol_hrm_data[array_pos].buf[SELF_GREEN_CH][i], CONVER_FLOAT);
data[SELF_GREEN_CH] = eol_data->eol_hrm_data[array_pos].buf[SELF_GREEN_CH][i];
do_div(eol_data->eol_hrm_data[array_pos].buf[SELF_BLUE_CH][i], CONVER_FLOAT);
data[SELF_BLUE_CH] = eol_data->eol_hrm_data[array_pos].buf[SELF_BLUE_CH][i];
eol_data->eol_hrm_data[array_pos].std_sum[SELF_IR_CH]
+= (data[SELF_IR_CH] - average[SELF_IR_CH])
* (data[SELF_IR_CH] - average[SELF_IR_CH]);
eol_data->eol_hrm_data[array_pos].std_sum[SELF_RED_CH]
+= (data[SELF_RED_CH] - average[SELF_RED_CH])
* (data[SELF_RED_CH] - average[SELF_RED_CH]);
eol_data->eol_hrm_data[array_pos].std_sum[SELF_GREEN_CH]
+= (data[SELF_GREEN_CH] - average[SELF_GREEN_CH])
* (data[SELF_GREEN_CH] - average[SELF_GREEN_CH]);
eol_data->eol_hrm_data[array_pos].std_sum[SELF_BLUE_CH]
+= (data[SELF_BLUE_CH] - average[SELF_BLUE_CH])
* (data[SELF_BLUE_CH] - average[SELF_BLUE_CH]);
}
eol_data->eol_hrm_data[array_pos].done = 1;
eol_data->eol_hrm_flag |= 1 << array_pos;
HRM_dbg("%s - STEP [%d], [%d], [%x] Done\n",
__func__, array_pos, array_pos + 2, eol_data->eol_hrm_flag);
}
hrm_eol_exit:
eol_data->eol_hrm_data[array_pos].count++;
}
static void __max869_eol_freq_data(struct max86915_eol_data *eol_data, u8 divid)
{
int freq_state = eol_data->eol_freq_data.state;
unsigned long freq_end_time = eol_data->eol_freq_data.end_time;
unsigned long freq_prev_time = eol_data->eol_freq_data.prev_time;
unsigned long freq_current_time = jiffies;
int freq_count = eol_data->eol_freq_data.skip_count;
unsigned long freq_interval;
int freq_start_time;
int freq_work_time;
u32 freq_skip_cnt = EOL_NEW_FREQ_SKIP_CNT / divid;
u32 freq_interrupt_min = EOL_NEW_FREQ_MIN / divid;
u32 freq_timeout = (HZ / (HZ / divid));
if (freq_count < freq_skip_cnt)
goto seq3_exit;
switch (freq_state) {
case 0:
HRM_dbg("%s - EOL FREQ 7 stated ###\n", __func__);
eol_data->eol_freq_data.start_time = jiffies;
do_gettimeofday(&eol_data->eol_freq_data.start_time_t);
eol_data->eol_freq_data.end_time = jiffies + HZ; /* timeout in 1s */
eol_data->eol_freq_data.state = 1;
eol_data->eol_freq_data.count++;
break;
case 1:
if (time_is_after_eq_jiffies(freq_end_time)) {
if (time_is_after_eq_jiffies(freq_prev_time)) {
eol_data->eol_freq_data.count++;
} else {
if (eol_data->eol_freq_data.skew_retry < EOL_NEW_FREQ_RETRY_CNT) {
eol_data->eol_freq_data.skew_retry++;
freq_interval = freq_current_time-freq_prev_time;
HRM_dbg("%s - !!!!!! EOL FREQ 7 Clock skew too large retry : %d ,interval : %lu ms, count : %d\n", __func__,
eol_data->eol_freq_data.skew_retry, freq_interval*10,
eol_data->eol_freq_data.count);
eol_data->eol_freq_data.state = 0;
eol_data->eol_freq_data.count = 0;
eol_data->eol_freq_data.prev_time = 0;
}
}
do_gettimeofday(&eol_data->eol_freq_data.work_time_t);
} else {
if (eol_data->eol_freq_data.count < freq_interrupt_min
&& eol_data->eol_freq_data.retry < EOL_NEW_FREQ_SKEW_RETRY_CNT) {
HRM_dbg("%s - !!!!!! EOL FREQ 7 CNT Gap too large : %d, retry : %d\n", __func__,
eol_data->eol_freq_data.count, eol_data->eol_freq_data.retry);
eol_data->eol_freq_data.retry++;
eol_data->eol_freq_data.state = 0;
eol_data->eol_freq_data.count = 0;
eol_data->eol_freq_data.prev_time = 0;
} else {
eol_data->eol_freq_data.count *= divid;
eol_data->eol_freq_data.done = 1;
freq_start_time = (eol_data->eol_freq_data.start_time_t.tv_sec * 1000)
+ (eol_data->eol_freq_data.start_time_t.tv_usec / 1000);
freq_work_time = (eol_data->eol_freq_data.work_time_t.tv_sec * 1000)
+ (eol_data->eol_freq_data.work_time_t.tv_usec / 1000);
HRM_dbg("%s - EOL FREQ 7 Done###\n", __func__);
HRM_dbg("%s - EOL FREQ 7 sample_no_cnt : %d, time : %d ms, divid : %d\n", __func__,
eol_data->eol_freq_data.count, freq_work_time - freq_start_time, divid);
}
}
break;
default:
HRM_dbg("%s - EOL FREQ 7 Should not call this routine !!!###\n", __func__);
break;
}
seq3_exit:
eol_data->eol_freq_data.prev_time = freq_current_time + freq_timeout; /* timeout in 10 or 40 ms or 100 ms */
eol_data->eol_freq_data.skip_count++;
}
static int __max869_eol_check_done(struct max86915_eol_data *eol_data, u8 mode, struct max869_device_data *device)
{
int i = 0;
int start_time;
int end_time;
if ((eol_data->eol_flicker_data.done || (device->pre_eol_test_is_enable == 2))
&& ((eol_data->eol_hrm_flag == EOL_NEW_HRM_COMPLETE_ALL_STEP) || (device->pre_eol_test_is_enable))
&& (eol_data->eol_freq_data.done || (device->pre_eol_test_is_enable == 1))) {
do_gettimeofday(&eol_data->end_time);
start_time = (eol_data->start_time.tv_sec * 1000) + (eol_data->start_time.tv_usec / 1000);
end_time = (eol_data->end_time.tv_sec * 1000) + (eol_data->end_time.tv_usec / 1000);
HRM_dbg("%s - EOL Tested Time : %d ms Tested Count : %d\n",
__func__, end_time - start_time, device->sample_cnt);
HRM_dbg("%s - SETTING CUREENT %x, %x\n", __func__,
EOL_NEW_DC_HIGH_LED_IR_CURRENT, EOL_NEW_DC_HIGH_LED_RED_CURRENT);
for (i = 0; i < EOL_NEW_HRM_ARRY_SIZE; i++)
HRM_dbg("%s - DOUBLE CHECK STEP[%d], done : %d\n",
__func__, i + 2, eol_data->eol_hrm_data[i].done);
if (device->pre_eol_test_is_enable != 2) {
for (i = 0; i < EOL_NEW_FLICKER_SIZE; i++)
HRM_dbg("%s - EOL_NEW_FLICKER_MODE EOLRAW,%llu,%d\n",
__func__, eol_data->eol_flicker_data.buf[SELF_IR_CH][i], 0);
}
if (device->pre_eol_test_is_enable == 0) {
for (i = 0; i < EOL_NEW_DC_LOW_SIZE; i++)
HRM_dbg("%s - EOL_NEW_DC_LOW_MODE EOLRAW,%llu,%llu,%llu,%llu\n", __func__,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_LOW].buf[SELF_IR_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_LOW].buf[SELF_RED_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_LOW].buf[SELF_GREEN_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_LOW].buf[SELF_BLUE_CH][i]);
for (i = 0; i < EOL_NEW_DC_MIDDLE_SIZE; i++)
HRM_dbg("%s - EOL_NEW_DC_MIDDLE_MODE EOLRAW,%llu,%llu,%llu,%llu\n", __func__,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].buf[SELF_IR_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].buf[SELF_RED_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].buf[SELF_GREEN_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].buf[SELF_BLUE_CH][i]);
for (i = 0; i < EOL_NEW_DC_HIGH_SIZE; i++)
HRM_dbg("%s - EOL_NEW_DC_HIGH_MODE EOLRAW,%llu,%llu,%llu,%llu\n", __func__,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].buf[SELF_IR_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].buf[SELF_RED_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].buf[SELF_GREEN_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].buf[SELF_BLUE_CH][i]);
for (i = 0; i < EOL_NEW_DC_XTALK_SIZE; i++)
HRM_dbg("%s - EOL_NEW_DC_XTALK EOLRAW,%llu,%llu,%llu,%llu\n", __func__,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].buf[SELF_IR_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].buf[SELF_RED_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].buf[SELF_GREEN_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].buf[SELF_BLUE_CH][i]);
HRM_dbg("%s - EOL_NEW_FLICKER_MODE RESULT,%llu,%llu\n", __func__,
eol_data->eol_flicker_data.average[SELF_IR_CH],
eol_data->eol_flicker_data.std_sum[SELF_IR_CH]);
HRM_dbg("%s - EOL_NEW_DC_LOW_MODE RESULT,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n", __func__,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_LOW].average[SELF_IR_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_LOW].std_sum[SELF_IR_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_LOW].average[SELF_RED_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_LOW].std_sum[SELF_RED_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_LOW].average[SELF_GREEN_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_LOW].std_sum[SELF_GREEN_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_LOW].average[SELF_BLUE_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_LOW].std_sum[SELF_BLUE_CH]);
HRM_dbg("%s - EOL_NEW_DC_MIDDLE_MODE RESULT,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n", __func__,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_IR_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].std_sum[SELF_IR_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_RED_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].std_sum[SELF_RED_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_GREEN_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].std_sum[SELF_GREEN_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_BLUE_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].std_sum[SELF_BLUE_CH]);
HRM_dbg("%s - EOL_NEW_DC_HIGH_MODE RESULT,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n", __func__,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_IR_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum[SELF_IR_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_RED_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum[SELF_RED_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_GREEN_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum[SELF_GREEN_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_BLUE_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum[SELF_BLUE_CH]);
HRM_dbg("%s - EOL_NEW_DC_XTALK RESULT,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n", __func__,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_IR_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].std_sum[SELF_IR_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_RED_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].std_sum[SELF_RED_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_GREEN_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].std_sum[SELF_GREEN_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_BLUE_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].std_sum[SELF_BLUE_CH]);
}
if (device->pre_eol_test_is_enable != 1) {
HRM_dbg("%s - EOL FREQ RESULT,%d\n", __func__,
eol_data->eol_freq_data.count);
}
__max869_eol_conv2float(eol_data, mode, device);
device->eol_test_status = 1;
return 1;
} else {
return 0;
}
}
static void __max869_eol_conv2float(struct max86915_eol_data *eol_data, u8 mode, struct max869_device_data *device)
{
struct max86915_div_data div_data;
char flicker_max[2][INTEGER_LEN] = { {'\0', }, };
char ir_ldc_avg[2][INTEGER_LEN] = { {'\0', }, }, red_ldc_avg[2][INTEGER_LEN] = { {'\0', }, };
char green_ldc_avg[2][INTEGER_LEN] = { {'\0', }, }, blue_ldc_avg[2][INTEGER_LEN] = { {'\0', }, };
char ir_mdc_avg[2][INTEGER_LEN] = { {'\0', }, }, red_mdc_avg[2][INTEGER_LEN] = { {'\0', }, };
char green_mdc_avg[2][INTEGER_LEN] = { {'\0', }, }, blue_mdc_avg[2][INTEGER_LEN] = { {'\0', }, };
char ir_hdc_avg[2][INTEGER_LEN] = { {'\0', }, }, ir_hdc_std[2][INTEGER_LEN] = { {'\0', }, };
char red_hdc_avg[2][INTEGER_LEN] = { {'\0', }, }, red_hdc_std[2][INTEGER_LEN] = { {'\0', }, };
char green_hdc_avg[2][INTEGER_LEN] = { {'\0', }, }, green_hdc_std[2][INTEGER_LEN] = { {'\0', }, };
char blue_hdc_avg[2][INTEGER_LEN] = { {'\0', }, }, blue_hdc_std[2][INTEGER_LEN] = { {'\0', }, };
char ir_xtalk_avg[2][INTEGER_LEN] = { {'\0', }, }, red_xtalk_avg[2][INTEGER_LEN] = { {'\0', }, };
char green_xtalk_avg[2][INTEGER_LEN] = { {'\0', }, }, blue_xtalk_avg[2][INTEGER_LEN] = { {'\0', }, };
HRM_info("%s - EOL_TEST __max869_eol_conv2float\n", __func__);
if (device->pre_eol_test_is_enable != 2) {
/* flicker */
__max869_div_float(&div_data, eol_data->eol_flicker_data.max, CONVER_FLOAT);
strncpy(flicker_max[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(flicker_max[1], div_data.result_decimal, INTEGER_LEN - 1);
}
if (device->pre_eol_test_is_enable == 0) {
/* dc low */
__max869_div_float(&div_data, eol_data->eol_hrm_data[EOL_NEW_MODE_DC_LOW].average[SELF_IR_CH], CONVER_FLOAT);
strncpy(ir_ldc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(ir_ldc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
__max869_div_float(&div_data, eol_data->eol_hrm_data[EOL_NEW_MODE_DC_LOW].average[SELF_RED_CH], CONVER_FLOAT);
strncpy(red_ldc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(red_ldc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
__max869_div_float(&div_data, eol_data->eol_hrm_data[EOL_NEW_MODE_DC_LOW].average[SELF_GREEN_CH], CONVER_FLOAT);
strncpy(green_ldc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(green_ldc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
__max869_div_float(&div_data, eol_data->eol_hrm_data[EOL_NEW_MODE_DC_LOW].average[SELF_BLUE_CH], CONVER_FLOAT);
strncpy(blue_ldc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(blue_ldc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
/* dc mid */
__max869_div_float(&div_data, eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_IR_CH], CONVER_FLOAT);
strncpy(ir_mdc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(ir_mdc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
__max869_div_float(&div_data, eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_RED_CH], CONVER_FLOAT);
strncpy(red_mdc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(red_mdc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
__max869_div_float(&div_data, eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_GREEN_CH], CONVER_FLOAT);
strncpy(green_mdc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(green_mdc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
__max869_div_float(&div_data, eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_BLUE_CH], CONVER_FLOAT);
strncpy(blue_mdc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(blue_mdc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
/* dc high */
__max869_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_IR_CH], CONVER_FLOAT);
strncpy(ir_hdc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(ir_hdc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
__max869_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum[SELF_IR_CH], EOL_NEW_DC_HIGH_SIZE);
strncpy(ir_hdc_std[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(ir_hdc_std[1], div_data.result_decimal, INTEGER_LEN - 1);
__max869_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_RED_CH], CONVER_FLOAT);
strncpy(red_hdc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(red_hdc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
__max869_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum[SELF_RED_CH], EOL_NEW_DC_HIGH_SIZE);
strncpy(red_hdc_std[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(red_hdc_std[1], div_data.result_decimal, INTEGER_LEN - 1);
__max869_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_GREEN_CH], CONVER_FLOAT);
strncpy(green_hdc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(green_hdc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
__max869_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum[SELF_GREEN_CH], EOL_NEW_DC_HIGH_SIZE);
strncpy(green_hdc_std[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(green_hdc_std[1], div_data.result_decimal, INTEGER_LEN - 1);
__max869_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_BLUE_CH], CONVER_FLOAT);
strncpy(blue_hdc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(blue_hdc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
__max869_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum[SELF_BLUE_CH], EOL_NEW_DC_HIGH_SIZE);
strncpy(blue_hdc_std[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(blue_hdc_std[1], div_data.result_decimal, INTEGER_LEN - 1);
/* dc xtalk */
__max869_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_IR_CH], CONVER_FLOAT);
strncpy(ir_xtalk_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(ir_xtalk_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
__max869_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_RED_CH], CONVER_FLOAT);
strncpy(red_xtalk_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(red_xtalk_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
__max869_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_GREEN_CH], CONVER_FLOAT);
strncpy(green_xtalk_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(green_xtalk_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
__max869_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_BLUE_CH], CONVER_FLOAT);
strncpy(blue_xtalk_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(blue_xtalk_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
HRM_info("%s - hdc_std : %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c\n", __func__,
ir_hdc_std[0], ir_hdc_std[1][0], ir_hdc_std[1][1],
red_hdc_std[0], red_hdc_std[1][0], red_hdc_std[1][1],
green_hdc_std[0], green_hdc_std[1][0], green_hdc_std[1][1],
blue_hdc_std[0], blue_hdc_std[1][0], blue_hdc_std[1][1]);
__max869_float_sqrt(ir_hdc_std[0], ir_hdc_std[1]);
__max869_float_sqrt(red_hdc_std[0], red_hdc_std[1]);
__max869_float_sqrt(green_hdc_std[0], green_hdc_std[1]);
__max869_float_sqrt(blue_hdc_std[0], blue_hdc_std[1]);
}
if (device->eol_test_result != NULL)
kfree(device->eol_test_result);
device->eol_test_result = kzalloc(sizeof(char) * MAX_EOL_RESULT, GFP_KERNEL);
if (device->eol_test_result == NULL) {
HRM_dbg("max86915_%s - couldn't allocate memory\n",
__func__);
return;
}
if (device->pre_eol_test_is_enable == 1) { /* PRE EOL System Noise */
snprintf(device->eol_test_result, MAX_EOL_RESULT, "%s.%c%c\n",
flicker_max[0], flicker_max[1][0], flicker_max[1][1]);
} else if (device->pre_eol_test_is_enable == 2) { /* PRE EOL FREQ */
snprintf(device->eol_test_result, MAX_EOL_RESULT, "%d\n",
eol_data->eol_freq_data.count);
} else if (device->pre_eol_test_is_enable == 3) { /* PRE EOL BOTH */
snprintf(device->eol_test_result, MAX_EOL_RESULT, "%s.%c%c, %d\n",
flicker_max[0], flicker_max[1][0], flicker_max[1][1],
eol_data->eol_freq_data.count);
} else {
snprintf(device->eol_test_result, MAX_EOL_RESULT, "%s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %d\n",
flicker_max[0], flicker_max[1][0], flicker_max[1][1],
ir_ldc_avg[0], ir_ldc_avg[1][0], ir_ldc_avg[1][1],
red_ldc_avg[0], red_ldc_avg[1][0], red_ldc_avg[1][1],
green_ldc_avg[0], green_ldc_avg[1][0], green_ldc_avg[1][1],
blue_ldc_avg[0], blue_ldc_avg[1][0], blue_ldc_avg[1][1],
ir_mdc_avg[0], ir_mdc_avg[1][0], ir_mdc_avg[1][1],
red_mdc_avg[0], red_mdc_avg[1][0], red_mdc_avg[1][1],
green_mdc_avg[0], green_mdc_avg[1][0], green_mdc_avg[1][1],
blue_mdc_avg[0], blue_mdc_avg[1][0], blue_mdc_avg[1][1],
ir_hdc_avg[0], ir_hdc_avg[1][0], ir_hdc_avg[1][1],
red_hdc_avg[0], red_hdc_avg[1][0], red_hdc_avg[1][1],
green_hdc_avg[0], green_hdc_avg[1][0], green_hdc_avg[1][1],
blue_hdc_avg[0], blue_hdc_avg[1][0], blue_hdc_avg[1][1],
ir_xtalk_avg[0], ir_xtalk_avg[1][0], ir_xtalk_avg[1][1],
red_xtalk_avg[0], red_xtalk_avg[1][0], red_xtalk_avg[1][1],
green_xtalk_avg[0], green_xtalk_avg[1][0], green_xtalk_avg[1][1],
blue_xtalk_avg[0], blue_xtalk_avg[1][0], blue_xtalk_avg[1][1],
ir_hdc_std[0], ir_hdc_std[1][0], ir_hdc_std[1][1],
red_hdc_std[0], red_hdc_std[1][0], red_hdc_std[1][1],
green_hdc_std[0], green_hdc_std[1][0], green_hdc_std[1][1],
blue_hdc_std[0], blue_hdc_std[1][0], blue_hdc_std[1][1],
eol_data->eol_freq_data.count);
}
HRM_dbg("%s result - %s\n", __func__, device->eol_test_result);
}
static int __max86915_enable_eol_flicker(struct max869_device_data *data)
{
int err;
data->sample_cnt = 0;
data->num_samples = 1;
data->flex_mode = 1;
err = __max869_init(data);
/* 400Hz, LED_PW=400us, SPO2_ADC_RANGE=4096nA */
err = __max869_write_reg(data,
MAX86915_MODE_CONFIGURATION_2, 0x0F);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data,
MAX86915_FIFO_CONFIG, 0x0C | MAX86915_FIFO_ROLLS_ON_MASK);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data,
MAX86915_INTERRUPT_ENABLE, A_FULL_MASK);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_INTERRUPT_ENABLE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED_SEQ_REG_1,
0x01);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED_SEQ_REG_1!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data,
MAX86915_MODE_CONFIGURATION, 0x07);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
data->eol_data.state = _EOL_STATE_TYPE_NEW_FLICKER_INIT;
return err;
}
static int __max86915_enable_eol_dc(struct max869_device_data *data)
{
int err;
u8 flex_config[2] = {0, };
/* data->led = 1; */ /* Prevent resetting MAX86915_LED_CONFIGURATION */
data->num_samples = 0;
data->flex_mode = 0;
flex_config[0] = (RED_LED_CH << MAX86915_LED2_OFFSET) | IR_LED_CH;
flex_config[1] = (BLUE_LED_CH << MAX86915_LED4_OFFSET) | GREEN_LED_CH;
if (flex_config[0] & MAX86915_LED1_MASK) {
data->num_samples++;
data->flex_mode |= (1 << 0);
}
if (flex_config[0] & MAX86915_LED2_MASK) {
data->num_samples++;
data->flex_mode |= (1 << 1);
}
if (flex_config[1] & MAX86915_LED3_MASK) {
data->num_samples++;
data->flex_mode |= (1 << 2);
}
if (flex_config[1] & MAX86915_LED4_MASK) {
data->num_samples++;
data->flex_mode |= (1 << 3);
}
HRM_info("%s - flexmode : 0x%02x, num_samples : %d\n", __func__,
data->flex_mode, data->num_samples);
err = __max869_write_reg(data, MAX86915_MODE_CONFIGURATION, 0x00);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED1_PA,
EOL_NEW_DC_LOW_LED_IR_CURRENT);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED1_PA!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED2_PA,
EOL_NEW_DC_LOW_LED_RED_CURRENT);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED2_PA!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED3_PA,
EOL_NEW_DC_LOW_LED_GREEN_CURRENT);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED3_PA!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED4_PA,
EOL_NEW_DC_LOW_LED_BLUE_CURRENT);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED4_PA!\n",
__func__);
return -EIO;
}
/* LED Range */
err = __max869_write_reg(data, MAX86915_LED_RANGE,
(MAX86915_DEFAULT_LED_RGE << MAX86915_LED1_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE << MAX86915_LED2_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE << MAX86915_LED3_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE << MAX86915_LED4_RGE_OFFSET));
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED_RANGE!\n",
__func__);
return -EIO;
}
/* XTALK */
err = __max869_write_reg(data, MAX86915_DAC1_XTALK_CODE,
0x00);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC1_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_DAC2_XTALK_CODE,
0x00);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC2_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_DAC3_XTALK_CODE,
0x00);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC3_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_DAC4_XTALK_CODE,
0x00);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC4_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_INTERRUPT_ENABLE, A_FULL_MASK);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_INTERRUPT_ENABLE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED_SEQ_REG_1,
flex_config[0]);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED_SEQ_REG_1!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED_SEQ_REG_2,
flex_config[1]);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED_SEQ_REG_2!\n",
__func__);
return -EIO;
}
/* 32uA, 400Hz, 120us */
err = __max869_write_reg(data, MAX86915_MODE_CONFIGURATION_2,
0x0D | (0x03 << MAX86915_ADC_RGE_OFFSET));
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_FIFO_CONFIG, 0x00);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_MODE_CONFIGURATION, 0x03);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
data->eol_data.state = _EOL_STATE_TYPE_NEW_DC_MODE_LOW;
return 0;
}
static int __max86915_enable_eol_freq(struct max869_device_data *data)
{
int err;
u8 flex_config[2] = {0, };
data->num_samples = 0;
data->flex_mode = 0;
flex_config[0] = (RED_LED_CH << MAX86915_LED2_OFFSET) | IR_LED_CH;
flex_config[1] = (BLUE_LED_CH << MAX86915_LED4_OFFSET) | GREEN_LED_CH;
if (flex_config[0] & MAX86915_LED1_MASK) {
data->num_samples++;
data->flex_mode |= (1 << 0);
}
if (flex_config[0] & MAX86915_LED2_MASK) {
data->num_samples++;
data->flex_mode |= (1 << 1);
}
if (flex_config[1] & MAX86915_LED3_MASK) {
data->num_samples++;
data->flex_mode |= (1 << 2);
}
if (flex_config[1] & MAX86915_LED4_MASK) {
data->num_samples++;
data->flex_mode |= (1 << 3);
}
HRM_info("%s - flexmode : 0x%02x, num_samples : %d\n", __func__,
data->flex_mode, data->num_samples);
err = __max869_write_reg(data, MAX86915_MODE_CONFIGURATION, 0x00);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED_SEQ_REG_1,
flex_config[0]);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED_SEQ_REG_1!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED_SEQ_REG_2,
flex_config[1]);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED_SEQ_REG_2!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED1_PA,
EOL_NEW_FREQUENCY_LED_IR_CURRENT);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED1_PA!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED2_PA,
EOL_NEW_FREQUENCY_LED_RED_CURRENT);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED2_PA!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED3_PA,
EOL_NEW_FREQUENCY_LED_GREEN_CURRENT);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED3_PA!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_LED4_PA,
EOL_NEW_FREQUENCY_LED_BLUE_CURRENT);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED4_PA!\n",
__func__);
return -EIO;
}
/* LED Range */
err = __max869_write_reg(data, MAX86915_LED_RANGE,
(MAX86915_DEFAULT_LED_RGE << MAX86915_LED1_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE << MAX86915_LED2_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE << MAX86915_LED3_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE << MAX86915_LED4_RGE_OFFSET));
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED_RANGE!\n",
__func__);
return -EIO;
}
/* XTALK */
err = __max869_write_reg(data, MAX86915_DAC1_XTALK_CODE,
0x00);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC1_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_DAC2_XTALK_CODE,
0x00);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC2_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_DAC3_XTALK_CODE,
0x00);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC3_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_DAC4_XTALK_CODE,
0x00);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC4_XTALK_CODE!\n",
__func__);
return -EIO;
}
/* 32uA, 400Hz, 120us */
err = __max869_write_reg(data, MAX86915_MODE_CONFIGURATION_2,
0x0D | (0x03 << MAX86915_ADC_RGE_OFFSET)); /* Maximum PW is 100us for 4ch/400Hz */
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
/* AVERAGE 4 */
err = __max869_write_reg(data, MAX86915_FIFO_CONFIG,
(0x02 << MAX86915_SMP_AVE_OFFSET) & MAX86915_SMP_AVE_MASK);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_INTERRUPT_ENABLE, PPG_RDY_MASK);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_INTERRUPT_ENABLE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(data, MAX86915_MODE_CONFIGURATION, 0x03);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
/* set the mode */
data->eol_data.state = _EOL_STATE_TYPE_NEW_FREQ_MODE;
return err;
}
static void __max86915_eol_test_onoff(struct max869_device_data *data, int onoff)
{
int err;
if (onoff) {
data->agc_mode = M_NONE;
__max869_init_eol_data(&data->eol_data);
data->eol_test_is_enable = 1;
if (data->pre_eol_test_is_enable == 2) /* Pre Test Only for Frequency */
err = __max86915_enable_eol_freq(data);
else
err = __max86915_enable_eol_flicker(data);
if (err != 0)
HRM_dbg("__max86915_eol_test_onoff err : %d\n", err);
} else {
HRM_info("%s - eol test off\n", __func__);
err = __max86915_disable(data);
if (err != 0)
HRM_dbg("__max86915_disable err : %d\n", err);
/*
* data->led_current1 = data->default_current1;
* data->led_current2 = data->default_current2;
* data->led_current3 = data->default_current3;
* data->led_current4 = data->default_current4;
*/
err = __max86915_init_device(data);
if (err)
HRM_dbg("%s __max86915_init_devicedevice fail err = %d\n",
__func__, err);
data->eol_test_is_enable = 0;
err = __max86915_hrm_enable(data);
if (err != 0)
HRM_dbg("__max86915_hrm_enable err : %d\n", err);
}
HRM_info("%s - onoff = %d\n", __func__, onoff);
}
static int __max86915_eol_read_data(struct hrm_output_data *data, struct max869_device_data *device, int *raw_data)
{
int err = 0;
int i = 0, j = 0;
int total_eol_cnt = (330 + (400 * 20)); /* maximnum count */
u8 recvData[MAX_LED_NUM * NUM_BYTES_PER_SAMPLE] = { 0x00, };
switch (device->eol_data.state) {
case _EOL_STATE_TYPE_NEW_FREQ_MODE:
case _EOL_STATE_TYPE_NEW_END:
case _EOL_STATE_TYPE_NEW_STOP:
recvData[0] = MAX86915_FIFO_DATA;
err = __max869_read_reg(device, recvData,
device->num_samples * NUM_BYTES_PER_SAMPLE);
if (err != 0) {
HRM_dbg("%s __max869_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData[0]);
return -EIO;
}
for (i = 0; i < MAX_LED_NUM; i++) {
if (device->flex_mode & (1 << i)) {
raw_data[i] = recvData[j++] << 16 & 0x30000;
raw_data[i] += recvData[j++] << 8;
raw_data[i] += recvData[j++] << 0;
} else
raw_data[i] = 0;
}
device->sample_cnt++;
device->eol_data.eol_count++;
break;
default:
err = __max869_fifo_irq_handler(device);
if (err != 0) {
HRM_dbg("%s __max869_fifo_irq_handler err:%d\n",
__func__, err);
return -EIO;
}
data->fifo_num = device->fifo_samples;
device->sample_cnt += device->fifo_samples;
device->eol_data.eol_count += device->fifo_samples;
break;
}
switch (device->eol_data.state) {
case _EOL_STATE_TYPE_NEW_INIT:
HRM_dbg("%s _EOL_STATE_TYPE_NEW_INIT\n", __func__);
data->fifo_num = 0; /* doesn't report the data */
err = 1;
if (device->eol_data.eol_count >= EOL_NEW_START_SKIP_CNT) {
device->eol_data.state = _EOL_STATE_TYPE_NEW_FLICKER_INIT;
device->eol_data.eol_count = 0;
}
break;
case _EOL_STATE_TYPE_NEW_FLICKER_INIT:
for (i = 0; i < device->fifo_samples; i++) {
data->fifo_main[AGC_IR][i] = device->fifo_data[AGC_IR][i];
data->fifo_main[AGC_RED][i] = 0;
device->eol_data.eol_flicker_data.max = 0;
}
if (device->eol_data.eol_count >= EOL_NEW_START_SKIP_CNT) {
HRM_dbg("%s FINISH : _EOL_STATE_TYPE_NEW_FLICKER_INIT\n", __func__);
device->eol_data.state = _EOL_STATE_TYPE_NEW_FLICKER_MODE;
device->eol_data.eol_count = 0;
}
break;
case _EOL_STATE_TYPE_NEW_FLICKER_MODE:
for (i = 0; i < device->fifo_samples; i++) {
device->fifo_data[AGC_IR][i] = device->fifo_data[AGC_IR][i] >> 3;
data->fifo_main[AGC_IR][i] = device->fifo_data[AGC_IR][i];
data->fifo_main[AGC_RED][i] = 0;
__max869_eol_flicker_data(data->fifo_main[AGC_IR][i], &device->eol_data);
}
if (device->eol_data.eol_flicker_data.done == EOL_SUCCESS) {
HRM_dbg("%s FINISH : _EOL_STATE_TYPE_NEW_FLICKER_MODE : %d, %d\n", __func__,
device->eol_data.eol_flicker_data.index, device->eol_data.eol_flicker_data.done);
if (device->pre_eol_test_is_enable == 3) /* PRE EOL for both */
__max86915_enable_eol_freq(device);
else if (device->pre_eol_test_is_enable == 1) /* Finish PRE EOL */
__max869_eol_check_done(&device->eol_data, SELF_MODE_400HZ, device);
else
__max86915_enable_eol_dc(device);
device->eol_data.eol_count = 0;
/* clear the remained datasheet after changing mode (interrupt TRIGGER mode). */
err = __max869_fifo_irq_handler(device);
if (err != 0) {
HRM_dbg("%s __max869_fifo_irq_handler err:%d\n",
__func__, err);
return -EIO;
}
}
break;
case _EOL_STATE_TYPE_NEW_DC_MODE_LOW:
for (i = 0; i < device->fifo_samples; i++) {
data->fifo_main[AGC_IR][i] = device->fifo_data[AGC_IR][i];
data->fifo_main[AGC_RED][i] = device->fifo_data[AGC_RED][i];
data->fifo_main[AGC_GREEN][i] = device->fifo_data[AGC_GREEN][i];
data->fifo_main[AGC_BLUE][i] = device->fifo_data[AGC_BLUE][i];
__max869_eol_hrm_data(data->fifo_main[AGC_IR][i], data->fifo_main[AGC_RED][i],
data->fifo_main[AGC_GREEN][i], data->fifo_main[AGC_BLUE][i], &device->eol_data,
EOL_NEW_MODE_DC_LOW, EOL_NEW_DC_LOW_SKIP_CNT, EOL_NEW_DC_LOW_SIZE);
}
if (device->eol_data.eol_hrm_data[EOL_NEW_MODE_DC_LOW].done == EOL_SUCCESS) {
HRM_dbg("%s FINISH : _EOL_STATE_TYPE_NEW_DC_MODE_LOW\n", __func__);
err = max869_set_current(EOL_NEW_DC_MID_LED_IR_CURRENT, EOL_NEW_DC_MID_LED_RED_CURRENT,
EOL_NEW_DC_MID_LED_GREEN_CURRENT, EOL_NEW_DC_MID_LED_BLUE_CURRENT);
device->eol_data.state = _EOL_STATE_TYPE_NEW_DC_MODE_MID;
device->eol_data.eol_count = 0;
}
break;
case _EOL_STATE_TYPE_NEW_DC_MODE_MID:
for (i = 0; i < device->fifo_samples; i++) {
data->fifo_main[AGC_IR][i] = device->fifo_data[AGC_IR][i];
data->fifo_main[AGC_RED][i] = device->fifo_data[AGC_RED][i];
data->fifo_main[AGC_GREEN][i] = device->fifo_data[AGC_GREEN][i];
data->fifo_main[AGC_BLUE][i] = device->fifo_data[AGC_BLUE][i];
__max869_eol_hrm_data(data->fifo_main[AGC_IR][i], data->fifo_main[AGC_RED][i],
data->fifo_main[AGC_GREEN][i], data->fifo_main[AGC_BLUE][i], &device->eol_data,
EOL_NEW_MODE_DC_MID, EOL_NEW_DC_MIDDLE_SKIP_CNT, EOL_NEW_DC_MIDDLE_SIZE);
}
if (device->eol_data.eol_hrm_data[EOL_NEW_MODE_DC_MID].done == EOL_SUCCESS) {
HRM_dbg("%s FINISH : _EOL_STATE_TYPE_NEW_DC_MODE_MID\n", __func__);
err = max869_set_current(EOL_NEW_DC_HIGH_LED_IR_CURRENT, EOL_NEW_DC_HIGH_LED_RED_CURRENT,
EOL_NEW_DC_HIGH_LED_GREEN_CURRENT, EOL_NEW_DC_HIGH_LED_BLUE_CURRENT);
device->eol_data.state = _EOL_STATE_TYPE_NEW_DC_MODE_HIGH;
device->eol_data.eol_count = 0;
}
break;
case _EOL_STATE_TYPE_NEW_DC_MODE_HIGH:
for (i = 0; i < device->fifo_samples; i++) {
data->fifo_main[AGC_IR][i] = device->fifo_data[AGC_IR][i];
data->fifo_main[AGC_RED][i] = device->fifo_data[AGC_RED][i];
data->fifo_main[AGC_GREEN][i] = device->fifo_data[AGC_GREEN][i];
data->fifo_main[AGC_BLUE][i] = device->fifo_data[AGC_BLUE][i];
__max869_eol_hrm_data(data->fifo_main[AGC_IR][i], data->fifo_main[AGC_RED][i],
data->fifo_main[AGC_GREEN][i], data->fifo_main[AGC_BLUE][i], &device->eol_data,
EOL_NEW_MODE_DC_HIGH, EOL_NEW_DC_HIGH_SKIP_CNT, EOL_NEW_DC_HIGH_SIZE);
}
if (device->eol_data.eol_hrm_data[EOL_NEW_MODE_DC_HIGH].done == EOL_SUCCESS) {
HRM_dbg("%s FINISH : _EOL_STATE_TYPE_NEW_DC_MODE_HIGH\n", __func__);
err = __max869_write_reg(device, MAX86915_DAC1_XTALK_CODE,
0x1f);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC1_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(device, MAX86915_DAC2_XTALK_CODE,
0x1f);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC2_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(device, MAX86915_DAC3_XTALK_CODE,
0x1f);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC3_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(device, MAX86915_DAC4_XTALK_CODE,
0x1f);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC4_XTALK_CODE!\n",
__func__);
return -EIO;
}
device->eol_data.state = _EOL_STATE_TYPE_NEW_DC_XTALK;
device->eol_data.eol_count = 0;
}
break;
case _EOL_STATE_TYPE_NEW_DC_XTALK:
for (i = 0; i < device->fifo_samples; i++) {
data->fifo_main[AGC_IR][i] = device->fifo_data[AGC_IR][i];
data->fifo_main[AGC_RED][i] = device->fifo_data[AGC_RED][i];
data->fifo_main[AGC_GREEN][i] = device->fifo_data[AGC_GREEN][i];
data->fifo_main[AGC_BLUE][i] = device->fifo_data[AGC_BLUE][i];
__max869_eol_hrm_data(data->fifo_main[AGC_IR][i], data->fifo_main[AGC_RED][i],
data->fifo_main[AGC_GREEN][i], data->fifo_main[AGC_BLUE][i], &device->eol_data,
EOL_NEW_MODE_DC_XTALK, EOL_NEW_DC_XTALK_SKIP_CNT, EOL_NEW_DC_XTALK_SIZE);
}
if (device->eol_data.eol_hrm_data[EOL_NEW_MODE_DC_XTALK].done == EOL_SUCCESS) {
HRM_dbg("%s FINISH : _EOL_STATE_TYPE_NEW_DC_XTALK\n", __func__);
__max86915_enable_eol_freq(device);
device->eol_data.eol_count = 0;
}
break;
case _EOL_STATE_TYPE_NEW_FREQ_MODE:
if (device->eol_data.eol_freq_data.done != EOL_SUCCESS) {
__max869_eol_freq_data(&device->eol_data, SELF_DIVID_100HZ);
__max869_eol_check_done(&device->eol_data, SELF_MODE_400HZ, device);
}
if (!(device->eol_test_status) && device->sample_cnt >= total_eol_cnt)
device->eol_data.state = _EOL_STATE_TYPE_NEW_END;
break;
case _EOL_STATE_TYPE_NEW_END:
HRM_dbg("@@@@@NEVER CALL !!!!FAIL EOL TEST : HR_STATE: _EOL_STATE_TYPE_NEW_END !!!!!!!!!!!!!!!!!!\n");
__max869_eol_check_done(&device->eol_data, SELF_MODE_400HZ, device);
device->eol_data.state = _EOL_STATE_TYPE_NEW_STOP;
break;
case _EOL_STATE_TYPE_NEW_STOP:
break;
default:
break;
}
return err;
}
static int __max86915_hrm_read_data(struct max869_device_data *device, int *data)
{
int err;
u8 recvData[MAX_LED_NUM * NUM_BYTES_PER_SAMPLE] = { 0x00, };
int i, j = 0;
int ret = 0;
if (device->sample_cnt == MAX86915_COUNT_MAX)
device->sample_cnt = 0;
recvData[0] = MAX86915_FIFO_DATA;
err = __max869_read_reg(device, recvData,
device->num_samples * NUM_BYTES_PER_SAMPLE);
if (err != 0) {
HRM_dbg("%s __max869_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData[0]);
return -EIO;
}
for (i = 0; i < MAX_LED_NUM; i++) {
if (device->flex_mode & (1 << i)) {
data[i] = recvData[j++] << 16 & 0x70000;
data[i] += recvData[j++] << 8;
data[i] += recvData[j++] << 0;
} else
data[i] = 0;
}
if ((device->sample_cnt % 1000) == 0)
HRM_info("%s - %u, %u, %u, %u\n", __func__,
data[0], data[1], data[2], data[3]);
if (device->agc_led_set == 0) {
device->agc_ch_adc[0] = data[0];
device->agc_ch_adc[1] = data[1];
device->agc_ch_adc[2] = data[2];
device->agc_ch_adc[3] = data[3];
device->sample_cnt++;
} else {
data[0] = device->agc_ch_adc[0];
data[1] = device->agc_ch_adc[1];
data[2] = device->agc_ch_adc[2];
data[3] = device->agc_ch_adc[3];
}
return ret;
}
static int __max86915_awb_flicker_read_data(struct max869_device_data *device, int *data)
{
int err;
u8 recvData[AWB_INTERVAL * NUM_BYTES_PER_SAMPLE] = { 0x00, };
int ret = 0;
int mode_changed = 0;
int i;
u8 irq_status = 0;
int previous_awb_data = 0;
recvData[0] = MAX86915_INTERRUPT_STATUS;
err = __max869_read_reg(device, recvData, 1);
if (err != 0) {
HRM_dbg("%s __max869_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData[0]);
return -EIO;
}
irq_status = recvData[0];
if (irq_status != 0x80) {
HRM_dbg("%s - irq_status 0x%x, return 1\n", __func__, irq_status);
return 1;
}
recvData[0] = MAX86915_FIFO_DATA;
err = __max869_read_reg(device, recvData, AWB_INTERVAL * NUM_BYTES_PER_SAMPLE);
if (err != 0) {
HRM_dbg("%s __max869_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData[0]);
return -EIO;
}
*data = (recvData[0 + (AWB_INTERVAL - 1)*NUM_BYTES_PER_SAMPLE] << 16 & 0x70000)
+ (recvData[1 + (AWB_INTERVAL - 1)*NUM_BYTES_PER_SAMPLE] << 8)
+ (recvData[2 + (AWB_INTERVAL - 1)*NUM_BYTES_PER_SAMPLE] << 0);
previous_awb_data = (recvData[0 + (AWB_INTERVAL - 2)*NUM_BYTES_PER_SAMPLE] << 16 & 0x70000)
+ (recvData[1 + (AWB_INTERVAL - 2)*NUM_BYTES_PER_SAMPLE] << 8)
+ (recvData[2 + (AWB_INTERVAL - 2)*NUM_BYTES_PER_SAMPLE] << 0);
if (device->awb_sample_cnt > CONFIG_SKIP_CNT) {
mutex_lock(&device->flickerdatalock);
for (i = 0; i < AWB_INTERVAL; i++) {
if (device->flicker_data_cnt < FLICKER_DATA_CNT) {
device->flicker_data[device->flicker_data_cnt++] =
(recvData[0 + i*NUM_BYTES_PER_SAMPLE] << 16 & 0x70000)
+ (recvData[1 + i*NUM_BYTES_PER_SAMPLE] << 8)
+ (recvData[2 + i*NUM_BYTES_PER_SAMPLE] << 0);
}
}
mutex_unlock(&device->flickerdatalock);
}
/* Change Configuation */
if (device->awb_flicker_status == AWB_CONFIG1) {
if (*data > AWB_MAX86915_CONFIG_TH2
&& previous_awb_data > AWB_MAX86915_CONFIG_TH2) { /* Change to AWB_CONFIG2 */
err = __max869_write_reg(device, MAX86915_MODE_CONFIGURATION, 0x00);
/* 16384 uA setting */
err = __max869_write_reg(device,
MAX86915_MODE_CONFIGURATION_2, 0x6F);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
device->awb_flicker_status = AWB_CONFIG2;
mode_changed = 1;
}
} else if (device->awb_flicker_status == AWB_CONFIG2) {
if (*data < AWB_MAX86915_CONFIG_TH3
&& previous_awb_data < AWB_MAX86915_CONFIG_TH3) { /* Change to AWB_CONFIG1 */
err = __max869_write_reg(device, MAX86915_MODE_CONFIGURATION, 0x00);
/* 4096 uA setting */
err = __max869_write_reg(device,
MAX86915_MODE_CONFIGURATION_2, 0x0F);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
device->awb_flicker_status = AWB_CONFIG1;
mode_changed = 1;
}
}
if (device->awb_sample_cnt > CONFIG_SKIP_CNT) {
if (device->awb_flicker_status < AWB_CONFIG2)
*data = *data >> 3; /* 2uA setting should devided by 8 */
ret = 0;
} else {
ret = 1;
HRM_dbg("%s - awb sample cnt %d, less than CONFIG SKIP CNT\n", __func__, device->awb_sample_cnt);
}
if (mode_changed == 1) {
/* Flush Buffer */
err = __max869_write_reg(device,
MAX86915_MODE_CONFIGURATION, 0x07);
if (err != 0) {
HRM_dbg("%s - error init MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
device->flicker_data_cnt = 0;
device->awb_sample_cnt = 0;
ret = 1;
HRM_dbg("%s - mode changed to : %d\n", __func__, device->awb_flicker_status);
} else
device->awb_sample_cnt += AWB_INTERVAL;
return ret;
}
int max869_i2c_read(u32 reg, u32 *value, u32 *size)
{
int err;
*value = reg;
err = __max869_read_reg(max869_data, (u8 *)value, 1);
*size = 1;
return err;
}
int max869_i2c_write(u32 reg, u32 value)
{
int err;
err = __max869_write_reg(max869_data, (u8)reg, (u8)value);
return err;
}
static long __max869_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
void __user *argp = (void __user *)arg;
int ret = 0;
struct max869_device_data *data = container_of(file->private_data,
struct max869_device_data, miscdev);
/* HRM_info("%s - ioctl start\n", __func__); */
mutex_lock(&data->flickerdatalock);
switch (cmd) {
case MAX86915_IOCTL_READ_FLICKER:
ret = copy_to_user(argp,
data->flicker_data,
sizeof(int)*FLICKER_DATA_CNT);
if (unlikely(ret))
goto ioctl_error;
break;
default:
HRM_dbg("%s - invalid cmd\n", __func__);
break;
}
mutex_unlock(&data->flickerdatalock);
return ret;
ioctl_error:
mutex_unlock(&data->flickerdatalock);
HRM_dbg("%s - read flicker data err(%d)\n", __func__, ret);
return -ret;
}
static const struct file_operations __max869_fops = {
.owner = THIS_MODULE,
.open = nonseekable_open,
.unlocked_ioctl = __max869_ioctl,
};
static void __max869_init_agc_settings(struct max869_device_data *data)
{
data->agc_is_enable = true;
data->update_led = max86915_update_led_current;
data->agc_led_out_percent = MAX86915_AGC_DEFAULT_LED_OUT_RANGE;
data->agc_corr_coeff = MAX86915_AGC_DEFAULT_CORRECTION_COEFF;
data->agc_min_num_samples = MAX86915_AGC_DEFAULT_MIN_NUM_PERCENT;
data->agc_sensitivity_percent = MAX86915_AGC_DEFAULT_SENSITIVITY_PERCENT;
data->threshold_default = MAX86915_THRESHOLD_DEFAULT;
}
static int __max869_trim_check(void)
{
u8 recvData;
u8 reg93_val;
int err;
max869_data->isTrimmed = 0;
err = __max869_write_reg(max869_data, 0xFF, 0x54);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_TEST0!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(max869_data, 0xFF, 0x4d);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_TEST1!\n",
__func__);
return -EIO;
}
recvData = 0x93;
err = __max869_read_reg(max869_data, &recvData, 1);
if (err != 0) {
HRM_dbg("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
reg93_val = recvData;
err = __max869_write_reg(max869_data, 0xFF, 0x00);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_TEST3!\n",
__func__);
return -EIO;
}
max869_data->isTrimmed = (reg93_val & 0x20);
HRM_dbg("%s isTrimmed :%d, reg93_val : %d\n", __func__, max869_data->isTrimmed, reg93_val);
return err;
}
static int __max869_init_var(struct max869_device_data *data)
{
int err;
u8 buffer[2] = {0, };
buffer[0] = MAX86915_REV_ID_REG;
err = __max869_read_reg(data, buffer, 2);
if (err) {
HRM_dbg("%s MAX86915 WHOAMI read fail0\n", __func__);
err = -ENODEV;
goto err_of_read_chipid;
}
data->ir_led = 0;
data->red_led = 1;
data->green_led = 2;
data->blue_led = 3;
data->i2c_err_cnt = 0;
data->ir_curr = 0;
data->red_curr = 0;
data->green_curr = 0;
data->blue_curr = 0;
data->ir_adc = 0;
data->red_adc = 0;
data->green_adc = 0;
data->blue_adc = 0;
data->mode_sdk_enabled = 0;
if (buffer[1] == MAX86915_PART_ID) {
data->default_current1 = data->init_current[AGC_IR];
data->default_current2 = data->init_current[AGC_RED];
data->default_current3 = data->init_current[AGC_GREEN];
data->default_current4 = data->init_current[AGC_BLUE];
data->default_xtalk_code1 = MAX86915_DEFAULT_XTALK_CODE1;
data->default_xtalk_code2 = MAX86915_DEFAULT_XTALK_CODE2;
data->default_xtalk_code3 = MAX86915_DEFAULT_XTALK_CODE3;
data->default_xtalk_code4 = MAX86915_DEFAULT_XTALK_CODE4;
data->part_type = __max869_get_part_id(data);
} else {
HRM_dbg("%s MAX86915 WHOAMI read fail\n", __func__);
err = -ENODEV;
goto err_of_read_chipid;
}
/* AGC setting */
__max869_init_agc_settings(data);
return 0;
err_of_read_chipid:
return -EINVAL;
}
int max869_init_device(struct i2c_client *client)
{
int err = 0;
HRM_info("%s client = %p\n", __func__, client);
/* allocate some memory for the device */
max869_data = kzalloc(sizeof(struct max869_device_data), GFP_KERNEL);
if (max869_data == NULL) {
HRM_dbg("%s - couldn't allocate memory\n", __func__);
return -ENOMEM;
}
max869_data->miscdev.minor = MISC_DYNAMIC_MINOR;
max869_data->miscdev.name = "max_hrm";
max869_data->miscdev.fops = &__max869_fops;
max869_data->miscdev.mode = S_IRUGO;
err = misc_register(&max869_data->miscdev);
if (err < 0) {
HRM_dbg("%s - failed to register Device\n", __func__);
goto err_register_fail;
}
max869_data->flicker_data = kzalloc(sizeof(int)*FLICKER_DATA_CNT, GFP_KERNEL);
if (max869_data->flicker_data == NULL) {
HRM_dbg("%s - couldn't allocate flicker memory\n", __func__);
goto err_flicker_alloc_fail;
}
mutex_init(&max869_data->flickerdatalock);
max869_data->threshold_default = MAX86915_THRESHOLD_DEFAULT;
max869_data->client = client;
hrm_data = i2c_get_clientdata(max869_data->client);
if (hrm_data == NULL) {
err = -EIO;
HRM_dbg("%s couldn't get hrm_data\n", __func__);
goto err_get_hrm_data;
}
if (hrm_data->init_current[AGC_IR] != 0) {
max869_data->init_current[AGC_IR] = hrm_data->init_current[AGC_IR];
max869_data->init_current[AGC_RED] = hrm_data->init_current[AGC_RED];
max869_data->init_current[AGC_GREEN] = hrm_data->init_current[AGC_GREEN];
max869_data->init_current[AGC_BLUE] = hrm_data->init_current[AGC_BLUE];
} else {
max869_data->init_current[AGC_IR] = MAX86915_IR_INIT_CURRENT;
max869_data->init_current[AGC_RED] = MAX86915_RED_INIT_CURRENT;
max869_data->init_current[AGC_GREEN] = MAX86915_GREEN_INIT_CURRENT;
max869_data->init_current[AGC_BLUE] = MAX86915_BLUE_INIT_CURRENT;
}
err = __max869_init_var(max869_data);
if (err < 0) {
err = -EIO;
goto err_init_fail;
}
if (__max869_init(max869_data) < 0) {
err = -EIO;
HRM_dbg("%s __max869_init failed\n", __func__);
goto err_init_fail;
}
__max869_trim_check();
goto done;
err_init_fail:
err_get_hrm_data:
mutex_destroy(&max869_data->flickerdatalock);
kfree(max869_data->flicker_data);
err_flicker_alloc_fail:
misc_deregister(&max869_data->miscdev);
err_register_fail:
kfree(max869_data);
HRM_dbg("%s failed\n", __func__);
done:
return err;
}
int max869_deinit_device(void)
{
mutex_destroy(&max869_data->flickerdatalock);
misc_deregister(&max869_data->miscdev);
kfree(max869_data->flicker_data);
kfree(max869_data);
max869_data = NULL;
return 0;
}
int max869_enable(enum hrm_mode mode)
{
int err = 0;
max869_data->hrm_mode = mode;
HRM_dbg("%s - enable_m : 0x%x\t cur_m : 0x%x\t cur_p : 0x%x\n",
__func__, mode, max869_data->hrm_mode, max869_data->part_type);
__max869_print_mode(max869_data->hrm_mode);
if (mode == MODE_HRM)
err = __max869_set_reg_hrm(max869_data);
else if (mode == MODE_AMBIENT)
err = __max869_set_reg_ambient(max869_data);
else if (mode == MODE_PROX)
err = __max869_set_reg_prox(max869_data);
else if (mode == MODE_SDK_IR)
err = __max869_set_reg_sdk(max869_data);
else
HRM_dbg("%s - MODE_UNKNOWN\n", __func__);
if (max869_data->agc_mode != M_NONE) {
agc_pre_s = S_UNKNOWN;
agc_cur_s = S_INIT;
}
max869_data->agc_sample_cnt[0] = 0;
max869_data->agc_sample_cnt[1] = 0;
max869_data->agc_sample_cnt[2] = 0;
max869_data->agc_sample_cnt[3] = 0;
return err;
}
int max869_disable(enum hrm_mode mode)
{
int err = 0;
max869_data->hrm_mode = mode;
HRM_info("%s - disable_m : 0x%x\t cur_m : 0x%x\n", __func__, mode, max869_data->hrm_mode);
__max869_print_mode(max869_data->hrm_mode);
if (max869_data->hrm_mode == 0) {
__max869_disable(max869_data);
return err;
}
if (max869_data->agc_mode != M_NONE) {
agc_pre_s = S_UNKNOWN;
agc_cur_s = S_INIT;
}
return err;
}
int max869_get_current(u8 *d1, u8 *d2, u8 *d3, u8 *d4)
{
*d1 = max869_data->led_current1; /* IR */
*d2 = max869_data->led_current2; /* RED */
*d3 = max869_data->led_current3; /* GEEN */
*d4 = max869_data->led_current4; /* BLUE */
return 0;
}
int max869_set_current(u8 d1, u8 d2, u8 d3, u8 d4)
{
int err = 0;
u8 recvData;
max869_data->led_current1 = d1; /* set ir; */
max869_data->led_current2 = d2; /* set red; */
max869_data->led_current3 = d3; /* set ir; */
max869_data->led_current4 = d4; /* set red; */
HRM_info("%s - 0x%x 0x%x 0x%x 0x%x\n", __func__,
max869_data->led_current1, max869_data->led_current2,
max869_data->led_current3, max869_data->led_current4);
if (max869_data->part_type < PART_TYPE_MAX86915_CS_211) {
err = __max869_write_reg(max869_data, MAX86915_MODE_CONFIGURATION, 0x00);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
}
err = __max869_write_reg(max869_data, MAX86915_LED1_PA,
max869_data->led_current1);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED1_PA!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(max869_data, MAX86915_LED2_PA,
max869_data->led_current2);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED2_PA!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(max869_data, MAX86915_LED3_PA,
max869_data->led_current3);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED3_PA!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(max869_data, MAX86915_LED4_PA,
max869_data->led_current4);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_LED4_PA!\n",
__func__);
return -EIO;
}
if (max869_data->part_type < PART_TYPE_MAX86915_CS_211) {
recvData = MAX86915_FIFO_CONFIG;
err = __max869_read_reg(max869_data, &recvData, 1);
max869_data->agc_led_set = AGC_LED_SKIP_CNT >>
((recvData & MAX86915_SMP_AVE_MASK) >> MAX86915_SMP_AVE_OFFSET);
err = __max869_write_reg(max869_data, MAX86915_MODE_CONFIGURATION, 0x03);
}
return err;
}
int max869_get_led_test(u8 *result)
{
int err = 0;
u8 recvData = 0;
int i = 0;
int retry = 0;
*result = 0;
err = __max869_write_reg(max869_data, MAX86915_MODE_CONFIGURATION,
0x1 << MAX86915_RESET_OFFSET);
if (err != 0) {
HRM_dbg("%s - error writing MAX86915_MODE_CONFIGURATION err:%d\n",
__func__, err);
return -EIO;
}
usleep_range(100000, 110000);
err = __max869_write_reg(max869_data, MAX86915_LED1_PA, 0xFF);
if (err != 0) {
HRM_dbg("%s - error writing MAX86915_LED1_PA err:%d\n",
__func__, err);
return -EIO;
}
err = __max869_write_reg(max869_data, MAX86915_LED2_PA, 0xFF);
if (err != 0) {
HRM_dbg("%s - error writing MAX86915_LED2_PA err:%d\n",
__func__, err);
return -EIO;
}
err = __max869_write_reg(max869_data, MAX86915_LED3_PA, 0x08);
if (err != 0) {
HRM_dbg("%s - error writing MAX86915_LED3_PA err:%d\n",
__func__, err);
return -EIO;
}
err = __max869_write_reg(max869_data, MAX86915_LED4_PA, 0x08);
if (err != 0) {
HRM_dbg("%s - error writing MAX86915_LED4_PA err:%d\n",
__func__, err);
return -EIO;
}
err = __max869_write_reg(max869_data, MAX86915_MODE_CONFIGURATION, 0x01);
if (err != 0) {
HRM_dbg("%s - error writing MAX86915_MODE_CONFIGURATION err:%d\n",
__func__, err);
return -EIO;
}
usleep_range(1200, 1210);
for (retry = 0 ; retry < 3; retry++) {
*result = 0;
for (i = 0; i < MAX_LED_NUM; i++) {
err = __max869_write_reg(max869_data, 0x31, 0x10 << i);
if (err != 0) {
HRM_dbg("%s - error writing MAX86915_LED_TEST_EN%d err:%d\n",
__func__, i, err);
return -EIO;
}
usleep_range(100, 110);
err = __max869_write_reg(max869_data, 0x31, (0x10 << i) | 0x01);
if (err != 0) {
HRM_dbg("%s - error writing MAX86915_LED_TEST_EN%d err:%d\n",
__func__, i, err);
return -EIO;
}
usleep_range(1000, 1100);
recvData = 0x32;
err = __max869_read_reg(max869_data, &recvData, 1);
if (err != 0) {
HRM_dbg("%s - error reading MAX86915_COMP_OUT%d err:%d\n",
__func__, i, err);
return -EIO;
}
*result |= (recvData & (1 << i));
}
if (*result == 0)
break;
HRM_dbg("%s - fail retry %d = 0x%02x\n", __func__, retry, *result);
}
err = __max869_write_reg(max869_data, MAX86915_MODE_CONFIGURATION,
0x1 << MAX86915_SHDN_OFFSET);
if (err != 0) {
HRM_dbg("%s - error writing MAX86915_MODE_CONFIGURATION err:%d\n",
__func__, err);
return -EIO;
}
return 0;
}
int max869_get_xtalk(u8 *d1, u8 *d2, u8 *d3, u8 *d4)
{
*d1 = max869_data->xtalk_code1; /* IR */
*d2 = max869_data->xtalk_code2; /* RED */
*d3 = max869_data->xtalk_code3; /* GEEN */
*d4 = max869_data->xtalk_code4; /* BLUE */
return 0;
}
int max869_set_xtalk(u8 d1, u8 d2, u8 d3, u8 d4)
{
int err = 0;
max869_data->xtalk_code1 = d1; /* set ir; */
max869_data->xtalk_code2 = d2; /* set red; */
max869_data->xtalk_code3 = d3; /* set ir; */
max869_data->xtalk_code4 = d4; /* set red; */
HRM_info("%s - 0x%x 0x%x 0x%x 0x%x\n", __func__,
max869_data->xtalk_code1, max869_data->xtalk_code2,
max869_data->xtalk_code3, max869_data->xtalk_code4);
if (max869_data->led_current1 == MAX86915_MIN_CURRENT)
max869_data->default_current1 = max869_data->init_current[AGC_IR]
+ MAX86915_IR_CURRENT_STEP * max869_data->xtalk_code1;
if (max869_data->led_current2 == MAX86915_MIN_CURRENT)
max869_data->default_current2 = max869_data->init_current[AGC_RED]
+ MAX86915_RED_CURRENT_STEP * max869_data->xtalk_code2;
if (max869_data->led_current3 == MAX86915_MIN_CURRENT)
max869_data->default_current3 = max869_data->init_current[AGC_GREEN]
+ MAX86915_GREEN_CURRENT_STEP * max869_data->xtalk_code3;
if (max869_data->led_current4 == MAX86915_MIN_CURRENT)
max869_data->default_current4 = max869_data->init_current[AGC_BLUE]
+ MAX86915_BLUE_CURRENT_STEP * max869_data->xtalk_code4;
HRM_info("%s - current 0x%x 0x%x 0x%x 0x%x\n", __func__,
max869_data->default_current1, max869_data->default_current2,
max869_data->default_current3, max869_data->default_current4);
if (max869_data->hrm_mode) {
err = __max869_write_reg(max869_data, MAX86915_DAC1_XTALK_CODE,
max869_data->xtalk_code1);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC1_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(max869_data, MAX86915_DAC2_XTALK_CODE,
max869_data->xtalk_code2);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC2_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(max869_data, MAX86915_DAC3_XTALK_CODE,
max869_data->xtalk_code3);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC3_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(max869_data, MAX86915_DAC4_XTALK_CODE,
max869_data->xtalk_code4);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_DAC4_XTALK_CODE!\n",
__func__);
return -EIO;
}
}
return err;
}
int max869_get_sdk_enabled(u8 *enabled)
{
*enabled = max869_data->mode_sdk_enabled;
return 0;
}
int max869_set_sdk_enabled(int channel, int enable)
{
HRM_dbg("%s - channel enable %d, %d\n", __func__, channel, enable);
if (enable) {
max869_data->mode_sdk_enabled |= (1<<channel);
} else {
max869_data->mode_sdk_enabled &= ~(1<<channel);
/* LED PA to 0 */
switch (channel) {
case 0:
max869_data->led_current1 = 0x0;
max869_data->reached_thresh[AGC_IR] = 0;
max869_data->agc_sum[AGC_IR] = 0;
max869_data->agc_sample_cnt[AGC_IR] = 0;
max869_data->agc_current[AGC_IR] =
(max869_data->led_current1 * MAX86915_CURRENT_PER_STEP);
break;
case 1:
max869_data->led_current2 = 0x0;
max869_data->reached_thresh[AGC_RED] = 0;
max869_data->agc_sum[AGC_RED] = 0;
max869_data->agc_sample_cnt[AGC_RED] = 0;
max869_data->agc_current[AGC_RED] =
(max869_data->led_current2 * MAX86915_CURRENT_PER_STEP);
break;
case 2:
max869_data->led_current3 = 0x0;
max869_data->reached_thresh[AGC_GREEN] = 0;
max869_data->agc_sum[AGC_GREEN] = 0;
max869_data->agc_sample_cnt[AGC_GREEN] = 0;
max869_data->agc_current[AGC_GREEN] =
(max869_data->led_current3 * MAX86915_CURRENT_PER_STEP);
break;
case 3:
max869_data->led_current4 = 0x0;
max869_data->reached_thresh[AGC_BLUE] = 0;
max869_data->agc_sum[AGC_BLUE] = 0;
max869_data->agc_sample_cnt[AGC_BLUE] = 0;
max869_data->agc_current[AGC_BLUE] =
(max869_data->led_current4 * MAX86915_CURRENT_PER_STEP);
break;
}
max869_set_current(max869_data->led_current1,
max869_data->led_current2,
max869_data->led_current3,
max869_data->led_current4);
}
return 0;
}
int max869_read_data(struct hrm_output_data *data)
{
int err = 0;
int ret;
int raw_data[4] = {0x00, };
u8 recvData;
int i = 0;
int j = 0;
int data_fifo[MAX_LED_NUM][MAX86915_FIFO_SIZE];
data->sub_num = 0;
data->fifo_num = 0;
for (i = 0; i < MAX86915_FIFO_SIZE; i++) {
for (j = 0; j < MAX_LED_NUM; j++) {
data_fifo[j][i] = 0;
data->fifo_main[j][i] = 0;
}
}
switch (max869_data->hrm_mode) {
case MODE_HRM:
#ifndef ENABLE_POLL_DELAY
case MODE_SDK_IR:
#endif
case MODE_PROX:
if (max869_data->eol_test_is_enable) {
err = __max86915_eol_read_data(data, max869_data, raw_data);
} else {
err = __max86915_hrm_read_data(max869_data, raw_data);
}
break;
case MODE_AMBIENT:
err = __max86915_awb_flicker_read_data(max869_data, raw_data);
break;
#ifdef ENABLE_POLL_DELAY
case MODE_SDK_IR:
err = __max869_fifo_irq_handler(max869_data);
#ifdef CONFIG_4CH_IOCTL
mutex_lock(&max869_data->flickerdatalock);
for (i = 0; i < max869_data->fifo_samples; i++) {
if (max869_data->agc_led_set > 0) {
max869_data->flicker_data[i*4+0] = max869_data->agc_ch_adc[0];
max869_data->flicker_data[i*4+1] = max869_data->agc_ch_adc[1];
max869_data->flicker_data[i*4+2] = max869_data->agc_ch_adc[2];
max869_data->flicker_data[i*4+3] = max869_data->agc_ch_adc[3];
} else {
max869_data->flicker_data[i*4+0] = max869_data->fifo_data[0][i];
max869_data->flicker_data[i*4+1] = max869_data->fifo_data[1][i];
max869_data->flicker_data[i*4+2] = max869_data->fifo_data[2][i];
max869_data->flicker_data[i*4+3] = max869_data->fifo_data[3][i];
max869_data->agc_ch_adc[0] = max869_data->fifo_data[0][max869_data->fifo_samples-1];
max869_data->agc_ch_adc[1] = max869_data->fifo_data[1][max869_data->fifo_samples-1];
max869_data->agc_ch_adc[2] = max869_data->fifo_data[2][max869_data->fifo_samples-1];
max869_data->agc_ch_adc[3] = max869_data->fifo_data[3][max869_data->fifo_samples-1];
}
}
mutex_unlock(&max869_data->flickerdatalock);
#endif
#ifdef CONFIG_4CH_INPUT
for (i = 0; i < max869_data->fifo_samples; i++) {
if (max869_data->agc_led_set > 0) {
data->fifo_main[AGC_IR][i] = max869_data->agc_ch_adc[0];
data->fifo_main[AGC_RED][i] = max869_data->agc_ch_adc[1];
data->fifo_main[AGC_GREEN][i] = max869_data->agc_ch_adc[2];
data->fifo_main[AGC_BLUE][i] = max869_data->agc_ch_adc[3];
} else {
data->fifo_main[AGC_IR][i] = max869_data->fifo_data[0][i];
data->fifo_main[AGC_RED][i] = max869_data->fifo_data[1][i];
data->fifo_main[AGC_GREEN][i] = max869_data->fifo_data[2][i];
data->fifo_main[AGC_BLUE][i] = max869_data->fifo_data[3][i];
max869_data->agc_ch_adc[0] = max869_data->fifo_data[0][max869_data->fifo_samples-1];
max869_data->agc_ch_adc[1] = max869_data->fifo_data[1][max869_data->fifo_samples-1];
max869_data->agc_ch_adc[2] = max869_data->fifo_data[2][max869_data->fifo_samples-1];
max869_data->agc_ch_adc[3] = max869_data->fifo_data[3][max869_data->fifo_samples-1];
}
}
data->fifo_num = fifo_full_cnt;
#endif
if (max869_data->agc_led_set == 0) /* for AGC */
max869_data->sample_cnt += max869_data->fifo_samples;
break;
#endif
default:
err = 1; /* error case */
break;
}
if (err < 0)
HRM_dbg("__max86915_hrm_read_data err : %d\n", err);
if (err == 0) {
data->main_num = 2;
if (max869_data->hrm_mode == MODE_AMBIENT) {
data->data_main[0] = raw_data[0];
if (max869_data->flicker_data_cnt == FLICKER_DATA_CNT) {
data->data_main[1] = -2;
max869_data->flicker_data_cnt = 0;
} else {
data->data_main[1] = 0;
}
#ifndef ENABLE_POLL_DELAY
} else if (max869_data->hrm_mode == MODE_SDK_IR) {
data->main_num = 4;
data->data_main[0] = raw_data[0];
data->data_main[1] = raw_data[1];
data->data_main[2] = raw_data[2];
data->data_main[3] = raw_data[3];
#else
} else if (max869_data->hrm_mode == MODE_SDK_IR) {
#ifdef CONFIG_4CH_IOCTL
data->data_main[1] = -3;
#endif
#ifdef CONFIG_4CH_INPUT
data->data_main[1] = -3;
raw_data[0] = data->fifo_main[AGC_IR][0];
raw_data[1] = data->fifo_main[AGC_RED][0];
raw_data[2] = data->fifo_main[AGC_GREEN][0];
raw_data[3] = data->fifo_main[AGC_BLUE][0];
#endif
#endif
} else if (max869_data->hrm_mode == MODE_HRM) {
data->main_num = 2;
data->data_main[0] = raw_data[0];
data->data_main[1] = raw_data[1];
#ifdef CONFIG_HRM_GREEN_BLUE
data->main_num = 4;
data->data_main[2] = raw_data[2];
data->data_main[3] = raw_data[3];
#endif
} else {
data->main_num = 2;
data->data_main[0] = raw_data[0];
data->data_main[1] = raw_data[1];
}
data->mode = max869_data->hrm_mode;
ret = 0;
} else
ret = 1;
/* Interrupt Clear */
recvData = MAX86915_INTERRUPT_STATUS;
err = __max869_read_reg(max869_data, &recvData, 1);
if (err != 0) {
HRM_dbg("%s - __max869_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
if (!max869_data->agc_enabled || max869_data->hrm_mode == MODE_PROX) {
if (max869_data->sample_cnt > AGC_SKIP_CNT)
max869_data->agc_enabled = 1;
else {
data->main_num = 0;
data->fifo_num = 0;
}
}
if (max869_data->agc_mode != M_NONE
&& agc_is_enabled
&& max869_data->agc_enabled
&& (max869_data->agc_led_set == 0)) {
__max869_cal_agc(raw_data[0], raw_data[1], raw_data[2], raw_data[3]);
}
if (max869_data->agc_led_set > 0) {
if (max869_data->hrm_mode == MODE_SDK_IR) {
if (max869_data->agc_led_set < fifo_full_cnt)
max869_data->agc_led_set = 0;
else
max869_data->agc_led_set -= fifo_full_cnt;
} else {
max869_data->agc_led_set--;
}
}
return ret;
}
int max869_get_chipid(u64 *chip_id)
{
u8 recvData;
int err;
u32 c1_code = 0;
u32 c2_code = 0;
u32 c3_code = 0;
u32 c4_code = 0;
*chip_id = 0;
err = __max869_write_reg(max869_data, 0xFF, 0x54);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_TEST0!\n",
__func__);
return -EIO;
}
err = __max869_write_reg(max869_data, 0xFF, 0x4d);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_TEST1!\n",
__func__);
return -EIO;
}
recvData = 0xC1;
err = __max869_read_reg(max869_data, &recvData, 1);
if (err != 0) {
HRM_dbg("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
c1_code = recvData;
recvData = 0xC2;
err = __max869_read_reg(max869_data, &recvData, 1);
if (err != 0) {
HRM_dbg("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
c2_code = recvData;
recvData = 0xC3;
err = __max869_read_reg(max869_data, &recvData, 1);
if (err != 0) {
HRM_dbg("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
c3_code = recvData;
recvData = 0xC4;
err = __max869_read_reg(max869_data, &recvData, 1);
if (err != 0) {
HRM_dbg("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
c4_code = recvData;
err = __max869_write_reg(max869_data, 0xFF, 0x00);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86900_MODE_TEST0!\n",
__func__);
return -EIO;
}
*chip_id = (((u64)c1_code) << 24) + (((u64)c2_code) << 16)
+ (((u64)c3_code) << 8) + (c4_code);
HRM_info("%s - Device ID = %lld\n", __func__, *chip_id);
return 0;
}
int max869_get_part_type(u16 *part_type)
{
int err;
u8 buffer[2] = {0, };
buffer[0] = MAX86915_REV_ID_REG;
err = __max869_read_reg(max869_data, buffer, 2);
if (err) {
HRM_dbg("%s Max86915 WHOAMI read fail0\n", __func__);
err = -ENODEV;
goto err_of_read_part_type;
}
if (buffer[1] == MAX86915_PART_ID) {
max869_data->part_type = __max869_get_part_id(max869_data);
} else {
HRM_dbg("%s Max86915 WHOAMI read fail2\n", __func__);
err = -ENODEV;
goto err_of_read_part_type;
}
*part_type = max869_data->part_type;
return err;
err_of_read_part_type:
return -EINVAL;
}
int max869_get_i2c_err_cnt(u32 *err_cnt)
{
int err = 0;
*err_cnt = max869_data->i2c_err_cnt;
return err;
}
int max869_set_i2c_err_cnt(void)
{
int err = 0;
max869_data->i2c_err_cnt = 0;
return err;
}
int max869_get_curr_adc(u16 *ir_curr, u16 *red_curr, u32 *ir_adc, u32 *red_adc)
{
int err = 0;
*ir_curr = max869_data->ir_curr;
*red_curr = max869_data->red_curr;
/*
* *green_curr = max869_data->green_curr;
* *blue_curr = max869_data->blue_curr;
*/
*ir_adc = max869_data->ir_adc;
*red_adc = max869_data->red_adc;
/*
* *green_adc = max869_data->green_adc;
* *blue_adc = max869_data->blue_adc;
*/
return err;
}
int max869_set_curr_adc(void)
{
int err = 0;
max869_data->ir_curr = 0;
max869_data->red_curr = 0;
max869_data->ir_adc = 0;
max869_data->red_adc = 0;
return err;
}
int max869_get_name_chipset(char *name)
{
if (max869_data->part_type >= PART_TYPE_MAX86915_ES)
strlcpy(name, MAX86915_CHIP_NAME, strlen(MAX86915_CHIP_NAME) + 1);
else
strlcpy(name, "NONE", 5);
return 0;
}
int max869_get_name_vendor(char *name)
{
strlcpy(name, VENDOR, strlen(VENDOR) + 1);
if (strncmp(name, VENDOR, strlen(VENDOR) + 1))
return -EINVAL;
else
return 0;
}
int max869_get_threshold(s32 *threshold)
{
*threshold = max869_data->threshold_default;
if (*threshold != max869_data->threshold_default)
return -EINVAL;
else
return 0;
}
int max869_set_threshold(s32 threshold)
{
max869_data->threshold_default = threshold;
if (threshold != max869_data->threshold_default)
return -EINVAL;
else
return 0;
}
int max869_set_eol_enable(u8 enable)
{
int err = 0;
HRM_dbg("%s - CONFIG_SENSORS_HRM_MAX869_NEW_EOL\n", __func__);
__max86915_eol_test_onoff(max869_data, enable);
return err;
}
int max869_set_pre_eol_enable(u8 enable)
{
int err = 0;
HRM_dbg("%s - %d\n", __func__, enable);
max869_data->pre_eol_test_is_enable = enable;
return err;
}
int max869_get_eol_result(char *result)
{
if (max869_data->eol_test_status == 0) {
HRM_info("%s - max869_data->eol_test_status is 0\n",
__func__);
max869_data->eol_test_status = 0;
return snprintf(result, MAX_EOL_RESULT, "%s\n", "NO_EOL_TEST");
}
HRM_info("%s - result = %d\n", __func__, max869_data->eol_test_status);
max869_data->eol_test_status = 0;
snprintf(result, MAX_EOL_RESULT, max869_data->eol_test_result);
return 0;
}
int max869_get_eol_status(u8 *status)
{
*status = max869_data->eol_test_status;
if (*status != max869_data->eol_test_status)
return -EINVAL;
else
return 0;
}
int max869_debug_set(u8 mode)
{
HRM_info("%s - mode = %d\n", __func__, mode);
switch (mode) {
case DEBUG_WRITE_REG_TO_FILE:
__max869_write_reg_to_file();
break;
case DEBUG_WRITE_FILE_TO_REG:
__max869_write_file_to_reg();
__max869_write_default_regs();
break;
case DEBUG_SHOW_DEBUG_INFO:
break;
case DEBUG_ENABLE_AGC:
agc_is_enabled = 1;
break;
case DEBUG_DISABLE_AGC:
agc_is_enabled = 0;
break;
default:
break;
}
return 0;
}
int max869_set_sampling_rate(u32 sampling_period_ns)
{
int err = 0;
u8 recvData;
switch (sampling_period_ns) {
case 10000000:
fifo_full_cnt = 1;
break;
case 5000000:
fifo_full_cnt = 2;
break;
case 2500000:
fifo_full_cnt = 4;
break;
case 1250000:
fifo_full_cnt = 8;
break;
default:
HRM_dbg("%s - %dns is not suppoted.\n", __func__, sampling_period_ns);
return -EIO;
}
if (max869_data->hrm_mode == MODE_SDK_IR) {
err = __max869_write_reg(max869_data, MAX86915_MODE_CONFIGURATION, 0x00);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
if (fifo_full_cnt == 8) {
/* 16uA, 800Hz, 70us */
err = __max869_write_reg(max869_data, MAX86915_MODE_CONFIGURATION_2,
0x50);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
/* 1 Samples avg */
err = __max869_write_reg(max869_data, MAX86915_FIFO_CONFIG,
0x1f);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
} else if (fifo_full_cnt == 4) {
/* 16uA, 400Hz, 120us */
err = __max869_write_reg(max869_data, MAX86915_MODE_CONFIGURATION_2,
0x50);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
/* 2 Samples avg */
err = __max869_write_reg(max869_data, MAX86915_FIFO_CONFIG,
0x3f);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
} else if (fifo_full_cnt == 2) {
/* 16uA, 200Hz, 120us */
err = __max869_write_reg(max869_data, MAX86915_MODE_CONFIGURATION_2,
0x50);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
/* 4 Samples avg */
err = __max869_write_reg(max869_data, MAX86915_FIFO_CONFIG,
0x5f);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
} else if (fifo_full_cnt == 1) {
/* 32uA, 400Hz, 120us */
err = __max869_write_reg(max869_data, MAX86915_MODE_CONFIGURATION_2,
0x6D);
/* 4 Samples avg */
err = __max869_write_reg(max869_data, MAX86915_FIFO_CONFIG,
0x5f);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
}
/* SDK mode only use FLEX MODE 0x03 After changing FIFO CONFIG skip initial sample. */
recvData = MAX86915_FIFO_CONFIG;
err = __max869_read_reg(max869_data, &recvData, 1);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
max869_data->agc_led_set = AGC_LED_SKIP_CNT >>
((recvData & MAX86915_SMP_AVE_MASK) >> MAX86915_SMP_AVE_OFFSET);
err = __max869_write_reg(max869_data, MAX86915_MODE_CONFIGURATION, 0x03);
if (err != 0) {
HRM_dbg("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
max869_data->sample_cnt = 0;
err = sampling_period_ns;
}
return err;
}
int max869_get_fac_cmd(char *cmd_result)
{
if (max869_data->isTrimmed)
return snprintf(cmd_result, MAX_BUF_LEN, "%d", 1);
else
return snprintf(cmd_result, MAX_BUF_LEN, "%d", 0);
}
int max869_get_version(char *version)
{
return snprintf(version, MAX_BUF_LEN, "%s%s", VENDOR_VERISON, VERSION);
}