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 { 35*9b587815SDmitry 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 48*9b587815SDmitry Torokhov static int ts4800_ts_open(struct input_dev *input_dev) 4940e3be39SDamien Riegel { 50*9b587815SDmitry Torokhov struct ts4800_ts *ts = input_get_drvdata(input_dev); 51*9b587815SDmitry Torokhov int error; 5240e3be39SDamien Riegel 5340e3be39SDamien Riegel ts->pendown = false; 5440e3be39SDamien Riegel ts->debounce = DEBOUNCE_COUNT; 5540e3be39SDamien Riegel 56*9b587815SDmitry Torokhov error = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit); 57*9b587815SDmitry Torokhov if (error) { 58*9b587815SDmitry Torokhov dev_warn(ts->dev, "Failed to enable touchscreen: %d\n", error); 59*9b587815SDmitry Torokhov return error; 6040e3be39SDamien Riegel } 6140e3be39SDamien Riegel 62*9b587815SDmitry Torokhov return 0; 63*9b587815SDmitry Torokhov } 64*9b587815SDmitry Torokhov 65*9b587815SDmitry Torokhov static void ts4800_ts_close(struct input_dev *input_dev) 6640e3be39SDamien Riegel { 67*9b587815SDmitry 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 76*9b587815SDmitry Torokhov static void ts4800_ts_poll(struct input_dev *input_dev) 7740e3be39SDamien Riegel { 78*9b587815SDmitry 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 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, ®); 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 14940e3be39SDamien Riegel static int ts4800_ts_probe(struct platform_device *pdev) 15040e3be39SDamien Riegel { 151*9b587815SDmitry 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 167*9b587815SDmitry Torokhov input_dev = devm_input_allocate_device(&pdev->dev); 168*9b587815SDmitry Torokhov if (!input_dev) 16940e3be39SDamien Riegel return -ENOMEM; 17040e3be39SDamien Riegel 17140e3be39SDamien Riegel snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev)); 172*9b587815SDmitry Torokhov ts->input = input_dev; 17340e3be39SDamien Riegel ts->dev = &pdev->dev; 17440e3be39SDamien Riegel 175*9b587815SDmitry Torokhov input_set_drvdata(input_dev, ts); 17640e3be39SDamien Riegel 177*9b587815SDmitry Torokhov input_dev->name = "TS-4800 Touchscreen"; 178*9b587815SDmitry Torokhov input_dev->phys = ts->phys; 17940e3be39SDamien Riegel 180*9b587815SDmitry Torokhov input_dev->open = ts4800_ts_open; 181*9b587815SDmitry Torokhov input_dev->close = ts4800_ts_close; 18240e3be39SDamien Riegel 183*9b587815SDmitry Torokhov input_set_capability(input_dev, EV_KEY, BTN_TOUCH); 184*9b587815SDmitry Torokhov input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); 185*9b587815SDmitry Torokhov input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); 186*9b587815SDmitry Torokhov 187*9b587815SDmitry Torokhov error = input_setup_polling(input_dev, ts4800_ts_poll); 188*9b587815SDmitry Torokhov if (error) { 189*9b587815SDmitry Torokhov dev_err(&pdev->dev, "Unable to set up polling: %d\n", error); 190*9b587815SDmitry Torokhov return error; 191*9b587815SDmitry Torokhov } 192*9b587815SDmitry Torokhov 193*9b587815SDmitry Torokhov input_set_poll_interval(input_dev, POLL_INTERVAL); 194*9b587815SDmitry Torokhov 195*9b587815SDmitry Torokhov error = input_register_device(input_dev); 19640e3be39SDamien Riegel if (error) { 19740e3be39SDamien Riegel dev_err(&pdev->dev, 198*9b587815SDmitry 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