Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: add support for TI Touchscreen controller

This patch adds support for TI's touchscreen
controller for a 4/5/8 wire resistive panel
that is directly fed to the ADC.

This touchscreen controller will be part of
AM335x TI SoC. The TRM can be found at:
http://www.ti.com/lit/ug/spruh73a/spruh73a.pdf

Signed-off-by: Patil, Rachna <rachna@ti.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

authored by

Rachna Patil and committed by
Dmitry Torokhov
1b8be32e f79e30a8

+516
+12
drivers/input/touchscreen/Kconfig
··· 460 460 To compile this driver as a module, choose M here: the 461 461 module will be called touchwin. 462 462 463 + config TOUCHSCREEN_TI_TSCADC 464 + tristate "TI Touchscreen Interface" 465 + depends on ARCH_OMAP2PLUS 466 + help 467 + Say Y here if you have 4/5/8 wire touchscreen controller 468 + to be connected to the ADC controller on your TI AM335x SoC. 469 + 470 + If unsure, say N. 471 + 472 + To compile this driver as a module, choose M here: the 473 + module will be called ti_tscadc. 474 + 463 475 config TOUCHSCREEN_ATMEL_TSADCC 464 476 tristate "Atmel Touchscreen Interface" 465 477 depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
+1
drivers/input/touchscreen/Makefile
··· 48 48 obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o 49 49 obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o 50 50 obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o 51 + obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC) += ti_tscadc.o 51 52 obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o 52 53 obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o 53 54 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
+486
drivers/input/touchscreen/ti_tscadc.c
··· 1 + /* 2 + * TI Touch Screen driver 3 + * 4 + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ 5 + * 6 + * This program is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU General Public License as 8 + * published by the Free Software Foundation version 2. 9 + * 10 + * This program is distributed "as is" WITHOUT ANY WARRANTY of any 11 + * kind, whether express or implied; without even the implied warranty 12 + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + */ 15 + 16 + 17 + #include <linux/init.h> 18 + #include <linux/kernel.h> 19 + #include <linux/err.h> 20 + #include <linux/module.h> 21 + #include <linux/input.h> 22 + #include <linux/slab.h> 23 + #include <linux/interrupt.h> 24 + #include <linux/clk.h> 25 + #include <linux/platform_device.h> 26 + #include <linux/io.h> 27 + #include <linux/input/ti_tscadc.h> 28 + #include <linux/delay.h> 29 + 30 + #define REG_IRQEOI 0x020 31 + #define REG_RAWIRQSTATUS 0x024 32 + #define REG_IRQSTATUS 0x028 33 + #define REG_IRQENABLE 0x02C 34 + #define REG_IRQWAKEUP 0x034 35 + #define REG_CTRL 0x040 36 + #define REG_ADCFSM 0x044 37 + #define REG_CLKDIV 0x04C 38 + #define REG_SE 0x054 39 + #define REG_IDLECONFIG 0x058 40 + #define REG_CHARGECONFIG 0x05C 41 + #define REG_CHARGEDELAY 0x060 42 + #define REG_STEPCONFIG(n) (0x64 + ((n - 1) * 8)) 43 + #define REG_STEPDELAY(n) (0x68 + ((n - 1) * 8)) 44 + #define REG_STEPCONFIG13 0x0C4 45 + #define REG_STEPDELAY13 0x0C8 46 + #define REG_STEPCONFIG14 0x0CC 47 + #define REG_STEPDELAY14 0x0D0 48 + #define REG_FIFO0CNT 0xE4 49 + #define REG_FIFO1THR 0xF4 50 + #define REG_FIFO0 0x100 51 + #define REG_FIFO1 0x200 52 + 53 + /* Register Bitfields */ 54 + #define IRQWKUP_ENB BIT(0) 55 + #define STPENB_STEPENB 0x7FFF 56 + #define IRQENB_FIFO1THRES BIT(5) 57 + #define IRQENB_PENUP BIT(9) 58 + #define STEPCONFIG_MODE_HWSYNC 0x2 59 + #define STEPCONFIG_SAMPLES_AVG (1 << 4) 60 + #define STEPCONFIG_XPP (1 << 5) 61 + #define STEPCONFIG_XNN (1 << 6) 62 + #define STEPCONFIG_YPP (1 << 7) 63 + #define STEPCONFIG_YNN (1 << 8) 64 + #define STEPCONFIG_XNP (1 << 9) 65 + #define STEPCONFIG_YPN (1 << 10) 66 + #define STEPCONFIG_INM (1 << 18) 67 + #define STEPCONFIG_INP (1 << 20) 68 + #define STEPCONFIG_INP_5 (1 << 21) 69 + #define STEPCONFIG_FIFO1 (1 << 26) 70 + #define STEPCONFIG_OPENDLY 0xff 71 + #define STEPCONFIG_Z1 (3 << 19) 72 + #define STEPIDLE_INP (1 << 22) 73 + #define STEPCHARGE_RFP (1 << 12) 74 + #define STEPCHARGE_INM (1 << 15) 75 + #define STEPCHARGE_INP (1 << 19) 76 + #define STEPCHARGE_RFM (1 << 23) 77 + #define STEPCHARGE_DELAY 0x1 78 + #define CNTRLREG_TSCSSENB (1 << 0) 79 + #define CNTRLREG_STEPID (1 << 1) 80 + #define CNTRLREG_STEPCONFIGWRT (1 << 2) 81 + #define CNTRLREG_4WIRE (1 << 5) 82 + #define CNTRLREG_5WIRE (1 << 6) 83 + #define CNTRLREG_8WIRE (3 << 5) 84 + #define CNTRLREG_TSCENB (1 << 7) 85 + #define ADCFSM_STEPID 0x10 86 + 87 + #define SEQ_SETTLE 275 88 + #define ADC_CLK 3000000 89 + #define MAX_12BIT ((1 << 12) - 1) 90 + #define TSCADC_DELTA_X 15 91 + #define TSCADC_DELTA_Y 15 92 + 93 + struct tscadc { 94 + struct input_dev *input; 95 + struct clk *tsc_ick; 96 + void __iomem *tsc_base; 97 + unsigned int irq; 98 + unsigned int wires; 99 + unsigned int x_plate_resistance; 100 + bool pen_down; 101 + }; 102 + 103 + static unsigned int tscadc_readl(struct tscadc *ts, unsigned int reg) 104 + { 105 + return readl(ts->tsc_base + reg); 106 + } 107 + 108 + static void tscadc_writel(struct tscadc *tsc, unsigned int reg, 109 + unsigned int val) 110 + { 111 + writel(val, tsc->tsc_base + reg); 112 + } 113 + 114 + static void tscadc_step_config(struct tscadc *ts_dev) 115 + { 116 + unsigned int config; 117 + int i; 118 + 119 + /* Configure the Step registers */ 120 + 121 + config = STEPCONFIG_MODE_HWSYNC | 122 + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_XPP; 123 + switch (ts_dev->wires) { 124 + case 4: 125 + config |= STEPCONFIG_INP | STEPCONFIG_XNN; 126 + break; 127 + case 5: 128 + config |= STEPCONFIG_YNN | 129 + STEPCONFIG_INP_5 | STEPCONFIG_XNN | 130 + STEPCONFIG_YPP; 131 + break; 132 + case 8: 133 + config |= STEPCONFIG_INP | STEPCONFIG_XNN; 134 + break; 135 + } 136 + 137 + for (i = 1; i < 7; i++) { 138 + tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); 139 + tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); 140 + } 141 + 142 + config = 0; 143 + config = STEPCONFIG_MODE_HWSYNC | 144 + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YNN | 145 + STEPCONFIG_INM | STEPCONFIG_FIFO1; 146 + switch (ts_dev->wires) { 147 + case 4: 148 + config |= STEPCONFIG_YPP; 149 + break; 150 + case 5: 151 + config |= STEPCONFIG_XPP | STEPCONFIG_INP_5 | 152 + STEPCONFIG_XNP | STEPCONFIG_YPN; 153 + break; 154 + case 8: 155 + config |= STEPCONFIG_YPP; 156 + break; 157 + } 158 + 159 + for (i = 7; i < 13; i++) { 160 + tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); 161 + tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); 162 + } 163 + 164 + config = 0; 165 + /* Charge step configuration */ 166 + config = STEPCONFIG_XPP | STEPCONFIG_YNN | 167 + STEPCHARGE_RFP | STEPCHARGE_RFM | 168 + STEPCHARGE_INM | STEPCHARGE_INP; 169 + 170 + tscadc_writel(ts_dev, REG_CHARGECONFIG, config); 171 + tscadc_writel(ts_dev, REG_CHARGEDELAY, STEPCHARGE_DELAY); 172 + 173 + config = 0; 174 + /* Configure to calculate pressure */ 175 + config = STEPCONFIG_MODE_HWSYNC | 176 + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YPP | 177 + STEPCONFIG_XNN | STEPCONFIG_INM; 178 + tscadc_writel(ts_dev, REG_STEPCONFIG13, config); 179 + tscadc_writel(ts_dev, REG_STEPDELAY13, STEPCONFIG_OPENDLY); 180 + 181 + config |= STEPCONFIG_Z1 | STEPCONFIG_FIFO1; 182 + tscadc_writel(ts_dev, REG_STEPCONFIG14, config); 183 + tscadc_writel(ts_dev, REG_STEPDELAY14, STEPCONFIG_OPENDLY); 184 + 185 + tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); 186 + } 187 + 188 + static void tscadc_idle_config(struct tscadc *ts_config) 189 + { 190 + unsigned int idleconfig; 191 + 192 + idleconfig = STEPCONFIG_YNN | 193 + STEPCONFIG_INM | 194 + STEPCONFIG_YPN | STEPIDLE_INP; 195 + tscadc_writel(ts_config, REG_IDLECONFIG, idleconfig); 196 + } 197 + 198 + static void tscadc_read_coordinates(struct tscadc *ts_dev, 199 + unsigned int *x, unsigned int *y) 200 + { 201 + unsigned int fifocount = tscadc_readl(ts_dev, REG_FIFO0CNT); 202 + unsigned int prev_val_x = ~0, prev_val_y = ~0; 203 + unsigned int prev_diff_x = ~0, prev_diff_y = ~0; 204 + unsigned int read, diff; 205 + unsigned int i; 206 + 207 + /* 208 + * Delta filter is used to remove large variations in sampled 209 + * values from ADC. The filter tries to predict where the next 210 + * coordinate could be. This is done by taking a previous 211 + * coordinate and subtracting it form current one. Further the 212 + * algorithm compares the difference with that of a present value, 213 + * if true the value is reported to the sub system. 214 + */ 215 + for (i = 0; i < fifocount - 1; i++) { 216 + read = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff; 217 + diff = abs(read - prev_val_x); 218 + if (diff < prev_diff_x) { 219 + prev_diff_x = diff; 220 + *x = read; 221 + } 222 + prev_val_x = read; 223 + 224 + read = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff; 225 + diff = abs(read - prev_val_y); 226 + if (diff < prev_diff_y) { 227 + prev_diff_y = diff; 228 + *y = read; 229 + } 230 + prev_val_y = read; 231 + } 232 + } 233 + 234 + static irqreturn_t tscadc_irq(int irq, void *dev) 235 + { 236 + struct tscadc *ts_dev = dev; 237 + struct input_dev *input_dev = ts_dev->input; 238 + unsigned int status, irqclr = 0; 239 + unsigned int x = 0, y = 0; 240 + unsigned int z1, z2, z; 241 + unsigned int fsm; 242 + 243 + status = tscadc_readl(ts_dev, REG_IRQSTATUS); 244 + if (status & IRQENB_FIFO1THRES) { 245 + tscadc_read_coordinates(ts_dev, &x, &y); 246 + 247 + z1 = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff; 248 + z2 = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff; 249 + 250 + if (ts_dev->pen_down && z1 != 0 && z2 != 0) { 251 + /* 252 + * Calculate pressure using formula 253 + * Resistance(touch) = x plate resistance * 254 + * x postion/4096 * ((z2 / z1) - 1) 255 + */ 256 + z = z2 - z1; 257 + z *= x; 258 + z *= ts_dev->x_plate_resistance; 259 + z /= z1; 260 + z = (z + 2047) >> 12; 261 + 262 + if (z <= MAX_12BIT) { 263 + input_report_abs(input_dev, ABS_X, x); 264 + input_report_abs(input_dev, ABS_Y, y); 265 + input_report_abs(input_dev, ABS_PRESSURE, z); 266 + input_report_key(input_dev, BTN_TOUCH, 1); 267 + input_sync(input_dev); 268 + } 269 + } 270 + irqclr |= IRQENB_FIFO1THRES; 271 + } 272 + 273 + /* 274 + * Time for sequencer to settle, to read 275 + * correct state of the sequencer. 276 + */ 277 + udelay(SEQ_SETTLE); 278 + 279 + status = tscadc_readl(ts_dev, REG_RAWIRQSTATUS); 280 + if (status & IRQENB_PENUP) { 281 + /* Pen up event */ 282 + fsm = tscadc_readl(ts_dev, REG_ADCFSM); 283 + if (fsm == ADCFSM_STEPID) { 284 + ts_dev->pen_down = false; 285 + input_report_key(input_dev, BTN_TOUCH, 0); 286 + input_report_abs(input_dev, ABS_PRESSURE, 0); 287 + input_sync(input_dev); 288 + } else { 289 + ts_dev->pen_down = true; 290 + } 291 + irqclr |= IRQENB_PENUP; 292 + } 293 + 294 + tscadc_writel(ts_dev, REG_IRQSTATUS, irqclr); 295 + /* check pending interrupts */ 296 + tscadc_writel(ts_dev, REG_IRQEOI, 0x0); 297 + 298 + tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); 299 + return IRQ_HANDLED; 300 + } 301 + 302 + /* 303 + * The functions for inserting/removing driver as a module. 304 + */ 305 + 306 + static int __devinit tscadc_probe(struct platform_device *pdev) 307 + { 308 + const struct tsc_data *pdata = pdev->dev.platform_data; 309 + struct resource *res; 310 + struct tscadc *ts_dev; 311 + struct input_dev *input_dev; 312 + struct clk *clk; 313 + int err; 314 + int clk_value, ctrl, irq; 315 + 316 + if (!pdata) { 317 + dev_err(&pdev->dev, "missing platform data.\n"); 318 + return -EINVAL; 319 + } 320 + 321 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 322 + if (!res) { 323 + dev_err(&pdev->dev, "no memory resource defined.\n"); 324 + return -EINVAL; 325 + } 326 + 327 + irq = platform_get_irq(pdev, 0); 328 + if (irq < 0) { 329 + dev_err(&pdev->dev, "no irq ID is specified.\n"); 330 + return -EINVAL; 331 + } 332 + 333 + /* Allocate memory for device */ 334 + ts_dev = kzalloc(sizeof(struct tscadc), GFP_KERNEL); 335 + input_dev = input_allocate_device(); 336 + if (!ts_dev || !input_dev) { 337 + dev_err(&pdev->dev, "failed to allocate memory.\n"); 338 + err = -ENOMEM; 339 + goto err_free_mem; 340 + } 341 + 342 + ts_dev->input = input_dev; 343 + ts_dev->irq = irq; 344 + ts_dev->wires = pdata->wires; 345 + ts_dev->x_plate_resistance = pdata->x_plate_resistance; 346 + 347 + res = request_mem_region(res->start, resource_size(res), pdev->name); 348 + if (!res) { 349 + dev_err(&pdev->dev, "failed to reserve registers.\n"); 350 + err = -EBUSY; 351 + goto err_free_mem; 352 + } 353 + 354 + ts_dev->tsc_base = ioremap(res->start, resource_size(res)); 355 + if (!ts_dev->tsc_base) { 356 + dev_err(&pdev->dev, "failed to map registers.\n"); 357 + err = -ENOMEM; 358 + goto err_release_mem_region; 359 + } 360 + 361 + err = request_irq(ts_dev->irq, tscadc_irq, 362 + 0, pdev->dev.driver->name, ts_dev); 363 + if (err) { 364 + dev_err(&pdev->dev, "failed to allocate irq.\n"); 365 + goto err_unmap_regs; 366 + } 367 + 368 + ts_dev->tsc_ick = clk_get(&pdev->dev, "adc_tsc_ick"); 369 + if (IS_ERR(ts_dev->tsc_ick)) { 370 + dev_err(&pdev->dev, "failed to get TSC ick\n"); 371 + goto err_free_irq; 372 + } 373 + clk_enable(ts_dev->tsc_ick); 374 + 375 + clk = clk_get(&pdev->dev, "adc_tsc_fck"); 376 + if (IS_ERR(clk)) { 377 + dev_err(&pdev->dev, "failed to get TSC fck\n"); 378 + err = PTR_ERR(clk); 379 + goto err_disable_clk; 380 + } 381 + 382 + clk_value = clk_get_rate(clk) / ADC_CLK; 383 + clk_put(clk); 384 + 385 + if (clk_value < 7) { 386 + dev_err(&pdev->dev, "clock input less than min clock requirement\n"); 387 + goto err_disable_clk; 388 + } 389 + /* CLKDIV needs to be configured to the value minus 1 */ 390 + tscadc_writel(ts_dev, REG_CLKDIV, clk_value - 1); 391 + 392 + /* Enable wake-up of the SoC using touchscreen */ 393 + tscadc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB); 394 + 395 + ctrl = CNTRLREG_STEPCONFIGWRT | 396 + CNTRLREG_TSCENB | 397 + CNTRLREG_STEPID; 398 + switch (ts_dev->wires) { 399 + case 4: 400 + ctrl |= CNTRLREG_4WIRE; 401 + break; 402 + case 5: 403 + ctrl |= CNTRLREG_5WIRE; 404 + break; 405 + case 8: 406 + ctrl |= CNTRLREG_8WIRE; 407 + break; 408 + } 409 + tscadc_writel(ts_dev, REG_CTRL, ctrl); 410 + 411 + tscadc_idle_config(ts_dev); 412 + tscadc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO1THRES); 413 + tscadc_step_config(ts_dev); 414 + tscadc_writel(ts_dev, REG_FIFO1THR, 6); 415 + 416 + ctrl |= CNTRLREG_TSCSSENB; 417 + tscadc_writel(ts_dev, REG_CTRL, ctrl); 418 + 419 + input_dev->name = "ti-tsc-adc"; 420 + input_dev->dev.parent = &pdev->dev; 421 + 422 + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 423 + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 424 + 425 + input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); 426 + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); 427 + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); 428 + 429 + /* register to the input system */ 430 + err = input_register_device(input_dev); 431 + if (err) 432 + goto err_disable_clk; 433 + 434 + platform_set_drvdata(pdev, ts_dev); 435 + return 0; 436 + 437 + err_disable_clk: 438 + clk_disable(ts_dev->tsc_ick); 439 + clk_put(ts_dev->tsc_ick); 440 + err_free_irq: 441 + free_irq(ts_dev->irq, ts_dev); 442 + err_unmap_regs: 443 + iounmap(ts_dev->tsc_base); 444 + err_release_mem_region: 445 + release_mem_region(res->start, resource_size(res)); 446 + err_free_mem: 447 + input_free_device(input_dev); 448 + kfree(ts_dev); 449 + return err; 450 + } 451 + 452 + static int __devexit tscadc_remove(struct platform_device *pdev) 453 + { 454 + struct tscadc *ts_dev = platform_get_drvdata(pdev); 455 + struct resource *res; 456 + 457 + free_irq(ts_dev->irq, ts_dev); 458 + 459 + input_unregister_device(ts_dev->input); 460 + 461 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 462 + iounmap(ts_dev->tsc_base); 463 + release_mem_region(res->start, resource_size(res)); 464 + 465 + clk_disable(ts_dev->tsc_ick); 466 + clk_put(ts_dev->tsc_ick); 467 + 468 + kfree(ts_dev); 469 + 470 + platform_set_drvdata(pdev, NULL); 471 + return 0; 472 + } 473 + 474 + static struct platform_driver ti_tsc_driver = { 475 + .probe = tscadc_probe, 476 + .remove = __devexit_p(tscadc_remove), 477 + .driver = { 478 + .name = "tsc", 479 + .owner = THIS_MODULE, 480 + }, 481 + }; 482 + module_platform_driver(ti_tsc_driver); 483 + 484 + MODULE_DESCRIPTION("TI touchscreen controller driver"); 485 + MODULE_AUTHOR("Rachna Patil <rachna@ti.com>"); 486 + MODULE_LICENSE("GPL");
+17
include/linux/input/ti_tscadc.h
··· 1 + #ifndef __LINUX_TI_TSCADC_H 2 + #define __LINUX_TI_TSCADC_H 3 + 4 + /** 5 + * struct tsc_data Touchscreen wire configuration 6 + * @wires: Wires refer to application modes 7 + * i.e. 4/5/8 wire touchscreen support 8 + * on the platform. 9 + * @x_plate_resistance: X plate resistance. 10 + */ 11 + 12 + struct tsc_data { 13 + int wires; 14 + int x_plate_resistance; 15 + }; 16 + 17 + #endif