/* /sys/class/factory/tsp/fw_ver */
ssize_t factory_tsp_fw_ver_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	int ret;

	ist30xx_disable_irq(ts_data);

	ret = ist30xx_cmd_run_device(ts_data->client);
	if (ret)
		return sprintf(buf, "NULL");

	ret = ist30xx_read_cmd(ts_data->client, CMD_GET_FW_VER, &ts_data->fw.ver);
	if (ret)
		return sprintf(buf, "NULL");

	ret = ist30xx_read_cmd(ts_data->client, CMD_GET_PARAM_VER, &ts_data->param_ver);
	if (ret)
		return sprintf(buf, "NULL");

	ist30xx_enable_irq(ts_data);

	tsp_info("tsp version: %d.%d(%d.%d.%d)\n",
	       (ts_data->param_ver >> 8) & 0xFF, ts_data->param_ver & 0xFF,
	       (ts_data->fw.ver >> 16) & 0xFF, (ts_data->fw.ver >> 8) & 0xFF,
	       ts_data->fw.ver & 0xFF);

	return sprintf(buf, "%d.%d(%d.%d.%d)\n",
		       (ts_data->param_ver >> 8) & 0xFF, ts_data->param_ver & 0xFF,
		       (ts_data->fw.ver >> 16) & 0xFF, (ts_data->fw.ver >> 8) & 0xFF,
		       ts_data->fw.ver & 0xFF);
}
/* /sys/class/factory/tsp/panel_vendor */
ssize_t factory_tsp_panel_vendor_show(struct device *dev,
				      struct device_attribute *attr, char *buf)
{
	int ret;
	u32 panel_vendor;

	ist30xx_disable_irq(ts_data);
	ret = ist30xx_cmd_run_device(ts_data->client);
	if (ret)
		return sprintf(buf, "NULL");

	ret = ist30xx_read_cmd(ts_data->client, CMD_GET_TSP_VENDOR, &panel_vendor);
	if (ret)
		return sprintf(buf, "NULL");
	/*
	 * TSP panel_vendor
	 * - ALPS     1
	 * - SUNTEL   2
	 * - TOVIS    3
	 * - ILJIN    4
	 * - SYNOPEX  5
	 * - TAEYANG  6
	 */

	ist30xx_enable_irq(ts_data);

	tsp_info("tsp panel vendor: %d\n", panel_vendor);

	return sprintf(buf, "%d\n", panel_vendor);
}
/* /sys/class/factory/tsp/chip_name */
ssize_t factory_tsp_chip_name_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	int ret;

	ist30xx_disable_irq(ts_data);

	ret = ist30xx_cmd_run_device(ts_data->client);
	if (ret)
		return sprintf(buf, "NULL");

	ret = ist30xx_read_cmd(ts_data->client, CMD_GET_CHIP_ID, &ts_data->chip_id);
	if (ret)
		return sprintf(buf, "NULL");

	ist30xx_enable_irq(ts_data);

	if ((ts_data->chip_id != IST30XX_CHIP_ID) &&
	    (ts_data->chip_id != IST30XXA_CHIP_ID))
		return sprintf(buf, "NULL");

	tsp_info("tsp chip name: %s\n", IST30XX_CHIP_NAME);

	return sprintf(buf, "%s\n", IST30XX_CHIP_NAME);
}
ssize_t factory_tkey_zvalue_store(struct device *dev,
				  struct device_attribute *attr, const char *buf, size_t size)
{
	int ret;
	int cmd = 0;
	u32 val = 0;

	sscanf(buf, "%d", &cmd);

	DMSG("[TSP] tkey zvalue cmd(%d)\n", cmd);

	val = (cmd > 0 ? 0x00C80001 : 0);

	if (cmd) {
		ist30xx_disable_irq(ts_data);
		ist30xx_cmd_run_device(ts_data->client);
		ist30xx_enable_irq(ts_data);
	}

	ret = ist30xx_write_cmd(ts_data->client, CMD_ZVALUE_MODE, val);
	if (ret) {
		pr_err("[ TSP ] fail, zvalue mode enter(%d)\n", ret);
		return size;
	}

	get_zvalue_mode = (cmd > 0 ? true : false);

	DMSG("[TSP] tkey zvalue val: %x, cmd: %d\n", val, cmd);

	return size;
}
static void fw_update_func(struct work_struct *work)
{
	if ((ts_data == NULL) || (ts_data->client == NULL))
		return;

	tsp_info("FW update function\n");

	if (ist30xx_auto_bin_update(ts_data))
		ist30xx_disable_irq(ts_data);
}
int ist30xx_get_info(struct ist30xx_data *data)
{
	int ret;
	u32 calib_msg;
	int retry = 0;

	ist30xx_tsp_info.finger_num = IST30XX_MAX_MT_FINGERS;
	mutex_lock(&ist30xx_mutex);
	ist30xx_disable_irq(data);

RETRY :
	ret = ist30xx_write_cmd(data->client, CMD_RUN_DEVICE, 0);

	msleep(50);

	ret = ist30xx_get_ver_info(data);
	if(ret != 0) {
		if(retry++ < 10) {
			tsp_debug("ist30xx_get_info retry : %d \n", retry);
			ist30xx_ts_reset();
			goto RETRY;
		}
	}

	ret = ist30xx_tsp_update_info();
	ret = ist30xx_tkey_update_info();

	ist30xx_print_info();

	ret = ist30xx_read_cmd(ts_data->client, CMD_GET_CALIB_RESULT, &calib_msg);
	if (ret == 0) {
		tsp_info("calib status: 0x%08x\n", calib_msg);
		if ((calib_msg & CALIB_MSG_MASK) != CALIB_MSG_VALID ||
		    CALIB_TO_STATUS(calib_msg) > 0) {
			ist30xx_calibrate(IST30XX_FW_UPDATE_RETRY);

			ist30xx_cmd_run_device(data->client);
		}
	}

	ist30xx_start(ts_data);

#if IST30XX_EVENT_MODE
	ktime_get_ts(&t_event);
#endif

	data->status.calib = 0;

	ist30xx_enable_irq(data);

	mutex_unlock(&ist30xx_mutex);

	return ret;
}
static void ist30xx_early_suspend(struct early_suspend *h)
{
	struct ist30xx_data *data = container_of(h, struct ist30xx_data,
						 early_suspend);

	mutex_lock(&ist30xx_mutex);
	ist30xx_disable_irq(data);
	ist30xx_internal_suspend(data);
	clear_input_data(data);
	mutex_unlock(&ist30xx_mutex);
}
static int ist30xx_suspend(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct ist30xx_data *data = i2c_get_clientdata(client);

	tsp_debug("ist30xx_suspend \n");

	mutex_lock(&ist30xx_mutex);
	ist30xx_disable_irq(data);
	ist30xx_internal_suspend(data);
	clear_input_data(data);
	mutex_unlock(&ist30xx_mutex);

	return 0;
}
static void reset_work_func(struct work_struct *work)
{
	if ((ts_data == NULL) || (ts_data->client == NULL))
		return;

	tsp_info("Request reset function\n");

	if ((ts_data->status.power == 1) &&
	    (ts_data->status.update != 1) && (ts_data->status.calib != 1)) {
		mutex_lock(&ist30xx_mutex);
		ist30xx_disable_irq(ts_data);

		clear_input_data(ts_data);

		ist30xx_cmd_run_device(ts_data->client);

		ist30xx_start(ts_data);

		ist30xx_enable_irq(ts_data);
		mutex_unlock(&ist30xx_mutex);
	}
}
int ist30xx_init_touch_driver(struct ist30xx_data *data)
{
	int ret = 0;

	mutex_lock(&ist30xx_mutex);
	ist30xx_disable_irq(data);

	ret = ist30xx_cmd_run_device(data->client);
	if (ret)
		goto init_touch_end;

	ret = ist30xx_get_ver_info(data);
	if (ret)
		goto init_touch_end;

init_touch_end:
	ist30xx_start(data);

	ist30xx_enable_irq(data);
	mutex_unlock(&ist30xx_mutex);

	return ret;
}
int ist30xx_cmcs_test(const u8 *buf, int size)
{
	int ret;
	int len;
	u32 chksum = 0;
	u32 *buf32;
	struct i2c_client *client = (struct i2c_client *)ts_data->client;
	CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;

	tsp_info("*** CM/CS test ***\n");
	tsp_info(" mode: 0x%x, baseline(screen: %d, key: %d)\n",
		 cmcs->cmd.mode, cmcs->cmd.base_screen, cmcs->cmd.base_key);
	tsp_info(" start_cp (cm: %d, cs: %d), vcmp (cm: %d, cs: %d)\n",
		 cmcs->cmd.start_cp_cm, cmcs->cmd.start_cp_cs,
		 cmcs->cmd.vcmp_cm, cmcs->cmd.vcmp_cs);

	ist30xx_disable_irq(ts_data);

	ist30xx_reset(false);

	ret = ist30xx_cmd_reg(client, CMD_ENTER_REG_ACCESS);
	cmcs_next_step(ret);

	/* Set sensor register */
	buf32 = ts_cmcs->buf_sensor;
	ret = ist30xx_set_cmcs_sensor(client, cmcs, buf32);
	cmcs_next_step(ret);

	/* Set command */
	ret = ist30xx_set_cmcs_cmd(client, cmcs);
	cmcs_next_step(ret);

	ret = ist30xx_cmd_reg(client, CMD_EXIT_REG_ACCESS);
	cmcs_next_step(ret);

	/* Load cmcs test code */
	ret = ist30xx_write_cmd(client, CMD_EXEC_MEM_CODE, 0);
	cmcs_next_step(ret);

	buf32 = (u32 *)ts_cmcs->buf_cmcs;
	len = cmcs->cmd.cmcs_size / IST30XX_DATA_LEN;
	tsp_verb("%08x %08x %08x %08x\n", buf32[0], buf32[1], buf32[2], buf32[3]);
	ret = ist30xx_write_buf(client, len, buf32, len);
	cmcs_next_step(ret);

	/* Check checksum */
	ret = ist30xx_read_cmd(client, CMD_DEFAULT, &chksum);
	cmcs_next_step(ret);
	if (chksum != IST30XX_CMCS_LOAD_END)
		goto end;
	tsp_info("CM/CS code ready!!\n");

	/* Check checksum */
	ret = ist30xx_read_cmd(client, CMD_DEFAULT, &chksum);
	cmcs_next_step(ret);
	tsp_info("CM/CS code chksum: %08x, %08x\n", chksum, cmcs->cmcs_chksum);

	ist30xx_enable_irq(ts_data);
	/* Wait CMCS test result */
	if (ist30xx_calib_wait() == 1)
		tsp_info("CM/CS test OK.\n");
	else
		tsp_info("CM/CS test fail.\n");
	ist30xx_disable_irq(ts_data);

	/* Read CM/CS data*/
	if (ENABLE_CM_MODE(cmcs->cmd.mode)) {
		/* Read CM data */
		memset(ts_cmcs_buf->cm, 0, sizeof(ts_cmcs_buf->cm));

		ret = ist30xx_get_cmcs_buf(client, cmcs, ts_cmcs_buf->cm);
		cmcs_next_step(ret);

		ret = ist30xx_apply_cmcs_slope(cmcs, ts_cmcs_buf);
	}

	if (ENABLE_CS_MODE(cmcs->cmd.mode)) {
		/* Read CS0 data */
		memset(ts_cmcs_buf->cs0, 0, sizeof(ts_cmcs_buf->cs0));
		memset(ts_cmcs_buf->cs1, 0, sizeof(ts_cmcs_buf->cs1));

		ret = ist30xx_get_cmcs_buf(client, cmcs, ts_cmcs_buf->cs0);
		cmcs_next_step(ret);

		/* Read CS1 data */
		ret = ist30xx_get_cmcs_buf(client, cmcs, ts_cmcs_buf->cs1);
		cmcs_next_step(ret);
	}

	ist30xx_reset(false);

	ist30xx_start(ts_data);

	cmcs_ready = CMCS_READY;

end:
	if (unlikely(ret)) {
		tsp_warn("CM/CS test Fail!, ret=%d\n", ret);
	} else if (unlikely(chksum != cmcs->cmcs_chksum)) {
		tsp_warn("Error CheckSum: %x(%x)\n", chksum, cmcs->cmcs_chksum);
		ret = -ENOEXEC;
	}

	ist30xx_enable_irq(ts_data);

	return ret;
}
int ist30xx_get_info(struct ist30xx_data *data)
{
	int ret;
	u32 calib_msg;

	ist30xx_tsp_info.finger_num = IST30XX_MAX_MT_FINGERS;
	mutex_lock(&ist30xx_mutex);
	ist30xx_disable_irq(data);

#if !(IST30XX_INTERNAL_BIN)
	ret = ist30xx_write_cmd(data->client, CMD_RUN_DEVICE, 0);
	if (ret)
		goto get_info_end;
	msleep(10);
	ret = ist30xx_get_ver_info(data);
	if (ret) goto get_info_end;
#endif  // !(IST30XX_INTERNAL_BIN)

#if IST30XX_DEBUG
# if IST30XX_INTERNAL_BIN
	ist30xx_get_tsp_info();
	ist30xx_get_tkey_info();
# else
	ret = ist30xx_tsp_update_info();
	if (ret) goto get_info_end;

	ret = ist30xx_tkey_update_info();
	if (ret) goto get_info_end;
# endif

	ist30xx_print_info();
#endif  // IST30XX_DEBUG

	ret = ist30xx_read_cmd(ts_data->client, CMD_GET_CALIB_RESULT, &calib_msg);
	if (ret == 0) {
		tsp_info("calib status: 0x%08x\n", calib_msg);
		if ((calib_msg & CALIB_MSG_MASK) != CALIB_MSG_VALID ||
		    CALIB_TO_STATUS(calib_msg) > 0) {
			ist30xx_calibrate(IST30XX_FW_UPDATE_RETRY);

			ist30xx_cmd_run_device(data->client);
		}
	}

	ist30xx_start(ts_data);

#if IST30XX_EVENT_MODE
	ktime_get_ts(&t_event);
#endif

	data->status.calib = 0;

#if !(IST30XX_INTERNAL_BIN)
get_info_end:
#endif
	if (ret == 0)
		ist30xx_enable_irq(data);
	else
		printk("[ TSP ] ist30xx_get_info return value : %d\n", ret);
	mutex_unlock(&ist30xx_mutex);

	return ret;
}
static int __devinit ist30xx_probe(struct i2c_client *		client,
				   const struct i2c_device_id * id)
{
	int ret;
	struct ist30xx_data *data;
	struct input_dev *input_dev;

#if 0
	/* [email protected] */
	struct touch_platform_data *ts_pdata;
//	struct ist30xx_ts_device *dev;

	ts_pdata = client->dev.platform_data;
//	dev = &ist30xx_ts_dev;
	/* [email protected] */
#endif

	tsp_info("\n%s(), the i2c addr=0x%x \n", __func__, client->addr);

/*	dev->power = ts_pdata->power;
	dev->num_irq = ts_pdata->irq;
	dev->sda_gpio = ts_pdata->sda;
	dev->scl_gpio  = ts_pdata->scl;*/

	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
		tsp_debug("failed to i2c functionality check");
		ret = -ENODEV;
		goto err_check_functionality_failed;
	}
	
	data = kzalloc(sizeof(*data), GFP_KERNEL);
	if (!data)
		return -ENOMEM;

	data->client = client;

	if(client->dev.of_node) {
		data->pdata = devm_kzalloc(&client->dev, sizeof(struct ist30xx_tsi_platform_data), GFP_KERNEL);
		if(!data->pdata) {
			tsp_debug("failed to allocate platform_data");
			return -ENOMEM;
		}

		ret = ist30xx_parse_dt(&client->dev, data->pdata);
		if(ret) {
			tsp_debug("device tree parsing failed");
			return ret;
		}
	} else {
		data->pdata = client->dev.platform_data;
	}

	ret = ist30xx_regulator_configure(data, true);
	if (ret < 0) {
			tsp_debug("Failed to configure regulators");
	}

	ret = ist30xx_ldo_power_on(data, true);
	if (ret < 0) {
			tsp_debug("Failed to power on");
	}

	input_dev = input_allocate_device();
	if (!input_dev) {
		ret = -ENOMEM;
		tsp_err("%s(), input_allocate_device failed (%d)\n", __func__, ret);
		goto err_alloc_dev;
	}

#if 0
	DMSG("[ TSP ] irq : %d, scl : %d, sda : %d\n", client->irq, ts_pdata->scl, ts_pdata->sda);
#endif
	data->num_fingers = IST30XX_MAX_MT_FINGERS;
	data->num_keys = IST30XX_MAX_MT_FINGERS;
	data->irq_enabled = 1;
	data->client = client;
	data->input_dev = input_dev;
#if 0
	/* [email protected] */
	data->power = ts_pdata->power;
	/* [email protected] */
#endif
	i2c_set_clientdata(client, data);

#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 0, 0))
	input_mt_init_slots(input_dev, IST30XX_MAX_MT_FINGERS);
#endif

	input_dev->name = "ist30xx_ts_input";
	input_dev->id.bustype = BUS_I2C;
	input_dev->dev.parent = &client->dev;

	set_bit(EV_ABS, input_dev->evbit);
#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 0, 0))
	set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
#endif

	input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, IST30XX_MAX_X, 0, 0);
	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, IST30XX_MAX_Y, 0, 0);
#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 0, 0))
	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, IST30XX_MAX_W, 0, 0);
#else
	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, IST30XX_MAX_Z, 0, 0);
	input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, IST30XX_MAX_W, 0, 0);
#endif

#if IST30XX_USE_KEY
	{
		int i;
		set_bit(EV_KEY, input_dev->evbit);
		set_bit(EV_SYN, input_dev->evbit);
		for (i = 1; i < ARRAY_SIZE(ist30xx_key_code); i++)
			set_bit(ist30xx_key_code[i], input_dev->keybit);
	}
#endif

	input_set_drvdata(input_dev, data);
	ret = input_register_device(input_dev);
	if (ret) {
		input_free_device(input_dev);
		goto err_reg_dev;
	}

#if defined(CONFIG_FB)
	data->fb_notif.notifier_call = fb_notifier_callback;
	ret = fb_register_client(&data->fb_notif);
	if(ret)
		tsp_debug("Unable to register fb_notifier \n");
	else
		tsp_debug("Register fb_notifier \n");
#elif defined(CONFIG_HAS_EARLYSUSPEND)
	data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
	data->early_suspend.suspend = ist30xx_early_suspend;
	data->early_suspend.resume = ist30xx_late_resume;
	register_early_suspend(&data->early_suspend);
#endif

	ts_data = data;

	ret = ist30xx_init_system();
	if (ret) {
		dev_err(&client->dev, "chip initialization failed\n");
		goto err_init_drv;
	}

	ret = ist30xx_init_update_sysfs();
	if (ret)
		goto err_init_drv;

#if IST30XX_DEBUG
	ret = ist30xx_init_misc_sysfs();
	if (ret)
		goto err_init_drv;
#endif

# if IST30XX_FACTORY_TEST
	ret = ist30xx_init_factory_sysfs();
	if (ret)
		goto err_init_drv;
#endif

#if IST30XX_TRACKING_MODE
	ret = ist30xx_init_tracking_sysfs();
	if (ret)
		goto err_init_drv;
#endif

	ret = request_threaded_irq(client->irq, NULL, ist30xx_irq_thread,
				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "ist30xx_ts", data);
	if (ret)
		goto err_irq;

	ist30xx_disable_irq(data);

#if IST30XX_INTERNAL_BIN
# if IST30XX_UPDATE_BY_WORKQUEUE
	INIT_DELAYED_WORK(&work_fw_update, fw_update_func);
	schedule_delayed_work(&work_fw_update, IST30XX_UPDATE_DELAY);
# else
	ret = ist30xx_auto_bin_update(data);
	if (ret < 0)
		goto err_irq;
# endif
#endif  // IST30XX_INTERNAL_BIN

	ret = ist30xx_get_info(data);
	tsp_info("Get info: %s\n", (ret == 0 ? "success" : "fail"));

	INIT_DELAYED_WORK(&work_reset_check, reset_work_func);

#if IRQ_THREAD_WORK_QUEUE
	INIT_WORK(&work_irq_thread, irq_thread_func);
#endif

#if IST30XX_DETECT_TA
	ist30xx_ta_status = 0;
#endif

#if IST30XX_EVENT_MODE
	init_timer(&idle_timer);
	idle_timer.function = timer_handler;
	idle_timer.expires = jiffies_64 + (EVENT_TIMER_INTERVAL);

	mod_timer(&idle_timer, get_jiffies_64() + EVENT_TIMER_INTERVAL);

	ktime_get_ts(&t_event);
#endif

	ist30xx_initialized = 1;

	return 0;

err_irq:
	ist30xx_disable_irq(data);
	free_irq(client->irq, data);
err_init_drv:
#if IST30XX_EVENT_MODE
	get_event_mode = false;
#endif
	tsp_err("Error, ist30xx init driver\n");
//	ist30xx_power_off();
	ist30xx_ts_off();
	input_unregister_device(input_dev);
	return 0;

err_reg_dev:
err_alloc_dev:
	tsp_err("Error, ist30xx mem free\n");
	kfree(data);
err_check_functionality_failed:
	return 0;
}