140e3be39SDamien Riegel /*
240e3be39SDamien Riegel  * Touchscreen driver for the TS-4800 board
340e3be39SDamien Riegel  *
440e3be39SDamien Riegel  * Copyright (c) 2015 - Savoir-faire Linux
540e3be39SDamien Riegel  *
640e3be39SDamien Riegel  * This file is licensed under the terms of the GNU General Public
740e3be39SDamien Riegel  * License version 2. This program is licensed "as is" without any
840e3be39SDamien Riegel  * warranty of any kind, whether express or implied.
940e3be39SDamien Riegel  */
1040e3be39SDamien Riegel 
1140e3be39SDamien Riegel #include <linux/bitops.h>
1240e3be39SDamien Riegel #include <linux/input.h>
1340e3be39SDamien Riegel #include <linux/io.h>
1440e3be39SDamien Riegel #include <linux/kernel.h>
1540e3be39SDamien Riegel #include <linux/mfd/syscon.h>
1640e3be39SDamien Riegel #include <linux/module.h>
1740e3be39SDamien Riegel #include <linux/of.h>
1840e3be39SDamien Riegel #include <linux/platform_device.h>
1940e3be39SDamien Riegel #include <linux/regmap.h>
2040e3be39SDamien Riegel 
2140e3be39SDamien Riegel /* polling interval in ms */
2240e3be39SDamien Riegel #define POLL_INTERVAL		3
2340e3be39SDamien Riegel 
2440e3be39SDamien Riegel #define DEBOUNCE_COUNT		1
2540e3be39SDamien Riegel 
2640e3be39SDamien Riegel /* sensor values are 12-bit wide */
2740e3be39SDamien Riegel #define MAX_12BIT		((1 << 12) - 1)
2840e3be39SDamien Riegel 
2940e3be39SDamien Riegel #define PENDOWN_MASK		0x1
3040e3be39SDamien Riegel 
3140e3be39SDamien Riegel #define X_OFFSET		0x0
3240e3be39SDamien Riegel #define Y_OFFSET		0x2
3340e3be39SDamien Riegel 
3440e3be39SDamien Riegel struct ts4800_ts {
359b587815SDmitry Torokhov 	struct input_dev        *input;
3640e3be39SDamien Riegel 	struct device           *dev;
3740e3be39SDamien Riegel 	char                    phys[32];
3840e3be39SDamien Riegel 
3940e3be39SDamien Riegel 	void __iomem            *base;
4040e3be39SDamien Riegel 	struct regmap           *regmap;
4140e3be39SDamien Riegel 	unsigned int            reg;
4240e3be39SDamien Riegel 	unsigned int            bit;
4340e3be39SDamien Riegel 
4440e3be39SDamien Riegel 	bool                    pendown;
4540e3be39SDamien Riegel 	int                     debounce;
4640e3be39SDamien Riegel };
4740e3be39SDamien Riegel 
ts4800_ts_open(struct input_dev * input_dev)489b587815SDmitry Torokhov static int ts4800_ts_open(struct input_dev *input_dev)
4940e3be39SDamien Riegel {
509b587815SDmitry Torokhov 	struct ts4800_ts *ts = input_get_drvdata(input_dev);
519b587815SDmitry Torokhov 	int error;
5240e3be39SDamien Riegel 
5340e3be39SDamien Riegel 	ts->pendown = false;
5440e3be39SDamien Riegel 	ts->debounce = DEBOUNCE_COUNT;
5540e3be39SDamien Riegel 
569b587815SDmitry Torokhov 	error = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit);
579b587815SDmitry Torokhov 	if (error) {
589b587815SDmitry Torokhov 		dev_warn(ts->dev, "Failed to enable touchscreen: %d\n", error);
599b587815SDmitry Torokhov 		return error;
6040e3be39SDamien Riegel 	}
6140e3be39SDamien Riegel 
629b587815SDmitry Torokhov 	return 0;
639b587815SDmitry Torokhov }
649b587815SDmitry Torokhov 
ts4800_ts_close(struct input_dev * input_dev)659b587815SDmitry Torokhov static void ts4800_ts_close(struct input_dev *input_dev)
6640e3be39SDamien Riegel {
679b587815SDmitry Torokhov 	struct ts4800_ts *ts = input_get_drvdata(input_dev);
6840e3be39SDamien Riegel 	int ret;
6940e3be39SDamien Riegel 
7040e3be39SDamien Riegel 	ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, 0);
7140e3be39SDamien Riegel 	if (ret)
7240e3be39SDamien Riegel 		dev_warn(ts->dev, "Failed to disable touchscreen\n");
7340e3be39SDamien Riegel 
7440e3be39SDamien Riegel }
7540e3be39SDamien Riegel 
ts4800_ts_poll(struct input_dev * input_dev)769b587815SDmitry Torokhov static void ts4800_ts_poll(struct input_dev *input_dev)
7740e3be39SDamien Riegel {
789b587815SDmitry Torokhov 	struct ts4800_ts *ts = input_get_drvdata(input_dev);
7940e3be39SDamien Riegel 	u16 last_x = readw(ts->base + X_OFFSET);
8040e3be39SDamien Riegel 	u16 last_y = readw(ts->base + Y_OFFSET);
8140e3be39SDamien Riegel 	bool pendown = last_x & PENDOWN_MASK;
8240e3be39SDamien Riegel 
8340e3be39SDamien Riegel 	if (pendown) {
8440e3be39SDamien Riegel 		if (ts->debounce) {
8540e3be39SDamien Riegel 			ts->debounce--;
8640e3be39SDamien Riegel 			return;
8740e3be39SDamien Riegel 		}
8840e3be39SDamien Riegel 
8940e3be39SDamien Riegel 		if (!ts->pendown) {
9040e3be39SDamien Riegel 			input_report_key(input_dev, BTN_TOUCH, 1);
9140e3be39SDamien Riegel 			ts->pendown = true;
9240e3be39SDamien Riegel 		}
9340e3be39SDamien Riegel 
9440e3be39SDamien Riegel 		last_x = ((~last_x) >> 4) & MAX_12BIT;
9540e3be39SDamien Riegel 		last_y = ((~last_y) >> 4) & MAX_12BIT;
9640e3be39SDamien Riegel 
9740e3be39SDamien Riegel 		input_report_abs(input_dev, ABS_X, last_x);
9840e3be39SDamien Riegel 		input_report_abs(input_dev, ABS_Y, last_y);
9940e3be39SDamien Riegel 		input_sync(input_dev);
10040e3be39SDamien Riegel 	} else if (ts->pendown) {
10140e3be39SDamien Riegel 		ts->pendown = false;
10240e3be39SDamien Riegel 		ts->debounce = DEBOUNCE_COUNT;
10340e3be39SDamien Riegel 		input_report_key(input_dev, BTN_TOUCH, 0);
10440e3be39SDamien Riegel 		input_sync(input_dev);
10540e3be39SDamien Riegel 	}
10640e3be39SDamien Riegel }
10740e3be39SDamien Riegel 
ts4800_parse_dt(struct platform_device * pdev,struct ts4800_ts * ts)10840e3be39SDamien Riegel static int ts4800_parse_dt(struct platform_device *pdev,
10940e3be39SDamien Riegel 			   struct ts4800_ts *ts)
11040e3be39SDamien Riegel {
11140e3be39SDamien Riegel 	struct device *dev = &pdev->dev;
11240e3be39SDamien Riegel 	struct device_node *np = dev->of_node;
11340e3be39SDamien Riegel 	struct device_node *syscon_np;
11440e3be39SDamien Riegel 	u32 reg, bit;
11540e3be39SDamien Riegel 	int error;
11640e3be39SDamien Riegel 
11740e3be39SDamien Riegel 	syscon_np = of_parse_phandle(np, "syscon", 0);
11840e3be39SDamien Riegel 	if (!syscon_np) {
11940e3be39SDamien Riegel 		dev_err(dev, "no syscon property\n");
12040e3be39SDamien Riegel 		return -ENODEV;
12140e3be39SDamien Riegel 	}
12240e3be39SDamien Riegel 
1236a5029e6SPeter Chen 	ts->regmap = syscon_node_to_regmap(syscon_np);
1246a5029e6SPeter Chen 	of_node_put(syscon_np);
1256a5029e6SPeter Chen 	if (IS_ERR(ts->regmap)) {
1266a5029e6SPeter Chen 		dev_err(dev, "cannot get parent's regmap\n");
1276a5029e6SPeter Chen 		return PTR_ERR(ts->regmap);
1286a5029e6SPeter Chen 	}
1296a5029e6SPeter Chen 
13040e3be39SDamien Riegel 	error = of_property_read_u32_index(np, "syscon", 1, &reg);
13140e3be39SDamien Riegel 	if (error < 0) {
13240e3be39SDamien Riegel 		dev_err(dev, "no offset in syscon\n");
13340e3be39SDamien Riegel 		return error;
13440e3be39SDamien Riegel 	}
13540e3be39SDamien Riegel 
13640e3be39SDamien Riegel 	ts->reg = reg;
13740e3be39SDamien Riegel 
13840e3be39SDamien Riegel 	error = of_property_read_u32_index(np, "syscon", 2, &bit);
13940e3be39SDamien Riegel 	if (error < 0) {
14040e3be39SDamien Riegel 		dev_err(dev, "no bit in syscon\n");
14140e3be39SDamien Riegel 		return error;
14240e3be39SDamien Riegel 	}
14340e3be39SDamien Riegel 
14440e3be39SDamien Riegel 	ts->bit = BIT(bit);
14540e3be39SDamien Riegel 
14640e3be39SDamien Riegel 	return 0;
14740e3be39SDamien Riegel }
14840e3be39SDamien Riegel 
ts4800_ts_probe(struct platform_device * pdev)14940e3be39SDamien Riegel static int ts4800_ts_probe(struct platform_device *pdev)
15040e3be39SDamien Riegel {
1519b587815SDmitry Torokhov 	struct input_dev *input_dev;
15240e3be39SDamien Riegel 	struct ts4800_ts *ts;
15340e3be39SDamien Riegel 	int error;
15440e3be39SDamien Riegel 
15540e3be39SDamien Riegel 	ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL);
15640e3be39SDamien Riegel 	if (!ts)
15740e3be39SDamien Riegel 		return -ENOMEM;
15840e3be39SDamien Riegel 
15940e3be39SDamien Riegel 	error = ts4800_parse_dt(pdev, ts);
16040e3be39SDamien Riegel 	if (error)
16140e3be39SDamien Riegel 		return error;
16240e3be39SDamien Riegel 
163f8890bd2SMukesh Ojha 	ts->base = devm_platform_ioremap_resource(pdev, 0);
16440e3be39SDamien Riegel 	if (IS_ERR(ts->base))
16540e3be39SDamien Riegel 		return PTR_ERR(ts->base);
16640e3be39SDamien Riegel 
1679b587815SDmitry Torokhov 	input_dev = devm_input_allocate_device(&pdev->dev);
1689b587815SDmitry Torokhov 	if (!input_dev)
16940e3be39SDamien Riegel 		return -ENOMEM;
17040e3be39SDamien Riegel 
17140e3be39SDamien Riegel 	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev));
1729b587815SDmitry Torokhov 	ts->input = input_dev;
17340e3be39SDamien Riegel 	ts->dev = &pdev->dev;
17440e3be39SDamien Riegel 
1759b587815SDmitry Torokhov 	input_set_drvdata(input_dev, ts);
17640e3be39SDamien Riegel 
1779b587815SDmitry Torokhov 	input_dev->name = "TS-4800 Touchscreen";
1789b587815SDmitry Torokhov 	input_dev->phys = ts->phys;
17940e3be39SDamien Riegel 
1809b587815SDmitry Torokhov 	input_dev->open = ts4800_ts_open;
1819b587815SDmitry Torokhov 	input_dev->close = ts4800_ts_close;
18240e3be39SDamien Riegel 
1839b587815SDmitry Torokhov 	input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
1849b587815SDmitry Torokhov 	input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
1859b587815SDmitry Torokhov 	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
1869b587815SDmitry Torokhov 
1879b587815SDmitry Torokhov 	error = input_setup_polling(input_dev, ts4800_ts_poll);
1889b587815SDmitry Torokhov 	if (error) {
1899b587815SDmitry Torokhov 		dev_err(&pdev->dev, "Unable to set up polling: %d\n", error);
1909b587815SDmitry Torokhov 		return error;
1919b587815SDmitry Torokhov 	}
1929b587815SDmitry Torokhov 
1939b587815SDmitry Torokhov 	input_set_poll_interval(input_dev, POLL_INTERVAL);
1949b587815SDmitry Torokhov 
1959b587815SDmitry Torokhov 	error = input_register_device(input_dev);
19640e3be39SDamien Riegel 	if (error) {
19740e3be39SDamien Riegel 		dev_err(&pdev->dev,
1989b587815SDmitry Torokhov 			"Unable to register input device: %d\n", error);
19940e3be39SDamien Riegel 		return error;
20040e3be39SDamien Riegel 	}
20140e3be39SDamien Riegel 
20240e3be39SDamien Riegel 	return 0;
20340e3be39SDamien Riegel }
20440e3be39SDamien Riegel 
20540e3be39SDamien Riegel static const struct of_device_id ts4800_ts_of_match[] = {
20640e3be39SDamien Riegel 	{ .compatible = "technologic,ts4800-ts", },
20740e3be39SDamien Riegel 	{ },
20840e3be39SDamien Riegel };
20940e3be39SDamien Riegel MODULE_DEVICE_TABLE(of, ts4800_ts_of_match);
21040e3be39SDamien Riegel 
21140e3be39SDamien Riegel static struct platform_driver ts4800_ts_driver = {
21240e3be39SDamien Riegel 	.driver = {
21340e3be39SDamien Riegel 		.name = "ts4800-ts",
21440e3be39SDamien Riegel 		.of_match_table = ts4800_ts_of_match,
21540e3be39SDamien Riegel 	},
21640e3be39SDamien Riegel 	.probe = ts4800_ts_probe,
21740e3be39SDamien Riegel };
21840e3be39SDamien Riegel module_platform_driver(ts4800_ts_driver);
21940e3be39SDamien Riegel 
22040e3be39SDamien Riegel MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>");
22140e3be39SDamien Riegel MODULE_DESCRIPTION("TS-4800 Touchscreen Driver");
22240e3be39SDamien Riegel MODULE_LICENSE("GPL v2");
22340e3be39SDamien Riegel MODULE_ALIAS("platform:ts4800_ts");
224