18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Touchscreen driver for the tps6507x chip.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com)
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Credits:
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *    Using code from tsc2007, MtekVision Co., Ltd.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * For licencing details see kernel-base/COPYING
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * TPS65070, TPS65073, TPS650731, and TPS650732 support
138c2ecf20Sopenharmony_ci * 10 bit touch screen interface.
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci#include <linux/input.h>
208c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
218c2ecf20Sopenharmony_ci#include <linux/mfd/tps6507x.h>
228c2ecf20Sopenharmony_ci#include <linux/input/tps6507x-ts.h>
238c2ecf20Sopenharmony_ci#include <linux/delay.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define TSC_DEFAULT_POLL_PERIOD 30 /* ms */
268c2ecf20Sopenharmony_ci#define TPS_DEFAULT_MIN_PRESSURE 0x30
278c2ecf20Sopenharmony_ci#define MAX_10BIT ((1 << 10) - 1)
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define	TPS6507X_ADCONFIG_CONVERT_TS (TPS6507X_ADCONFIG_AD_ENABLE | \
308c2ecf20Sopenharmony_ci					 TPS6507X_ADCONFIG_START_CONVERSION | \
318c2ecf20Sopenharmony_ci					 TPS6507X_ADCONFIG_INPUT_REAL_TSC)
328c2ecf20Sopenharmony_ci#define	TPS6507X_ADCONFIG_POWER_DOWN_TS (TPS6507X_ADCONFIG_INPUT_REAL_TSC)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistruct ts_event {
358c2ecf20Sopenharmony_ci	u16	x;
368c2ecf20Sopenharmony_ci	u16	y;
378c2ecf20Sopenharmony_ci	u16	pressure;
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistruct tps6507x_ts {
418c2ecf20Sopenharmony_ci	struct device		*dev;
428c2ecf20Sopenharmony_ci	struct input_dev	*input;
438c2ecf20Sopenharmony_ci	struct tps6507x_dev	*mfd;
448c2ecf20Sopenharmony_ci	char			phys[32];
458c2ecf20Sopenharmony_ci	struct ts_event		tc;
468c2ecf20Sopenharmony_ci	u16			min_pressure;
478c2ecf20Sopenharmony_ci	bool			pendown;
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int tps6507x_read_u8(struct tps6507x_ts *tsc, u8 reg, u8 *data)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	return tsc->mfd->read_dev(tsc->mfd, reg, 1, data);
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic int tps6507x_write_u8(struct tps6507x_ts *tsc, u8 reg, u8 data)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	return tsc->mfd->write_dev(tsc->mfd, reg, 1, &data);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic s32 tps6507x_adc_conversion(struct tps6507x_ts *tsc,
618c2ecf20Sopenharmony_ci				   u8 tsc_mode, u16 *value)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	s32 ret;
648c2ecf20Sopenharmony_ci	u8 adc_status;
658c2ecf20Sopenharmony_ci	u8 result;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	/* Route input signal to A/D converter */
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE, tsc_mode);
708c2ecf20Sopenharmony_ci	if (ret) {
718c2ecf20Sopenharmony_ci		dev_err(tsc->dev, "TSC mode read failed\n");
728c2ecf20Sopenharmony_ci		goto err;
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/* Start A/D conversion */
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG,
788c2ecf20Sopenharmony_ci				TPS6507X_ADCONFIG_CONVERT_TS);
798c2ecf20Sopenharmony_ci	if (ret) {
808c2ecf20Sopenharmony_ci		dev_err(tsc->dev, "ADC config write failed\n");
818c2ecf20Sopenharmony_ci		return ret;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	do {
858c2ecf20Sopenharmony_ci		ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADCONFIG,
868c2ecf20Sopenharmony_ci				       &adc_status);
878c2ecf20Sopenharmony_ci		if (ret) {
888c2ecf20Sopenharmony_ci			dev_err(tsc->dev, "ADC config read failed\n");
898c2ecf20Sopenharmony_ci			goto err;
908c2ecf20Sopenharmony_ci		}
918c2ecf20Sopenharmony_ci	} while (adc_status & TPS6507X_ADCONFIG_START_CONVERSION);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_2, &result);
948c2ecf20Sopenharmony_ci	if (ret) {
958c2ecf20Sopenharmony_ci		dev_err(tsc->dev, "ADC result 2 read failed\n");
968c2ecf20Sopenharmony_ci		goto err;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	*value = (result & TPS6507X_REG_ADRESULT_2_MASK) << 8;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_1, &result);
1028c2ecf20Sopenharmony_ci	if (ret) {
1038c2ecf20Sopenharmony_ci		dev_err(tsc->dev, "ADC result 1 read failed\n");
1048c2ecf20Sopenharmony_ci		goto err;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	*value |= result;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	dev_dbg(tsc->dev, "TSC channel %d = 0x%X\n", tsc_mode, *value);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cierr:
1128c2ecf20Sopenharmony_ci	return ret;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/* Need to call tps6507x_adc_standby() after using A/D converter for the
1168c2ecf20Sopenharmony_ci * touch screen interrupt to work properly.
1178c2ecf20Sopenharmony_ci */
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic s32 tps6507x_adc_standby(struct tps6507x_ts *tsc)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	s32 ret;
1228c2ecf20Sopenharmony_ci	s32 loops = 0;
1238c2ecf20Sopenharmony_ci	u8 val;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	ret = tps6507x_write_u8(tsc,  TPS6507X_REG_ADCONFIG,
1268c2ecf20Sopenharmony_ci				TPS6507X_ADCONFIG_INPUT_TSC);
1278c2ecf20Sopenharmony_ci	if (ret)
1288c2ecf20Sopenharmony_ci		return ret;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE,
1318c2ecf20Sopenharmony_ci				TPS6507X_TSCMODE_STANDBY);
1328c2ecf20Sopenharmony_ci	if (ret)
1338c2ecf20Sopenharmony_ci		return ret;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val);
1368c2ecf20Sopenharmony_ci	if (ret)
1378c2ecf20Sopenharmony_ci		return ret;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	while (val & TPS6507X_REG_TSC_INT) {
1408c2ecf20Sopenharmony_ci		mdelay(10);
1418c2ecf20Sopenharmony_ci		ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val);
1428c2ecf20Sopenharmony_ci		if (ret)
1438c2ecf20Sopenharmony_ci			return ret;
1448c2ecf20Sopenharmony_ci		loops++;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return ret;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic void tps6507x_ts_poll(struct input_dev *input_dev)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	struct tps6507x_ts *tsc = input_get_drvdata(input_dev);
1538c2ecf20Sopenharmony_ci	bool pendown;
1548c2ecf20Sopenharmony_ci	s32 ret;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE,
1578c2ecf20Sopenharmony_ci				      &tsc->tc.pressure);
1588c2ecf20Sopenharmony_ci	if (ret)
1598c2ecf20Sopenharmony_ci		goto done;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	pendown = tsc->tc.pressure > tsc->min_pressure;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (unlikely(!pendown && tsc->pendown)) {
1648c2ecf20Sopenharmony_ci		dev_dbg(tsc->dev, "UP\n");
1658c2ecf20Sopenharmony_ci		input_report_key(input_dev, BTN_TOUCH, 0);
1668c2ecf20Sopenharmony_ci		input_report_abs(input_dev, ABS_PRESSURE, 0);
1678c2ecf20Sopenharmony_ci		input_sync(input_dev);
1688c2ecf20Sopenharmony_ci		tsc->pendown = false;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (pendown) {
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci		if (!tsc->pendown) {
1748c2ecf20Sopenharmony_ci			dev_dbg(tsc->dev, "DOWN\n");
1758c2ecf20Sopenharmony_ci			input_report_key(input_dev, BTN_TOUCH, 1);
1768c2ecf20Sopenharmony_ci		} else
1778c2ecf20Sopenharmony_ci			dev_dbg(tsc->dev, "still down\n");
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci		ret =  tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_X_POSITION,
1808c2ecf20Sopenharmony_ci					       &tsc->tc.x);
1818c2ecf20Sopenharmony_ci		if (ret)
1828c2ecf20Sopenharmony_ci			goto done;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		ret =  tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_Y_POSITION,
1858c2ecf20Sopenharmony_ci					       &tsc->tc.y);
1868c2ecf20Sopenharmony_ci		if (ret)
1878c2ecf20Sopenharmony_ci			goto done;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci		input_report_abs(input_dev, ABS_X, tsc->tc.x);
1908c2ecf20Sopenharmony_ci		input_report_abs(input_dev, ABS_Y, tsc->tc.y);
1918c2ecf20Sopenharmony_ci		input_report_abs(input_dev, ABS_PRESSURE, tsc->tc.pressure);
1928c2ecf20Sopenharmony_ci		input_sync(input_dev);
1938c2ecf20Sopenharmony_ci		tsc->pendown = true;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cidone:
1978c2ecf20Sopenharmony_ci	tps6507x_adc_standby(tsc);
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic int tps6507x_ts_probe(struct platform_device *pdev)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent);
2038c2ecf20Sopenharmony_ci	const struct tps6507x_board *tps_board;
2048c2ecf20Sopenharmony_ci	const struct touchscreen_init_data *init_data;
2058c2ecf20Sopenharmony_ci	struct tps6507x_ts *tsc;
2068c2ecf20Sopenharmony_ci	struct input_dev *input_dev;
2078c2ecf20Sopenharmony_ci	int error;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/*
2108c2ecf20Sopenharmony_ci	 * tps_board points to pmic related constants
2118c2ecf20Sopenharmony_ci	 * coming from the board-evm file.
2128c2ecf20Sopenharmony_ci	 */
2138c2ecf20Sopenharmony_ci	tps_board = dev_get_platdata(tps6507x_dev->dev);
2148c2ecf20Sopenharmony_ci	if (!tps_board) {
2158c2ecf20Sopenharmony_ci		dev_err(tps6507x_dev->dev,
2168c2ecf20Sopenharmony_ci			"Could not find tps6507x platform data\n");
2178c2ecf20Sopenharmony_ci		return -ENODEV;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/*
2218c2ecf20Sopenharmony_ci	 * init_data points to array of regulator_init structures
2228c2ecf20Sopenharmony_ci	 * coming from the board-evm file.
2238c2ecf20Sopenharmony_ci	 */
2248c2ecf20Sopenharmony_ci	init_data = tps_board->tps6507x_ts_init_data;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	tsc = devm_kzalloc(&pdev->dev, sizeof(struct tps6507x_ts), GFP_KERNEL);
2278c2ecf20Sopenharmony_ci	if (!tsc) {
2288c2ecf20Sopenharmony_ci		dev_err(tps6507x_dev->dev, "failed to allocate driver data\n");
2298c2ecf20Sopenharmony_ci		return -ENOMEM;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	tsc->mfd = tps6507x_dev;
2338c2ecf20Sopenharmony_ci	tsc->dev = tps6507x_dev->dev;
2348c2ecf20Sopenharmony_ci	tsc->min_pressure = init_data ?
2358c2ecf20Sopenharmony_ci			init_data->min_pressure : TPS_DEFAULT_MIN_PRESSURE;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	snprintf(tsc->phys, sizeof(tsc->phys),
2388c2ecf20Sopenharmony_ci		 "%s/input0", dev_name(tsc->dev));
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	input_dev = devm_input_allocate_device(&pdev->dev);
2418c2ecf20Sopenharmony_ci	if (!input_dev) {
2428c2ecf20Sopenharmony_ci		dev_err(tsc->dev, "Failed to allocate polled input device.\n");
2438c2ecf20Sopenharmony_ci		return -ENOMEM;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	tsc->input = input_dev;
2478c2ecf20Sopenharmony_ci	input_set_drvdata(input_dev, tsc);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
2508c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev, ABS_X, 0, MAX_10BIT, 0, 0);
2518c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev, ABS_Y, 0, MAX_10BIT, 0, 0);
2528c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	input_dev->name = "TPS6507x Touchscreen";
2558c2ecf20Sopenharmony_ci	input_dev->phys = tsc->phys;
2568c2ecf20Sopenharmony_ci	input_dev->dev.parent = tsc->dev;
2578c2ecf20Sopenharmony_ci	input_dev->id.bustype = BUS_I2C;
2588c2ecf20Sopenharmony_ci	if (init_data) {
2598c2ecf20Sopenharmony_ci		input_dev->id.vendor = init_data->vendor;
2608c2ecf20Sopenharmony_ci		input_dev->id.product = init_data->product;
2618c2ecf20Sopenharmony_ci		input_dev->id.version = init_data->version;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	error = tps6507x_adc_standby(tsc);
2658c2ecf20Sopenharmony_ci	if (error)
2668c2ecf20Sopenharmony_ci		return error;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	error = input_setup_polling(input_dev, tps6507x_ts_poll);
2698c2ecf20Sopenharmony_ci	if (error)
2708c2ecf20Sopenharmony_ci		return error;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	input_set_poll_interval(input_dev,
2738c2ecf20Sopenharmony_ci				init_data ? init_data->poll_period :
2748c2ecf20Sopenharmony_ci					    TSC_DEFAULT_POLL_PERIOD);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	error = input_register_device(input_dev);
2778c2ecf20Sopenharmony_ci	if (error)
2788c2ecf20Sopenharmony_ci		return error;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return 0;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic struct platform_driver tps6507x_ts_driver = {
2848c2ecf20Sopenharmony_ci	.driver = {
2858c2ecf20Sopenharmony_ci		.name = "tps6507x-ts",
2868c2ecf20Sopenharmony_ci	},
2878c2ecf20Sopenharmony_ci	.probe = tps6507x_ts_probe,
2888c2ecf20Sopenharmony_ci};
2898c2ecf20Sopenharmony_cimodule_platform_driver(tps6507x_ts_driver);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ciMODULE_AUTHOR("Todd Fischer <todd.fischer@ridgerun.com>");
2928c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TPS6507x - TouchScreen driver");
2938c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
2948c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:tps6507x-ts");
295