Index: linux-2.6.21/drivers/input/touchscreen/Kconfig =================================================================== --- linux-2.6.21.orig/drivers/input/touchscreen/Kconfig 2007-06-02 20:17:58.000000000 -0300 +++ linux-2.6.21/drivers/input/touchscreen/Kconfig 2007-06-02 20:18:40.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-06-02 20:19:39.000000000 -0300 @@ -0,0 +1,372 @@ +/* + * 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 PRESSURE 1 +#define COORDINATE 2 +#define STANDBY 3 + +extern int ezx_pcap_read(u_int8_t, u_int32_t *); +extern int ezx_pcap_write(u_int8_t, u_int32_t); +extern int ezx_pcap_bit_set(u_int32_t, u_int8_t); + +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(u_int32_t mode) +{ + u_int32_t tmp; + + ezx_pcap_read(SSP_PCAP_ADJ_ADC1_REGISTER, &tmp); + tmp &= ~SSP_PCAP_TOUCH_PANEL_POSITION_DETECT_MODE_MASK; + tmp |= mode; + ezx_pcap_write(SSP_PCAP_ADJ_ADC1_REGISTER, tmp); +} + +/* issue a XY read command to the ADC of PCAP2. Well get an ADCDONE2 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(SSP_PCAP_ADJ_ADC1_REGISTER, &tmp); + tmp &= SSP_PCAP_ADC_START_VALUE_SET_MASK; + tmp |= SSP_PCAP_ADC_START_VALUE; + ezx_pcap_write(SSP_PCAP_ADJ_ADC1_REGISTER, tmp); + ezx_pcap_bit_set(SSP_PCAP_ADJ_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(SSP_PCAP_ADJ_ADC2_REGISTER, &tmp); + + if (pcap_ts->read_state == COORDINATE && !(tmp & 0x00400000)) { + 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 = (tmp & SSP_PCAP_ADD2_VALUE_MASK) + >>SSP_PCAP_ADD2_VALUE_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 == COORDINATE ? "COORD" : "PRESS", + pcap_ts->x, pcap_ts->y, pcap_ts->pressure); + + switch (pcap_ts->read_state) { + case PRESSURE: + 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->read_state = COORDINATE; + pcap_ts_mode(PCAP_TS_POSITION_XY_MEASUREMENT); + pcap_ts_start_xy_read(pcap_ts); + break; + case COORDINATE: + 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->read_state = STANDBY; + pcap_ts_mode(PCAP_TS_STANDBY_MODE); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_MSR_TSM, 0); + } 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->read_state = PRESSURE; + 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 */ + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_MSR_TSM, 1); + + DEBUGP("touched!!\n"); + 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; + + pcap_ts_start_xy_read(pcap_ts); +} + +static int __init ezxts_probe(struct platform_device *pdev) +{ + int ret; + u_int32_t tmp; + 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; + } + + // Some initialization before done in ssp_pcap_open() + ezx_pcap_bit_set( SSP_PCAP_ADJ_BIT_ADC1_TS_REFENB, 0); + // ack interrupts + ezx_pcap_bit_set( SSP_PCAP_ADJ_BIT_ISR_ADCDONE2I, 1); + ezx_pcap_bit_set( SSP_PCAP_ADJ_BIT_ISR_TSI, 1); + // unmask interrupts + // ezx_pcap_bit_set( SSP_PCAP_ADJ_BIT_MSR_TSM, 0); + ezx_pcap_bit_set( SSP_PCAP_ADJ_BIT_MSR_ADCDONE2M, 0); + // set adc bits? FIXME I dont think its necessary - WM + ezx_pcap_bit_set( SSP_PCAP_ADJ_BIT_ADC2_ADINC1, 0); + ezx_pcap_bit_set( SSP_PCAP_ADJ_BIT_ADC2_ADINC2, 0); + ezx_pcap_bit_set( SSP_PCAP_ADJ_BIT_ADC1_ATO0, 0); + ezx_pcap_bit_set( SSP_PCAP_ADJ_BIT_ADC1_ATO1, 0); + ezx_pcap_bit_set( SSP_PCAP_ADJ_BIT_ADC1_ATO2, 0); + ezx_pcap_bit_set( SSP_PCAP_ADJ_BIT_ADC1_ATO3, 0); + ezx_pcap_bit_set( SSP_PCAP_ADJ_BIT_ADC1_ATOX, 0); + ezx_pcap_bit_set( SSP_PCAP_ADJ_BIT_ADC1_MTR1, 0); + ezx_pcap_bit_set( SSP_PCAP_ADJ_BIT_ADC1_MTR2, 0); + + 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 |= PCAP_TS_STANDBY_MODE; + + ret = ezx_pcap_write(SSP_PCAP_ADJ_ADC1_REGISTER, tmp); + if (ret < 0) + return ret; + + 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); + + pcap_ts->read_state = STANDBY; + pcap_ts_mode(PCAP_TS_STANDBY_MODE); + + /* enable pressure interrupt */ + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_MSR_TSM, 0); + + 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(SSP_PCAP_ADJ_BIT_ADC1_TS_REF_LOWPWR, 1); + return 0; +} + +static int ezxts_resume(struct platform_device *dev) +{ + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_ADC1_TS_REF_LOWPWR, 0); + /* just in case we suspend with TSI masked. */ + ezx_pcap_bit_set(SSP_PCAP_ADJ_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-06-02 20:17:58.000000000 -0300 +++ linux-2.6.21/drivers/input/touchscreen/Makefile 2007-06-02 20:18:40.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