Index: linux-2.6.21/drivers/input/touchscreen/Kconfig =================================================================== --- linux-2.6.21.orig/drivers/input/touchscreen/Kconfig 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21/drivers/input/touchscreen/Kconfig 2007-04-26 23:27:05.000000000 +0200 @@ -164,4 +164,16 @@ To compile this driver as a module, choose M here: the module will be called ucb1400_ts. +config TOUCHSCREEN_PCAP + tristate "Motorola PCAP touchscreen" + depends on PXA_EZX_PCAP + help + Say Y here if you have a Motorola EZX (E680, A780) telephone + and want to support the built-in touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called hp680_ts_input. + endif Index: linux-2.6.21/drivers/input/touchscreen/Makefile =================================================================== --- linux-2.6.21.orig/drivers/input/touchscreen/Makefile 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21/drivers/input/touchscreen/Makefile 2007-04-26 23:27:52.000000000 +0200 @@ -16,3 +16,4 @@ obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o +obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o Index: linux-2.6.21/drivers/input/touchscreen/pcap_ts.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.21/drivers/input/touchscreen/pcap_ts.c 2007-04-26 23:27:05.000000000 +0200 @@ -0,0 +1,364 @@ +/* + * pcap_ts.c - Touchscreen driver for Motorola PCAP2 based touchscreen as found + * in the EZX phone platform. + * + * Copyright (C) 2006 Harald Welte + * + * Based on information found in the original Motorola 2.4.x ezx-ts.c driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * TODO: + * split this in a hardirq handler and a tasklet/bh + * suspend/resume support + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../../misc/ezx/ssp_pcap.h" + +#if 1 +#define DEBUGP(x, args ...) printk(KERN_DEBUG "%s: " x, __FUNCTION__, ## args) +#else +#define DEBUGP(x, args ...) +#endif + +#define PRESSURE 1 +#define COORDINATE 2 + +struct pcap_ts { + int irq_xy; + int irq_touch; + struct input_dev *input; + struct timer_list timer; + + u_int16_t x, y; + u_int16_t pressure, pressure_last; + + u_int8_t read_state; +}; + +#define X_AXIS_MIN 0 +#define X_AXIS_MAX 1023 + +#define Y_AXIS_MAX X_AXIS_MAX +#define Y_AXIS_MIN X_AXIS_MIN + +#define PRESSURE_MAX X_AXIS_MAX +#define PRESSURE_MIN X_AXIS_MIN + +static int pcap_ts_mode(u_int32_t mode) +{ + int ret; + + u_int32_t tmp; + + ret = ezx_pcap_read(SSP_PCAP_ADJ_ADC1_REGISTER, &tmp); + if (ret < 0) + return ret; + + tmp &= ~SSP_PCAP_TOUCH_PANEL_POSITION_DETECT_MODE_MASK; + tmp |= mode; + ret = ezx_pcap_write(SSP_PCAP_ADJ_ADC1_REGISTER, tmp); + + DEBUGP("set ts mode "); + if (mode == PCAP_TS_POSITION_XY_MEASUREMENT) + DEBUGP("COORD\n"); + else if (mode == PCAP_TS_PRESSURE_MEASUREMENT) + DEBUGP("PRESS\n"); + else if (mode == PCAP_TS_STANDBY_MODE) + DEBUGP("STANDBY\n"); + else + printk("UNKNOWN\n"); + + return ret; +} + +/* issue a XY read command to the ADC of PCAP2. Well get an ADCDONE2 interrupt + * once the result of the conversion is available */ +static int pcap_ts_start_xy_read(struct pcap_ts *pcap_ts) +{ + int ret; + u_int32_t tmp; + DEBUGP("start xy read in mode %s\n", + pcap_ts->read_state == COORDINATE ? "COORD" : "PRESS"); + + ret = ezx_pcap_read(SSP_PCAP_ADJ_ADC1_REGISTER, &tmp); + if (ret < 0) + return ret; + + tmp &= SSP_PCAP_ADC_START_VALUE_SET_MASK; + tmp |= SSP_PCAP_ADC_START_VALUE; + + ret = ezx_pcap_write(SSP_PCAP_ADJ_ADC1_REGISTER, tmp); + if (ret < 0) + return ret; + + ret = ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_ADC2_ASC, 1); + + return ret; +} + +/* read the XY result from the ADC of PCAP2 */ +static int pcap_ts_get_xy_value(struct pcap_ts *pcap_ts) +{ + int ret; + u_int32_t tmp; + + DEBUGP("get xy value in mode %s\n", + pcap_ts->read_state == COORDINATE ? "COORD" : "PRESS"); + + ret = ezx_pcap_read(SSP_PCAP_ADJ_ADC2_REGISTER, &tmp); + if (ret < 0) + return ret; + + if (tmp & 0x00400000) + return -EIO; + + if (pcap_ts->read_state == COORDINATE) { + pcap_ts->x = (tmp & SSP_PCAP_ADD1_VALUE_MASK); + pcap_ts->y = (tmp & SSP_PCAP_ADD2_VALUE_MASK) + >>SSP_PCAP_ADD2_VALUE_SHIFT; + } else { + pcap_ts->pressure_last = pcap_ts->pressure; + pcap_ts->pressure = (tmp & SSP_PCAP_ADD2_VALUE_MASK) + >>SSP_PCAP_ADD2_VALUE_SHIFT; + } + + return 0; +} + +/* PCAP2 interrupts us when ADC conversion result is available */ +static irqreturn_t pcap_ts_irq_xy(int irq, void *dev_id, struct pt_regs *regs) +{ + struct pcap_ts *pcap_ts = dev_id; + + if (pcap_ts_get_xy_value(pcap_ts) < 0) { + printk("pcap_ts: error reading XY value\n"); + return IRQ_HANDLED; + } + + DEBUGP("%s X=%4d, Y=%4d Z=%4d\n", + pcap_ts->read_state == COORDINATE ? "COORD" : "PRESS", + pcap_ts->x, pcap_ts->y, pcap_ts->pressure); + + if (pcap_ts->read_state == PRESSURE) { + input_report_abs(pcap_ts->input, ABS_PRESSURE, + pcap_ts->pressure); + if ((pcap_ts->pressure >= PRESSURE_MAX || + pcap_ts->pressure <= PRESSURE_MIN ) && + pcap_ts->pressure == pcap_ts->pressure_last) { + /* pen has been released */ + input_report_key(pcap_ts->input, BTN_TOUCH, 0); + input_sync(pcap_ts->input); + + pcap_ts->x = pcap_ts->y = 0; + + /* ask PCAP2 to interrupt us if touch event happens + * again */ + pcap_ts->read_state = PRESSURE; + pcap_ts_mode(PCAP_TS_STANDBY_MODE); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_MSR_TSM, 0); + + /* no need for timer, we'll get interrupted with + * next touch down event */ + del_timer(&pcap_ts->timer); + } else { + /* pen has been touched down */ + input_report_key(pcap_ts->input, BTN_TOUCH, 1); + /* don't input_sync(), we don't know position yet */ + + /* switch state machine into coordinate read mode */ + pcap_ts->read_state = COORDINATE; + pcap_ts_mode(PCAP_TS_POSITION_XY_MEASUREMENT); + pcap_ts_start_xy_read(pcap_ts); + + mod_timer(&pcap_ts->timer, jiffies + HZ/20); + } + } else { + /* we are in coordinate mode */ + if (pcap_ts->x <= X_AXIS_MIN || pcap_ts->x >= X_AXIS_MAX || + pcap_ts->y <= Y_AXIS_MIN || pcap_ts->y >= Y_AXIS_MAX) { + DEBUGP("invalid x/y coordinate position: PEN_UP?\n"); +#if 0 + input_report_key(pcap_ts->input, BTN_TOUCH, 0); + pcap_ts->x = pcap_ts->y = 0; +#endif + } else { + input_report_abs(pcap_ts->input, ABS_X, pcap_ts->x); + input_report_abs(pcap_ts->input, ABS_Y, pcap_ts->y); + } + input_sync(pcap_ts->input); + + /* switch back to pressure read mode */ + pcap_ts->read_state = PRESSURE; + pcap_ts_mode(PCAP_TS_STANDBY_MODE); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_MSR_TSM, 0); + } + + return IRQ_HANDLED; +} + +/* PCAP2 interrupts us if the pen touches down */ +static irqreturn_t pcap_ts_irq_touch(int irq, void *dev_id, struct pt_regs *regs) +{ + struct pcap_ts *pcap_ts = dev_id; + DEBUGP("entered\n"); + + /* mask Touchscreen interrupt bit, prevents further touch events + * from being reported to us until we're finished with reading + * both pressure and x/y from ADC */ + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_MSR_TSM, 1); + pcap_ts_mode(PCAP_TS_PRESSURE_MEASUREMENT); + pcap_ts->read_state = PRESSURE; + pcap_ts_start_xy_read(pcap_ts); + + return IRQ_HANDLED; +} + +static void pcap_ts_timer_fn(unsigned long data) +{ + struct pcap_ts *pcap_ts = (struct pcap_ts *) data; + + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_MSR_TSM, 1); + pcap_ts_mode(PCAP_TS_PRESSURE_MEASUREMENT); + pcap_ts->read_state = PRESSURE; + pcap_ts_start_xy_read(pcap_ts); +} + +static int __init ezxts_probe(struct platform_device *pdev) +{ + struct pcap_ts *pcap_ts; + struct input_dev *input_dev; + int err = -ENOMEM; + + pcap_ts = kzalloc(sizeof(*pcap_ts), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!pcap_ts || !input_dev) + goto fail; + + pcap_ts->irq_xy = platform_get_irq(pdev, 0); + if (pcap_ts->irq_xy < 0) { + err = pcap_ts->irq_xy; + goto fail; + } + + pcap_ts->irq_touch = platform_get_irq(pdev, 1); + if (pcap_ts->irq_touch < 0) { + err = pcap_ts->irq_touch; + goto fail; + } + + ssp_pcap_open(SSP_PCAP_TS_OPEN); + + err = request_irq(pcap_ts->irq_xy, pcap_ts_irq_xy, SA_INTERRUPT, + "PCAP Touchscreen XY", pcap_ts); + if (err < 0) { + printk(KERN_ERR "pcap_ts: can't grab xy irq %d: %d\n", + pcap_ts->irq_xy, err); + goto fail; + } + + err = request_irq(pcap_ts->irq_touch, pcap_ts_irq_touch, SA_INTERRUPT, + "PCAP Touchscreen Touch", pcap_ts); + if (err < 0) { + printk(KERN_ERR "pcap_ts: can't grab touch irq %d: %d\n", + pcap_ts->irq_touch, err); + goto fail_xy; + } + + pcap_ts->input = input_dev; + pcap_ts->read_state = PRESSURE; + init_timer(&pcap_ts->timer); + pcap_ts->timer.data = (unsigned long) pcap_ts; + pcap_ts->timer.function = &pcap_ts_timer_fn; + + platform_set_drvdata(pdev, pcap_ts); + + /* enable pressure interrupt */ + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_MSR_TSM, 0); + + input_dev->name = "EZX PCAP2 Touchscreen"; + input_dev->phys = "ezxts/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0002; + input_dev->id.version = 0x0100; + input_dev->cdev.dev = &pdev->dev; + input_dev->private = pcap_ts; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, X_AXIS_MIN, X_AXIS_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, PRESSURE_MIN, + PRESSURE_MAX, 0, 0); + + input_register_device(pcap_ts->input); + + return 0; + +fail_xy: + free_irq(pcap_ts->irq_xy, pcap_ts); +fail: + input_free_device(input_dev); + kfree(pcap_ts); + + return err; +} + +static int ezxts_remove(struct platform_device *pdev) +{ + struct pcap_ts *pcap_ts = platform_get_drvdata(pdev); + + del_timer_sync(&pcap_ts->timer); + + free_irq(pcap_ts->irq_touch, pcap_ts); + free_irq(pcap_ts->irq_xy, pcap_ts); + + input_unregister_device(pcap_ts->input); + kfree(pcap_ts); + + return 0; +} + +static struct platform_driver ezxts_driver = { + .probe = ezxts_probe, + .remove = ezxts_remove, + //.suspend = ezxts_suspend, + //.resume = ezxts_resume, + .driver = { + .name = "pcap-ts", + }, +}; + +static int __devinit ezxts_init(void) +{ + return platform_driver_register(&ezxts_driver); +} + +static void __exit ezxts_exit(void) +{ + platform_driver_unregister(&ezxts_driver); +} + +module_init(ezxts_init); +module_exit(ezxts_exit); + +MODULE_DESCRIPTION("Motorola PCAP2 touchscreen driver"); +MODULE_AUTHOR("Harald Welte "); +MODULE_LICENSE("GPL");