diff options
author | Ian Wisbon <ian.wisbon@timesys.com> | 2011-02-10 17:15:15 -0500 |
---|---|---|
committer | Ian Wisbon <ian.wisbon@timesys.com> | 2011-02-10 17:19:54 -0500 |
commit | 0eb553bf96e2c990d3bfccaa07da0863624c89ab (patch) | |
tree | 79b396bf70ae3795e6ee9a3b645e64f7e29474e7 /drivers/input | |
parent | effff5718c380983788fe6c380671c18e15ac7c2 (diff) |
Linux 2.6.31 Release for Digi ConnectCore Wi-i.MX boards
Digi 01262011 Release
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/evdev.c | 29 | ||||
-rw-r--r-- | drivers/input/keyboard/Makefile | 2 | ||||
-rw-r--r-- | drivers/input/keyboard/mxc_keyb.c | 239 | ||||
-rw-r--r-- | drivers/input/misc/mma7455l.c | 2 | ||||
-rw-r--r-- | drivers/input/touchscreen/ads7846.c | 1157 | ||||
-rw-r--r-- | drivers/input/touchscreen/mxc_ts.c | 58 |
6 files changed, 795 insertions, 692 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 1148140d08a1..5598ecb48c5b 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -19,6 +19,9 @@ #include <linux/input.h> #include <linux/major.h> #include <linux/device.h> +#ifdef CONFIG_WAKELOCK +#include <linux/wakelock.h> +#endif #include "input-compat.h" struct evdev { @@ -42,6 +45,10 @@ struct evdev_client { struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; +#ifdef CONFIG_WAKELOCK + struct wake_lock wake_lock; + char name[28]; +#endif }; static struct evdev *evdev_table[EVDEV_MINORS]; @@ -54,6 +61,9 @@ static void evdev_pass_event(struct evdev_client *client, * Interrupts are disabled, just acquire the lock */ spin_lock(&client->buffer_lock); +#ifdef CONFIG_WAKELOCK + wake_lock_timeout(&client->wake_lock, 5 * HZ); +#endif client->buffer[client->head++] = *event; client->head &= EVDEV_BUFFER_SIZE - 1; spin_unlock(&client->buffer_lock); @@ -70,8 +80,15 @@ static void evdev_event(struct input_handle *handle, struct evdev *evdev = handle->private; struct evdev_client *client; struct input_event event; +#ifdef CONFIG_WAKELOCK + struct timespec ts; + ktime_get_ts(&ts); + event.time.tv_sec = ts.tv_sec; + event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC; +#else do_gettimeofday(&event.time); +#endif event.type = type; event.code = code; event.value = value; @@ -232,6 +249,9 @@ static int evdev_release(struct inode *inode, struct file *file) mutex_unlock(&evdev->mutex); evdev_detach_client(evdev, client); +#ifdef CONFIG_WAKELOCK + wake_lock_destroy(&client->wake_lock); +#endif kfree(client); evdev_close_device(evdev); @@ -268,6 +288,11 @@ static int evdev_open(struct inode *inode, struct file *file) } spin_lock_init(&client->buffer_lock); +#ifdef CONFIG_WAKELOCK + snprintf(client->name, sizeof(client->name), "%s-%d", dev_name(&evdev->dev), + task_tgid_vnr(current)); + wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name); +#endif client->evdev = evdev; evdev_attach_client(evdev, client); @@ -331,6 +356,10 @@ static int evdev_fetch_next_event(struct evdev_client *client, if (have_event) { *event = client->buffer[client->tail++]; client->tail &= EVDEV_BUFFER_SIZE - 1; +#ifdef CONFIG_WAKELOCK + if (client->head == client->tail) + wake_unlock(&client->wake_lock); +#endif } spin_unlock_irq(&client->buffer_lock); diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index fea01a591076..88e2a24d9cff 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -31,7 +31,7 @@ obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o -obj-$(CONFIG_KEYBOARD_MXC) += mxc_keyb.o +obj-$(CONFIG_KEYBOARD_MXC) += mxc_keyb.o mxc_pwrkey.o obj-$(CONFIG_KEYBOARD_MPR084) += mpr084.o obj-$(CONFIG_KEYBOARD_STMP3XXX) += stmp3xxx-kbd.o obj-$(CONFIG_KEYBOARD_MXS) += mxs-kbd.o diff --git a/drivers/input/keyboard/mxc_keyb.c b/drivers/input/keyboard/mxc_keyb.c index 99dd7cf51cb5..bd5ecd8c20f1 100644 --- a/drivers/input/keyboard/mxc_keyb.c +++ b/drivers/input/keyboard/mxc_keyb.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -58,12 +58,168 @@ #include <linux/clk.h> #include <asm/mach/keypad.h> +/*! + * Keypad Module Name + */ +#define MOD_NAME "mxckpd" + +/*! + * XLATE mode selection + */ +#define KEYPAD_XLATE 0 + +/*! + * RAW mode selection + */ +#define KEYPAD_RAW 1 + +/*! + * Maximum number of keys. + */ +#define MAXROW 8 +#define MAXCOL 8 +#define MXC_MAXKEY (MAXROW * MAXCOL) + +/*! + * This define indicates break scancode for every key release. A constant + * of 128 is added to the key press scancode. + */ +#define MXC_KEYRELEASE 128 + +/* + * _reg_KPP_KPCR _reg_KPP_KPSR _reg_KPP_KDDR _reg_KPP_KPDR + * The offset of Keypad Control Register Address + */ +#define KPCR 0x00 + +/* + * The offset of Keypad Status Register Address + */ +#define KPSR 0x02 + +/* + * The offset of Keypad Data Direction Address + */ +#define KDDR 0x04 + +/* + * The offset of Keypad Data Register + */ +#define KPDR 0x06 + +/* + * Key Press Interrupt Status bit + */ +#define KBD_STAT_KPKD 0x01 + +/* + * Key Release Interrupt Status bit + */ +#define KBD_STAT_KPKR 0x02 + +/* + * Key Depress Synchronizer Chain Status bit + */ +#define KBD_STAT_KDSC 0x04 + +/* + * Key Release Synchronizer Status bit + */ +#define KBD_STAT_KRSS 0x08 + +/* + * Key Depress Interrupt Enable Status bit + */ +#define KBD_STAT_KDIE 0x100 + /* - * Module header file + * Key Release Interrupt Enable */ -#include "mxc_keyb.h" +#define KBD_STAT_KRIE 0x200 + +/* + * Keypad Clock Enable + */ +#define KBD_STAT_KPPEN 0x400 + +/*! + * Buffer size of keypad queue. Should be a power of 2. + */ +#define KPP_BUF_SIZE 128 + +/*! + * Test whether bit is set for integer c + */ +#define TEST_BIT(c, n) ((c) & (0x1 << (n))) + +/*! + * Set nth bit in the integer c + */ +#define BITSET(c, n) ((c) | (1 << (n))) + +/*! + * Reset nth bit in the integer c + */ +#define BITRESET(c, n) ((c) & ~(1 << (n))) + +/*! + * This enum represents the keypad state machine to maintain debounce logic + * for key press/release. + */ +enum KeyState { + + /*! + * Key press state. + */ + KStateUp, + + /*! + * Key press debounce state. + */ + KStateFirstDown, + + /*! + * Key release state. + */ + KStateDown, + + /*! + * Key release debounce state. + */ + KStateFirstUp +}; /*! + * Keypad Private Data Structure + */ +struct keypad_priv { + + /*! + * Keypad state machine. + */ + enum KeyState iKeyState; + + /*! + * Number of rows configured in the keypad matrix + */ + unsigned long kpp_rows; + + /*! + * Number of Columns configured in the keypad matrix + */ + unsigned long kpp_cols; + + /*! + * Timer used for Keypad polling. + */ + struct timer_list poll_timer; + + /*! + * The base address + */ + void __iomem *base; +}; +/*! * This structure holds the keypad private data structure. */ static struct keypad_priv kpp_dev; @@ -269,26 +425,26 @@ static int mxc_kpp_scan_matrix(void) for (col = 0; col < kpp_dev.kpp_cols; col++) { /* Col */ /* 2. Write 1.s to KPDR[15:8] setting column data to 1.s */ - reg_val = __raw_readw(KPDR); + reg_val = __raw_readw(kpp_dev.base + KPDR); reg_val |= 0xff00; - __raw_writew(reg_val, KPDR); + __raw_writew(reg_val, kpp_dev.base + KPDR); /* * 3. Configure columns as totem pole outputs(for quick * discharging of keypad capacitance) */ - reg_val = __raw_readw(KPCR); + reg_val = __raw_readw(kpp_dev.base + KPCR); reg_val &= 0x00ff; - __raw_writew(reg_val, KPCR); + __raw_writew(reg_val, kpp_dev.base + KPCR); udelay(2); /* * 4. Configure columns as open-drain */ - reg_val = __raw_readw(KPCR); + reg_val = __raw_readw(kpp_dev.base + KPCR); reg_val |= ((1 << kpp_dev.kpp_cols) - 1) << 8; - __raw_writew(reg_val, KPCR); + __raw_writew(reg_val, kpp_dev.base + KPCR); /* * 5. Write a single column to 0, others to 1. @@ -298,9 +454,9 @@ static int mxc_kpp_scan_matrix(void) */ /* Col bit starts at 8th bit in KPDR */ - reg_val = __raw_readw(KPDR); + reg_val = __raw_readw(kpp_dev.base + KPDR); reg_val &= ~(1 << (8 + col)); - __raw_writew(reg_val, KPDR); + __raw_writew(reg_val, kpp_dev.base + KPDR); /* Delay added to avoid propagating the 0 from column to row * when scanning. */ @@ -308,7 +464,7 @@ static int mxc_kpp_scan_matrix(void) udelay(5); /* Read row input */ - reg_val = __raw_readw(KPDR); + reg_val = __raw_readw(kpp_dev.base + KPDR); for (row = 0; row < kpp_dev.kpp_rows; row++) { /* sample row */ if (TEST_BIT(reg_val, row) == 0) { cur_rcmap[row] = BITSET(cur_rcmap[row], col); @@ -324,12 +480,12 @@ static int mxc_kpp_scan_matrix(void) * clear the KPKD synchronizer chain by writing "1" to KDSC register */ reg_val = 0x00; - __raw_writew(reg_val, KPDR); - reg_val = __raw_readw(KPDR); - reg_val = __raw_readw(KPSR); + __raw_writew(reg_val, kpp_dev.base + KPDR); + reg_val = __raw_readw(kpp_dev.base + KPDR); + reg_val = __raw_readw(kpp_dev.base + KPSR); reg_val |= KBD_STAT_KPKD | KBD_STAT_KPKR | KBD_STAT_KRSS | KBD_STAT_KDSC; - __raw_writew(reg_val, KPSR); + __raw_writew(reg_val, kpp_dev.base + KPSR); /* Check key press status change */ @@ -558,14 +714,14 @@ static void mxc_kpp_handle_timer(unsigned long data) * Stop scanning and wait for interrupt. * Enable press interrupt and disable release interrupt. */ - __raw_writew(0x00FF, KPDR); - reg_val = __raw_readw(KPSR); + __raw_writew(0x00FF, kpp_dev.base + KPDR); + reg_val = __raw_readw(kpp_dev.base + KPSR); reg_val |= (KBD_STAT_KPKR | KBD_STAT_KPKD); reg_val |= KBD_STAT_KRSS | KBD_STAT_KDSC; - __raw_writew(reg_val, KPSR); + __raw_writew(reg_val, kpp_dev.base + KPSR); reg_val |= KBD_STAT_KDIE; reg_val &= ~KBD_STAT_KRIE; - __raw_writew(reg_val, KPSR); + __raw_writew(reg_val, kpp_dev.base + KPSR); /* * No more keys pressed... make sure unwanted key codes are @@ -613,7 +769,7 @@ static irqreturn_t mxc_kpp_interrupt(int irq, void *dev_id) /* Delete the polling timer */ del_timer(&kpp_dev.poll_timer); - reg_val = __raw_readw(KPSR); + reg_val = __raw_readw(kpp_dev.base + KPSR); /* Check if it is key press interrupt */ if (reg_val & KBD_STAT_KPKD) { @@ -621,7 +777,7 @@ static irqreturn_t mxc_kpp_interrupt(int irq, void *dev_id) * Disable key press(KDIE status bit) interrupt */ reg_val &= ~KBD_STAT_KDIE; - __raw_writew(reg_val, KPSR); + __raw_writew(reg_val, kpp_dev.base + KPSR); } else { /* spurious interrupt */ return IRQ_RETVAL(0); @@ -767,6 +923,7 @@ static int mxc_kpp_probe(struct platform_device *pdev) int i, irq; int retval; unsigned int reg_val; + struct resource *res; keypad = (struct keypad_data *)pdev->dev.platform_data; @@ -774,6 +931,14 @@ static int mxc_kpp_probe(struct platform_device *pdev) kpp_dev.kpp_rows = keypad->rowmax; key_pad_enabled = 0; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + kpp_dev.base = ioremap(res->start, res->end - res->start + 1); + if (!kpp_dev.base) + return -ENOMEM; + irq = platform_get_irq(pdev, 0); keypad->irq = irq; @@ -793,30 +958,30 @@ static int mxc_kpp_probe(struct platform_device *pdev) * LSB nibble in KPP is for 8 rows * MSB nibble in KPP is for 8 cols */ - reg_val = __raw_readw(KPCR); + reg_val = __raw_readw(kpp_dev.base + KPCR); reg_val |= (1 << keypad->rowmax) - 1; /* LSB */ reg_val |= ((1 << keypad->colmax) - 1) << 8; /* MSB */ - __raw_writew(reg_val, KPCR); + __raw_writew(reg_val, kpp_dev.base + KPCR); /* Write 0's to KPDR[15:8] */ - reg_val = __raw_readw(KPDR); + reg_val = __raw_readw(kpp_dev.base + KPDR); reg_val &= 0x00ff; - __raw_writew(reg_val, KPDR); + __raw_writew(reg_val, kpp_dev.base + KPDR); /* Configure columns as output, rows as input (KDDR[15:0]) */ - reg_val = __raw_readw(KDDR); + reg_val = __raw_readw(kpp_dev.base + KDDR); reg_val |= 0xff00; reg_val &= 0xff00; - __raw_writew(reg_val, KDDR); + __raw_writew(reg_val, kpp_dev.base + KDDR); - reg_val = __raw_readw(KPSR); + reg_val = __raw_readw(kpp_dev.base + KPSR); reg_val &= ~(KBD_STAT_KPKR | KBD_STAT_KPKD); reg_val |= KBD_STAT_KPKD; reg_val |= KBD_STAT_KRSS | KBD_STAT_KDSC; - __raw_writew(reg_val, KPSR); + __raw_writew(reg_val, kpp_dev.base + KPSR); reg_val |= KBD_STAT_KDIE; reg_val &= ~KBD_STAT_KRIE; - __raw_writew(reg_val, KPSR); + __raw_writew(reg_val, kpp_dev.base + KPSR); has_leaning_key = keypad->learning; mxckpd_keycodes = keypad->matrix; @@ -912,7 +1077,7 @@ static int mxc_kpp_probe(struct platform_device *pdev) retval = request_irq(irq, mxc_kpp_interrupt, 0, MOD_NAME, MOD_NAME); if (retval) { pr_debug("KPP: request_irq(%d) returned error %d\n", - MXC_INT_KPP, retval); + irq, retval); goto err3; } @@ -950,16 +1115,16 @@ static int mxc_kpp_remove(struct platform_device *pdev) * Set KDIE control bit, clear KRIE control bit (avoid false release * events. Disable the keypad GPIO pins. */ - __raw_writew(0x00, KPCR); - __raw_writew(0x00, KPDR); - __raw_writew(0x00, KDDR); + __raw_writew(0x00, kpp_dev.base + KPCR); + __raw_writew(0x00, kpp_dev.base + KPDR); + __raw_writew(0x00, kpp_dev.base + KDDR); - reg_val = __raw_readw(KPSR); + reg_val = __raw_readw(kpp_dev.base + KPSR); reg_val |= KBD_STAT_KPKD; reg_val &= ~KBD_STAT_KRSS; reg_val |= KBD_STAT_KDIE; reg_val &= ~KBD_STAT_KRIE; - __raw_writew(reg_val, KPSR); + __raw_writew(reg_val, kpp_dev.base + KPSR); gpio_keypad_inactive(); clk_disable(kpp_clk); diff --git a/drivers/input/misc/mma7455l.c b/drivers/input/misc/mma7455l.c index 1cee2d1add04..48dca60d2cfe 100644 --- a/drivers/input/misc/mma7455l.c +++ b/drivers/input/misc/mma7455l.c @@ -583,6 +583,8 @@ static int __devexit mma7455l_remove(struct i2c_client *client) { struct mma7455l_info *mma = dev_get_drvdata(&client->dev); + free_irq(client->irq, mma); + sysfs_remove_group(&client->dev.kobj, &mma7455l_attr_group); input_unregister_device(mma->input_dev); dev_set_drvdata(&client->dev, NULL); diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index ba9d38c3f412..0f74aeee2aea 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -17,14 +17,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include <linux/hwmon.h> +#include <linux/device.h> #include <linux/init.h> -#include <linux/err.h> #include <linux/delay.h> #include <linux/input.h> #include <linux/interrupt.h> -#include <linux/slab.h> -#include <linux/gpio.h> #include <linux/spi/spi.h> #include <linux/spi/ads7846.h> #include <asm/irq.h> @@ -33,9 +30,7 @@ /* * This code has been heavily tested on a Nokia 770, and lightly * tested on other ads7846 devices (OSK/Mistral, Lubbock). - * TSC2046 is just newer ads7846 silicon. - * Support for ads7843 tested on Atmel at91sam926x-EK. - * Support for ads7845 has only been stubbed in. + * Support for ads7843 and ads7845 has only been stubbed in. * * IRQ handling needs a workaround because of a shortcoming in handling * edge triggered IRQs on some platforms like the OMAP1/2. These @@ -51,8 +46,7 @@ * files. */ -#define TS_POLL_DELAY (1 * 1000000) /* ns delay before the first sample */ -#define TS_POLL_PERIOD (5 * 1000000) /* ns delay between samples */ +#define TS_POLL_PERIOD msecs_to_jiffies(10) /* this driver doesn't aim at the peak continuous sample rate */ #define SAMPLE_BITS (8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */) @@ -61,77 +55,57 @@ 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 *** and range adjustment. + * which need byteswapping then range adjustment. */ - u16 x; - u16 y; - u16 z1, z2; - int ignore; -}; - -/* - * We allocate this separately to avoid cache line sharing issues when - * driver is used with DMA-based SPI controllers (like atmel_spi) on - * systems where main memory is not DMA-coherent (most non-x86 boards). - */ -struct ads7846_packet { - u8 read_x, read_y, read_z1, read_z2, pwrdown; - u16 dummy; /* for the pwrdown read */ - struct ts_event tc; + __be16 x; + __be16 y; + __be16 z1, z2; + int ignore; }; struct ads7846 { struct input_dev *input; char phys[32]; - char name[32]; - struct spi_device *spi; - -#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE) - struct attribute_group *attr_group; - struct device *hwmon; -#endif + u32 *txbuf; + u32 *rxbuf; + u8 buflen; + u8 skip_samples; + u16 rotate; + struct spi_device *spi; u16 model; - u16 vref_mv; u16 vref_delay_usecs; u16 x_plate_ohms; u16 pressure_max; - bool swap_xy; - - struct ads7846_packet *packet; + u8 read_x, read_y, read_z1, read_z2, pwrdown; + u16 zerro; /* to send zerros while receiving */ + u16 dummy; /* for the pwrdown read */ + struct ts_event tc; - struct spi_transfer xfer[18]; + struct spi_transfer xfer[10]; struct spi_message msg[5]; struct spi_message *last_msg; int msg_idx; int read_cnt; int read_rep; int last_read; + int skip_this_sample; u16 debounce_max; u16 debounce_tol; u16 debounce_rep; - u16 penirq_recheck_delay_usecs; - spinlock_t lock; - struct hrtimer timer; + struct timer_list timer; /* P: lock */ unsigned pendown:1; /* P: lock */ unsigned pending:1; /* P: lock */ // FIXME remove "irq_disabled" unsigned irq_disabled:1; /* P: lock */ unsigned disabled:1; - unsigned is_suspended:1; - int (*filter)(void *data, int data_idx, int *val); - void *filter_data; - void (*filter_cleanup)(void *data); int (*get_pendown_state)(void); - int gpio_pendown; - - void (*wait_for_sync)(void); }; /* leave chip selected when we're done, for quicker re-select? */ @@ -141,6 +115,7 @@ struct ads7846 { #define CS_CHANGE(xfer) ((xfer).cs_change = 0) #endif + /*--------------------------------------------------------------------------*/ /* The ADS7846 has touchscreen and other sensors. @@ -167,16 +142,19 @@ struct ads7846 { #define MAX_12BIT ((1<<12)-1) /* leave ADC powered up (disables penirq) between differential samples */ -#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_12BIT_DFR(x) (ADS_START | ADS_A2A1A0_d_ ## x \ + | ADS_12_BIT | ADS_DFR) + +#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 */ +/* alternate ads7843 commands */ +#define ALT_READ_Y (READ_12BIT_DFR(y) | ADS_PD10_ALL_ON) +#define ALT_READ_X (READ_12BIT_DFR(x) | ADS_PD10_ALL_ON) /* single-ended samples need to first power up reference voltage; * we leave both ADC and VREF powered @@ -184,15 +162,21 @@ struct ads7846 { #define READ_12BIT_SER(x) (ADS_START | ADS_A2A1A0_ ## x \ | ADS_12_BIT | ADS_SER) -#define REF_ON (READ_12BIT_DFR(x, 1, 1)) -#define REF_OFF (READ_12BIT_DFR(y, 0, 0)) +#define REF_ON (READ_12BIT_DFR(x) | ADS_PD10_ALL_ON) +#define REF_OFF (READ_12BIT_DFR(y) | ADS_PD10_PDOWN) + +#define MAX_BUF_SAMPLE_LEN (20) +/* Following configuration should be done in the platform configuration */ +#define SCREEN_LANDSCAPE 1 +#undef SCREEN_PORTRAIT +#define MAX_DIFF_BETWEEN_SAMPLES_X 100 +#define MAX_DIFF_BETWEEN_SAMPLES_Y 100 + /*--------------------------------------------------------------------------*/ /* * 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. */ struct ser_req { @@ -200,6 +184,7 @@ struct ser_req { u8 command; u8 ref_off; u16 scratch; + u16 zerro; __be16 sample; struct spi_message msg; struct spi_transfer xfer[6]; @@ -211,247 +196,132 @@ static void ads7846_disable(struct ads7846 *ts); static int device_suspended(struct device *dev) { struct ads7846 *ts = dev_get_drvdata(dev); - return ts->is_suspended || ts->disabled; + return dev->power.power_state.event != PM_EVENT_ON || ts->disabled; } +static int ads7843_setup_buffers(struct device *dev) +{ + struct ads7846 *ts = dev_get_drvdata(dev); + int i; + + ts->txbuf = kzalloc(sizeof(u32) * ts->buflen * 3, GFP_KERNEL); + if (!ts->txbuf) + return -ENOMEM; + + ts->rxbuf = kzalloc(sizeof(u32) * ts->buflen * 3, GFP_KERNEL); + if (!ts->rxbuf) { + kfree(ts->txbuf); + return -ENOMEM; + } + for (i = 0; i < ((ts->buflen * 3) / 2); i++) +#if defined( SCREEN_LANDSCAPE ) + ts->txbuf[i] = (READ_12BIT_DFR(x) | ADS_PD10_PDOWN) << 8; +#else + ts->txbuf[i] = (READ_12BIT_DFR(y) | ADS_PD10_PDOWN) << 8; +#endif + for (; i < ts->buflen * 3; i++) +#if defined( SCREEN_LANDSCAPE ) + ts->txbuf[i] = (READ_12BIT_DFR(y) | ADS_PD10_PDOWN) << 8; +#else + ts->txbuf[i] = (READ_12BIT_DFR(x) | ADS_PD10_PDOWN) << 8; +#endif + return 0; +} + + static int ads7846_read12_ser(struct device *dev, unsigned command) { struct spi_device *spi = to_spi_device(dev); struct ads7846 *ts = dev_get_drvdata(dev); struct ser_req *req = kzalloc(sizeof *req, GFP_KERNEL); int status; - int use_internal; + int sample; + int i; if (!req) return -ENOMEM; spi_message_init(&req->msg); - /* 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); - } + /* 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].tx_buf = &req->zerro; + 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; /* 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].tx_buf = &req->zerro; 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 ... */ - /* converter in low power mode & enable PENIRQ */ - req->ref_off = PWRDOWN; + /* turn off reference */ + 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); - + // TODO req->xfer[3].tx_buf = &req->zerro; + req->xfer[5].tx_buf = &req->zerro; 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); + + /* 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); ts->irq_disabled = 1; - disable_irq(spi->irq); + disable_irq_nosync(spi->irq); status = spi_sync(spi, &req->msg); ts->irq_disabled = 0; enable_irq(spi->irq); - if (status == 0) { - /* on-wire is a must-ignore bit, a BE12 value, then padding */ - status = be16_to_cpu(req->sample); - status = status >> 3; - status &= 0x0fff; - } + if (req->msg.status) + status = req->msg.status; + + /* on-wire is a must-ignore bit, a BE12 value, then padding */ + sample = be16_to_cpu(req->sample); + sample = sample >> 3; + sample &= 0x0fff; kfree(req); - return status; + return status ? status : sample; } -#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE) - -#define SHOW(name, var, adjust) static ssize_t \ +#define SHOW(name) 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(var) | ADS_PD10_ALL_ON); \ + READ_12BIT_SER(name) | ADS_PD10_ALL_ON); \ if (v < 0) \ return v; \ - return sprintf(buf, "%u\n", adjust(ts, v)); \ + return sprintf(buf, "%u\n", (unsigned) v); \ } \ static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL); - -/* Sysfs conventions report temperatures in millidegrees Celsius. - * 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 *= ts->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 device *hwmon; - int err; - - /* hwmon sensors need a reference voltage */ - switch (ts->model) { - case 7846: - if (!ts->vref_mv) { - dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n"); - ts->vref_mv = 2500; - } - break; - case 7845: - case 7843: - if (!ts->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 +SHOW(temp0) +SHOW(temp1) +SHOW(vaux) +SHOW(vbatt) 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; } @@ -477,11 +347,10 @@ static ssize_t ads7846_disable_store(struct device *dev, const char *buf, size_t count) { struct ads7846 *ts = dev_get_drvdata(dev); - unsigned long i; - - if (strict_strtoul(buf, 10, &i)) - return -EINVAL; + char *endp; + int i; + i = simple_strtoul(buf, &endp, 10); spin_lock_irq(&ts->lock); if (i) @@ -496,30 +365,8 @@ static ssize_t ads7846_disable_store(struct device *dev, static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store); -static struct attribute *ads784x_attributes[] = { - &dev_attr_pen_down.attr, - &dev_attr_disable.attr, - NULL, -}; - -static struct attribute_group ads784x_attr_group = { - .attrs = ads784x_attributes, -}; - /*--------------------------------------------------------------------------*/ -static int get_pendown_state(struct ads7846 *ts) -{ - if (ts->get_pendown_state) - return ts->get_pendown_state(); - - return !gpio_get_value(ts->gpio_pendown); -} - -static void null_wait_for_sync(void) -{ -} - /* * PENIRQ only kicks the timer. The timer only reissues the SPI transfer, * to retrieve touchscreen status. @@ -531,25 +378,25 @@ static void null_wait_for_sync(void) static void ads7846_rx(void *ads) { struct ads7846 *ts = ads; - struct ads7846_packet *packet = ts->packet; + 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. + /* adjust: on-wire is a must-ignore bit, a BE12 value, then padding; + * built from two 8 bit values written msb-first. */ - x = packet->tc.x; - y = packet->tc.y; - z1 = packet->tc.z1; - z2 = packet->tc.z2; + 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; /* range filtering */ if (x == MAX_12BIT) x = 0; - if (ts->model == 7843) { - Rt = ts->pressure_max / 2; - } else if (likely(x && z1)) { + if (likely(x && z1 && !device_suspended(&ts->spi->dev))) { /* compute touch pressure resistance using equation #2 */ Rt = z2; Rt -= z1; @@ -557,194 +404,281 @@ static void ads7846_rx(void *ads) Rt *= ts->x_plate_ohms; Rt /= z1; Rt = (Rt + 2047) >> 12; - } else { + } else 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 - */ - if (packet->tc.ignore || Rt > ts->pressure_max) { -#ifdef VERBOSE - pr_debug("%s: ignored %d pressure %d\n", - dev_name(&ts->spi->dev), packet->tc.ignore, Rt); -#endif - hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), - HRTIMER_MODE_REL); + * 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); return; } - /* Maybe check the pendown state before reporting. This discards - * false readings when the pen is lifted. + /* 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... + * + * REVISIT: the touchscreen might not be connected; this code + * won't notice that, even if nPENIRQ never fires ... */ - if (ts->penirq_recheck_delay_usecs) { - udelay(ts->penirq_recheck_delay_usecs); - if (!get_pendown_state(ts)) - Rt = 0; + 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; } - /* 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. - * - * 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 (Rt) { - struct input_dev *input = ts->input; + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + sync = 1; + } - if (!ts->pendown) { - input_report_key(input, BTN_TOUCH, 1); - ts->pendown = 1; -#ifdef VERBOSE - dev_dbg(&ts->spi->dev, "DOWN\n"); + 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", dev_name(&ts->spi->dev), + x, y, Rt, Rt ? "" : " UP"); #endif + + spin_lock_irqsave(&ts->lock, flags); + + ts->pendown = (Rt != 0); + mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD); + + spin_unlock_irqrestore(&ts->lock, flags); +} + +static inline u16 ad7843_get_sample_val(u32 sample) +{ + return (((((sample & 0x00ff0000) >> 8) | (sample >> 24)) >> 3) & 0x0fff); +} + +static u32 ad7843_get_better_values(struct ads7846 *ts, int index, int skiplimit) +{ + u32 diff12, diff23, diff31; + u32 vals[3]; + int i; + + for (i = 0; i < 3; i++) { + vals[i] = ad7843_get_sample_val(ts->rxbuf[index+i]); + if (vals[i] == 0x0fff || vals[i] == 0) { + ts->skip_this_sample = 1; + return 0; } + } - if (ts->swap_xy) - swap(x, y); + diff12 = (vals[0] > vals[1]) ? vals[0] - vals[1] : vals[1] - vals[0]; + if (diff12 > skiplimit) { + ts->skip_this_sample = 1; + return 0; + } - input_report_abs(input, ABS_X, x); - input_report_abs(input, ABS_Y, y); - input_report_abs(input, ABS_PRESSURE, Rt); + diff23 = (vals[1] > vals[2]) ? vals[1] - vals[2] : vals[2] - vals[1]; + if (diff23 > skiplimit) { + ts->skip_this_sample = 1; + return 0; + } - input_sync(input); -#ifdef VERBOSE - dev_dbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt); -#endif + diff31 = (vals[2] > vals[0]) ? vals[2] - vals[0] : vals[0] - vals[2]; + if (diff31 > skiplimit) { + ts->skip_this_sample = 1; + return 0; } - hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), - HRTIMER_MODE_REL); + if (diff12 < diff23 && diff12 < diff31) + return (vals[0] + vals[1]) / 2; + if (diff23 < diff12 && diff23 < diff31) + return (vals[1] + vals[2]) / 2; + + return (vals[0] + vals[2]) / 2; } -static int ads7846_debounce(void *ads, int data_idx, int *val) +static void ads7843_rx_average(void *ads) +{ + struct ads7846 *ts = ads; + struct input_dev *input_dev = ts->input; + u16 x, y, temp; + unsigned long flags; + int i, sample_count; + + dev_dbg(&ts->spi->dev, "%s\n", __FUNCTION__); + + for (i = 0, y = 0, x = 0, sample_count = 0; i < (ts->buflen * 3 / 2); i+=3) { + if (i >= ts->skip_samples*3) { + temp = ad7843_get_better_values(ts, i, MAX_DIFF_BETWEEN_SAMPLES_Y); + if (!ts->skip_this_sample) { + if (ts->rotate == 180) { + y += MAX_12BIT - temp; + } else if (ts->rotate == 0) { + y += temp; + } else { + dev_info(&ts->spi->dev, + "Rotate mode %d, not implemented yet\n", + ts->rotate); + } + sample_count++; + } + } + ts->skip_this_sample = 0; + } + + if (!sample_count) + goto sample_taken; + + y /= sample_count; + + for (sample_count = 0; i < (ts->buflen * 3); i+=3) { + if (i >= (ts->skip_samples + ts->buflen / 2)*3) { + temp = ad7843_get_better_values(ts, i, MAX_DIFF_BETWEEN_SAMPLES_X); + if (!ts->skip_this_sample) { + if (ts->rotate == 180) { + x += MAX_12BIT - temp; + } else if (ts->rotate == 0) { + x += temp; + } else { + dev_info(&ts->spi->dev, + "Rotate mode %d, not implemented yet\n", + ts->rotate); + } + sample_count++; + } + } + ts->skip_this_sample = 0; + } + + if (!sample_count) + goto sample_taken; + + x /= sample_count; + + if (ts->pendown) { + + input_report_key(input_dev, BTN_TOUCH, 1); + input_report_abs(input_dev, ABS_PRESSURE, ts->pressure_max / 2); + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + } else { + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_abs(input_dev, ABS_PRESSURE, 0); + } + + input_sync(input_dev); + dev_dbg(&ts->spi->dev, "%d/%d %s\n", x, y, ts->pendown ? "" : " UP"); + +sample_taken: + if (ts->pendown) { + spin_lock_irqsave(&ts->lock, flags); + mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD); + spin_unlock_irqrestore(&ts->lock, flags); + } +} + +static void ads7846_debounce(void *ads) { struct ads7846 *ts = ads; + struct spi_message *m; + struct spi_transfer *t; + int val; + int status; - if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) { - /* Start over collecting consistent readings. */ - ts->read_rep = 0; + 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)) { /* 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; - return ADS7846_FILTER_IGNORE; + /* Last message will contain ads7846_rx() as the + * completion function. + */ + m = ts->last_msg; } + /* 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; - return ADS7846_FILTER_OK; - } else { + m++; + } 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 ads7846_packet *packet = ts->packet; - struct spi_message *m; - struct spi_transfer *t; - int val; - int action; - int status; - - m = &ts->msg[ts->msg_idx]; - t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); - - /* 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_cpup((__be16 *)t->rx_buf) >> 3; - - action = ts->filter(ts->filter_data, ts->msg_idx, &val); - switch (action) { - case ADS7846_FILTER_REPEAT: - break; - case ADS7846_FILTER_IGNORE: - packet->tc.ignore = 1; - /* Last message will contain ads7846_rx() as the - * completion function. - */ - m = ts->last_msg; - break; - case ADS7846_FILTER_OK: - *(u16 *)t->rx_buf = val; - packet->tc.ignore = 0; - m = &ts->msg[++ts->msg_idx]; - break; - default: - BUG(); - } - ts->wait_for_sync(); status = spi_async(ts->spi, m); if (status) dev_err(&ts->spi->dev, "spi_async --> %d\n", status); } -static enum hrtimer_restart ads7846_timer(struct hrtimer *handle) +static void ads7846_timer(unsigned long handle) { - struct ads7846 *ts = container_of(handle, struct ads7846, timer); - int status = 0; + struct ads7846 *ts = (void *)handle; + struct input_dev *input_dev = ts->input; + int status = 0; - spin_lock(&ts->lock); - - if (unlikely(!get_pendown_state(ts) || - 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); + /* get sample */ + ts->pendown = ts->get_pendown_state(); + spin_lock_irq(&ts->lock); + if (ts->model == 7843) { + ts->pending = 0; + if (unlikely(!ts->pendown)) { - ts->pendown = 0; -#ifdef VERBOSE - dev_dbg(&ts->spi->dev, "UP\n"); -#endif - } + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_abs(input_dev, ABS_PRESSURE, 0); + input_sync(input_dev); - /* measurement cycle ended */ - if (!device_suspended(&ts->spi->dev)) { - ts->irq_disabled = 0; - enable_irq(ts->spi->irq); + if (!device_suspended(&ts->spi->dev)) { + ts->irq_disabled = 0; + enable_irq(ts->spi->irq); + } + } else { + /* pen is still down, continue with the measurement */ + status = spi_async(ts->spi, &ts->msg[0]); + if (status) + dev_err(&ts->spi->dev, "spi_async --> %d\n", status); } - ts->pending = 0; } else { - /* pen is still down, continue with the measurement */ - ts->msg_idx = 0; - ts->wait_for_sync(); - status = spi_async(ts->spi, &ts->msg[0]); - if (status) - dev_err(&ts->spi->dev, "spi_async --> %d\n", status); + if (unlikely(ts->msg_idx && !ts->pendown)) { + /* 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; + status = spi_async(ts->spi, &ts->msg[0]); + if (status) + dev_err(&ts->spi->dev, "spi_async --> %d\n", status); + } } - - spin_unlock(&ts->lock); - return HRTIMER_NORESTART; + spin_unlock_irq(&ts->lock); } static irqreturn_t ads7846_irq(int irq, void *handle) @@ -753,7 +687,8 @@ static irqreturn_t ads7846_irq(int irq, void *handle) unsigned long flags; spin_lock_irqsave(&ts->lock, flags); - if (likely(get_pendown_state(ts))) { + + if (likely(ts->get_pendown_state())) { if (!ts->irq_disabled) { /* The ARM do_simple_IRQ() dispatcher doesn't act * like the other dispatchers: it will report IRQs @@ -763,8 +698,7 @@ static irqreturn_t ads7846_irq(int irq, void *handle) ts->irq_disabled = 1; disable_irq_nosync(ts->spi->irq); ts->pending = 1; - hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY), - HRTIMER_MODE_REL); + mod_timer(&ts->timer, jiffies); } } spin_unlock_irqrestore(&ts->lock, flags); @@ -785,7 +719,7 @@ static void ads7846_disable(struct ads7846 *ts) /* are we waiting for IRQ, or polling? */ if (!ts->pending) { ts->irq_disabled = 1; - disable_irq(ts->spi->irq); + disable_irq_nosync(ts->spi->irq); } else { /* the timer will run at least once more, and * leave everything in a clean state, IRQ disabled @@ -800,6 +734,7 @@ static void ads7846_disable(struct ads7846 *ts) /* we know the chip's in lowpower mode since we always * leave it that way after every request */ + } /* Must be called with ts->lock held */ @@ -819,7 +754,7 @@ static int ads7846_suspend(struct spi_device *spi, pm_message_t message) spin_lock_irq(&ts->lock); - ts->is_suspended = 1; + spi->dev.power.power_state = message; ads7846_disable(ts); spin_unlock_irq(&ts->lock); @@ -834,7 +769,7 @@ static int ads7846_resume(struct spi_device *spi) spin_lock_irq(&ts->lock); - ts->is_suspended = 0; + spi->dev.power.power_state = PMSG_ON; ads7846_enable(ts); spin_unlock_irq(&ts->lock); @@ -842,45 +777,13 @@ static int ads7846_resume(struct spi_device *spi) return 0; } -static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts) -{ - struct ads7846_platform_data *pdata = spi->dev.platform_data; - int err; - - /* REVISIT when the irq can be triggered active-low, or if for some - * reason the touchscreen isn't hooked up, we don't need to access - * the pendown state. - */ - if (!pdata->get_pendown_state && !gpio_is_valid(pdata->gpio_pendown)) { - dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n"); - return -EINVAL; - } - - if (pdata->get_pendown_state) { - ts->get_pendown_state = pdata->get_pendown_state; - return 0; - } - - err = gpio_request(pdata->gpio_pendown, "ads7846_pendown"); - if (err) { - dev_err(&spi->dev, "failed to request pendown GPIO%d\n", - pdata->gpio_pendown); - return err; - } - - ts->gpio_pendown = pdata->gpio_pendown; - return 0; -} - static int __devinit ads7846_probe(struct spi_device *spi) { struct ads7846 *ts; - struct ads7846_packet *packet; struct input_dev *input_dev; struct ads7846_platform_data *pdata = spi->dev.platform_data; struct spi_message *m; struct spi_transfer *x; - int vref; int err; if (!spi->irq) { @@ -900,33 +803,46 @@ static int __devinit ads7846_probe(struct spi_device *spi) return -EINVAL; } + /* REVISIT when the irq can be triggered active-low, or if for some + * reason the touchscreen isn't hooked up, we don't need to access + * the pendown state. + */ + if (pdata->get_pendown_state == NULL) { + dev_dbg(&spi->dev, "no get_pendown_state function?\n"); + return -EINVAL; + } + /* We'd set TX wordsize 8 bits and RX wordsize to 13 bits ... except * that even if the hardware can do that, the SPI controller driver * may not. So we stick to very-portable 8 bit words, both RX and TX. */ spi->bits_per_word = 8; - spi->mode = SPI_MODE_0; - err = spi_setup(spi); - if (err < 0) - return err; ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL); - packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL); input_dev = input_allocate_device(); - if (!ts || !packet || !input_dev) { + if (!ts || !input_dev) { err = -ENOMEM; goto err_free_mem; } dev_set_drvdata(&spi->dev, ts); - ts->packet = packet; + spi->dev.power.power_state = PMSG_ON; + ts->spi = spi; ts->input = input_dev; - ts->vref_mv = pdata->vref_mv; - ts->swap_xy = pdata->swap_xy; - hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->buflen = pdata->buflen ? : MAX_BUF_SAMPLE_LEN; + ts->buflen = ts->buflen & ~0x1; /* must be even */ + + if (ads7843_setup_buffers(&spi->dev)) { + dev_dbg(&spi->dev, "error allocating memory for sample buffers\n"); + err = -ENOMEM; + goto err_free_mem; + } + + init_timer(&ts->timer); + ts->timer.data = (unsigned long) ts; ts->timer.function = ads7846_timer; spin_lock_init(&ts->lock); @@ -935,40 +851,22 @@ 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; + ts->skip_samples = pdata->skip_samples ? : 0; + ts->rotate = pdata->rotate ? : 0; - 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) { + 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; - ts->filter = ads7846_debounce; - ts->filter_data = ts; + if (ts->debounce_rep > ts->debounce_max + 1) + ts->debounce_rep = ts->debounce_max - 1; } else - ts->filter = ads7846_no_filter; - - err = setup_pendown(spi, ts); - if (err) - goto err_cleanup_filter; - - if (pdata->penirq_recheck_delay_usecs) - ts->penirq_recheck_delay_usecs = - pdata->penirq_recheck_delay_usecs; - - ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync; + ts->debounce_tol = ~0; + ts->get_pendown_state = pdata->get_pendown_state; snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev)); - snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model); - input_dev->name = ts->name; + input_dev->name = "ADS784x Touchscreen"; input_dev->phys = ts->phys; input_dev->dev.parent = &spi->dev; @@ -983,9 +881,8 @@ static int __devinit ads7846_probe(struct spi_device *spi) pdata->y_max ? : MAX_12BIT, 0, 0); input_set_abs_params(input_dev, ABS_PRESSURE, - pdata->pressure_min, pdata->pressure_max, 0, 0); - - vref = pdata->keep_vref_on; + pdata->pressure_min ? : 0, + pdata->pressure_max ? : 1, 0, 0); /* set up the transfers to read touchscreen state; this assumes we * use formula #2 for pressure, not #3. @@ -995,209 +892,176 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_init(m); - /* y- still on; turn on only y+ (and ADC) */ - packet->read_y = READ_Y(vref); - x->tx_buf = &packet->read_y; - x->len = 1; - spi_message_add_tail(x, m); - - x++; - x->rx_buf = &packet->tc.y; - x->len = 2; - spi_message_add_tail(x, m); - - /* the first sample after switching drivers can be low quality; - * optionally discard it, using a second one after the signals - * have had enough time to stabilize. - */ - if (pdata->settle_delay_usecs) { - x->delay_usecs = pdata->settle_delay_usecs; - - x++; - x->tx_buf = &packet->read_y; - x->len = 1; + if (ts->model == 7843) { + x->tx_buf = ts->txbuf; + x->rx_buf = ts->rxbuf; + x->len = ts->buflen * sizeof(u32) * 3; /* For every sample we take 3 samples and choose the better 2 */ spi_message_add_tail(x, m); - x++; - x->rx_buf = &packet->tc.y; - x->len = 2; - spi_message_add_tail(x, m); - } - - m->complete = ads7846_rx_val; - m->context = ts; - - m++; - spi_message_init(m); - - /* turn y- off, x+ on, then leave in lowpower */ - x++; - packet->read_x = READ_X(vref); - x->tx_buf = &packet->read_x; - x->len = 1; - spi_message_add_tail(x, m); - - x++; - x->rx_buf = &packet->tc.x; - x->len = 2; - spi_message_add_tail(x, m); - - /* ... maybe discard first sample ... */ - if (pdata->settle_delay_usecs) { - x->delay_usecs = pdata->settle_delay_usecs; + m->complete = ads7843_rx_average; + m->context = ts; - x++; - x->tx_buf = &packet->read_x; + ts->last_msg = m; + } else { + /* y- still on; turn on only y+ (and ADC) */ + ts->read_y = READ_Y; + x->tx_buf = &ts->read_y; x->len = 1; spi_message_add_tail(x, m); x++; - x->rx_buf = &packet->tc.x; + x->rx_buf = &ts->tc.y; x->len = 2; spi_message_add_tail(x, m); - } - m->complete = ads7846_rx_val; - m->context = ts; + m->complete = ads7846_debounce; + m->context = ts; - /* turn y+ off, x- on; we'll use formula #2 */ - if (ts->model == 7846) { m++; spi_message_init(m); + /* turn y- off, x+ on, then leave in lowpower */ x++; - packet->read_z1 = READ_Z1(vref); - x->tx_buf = &packet->read_z1; + ts->read_x = READ_X; + x->tx_buf = &ts->read_x; x->len = 1; spi_message_add_tail(x, m); x++; - x->rx_buf = &packet->tc.z1; + x->rx_buf = &ts->tc.x; x->len = 2; spi_message_add_tail(x, m); - /* ... maybe discard first sample ... */ - if (pdata->settle_delay_usecs) { - x->delay_usecs = pdata->settle_delay_usecs; + m->complete = ads7846_debounce; + m->context = ts; + + /* turn y+ off, x- on; we'll use formula #2 */ + if (ts->model == 7846) { + m++; + spi_message_init(m); x++; - x->tx_buf = &packet->read_z1; + ts->read_z1 = READ_Z1; + x->tx_buf = &ts->read_z1; x->len = 1; spi_message_add_tail(x, m); x++; - x->rx_buf = &packet->tc.z1; + x->rx_buf = &ts->tc.z1; x->len = 2; spi_message_add_tail(x, m); - } - - m->complete = ads7846_rx_val; - m->context = ts; - - m++; - spi_message_init(m); - x++; - packet->read_z2 = READ_Z2(vref); - x->tx_buf = &packet->read_z2; - x->len = 1; - spi_message_add_tail(x, m); - - x++; - x->rx_buf = &packet->tc.z2; - x->len = 2; - spi_message_add_tail(x, m); + m->complete = ads7846_debounce; + m->context = ts; - /* ... maybe discard first sample ... */ - if (pdata->settle_delay_usecs) { - x->delay_usecs = pdata->settle_delay_usecs; + m++; + spi_message_init(m); x++; - x->tx_buf = &packet->read_z2; + ts->read_z2 = READ_Z2; + x->tx_buf = &ts->read_z2; x->len = 1; spi_message_add_tail(x, m); x++; - x->rx_buf = &packet->tc.z2; + x->rx_buf = &ts->tc.z2; x->len = 2; spi_message_add_tail(x, m); - } - m->complete = ads7846_rx_val; - m->context = ts; - } + m->complete = ads7846_debounce; + m->context = ts; + } - /* power down */ - m++; - spi_message_init(m); + /* power down */ + m++; + spi_message_init(m); - x++; - packet->pwrdown = PWRDOWN; - x->tx_buf = &packet->pwrdown; - x->len = 1; - spi_message_add_tail(x, m); + x++; + ts->pwrdown = PWRDOWN; + x->tx_buf = &ts->pwrdown; + x->len = 1; + spi_message_add_tail(x, m); - x++; - x->rx_buf = &packet->dummy; - x->len = 2; - CS_CHANGE(*x); - spi_message_add_tail(x, m); + x++; + x->rx_buf = &ts->dummy; + x->len = 2; + CS_CHANGE(*x); + spi_message_add_tail(x, m); - m->complete = ads7846_rx; - m->context = ts; + m->complete = ads7846_rx; + m->context = ts; - ts->last_msg = m; + ts->last_msg = m; + } if (request_irq(spi->irq, ads7846_irq, IRQF_TRIGGER_FALLING, spi->dev.driver->name, ts)) { - dev_info(&spi->dev, - "trying pin change workaround on irq %d\n", spi->irq); - err = request_irq(spi->irq, ads7846_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - spi->dev.driver->name, ts); - if (err) { - dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); - goto err_free_gpio; - } + dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); + err = -EBUSY; + goto err_free_buf; } - 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 and vREF off; avoid + /* take a first sample, leaving nPENIRQ active; avoid * the touchscreen, in case it's not connected. */ - (void) ads7846_read12_ser(&spi->dev, - READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON); + if (ts->model != 7843) { + /* take a first sample, leaving nPENIRQ active; avoid + * the touchscreen, in case it's not connected. + */ + (void) ads7846_read12_ser(&spi->dev, + READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON); + } - err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group); - if (err) - goto err_remove_hwmon; + /* ads7843/7845 don't have temperature sensors, and + * use the other sensors a bit differently too + */ + if (ts->model == 7846) { + device_create_file(&spi->dev, &dev_attr_temp0); + device_create_file(&spi->dev, &dev_attr_temp1); + } + + if (ts->model != 7845 && ts->model != 7843) + device_create_file(&spi->dev, &dev_attr_vbatt); + + if (ts->model != 7843) { + device_create_file(&spi->dev, &dev_attr_vaux); + } + + device_create_file(&spi->dev, &dev_attr_pen_down); + device_create_file(&spi->dev, &dev_attr_disable); err = input_register_device(input_dev); if (err) - goto err_remove_attr_group; + goto err_remove_attr; return 0; - err_remove_attr_group: - sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group); - err_remove_hwmon: - ads784x_hwmon_unregister(spi, ts); - err_free_irq: + err_remove_attr: + device_remove_file(&spi->dev, &dev_attr_disable); + device_remove_file(&spi->dev, &dev_attr_pen_down); + if (ts->model == 7846) { + device_remove_file(&spi->dev, &dev_attr_temp1); + device_remove_file(&spi->dev, &dev_attr_temp0); + } + + if (ts->model != 7845 && ts->model != 7843) + device_remove_file(&spi->dev, &dev_attr_vbatt); + + if (ts->model != 7843) { + device_remove_file(&spi->dev, &dev_attr_vaux); + } + free_irq(spi->irq, ts); - err_free_gpio: - if (ts->gpio_pendown != -1) - gpio_free(ts->gpio_pendown); - err_cleanup_filter: - if (ts->filter_cleanup) - ts->filter_cleanup(ts->filter_data); + + err_free_buf: + if (ts->txbuf) + kfree(ts->txbuf); + if (ts->rxbuf) + kfree(ts->rxbuf); err_free_mem: input_free_device(input_dev); - kfree(packet); kfree(ts); return err; } @@ -1206,24 +1070,33 @@ 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, &ads784x_attr_group); + if (ts->txbuf) + kfree(ts->txbuf); + if (ts->rxbuf) + kfree(ts->rxbuf); - free_irq(ts->spi->irq, ts); - /* suspend left the IRQ disabled */ - enable_irq(ts->spi->irq); + device_remove_file(&spi->dev, &dev_attr_disable); + device_remove_file(&spi->dev, &dev_attr_pen_down); + if (ts->model == 7846) { + device_remove_file(&spi->dev, &dev_attr_temp1); + device_remove_file(&spi->dev, &dev_attr_temp0); + } + + if (ts->model != 7845 && ts->model != 7843) + device_remove_file(&spi->dev, &dev_attr_vbatt); - if (ts->gpio_pendown != -1) - gpio_free(ts->gpio_pendown); + if (ts->model != 7843) { + device_remove_file(&spi->dev, &dev_attr_vaux); + } - if (ts->filter_cleanup) - ts->filter_cleanup(ts->filter_data); + free_irq(ts->spi->irq, ts); + /* suspend left the IRQ disabled */ + disable_irq_nosync(ts->spi->irq); - kfree(ts->packet); kfree(ts); dev_dbg(&spi->dev, "unregistered touchscreen\n"); diff --git a/drivers/input/touchscreen/mxc_ts.c b/drivers/input/touchscreen/mxc_ts.c index 610f31e65778..6cce76f1494f 100644 --- a/drivers/input/touchscreen/mxc_ts.c +++ b/drivers/input/touchscreen/mxc_ts.c @@ -43,6 +43,9 @@ #include <linux/pmic_external.h> #include <linux/pmic_adc.h> #include <linux/kthread.h> +#ifdef CONFIG_EARLYSUSPEND +#include <linux/earlysuspend.h> +#endif #define MXC_TS_NAME "mxc_ts" @@ -60,6 +63,30 @@ static struct task_struct *tstask; static int calibration[7]; module_param_array(calibration, int, NULL, S_IRUGO | S_IWUSR); +#ifdef CONFIG_EARLYSUSPEND + +static wait_queue_head_t ts_wait; +static int ts_suspend; + +static void stop_ts_early_suspend(struct early_suspend *h) +{ + ts_suspend = 1; +} + +static void start_ts_late_resume(struct early_suspend *h) +{ + ts_suspend = 0; + wake_up_interruptible(&ts_wait); +} + +static struct early_suspend stop_ts_early_suspend_desc = { + .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING, + .suspend = stop_ts_early_suspend, + .resume = start_ts_late_resume, +}; + +#endif + static int ts_thread(void *arg) { t_touch_screen ts_sample; @@ -69,6 +96,9 @@ static int ts_thread(void *arg) int x, y; static int last_x = -1, last_y = -1, last_press = -1; +#ifdef CONFIG_EARLYSUSPEND + wait_event_interruptible(ts_wait, !ts_suspend); +#endif memset(&ts_sample, 0, sizeof(t_touch_screen)); /* After 2 consecutive samples with the pen up, enable irq waiting */ @@ -76,14 +106,13 @@ static int ts_thread(void *arg) msleep(20); continue; } - if (!(ts_sample.contact_resistance || wait)) - { + if (!(ts_sample.contact_resistance || wait)) { msleep(20); continue; } if (ts_sample.x_position == 0 && ts_sample.y_position == 0 && - ts_sample.contact_resistance == 0) { + ts_sample.contact_resistance == 0) { x = last_x; y = last_y; } else if (calibration[6] == 0) { @@ -91,14 +120,14 @@ static int ts_thread(void *arg) y = ts_sample.y_position; } else { x = calibration[0] * (int)ts_sample.x_position + - calibration[1] * (int)ts_sample.y_position + - calibration[2]; + calibration[1] * (int)ts_sample.y_position + + calibration[2]; x /= calibration[6]; if (x < 0) x = 0; y = calibration[3] * (int)ts_sample.x_position + - calibration[4] * (int)ts_sample.y_position + - calibration[5]; + calibration[4] * (int)ts_sample.y_position + + calibration[5]; y /= calibration[6]; if (y < 0) y = 0; @@ -127,7 +156,7 @@ static int ts_thread(void *arg) /* report the BTN_TOUCH */ if (ts_sample.contact_resistance != last_press) input_event(mxc_inputdev, EV_KEY, - BTN_TOUCH, ts_sample.contact_resistance); + BTN_TOUCH, ts_sample.contact_resistance); input_sync(mxc_inputdev); last_press = ts_sample.contact_resistance; @@ -148,8 +177,7 @@ static int __init mxc_ts_init(void) mxc_inputdev = input_allocate_device(); if (!mxc_inputdev) { - printk(KERN_ERR - "mxc_ts_init: not enough memory\n"); + printk(KERN_ERR "mxc_ts_init: not enough memory\n"); return -ENOMEM; } @@ -166,11 +194,14 @@ static int __init mxc_ts_init(void) tstask = kthread_run(ts_thread, NULL, "mxc_ts"); if (IS_ERR(tstask)) { - printk(KERN_ERR - "mxc_ts_init: failed to create kthread"); + printk(KERN_ERR "mxc_ts_init: failed to create kthread"); tstask = NULL; return -1; } +#ifdef CONFIG_EARLYSUSPEND + init_waitqueue_head(&ts_wait); + register_early_suspend(&stop_ts_early_suspend_desc); +#endif printk("mxc input touchscreen loaded\n"); return 0; } @@ -186,6 +217,9 @@ static void __exit mxc_ts_exit(void) input_free_device(mxc_inputdev); mxc_inputdev = NULL; } +#ifdef CONFIG_EARLYSUSPEND + unregister_early_suspend(&stop_ts_early_suspend_desc); +#endif } late_initcall(mxc_ts_init); |