/*
* Copyright (C) 2017-2018 InvenSense, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, 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.
*/
#define pr_fmt(fmt) "inv_mpu: " fmt
#include "../inv_mpu_iio.h"

/* set LN mode for gyro regardless of conditions */
#define USE_GYRO_LN_MODE

static int inv_calc_engine_dur(struct inv_engine_info *ei)
{
	if (!ei->running_rate)
		return -EINVAL;
	ei->dur = ei->base_time / ei->orig_rate;
	ei->dur *= ei->divider;

	return 0;
}

static int inv_turn_on_fifo(struct inv_mpu_state *st)
{
	u8 int_en, fifo_en, mode, user;
	int r;

	r = inv_plat_single_write(st, REG_FIFO_EN, 0);
	if (r)
		return r;
	r = inv_plat_single_write(st, REG_USER_CTRL, BIT_FIFO_RST);
	if (r)
		return r;
	fifo_en = 0;
	int_en = 0;

	if (st->gesture_only_on && (!st->batch.timeout)) {
		st->gesture_int_count = WOM_DELAY_THRESHOLD;
		int_en |= BIT_WOM_ALL_INT_EN;
	}
#ifdef TIMER_BASED_BATCHING
	if (st->chip_config.eis_enable)
		int_en |= BIT_FSYNC_INT_EN;
	if (!st->batch_timeout) {
		int_en |= BIT_DATA_RDY_EN;
	}
#else
	if (st->batch.timeout) {
		if(!st->batch.fifo_wm_th)
			int_en = BIT_DATA_RDY_EN;
	} else {
		int_en = BIT_DATA_RDY_EN;
		if (st->chip_config.eis_enable)
			int_en |= BIT_FSYNC_INT_EN;
	}
#endif
	if (st->sensor[SENSOR_GYRO].on)
		fifo_en |= BITS_GYRO_FIFO_EN;

	if (st->sensor[SENSOR_ACCEL].on)
		fifo_en |= BIT_ACCEL_FIFO_EN;
	r = inv_plat_single_write(st, REG_FIFO_EN, fifo_en);
	if (r)
		return r;
	st->int_en = int_en;
	r = inv_plat_single_write(st, REG_INT_ENABLE, int_en);
	if (r)
		return r;
	if (st->gesture_only_on && (!st->batch.timeout)) {
		mode = BIT_ACCEL_INTEL_EN | BIT_ACCEL_INTEL_MODE;
	} else {
		mode = 0;
	}
	r = inv_plat_single_write(st, REG_ACCEL_INTEL_CTRL, mode);
#ifdef SENSOR_DATA_FROM_REGISTERS
	user = 0;
#else
	user = BIT_FIFO_EN;
#endif
	r = inv_plat_single_write(st, REG_USER_CTRL, user | st->i2c_dis);
#ifdef TIMER_BASED_BATCHING
	if (fifo_en && st->batch_timeout) {
		if (st->is_batch_timer_running)
			hrtimer_cancel(&st ->hr_batch_timer);
		st->is_batch_timer_running = true;
		hrtimer_start(&st ->hr_batch_timer,
			ns_to_ktime(st->batch_timeout), HRTIMER_MODE_REL);
	} else {
		if (st->is_batch_timer_running)
			hrtimer_cancel(&st ->hr_batch_timer);
		st->is_batch_timer_running = false;
	}
#endif

	return r;
}

/*
 *  inv_reset_fifo() - Reset FIFO related registers.
 */
int inv_reset_fifo(struct inv_mpu_state *st, bool turn_off)
{
	int r, i;
	struct inv_timestamp_algo *ts_algo = &st->ts_algo;
	int dur_ms;

	r = inv_turn_on_fifo(st);
	if (r)
		return r;

	ts_algo->last_run_time = get_time_ns();
	ts_algo->reset_ts = ts_algo->last_run_time;
	if (st->mode_1k_on)
		ts_algo->first_sample = MODE_1K_INIT_SAMPLE;
	else
		ts_algo->first_sample = 1;

	dur_ms = st->smplrt_div + 1;
	if ((ts_algo->first_sample * dur_ms) < FIRST_SAMPLE_BUF_MS)
		ts_algo->first_sample = FIRST_SAMPLE_BUF_MS / dur_ms;
	if (ts_algo->first_sample == 0)
		ts_algo->first_sample = 1;

	st->last_temp_comp_time = ts_algo->last_run_time;
	st->left_over_size = 0;
	for (i = 0; i < SENSOR_NUM_MAX; i++) {
		st->sensor[i].calib_flag = 0;
		st->sensor[i].sample_calib = 0;
		st->sensor[i].time_calib = ts_algo->last_run_time;
	}

	ts_algo->calib_counter = 0;

	return 0;
}

static int inv_turn_on_engine(struct inv_mpu_state *st)
{
	u8 v, w;
	int r;
	unsigned int wait_ms;

	if (st->chip_config.gyro_enable | st->chip_config.accel_enable) {
		w = 0;
		if (!st->chip_config.gyro_enable)
			w |= BIT_PWR_GYRO_STBY;
		if (!st->chip_config.accel_enable)
			w |= BIT_PWR_ACCEL_STBY;
	} else if (st->chip_config.compass_enable) {
		w = BIT_PWR_GYRO_STBY;
	} else {
		w = (BIT_PWR_GYRO_STBY | BIT_PWR_ACCEL_STBY);
	}

	r = inv_plat_read(st, REG_PWR_MGMT_2, 1, &v);
	if (r)
		return r;
	r = inv_plat_single_write(st, REG_PWR_MGMT_2, w);
	if (r)
		return r;

	wait_ms = 0;
	if (st->chip_config.gyro_enable
		&& (v & BIT_PWR_GYRO_STBY)) {
		wait_ms = INV_IAM20680_GYRO_START_TIME;
	}
	if (st->chip_config.accel_enable
		&& (v & BIT_PWR_ACCEL_STBY)) {
		if (INV_IAM20680_ACCEL_START_TIME > wait_ms)
			wait_ms = INV_IAM20680_ACCEL_START_TIME;
	}
	if (wait_ms)
		msleep(wait_ms);

	if (st->chip_config.has_compass) {
		if (st->chip_config.compass_enable)
			r = st->slave_compass->resume(st);
		else
			r = st->slave_compass->suspend(st);
		if (r)
			return r;
	}

	return 0;
}

static int inv_setup_dmp_rate(struct inv_mpu_state *st)
{
	int i;

	for (i = 0; i < SENSOR_NUM_MAX; i++) {
		if (st->sensor[i].on) {
			st->cntl |= st->sensor[i].output;
			st->sensor[i].dur =
				st->eng_info[st->sensor[i].engine_base].dur;
			st->sensor[i].div = 1;
		}
	}

	return 0;
}

/*
 *  inv_set_lpf() - set low pass filer based on fifo rate.
 */
static int inv_set_lpf(struct inv_mpu_state *st, int rate)
{
	const short hz[] = {188, 98, 42, 20, 10, 5};
	const int   d[] = {INV_FILTER_188HZ, INV_FILTER_98HZ,
			INV_FILTER_42HZ, INV_FILTER_20HZ,
			INV_FILTER_10HZ, INV_FILTER_5HZ};
	int i, h, data, result;

#ifdef USE_GYRO_LN_MODE
	if (1) {
#else
	if (st->chip_config.eis_enable || st->ois.en || st->mode_1k_on) {
#endif
		h = (rate >> 1);
		i = 0;
		while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1))
			i++;
		data = d[i];
		data |= EXT_SYNC_SET;
		result = inv_plat_single_write(st, REG_CONFIG, data);
		if (result)
			return result;

		st->chip_config.lpf = data;
		result = inv_plat_single_write(st, REG_LP_MODE_CTRL, 0);
	} else {
		result = inv_plat_single_write(st, REG_LP_MODE_CTRL,
							BIT_GYRO_CYCLE_EN);
		if (result)
			return result;
		data = 0;
		result = inv_plat_single_write(st, REG_CONFIG, data | 3);
	}

	return result;
}

static int inv_set_div(struct inv_mpu_state *st, int a_d, int g_d)
{
	int result, div;

	if (st->chip_config.gyro_enable)
		div = g_d;
	else
		div = a_d;
	if (st->chip_config.eis_enable)
		div = 0;

	st->smplrt_div = div;
	pr_debug("div= %d\n", div);
	result = inv_plat_single_write(st, REG_SAMPLE_RATE_DIV, div);

	return result;
}

// 20680 does not support batching
static int inv_set_batch(struct inv_mpu_state *st)
{
#ifdef TIMER_BASED_BATCHING
	u64 timeout;
	int required_fifo_size;

#ifdef CONFIG_ENABLE_IAM_ACC_GYRO_BUFFERING
	st->batch.timeout = 100;
#endif
	if (st->batch.timeout) {
		required_fifo_size = st->batch.timeout * st->eng_info[ENGINE_GYRO].running_rate
					* st->batch.pk_size / 1000;
		if (required_fifo_size > MAX_BATCH_FIFO_SIZE) {
			required_fifo_size = MAX_BATCH_FIFO_SIZE;
			timeout = (required_fifo_size / st->batch.pk_size) * (1000 / st->eng_info[ENGINE_GYRO].running_rate);
		} else {
			timeout = st->batch.timeout;
		}
	} else {
		timeout = 1000 / st->eng_info[ENGINE_GYRO].running_rate;
	}
	if (timeout <= 1000 / st->eng_info[ENGINE_GYRO].running_rate)
		st->batch_timeout = 0;
	else
		st->batch_timeout = timeout * 1000000; // ms to ns
#endif
	st->batch.fifo_wm_th = 0;

	return 0;
}

static int inv_set_rate(struct inv_mpu_state *st)
{
	int g_d, a_d, result, i;

	result = inv_setup_dmp_rate(st);
	if (result)
		return result;

	g_d = st->eng_info[ENGINE_GYRO].divider - 1;
	a_d = st->eng_info[ENGINE_ACCEL].divider - 1;
	result = inv_set_div(st, a_d, g_d);
	if (result)
		return result;
	result = inv_set_lpf(st, st->eng_info[ENGINE_GYRO].running_rate);
	if (result)
		return result;
	// set ADLPF at this point not to change after accel is enabled
	result = inv_set_accel_config2(st, false);
	st->batch.pk_size = 0;
	for (i = 0; i < SENSOR_NUM_MAX; i++) {
		if (st->sensor[i].on)
			st->batch.pk_size +=  st->sensor[i].sample_size;
	}

	inv_set_batch(st);

	return result;
}

static int inv_determine_engine(struct inv_mpu_state *st)
{
	int i;
	bool a_en, g_en;
	int accel_rate, gyro_rate;

	a_en = false;
	g_en = false;
	gyro_rate = MPU_INIT_SENSOR_RATE;
	accel_rate = MPU_INIT_SENSOR_RATE;
	/* loop the streaming sensors to see which engine needs to be turned on
		*/
	for (i = 0; i < SENSOR_NUM_MAX; i++) {
		if (st->sensor[i].on) {
			a_en |= st->sensor[i].a_en;
			g_en |= st->sensor[i].g_en;
		}
	}

	if (st->chip_config.eis_enable) {
		g_en = true;
		st->eis.frame_count = 0;
		st->eis.fsync_delay = 0;
		st->eis.gyro_counter = 0;
		st->eis.voting_count = 0;
		st->eis.voting_count_sub = 0;
		gyro_rate = BASE_SAMPLE_RATE;
	} else {
		st->eis.eis_triggered = false;
		st->eis.prev_state = false;
	}

	accel_rate = st->sensor[SENSOR_ACCEL].rate;
	gyro_rate  = max(gyro_rate, st->sensor[SENSOR_GYRO].rate);

	st->ts_algo.clock_base = ENGINE_ACCEL;

	if (g_en) {
		/* gyro engine needs to be fastest */
		if (a_en)
			gyro_rate = max(gyro_rate, accel_rate);
		accel_rate = gyro_rate;
		st->ts_algo.clock_base = ENGINE_GYRO;
	} else if (a_en) {
		/* accel engine needs to be fastest if gyro engine is off */
		gyro_rate = accel_rate;
		st->ts_algo.clock_base = ENGINE_ACCEL;
	}

	st->eng_info[ENGINE_GYRO].running_rate = gyro_rate;
	st->eng_info[ENGINE_ACCEL].running_rate = accel_rate;
	if ((gyro_rate >= BASE_SAMPLE_RATE) ||
					(accel_rate >= BASE_SAMPLE_RATE))
		st->mode_1k_on = true;
	else
		st->mode_1k_on = false;
	/* engine divider for pressure and compass is set later */
	if (st->chip_config.eis_enable || st->mode_1k_on) {
		st->eng_info[ENGINE_GYRO].divider = 1;
		st->eng_info[ENGINE_ACCEL].divider = 1;
		// need to update rate and div for 1khz mode
		for ( i = 0 ; i < SENSOR_L_NUM_MAX ; i++ ) {
			if (st->sensor_l[i].on) {
				st->sensor_l[i].counter = 0;
				if (st->sensor_l[i].rate)
					st->sensor_l[i].div =
						BASE_SAMPLE_RATE
						/ st->sensor_l[i].rate;
				else
					st->sensor_l[i].div = 0xffff;
			}
		}
	} else {
		st->eng_info[ENGINE_GYRO].divider = BASE_SAMPLE_RATE /
			st->eng_info[ENGINE_GYRO].running_rate;
		st->eng_info[ENGINE_ACCEL].divider = BASE_SAMPLE_RATE /
			st->eng_info[ENGINE_ACCEL].running_rate;
	}

	for ( i = 0 ; i < SENSOR_L_NUM_MAX ; i++ )
		st->sensor_l[i].counter = 0;

	inv_calc_engine_dur(&st->eng_info[ENGINE_GYRO]);
	inv_calc_engine_dur(&st->eng_info[ENGINE_ACCEL]);

	pr_debug("gen: %d aen: %d grate: %d arate: %d\n",
				g_en, a_en, gyro_rate, accel_rate);

	st->chip_config.gyro_enable = g_en;
	st->chip_config.accel_enable = a_en;

	return 0;
}

/*
 *  set_inv_enable() - enable function.
 */
int set_inv_enable(struct iio_dev *indio_dev)
{
	int result;
	struct inv_mpu_state *st = iio_priv(indio_dev);

	result = inv_switch_power_in_lp(st, true);
	if (result)
		return result;
	inv_stop_interrupt(st);
	inv_determine_engine(st);
	result = inv_set_rate(st);
	if (result) {
		pr_err("inv_set_rate error\n");
		return result;
	}
	result = inv_turn_on_engine(st);
	if (result) {
		pr_err("inv_turn_on_engine error\n");
		return result;
	}
	result = inv_reset_fifo(st, false);
	if (result)
		return result;
	result = inv_switch_power_in_lp(st, false);
	if ((!st->chip_config.gyro_enable) &&
		(!st->chip_config.accel_enable)) {
		inv_set_power(st, false);
		return 0;
	}

	return result;
}
/* dummy function for 20608D */
int inv_enable_pedometer_interrupt(struct inv_mpu_state *st, bool en)
{
	return 0;
}
int inv_dmp_read(struct inv_mpu_state *st, int off, int size, u8 *buf)
{
	return 0;
}
int inv_firmware_load(struct inv_mpu_state *st)
{
	return 0;
}
