Path: blob/master/drivers/input/touchscreen/h3600_ts_input.c
15109 views
/*1* Copyright (c) 2001 "Crazy" James Simmons [email protected]2*3* Sponsored by Transvirtual Technology.4*5* Derived from the code in h3600_ts.[ch] by Charles Flynn6*/78/*9* Driver for the h3600 Touch Screen and other Atmel controlled devices.10*/1112/*13* This program is free software; you can redistribute it and/or modify14* it under the terms of the GNU General Public License as published by15* the Free Software Foundation; either version 2 of the License, or16* (at your option) any later version.17*18* This program is distributed in the hope that it will be useful,19* but WITHOUT ANY WARRANTY; without even the implied warranty of20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the21* GNU General Public License for more details.22*23* You should have received a copy of the GNU General Public License24* along with this program; if not, write to the Free Software25* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA26*27* Should you need to contact me, the author, you can do so by28* e-mail - mail your message to <[email protected]>.29*/3031#include <linux/errno.h>32#include <linux/kernel.h>33#include <linux/module.h>34#include <linux/slab.h>35#include <linux/input.h>36#include <linux/serio.h>37#include <linux/init.h>38#include <linux/delay.h>3940/* SA1100 serial defines */41#include <mach/hardware.h>42#include <mach/irqs.h>4344#define DRIVER_DESC "H3600 touchscreen driver"4546MODULE_AUTHOR("James Simmons <[email protected]>");47MODULE_DESCRIPTION(DRIVER_DESC);48MODULE_LICENSE("GPL");4950/*51* Definitions & global arrays.52*/5354/* The start and end of frame characters SOF and EOF */55#define CHAR_SOF 0x0256#define CHAR_EOF 0x0357#define FRAME_OVERHEAD 3 /* CHAR_SOF,CHAR_EOF,LENGTH = 3 */5859/*60Atmel events and response IDs contained in frame.61Programmer has no control over these numbers.62TODO there are holes - specifically 1,7,0x0a63*/64#define VERSION_ID 0 /* Get Version (request/response) */65#define KEYBD_ID 2 /* Keyboard (event) */66#define TOUCHS_ID 3 /* Touch Screen (event)*/67#define EEPROM_READ_ID 4 /* (request/response) */68#define EEPROM_WRITE_ID 5 /* (request/response) */69#define THERMAL_ID 6 /* (request/response) */70#define NOTIFY_LED_ID 8 /* (request/response) */71#define BATTERY_ID 9 /* (request/response) */72#define SPI_READ_ID 0x0b /* ( request/response) */73#define SPI_WRITE_ID 0x0c /* ( request/response) */74#define FLITE_ID 0x0d /* backlight ( request/response) */75#define STX_ID 0xa1 /* extension pack status (req/resp) */7677#define MAX_ID 147879#define H3600_MAX_LENGTH 1680#define H3600_KEY 0xf8182#define H3600_SCANCODE_RECORD 1 /* 1 -> record button */83#define H3600_SCANCODE_CALENDAR 2 /* 2 -> calendar */84#define H3600_SCANCODE_CONTACTS 3 /* 3 -> contact */85#define H3600_SCANCODE_Q 4 /* 4 -> Q button */86#define H3600_SCANCODE_START 5 /* 5 -> start menu */87#define H3600_SCANCODE_UP 6 /* 6 -> up */88#define H3600_SCANCODE_RIGHT 7 /* 7 -> right */89#define H3600_SCANCODE_LEFT 8 /* 8 -> left */90#define H3600_SCANCODE_DOWN 9 /* 9 -> down */9192/*93* Per-touchscreen data.94*/95struct h3600_dev {96struct input_dev *dev;97struct serio *serio;98unsigned char event; /* event ID from packet */99unsigned char chksum;100unsigned char len;101unsigned char idx;102unsigned char buf[H3600_MAX_LENGTH];103char phys[32];104};105106static irqreturn_t action_button_handler(int irq, void *dev_id)107{108int down = (GPLR & GPIO_BITSY_ACTION_BUTTON) ? 0 : 1;109struct input_dev *dev = dev_id;110111input_report_key(dev, KEY_ENTER, down);112input_sync(dev);113114return IRQ_HANDLED;115}116117static irqreturn_t npower_button_handler(int irq, void *dev_id)118{119int down = (GPLR & GPIO_BITSY_NPOWER_BUTTON) ? 0 : 1;120struct input_dev *dev = dev_id;121122/*123* This interrupt is only called when we release the key. So we have124* to fake a key press.125*/126input_report_key(dev, KEY_SUSPEND, 1);127input_report_key(dev, KEY_SUSPEND, down);128input_sync(dev);129130return IRQ_HANDLED;131}132133#ifdef CONFIG_PM134135static int flite_brightness = 25;136137enum flite_pwr {138FLITE_PWR_OFF = 0,139FLITE_PWR_ON = 1140};141142/*143* h3600_flite_power: enables or disables power to frontlight, using last bright */144unsigned int h3600_flite_power(struct input_dev *dev, enum flite_pwr pwr)145{146unsigned char brightness = (pwr == FLITE_PWR_OFF) ? 0 : flite_brightness;147struct h3600_dev *ts = input_get_drvdata(dev);148149/* Must be in this order */150serio_write(ts->serio, 1);151serio_write(ts->serio, pwr);152serio_write(ts->serio, brightness);153154return 0;155}156157#endif158159/*160* This function translates the native event packets to linux input event161* packets. Some packets coming from serial are not touchscreen related. In162* this case we send them off to be processed elsewhere.163*/164static void h3600ts_process_packet(struct h3600_dev *ts)165{166struct input_dev *dev = ts->dev;167static int touched = 0;168int key, down = 0;169170switch (ts->event) {171/*172Buttons - returned as a single byte1737 6 5 4 3 2 1 0174S x x x N N N N175176S switch state ( 0=pressed 1=released)177x Unused.178NNNN switch number 0-15179180Note: This is true for non interrupt generated key events.181*/182case KEYBD_ID:183down = (ts->buf[0] & 0x80) ? 0 : 1;184185switch (ts->buf[0] & 0x7f) {186case H3600_SCANCODE_RECORD:187key = KEY_RECORD;188break;189case H3600_SCANCODE_CALENDAR:190key = KEY_PROG1;191break;192case H3600_SCANCODE_CONTACTS:193key = KEY_PROG2;194break;195case H3600_SCANCODE_Q:196key = KEY_Q;197break;198case H3600_SCANCODE_START:199key = KEY_PROG3;200break;201case H3600_SCANCODE_UP:202key = KEY_UP;203break;204case H3600_SCANCODE_RIGHT:205key = KEY_RIGHT;206break;207case H3600_SCANCODE_LEFT:208key = KEY_LEFT;209break;210case H3600_SCANCODE_DOWN:211key = KEY_DOWN;212break;213default:214key = 0;215}216if (key)217input_report_key(dev, key, down);218break;219/*220* Native touchscreen event data is formatted as shown below:-221*222* +-------+-------+-------+-------+223* | Xmsb | Xlsb | Ymsb | Ylsb |224* +-------+-------+-------+-------+225* byte 0 1 2 3226*/227case TOUCHS_ID:228if (!touched) {229input_report_key(dev, BTN_TOUCH, 1);230touched = 1;231}232233if (ts->len) {234unsigned short x, y;235236x = ts->buf[0]; x <<= 8; x += ts->buf[1];237y = ts->buf[2]; y <<= 8; y += ts->buf[3];238239input_report_abs(dev, ABS_X, x);240input_report_abs(dev, ABS_Y, y);241} else {242input_report_key(dev, BTN_TOUCH, 0);243touched = 0;244}245break;246default:247/* Send a non input event elsewhere */248break;249}250251input_sync(dev);252}253254/*255* h3600ts_event() handles events from the input module.256*/257static int h3600ts_event(struct input_dev *dev, unsigned int type,258unsigned int code, int value)259{260#if 0261struct h3600_dev *ts = input_get_drvdata(dev);262263switch (type) {264case EV_LED: {265// serio_write(ts->serio, SOME_CMD);266return 0;267}268}269return -1;270#endif271return 0;272}273274/*275Frame format276byte 1 2 3 len + 4277+-------+---------------+---------------+--=------------+278|SOF |id |len | len bytes | Chksum |279+-------+---------------+---------------+--=------------+280bit 0 7 8 11 12 15 16281282+-------+---------------+-------+283|SOF |id |0 |Chksum | - Note Chksum does not include SOF284+-------+---------------+-------+285bit 0 7 8 11 12 15 16286287*/288289static int state;290291/* decode States */292#define STATE_SOF 0 /* start of FRAME */293#define STATE_ID 1 /* state where we decode the ID & len */294#define STATE_DATA 2 /* state where we decode data */295#define STATE_EOF 3 /* state where we decode checksum or EOF */296297static irqreturn_t h3600ts_interrupt(struct serio *serio, unsigned char data,298unsigned int flags)299{300struct h3600_dev *ts = serio_get_drvdata(serio);301302/*303* We have a new frame coming in.304*/305switch (state) {306case STATE_SOF:307if (data == CHAR_SOF)308state = STATE_ID;309break;310case STATE_ID:311ts->event = (data & 0xf0) >> 4;312ts->len = (data & 0xf);313ts->idx = 0;314if (ts->event >= MAX_ID) {315state = STATE_SOF;316break;317}318ts->chksum = data;319state = (ts->len > 0) ? STATE_DATA : STATE_EOF;320break;321case STATE_DATA:322ts->chksum += data;323ts->buf[ts->idx]= data;324if (++ts->idx == ts->len)325state = STATE_EOF;326break;327case STATE_EOF:328state = STATE_SOF;329if (data == CHAR_EOF || data == ts->chksum)330h3600ts_process_packet(ts);331break;332default:333printk("Error3\n");334break;335}336337return IRQ_HANDLED;338}339340/*341* h3600ts_connect() is the routine that is called when someone adds a342* new serio device that supports H3600 protocol and registers it as343* an input device.344*/345static int h3600ts_connect(struct serio *serio, struct serio_driver *drv)346{347struct h3600_dev *ts;348struct input_dev *input_dev;349int err;350351ts = kzalloc(sizeof(struct h3600_dev), GFP_KERNEL);352input_dev = input_allocate_device();353if (!ts || !input_dev) {354err = -ENOMEM;355goto fail1;356}357358ts->serio = serio;359ts->dev = input_dev;360snprintf(ts->phys, sizeof(ts->phys), "%s/input0", serio->phys);361362input_dev->name = "H3600 TouchScreen";363input_dev->phys = ts->phys;364input_dev->id.bustype = BUS_RS232;365input_dev->id.vendor = SERIO_H3600;366input_dev->id.product = 0x0666; /* FIXME !!! We can ask the hardware */367input_dev->id.version = 0x0100;368input_dev->dev.parent = &serio->dev;369370input_set_drvdata(input_dev, ts);371372input_dev->event = h3600ts_event;373374input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) |375BIT_MASK(EV_LED) | BIT_MASK(EV_PWR);376input_dev->ledbit[0] = BIT_MASK(LED_SLEEP);377input_set_abs_params(input_dev, ABS_X, 60, 985, 0, 0);378input_set_abs_params(input_dev, ABS_Y, 35, 1024, 0, 0);379380set_bit(KEY_RECORD, input_dev->keybit);381set_bit(KEY_Q, input_dev->keybit);382set_bit(KEY_PROG1, input_dev->keybit);383set_bit(KEY_PROG2, input_dev->keybit);384set_bit(KEY_PROG3, input_dev->keybit);385set_bit(KEY_UP, input_dev->keybit);386set_bit(KEY_RIGHT, input_dev->keybit);387set_bit(KEY_LEFT, input_dev->keybit);388set_bit(KEY_DOWN, input_dev->keybit);389set_bit(KEY_ENTER, input_dev->keybit);390set_bit(KEY_SUSPEND, input_dev->keybit);391set_bit(BTN_TOUCH, input_dev->keybit);392393/* Device specific stuff */394set_GPIO_IRQ_edge(GPIO_BITSY_ACTION_BUTTON, GPIO_BOTH_EDGES);395set_GPIO_IRQ_edge(GPIO_BITSY_NPOWER_BUTTON, GPIO_RISING_EDGE);396397if (request_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, action_button_handler,398IRQF_SHARED | IRQF_DISABLED, "h3600_action", ts->dev)) {399printk(KERN_ERR "h3600ts.c: Could not allocate Action Button IRQ!\n");400err = -EBUSY;401goto fail1;402}403404if (request_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, npower_button_handler,405IRQF_SHARED | IRQF_DISABLED, "h3600_suspend", ts->dev)) {406printk(KERN_ERR "h3600ts.c: Could not allocate Power Button IRQ!\n");407err = -EBUSY;408goto fail2;409}410411serio_set_drvdata(serio, ts);412413err = serio_open(serio, drv);414if (err)415goto fail3;416417//h3600_flite_control(1, 25); /* default brightness */418err = input_register_device(ts->dev);419if (err)420goto fail4;421422return 0;423424fail4: serio_close(serio);425fail3: serio_set_drvdata(serio, NULL);426free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev);427fail2: free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts->dev);428fail1: input_free_device(input_dev);429kfree(ts);430return err;431}432433/*434* h3600ts_disconnect() is the opposite of h3600ts_connect()435*/436437static void h3600ts_disconnect(struct serio *serio)438{439struct h3600_dev *ts = serio_get_drvdata(serio);440441free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts->dev);442free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev);443input_get_device(ts->dev);444input_unregister_device(ts->dev);445serio_close(serio);446serio_set_drvdata(serio, NULL);447input_put_device(ts->dev);448kfree(ts);449}450451/*452* The serio driver structure.453*/454455static struct serio_device_id h3600ts_serio_ids[] = {456{457.type = SERIO_RS232,458.proto = SERIO_H3600,459.id = SERIO_ANY,460.extra = SERIO_ANY,461},462{ 0 }463};464465MODULE_DEVICE_TABLE(serio, h3600ts_serio_ids);466467static struct serio_driver h3600ts_drv = {468.driver = {469.name = "h3600ts",470},471.description = DRIVER_DESC,472.id_table = h3600ts_serio_ids,473.interrupt = h3600ts_interrupt,474.connect = h3600ts_connect,475.disconnect = h3600ts_disconnect,476};477478/*479* The functions for inserting/removing us as a module.480*/481482static int __init h3600ts_init(void)483{484return serio_register_driver(&h3600ts_drv);485}486487static void __exit h3600ts_exit(void)488{489serio_unregister_driver(&h3600ts_drv);490}491492module_init(h3600ts_init);493module_exit(h3600ts_exit);494495496