From 65a2d2258e0f29371606aa0f7f4258e618ecebe8 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Thu, 21 Dec 2006 00:42:55 -0500 Subject: Input: pc110pad - return proper error The driver should return -ENODEV rather than -ENOENT when it detects a PCI device in the box. Signed-off-by: Akinobu Mita Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/pc110pad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c index f155c1fea04e..05d992e514f0 100644 --- a/drivers/input/mouse/pc110pad.c +++ b/drivers/input/mouse/pc110pad.c @@ -113,7 +113,7 @@ static int __init pc110pad_init(void) dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); if (dev) { pci_dev_put(dev); - return -ENOENT; + return -ENODEV; } if (!request_region(pc110pad_io, 4, "pc110pad")) { -- cgit v1.2.2 From 19f3c3e37314a234998fd75f5ea9388dfb6ab00a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 18 Jan 2007 00:42:31 -0500 Subject: Input: i8042 - really suppress ACK/NAK during panic blink On some boxes panic blink procedure manages to send both bytes to keyboard contoller before getting first ACK so we need to make i8042_suppress_kbd_ack a counter instead of boolean. Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index debe9445488c..1364c7964db4 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -371,7 +371,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) if (unlikely(i8042_suppress_kbd_ack)) if (port_no == I8042_KBD_PORT_NO && (data == 0xfa || data == 0xfe)) { - i8042_suppress_kbd_ack = 0; + i8042_suppress_kbd_ack--; goto out; } @@ -838,13 +838,14 @@ static long i8042_panic_blink(long count) led ^= 0x01 | 0x04; while (i8042_read_status() & I8042_STR_IBF) DELAY; - i8042_suppress_kbd_ack = 1; + dbg("%02x -> i8042 (panic blink)", 0xed); + i8042_suppress_kbd_ack = 2; i8042_write_data(0xed); /* set leds */ DELAY; while (i8042_read_status() & I8042_STR_IBF) DELAY; DELAY; - i8042_suppress_kbd_ack = 1; + dbg("%02x -> i8042 (panic blink)", led); i8042_write_data(led); DELAY; last_blink = count; -- cgit v1.2.2 From 78a56aab11234e53b7e94e5a255cc3d27ab0a62b Mon Sep 17 00:00:00 2001 From: Phil Blundell Date: Thu, 18 Jan 2007 00:44:09 -0500 Subject: Input: gpio-keys - keyboard driver for GPIO buttons This is an interrupt-driven keyboard driver for simple buttons connected directly to CPU GPIO lines of embedded ARM systems. It supports pxa architectures and is used by a number of PDAs and PocketPC phones in the handhelds.org kernel. Support for other architectures, such as sa11xx and sc2410, will be added once generic GPIO API is available. Signed-off-by: Paul Sokolovsky Signed-off-by: Philipp Zabel Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 19 ++++- drivers/input/keyboard/Makefile | 5 +- drivers/input/keyboard/gpio_keys.c | 147 +++++++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 5 deletions(-) create mode 100644 drivers/input/keyboard/gpio_keys.c (limited to 'drivers/input') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 049f2f544e75..1b81a72e19d9 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -135,12 +135,12 @@ config KEYBOARD_STOWAWAY config KEYBOARD_CORGI tristate "Corgi keyboard" depends on PXA_SHARPSL - default y + default y help - Say Y here to enable the keyboard on the Sharp Zaurus SL-C7xx + Say Y here to enable the keyboard on the Sharp Zaurus SL-C7xx series of PDAs. - To compile this driver as a module, choose M here: the + To compile this driver as a module, choose M here: the module will be called corgikbd. config KEYBOARD_SPITZ @@ -214,4 +214,17 @@ config KEYBOARD_AAED2000 To compile this driver as a module, choose M here: the module will be called aaed2000_kbd. +config KEYBOARD_GPIO + tristate "Buttons on CPU GPIOs (PXA)" + depends on ARCH_PXA + help + This driver implements support for buttons connected + directly to GPIO pins of PXA CPUs. + + Say Y here if your device has buttons connected + directly to GPIO pins of the CPU. + + To compile this driver as a module, choose M here: the + module will be called gpio-keys. + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 568797907347..586a0fe53be6 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o -obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o -obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o +obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o +obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o +obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c new file mode 100644 index 000000000000..3a8f1b427a7f --- /dev/null +++ b/drivers/input/keyboard/gpio_keys.c @@ -0,0 +1,147 @@ +/* + * Driver for keys on GPIO lines capable of generating interrupts. + * + * Copyright 2005 Phil Blundell + * + * 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. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +static irqreturn_t gpio_keys_isr(int irq, void *dev_id) +{ + int i; + struct platform_device *pdev = dev_id; + struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; + struct input_dev *input = platform_get_drvdata(pdev); + + for (i = 0; i < pdata->nbuttons; i++) { + int gpio = pdata->buttons[i].gpio; + if (irq == IRQ_GPIO(gpio)) { + int state = ((GPLR(gpio) & GPIO_bit(gpio)) ? 1 : 0) ^ (pdata->buttons[i].active_low); + + input_report_key(input, pdata->buttons[i].keycode, state); + input_sync(input); + } + } + + return IRQ_HANDLED; +} + +static int __devinit gpio_keys_probe(struct platform_device *pdev) +{ + struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; + struct input_dev *input; + int i, error; + + input = input_allocate_device(); + if (!input) + return -ENOMEM; + + platform_set_drvdata(pdev, input); + + input->evbit[0] = BIT(EV_KEY); + + input->name = pdev->name; + input->phys = "gpio-keys/input0"; + input->cdev.dev = &pdev->dev; + input->private = pdata; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + for (i = 0; i < pdata->nbuttons; i++) { + int code = pdata->buttons[i].keycode; + int irq = IRQ_GPIO(pdata->buttons[i].gpio); + + set_irq_type(irq, IRQ_TYPE_EDGE_BOTH); + error = request_irq(irq, gpio_keys_isr, SA_SAMPLE_RANDOM, + pdata->buttons[i].desc ? pdata->buttons[i].desc : "gpio_keys", + pdev); + if (error) { + printk(KERN_ERR "gpio-keys: unable to claim irq %d; error %d\n", irq, ret); + goto fail; + } + set_bit(code, input->keybit); + } + + error = input_register_device(input); + if (error) { + printk(KERN_ERR "Unable to register gpio-keys input device\n"); + goto fail; + } + + return 0; + + fail: + for (i = i - 1; i >= 0; i--) + free_irq(IRQ_GPIO(pdata->buttons[i].gpio), pdev); + + input_free_device(input); + + return error; +} + +static int __devexit gpio_keys_remove(struct platform_device *pdev) +{ + struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; + struct input_dev *input = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < pdata->nbuttons; i++) { + int irq = IRQ_GPIO(pdata->buttons[i].gpio); + free_irq(irq, pdev); + } + + input_unregister_device(input); + + return 0; +} + +struct platform_driver gpio_keys_device_driver = { + .probe = gpio_keys_probe, + .remove = __devexit_p(gpio_keys_remove), + .driver = { + .name = "gpio-keys", + } +}; + +static int __init gpio_keys_init(void) +{ + return platform_driver_register(&gpio_keys_device_driver); +} + +static void __exit gpio_keys_exit(void) +{ + platform_driver_unregister(&gpio_keys_device_driver); +} + +module_init(gpio_keys_init); +module_exit(gpio_keys_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Phil Blundell "); +MODULE_DESCRIPTION("Keyboard driver for CPU GPIOs"); -- cgit v1.2.2 From da970e69efb9fd0be0c23ace5bde42d4caf17b40 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 18 Jan 2007 00:44:41 -0500 Subject: Input: ads7846 - pluggable filtering logic Some LCDs like the LS041Y3 require a customized filtering logic for reliable readings, so make the filtering function replacable through platform specific hooks. Signed-off-by: Imre Deak Signed-off-by: Juha Yrjola Signed-off-by: David Brownell Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 135 +++++++++++++++++++++++++----------- 1 file changed, 95 insertions(+), 40 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index c6164b6f476a..03e527c16c23 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -63,11 +63,11 @@ struct ts_event { /* For portability, we can't read 12 bit values using SPI (which * would make the controller deliver them as native byteorder u16 * with msbs zeroed). Instead, we read them as two 8-bit values, - * which need byteswapping then range adjustment. + * *** WHICH NEED BYTESWAPPING *** and range adjustment. */ - __be16 x; - __be16 y; - __be16 z1, z2; + u16 x; + u16 y; + u16 z1, z2; int ignore; }; @@ -106,6 +106,9 @@ struct ads7846 { unsigned irq_disabled:1; /* P: lock */ unsigned disabled:1; + int (*filter)(void *data, int data_idx, int *val); + void *filter_data; + void (*filter_cleanup)(void *data); int (*get_pendown_state)(void); }; @@ -379,13 +382,13 @@ static void ads7846_rx(void *ads) u16 x, y, z1, z2; unsigned long flags; - /* adjust: on-wire is a must-ignore bit, a BE12 value, then padding; - * built from two 8 bit values written msb-first. + /* ads7846_rx_val() did in-place conversion (including byteswap) from + * on-the-wire format as part of debouncing to get stable readings. */ - x = (be16_to_cpu(ts->tc.x) >> 3) & 0x0fff; - y = (be16_to_cpu(ts->tc.y) >> 3) & 0x0fff; - z1 = (be16_to_cpu(ts->tc.z1) >> 3) & 0x0fff; - z2 = (be16_to_cpu(ts->tc.z2) >> 3) & 0x0fff; + x = ts->tc.x; + y = ts->tc.y; + z1 = ts->tc.z1; + z2 = ts->tc.z2; /* range filtering */ if (x == MAX_12BIT) @@ -453,50 +456,85 @@ static void ads7846_rx(void *ads) spin_unlock_irqrestore(&ts->lock, flags); } -static void ads7846_debounce(void *ads) +static int ads7846_debounce(void *ads, int data_idx, int *val) { struct ads7846 *ts = ads; - struct spi_message *m; - struct spi_transfer *t; - int val; - int status; - m = &ts->msg[ts->msg_idx]; - t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); - val = (be16_to_cpu(*(__be16 *)t->rx_buf) >> 3) & 0x0fff; - if (!ts->read_cnt || (abs(ts->last_read - val) > ts->debounce_tol)) { + if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) { + /* Start over collecting consistent readings. */ + ts->read_rep = 0; /* Repeat it, if this was the first read or the read * wasn't consistent enough. */ if (ts->read_cnt < ts->debounce_max) { - ts->last_read = val; + ts->last_read = *val; ts->read_cnt++; + return ADS7846_FILTER_REPEAT; } else { /* Maximum number of debouncing reached and still * not enough number of consistent readings. Abort * the whole sample, repeat it in the next sampling * period. */ - ts->tc.ignore = 1; ts->read_cnt = 0; - /* Last message will contain ads7846_rx() as the - * completion function. - */ - m = ts->last_msg; + return ADS7846_FILTER_IGNORE; } - /* Start over collecting consistent readings. */ - ts->read_rep = 0; } else { if (++ts->read_rep > ts->debounce_rep) { /* Got a good reading for this coordinate, * go for the next one. */ - ts->tc.ignore = 0; - ts->msg_idx++; ts->read_cnt = 0; ts->read_rep = 0; - m++; - } else + return ADS7846_FILTER_OK; + } else { /* Read more values that are consistent. */ ts->read_cnt++; + return ADS7846_FILTER_REPEAT; + } + } +} + +static int ads7846_no_filter(void *ads, int data_idx, int *val) +{ + return ADS7846_FILTER_OK; +} + +static void ads7846_rx_val(void *ads) +{ + struct ads7846 *ts = ads; + struct spi_message *m; + struct spi_transfer *t; + u16 *rx_val; + int val; + int action; + int status; + + m = &ts->msg[ts->msg_idx]; + t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); + rx_val = t->rx_buf; + + /* adjust: on-wire is a must-ignore bit, a BE12 value, then padding; + * built from two 8 bit values written msb-first. + */ + val = be16_to_cpu(*rx_val) >> 3; + + action = ts->filter(ts->filter_data, ts->msg_idx, &val); + switch (action) { + case ADS7846_FILTER_REPEAT: + break; + case ADS7846_FILTER_IGNORE: + ts->tc.ignore = 1; + /* Last message will contain ads7846_rx() as the + * completion function. + */ + m = ts->last_msg; + break; + case ADS7846_FILTER_OK: + *rx_val = val; + ts->tc.ignore = 0; + m = &ts->msg[++ts->msg_idx]; + break; + default: + BUG(); } status = spi_async(ts->spi, m); if (status) @@ -689,14 +727,25 @@ static int __devinit ads7846_probe(struct spi_device *spi) ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; ts->pressure_max = pdata->pressure_max ? : ~0; - if (pdata->debounce_max) { + + if (pdata->filter != NULL) { + if (pdata->filter_init != NULL) { + err = pdata->filter_init(pdata, &ts->filter_data); + if (err < 0) + goto err_free_mem; + } + ts->filter = pdata->filter; + ts->filter_cleanup = pdata->filter_cleanup; + } else if (pdata->debounce_max) { ts->debounce_max = pdata->debounce_max; + if (ts->debounce_max < 2) + ts->debounce_max = 2; ts->debounce_tol = pdata->debounce_tol; ts->debounce_rep = pdata->debounce_rep; - if (ts->debounce_rep > ts->debounce_max + 1) - ts->debounce_rep = ts->debounce_max - 1; + ts->filter = ads7846_debounce; + ts->filter_data = ts; } else - ts->debounce_tol = ~0; + ts->filter = ads7846_no_filter; ts->get_pendown_state = pdata->get_pendown_state; snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id); @@ -737,7 +786,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) x->len = 2; spi_message_add_tail(x, m); - m->complete = ads7846_debounce; + m->complete = ads7846_rx_val; m->context = ts; m++; @@ -755,7 +804,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) x->len = 2; spi_message_add_tail(x, m); - m->complete = ads7846_debounce; + m->complete = ads7846_rx_val; m->context = ts; /* turn y+ off, x- on; we'll use formula #2 */ @@ -774,7 +823,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) x->len = 2; spi_message_add_tail(x, m); - m->complete = ads7846_debounce; + m->complete = ads7846_rx_val; m->context = ts; m++; @@ -791,7 +840,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) x->len = 2; spi_message_add_tail(x, m); - m->complete = ads7846_debounce; + m->complete = ads7846_rx_val; m->context = ts; } @@ -820,7 +869,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi->dev.driver->name, ts)) { dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); err = -EBUSY; - goto err_free_mem; + goto err_cleanup_filter; } dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq); @@ -856,6 +905,9 @@ static int __devinit ads7846_probe(struct spi_device *spi) sysfs_remove_group(&spi->dev.kobj, ts->attr_group); err_free_irq: free_irq(spi->irq, ts); + err_cleanup_filter: + if (ts->filter_cleanup) + ts->filter_cleanup(ts->filter_data); err_free_mem: input_free_device(input_dev); kfree(ts); @@ -876,6 +928,9 @@ static int __devexit ads7846_remove(struct spi_device *spi) /* suspend left the IRQ disabled */ enable_irq(ts->spi->irq); + if (ts->filter_cleanup) + ts->filter_cleanup(ts->filter_data); + kfree(ts); dev_dbg(&spi->dev, "unregistered touchscreen\n"); -- cgit v1.2.2 From de2defd96d7d92fe8b5f9cf2bfd385d8d4819923 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 18 Jan 2007 00:45:21 -0500 Subject: Input: ads7846 - optionally leave Vref on during differential measurements On some LCDs leaving the Vref on provides much better readings. Signed-off-by: Jarkko Oikarinen Signed-off-by: Imre Deak Signed-off-by: Juha Yrjola Signed-off-by: David Brownell Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 03e527c16c23..c34e59b720a6 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -145,15 +145,16 @@ struct ads7846 { #define MAX_12BIT ((1<<12)-1) /* leave ADC powered up (disables penirq) between differential samples */ -#define READ_12BIT_DFR(x) (ADS_START | ADS_A2A1A0_d_ ## x \ - | ADS_12_BIT | ADS_DFR) +#define READ_12BIT_DFR(x, adc, vref) (ADS_START | ADS_A2A1A0_d_ ## x \ + | ADS_12_BIT | ADS_DFR | \ + (adc ? ADS_PD10_ADC_ON : 0) | (vref ? ADS_PD10_REF_ON : 0)) -#define READ_Y (READ_12BIT_DFR(y) | ADS_PD10_ADC_ON) -#define READ_Z1 (READ_12BIT_DFR(z1) | ADS_PD10_ADC_ON) -#define READ_Z2 (READ_12BIT_DFR(z2) | ADS_PD10_ADC_ON) +#define READ_Y(vref) (READ_12BIT_DFR(y, 1, vref)) +#define READ_Z1(vref) (READ_12BIT_DFR(z1, 1, vref)) +#define READ_Z2(vref) (READ_12BIT_DFR(z2, 1, vref)) -#define READ_X (READ_12BIT_DFR(x) | ADS_PD10_ADC_ON) -#define PWRDOWN (READ_12BIT_DFR(y) | ADS_PD10_PDOWN) /* LAST */ +#define READ_X(vref) (READ_12BIT_DFR(x, 1, vref)) +#define PWRDOWN (READ_12BIT_DFR(y, 0, 0)) /* LAST */ /* single-ended samples need to first power up reference voltage; * we leave both ADC and VREF powered @@ -161,8 +162,8 @@ struct ads7846 { #define READ_12BIT_SER(x) (ADS_START | ADS_A2A1A0_ ## x \ | ADS_12_BIT | ADS_SER) -#define REF_ON (READ_12BIT_DFR(x) | ADS_PD10_ALL_ON) -#define REF_OFF (READ_12BIT_DFR(y) | ADS_PD10_PDOWN) +#define REF_ON (READ_12BIT_DFR(x, 1, 1)) +#define REF_OFF (READ_12BIT_DFR(y, 0, 0)) /*--------------------------------------------------------------------------*/ @@ -670,6 +671,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) struct ads7846_platform_data *pdata = spi->dev.platform_data; struct spi_message *m; struct spi_transfer *x; + int vref; int err; if (!spi->irq) { @@ -767,6 +769,8 @@ static int __devinit ads7846_probe(struct spi_device *spi) input_set_abs_params(input_dev, ABS_PRESSURE, pdata->pressure_min, pdata->pressure_max, 0, 0); + vref = pdata->keep_vref_on; + /* set up the transfers to read touchscreen state; this assumes we * use formula #2 for pressure, not #3. */ @@ -776,7 +780,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_init(m); /* y- still on; turn on only y+ (and ADC) */ - ts->read_y = READ_Y; + ts->read_y = READ_Y(vref); x->tx_buf = &ts->read_y; x->len = 1; spi_message_add_tail(x, m); @@ -794,7 +798,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) /* turn y- off, x+ on, then leave in lowpower */ x++; - ts->read_x = READ_X; + ts->read_x = READ_X(vref); x->tx_buf = &ts->read_x; x->len = 1; spi_message_add_tail(x, m); @@ -813,7 +817,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_init(m); x++; - ts->read_z1 = READ_Z1; + ts->read_z1 = READ_Z1(vref); x->tx_buf = &ts->read_z1; x->len = 1; spi_message_add_tail(x, m); @@ -830,7 +834,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_init(m); x++; - ts->read_z2 = READ_Z2; + ts->read_z2 = READ_Z2(vref); x->tx_buf = &ts->read_z2; x->len = 1; spi_message_add_tail(x, m); -- cgit v1.2.2 From 1936d590a9b72ff6a7a0c826bc613e4757cde1c9 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 18 Jan 2007 00:45:31 -0500 Subject: Input: ads7846 - switch to using hrtimer Use hrtimer instead of the normal timer, since it provides better sampling resolution. This will: - avoid a problem where we have a 1 jiffy poll period and dynamic tick on - utilize high resolution HW clocks when they are added to the hrtimer framework Signed-off-by: Imre Deak Signed-off-by: Juha Yrjola Signed-off-by: David Brownell Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index c34e59b720a6..d983cc51ad32 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -54,7 +54,8 @@ * files. */ -#define TS_POLL_PERIOD msecs_to_jiffies(10) +#define TS_POLL_DELAY (1 * 1000000) /* ns delay before the first sample */ +#define TS_POLL_PERIOD (5 * 1000000) /* ns delay between samples */ /* this driver doesn't aim at the peak continuous sample rate */ #define SAMPLE_BITS (8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */) @@ -99,7 +100,7 @@ struct ads7846 { u16 debounce_rep; spinlock_t lock; - struct timer_list timer; /* P: lock */ + struct hrtimer timer; unsigned pendown:1; /* P: lock */ unsigned pending:1; /* P: lock */ // FIXME remove "irq_disabled" @@ -407,10 +408,12 @@ static void ads7846_rx(void *ads) Rt = 0; /* Sample found inconsistent by debouncing or pressure is beyond - * the maximum. Don't report it to user space, repeat at least - * once more the measurement */ + * the maximum. Don't report it to user space, repeat at least + * once more the measurement + */ if (ts->tc.ignore || Rt > ts->pressure_max) { - mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD); + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), + HRTIMER_REL); return; } @@ -452,7 +455,7 @@ static void ads7846_rx(void *ads) spin_lock_irqsave(&ts->lock, flags); ts->pendown = (Rt != 0); - mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD); + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), HRTIMER_REL); spin_unlock_irqrestore(&ts->lock, flags); } @@ -543,9 +546,9 @@ static void ads7846_rx_val(void *ads) status); } -static void ads7846_timer(unsigned long handle) +static int ads7846_timer(struct hrtimer *handle) { - struct ads7846 *ts = (void *)handle; + struct ads7846 *ts = container_of(handle, struct ads7846, timer); int status = 0; spin_lock_irq(&ts->lock); @@ -567,6 +570,7 @@ static void ads7846_timer(unsigned long handle) } spin_unlock_irq(&ts->lock); + return HRTIMER_NORESTART; } static irqreturn_t ads7846_irq(int irq, void *handle) @@ -585,7 +589,8 @@ static irqreturn_t ads7846_irq(int irq, void *handle) ts->irq_disabled = 1; disable_irq(ts->spi->irq); ts->pending = 1; - mod_timer(&ts->timer, jiffies); + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY), + HRTIMER_REL); } } spin_unlock_irqrestore(&ts->lock, flags); @@ -719,8 +724,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) ts->spi = spi; ts->input = input_dev; - init_timer(&ts->timer); - ts->timer.data = (unsigned long) ts; + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_REL); ts->timer.function = ads7846_timer; spin_lock_init(&ts->lock); -- cgit v1.2.2 From 7937e86a70235e1584486654687dc9908a11e00a Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 18 Jan 2007 00:45:38 -0500 Subject: Input: ads7846 - select correct SPI mode Talk to ADS7846 chip using SPI mode 1, which is what the chip supports: writes on falling clock edge, reads on rising. Signed-off-by: Imre Deak Signed-off-by: Tony Lindgren Signed-off-by: David Brownell Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index d983cc51ad32..11979be33589 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -710,6 +710,10 @@ static int __devinit ads7846_probe(struct spi_device *spi) * may not. So we stick to very-portable 8 bit words, both RX and TX. */ spi->bits_per_word = 8; + spi->mode = SPI_MODE_1; + err = spi_setup(spi); + if (err < 0) + return err; ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL); input_dev = input_allocate_device(); -- cgit v1.2.2 From 15e3589e59c35ed33823dda3d38ad171222b83b4 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 18 Jan 2007 00:45:43 -0500 Subject: Input: ads7846 - detect pen up from GPIO state We can't depend on the pressure value to determine when the pen was lifted, so use the GPIO line state instead. This also helps with chips (like ads7843) that don't have pressure sensors. Signed-off-by: Imre Deak Signed-off-by: Juha Yrjola Signed-off-by: David Brownell Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 80 +++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 39 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 11979be33589..b18c63a3f2ab 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -378,11 +378,8 @@ static struct attribute_group ads7845_attr_group = { static void ads7846_rx(void *ads) { struct ads7846 *ts = ads; - struct input_dev *input_dev = ts->input; unsigned Rt; - unsigned sync = 0; u16 x, y, z1, z2; - unsigned long flags; /* ads7846_rx_val() did in-place conversion (including byteswap) from * on-the-wire format as part of debouncing to get stable readings. @@ -396,7 +393,7 @@ static void ads7846_rx(void *ads) if (x == MAX_12BIT) x = 0; - if (likely(x && z1 && !device_suspended(&ts->spi->dev))) { + if (likely(x && z1)) { /* compute touch pressure resistance using equation #2 */ Rt = z2; Rt -= z1; @@ -412,52 +409,44 @@ static void ads7846_rx(void *ads) * once more the measurement */ if (ts->tc.ignore || Rt > ts->pressure_max) { +#ifdef VERBOSE + pr_debug("%s: ignored %d pressure %d\n", + ts->spi->dev.bus_id, ts->tc.ignore, Rt); +#endif hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), HRTIMER_REL); return; } - /* NOTE: "pendown" is inferred from pressure; we don't rely on - * being able to check nPENIRQ status, or "friendly" trigger modes - * (both-edges is much better than just-falling or low-level). - * - * REVISIT: some boards may require reading nPENIRQ; it's - * needed on 7843. and 7845 reads pressure differently... + /* NOTE: We can't rely on the pressure to determine the pen down + * state, even this controller has a pressure sensor. The pressure + * value can fluctuate for quite a while after lifting the pen and + * in some cases may not even settle at the expected value. * - * REVISIT: the touchscreen might not be connected; this code - * won't notice that, even if nPENIRQ never fires ... + * The only safe way to check for the pen up condition is in the + * timer by reading the pen signal state (it's a GPIO _and_ IRQ). */ - if (!ts->pendown && Rt != 0) { - input_report_key(input_dev, BTN_TOUCH, 1); - sync = 1; - } else if (ts->pendown && Rt == 0) { - input_report_key(input_dev, BTN_TOUCH, 0); - sync = 1; - } - if (Rt) { - input_report_abs(input_dev, ABS_X, x); - input_report_abs(input_dev, ABS_Y, y); - sync = 1; - } + struct input_dev *input = ts->input; - if (sync) { - input_report_abs(input_dev, ABS_PRESSURE, Rt); - input_sync(input_dev); - } - -#ifdef VERBOSE - if (Rt || ts->pendown) - pr_debug("%s: %d/%d/%d%s\n", ts->spi->dev.bus_id, - x, y, Rt, Rt ? "" : " UP"); + if (!ts->pendown) { + input_report_key(input, BTN_TOUCH, 1); + ts->pendown = 1; +#ifdef VERBOSE + dev_dbg(&ts->spi->dev, "DOWN\n"); #endif + } + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_PRESSURE, Rt); - spin_lock_irqsave(&ts->lock, flags); + input_sync(input); +#ifdef VERBOSE + dev_dbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt); +#endif + } - ts->pendown = (Rt != 0); hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), HRTIMER_REL); - - spin_unlock_irqrestore(&ts->lock, flags); } static int ads7846_debounce(void *ads, int data_idx, int *val) @@ -553,14 +542,27 @@ static int ads7846_timer(struct hrtimer *handle) spin_lock_irq(&ts->lock); - if (unlikely(ts->msg_idx && !ts->pendown)) { + if (unlikely(!ts->get_pendown_state() || + device_suspended(&ts->spi->dev))) { + if (ts->pendown) { + struct input_dev *input = ts->input; + + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); + input_sync(input); + + ts->pendown = 0; +#ifdef VERBOSE + dev_dbg(&ts->spi->dev, "UP\n"); +#endif + } + /* measurement cycle ended */ if (!device_suspended(&ts->spi->dev)) { ts->irq_disabled = 0; enable_irq(ts->spi->irq); } ts->pending = 0; - ts->msg_idx = 0; } else { /* pen is still down, continue with the measurement */ ts->msg_idx = 0; -- cgit v1.2.2 From 2c8dc071517ec2843869024dc82be2e246f41064 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 18 Jan 2007 00:45:48 -0500 Subject: Input: ads7846 - be more compatible with the hwmon framework - Hook up to hwmon * show sensor attributes only if hwmon is present * ... and the board's reference voltage is known * otherwise be just a touchscreen - Report voltages per hwmon convention * measure in millivolts * voltages are named in[0-8]_input (ugh) * for 7846 chips, properly range-adjust vBATT/in1_input Battery measurements help during recharge monitoring. On OSK/Mistral, the measured voltage agreed with a multimeter to several decimal places. Signed-off-by: David Brownell Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 9 +- drivers/input/touchscreen/ads7846.c | 306 +++++++++++++++++++++++++----------- 2 files changed, 224 insertions(+), 91 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 29ca0ab0acb8..4bec8422821f 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -12,13 +12,18 @@ menuconfig INPUT_TOUCHSCREEN if INPUT_TOUCHSCREEN config TOUCHSCREEN_ADS7846 - tristate "ADS 7846 based touchscreens" + tristate "ADS 7846/7843 based touchscreens" depends on SPI_MASTER + depends on HWMON = n || HWMON help Say Y here if you have a touchscreen interface using the - ADS7846 controller, and your board-specific initialization + ADS7846 or ADS7843 controller, and your board-specific setup code includes that in its table of SPI devices. + If HWMON is selected, and the driver is told the reference voltage + on your board, you will also get hwmon interfaces for the voltage + (and on ads7846, temperature) sensors of this chip. + If unsure, say N (but it's safe to say "Y"). To compile this driver as a module, choose M here: the diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index b18c63a3f2ab..cd251efda410 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -17,8 +17,9 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include +#include #include +#include #include #include #include @@ -69,7 +70,7 @@ struct ts_event { u16 x; u16 y; u16 z1, z2; - int ignore; + int ignore; }; struct ads7846 { @@ -77,7 +78,12 @@ struct ads7846 { char phys[32]; struct spi_device *spi; + +#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE) struct attribute_group *attr_group; + struct class_device *hwmon; +#endif + u16 model; u16 vref_delay_usecs; u16 x_plate_ohms; @@ -170,7 +176,12 @@ struct ads7846 { /* * Non-touchscreen sensors only use single-ended conversions. + * The range is GND..vREF. The ads7843 and ads7835 must use external vREF; + * ads7846 lets that pin be unconnected, to use internal vREF. */ +static unsigned vREF_mV; +module_param(vREF_mV, uint, 0); +MODULE_PARM_DESC(vREF_mV, "external vREF voltage, in milliVolts"); struct ser_req { u8 ref_on; @@ -198,50 +209,55 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) struct ser_req *req = kzalloc(sizeof *req, GFP_KERNEL); int status; int sample; - int i; + int use_internal; if (!req) return -ENOMEM; spi_message_init(&req->msg); - /* activate reference, so it has time to settle; */ - req->ref_on = REF_ON; - req->xfer[0].tx_buf = &req->ref_on; - req->xfer[0].len = 1; - req->xfer[1].rx_buf = &req->scratch; - req->xfer[1].len = 2; - - /* - * for external VREF, 0 usec (and assume it's always on); - * for 1uF, use 800 usec; - * no cap, 100 usec. - */ - req->xfer[1].delay_usecs = ts->vref_delay_usecs; + /* FIXME boards with ads7846 might use external vref instead ... */ + use_internal = (ts->model == 7846); + + /* maybe turn on internal vREF, and let it settle */ + if (use_internal) { + req->ref_on = REF_ON; + req->xfer[0].tx_buf = &req->ref_on; + req->xfer[0].len = 1; + spi_message_add_tail(&req->xfer[0], &req->msg); + + req->xfer[1].rx_buf = &req->scratch; + req->xfer[1].len = 2; + + /* for 1uF, settle for 800 usec; no cap, 100 usec. */ + req->xfer[1].delay_usecs = ts->vref_delay_usecs; + spi_message_add_tail(&req->xfer[1], &req->msg); + } /* take sample */ req->command = (u8) command; req->xfer[2].tx_buf = &req->command; req->xfer[2].len = 1; + spi_message_add_tail(&req->xfer[2], &req->msg); + req->xfer[3].rx_buf = &req->sample; req->xfer[3].len = 2; + spi_message_add_tail(&req->xfer[3], &req->msg); /* REVISIT: take a few more samples, and compare ... */ - /* turn off reference */ - req->ref_off = REF_OFF; - req->xfer[4].tx_buf = &req->ref_off; - req->xfer[4].len = 1; - req->xfer[5].rx_buf = &req->scratch; - req->xfer[5].len = 2; - - CS_CHANGE(req->xfer[5]); - - /* group all the transfers together, so we can't interfere with - * reading touchscreen state; disable penirq while sampling - */ - for (i = 0; i < 6; i++) - spi_message_add_tail(&req->xfer[i], &req->msg); + /* maybe off internal vREF */ + if (use_internal) { + req->ref_off = REF_OFF; + req->xfer[4].tx_buf = &req->ref_off; + req->xfer[4].len = 1; + spi_message_add_tail(&req->xfer[4], &req->msg); + + req->xfer[5].rx_buf = &req->scratch; + req->xfer[5].len = 2; + CS_CHANGE(req->xfer[5]); + spi_message_add_tail(&req->xfer[5], &req->msg); + } ts->irq_disabled = 1; disable_irq(spi->irq); @@ -261,25 +277,173 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) return status ? status : sample; } -#define SHOW(name) static ssize_t \ +#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE) + +#define SHOW(name, var, adjust) static ssize_t \ name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \ { \ + struct ads7846 *ts = dev_get_drvdata(dev); \ ssize_t v = ads7846_read12_ser(dev, \ - READ_12BIT_SER(name) | ADS_PD10_ALL_ON); \ + READ_12BIT_SER(var) | ADS_PD10_ALL_ON); \ if (v < 0) \ return v; \ - return sprintf(buf, "%u\n", (unsigned) v); \ + return sprintf(buf, "%u\n", adjust(ts, v)); \ } \ static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL); -SHOW(temp0) -SHOW(temp1) -SHOW(vaux) -SHOW(vbatt) + +/* Sysfs conventions report temperatures in millidegrees Celcius. + * ADS7846 could use the low-accuracy two-sample scheme, but can't do the high + * accuracy scheme without calibration data. For now we won't try either; + * userspace sees raw sensor values, and must scale/calibrate appropriately. + */ +static inline unsigned null_adjust(struct ads7846 *ts, ssize_t v) +{ + return v; +} + +SHOW(temp0, temp0, null_adjust) /* temp1_input */ +SHOW(temp1, temp1, null_adjust) /* temp2_input */ + + +/* sysfs conventions report voltages in millivolts. We can convert voltages + * if we know vREF. userspace may need to scale vAUX to match the board's + * external resistors; we assume that vBATT only uses the internal ones. + */ +static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v) +{ + unsigned retval = v; + + /* external resistors may scale vAUX into 0..vREF */ + retval *= vREF_mV; + retval = retval >> 12; + return retval; +} + +static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v) +{ + unsigned retval = vaux_adjust(ts, v); + + /* ads7846 has a resistor ladder to scale this signal down */ + if (ts->model == 7846) + retval *= 4; + return retval; +} + +SHOW(in0_input, vaux, vaux_adjust) +SHOW(in1_input, vbatt, vbatt_adjust) + + +static struct attribute *ads7846_attributes[] = { + &dev_attr_temp0.attr, + &dev_attr_temp1.attr, + &dev_attr_in0_input.attr, + &dev_attr_in1_input.attr, + NULL, +}; + +static struct attribute_group ads7846_attr_group = { + .attrs = ads7846_attributes, +}; + +static struct attribute *ads7843_attributes[] = { + &dev_attr_in0_input.attr, + &dev_attr_in1_input.attr, + NULL, +}; + +static struct attribute_group ads7843_attr_group = { + .attrs = ads7843_attributes, +}; + +static struct attribute *ads7845_attributes[] = { + &dev_attr_in0_input.attr, + NULL, +}; + +static struct attribute_group ads7845_attr_group = { + .attrs = ads7845_attributes, +}; + +static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts) +{ + struct class_device *hwmon; + int err; + + /* hwmon sensors need a reference voltage */ + switch (ts->model) { + case 7846: + if (!vREF_mV) { + dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n"); + vREF_mV = 2500; + } + break; + case 7845: + case 7843: + if (!vREF_mV) { + dev_warn(&spi->dev, + "external vREF for ADS%d not specified\n", + ts->model); + return 0; + } + break; + } + + /* different chips have different sensor groups */ + switch (ts->model) { + case 7846: + ts->attr_group = &ads7846_attr_group; + break; + case 7845: + ts->attr_group = &ads7845_attr_group; + break; + case 7843: + ts->attr_group = &ads7843_attr_group; + break; + default: + dev_dbg(&spi->dev, "ADS%d not recognized\n", ts->model); + return 0; + } + + err = sysfs_create_group(&spi->dev.kobj, ts->attr_group); + if (err) + return err; + + hwmon = hwmon_device_register(&spi->dev); + if (IS_ERR(hwmon)) { + sysfs_remove_group(&spi->dev.kobj, ts->attr_group); + return PTR_ERR(hwmon); + } + + ts->hwmon = hwmon; + return 0; +} + +static void ads784x_hwmon_unregister(struct spi_device *spi, + struct ads7846 *ts) +{ + if (ts->hwmon) { + sysfs_remove_group(&spi->dev.kobj, ts->attr_group); + hwmon_device_unregister(ts->hwmon); + } +} + +#else +static inline int ads784x_hwmon_register(struct spi_device *spi, + struct ads7846 *ts) +{ + return 0; +} + +static inline void ads784x_hwmon_unregister(struct spi_device *spi, + struct ads7846 *ts) +{ +} +#endif static int is_pen_down(struct device *dev) { - struct ads7846 *ts = dev_get_drvdata(dev); + struct ads7846 *ts = dev_get_drvdata(dev); return ts->pendown; } @@ -323,46 +487,14 @@ static ssize_t ads7846_disable_store(struct device *dev, static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store); -static struct attribute *ads7846_attributes[] = { - &dev_attr_temp0.attr, - &dev_attr_temp1.attr, - &dev_attr_vbatt.attr, - &dev_attr_vaux.attr, - &dev_attr_pen_down.attr, - &dev_attr_disable.attr, - NULL, -}; - -static struct attribute_group ads7846_attr_group = { - .attrs = ads7846_attributes, -}; - -/* - * ads7843/7845 don't have temperature sensors, and - * use the other sensors a bit differently too - */ - -static struct attribute *ads7843_attributes[] = { - &dev_attr_vbatt.attr, - &dev_attr_vaux.attr, +static struct attribute *ads784x_attributes[] = { &dev_attr_pen_down.attr, &dev_attr_disable.attr, NULL, }; -static struct attribute_group ads7843_attr_group = { - .attrs = ads7843_attributes, -}; - -static struct attribute *ads7845_attributes[] = { - &dev_attr_vaux.attr, - &dev_attr_pen_down.attr, - &dev_attr_disable.attr, - NULL, -}; - -static struct attribute_group ads7845_attr_group = { - .attrs = ads7845_attributes, +static struct attribute_group ads784x_attr_group = { + .attrs = ads784x_attributes, }; /*--------------------------------------------------------------------------*/ @@ -886,28 +1018,21 @@ static int __devinit ads7846_probe(struct spi_device *spi) goto err_cleanup_filter; } + err = ads784x_hwmon_register(spi, ts); + if (err) + goto err_free_irq; + dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq); - /* take a first sample, leaving nPENIRQ active; avoid + /* take a first sample, leaving nPENIRQ active and vREF off; avoid * the touchscreen, in case it's not connected. */ (void) ads7846_read12_ser(&spi->dev, READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON); - switch (ts->model) { - case 7846: - ts->attr_group = &ads7846_attr_group; - break; - case 7845: - ts->attr_group = &ads7845_attr_group; - break; - default: - ts->attr_group = &ads7843_attr_group; - break; - } - err = sysfs_create_group(&spi->dev.kobj, ts->attr_group); + err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group); if (err) - goto err_free_irq; + goto err_remove_hwmon; err = input_register_device(input_dev); if (err) @@ -916,7 +1041,9 @@ static int __devinit ads7846_probe(struct spi_device *spi) return 0; err_remove_attr_group: - sysfs_remove_group(&spi->dev.kobj, ts->attr_group); + sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group); + err_remove_hwmon: + ads784x_hwmon_unregister(spi, ts); err_free_irq: free_irq(spi->irq, ts); err_cleanup_filter: @@ -932,11 +1059,12 @@ static int __devexit ads7846_remove(struct spi_device *spi) { struct ads7846 *ts = dev_get_drvdata(&spi->dev); + ads784x_hwmon_unregister(spi, ts); input_unregister_device(ts->input); ads7846_suspend(spi, PMSG_SUSPEND); - sysfs_remove_group(&spi->dev.kobj, ts->attr_group); + sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group); free_irq(ts->spi->irq, ts); /* suspend left the IRQ disabled */ -- cgit v1.2.2 From 31ea7ff0f880dc3f4ad94e85c1432d4b910c9fca Mon Sep 17 00:00:00 2001 From: Jaya Kumar Date: Sat, 10 Feb 2007 01:29:00 -0500 Subject: Input: add Atlas button driver This patch adds support for the buttons on the Atlas wallmount touchscreen. Signed-off-by: Jaya Kumar Acked-by: Len Brown Signed-off-by: Dmitry Torokhov --- drivers/input/misc/Kconfig | 10 +++ drivers/input/misc/Makefile | 1 + drivers/input/misc/atlas_btns.c | 170 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+) create mode 100644 drivers/input/misc/atlas_btns.c (limited to 'drivers/input') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index ba0e88c64e1e..41b42587f5e9 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -50,6 +50,16 @@ config INPUT_WISTRON_BTNS To compile this driver as a module, choose M here: the module will be called wistron_btns. +config INPUT_ATLAS_BTNS + tristate "x86 Atlas button interface" + depends on X86 && ACPI + help + Say Y here for support of Atlas wallmount touchscreen buttons. + The events will show up as scancodes F1 through F9 via evdev. + + To compile this driver as a module, choose M here: the module will + be called atlas_btns. + config INPUT_IXP4XX_BEEPER tristate "IXP4XX Beeper support" depends on ARCH_IXP4XX diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 415c49178985..e0a8d58c9e9b 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -9,5 +9,6 @@ obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o obj-$(CONFIG_INPUT_UINPUT) += uinput.o obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o +obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c new file mode 100644 index 000000000000..0acc3a123604 --- /dev/null +++ b/drivers/input/misc/atlas_btns.c @@ -0,0 +1,170 @@ +/* + * atlas_btns.c - Atlas Wallmount Touchscreen ACPI Extras + * + * Copyright (C) 2006 Jaya Kumar + * Based on Toshiba ACPI by John Belmonte and ASUS ACPI + * This work was sponsored by CIS(M) Sdn Bhd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define ACPI_ATLAS_NAME "Atlas ACPI" +#define ACPI_ATLAS_CLASS "Atlas" +#define ACPI_ATLAS_BUTTON_HID "ASIM0000" + +static struct input_dev *input_dev; + +/* button handling code */ +static acpi_status acpi_atlas_button_setup(acpi_handle region_handle, + u32 function, void *handler_context, void **return_context) +{ + *return_context = + (function != ACPI_REGION_DEACTIVATE) ? handler_context : NULL; + + return AE_OK; +} + +static acpi_status acpi_atlas_button_handler(u32 function, + acpi_physical_address address, + u32 bit_width, acpi_integer *value, + void *handler_context, void *region_context) +{ + acpi_status status; + int keycode; + + if (function == ACPI_WRITE) { + keycode = KEY_F1 + (address & 0x0F); + input_report_key(input_dev, keycode, !(address & 0x10)); + input_sync(input_dev); + status = 0; + } else { + printk(KERN_WARNING "atlas: shrugged on unexpected function" + ":function=%x,address=%lx,value=%x\n", + function, (unsigned long)address, (u32)*value); + status = -EINVAL; + } + + return status; +} + +static int atlas_acpi_button_add(struct acpi_device *device) +{ + acpi_status status; + int err; + + input_dev = input_allocate_device(); + if (!input_dev) { + printk(KERN_ERR "atlas: unable to allocate input device\n"); + return -ENOMEM; + } + + input_dev->name = "Atlas ACPI button driver"; + input_dev->phys = "ASIM0000/atlas/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->evbit[LONG(EV_KEY)] = BIT(EV_KEY); + + set_bit(KEY_F1, input_dev->keybit); + set_bit(KEY_F2, input_dev->keybit); + set_bit(KEY_F3, input_dev->keybit); + set_bit(KEY_F4, input_dev->keybit); + set_bit(KEY_F5, input_dev->keybit); + set_bit(KEY_F6, input_dev->keybit); + set_bit(KEY_F7, input_dev->keybit); + set_bit(KEY_F8, input_dev->keybit); + set_bit(KEY_F9, input_dev->keybit); + + err = input_register_device(input_dev); + if (err) { + printk(KERN_ERR "atlas: couldn't register input device\n"); + input_free_device(input_dev); + return err; + } + + /* hookup button handler */ + status = acpi_install_address_space_handler(device->handle, + 0x81, &acpi_atlas_button_handler, + &acpi_atlas_button_setup, device); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR "Atlas: Error installing addr spc handler\n"); + input_unregister_device(input_dev); + status = -EINVAL; + } + + return status; +} + +static int atlas_acpi_button_remove(struct acpi_device *device, int type) +{ + acpi_status status; + + status = acpi_remove_address_space_handler(device->handle, + 0x81, &acpi_atlas_button_handler); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR "Atlas: Error removing addr spc handler\n"); + status = -EINVAL; + } + + input_unregister_device(input_dev); + + return status; +} + +static struct acpi_driver atlas_acpi_driver = { + .name = ACPI_ATLAS_NAME, + .class = ACPI_ATLAS_CLASS, + .ids = ACPI_ATLAS_BUTTON_HID, + .ops = { + .add = atlas_acpi_button_add, + .remove = atlas_acpi_button_remove, + }, +}; + +static int __init atlas_acpi_init(void) +{ + int result; + + if (acpi_disabled) + return -ENODEV; + + result = acpi_bus_register_driver(&atlas_acpi_driver); + if (result < 0) { + printk(KERN_ERR "Atlas ACPI: Unable to register driver\n"); + return -ENODEV; + } + + return 0; +} + +static void __exit atlas_acpi_exit(void) +{ + acpi_bus_unregister_driver(&atlas_acpi_driver); +} + +module_init(atlas_acpi_init); +module_exit(atlas_acpi_exit); + +MODULE_AUTHOR("Jaya Kumar"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Atlas button driver"); + -- cgit v1.2.2 From ff141a03e1909db719c6afd3230c2f16a4141860 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Sat, 10 Feb 2007 01:29:11 -0500 Subject: Input: tsdev - schedule removal Compaq touchscreen emulation (drivers/input/tsdev.c) is old, was obsolete when it was written by the authors own admission and much better userspace solutions like tslib now exist. The name is also confusing. Signed-off-by: Richard Purdie Acked-by: James Simmons Signed-off-by: Dmitry Torokhov --- drivers/input/tsdev.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c index a730c461227f..0300dca8591d 100644 --- a/drivers/input/tsdev.c +++ b/drivers/input/tsdev.c @@ -151,6 +151,10 @@ static int tsdev_open(struct inode *inode, struct file *file) int i = iminor(inode) - TSDEV_MINOR_BASE; struct tsdev_list *list; + printk(KERN_WARNING "tsdev (compaq touchscreen emulation) is scheduled " + "for removal.\nSee Documentation/feature-removal-schedule.txt " + "for details.\n"); + if (i >= TSDEV_MINORS || !tsdev_table[i & TSDEV_MINOR_MASK]) return -ENODEV; -- cgit v1.2.2 From b350620cc5e8e62782a4d47bf45952442a18a0b0 Mon Sep 17 00:00:00 2001 From: "Cyrill V. Gorcunov" Date: Sat, 10 Feb 2007 01:29:19 -0500 Subject: Input: HIL - handle erros from input_register_device() Also some whitespace cleanup. Signed-off-by: Cyrill V. Gorcunov Signed-off-by: Andrew Morton Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/hilkbd.c | 114 +++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 48 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c index 35461eab2faf..255a6ec75a48 100644 --- a/drivers/input/keyboard/hilkbd.c +++ b/drivers/input/keyboard/hilkbd.c @@ -6,10 +6,10 @@ * Copyright (C) 1999-2006 Helge Deller * * Very basic HP Human Interface Loop (HIL) driver. - * This driver handles the keyboard on HP300 (m68k) and on some + * This driver handles the keyboard on HP300 (m68k) and on some * HP700 (parisc) series machines. * - * + * * This file is subject to the terms and conditions of the GNU General Public * License version 2. See the file COPYING in the main directory of this * archive for more details. @@ -64,9 +64,9 @@ MODULE_LICENSE("GPL v2"); #endif - + /* HIL helper functions */ - + #define hil_busy() (hil_readb(HILBASE + HIL_CMD) & HIL_BUSY) #define hil_data_available() (hil_readb(HILBASE + HIL_CMD) & HIL_DATA_RDY) #define hil_status() (hil_readb(HILBASE + HIL_CMD)) @@ -75,7 +75,7 @@ MODULE_LICENSE("GPL v2"); #define hil_write_data(x) do { hil_writeb((x), HILBASE + HIL_DATA); } while (0) /* HIL constants */ - + #define HIL_BUSY 0x02 #define HIL_DATA_RDY 0x01 @@ -86,10 +86,10 @@ MODULE_LICENSE("GPL v2"); #define HIL_INTON 0x5C /* Turn on interrupts. */ #define HIL_INTOFF 0x5D /* Turn off interrupts. */ -#define HIL_READKBDSADR 0xF9 -#define HIL_WRITEKBDSADR 0xE9 +#define HIL_READKBDSADR 0xF9 +#define HIL_WRITEKBDSADR 0xE9 -static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] = +static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] = { HIL_KEYCODES_SET1 }; /* HIL structure */ @@ -97,11 +97,11 @@ static struct { struct input_dev *dev; unsigned int curdev; - + unsigned char s; unsigned char c; int valid; - + unsigned char data[16]; unsigned int ptr; spinlock_t lock; @@ -115,7 +115,7 @@ static void poll_finished(void) int down; int key; unsigned char scode; - + switch (hil_dev.data[0]) { case 0x40: down = (hil_dev.data[1] & 1) == 0; @@ -127,6 +127,7 @@ static void poll_finished(void) hil_dev.curdev = 0; } + static inline void handle_status(unsigned char s, unsigned char c) { if (c & 0x8) { @@ -143,6 +144,7 @@ static inline void handle_status(unsigned char s, unsigned char c) } } + static inline void handle_data(unsigned char s, unsigned char c) { if (hil_dev.curdev) { @@ -152,13 +154,11 @@ static inline void handle_data(unsigned char s, unsigned char c) } -/* - * Handle HIL interrupts. - */ +/* handle HIL interrupts */ static irqreturn_t hil_interrupt(int irq, void *handle) { unsigned char s, c; - + s = hil_status(); c = hil_read_data(); @@ -179,10 +179,8 @@ static irqreturn_t hil_interrupt(int irq, void *handle) return IRQ_HANDLED; } -/* - * Send a command to the HIL - */ +/* send a command to the HIL */ static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len) { unsigned long flags; @@ -200,16 +198,14 @@ static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len) } -/* - * Initialise HIL. - */ - +/* initialise HIL */ static int __init hil_keyb_init(void) { unsigned char c; unsigned int i, kbid; wait_queue_head_t hil_wait; + int err; if (hil_dev.dev) { return -ENODEV; /* already initialized */ @@ -219,15 +215,25 @@ hil_keyb_init(void) if (!hil_dev.dev) return -ENOMEM; hil_dev.dev->private = &hil_dev; - + #if defined(CONFIG_HP300) - if (!hwreg_present((void *)(HILBASE + HIL_DATA))) - return -ENODEV; - - request_region(HILBASE+HIL_DATA, 2, "hil"); + if (!hwreg_present((void *)(HILBASE + HIL_DATA))) { + printk(KERN_ERR "HIL: hardware register was not found\n"); + err = -ENODEV; + goto err1; + } + if (!request_region(HILBASE + HIL_DATA, 2, "hil")) { + printk(KERN_ERR "HIL: IOPORT region already used\n"); + err = -EIO; + goto err1; + } #endif - - request_irq(HIL_IRQ, hil_interrupt, 0, "hil", hil_dev.dev_id); + + err = request_irq(HIL_IRQ, hil_interrupt, 0, "hil", hil_dev.dev_id); + if (err) { + printk(KERN_ERR "HIL: Can't get IRQ\n"); + goto err2; + } /* Turn on interrupts */ hil_do(HIL_INTON, NULL, 0); @@ -239,47 +245,63 @@ hil_keyb_init(void) init_waitqueue_head(&hil_wait); wait_event_interruptible_timeout(hil_wait, hil_dev.valid, 3*HZ); if (!hil_dev.valid) { - printk(KERN_WARNING "HIL: timed out, assuming no keyboard present.\n"); + printk(KERN_WARNING "HIL: timed out, assuming no keyboard present\n"); } - c = hil_dev.c; + c = hil_dev.c; hil_dev.valid = 0; if (c == 0) { kbid = -1; - printk(KERN_WARNING "HIL: no keyboard present.\n"); + printk(KERN_WARNING "HIL: no keyboard present\n"); } else { kbid = ffz(~c); - /* printk(KERN_INFO "HIL: keyboard found at id %d\n", kbid); */ + printk(KERN_INFO "HIL: keyboard found at id %d\n", kbid); } /* set it to raw mode */ c = 0; hil_do(HIL_WRITEKBDSADR, &c, 1); - + for (i = 0; i < HIL_KEYCODES_SET1_TBLSIZE; i++) if (hphilkeyb_keycode[i] != KEY_RESERVED) set_bit(hphilkeyb_keycode[i], hil_dev.dev->keybit); - hil_dev.dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); - hil_dev.dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL); - hil_dev.dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE; - hil_dev.dev->keycodesize = sizeof(hphilkeyb_keycode[0]); - hil_dev.dev->keycode = hphilkeyb_keycode; - hil_dev.dev->name = "HIL keyboard"; - hil_dev.dev->phys = "hpkbd/input0"; + hil_dev.dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + hil_dev.dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL); + hil_dev.dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE; + hil_dev.dev->keycodesize= sizeof(hphilkeyb_keycode[0]); + hil_dev.dev->keycode = hphilkeyb_keycode; + hil_dev.dev->name = "HIL keyboard"; + hil_dev.dev->phys = "hpkbd/input0"; hil_dev.dev->id.bustype = BUS_HIL; hil_dev.dev->id.vendor = PCI_VENDOR_ID_HP; hil_dev.dev->id.product = 0x0001; hil_dev.dev->id.version = 0x0010; - input_register_device(hil_dev.dev); + err = input_register_device(hil_dev.dev); + if (err) { + printk(KERN_ERR "HIL: Can't register device\n"); + goto err3; + } printk(KERN_INFO "input: %s, ID %d at 0x%08lx (irq %d) found and attached\n", - hil_dev.dev->name, kbid, HILBASE, HIL_IRQ); + hil_dev.dev->name, kbid, HILBASE, HIL_IRQ); return 0; + +err3: + hil_do(HIL_INTOFF, NULL, 0); + disable_irq(HIL_IRQ); + free_irq(HIL_IRQ, hil_dev.dev_id); +err2: + release_region(HILBASE + HIL_DATA, 2); +err1: + input_free_device(hil_dev.dev); + hil_dev.dev = NULL; + return err; } + #if defined(CONFIG_PARISC) static int __init hil_init_chip(struct parisc_device *dev) @@ -292,7 +314,7 @@ hil_init_chip(struct parisc_device *dev) hil_base = dev->hpa.start; hil_irq = dev->irq; hil_dev.dev_id = dev; - + printk(KERN_INFO "Found HIL bus at 0x%08lx, IRQ %d\n", hil_base, hil_irq); return hil_keyb_init(); @@ -313,9 +335,6 @@ static struct parisc_driver hil_driver = { #endif /* CONFIG_PARISC */ - - - static int __init hil_init(void) { #if defined(CONFIG_PARISC) @@ -349,4 +368,3 @@ static void __exit hil_exit(void) module_init(hil_init); module_exit(hil_exit); - -- cgit v1.2.2 From 8370a643531699e5076fcac21c281c138bce1c87 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Sat, 10 Feb 2007 01:29:31 -0500 Subject: Input: inport - use correct config option for ATIXL Change the apparently incorrect check for CONFIG_INPUT_ATIXL in a source file to be consistent with the kernel config option CONFIG_MOUSE_ATIXL. Signed-off-by: Robert P. J. Day Signed-off-by: Andrew Morton Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/inport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/inport.c b/drivers/input/mouse/inport.c index 13dd96785e39..701ebd5473cf 100644 --- a/drivers/input/mouse/inport.c +++ b/drivers/input/mouse/inport.c @@ -61,7 +61,7 @@ MODULE_LICENSE("GPL"); #define INPORT_REG_MODE 0x07 #define INPORT_RESET 0x80 -#ifdef CONFIG_INPUT_ATIXL +#ifdef CONFIG_MOUSE_ATIXL #define INPORT_NAME "ATI XL Mouse" #define INPORT_VENDOR 0x0002 #define INPORT_SPEED_30HZ 0x01 -- cgit v1.2.2 From 5809d537c1bc7628cee1e580da35f6b4d254e23b Mon Sep 17 00:00:00 2001 From: Michael Leun Date: Sat, 10 Feb 2007 01:29:42 -0500 Subject: Input: wistron - add support for Fujitsu-Siemens Amilo D88x0 Tested on a Amilo D8820. Signed-off-by: Michael Leun Signed-off-by: Dmitry Torokhov --- drivers/input/misc/wistron_btns.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index 7b9d1c1da41a..e1183aeb8ed5 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -335,6 +335,17 @@ static struct key_entry keymap_aopen_1559as[] = { { KE_END, 0 }, }; +static struct key_entry keymap_fs_amilo_d88x0[] = { + { KE_KEY, 0x01, KEY_HELP }, + { KE_KEY, 0x08, KEY_MUTE }, + { KE_KEY, 0x31, KEY_MAIL }, + { KE_KEY, 0x36, KEY_WWW }, + { KE_KEY, 0x11, KEY_PROG1 }, + { KE_KEY, 0x12, KEY_PROG2 }, + { KE_KEY, 0x13, KEY_PROG3 }, + { KE_END, 0 } +}; + /* * If your machine is not here (which is currently rather likely), please send * a list of buttons and their key codes (reported when loading this module @@ -413,6 +424,15 @@ static struct dmi_system_id dmi_ids[] __initdata = { }, .driver_data = keymap_wistron_ms2111 }, + { + .callback = dmi_matched, + .ident = "Fujitsu Siemens Amilo D88x0", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO D"), + }, + .driver_data = keymap_fs_amilo_d88x0 + }, { NULL, } }; -- cgit v1.2.2 From 1e4865f8d469b1795e77877410951e5b808a2c4e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 10 Feb 2007 01:29:53 -0500 Subject: Input: i8042 - fix AUX IRQ delivery check On boxes that do not implement AUX LOOP command we can not verify AUX IRQ delivery and must assume that it is wired properly. Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 1364c7964db4..c3fdfc1f342a 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -543,6 +543,7 @@ static int __devinit i8042_check_aux(void) { int retval = -1; int irq_registered = 0; + int aux_loop_broken = 0; unsigned long flags; unsigned char param; @@ -572,6 +573,8 @@ static int __devinit i8042_check_aux(void) if (i8042_command(¶m, I8042_CMD_AUX_TEST) || (param && param != 0xfa && param != 0xff)) return -1; + + aux_loop_broken = 1; } /* @@ -595,7 +598,7 @@ static int __devinit i8042_check_aux(void) * used it for a PCI card or somethig else. */ - if (i8042_noloop) { + if (i8042_noloop || aux_loop_broken) { /* * Without LOOP command we can't test AUX IRQ delivery. Assume the port * is working and hope we are right. -- cgit v1.2.2