162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Dynapro serial touchscreen driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2009 Tias Guns 662306a36Sopenharmony_ci * Based on the inexio driver (c) Vojtech Pavlik and Dan Streetman and 762306a36Sopenharmony_ci * Richard Lemon 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* 1262306a36Sopenharmony_ci * 2009/09/19 Tias Guns <tias@ulyssis.org> 1362306a36Sopenharmony_ci * Copied inexio.c and edited for Dynapro protocol (from retired Xorg module) 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/errno.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/input.h> 2162306a36Sopenharmony_ci#include <linux/serio.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define DRIVER_DESC "Dynapro serial touchscreen driver" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciMODULE_AUTHOR("Tias Guns <tias@ulyssis.org>"); 2662306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 2762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * Definitions & global arrays. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define DYNAPRO_FORMAT_TOUCH_BIT 0x40 3462306a36Sopenharmony_ci#define DYNAPRO_FORMAT_LENGTH 3 3562306a36Sopenharmony_ci#define DYNAPRO_RESPONSE_BEGIN_BYTE 0x80 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define DYNAPRO_MIN_XC 0 3862306a36Sopenharmony_ci#define DYNAPRO_MAX_XC 0x3ff 3962306a36Sopenharmony_ci#define DYNAPRO_MIN_YC 0 4062306a36Sopenharmony_ci#define DYNAPRO_MAX_YC 0x3ff 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define DYNAPRO_GET_XC(data) (data[1] | ((data[0] & 0x38) << 4)) 4362306a36Sopenharmony_ci#define DYNAPRO_GET_YC(data) (data[2] | ((data[0] & 0x07) << 7)) 4462306a36Sopenharmony_ci#define DYNAPRO_GET_TOUCHED(data) (DYNAPRO_FORMAT_TOUCH_BIT & data[0]) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * Per-touchscreen data. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct dynapro { 5162306a36Sopenharmony_ci struct input_dev *dev; 5262306a36Sopenharmony_ci struct serio *serio; 5362306a36Sopenharmony_ci int idx; 5462306a36Sopenharmony_ci unsigned char data[DYNAPRO_FORMAT_LENGTH]; 5562306a36Sopenharmony_ci char phys[32]; 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic void dynapro_process_data(struct dynapro *pdynapro) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct input_dev *dev = pdynapro->dev; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (DYNAPRO_FORMAT_LENGTH == ++pdynapro->idx) { 6362306a36Sopenharmony_ci input_report_abs(dev, ABS_X, DYNAPRO_GET_XC(pdynapro->data)); 6462306a36Sopenharmony_ci input_report_abs(dev, ABS_Y, DYNAPRO_GET_YC(pdynapro->data)); 6562306a36Sopenharmony_ci input_report_key(dev, BTN_TOUCH, 6662306a36Sopenharmony_ci DYNAPRO_GET_TOUCHED(pdynapro->data)); 6762306a36Sopenharmony_ci input_sync(dev); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci pdynapro->idx = 0; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic irqreturn_t dynapro_interrupt(struct serio *serio, 7462306a36Sopenharmony_ci unsigned char data, unsigned int flags) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct dynapro *pdynapro = serio_get_drvdata(serio); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci pdynapro->data[pdynapro->idx] = data; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (DYNAPRO_RESPONSE_BEGIN_BYTE & pdynapro->data[0]) 8162306a36Sopenharmony_ci dynapro_process_data(pdynapro); 8262306a36Sopenharmony_ci else 8362306a36Sopenharmony_ci dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n", 8462306a36Sopenharmony_ci pdynapro->data[0]); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return IRQ_HANDLED; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void dynapro_disconnect(struct serio *serio) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct dynapro *pdynapro = serio_get_drvdata(serio); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci input_get_device(pdynapro->dev); 9462306a36Sopenharmony_ci input_unregister_device(pdynapro->dev); 9562306a36Sopenharmony_ci serio_close(serio); 9662306a36Sopenharmony_ci serio_set_drvdata(serio, NULL); 9762306a36Sopenharmony_ci input_put_device(pdynapro->dev); 9862306a36Sopenharmony_ci kfree(pdynapro); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* 10262306a36Sopenharmony_ci * dynapro_connect() is the routine that is called when someone adds a 10362306a36Sopenharmony_ci * new serio device that supports dynapro protocol and registers it as 10462306a36Sopenharmony_ci * an input device. This is usually accomplished using inputattach. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int dynapro_connect(struct serio *serio, struct serio_driver *drv) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct dynapro *pdynapro; 11062306a36Sopenharmony_ci struct input_dev *input_dev; 11162306a36Sopenharmony_ci int err; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci pdynapro = kzalloc(sizeof(struct dynapro), GFP_KERNEL); 11462306a36Sopenharmony_ci input_dev = input_allocate_device(); 11562306a36Sopenharmony_ci if (!pdynapro || !input_dev) { 11662306a36Sopenharmony_ci err = -ENOMEM; 11762306a36Sopenharmony_ci goto fail1; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci pdynapro->serio = serio; 12162306a36Sopenharmony_ci pdynapro->dev = input_dev; 12262306a36Sopenharmony_ci snprintf(pdynapro->phys, sizeof(pdynapro->phys), 12362306a36Sopenharmony_ci "%s/input0", serio->phys); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci input_dev->name = "Dynapro Serial TouchScreen"; 12662306a36Sopenharmony_ci input_dev->phys = pdynapro->phys; 12762306a36Sopenharmony_ci input_dev->id.bustype = BUS_RS232; 12862306a36Sopenharmony_ci input_dev->id.vendor = SERIO_DYNAPRO; 12962306a36Sopenharmony_ci input_dev->id.product = 0; 13062306a36Sopenharmony_ci input_dev->id.version = 0x0001; 13162306a36Sopenharmony_ci input_dev->dev.parent = &serio->dev; 13262306a36Sopenharmony_ci input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 13362306a36Sopenharmony_ci input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 13462306a36Sopenharmony_ci input_set_abs_params(pdynapro->dev, ABS_X, 13562306a36Sopenharmony_ci DYNAPRO_MIN_XC, DYNAPRO_MAX_XC, 0, 0); 13662306a36Sopenharmony_ci input_set_abs_params(pdynapro->dev, ABS_Y, 13762306a36Sopenharmony_ci DYNAPRO_MIN_YC, DYNAPRO_MAX_YC, 0, 0); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci serio_set_drvdata(serio, pdynapro); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci err = serio_open(serio, drv); 14262306a36Sopenharmony_ci if (err) 14362306a36Sopenharmony_ci goto fail2; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci err = input_register_device(pdynapro->dev); 14662306a36Sopenharmony_ci if (err) 14762306a36Sopenharmony_ci goto fail3; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci fail3: serio_close(serio); 15262306a36Sopenharmony_ci fail2: serio_set_drvdata(serio, NULL); 15362306a36Sopenharmony_ci fail1: input_free_device(input_dev); 15462306a36Sopenharmony_ci kfree(pdynapro); 15562306a36Sopenharmony_ci return err; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/* 15962306a36Sopenharmony_ci * The serio driver structure. 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic const struct serio_device_id dynapro_serio_ids[] = { 16362306a36Sopenharmony_ci { 16462306a36Sopenharmony_ci .type = SERIO_RS232, 16562306a36Sopenharmony_ci .proto = SERIO_DYNAPRO, 16662306a36Sopenharmony_ci .id = SERIO_ANY, 16762306a36Sopenharmony_ci .extra = SERIO_ANY, 16862306a36Sopenharmony_ci }, 16962306a36Sopenharmony_ci { 0 } 17062306a36Sopenharmony_ci}; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(serio, dynapro_serio_ids); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic struct serio_driver dynapro_drv = { 17562306a36Sopenharmony_ci .driver = { 17662306a36Sopenharmony_ci .name = "dynapro", 17762306a36Sopenharmony_ci }, 17862306a36Sopenharmony_ci .description = DRIVER_DESC, 17962306a36Sopenharmony_ci .id_table = dynapro_serio_ids, 18062306a36Sopenharmony_ci .interrupt = dynapro_interrupt, 18162306a36Sopenharmony_ci .connect = dynapro_connect, 18262306a36Sopenharmony_ci .disconnect = dynapro_disconnect, 18362306a36Sopenharmony_ci}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cimodule_serio_driver(dynapro_drv); 186