/*
 *
 * FocalTech TouchScreen driver.
 *
 * Copyright (c) 2010-2017, FocalTech Systems, Ltd., all rights reserved.
 *
 * 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.
 *
 */

/*****************************************************************************
*
* File Name: focaltech_esdcheck.c
*
*    Author: Focaltech Driver Team
*
*   Created: 2016-08-03
*
*  Abstract: Sensor
*
*   Version: v1.0
*
* Revision History:
*        v1.0:
*            First release. By luougojin 2016-08-03
*****************************************************************************/

/*****************************************************************************
* Included header files
*****************************************************************************/
#include "focaltech_core.h"

#if FTS_PSENSOR_EN
/*****************************************************************************
* Private constant and macro definitions using #define
*****************************************************************************/
/* psensor register address*/
#define FTS_REG_PSENSOR_ENABLE                  0xB0
#define FTS_REG_PSENSOR_STATUS                  0x01

/* psensor register bits*/
#define FTS_PSENSOR_ENABLE_MASK                 0x01
#define FTS_PSENSOR_STATUS_NEAR                 0xC0
#define FTS_PSENSOR_STATUS_FAR                  0xE0
#define FTS_PSENSOR_FAR_TO_NEAR                 0
#define FTS_PSENSOR_NEAR_TO_FAR                 1
#define FTS_PSENSOR_ORIGINAL_STATE_FAR          1
#define FTS_PSENSOR_WAKEUP_TIMEOUT              500

/*****************************************************************************
* Static variables
*****************************************************************************/
static struct sensors_classdev __maybe_unused sensors_proximity_cdev = {

	.name = "fts-proximity",
	.vendor = "FocalTech",
	.version = 1,
	.handle = SENSORS_PROXIMITY_HANDLE,
	.type = SENSOR_TYPE_PROXIMITY,
	.max_range = "5.0",
	.resolution = "5.0",
	.sensor_power = "0.1",
	.min_delay = 0,
	.fifo_reserved_event_count = 0,
	.fifo_max_event_count = 0,
	.enabled = 0,
	.delay_msec = 200,
	.sensors_enable = NULL,
	.sensors_poll_delay = NULL,
};

/*****************************************************************************
* functions body
*****************************************************************************/
/*****************************************************************************
*  Name: fts_psensor_support_enabled
*  Brief:
*  Input:
*  Output:
*  Return:
*****************************************************************************/
static inline bool fts_psensor_support_enabled(void)
{
	/*return config_enabled(CONFIG_TOUCHSCREEN_FTS_PSENSOR);*/
	return FTS_PSENSOR_EN;
}

/*****************************************************************************
*  Name: fts_psensor_enable
*  Brief:
*  Input:
*  Output:
*  Return:
*****************************************************************************/
static void fts_psensor_enable(struct fts_ts_data *data, int enable)
{
	u8 state;
	int ret = -1;

	if (data->client == NULL)
		return;

	fts_i2c_read_reg(data->client, FTS_REG_PSENSOR_ENABLE, &state);
	if (enable)
		state |= FTS_PSENSOR_ENABLE_MASK;
	else
		state &= ~FTS_PSENSOR_ENABLE_MASK;

	ret = fts_i2c_write_reg(data->client, FTS_REG_PSENSOR_ENABLE, state);
	if (ret < 0)
		FTS_ERROR("write psensor switch command failed");

}

/*****************************************************************************
*  Name: fts_psensor_enable_set
*  Brief:
*  Input:
*  Output:
*  Return:
*****************************************************************************/
static int fts_psensor_enable_set(struct sensors_classdev *sensors_cdev,
			unsigned int enable)
{
	struct fts_psensor_platform_data *psensor_pdata =
			container_of(sensors_cdev,
			struct fts_psensor_platform_data, ps_cdev);
	struct fts_ts_data *data = psensor_pdata->data;
	struct input_dev *input_dev = data->psensor_pdata->input_psensor_dev;

	mutex_lock(&input_dev->mutex);
	fts_psensor_enable(data, enable);
	psensor_pdata->tp_psensor_data = FTS_PSENSOR_ORIGINAL_STATE_FAR;
	if (enable)
		psensor_pdata->tp_psensor_opened = 1;
	else
		psensor_pdata->tp_psensor_opened = 0;

	mutex_unlock(&input_dev->mutex);

	return enable;
}

/*****************************************************************************
*  Name: fts_read_tp_psensor_data
*  Brief:
*  Input:
*  Output:
*  Return:
*****************************************************************************/
static int fts_read_tp_psensor_data(struct fts_ts_data *data)
{
	u8 psensor_status;
	char tmp;
	int ret = 1;

	fts_i2c_read_reg(data->client,
			FTS_REG_PSENSOR_STATUS, &psensor_status);

	tmp = data->psensor_pdata->tp_psensor_data;
	if (psensor_status == FTS_PSENSOR_STATUS_NEAR)
		data->psensor_pdata->tp_psensor_data =
			FTS_PSENSOR_FAR_TO_NEAR;
	else if (psensor_status == FTS_PSENSOR_STATUS_FAR)
		data->psensor_pdata->tp_psensor_data =
			FTS_PSENSOR_NEAR_TO_FAR;

	if (tmp != data->psensor_pdata->tp_psensor_data) {
		FTS_ERROR("%s sensor data changed", __func__);
		ret = 0;
	}

	return ret;
}


int fts_sensor_read_data(struct fts_ts_data *data)
{
	int ret = 0;

	if (fts_psensor_support_enabled()
			&& data->psensor_pdata->tp_psensor_opened) {
		ret = fts_read_tp_psensor_data(data);
		if (!ret) {
			if (data->suspended)
				pm_wakeup_event(&data->client->dev,
						FTS_PSENSOR_WAKEUP_TIMEOUT);

			input_report_abs(data->psensor_pdata->input_psensor_dev,
					ABS_DISTANCE,
					data->psensor_pdata->tp_psensor_data);
			input_sync(data->psensor_pdata->input_psensor_dev);
		}

		return 1;
	}

	return 0;
}

int fts_sensor_suspend(struct fts_ts_data *data)
{
	int ret = 0;

	if (fts_psensor_support_enabled()  &&
		 device_may_wakeup(&data->client->dev) &&
		 data->psensor_pdata->tp_psensor_opened) {
		ret = enable_irq_wake(data->client->irq);
		if (ret != 0)
			FTS_ERROR("%s: set_irq_wake failed", __func__);

		data->suspended = true;
		return 1;
	}

	return 0;
}


int fts_sensor_resume(struct fts_ts_data *data)
{
	int ret = 0;

	if (fts_psensor_support_enabled()
			&& device_may_wakeup(&data->client->dev)
			&& data->psensor_pdata->tp_psensor_opened) {
		ret = disable_irq_wake(data->client->irq);
		if (ret)
			FTS_ERROR("%s: disable_irq_wake failed",  __func__);

		data->suspended = false;
		return 1;
	}

	return 0;
}


int fts_sensor_init(struct fts_ts_data *data)
{
	struct fts_psensor_platform_data *psensor_pdata;
	struct input_dev *psensor_input_dev;
	int err;

	if (fts_psensor_support_enabled()) {
		device_init_wakeup(&data->client->dev, 1);
		psensor_pdata = devm_kzalloc(&data->client->dev,
				sizeof(struct fts_psensor_platform_data),
				GFP_KERNEL);
		if (!psensor_pdata) {
			FTS_ERROR("Failed to allocate memory");
			goto irq_free;
		}

		data->psensor_pdata = psensor_pdata;

		psensor_input_dev = input_allocate_device();
		if (!psensor_input_dev) {
			FTS_ERROR("Failed to allocate device");
			goto free_psensor_pdata;
		}

		__set_bit(EV_ABS, psensor_input_dev->evbit);
		input_set_abs_params(psensor_input_dev, ABS_DISTANCE,
				0, 1, 0, 0);
		psensor_input_dev->name = "proximity";
		psensor_input_dev->id.bustype = BUS_I2C;
		psensor_input_dev->dev.parent = &data->client->dev;
		data->psensor_pdata->input_psensor_dev = psensor_input_dev;

		err = input_register_device(psensor_input_dev);
		if (err) {
			FTS_ERROR("Unable to register device, err=%d", err);
			goto free_psensor_input_dev;
		}

		psensor_pdata->ps_cdev = sensors_proximity_cdev;
		psensor_pdata->ps_cdev.sensors_enable = fts_psensor_enable_set;
		psensor_pdata->data = data;

		err = sensors_classdev_register(&data->client->dev,
					&psensor_pdata->ps_cdev);
		if (err)
			goto unregister_psensor_input_device;
	}

	return 0;

unregister_psensor_input_device:
	if (fts_psensor_support_enabled())
		input_unregister_device(data->psensor_pdata->input_psensor_dev);
free_psensor_input_dev:
	if (fts_psensor_support_enabled())
		input_free_device(data->psensor_pdata->input_psensor_dev);
free_psensor_pdata:
	if (fts_psensor_support_enabled()) {
		devm_kfree(&data->client->dev, psensor_pdata);
		data->psensor_pdata = NULL;
	}
irq_free:
	if (fts_psensor_support_enabled())
		device_init_wakeup(&data->client->dev, 0);
	free_irq(data->client->irq, data);

	return 1;
}

int fts_sensor_remove(struct fts_ts_data *data)
{
	if (fts_psensor_support_enabled()) {
		device_init_wakeup(&data->client->dev, 0);
		sensors_classdev_unregister(&data->psensor_pdata->ps_cdev);
		input_unregister_device(data->psensor_pdata->input_psensor_dev);
		devm_kfree(&data->client->dev, data->psensor_pdata);
		data->psensor_pdata = NULL;
	}
	return 0;
}
#endif  /* FTS_PSENSOR_EN */
