Index: linux-2.6.21/drivers/input/touchscreen/Kconfig =================================================================== --- linux-2.6.21.orig/drivers/input/touchscreen/Kconfig 2007-08-31 22:27:48.000000000 -0300 +++ linux-2.6.21/drivers/input/touchscreen/Kconfig 2007-08-31 23:06:39.000000000 -0300 @@ -164,4 +164,13 @@ 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 EZX_PCAP + help + Say Y here if you have a Motorola EZX telephone and + want to support the built-in touchscreen. + + If unsure, say N. + endif 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-08-31 23:57:56.000000000 -0300 @@ -0,0 +1,331 @@ +/* + * pcap_ts.c - Touchscreen driver for Motorola PCAP2 based touchscreen as found + * in the EZX phone platform. + * + * Copyright (C) 2006 Harald Welte + * Copyright (C) 2007 Daniel Ribeiro + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if 0 +#define DEBUGP(x, args ...) printk(x, ## args) +#else +#define DEBUGP(x, args ...) +#endif + +#define POSITION_X_MEASUREMENT 0 +#define POSITION_XY_MEASUREMENT 1 +#define PRESSURE_MEASUREMENT 2 +#define PLATE_X_MEASUREMENT 3 +#define PLATE_Y_MEASUREMENT 4 +#define STANDBY_MODE 5 +#define NONTS_MODE 6 + +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; + 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 + +/* if we try to read faster, pressure reading becomes unreliable */ +#define SAMPLE_INTERVAL (HZ/50) + + +static void pcap_ts_mode(struct pcap_ts *pcap_ts, u_int32_t mode) +{ + u_int32_t tmp; + + pcap_ts->read_state = mode; + ezx_pcap_read(PCAP_REG_ADC1, &tmp); + tmp &= ~PCAP_ADC1_TS_M_MASK; + tmp |= ((mode << PCAP_ADC1_TS_M_SHIFT) & PCAP_ADC1_TS_M_MASK); + ezx_pcap_write(PCAP_REG_ADC1, tmp); +} + +/* issue a XY read command to the ADC of PCAP2. Well get an ADCDONE interrupt + * once the result of the conversion is available */ +static void pcap_ts_start_xy_read(struct pcap_ts *pcap_ts) +{ + u_int32_t tmp; + + ezx_pcap_read(PCAP_REG_ADC1, &tmp); + tmp &= ~(PCAP_BIT_ADC1_RAND | PCAP_ADC1_ADA1_MASK | + PCAP_ADC1_ADA2_MASK); + tmp |= (PCAP_BIT_ADC1_ADEN | PCAP_BIT_ADC1_AD_SEL1 | + PCAP_BIT_ADC1_AD_SEL2 | (5 << PCAP_ADC1_ADA1_SHIFT) | + (3 << PCAP_ADC1_ADA2_SHIFT)); + ezx_pcap_write(PCAP_REG_ADC1, tmp); + ezx_pcap_bit_set(PCAP_BIT_ADC2_ASC, 1); +} + +/* read the XY result from the ADC of PCAP2 */ +static void pcap_ts_get_xy_value(struct pcap_ts *pcap_ts) +{ + u_int32_t tmp; + + ezx_pcap_read(PCAP_REG_ADC2, &tmp); + + if (pcap_ts->read_state == POSITION_XY_MEASUREMENT) { + pcap_ts->x = (tmp & PCAP_ADC2_ADD1_MASK) >> + PCAP_ADC2_ADD1_SHIFT; + pcap_ts->y = (tmp & PCAP_ADC2_ADD2_MASK) >> + PCAP_ADC2_ADD2_SHIFT; + } else { + pcap_ts->pressure = (tmp & PCAP_ADC2_ADD2_MASK) >> + PCAP_ADC2_ADD2_SHIFT; + } +} + +/* PCAP2 interrupts us when ADC conversion result is available */ +static irqreturn_t pcap_ts_irq_xy(int irq, void *dev_id) +{ + struct pcap_ts *pcap_ts = dev_id; + + pcap_ts_get_xy_value(pcap_ts); + DEBUGP(KERN_DEBUG "%s X=%4d, Y=%4d Z=%4d ", + pcap_ts->read_state == POSITION_XY_MEASUREMENT ? "COORD" : + "PRESS", pcap_ts->x, pcap_ts->y, pcap_ts->pressure); + switch (pcap_ts->read_state) { + case PRESSURE_MEASUREMENT: + if (pcap_ts->pressure >= PRESSURE_MAX || + pcap_ts->pressure <= PRESSURE_MIN ) { + /* pen has been released (or cant read pressure - WM)*/ + DEBUGP("UP\n"); + /* do nothing */ + } else { + /* pen has been touched down */ + DEBUGP("DOWN\n"); + input_report_key(pcap_ts->input, BTN_TOUCH, 1); + input_report_abs(pcap_ts->input, ABS_PRESSURE, pcap_ts->pressure); + } + /* switch state machine into coordinate read mode */ + pcap_ts_mode(pcap_ts, POSITION_XY_MEASUREMENT); + pcap_ts_start_xy_read(pcap_ts); + break; + case POSITION_XY_MEASUREMENT: + 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) { + /* pen has been released */ + DEBUGP("UP END\n"); + + input_report_key(pcap_ts->input, BTN_TOUCH, 0); + input_report_abs(pcap_ts->input, ABS_PRESSURE, 0); + + /* no need for timer, we'll get interrupted with + * next touch down event */ + del_timer(&pcap_ts->timer); + + /* ask PCAP2 to interrupt us if touch event happens + * again */ + pcap_ts_mode(pcap_ts, STANDBY_MODE); + enable_irq(pcap_ts->irq_touch); + } else { + DEBUGP("DOWN\n"); + input_report_abs(pcap_ts->input, ABS_X, pcap_ts->x); + input_report_abs(pcap_ts->input, ABS_Y, pcap_ts->y); + + /* switch back to pressure read mode */ + pcap_ts_mode(pcap_ts, PRESSURE_MEASUREMENT); + mod_timer(&pcap_ts->timer, jiffies + SAMPLE_INTERVAL); + } + input_sync(pcap_ts->input); + break; + default: + DEBUGP("ERROR\n"); + break; + } + return IRQ_HANDLED; +} + +/* PCAP2 interrupts us if the pen touches down (interrupts also on pen up - WM)*/ +static irqreturn_t pcap_ts_irq_touch(int irq, void *dev_id) +{ + struct pcap_ts *pcap_ts = dev_id; + /* 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 */ + disable_irq(pcap_ts->irq_touch); + + DEBUGP("touched!!\n"); + pcap_ts_mode(pcap_ts, PRESSURE_MEASUREMENT); + 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; + + 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; + } + + ezx_pcap_bit_set(PCAP_BIT_ADC1_TS_REFENB, 0); + pcap_ts_mode(pcap_ts, STANDBY_MODE); + + err = request_irq(pcap_ts->irq_xy, pcap_ts_irq_xy, SA_INTERRUPT, + "pcap-ts X/Y", 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-ts 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; + 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); + + input_dev->name = "pcap-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 int ezxts_suspend(struct platform_device *dev, pm_message_t state) +{ + ezx_pcap_bit_set(PCAP_BIT_ADC1_TS_REF_LOWPWR, 1); + return 0; +} + +static int ezxts_resume(struct platform_device *dev) +{ + ezx_pcap_bit_set(PCAP_BIT_ADC1_TS_REF_LOWPWR, 0); + /* just in case we suspend with TSI masked. */ +// ezx_pcap_bit_set(PCAP_BIT_MSR_TSM, 0); + return 0; +} + + +static struct platform_driver ezxts_driver = { + .probe = ezxts_probe, + .remove = ezxts_remove, + .suspend = ezxts_suspend, + .resume = ezxts_resume, + .driver = { + .name = "pcap-ts", + .owner = THIS_MODULE, + }, +}; + +static int __init 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"); Index: linux-2.6.21/drivers/input/touchscreen/Makefile =================================================================== --- linux-2.6.21.orig/drivers/input/touchscreen/Makefile 2007-08-31 22:27:48.000000000 -0300 +++ linux-2.6.21/drivers/input/touchscreen/Makefile 2007-08-31 23:06:39.000000000 -0300 @@ -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