From 960f32665540af4064fe3b5b1ddd19ffe6696c87 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Tue, 26 Jun 2012 06:02:50 -0700 Subject: i.MX6: nitrogen6x: configure SD1 clock as 32K oscillator for TWL12xx slow clock --- arch/arm/mach-mx6/board-mx6q_sabrelite.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-mx6/board-mx6q_sabrelite.c b/arch/arm/mach-mx6/board-mx6q_sabrelite.c index f8e21a10df42..6669139f24d2 100644 --- a/arch/arm/mach-mx6/board-mx6q_sabrelite.c +++ b/arch/arm/mach-mx6/board-mx6q_sabrelite.c @@ -964,6 +964,7 @@ static void __init sabrelite_add_device_buttons(void) {} static iomux_v3_cfg_t n6x_sd2_pads[] = { MX6Q_USDHC_PAD_SETTING(2, 50), + MX6Q_PAD_SD1_CLK__OSC32K_32K_OUT, }; #ifdef CONFIG_WL12XX_PLATFORM_DATA -- cgit v1.2.3 From ac3dd13793916f0b873d4e68837661762a17e3e7 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Mon, 19 Mar 2012 14:56:29 -0700 Subject: imx uart: don't wait tx empty, reset port if changed --- drivers/tty/serial/imx.c | 90 +++++++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index d65c589cef60..a961f1df190b 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1172,9 +1172,12 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, { struct imx_port *sport = (struct imx_port *)port; unsigned long flags; - unsigned int ucr2, old_ucr1, old_txrxen, baud, quot; + unsigned new_ucr2, old_ucr2; + unsigned new_ufcr, old_ufcr; + unsigned old_ubir, old_ubmr; + unsigned int baud, quot; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; - unsigned int div, ufcr; + unsigned int div; unsigned long num, denom; uint64_t tdiv64; @@ -1197,26 +1200,25 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, old_csize = CS8; } + new_ucr2 = UCR2_SRST | UCR2_IRTS; if ((termios->c_cflag & CSIZE) == CS8) - ucr2 = UCR2_WS | UCR2_SRST | UCR2_IRTS; - else - ucr2 = UCR2_SRST | UCR2_IRTS; + new_ucr2 |= UCR2_WS; if (termios->c_cflag & CRTSCTS) { if( sport->have_rtscts ) { - ucr2 &= ~UCR2_IRTS; - ucr2 |= UCR2_CTSC; + new_ucr2 &= ~UCR2_IRTS; + new_ucr2 |= UCR2_CTSC; } else { termios->c_cflag &= ~CRTSCTS; } } if (termios->c_cflag & CSTOPB) - ucr2 |= UCR2_STPB; + new_ucr2 |= UCR2_STPB; if (termios->c_cflag & PARENB) { - ucr2 |= UCR2_PREN; + new_ucr2 |= UCR2_PREN; if (termios->c_cflag & PARODD) - ucr2 |= UCR2_PROE; + new_ucr2 |= UCR2_PROE; } /* @@ -1229,7 +1231,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, spin_lock_irqsave(&sport->port.lock, flags); - sport->port.read_status_mask = 0; + sport->port.read_status_mask = 0xff; if (termios->c_iflag & INPCK) sport->port.read_status_mask |= (URXD_FRMERR | URXD_PRERR); if (termios->c_iflag & (BRKINT | PARMRK)) @@ -1256,22 +1258,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, */ uart_update_timeout(port, termios->c_cflag, baud); - /* - * disable interrupts and drain transmitter - */ - old_ucr1 = readl(sport->port.membase + UCR1); - writel(old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN), - sport->port.membase + UCR1); - - while ( !(readl(sport->port.membase + USR2) & USR2_TXDC)) - barrier(); - - /* then, disable everything */ - old_txrxen = readl(sport->port.membase + UCR2); - writel(old_txrxen & ~( UCR2_TXEN | UCR2_RXEN), - sport->port.membase + UCR2); - old_txrxen &= (UCR2_TXEN | UCR2_RXEN); - if (USE_IRDA(sport)) { /* * use maximum available submodule frequency to @@ -1298,31 +1284,47 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, num -= 1; denom -= 1; - ufcr = readl(sport->port.membase + UFCR); - ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div); + old_ufcr = readl(sport->port.membase + UFCR); + new_ufcr = (old_ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div); - if (sport->use_dcedte) - ufcr |= UFCR_DCEDTE; + old_ubir = readl(sport->port.membase + UBIR); + old_ubmr = readl(sport->port.membase + UBMR); + old_ucr2 = readl(sport->port.membase + UCR2) & ~UCR2_CTS; + new_ucr2 |= old_ucr2 & (UCR2_TXEN | UCR2_RXEN); - writel(ufcr, sport->port.membase + UFCR); - - writel(num, sport->port.membase + UBIR); - writel(denom, sport->port.membase + UBMR); + if (sport->use_dcedte) + new_ufcr |= UFCR_DCEDTE; + if ((old_ufcr != new_ufcr) || (old_ucr2 != new_ucr2) || + (old_ubir != num) || (old_ubmr != denom)) { + int i; + /* software reset */ + writel(readl(sport->port.membase + UCR2) & + ~(UCR2_TXEN | UCR2_RXEN | UCR2_SRST | UCR2_CTS), + sport->port.membase + UCR2); + for (i = 0; i < 2000; i++) { + unsigned uts = readl(sport->port.membase + UTS); + if (!(uts & UTS_SOFTRST)) + break; + } + writel(new_ufcr, sport->port.membase + UFCR); + writel(num, sport->port.membase + UBIR); + writel(denom, sport->port.membase + UBMR); - if (!cpu_is_mx1()) - writel(sport->port.uartclk / div / 1000, + if (!cpu_is_mx1()) + writel(sport->port.uartclk / div / 1000, sport->port.membase + MX2_ONEMS); - writel(old_ucr1, sport->port.membase + UCR1); + /* set the parity, stop bits and data size */ + writel(new_ucr2, sport->port.membase + UCR2); - /* set the parity, stop bits and data size */ - writel(ucr2 | old_txrxen, sport->port.membase + UCR2); + if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) + imx_enable_ms(&sport->port); + pr_info("old_ufcr=%x new_ufcr=%x, old_ucr2=%x new_ucr2=%x, old_ubir=%x num=%lx, old_ubmr=%x denom=%lx\n", + old_ufcr, new_ufcr, old_ucr2, new_ucr2, old_ubir, num, old_ubmr, denom); + pr_info("clk=%i div=%i num=%li denom=%li baud=%i\n", sport->port.uartclk, div, num+1, denom+1, baud); + } spin_unlock_irqrestore(&sport->port.lock, flags); - - if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) - imx_enable_ms(&sport->port); - } static const char *imx_type(struct uart_port *port) -- cgit v1.2.3 From 5c661b1137dde54b8cc11ddc4216292461a86b6b Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Tue, 26 Jun 2012 11:16:19 -0700 Subject: i.MX6: mx6q_sabrelite: swap SD cards to have numbering match U-Boot --- arch/arm/mach-mx6/board-mx6q_sabrelite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-mx6/board-mx6q_sabrelite.c b/arch/arm/mach-mx6/board-mx6q_sabrelite.c index 6669139f24d2..c969b4856e05 100644 --- a/arch/arm/mach-mx6/board-mx6q_sabrelite.c +++ b/arch/arm/mach-mx6/board-mx6q_sabrelite.c @@ -1252,8 +1252,8 @@ static void __init mx6_sabrelite_board_init(void) imx6q_add_anatop_thermal_imx(1, &mx6q_sabrelite_anatop_thermal_data); imx6_init_fec(fec_data); imx6q_add_pm_imx(0, &mx6q_sabrelite_pm_data); - imx6q_add_sdhci_usdhc_imx(3, &mx6q_sabrelite_sd4_data); imx6q_add_sdhci_usdhc_imx(2, &mx6q_sabrelite_sd3_data); + imx6q_add_sdhci_usdhc_imx(3, &mx6q_sabrelite_sd4_data); imx_add_viv_gpu(&imx6_gpu_data, &imx6q_gpu_pdata); imx6q_sabrelite_init_usb(); imx6q_add_ahci(0, &mx6q_sabrelite_sata_data); -- cgit v1.2.3 From 6e3675d051cfeb0c57d59a03ee1a1e040e594f9c Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Wed, 27 Jun 2012 15:55:10 -0700 Subject: add focaltech-ft5x06 5-finger multi-touch driver --- drivers/input/touchscreen/Kconfig | 17 ++ drivers/input/touchscreen/Makefile | 2 + drivers/input/touchscreen/ft5x06_ts.c | 550 ++++++++++++++++++++++++++++++++++ 3 files changed, 569 insertions(+) create mode 100644 drivers/input/touchscreen/ft5x06_ts.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index a9a3cabd0add..1979ce87b6c3 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -778,4 +778,21 @@ config TOUCHSCREEN_MAX11801 To compile this driver as a module, choose M here: the module will be called max11801_ts + +config TOUCHSCREEN_FT5X06 + tristate "Focaltech FT5X06 5 point touchscreen" + select I2C + help + If you say yes here you get touchscreen support through + FocalTech's FT5X06 controller. + +config TOUCHSCREEN_FT5X06_SINGLE_TOUCH + bool "FT5X06 touchscreen as single-touch" + default N + depends on TOUCHSCREEN_FT5X06 + help + If you say yes here you get single-touch touchscreen support + on the FT5X06 I2C controller. + If you say "no", you'll get the normal 5-finger goodness. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index a453dcd562e4..e9b438d8c386 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -66,3 +66,5 @@ obj-$(CONFIG_TOUCHSCREEN_P1003) += p1003_ts.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o +obj-$(CONFIG_TOUCHSCREEN_FT5X06) += ft5x06_ts.o + diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c new file mode 100644 index 000000000000..6df8520405d7 --- /dev/null +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -0,0 +1,550 @@ +/* + * Boundary Devices FTx06 touch screen controller. + * + * Copyright (c) by Boundary Devices + * + * 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 +#include +#include +#include +#include +#include + +#ifdef CONFIG_TOUCHSCREEN_FT5X06_SINGLE_TOUCH +#else +#define USE_ABS_MT +#endif + +struct point { + int x; + int y; +}; + +struct ft5x06_ts { + struct i2c_client *client; + struct input_dev *idev; + wait_queue_head_t sample_waitq; + struct semaphore sem; + struct completion init_exit; + struct task_struct *rtask; + int use_count; + int bReady; + int irq; + unsigned gp; + struct proc_dir_entry *procentry; +}; +static const char *client_name = "ft5x06"; + +struct ft5x06_ts *gts; + +static char const procentryname[] = { + "ft5x06" +}; + +static int ts_startup(struct ft5x06_ts *ts); +static void ts_shutdown(struct ft5x06_ts *ts); + +static int ft5x06_proc_read + (char *page, + char **start, + off_t off, + int count, + int *eof, + void *data) +{ + printk(KERN_ERR "%s\n", __func__); + return 0 ; +} + +static int +ft5x06_proc_write + (struct file *file, + const char __user *buffer, + unsigned long count, + void *data) +{ + printk(KERN_ERR "%s\n", __func__); + return count ; +} + +/*-----------------------------------------------------------------------*/ +static inline void ts_evt_add(struct ft5x06_ts *ts, + unsigned buttons, struct point *p) +{ + struct input_dev *idev = ts->idev; + int i; + if (!buttons) { + /* send release to user space. */ +#ifdef USE_ABS_MT + input_event(idev, EV_ABS, ABS_MT_TOUCH_MAJOR, 0); + input_event(idev, EV_KEY, BTN_TOUCH, 0); + input_mt_sync(idev); +#else + input_report_abs(idev, ABS_PRESSURE, 0); + input_report_key(idev, BTN_TOUCH, 0); + input_sync(idev); +#endif + } else { + for (i = 0; i < buttons; i++) { +#ifdef USE_ABS_MT + input_event(idev, EV_ABS, ABS_MT_POSITION_X, p[i].x); + input_event(idev, EV_ABS, ABS_MT_POSITION_Y, p[i].y); + input_event(idev, EV_ABS, ABS_MT_TOUCH_MAJOR, 1); + input_mt_sync(idev); +#else + input_report_abs(idev, ABS_X, p[i].x); + input_report_abs(idev, ABS_Y, p[i].y); + input_report_abs(idev, ABS_PRESSURE, 1); + input_report_key(idev, BTN_TOUCH, 1); + input_sync(idev); +#endif + } + input_event(idev, EV_KEY, BTN_TOUCH, 1); + } +#ifdef USE_ABS_MT + input_sync(idev); +#endif +} + +static int ts_open(struct input_dev *idev) +{ + struct ft5x06_ts *ts = input_get_drvdata(idev); + return ts_startup(ts); +} + +static void ts_close(struct input_dev *idev) +{ + struct ft5x06_ts *ts = input_get_drvdata(idev); + ts_shutdown(ts); +} + +static inline int ts_register(struct ft5x06_ts *ts) +{ + struct input_dev *idev; + idev = input_allocate_device(); + if (idev == NULL) + return -ENOMEM; + + ts->idev = idev; + idev->name = procentryname ; + idev->id.product = ts->client->addr; + idev->open = ts_open; + idev->close = ts_close; + + __set_bit(EV_ABS, idev->evbit); + __set_bit(EV_KEY, idev->evbit); + __set_bit(BTN_TOUCH, idev->keybit); + +#ifdef USE_ABS_MT + input_set_abs_params(idev, ABS_MT_POSITION_X, 0, 1023, 0, 0); + input_set_abs_params(idev, ABS_MT_POSITION_Y, 0, 0x255, 0, 0); + input_set_abs_params(idev, ABS_MT_TOUCH_MAJOR, 0, 1, 0, 0); +#else + __set_bit(EV_SYN, idev->evbit); + input_set_abs_params(idev, ABS_X, 0, 1023, 0, 0); + input_set_abs_params(idev, ABS_Y, 0, 0x255, 0, 0); + input_set_abs_params(idev, ABS_PRESSURE, 0, 1, 0, 0); +#endif + + input_set_drvdata(idev, ts); + return input_register_device(idev); +} + +static inline void ts_deregister(struct ft5x06_ts *ts) +{ + if (ts->idev) { + input_unregister_device(ts->idev); + input_free_device(ts->idev); + ts->idev = NULL; + } +} + +#ifdef DEBUG +static void printHex(u8 const *buf, unsigned len) +{ + char hex[512]; + char *next = hex ; + char *end = hex+sizeof(hex); + + while (len--) { + next += snprintf(next, end-next, "%02x", *buf++); + if (next >= end) { + hex[sizeof(hex)-1] = '\0' ; + break; + } + } + printk(KERN_ERR "%s\n", hex); +} +#endif + +static void write_reg(struct ft5x06_ts *ts, int regnum, int value) +{ + u8 regnval[] = { + regnum, + value + }; + struct i2c_msg pkt = { + ts->client->addr, 0, sizeof(regnval), regnval + }; + int ret = i2c_transfer(ts->client->adapter, &pkt, 1); + if (ret != 1) + printk(KERN_WARNING "%s: i2c_transfer failed\n", __func__); + else + printk(KERN_ERR "%s: set register 0x%02x to 0x%02x\n", + __func__, regnum, value); +} + +static void set_mode(struct ft5x06_ts *ts, int mode) +{ + write_reg(ts, 0, (mode&7)<<4); + printk(KERN_ERR "%s: changed mode to 0x%02x\n", __func__, mode); +} + +#define WORK_MODE 0 +#define FACTORY_MODE 4 + +/*-----------------------------------------------------------------------*/ + +/* + * This is a RT kernel thread that handles the I2c accesses + * The I2c access functions are expected to be able to sleep. + */ +static int ts_thread(void *_ts) +{ + int ret; + struct point points[5]; + unsigned char buf[33]; + struct ft5x06_ts *ts = _ts; + unsigned char startch[1] = { 0 }; + struct i2c_msg readpkt[2] = { + {ts->client->addr, 0, 1, startch}, + {ts->client->addr, I2C_M_RD, sizeof(buf), buf} + }; + + struct task_struct *tsk = current; + + ts->rtask = tsk; + + daemonize("ft5x06tsd"); + /* only want to receive SIGKILL */ + allow_signal(SIGKILL); + + complete(&ts->init_exit); + + do { + int buttons = 0 ; + ts->bReady = 0; + ret = i2c_transfer(ts->client->adapter, readpkt, + ARRAY_SIZE(readpkt)); + if (ret != ARRAY_SIZE(readpkt)) { + printk(KERN_WARNING "%s: i2c_transfer failed\n", + client_name); + msleep(1000); + } else { + int i; + unsigned char *p = buf+3; +#ifdef DEBUG + printHex(buf, sizeof(buf)); +#endif + buttons = buf[2]; + if (buttons > 5) { + printk(KERN_ERR + "%s: invalid button count %02x\n", + __func__, buttons); + buttons = 0 ; + } else { + for (i = 0; i < buttons; i++) { + points[i].x = ((p[0] << 8) + | p[1]) & 0x7ff; + points[i].y = ((p[2] << 8) + | p[3]) & 0x7ff; + p += 6; + } + } + } + + if (signal_pending(tsk)) + break; +#ifdef DEBUG + printk(KERN_ERR "%s: buttons = %d, " + "points[0].x = %d, " + "points[0].y = %d\n", + client_name, buttons, points[0].x, points[0].y); +#endif + ts_evt_add(ts, buttons, points); + if (0 < buttons) + wait_event_interruptible_timeout(ts->sample_waitq, + ts->bReady, HZ/20); + else + wait_event_interruptible(ts->sample_waitq, ts->bReady); + if (gpio_get_value(ts->gp)) { + if (buttons) { + buttons = 0; + ts_evt_add(ts, buttons, points); + } + if (signal_pending(tsk)) + break; + } + } while (1); + + ts->rtask = NULL; + complete_and_exit(&ts->init_exit, 0); +} + +/* + * We only detect samples ready with this interrupt + * handler, and even then we just schedule our task. + */ +static irqreturn_t ts_interrupt(int irq, void *id) +{ + struct ft5x06_ts *ts = id; + int bit = gpio_get_value(ts->gp); + if (bit == 0) { + ts->bReady = 1; + wmb(); /* flush bReady */ + wake_up(&ts->sample_waitq); + } + return IRQ_HANDLED; +} + +#define ID_G_THGROUP 0x80 +#define ID_G_PERIODMONITOR 0x89 +#define FT5X0X_REG_HEIGHT_B 0x8a +#define FT5X0X_REG_MAX_FRAME 0x8b +#define FT5X0X_REG_FEG_FRAME 0x8e +#define FT5X0X_REG_LEFT_RIGHT_OFFSET 0x92 +#define FT5X0X_REG_UP_DOWN_OFFSET 0x93 +#define FT5X0X_REG_DISTANCE_LEFT_RIGHT 0x94 +#define FT5X0X_REG_DISTANCE_UP_DOWN 0x95 +#define FT5X0X_REG_MAX_X_HIGH 0x98 +#define FT5X0X_REG_MAX_X_LOW 0x99 +#define FT5X0X_REG_MAX_Y_HIGH 0x9a +#define FT5X0X_REG_MAX_Y_LOW 0x9b +#define FT5X0X_REG_K_X_HIGH 0x9c +#define FT5X0X_REG_K_X_LOW 0x9d +#define FT5X0X_REG_K_Y_HIGH 0x9e +#define FT5X0X_REG_K_Y_LOW 0x9f + +#define ID_G_AUTO_CLB 0xa0 +#define ID_G_B_AREA_TH 0xae + +#ifdef DEBUG +static void dumpRegs(struct ft5x06_ts *ts, unsigned start, unsigned end) +{ + u8 regbuf[512]; + unsigned char startch[1] = { start }; + int ret ; + struct i2c_msg readpkt[2] = { + {ts->client->addr, 0, 1, startch}, + {ts->client->addr, I2C_M_RD, end-start+1, regbuf} + }; + ret = i2c_transfer(ts->client->adapter, readpkt, ARRAY_SIZE(readpkt)); + if (ret != ARRAY_SIZE(readpkt)) { + printk(KERN_WARNING "%s: i2c_transfer failed\n", client_name); + } else { + printk(KERN_ERR "registers %02x..%02x\n", start, end); + printHex(regbuf, end-start+1); + } +} +#endif + +static int ts_startup(struct ft5x06_ts *ts) +{ + int ret = 0; + if (ts == NULL) + return -EIO; + + if (down_interruptible(&ts->sem)) + return -EINTR; + + if (ts->use_count++ != 0) + goto out; + + if (ts->rtask) + panic("ft5x06tsd: rtask running?"); + + ret = request_irq(ts->irq, &ts_interrupt, IRQF_TRIGGER_FALLING, + client_name, ts); + if (ret) { + printk(KERN_ERR "%s: request_irq failed, irq:%i\n", + client_name, ts->irq); + goto out; + } + +#ifdef DEBUG + set_mode(ts, FACTORY_MODE); + dumpRegs(ts, 0x4c, 0x4C); + write_reg(ts, 0x4C, 0x05); + dumpRegs(ts, 0, 0x4C); +#endif + set_mode(ts, WORK_MODE); +#ifdef DEBUG + dumpRegs(ts, 0x3b, 0x3b); + dumpRegs(ts, 0x6a, 0x6a); + dumpRegs(ts, ID_G_THGROUP, ID_G_PERIODMONITOR); + dumpRegs(ts, FT5X0X_REG_HEIGHT_B, FT5X0X_REG_K_Y_LOW); + dumpRegs(ts, ID_G_AUTO_CLB, ID_G_B_AREA_TH); +#endif + set_mode(ts, WORK_MODE); + + init_completion(&ts->init_exit); + ret = kernel_thread(ts_thread, ts, CLONE_KERNEL); + if (ret >= 0) { + wait_for_completion(&ts->init_exit); + ret = 0; + } else { + free_irq(ts->irq, ts); + } + + out: + if (ret) + ts->use_count--; + up(&ts->sem); + return ret; +} + +/* + * Release touchscreen resources. Disable IRQs. + */ +static void ts_shutdown(struct ft5x06_ts *ts) +{ + if (ts) { + down(&ts->sem); + if (--ts->use_count == 0) { + if (ts->rtask) { + send_sig(SIGKILL, ts->rtask, 1); + wait_for_completion(&ts->init_exit); + } + free_irq(ts->irq, ts); + } + up(&ts->sem); + } +} +/*-----------------------------------------------------------------------*/ + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int ts_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) + return -ENODEV; + strlcpy(info->type, "ft5x06-ts", I2C_NAME_SIZE); + return 0; +} + +static int ts_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int err = 0; + struct ft5x06_ts *ts; + struct device *dev = &client->dev; + if (gts) { + printk(KERN_ERR "%s: Error gts is already allocated\n", + client_name); + return -ENOMEM; + } + ts = kzalloc(sizeof(struct ft5x06_ts), GFP_KERNEL); + if (!ts) { + dev_err(dev, "Couldn't allocate memory for %s\n", client_name); + return -ENOMEM; + } + init_waitqueue_head(&ts->sample_waitq); + sema_init(&ts->sem, 1); + ts->client = client; + ts->irq = client->irq ; + ts->gp = irq_to_gpio(client->irq); + printk(KERN_INFO "%s: %s touchscreen irq=%i, gp=%i\n", __func__, + client_name, ts->irq, ts->gp); + i2c_set_clientdata(client, ts); + err = ts_register(ts); + if (err == 0) { + gts = ts; + ts->procentry = create_proc_entry(procentryname, 0, NULL); + if (ts->procentry) { + ts->procentry->read_proc = ft5x06_proc_read ; + ts->procentry->write_proc = ft5x06_proc_write ; + } + } else { + printk(KERN_WARNING "%s: ts_register failed\n", client_name); + ts_deregister(ts); + kfree(ts); + } + return err; +} + +static int ts_remove(struct i2c_client *client) +{ + struct ft5x06_ts *ts = i2c_get_clientdata(client); + remove_proc_entry(procentryname, 0); + if (ts == gts) { + gts = NULL; + ts_deregister(ts); + } else { + printk(KERN_ERR "%s: Error ts!=gts\n", client_name); + } + kfree(ts); + return 0; +} + + +/*-----------------------------------------------------------------------*/ + +static const struct i2c_device_id ts_idtable[] = { + { "ft5x06-ts", 0 }, + { } +}; + +static struct i2c_driver ts_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ft5x06-ts", + }, + .id_table = ts_idtable, + .probe = ts_probe, + .remove = __devexit_p(ts_remove), + .detect = ts_detect, +}; + +static int __init ts_init(void) +{ + int res = i2c_add_driver(&ts_driver); + if (res) { + printk(KERN_WARNING "%s: i2c_add_driver failed\n", client_name); + return res; + } + printk(KERN_INFO "%s: " __DATE__ "\n", client_name); + return 0; +} + +static void __exit ts_exit(void) +{ + i2c_del_driver(&ts_driver); +} + +MODULE_AUTHOR("Boundary Devices "); +MODULE_DESCRIPTION("I2C interface for FocalTech ft5x06 touch screen controller."); +MODULE_LICENSE("GPL"); + +module_init(ts_init) +module_exit(ts_exit) -- cgit v1.2.3 From e19a6d5964ea59e57a4ff3da52c98725240c7618 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Wed, 27 Jun 2012 15:56:36 -0700 Subject: i.MX6: mx6q_sabrelite: Add support for FocalTech ft5x06 driver --- arch/arm/mach-mx6/board-mx6q_sabrelite.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/mach-mx6/board-mx6q_sabrelite.c b/arch/arm/mach-mx6/board-mx6q_sabrelite.c index c969b4856e05..bb82ddec55a4 100644 --- a/arch/arm/mach-mx6/board-mx6q_sabrelite.c +++ b/arch/arm/mach-mx6/board-mx6q_sabrelite.c @@ -688,6 +688,13 @@ static struct i2c_board_info mxc_i2c2_board_info[] __initdata = { .platform_data = &tsc2007_info, .irq = gpio_to_irq(MX6Q_SABRELITE_DRGB_IRQGPIO), }, +#if defined(CONFIG_TOUCHSCREEN_FT5X06) \ + || defined(CONFIG_TOUCHSCREEN_FT5X06_MODULE) + { + I2C_BOARD_INFO("ft5x06-ts", 0x38), + .irq = gpio_to_irq(MX6Q_SABRELITE_CAP_TCH_INT1), + }, +#endif }; static void imx6q_sabrelite_usbotg_vbus(bool on) -- cgit v1.2.3 From 6aa9e872ea9c57eb1a6f02991a89882fef846737 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Wed, 27 Jun 2012 15:57:57 -0700 Subject: nitrogen6x_defconfig: add support for Focaltech ft5x06 multi-touch driver --- arch/arm/configs/nitrogen6x_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/configs/nitrogen6x_defconfig b/arch/arm/configs/nitrogen6x_defconfig index 0c3b5151b863..1123026cfa88 100644 --- a/arch/arm/configs/nitrogen6x_defconfig +++ b/arch/arm/configs/nitrogen6x_defconfig @@ -1100,6 +1100,8 @@ CONFIG_TOUCHSCREEN_TSC2004=y # CONFIG_TOUCHSCREEN_ST1232 is not set # CONFIG_TOUCHSCREEN_P1003 is not set # CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_TOUCHSCREEN_FT5X06=y +CONFIG_TOUCHSCREEN_FT5X06_SINGLE_TOUCH=y CONFIG_INPUT_MISC=y # CONFIG_INPUT_AD714X is not set # CONFIG_INPUT_ATI_REMOTE is not set -- cgit v1.2.3 From 7965c7dbe76791799a3818c055751e0a57dcae73 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Wed, 27 Jun 2012 16:59:18 -0700 Subject: ft5x06 multi-touch driver: make debug messages KERN_DEBUG, not KERN_ERR --- drivers/input/touchscreen/ft5x06_ts.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 6df8520405d7..24cfe3fab381 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -211,14 +211,14 @@ static void write_reg(struct ft5x06_ts *ts, int regnum, int value) if (ret != 1) printk(KERN_WARNING "%s: i2c_transfer failed\n", __func__); else - printk(KERN_ERR "%s: set register 0x%02x to 0x%02x\n", + printk(KERN_DEBUG "%s: set register 0x%02x to 0x%02x\n", __func__, regnum, value); } static void set_mode(struct ft5x06_ts *ts, int mode) { write_reg(ts, 0, (mode&7)<<4); - printk(KERN_ERR "%s: changed mode to 0x%02x\n", __func__, mode); + printk(KERN_DEBUG "%s: changed mode to 0x%02x\n", __func__, mode); } #define WORK_MODE 0 -- cgit v1.2.3 From b74ac600a6fb0d0bcc17595f1cc758f6f5bde0ae Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Fri, 29 Jun 2012 18:01:59 -0700 Subject: egalax_ts: Add support for single-touch in Kconfig Signed-off-by: Eric Nelson --- drivers/input/touchscreen/Kconfig | 10 ++++++++++ drivers/input/touchscreen/egalax_ts.c | 7 ++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 1979ce87b6c3..4ae5f8c7b850 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -187,6 +187,16 @@ config TOUCHSCREEN_EGALAX To compile this driver as a module, choose M here: the module will be called egalax_ts. +config TOUCHSCREEN_EGALAX_SINGLE_TOUCH + bool "EETI eGalax touchscreen as single-touch" + default N + depends on TOUCHSCREEN_EGALAX + help + If you say yes here you get single-touch touchscreen support + on the eGalax I2C controller. + If you say "no", you'll get the normal multi-touch. + + config TOUCHSCREEN_FUJITSU tristate "Fujitsu serial touchscreen" select SERIO diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c index fff8afe1b41a..0b6cde77c421 100644 --- a/drivers/input/touchscreen/egalax_ts.c +++ b/drivers/input/touchscreen/egalax_ts.c @@ -139,7 +139,7 @@ retry: events[id].x = x; events[id].y = y; -#ifdef FORCE_SINGLE_POINTER_SUPPORT +#ifdef CONFIG_TOUCHSCREEN_EGALAX_SINGLE_TOUCH input_report_abs(input_dev, ABS_X, x); input_report_abs(input_dev, ABS_Y, y); input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1); @@ -166,7 +166,7 @@ retry: dev_dbg(&client->dev, "release id:%d\n", id); events[id].valid = 0; events[id].status = 0; -#ifdef FORCE_SINGLE_POINTER_SUPPORT +#ifdef CONFIG_TOUCHSCREEN_EGALAX_SINGLE_TOUCH input_report_key(input_dev, BTN_TOUCH, 0); input_report_abs(input_dev, ABS_PRESSURE, 0); #else @@ -256,8 +256,9 @@ static int __devinit egalax_ts_probe(struct i2c_client *client, __set_bit(ABS_PRESSURE, input_dev->absbit); input_set_abs_params(input_dev, ABS_X, 0, 32767, 0, 0); input_set_abs_params(input_dev, ABS_Y, 0, 32767, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1, 0, 0); -#ifndef FORCE_SINGLE_POINTER_SUPPORT +#ifndef CONFIG_TOUCHSCREEN_EGALAX_SINGLE_TOUCH input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 32767, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 32767, 0, 0); input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); -- cgit v1.2.3 From e06baef465e2ee37e286b264182d4a23f8f2c067 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Fri, 29 Jun 2012 18:02:26 -0700 Subject: nitrogen6x_defconfig: configure egalax_ts for single-touch --- arch/arm/configs/nitrogen6x_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/nitrogen6x_defconfig b/arch/arm/configs/nitrogen6x_defconfig index 1123026cfa88..f387d81f50ec 100644 --- a/arch/arm/configs/nitrogen6x_defconfig +++ b/arch/arm/configs/nitrogen6x_defconfig @@ -1078,6 +1078,7 @@ CONFIG_INPUT_TOUCHSCREEN=y # CONFIG_TOUCHSCREEN_HAMPSHIRE is not set # CONFIG_TOUCHSCREEN_EETI is not set CONFIG_TOUCHSCREEN_EGALAX=y +CONFIG_TOUCHSCREEN_EGALAX_SINGLE_TOUCH=y # CONFIG_TOUCHSCREEN_FUJITSU is not set # CONFIG_TOUCHSCREEN_GUNZE is not set # CONFIG_TOUCHSCREEN_ELO is not set -- cgit v1.2.3