1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2422dee56SAdam Bennett /* 3422dee56SAdam Bennett * Hampshire serial touchscreen driver 4422dee56SAdam Bennett * 5422dee56SAdam Bennett * Copyright (c) 2010 Adam Bennett 6422dee56SAdam Bennett * Based on the dynapro driver (c) Tias Guns 7422dee56SAdam Bennett */ 8422dee56SAdam Bennett 9422dee56SAdam Bennett 10422dee56SAdam Bennett /* 11422dee56SAdam Bennett * 2010/04/08 Adam Bennett <abennett72@gmail.com> 12422dee56SAdam Bennett * Copied dynapro.c and edited for Hampshire 4-byte protocol 13422dee56SAdam Bennett */ 14422dee56SAdam Bennett 15422dee56SAdam Bennett #include <linux/errno.h> 16422dee56SAdam Bennett #include <linux/kernel.h> 17422dee56SAdam Bennett #include <linux/module.h> 18422dee56SAdam Bennett #include <linux/slab.h> 19422dee56SAdam Bennett #include <linux/input.h> 20422dee56SAdam Bennett #include <linux/serio.h> 21422dee56SAdam Bennett 22422dee56SAdam Bennett #define DRIVER_DESC "Hampshire serial touchscreen driver" 23422dee56SAdam Bennett 24422dee56SAdam Bennett MODULE_AUTHOR("Adam Bennett <abennett72@gmail.com>"); 25422dee56SAdam Bennett MODULE_DESCRIPTION(DRIVER_DESC); 26422dee56SAdam Bennett MODULE_LICENSE("GPL"); 27422dee56SAdam Bennett 28422dee56SAdam Bennett /* 29422dee56SAdam Bennett * Definitions & global arrays. 30422dee56SAdam Bennett */ 31422dee56SAdam Bennett 32422dee56SAdam Bennett #define HAMPSHIRE_FORMAT_TOUCH_BIT 0x40 33422dee56SAdam Bennett #define HAMPSHIRE_FORMAT_LENGTH 4 34422dee56SAdam Bennett #define HAMPSHIRE_RESPONSE_BEGIN_BYTE 0x80 35422dee56SAdam Bennett 36422dee56SAdam Bennett #define HAMPSHIRE_MIN_XC 0 37422dee56SAdam Bennett #define HAMPSHIRE_MAX_XC 0x1000 38422dee56SAdam Bennett #define HAMPSHIRE_MIN_YC 0 39422dee56SAdam Bennett #define HAMPSHIRE_MAX_YC 0x1000 40422dee56SAdam Bennett 41422dee56SAdam Bennett #define HAMPSHIRE_GET_XC(data) (((data[3] & 0x0c) >> 2) | (data[1] << 2) | ((data[0] & 0x38) << 6)) 42422dee56SAdam Bennett #define HAMPSHIRE_GET_YC(data) ((data[3] & 0x03) | (data[2] << 2) | ((data[0] & 0x07) << 9)) 43422dee56SAdam Bennett #define HAMPSHIRE_GET_TOUCHED(data) (HAMPSHIRE_FORMAT_TOUCH_BIT & data[0]) 44422dee56SAdam Bennett 45422dee56SAdam Bennett /* 46422dee56SAdam Bennett * Per-touchscreen data. 47422dee56SAdam Bennett */ 48422dee56SAdam Bennett 49422dee56SAdam Bennett struct hampshire { 50422dee56SAdam Bennett struct input_dev *dev; 51422dee56SAdam Bennett struct serio *serio; 52422dee56SAdam Bennett int idx; 53422dee56SAdam Bennett unsigned char data[HAMPSHIRE_FORMAT_LENGTH]; 54422dee56SAdam Bennett char phys[32]; 55422dee56SAdam Bennett }; 56422dee56SAdam Bennett 57422dee56SAdam Bennett static void hampshire_process_data(struct hampshire *phampshire) 58422dee56SAdam Bennett { 59422dee56SAdam Bennett struct input_dev *dev = phampshire->dev; 60422dee56SAdam Bennett 61422dee56SAdam Bennett if (HAMPSHIRE_FORMAT_LENGTH == ++phampshire->idx) { 62422dee56SAdam Bennett input_report_abs(dev, ABS_X, HAMPSHIRE_GET_XC(phampshire->data)); 63422dee56SAdam Bennett input_report_abs(dev, ABS_Y, HAMPSHIRE_GET_YC(phampshire->data)); 64422dee56SAdam Bennett input_report_key(dev, BTN_TOUCH, 65422dee56SAdam Bennett HAMPSHIRE_GET_TOUCHED(phampshire->data)); 66422dee56SAdam Bennett input_sync(dev); 67422dee56SAdam Bennett 68422dee56SAdam Bennett phampshire->idx = 0; 69422dee56SAdam Bennett } 70422dee56SAdam Bennett } 71422dee56SAdam Bennett 72422dee56SAdam Bennett static irqreturn_t hampshire_interrupt(struct serio *serio, 73422dee56SAdam Bennett unsigned char data, unsigned int flags) 74422dee56SAdam Bennett { 75422dee56SAdam Bennett struct hampshire *phampshire = serio_get_drvdata(serio); 76422dee56SAdam Bennett 77422dee56SAdam Bennett phampshire->data[phampshire->idx] = data; 78422dee56SAdam Bennett 79422dee56SAdam Bennett if (HAMPSHIRE_RESPONSE_BEGIN_BYTE & phampshire->data[0]) 80422dee56SAdam Bennett hampshire_process_data(phampshire); 81422dee56SAdam Bennett else 82422dee56SAdam Bennett dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n", 83422dee56SAdam Bennett phampshire->data[0]); 84422dee56SAdam Bennett 85422dee56SAdam Bennett return IRQ_HANDLED; 86422dee56SAdam Bennett } 87422dee56SAdam Bennett 88422dee56SAdam Bennett static void hampshire_disconnect(struct serio *serio) 89422dee56SAdam Bennett { 90422dee56SAdam Bennett struct hampshire *phampshire = serio_get_drvdata(serio); 91422dee56SAdam Bennett 92422dee56SAdam Bennett input_get_device(phampshire->dev); 93422dee56SAdam Bennett input_unregister_device(phampshire->dev); 94422dee56SAdam Bennett serio_close(serio); 95422dee56SAdam Bennett serio_set_drvdata(serio, NULL); 96422dee56SAdam Bennett input_put_device(phampshire->dev); 97422dee56SAdam Bennett kfree(phampshire); 98422dee56SAdam Bennett } 99422dee56SAdam Bennett 100422dee56SAdam Bennett /* 101422dee56SAdam Bennett * hampshire_connect() is the routine that is called when someone adds a 102422dee56SAdam Bennett * new serio device that supports hampshire protocol and registers it as 103422dee56SAdam Bennett * an input device. This is usually accomplished using inputattach. 104422dee56SAdam Bennett */ 105422dee56SAdam Bennett 106422dee56SAdam Bennett static int hampshire_connect(struct serio *serio, struct serio_driver *drv) 107422dee56SAdam Bennett { 108422dee56SAdam Bennett struct hampshire *phampshire; 109422dee56SAdam Bennett struct input_dev *input_dev; 110422dee56SAdam Bennett int err; 111422dee56SAdam Bennett 112422dee56SAdam Bennett phampshire = kzalloc(sizeof(struct hampshire), GFP_KERNEL); 113422dee56SAdam Bennett input_dev = input_allocate_device(); 114422dee56SAdam Bennett if (!phampshire || !input_dev) { 115422dee56SAdam Bennett err = -ENOMEM; 116422dee56SAdam Bennett goto fail1; 117422dee56SAdam Bennett } 118422dee56SAdam Bennett 119422dee56SAdam Bennett phampshire->serio = serio; 120422dee56SAdam Bennett phampshire->dev = input_dev; 121422dee56SAdam Bennett snprintf(phampshire->phys, sizeof(phampshire->phys), 122422dee56SAdam Bennett "%s/input0", serio->phys); 123422dee56SAdam Bennett 124422dee56SAdam Bennett input_dev->name = "Hampshire Serial TouchScreen"; 125422dee56SAdam Bennett input_dev->phys = phampshire->phys; 126422dee56SAdam Bennett input_dev->id.bustype = BUS_RS232; 127422dee56SAdam Bennett input_dev->id.vendor = SERIO_HAMPSHIRE; 128422dee56SAdam Bennett input_dev->id.product = 0; 129422dee56SAdam Bennett input_dev->id.version = 0x0001; 130422dee56SAdam Bennett input_dev->dev.parent = &serio->dev; 131422dee56SAdam Bennett input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 132422dee56SAdam Bennett input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 133422dee56SAdam Bennett input_set_abs_params(phampshire->dev, ABS_X, 134422dee56SAdam Bennett HAMPSHIRE_MIN_XC, HAMPSHIRE_MAX_XC, 0, 0); 135422dee56SAdam Bennett input_set_abs_params(phampshire->dev, ABS_Y, 136422dee56SAdam Bennett HAMPSHIRE_MIN_YC, HAMPSHIRE_MAX_YC, 0, 0); 137422dee56SAdam Bennett 138422dee56SAdam Bennett serio_set_drvdata(serio, phampshire); 139422dee56SAdam Bennett 140422dee56SAdam Bennett err = serio_open(serio, drv); 141422dee56SAdam Bennett if (err) 142422dee56SAdam Bennett goto fail2; 143422dee56SAdam Bennett 144422dee56SAdam Bennett err = input_register_device(phampshire->dev); 145422dee56SAdam Bennett if (err) 146422dee56SAdam Bennett goto fail3; 147422dee56SAdam Bennett 148422dee56SAdam Bennett return 0; 149422dee56SAdam Bennett 150422dee56SAdam Bennett fail3: serio_close(serio); 151422dee56SAdam Bennett fail2: serio_set_drvdata(serio, NULL); 152422dee56SAdam Bennett fail1: input_free_device(input_dev); 153422dee56SAdam Bennett kfree(phampshire); 154422dee56SAdam Bennett return err; 155422dee56SAdam Bennett } 156422dee56SAdam Bennett 157422dee56SAdam Bennett /* 158422dee56SAdam Bennett * The serio driver structure. 159422dee56SAdam Bennett */ 160422dee56SAdam Bennett 1619c4f4ba7SArvind Yadav static const struct serio_device_id hampshire_serio_ids[] = { 162422dee56SAdam Bennett { 163422dee56SAdam Bennett .type = SERIO_RS232, 164422dee56SAdam Bennett .proto = SERIO_HAMPSHIRE, 165422dee56SAdam Bennett .id = SERIO_ANY, 166422dee56SAdam Bennett .extra = SERIO_ANY, 167422dee56SAdam Bennett }, 168422dee56SAdam Bennett { 0 } 169422dee56SAdam Bennett }; 170422dee56SAdam Bennett 171422dee56SAdam Bennett MODULE_DEVICE_TABLE(serio, hampshire_serio_ids); 172422dee56SAdam Bennett 173422dee56SAdam Bennett static struct serio_driver hampshire_drv = { 174422dee56SAdam Bennett .driver = { 175422dee56SAdam Bennett .name = "hampshire", 176422dee56SAdam Bennett }, 177422dee56SAdam Bennett .description = DRIVER_DESC, 178422dee56SAdam Bennett .id_table = hampshire_serio_ids, 179422dee56SAdam Bennett .interrupt = hampshire_interrupt, 180422dee56SAdam Bennett .connect = hampshire_connect, 181422dee56SAdam Bennett .disconnect = hampshire_disconnect, 182422dee56SAdam Bennett }; 183422dee56SAdam Bennett 18465ac9f7aSAxel Lin module_serio_driver(hampshire_drv); 185