1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 285f202d5SDmitry Torokhov /* 385f202d5SDmitry Torokhov * Fujitsu serial touchscreen driver 485f202d5SDmitry Torokhov * 585f202d5SDmitry Torokhov * Copyright (c) Dmitry Torokhov <dtor@mail.ru> 685f202d5SDmitry Torokhov */ 785f202d5SDmitry Torokhov 885f202d5SDmitry Torokhov 985f202d5SDmitry Torokhov #include <linux/errno.h> 1085f202d5SDmitry Torokhov #include <linux/kernel.h> 1185f202d5SDmitry Torokhov #include <linux/module.h> 1285f202d5SDmitry Torokhov #include <linux/slab.h> 1385f202d5SDmitry Torokhov #include <linux/input.h> 1485f202d5SDmitry Torokhov #include <linux/serio.h> 1585f202d5SDmitry Torokhov 1685f202d5SDmitry Torokhov #define DRIVER_DESC "Fujitsu serial touchscreen driver" 1785f202d5SDmitry Torokhov 1885f202d5SDmitry Torokhov MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>"); 1985f202d5SDmitry Torokhov MODULE_DESCRIPTION(DRIVER_DESC); 2085f202d5SDmitry Torokhov MODULE_LICENSE("GPL"); 2185f202d5SDmitry Torokhov 2285f202d5SDmitry Torokhov #define FUJITSU_LENGTH 5 2385f202d5SDmitry Torokhov 2485f202d5SDmitry Torokhov /* 2585f202d5SDmitry Torokhov * Per-touchscreen data. 2685f202d5SDmitry Torokhov */ 2785f202d5SDmitry Torokhov struct fujitsu { 2885f202d5SDmitry Torokhov struct input_dev *dev; 2985f202d5SDmitry Torokhov struct serio *serio; 3085f202d5SDmitry Torokhov int idx; 3185f202d5SDmitry Torokhov unsigned char data[FUJITSU_LENGTH]; 3285f202d5SDmitry Torokhov char phys[32]; 3385f202d5SDmitry Torokhov }; 3485f202d5SDmitry Torokhov 3585f202d5SDmitry Torokhov /* 3685f202d5SDmitry Torokhov * Decode serial data (5 bytes per packet) 3785f202d5SDmitry Torokhov * First byte 3885f202d5SDmitry Torokhov * 1 C 0 0 R S S S 3985f202d5SDmitry Torokhov * Where C is 1 while in calibration mode (which we don't use) 4085f202d5SDmitry Torokhov * R is 1 when no coordinate corection was done. 4185f202d5SDmitry Torokhov * S are button state 4285f202d5SDmitry Torokhov */ 4385f202d5SDmitry Torokhov static irqreturn_t fujitsu_interrupt(struct serio *serio, 4485f202d5SDmitry Torokhov unsigned char data, unsigned int flags) 4585f202d5SDmitry Torokhov { 4685f202d5SDmitry Torokhov struct fujitsu *fujitsu = serio_get_drvdata(serio); 4785f202d5SDmitry Torokhov struct input_dev *dev = fujitsu->dev; 4885f202d5SDmitry Torokhov 4985f202d5SDmitry Torokhov if (fujitsu->idx == 0) { 5085f202d5SDmitry Torokhov /* resync skip until start of frame */ 5185f202d5SDmitry Torokhov if ((data & 0xf0) != 0x80) 5285f202d5SDmitry Torokhov return IRQ_HANDLED; 5385f202d5SDmitry Torokhov } else { 5485f202d5SDmitry Torokhov /* resync skip garbage */ 5585f202d5SDmitry Torokhov if (data & 0x80) { 5685f202d5SDmitry Torokhov fujitsu->idx = 0; 5785f202d5SDmitry Torokhov return IRQ_HANDLED; 5885f202d5SDmitry Torokhov } 5985f202d5SDmitry Torokhov } 6085f202d5SDmitry Torokhov 6185f202d5SDmitry Torokhov fujitsu->data[fujitsu->idx++] = data; 6285f202d5SDmitry Torokhov if (fujitsu->idx == FUJITSU_LENGTH) { 6385f202d5SDmitry Torokhov input_report_abs(dev, ABS_X, 6485f202d5SDmitry Torokhov (fujitsu->data[2] << 7) | fujitsu->data[1]); 6585f202d5SDmitry Torokhov input_report_abs(dev, ABS_Y, 6685f202d5SDmitry Torokhov (fujitsu->data[4] << 7) | fujitsu->data[3]); 6785f202d5SDmitry Torokhov input_report_key(dev, BTN_TOUCH, 6885f202d5SDmitry Torokhov (fujitsu->data[0] & 0x03) != 2); 6985f202d5SDmitry Torokhov input_sync(dev); 7085f202d5SDmitry Torokhov fujitsu->idx = 0; 7185f202d5SDmitry Torokhov } 7285f202d5SDmitry Torokhov 7385f202d5SDmitry Torokhov return IRQ_HANDLED; 7485f202d5SDmitry Torokhov } 7585f202d5SDmitry Torokhov 7685f202d5SDmitry Torokhov /* 7785f202d5SDmitry Torokhov * fujitsu_disconnect() is the opposite of fujitsu_connect() 7885f202d5SDmitry Torokhov */ 7985f202d5SDmitry Torokhov static void fujitsu_disconnect(struct serio *serio) 8085f202d5SDmitry Torokhov { 8185f202d5SDmitry Torokhov struct fujitsu *fujitsu = serio_get_drvdata(serio); 8285f202d5SDmitry Torokhov 8385f202d5SDmitry Torokhov input_get_device(fujitsu->dev); 8485f202d5SDmitry Torokhov input_unregister_device(fujitsu->dev); 8585f202d5SDmitry Torokhov serio_close(serio); 8685f202d5SDmitry Torokhov serio_set_drvdata(serio, NULL); 8785f202d5SDmitry Torokhov input_put_device(fujitsu->dev); 8885f202d5SDmitry Torokhov kfree(fujitsu); 8985f202d5SDmitry Torokhov } 9085f202d5SDmitry Torokhov 9185f202d5SDmitry Torokhov /* 9285f202d5SDmitry Torokhov * fujitsu_connect() is the routine that is called when someone adds a 9385f202d5SDmitry Torokhov * new serio device that supports the Fujitsu protocol and registers it 9485f202d5SDmitry Torokhov * as input device. 9585f202d5SDmitry Torokhov */ 9685f202d5SDmitry Torokhov static int fujitsu_connect(struct serio *serio, struct serio_driver *drv) 9785f202d5SDmitry Torokhov { 9885f202d5SDmitry Torokhov struct fujitsu *fujitsu; 9985f202d5SDmitry Torokhov struct input_dev *input_dev; 10085f202d5SDmitry Torokhov int err; 10185f202d5SDmitry Torokhov 10285f202d5SDmitry Torokhov fujitsu = kzalloc(sizeof(struct fujitsu), GFP_KERNEL); 10385f202d5SDmitry Torokhov input_dev = input_allocate_device(); 10485f202d5SDmitry Torokhov if (!fujitsu || !input_dev) { 10585f202d5SDmitry Torokhov err = -ENOMEM; 10685f202d5SDmitry Torokhov goto fail1; 10785f202d5SDmitry Torokhov } 10885f202d5SDmitry Torokhov 10985f202d5SDmitry Torokhov fujitsu->serio = serio; 11085f202d5SDmitry Torokhov fujitsu->dev = input_dev; 11185f202d5SDmitry Torokhov snprintf(fujitsu->phys, sizeof(fujitsu->phys), 11285f202d5SDmitry Torokhov "%s/input0", serio->phys); 11385f202d5SDmitry Torokhov 11485f202d5SDmitry Torokhov input_dev->name = "Fujitsu Serial Touchscreen"; 11585f202d5SDmitry Torokhov input_dev->phys = fujitsu->phys; 11685f202d5SDmitry Torokhov input_dev->id.bustype = BUS_RS232; 11785f202d5SDmitry Torokhov input_dev->id.vendor = SERIO_FUJITSU; 11885f202d5SDmitry Torokhov input_dev->id.product = 0; 11985f202d5SDmitry Torokhov input_dev->id.version = 0x0100; 1207b19ada2SJiri Slaby input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 1217b19ada2SJiri Slaby input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 12285f202d5SDmitry Torokhov 12385f202d5SDmitry Torokhov input_set_abs_params(input_dev, ABS_X, 0, 4096, 0, 0); 12485f202d5SDmitry Torokhov input_set_abs_params(input_dev, ABS_Y, 0, 4096, 0, 0); 12585f202d5SDmitry Torokhov serio_set_drvdata(serio, fujitsu); 12685f202d5SDmitry Torokhov 12785f202d5SDmitry Torokhov err = serio_open(serio, drv); 12885f202d5SDmitry Torokhov if (err) 12985f202d5SDmitry Torokhov goto fail2; 13085f202d5SDmitry Torokhov 13185f202d5SDmitry Torokhov err = input_register_device(fujitsu->dev); 13285f202d5SDmitry Torokhov if (err) 13385f202d5SDmitry Torokhov goto fail3; 13485f202d5SDmitry Torokhov 13585f202d5SDmitry Torokhov return 0; 13685f202d5SDmitry Torokhov 13785f202d5SDmitry Torokhov fail3: 13885f202d5SDmitry Torokhov serio_close(serio); 13985f202d5SDmitry Torokhov fail2: 14085f202d5SDmitry Torokhov serio_set_drvdata(serio, NULL); 14185f202d5SDmitry Torokhov fail1: 14285f202d5SDmitry Torokhov input_free_device(input_dev); 14385f202d5SDmitry Torokhov kfree(fujitsu); 14485f202d5SDmitry Torokhov return err; 14585f202d5SDmitry Torokhov } 14685f202d5SDmitry Torokhov 14785f202d5SDmitry Torokhov /* 14885f202d5SDmitry Torokhov * The serio driver structure. 14985f202d5SDmitry Torokhov */ 1500e2ba64eSArvind Yadav static const struct serio_device_id fujitsu_serio_ids[] = { 15185f202d5SDmitry Torokhov { 15285f202d5SDmitry Torokhov .type = SERIO_RS232, 15385f202d5SDmitry Torokhov .proto = SERIO_FUJITSU, 15485f202d5SDmitry Torokhov .id = SERIO_ANY, 15585f202d5SDmitry Torokhov .extra = SERIO_ANY, 15685f202d5SDmitry Torokhov }, 15785f202d5SDmitry Torokhov { 0 } 15885f202d5SDmitry Torokhov }; 15985f202d5SDmitry Torokhov 16085f202d5SDmitry Torokhov MODULE_DEVICE_TABLE(serio, fujitsu_serio_ids); 16185f202d5SDmitry Torokhov 16285f202d5SDmitry Torokhov static struct serio_driver fujitsu_drv = { 16385f202d5SDmitry Torokhov .driver = { 16485f202d5SDmitry Torokhov .name = "fujitsu_ts", 16585f202d5SDmitry Torokhov }, 16685f202d5SDmitry Torokhov .description = DRIVER_DESC, 16785f202d5SDmitry Torokhov .id_table = fujitsu_serio_ids, 16885f202d5SDmitry Torokhov .interrupt = fujitsu_interrupt, 16985f202d5SDmitry Torokhov .connect = fujitsu_connect, 17085f202d5SDmitry Torokhov .disconnect = fujitsu_disconnect, 17185f202d5SDmitry Torokhov }; 17285f202d5SDmitry Torokhov 17365ac9f7aSAxel Lin module_serio_driver(fujitsu_drv); 174