Hi! After a lot of struggle, I've finally managed to get my new PCAP2 touchscreen driver for the OpenEZX (http://www.openezx.org/) project into a somewhat working state. This means all the hardware-related bits such as ADC reading and IRQ handling works, and it reports some values to the upper layers. It uses the standard input core api and should thus theoretically behave like any other touchscreen. Since I've never even owned a linux-driven touchscreen before, let aside written a ts driver (though plenty of other drivers) yet, there are some questions that maybe somebody on this list can reply to. 0) What kind of X/Y/Pressure values should I return? Are they supposed to be scaled to the X/Y resolution of the LCD? As of now, I return X_ABS, Y_ABS and PRESSURE values between 0 and 1000 (each). However, the coordinates are actually inverted, so '0,0' corresponds to the lower right corner of the screen, whereas '1000,1000' is the upper left corner. Shall I invert them (i.e. return 1000-coord')? 1) where does touchscreen calibration happen? The EZX phones (like many other devices, I believe) only contain resistive touchscreens that appear pretty uncalibrated. I'm sure the factory-set calibration data must be stored somewhere in flash, but it's definitely handled in the proprietary EZX userland, since their old kernel driver doesn't have any calibration related bits. 2) what about the 'button' event. In addition to the pressure (which is about 300 for regular stylus use, > 400 if you press hard and > 600 if you use yourfinger), some existing TS drivers return a button press. Is it up to me to decide after which pressure level to consider the button to be pressed / released? Looking forward to your replies. I've attached the current status of pcap_ts.c to this file. It will use a number of symbols only present in the -ezx5 kernel. This is not intended for any kind of mainlinie inclusion, reports on coding style or the like. All will happen at its time. -- - Harald Welte <[email protected]> http://gnumonks.org/ ============================================================================ We all know Linux is great...it does infinite loops in 5 seconds. -- Linus
Index: linux-2.6.16.13-ezx3/drivers/input/touchscreen/Kconfig =================================================================== --- linux-2.6.16.13-ezx3.orig/drivers/input/touchscreen/Kconfig 2006-05-17 23:34:47.000000000 +0200 +++ linux-2.6.16.13-ezx3/drivers/input/touchscreen/Kconfig 2006-05-17 23:39:26.000000000 +0200 @@ -108,4 +108,16 @@ To compile this driver as a module, choose M here: the module will be called hp680_ts_input. +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.16.13-ezx3/drivers/input/touchscreen/Makefile =================================================================== --- linux-2.6.16.13-ezx3.orig/drivers/input/touchscreen/Makefile 2006-05-17 23:34:47.000000000 +0200 +++ linux-2.6.16.13-ezx3/drivers/input/touchscreen/Makefile 2006-05-17 23:39:26.000000000 +0200 @@ -12,3 +12,4 @@ obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o +obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o Index: linux-2.6.16.13-ezx3/drivers/input/touchscreen/pcap_ts.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.16.13-ezx3/drivers/input/touchscreen/pcap_ts.c 2006-05-18 00:04:02.000000000 +0200 @@ -0,0 +1,333 @@ +/* + * pcap_ts.c - Touchscreen driver for Motorola PCAP2 based touchscreen as found + * in the EZX phone platform. + * + * Copyright (C) 2006 Harald Welte <[email protected]> + * + * 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 <linux/module.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/pm.h> +#include <linux/timer.h> +#include <linux/config.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/input.h> + +#include <asm/arch/hardware.h> +#include <asm/arch/pxa-regs.h> + +#include "../../misc/ezx/ssp_pcap.h" + +#if 0 +#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; + + int read_state; + int x, y, pressure; + int x_pre, y_pre; +}; + +#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); + + 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(void) +{ + int ret; + u_int32_t tmp; + + 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; + + 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 = (tmp & SSP_PCAP_ADD2_VALUE_MASK) + >>SSP_PCAP_ADD2_VALUE_SHIFT; + + /* we need to switch back to standby mode, because PCAP only generates + * touch screen interrupts when in standby mode */ + pcap_ts_mode(PCAP_TS_STANDBY_MODE); + + return 0; +} + +static irqreturn_t pcap_ts_irq_timer(int irq, void *dev_id, struct pt_regs *regs) +{ + DEBUGP("entered\n"); + pcap_ts_mode(PCAP_TS_POSITION_XY_MEASUREMENT); + pcap_ts_start_xy_read(); + + return IRQ_HANDLED; +} + +/* 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) { + DEBUGP("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) { + if (pcap_ts->pressure >= PRESSURE_MAX || + pcap_ts->pressure <= PRESSURE_MIN) { + /* pen has been released */ + input_report_abs(pcap_ts->input, ABS_PRESSURE, pcap_ts->y); + input_report_key(pcap_ts->input, BTN_TOUCH, 0); + + pcap_ts->x = pcap_ts->y = 0; + + /* ask PCAP2 to interrupt us if touch event happens again */ + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_MSR_TSM, 0); + } else { + /* pen has been touched down */ + + /* FIXME: can we report this now, even that we don't + * know oure coordinates yet? */ + input_report_abs(pcap_ts->input, ABS_PRESSURE, pcap_ts->y); + input_report_key(pcap_ts->input, BTN_TOUCH, 1); + + /* 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(); + } + } 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"); + pcap_ts->x = pcap_ts->y = 0; + + /* switch back to pressure read mode */ + pcap_ts->read_state = PRESSURE; + pcap_ts_mode(PCAP_TS_STANDBY_MODE); + + /* ask PCAP2 to interrupt us if touch event happens again */ + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_MSR_TSM, 0); + } else { + input_report_abs(pcap_ts->input, ABS_X, pcap_ts->x); + input_report_abs(pcap_ts->input, ABS_Y, pcap_ts->y); + } + } + + 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"); + + pcap_ts_mode(PCAP_TS_PRESSURE_MEASUREMENT); + pcap_ts->read_state = PRESSURE; + pcap_ts_start_xy_read(); + pcap_ts->x_pre = 0; + pcap_ts->y_pre = 0; + + return IRQ_HANDLED; +} + +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; + + 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); + + pcap_ts->read_state = PRESSURE; + + return 0; + +fail_touch: + free_irq(pcap_ts->irq_touch, pcap_ts); +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); + + 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 <[email protected]>"); +MODULE_LICENSE("GPL");
Attachment:
pgpEb9gNRovnX.pgp
Description: PGP signature
- Follow-Ups:
- Re: How should Touchscreen Input Drives behave (OpenEZX pcap_ts)
- From: Andy Ross <[email protected]>
- Re: How should Touchscreen Input Drives behave (OpenEZX pcap_ts)
- From: Florent Thiery <[email protected]>
- Re: How should Touchscreen Input Drives behave (OpenEZX pcap_ts)
- From: Richard Purdie <[email protected]>
- Re: How should Touchscreen Input Drives behave (OpenEZX pcap_ts)
- Prev by Date: Re: [openib-general] Re: [PATCH 35 of 53] ipath - some interrelated stability and cleanliness fixes
- Next by Date: kobject_add failed for vcs1 with -EEXIST, don't try to register things with the same name in the same directory.
- Previous by thread: Where is Hariprasad Nellitheertha?
- Next by thread: Re: How should Touchscreen Input Drives behave (OpenEZX pcap_ts)
- Index(es):