18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Dynapro serial touchscreen driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2009 Tias Guns 68c2ecf20Sopenharmony_ci * Based on the inexio driver (c) Vojtech Pavlik and Dan Streetman and 78c2ecf20Sopenharmony_ci * Richard Lemon 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* 128c2ecf20Sopenharmony_ci * 2009/09/19 Tias Guns <tias@ulyssis.org> 138c2ecf20Sopenharmony_ci * Copied inexio.c and edited for Dynapro protocol (from retired Xorg module) 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/errno.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/input.h> 218c2ecf20Sopenharmony_ci#include <linux/serio.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define DRIVER_DESC "Dynapro serial touchscreen driver" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tias Guns <tias@ulyssis.org>"); 268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * Definitions & global arrays. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define DYNAPRO_FORMAT_TOUCH_BIT 0x40 348c2ecf20Sopenharmony_ci#define DYNAPRO_FORMAT_LENGTH 3 358c2ecf20Sopenharmony_ci#define DYNAPRO_RESPONSE_BEGIN_BYTE 0x80 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define DYNAPRO_MIN_XC 0 388c2ecf20Sopenharmony_ci#define DYNAPRO_MAX_XC 0x3ff 398c2ecf20Sopenharmony_ci#define DYNAPRO_MIN_YC 0 408c2ecf20Sopenharmony_ci#define DYNAPRO_MAX_YC 0x3ff 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define DYNAPRO_GET_XC(data) (data[1] | ((data[0] & 0x38) << 4)) 438c2ecf20Sopenharmony_ci#define DYNAPRO_GET_YC(data) (data[2] | ((data[0] & 0x07) << 7)) 448c2ecf20Sopenharmony_ci#define DYNAPRO_GET_TOUCHED(data) (DYNAPRO_FORMAT_TOUCH_BIT & data[0]) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* 478c2ecf20Sopenharmony_ci * Per-touchscreen data. 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct dynapro { 518c2ecf20Sopenharmony_ci struct input_dev *dev; 528c2ecf20Sopenharmony_ci struct serio *serio; 538c2ecf20Sopenharmony_ci int idx; 548c2ecf20Sopenharmony_ci unsigned char data[DYNAPRO_FORMAT_LENGTH]; 558c2ecf20Sopenharmony_ci char phys[32]; 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic void dynapro_process_data(struct dynapro *pdynapro) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct input_dev *dev = pdynapro->dev; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (DYNAPRO_FORMAT_LENGTH == ++pdynapro->idx) { 638c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_X, DYNAPRO_GET_XC(pdynapro->data)); 648c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_Y, DYNAPRO_GET_YC(pdynapro->data)); 658c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOUCH, 668c2ecf20Sopenharmony_ci DYNAPRO_GET_TOUCHED(pdynapro->data)); 678c2ecf20Sopenharmony_ci input_sync(dev); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci pdynapro->idx = 0; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic irqreturn_t dynapro_interrupt(struct serio *serio, 748c2ecf20Sopenharmony_ci unsigned char data, unsigned int flags) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct dynapro *pdynapro = serio_get_drvdata(serio); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci pdynapro->data[pdynapro->idx] = data; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (DYNAPRO_RESPONSE_BEGIN_BYTE & pdynapro->data[0]) 818c2ecf20Sopenharmony_ci dynapro_process_data(pdynapro); 828c2ecf20Sopenharmony_ci else 838c2ecf20Sopenharmony_ci dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n", 848c2ecf20Sopenharmony_ci pdynapro->data[0]); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return IRQ_HANDLED; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void dynapro_disconnect(struct serio *serio) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct dynapro *pdynapro = serio_get_drvdata(serio); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci input_get_device(pdynapro->dev); 948c2ecf20Sopenharmony_ci input_unregister_device(pdynapro->dev); 958c2ecf20Sopenharmony_ci serio_close(serio); 968c2ecf20Sopenharmony_ci serio_set_drvdata(serio, NULL); 978c2ecf20Sopenharmony_ci input_put_device(pdynapro->dev); 988c2ecf20Sopenharmony_ci kfree(pdynapro); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* 1028c2ecf20Sopenharmony_ci * dynapro_connect() is the routine that is called when someone adds a 1038c2ecf20Sopenharmony_ci * new serio device that supports dynapro protocol and registers it as 1048c2ecf20Sopenharmony_ci * an input device. This is usually accomplished using inputattach. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int dynapro_connect(struct serio *serio, struct serio_driver *drv) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct dynapro *pdynapro; 1108c2ecf20Sopenharmony_ci struct input_dev *input_dev; 1118c2ecf20Sopenharmony_ci int err; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci pdynapro = kzalloc(sizeof(struct dynapro), GFP_KERNEL); 1148c2ecf20Sopenharmony_ci input_dev = input_allocate_device(); 1158c2ecf20Sopenharmony_ci if (!pdynapro || !input_dev) { 1168c2ecf20Sopenharmony_ci err = -ENOMEM; 1178c2ecf20Sopenharmony_ci goto fail1; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci pdynapro->serio = serio; 1218c2ecf20Sopenharmony_ci pdynapro->dev = input_dev; 1228c2ecf20Sopenharmony_ci snprintf(pdynapro->phys, sizeof(pdynapro->phys), 1238c2ecf20Sopenharmony_ci "%s/input0", serio->phys); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci input_dev->name = "Dynapro Serial TouchScreen"; 1268c2ecf20Sopenharmony_ci input_dev->phys = pdynapro->phys; 1278c2ecf20Sopenharmony_ci input_dev->id.bustype = BUS_RS232; 1288c2ecf20Sopenharmony_ci input_dev->id.vendor = SERIO_DYNAPRO; 1298c2ecf20Sopenharmony_ci input_dev->id.product = 0; 1308c2ecf20Sopenharmony_ci input_dev->id.version = 0x0001; 1318c2ecf20Sopenharmony_ci input_dev->dev.parent = &serio->dev; 1328c2ecf20Sopenharmony_ci input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 1338c2ecf20Sopenharmony_ci input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 1348c2ecf20Sopenharmony_ci input_set_abs_params(pdynapro->dev, ABS_X, 1358c2ecf20Sopenharmony_ci DYNAPRO_MIN_XC, DYNAPRO_MAX_XC, 0, 0); 1368c2ecf20Sopenharmony_ci input_set_abs_params(pdynapro->dev, ABS_Y, 1378c2ecf20Sopenharmony_ci DYNAPRO_MIN_YC, DYNAPRO_MAX_YC, 0, 0); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci serio_set_drvdata(serio, pdynapro); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci err = serio_open(serio, drv); 1428c2ecf20Sopenharmony_ci if (err) 1438c2ecf20Sopenharmony_ci goto fail2; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci err = input_register_device(pdynapro->dev); 1468c2ecf20Sopenharmony_ci if (err) 1478c2ecf20Sopenharmony_ci goto fail3; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci fail3: serio_close(serio); 1528c2ecf20Sopenharmony_ci fail2: serio_set_drvdata(serio, NULL); 1538c2ecf20Sopenharmony_ci fail1: input_free_device(input_dev); 1548c2ecf20Sopenharmony_ci kfree(pdynapro); 1558c2ecf20Sopenharmony_ci return err; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* 1598c2ecf20Sopenharmony_ci * The serio driver structure. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic const struct serio_device_id dynapro_serio_ids[] = { 1638c2ecf20Sopenharmony_ci { 1648c2ecf20Sopenharmony_ci .type = SERIO_RS232, 1658c2ecf20Sopenharmony_ci .proto = SERIO_DYNAPRO, 1668c2ecf20Sopenharmony_ci .id = SERIO_ANY, 1678c2ecf20Sopenharmony_ci .extra = SERIO_ANY, 1688c2ecf20Sopenharmony_ci }, 1698c2ecf20Sopenharmony_ci { 0 } 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(serio, dynapro_serio_ids); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic struct serio_driver dynapro_drv = { 1758c2ecf20Sopenharmony_ci .driver = { 1768c2ecf20Sopenharmony_ci .name = "dynapro", 1778c2ecf20Sopenharmony_ci }, 1788c2ecf20Sopenharmony_ci .description = DRIVER_DESC, 1798c2ecf20Sopenharmony_ci .id_table = dynapro_serio_ids, 1808c2ecf20Sopenharmony_ci .interrupt = dynapro_interrupt, 1818c2ecf20Sopenharmony_ci .connect = dynapro_connect, 1828c2ecf20Sopenharmony_ci .disconnect = dynapro_disconnect, 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cimodule_serio_driver(dynapro_drv); 186