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

Input: add support for DA9052/53 touch screen controller

This driver adds support for DA9052/53 4-wire resistive ADC interfaced
touchscreen controller. DA9052/53 is a multi-function device, therefore
this driver depends on DA9052/53 core.

This patch is functionally tested on Samsung SMDKV6410.

Signed-off-by: David Dajun Chen <dchen@diasemi.com>
Signed-off-by: Ashish Jangam <ashish.jangam@kpitcummins.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

authored by

Ashish Jangam and committed by
Dmitry Torokhov
eead75a2 df052676

+388
+17
drivers/input/touchscreen/Kconfig
··· 187 187 Say Y here to enable the support for the touchscreen found 188 188 on Dialog Semiconductor DA9034 PMIC. 189 189 190 + If unsure, say N. 191 + 192 + To compile this driver as a module, choose M here: the 193 + module will be called da9034-ts. 194 + 195 + config TOUCHSCREEN_DA9052 196 + tristate "Dialog DA9052/DA9053 TSI" 197 + depends on PMIC_DA9052 198 + help 199 + Say Y here to support the touchscreen found on Dialog Semiconductor 200 + DA9052-BC and DA9053-AA/Bx PMICs. 201 + 202 + If unsure, say N. 203 + 204 + To compile this driver as a module, choose M here: the 205 + module will be called da9052_tsi. 206 + 190 207 config TOUCHSCREEN_DYNAPRO 191 208 tristate "Dynapro serial touchscreen" 192 209 select SERIO
+1
drivers/input/touchscreen/Makefile
··· 22 22 obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o 23 23 obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o 24 24 obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o 25 + obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o 25 26 obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o 26 27 obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o 27 28 obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
+370
drivers/input/touchscreen/da9052_tsi.c
··· 1 + /* 2 + * TSI driver for Dialog DA9052 3 + * 4 + * Copyright(c) 2012 Dialog Semiconductor Ltd. 5 + * 6 + * Author: David Dajun Chen <dchen@diasemi.com> 7 + * 8 + * This program is free software; you can redistribute it and/or modify it 9 + * under the terms of the GNU General Public License as published by the 10 + * Free Software Foundation; either version 2 of the License, or (at your 11 + * option) any later version. 12 + * 13 + */ 14 + #include <linux/module.h> 15 + #include <linux/input.h> 16 + #include <linux/delay.h> 17 + #include <linux/platform_device.h> 18 + #include <linux/interrupt.h> 19 + 20 + #include <linux/mfd/da9052/reg.h> 21 + #include <linux/mfd/da9052/da9052.h> 22 + 23 + #define TSI_PEN_DOWN_STATUS 0x40 24 + 25 + struct da9052_tsi { 26 + struct da9052 *da9052; 27 + struct input_dev *dev; 28 + struct delayed_work ts_pen_work; 29 + struct mutex mutex; 30 + unsigned int irq_pendwn; 31 + unsigned int irq_datardy; 32 + bool stopped; 33 + bool adc_on; 34 + }; 35 + 36 + static void da9052_ts_adc_toggle(struct da9052_tsi *tsi, bool on) 37 + { 38 + da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 0, on); 39 + tsi->adc_on = on; 40 + } 41 + 42 + static irqreturn_t da9052_ts_pendwn_irq(int irq, void *data) 43 + { 44 + struct da9052_tsi *tsi = data; 45 + 46 + if (!tsi->stopped) { 47 + /* Mask PEN_DOWN event and unmask TSI_READY event */ 48 + disable_irq_nosync(tsi->irq_pendwn); 49 + enable_irq(tsi->irq_datardy); 50 + 51 + da9052_ts_adc_toggle(tsi, true); 52 + 53 + schedule_delayed_work(&tsi->ts_pen_work, HZ / 50); 54 + } 55 + 56 + return IRQ_HANDLED; 57 + } 58 + 59 + static void da9052_ts_read(struct da9052_tsi *tsi) 60 + { 61 + struct input_dev *input = tsi->dev; 62 + int ret; 63 + u16 x, y, z; 64 + u8 v; 65 + 66 + ret = da9052_reg_read(tsi->da9052, DA9052_TSI_X_MSB_REG); 67 + if (ret < 0) 68 + return; 69 + 70 + x = (u16) ret; 71 + 72 + ret = da9052_reg_read(tsi->da9052, DA9052_TSI_Y_MSB_REG); 73 + if (ret < 0) 74 + return; 75 + 76 + y = (u16) ret; 77 + 78 + ret = da9052_reg_read(tsi->da9052, DA9052_TSI_Z_MSB_REG); 79 + if (ret < 0) 80 + return; 81 + 82 + z = (u16) ret; 83 + 84 + ret = da9052_reg_read(tsi->da9052, DA9052_TSI_LSB_REG); 85 + if (ret < 0) 86 + return; 87 + 88 + v = (u8) ret; 89 + 90 + x = ((x << 2) & 0x3fc) | (v & 0x3); 91 + y = ((y << 2) & 0x3fc) | ((v & 0xc) >> 2); 92 + z = ((z << 2) & 0x3fc) | ((v & 0x30) >> 4); 93 + 94 + input_report_key(input, BTN_TOUCH, 1); 95 + input_report_abs(input, ABS_X, x); 96 + input_report_abs(input, ABS_Y, y); 97 + input_report_abs(input, ABS_PRESSURE, z); 98 + input_sync(input); 99 + } 100 + 101 + static irqreturn_t da9052_ts_datardy_irq(int irq, void *data) 102 + { 103 + struct da9052_tsi *tsi = data; 104 + 105 + da9052_ts_read(tsi); 106 + 107 + return IRQ_HANDLED; 108 + } 109 + 110 + static void da9052_ts_pen_work(struct work_struct *work) 111 + { 112 + struct da9052_tsi *tsi = container_of(work, struct da9052_tsi, 113 + ts_pen_work.work); 114 + if (!tsi->stopped) { 115 + int ret = da9052_reg_read(tsi->da9052, DA9052_TSI_LSB_REG); 116 + if (ret < 0 || (ret & TSI_PEN_DOWN_STATUS)) { 117 + /* Pen is still DOWN (or read error) */ 118 + schedule_delayed_work(&tsi->ts_pen_work, HZ / 50); 119 + } else { 120 + struct input_dev *input = tsi->dev; 121 + 122 + /* Pen UP */ 123 + da9052_ts_adc_toggle(tsi, false); 124 + 125 + /* Report Pen UP */ 126 + input_report_key(input, BTN_TOUCH, 0); 127 + input_report_abs(input, ABS_PRESSURE, 0); 128 + input_sync(input); 129 + 130 + /* 131 + * FIXME: Fixes the unhandled irq issue when quick 132 + * pen down and pen up events occurs 133 + */ 134 + ret = da9052_reg_update(tsi->da9052, 135 + DA9052_EVENT_B_REG, 0xC0, 0xC0); 136 + if (ret < 0) 137 + return; 138 + 139 + /* Mask TSI_READY event and unmask PEN_DOWN event */ 140 + disable_irq(tsi->irq_datardy); 141 + enable_irq(tsi->irq_pendwn); 142 + } 143 + } 144 + } 145 + 146 + static int __devinit da9052_ts_configure_gpio(struct da9052 *da9052) 147 + { 148 + int error; 149 + 150 + error = da9052_reg_update(da9052, DA9052_GPIO_2_3_REG, 0x30, 0); 151 + if (error < 0) 152 + return error; 153 + 154 + error = da9052_reg_update(da9052, DA9052_GPIO_4_5_REG, 0x33, 0); 155 + if (error < 0) 156 + return error; 157 + 158 + error = da9052_reg_update(da9052, DA9052_GPIO_6_7_REG, 0x33, 0); 159 + if (error < 0) 160 + return error; 161 + 162 + return 0; 163 + } 164 + 165 + static int __devinit da9052_configure_tsi(struct da9052_tsi *tsi) 166 + { 167 + int error; 168 + 169 + error = da9052_ts_configure_gpio(tsi->da9052); 170 + if (error) 171 + return error; 172 + 173 + /* Measure TSI sample every 1ms */ 174 + error = da9052_reg_update(tsi->da9052, DA9052_ADC_CONT_REG, 175 + 1 << 6, 1 << 6); 176 + if (error < 0) 177 + return error; 178 + 179 + /* TSI_DELAY: 3 slots, TSI_SKIP: 0 slots, TSI_MODE: XYZP */ 180 + error = da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 0xFC, 0xC0); 181 + if (error < 0) 182 + return error; 183 + 184 + /* Supply TSIRef through LD09 */ 185 + error = da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x59); 186 + if (error < 0) 187 + return error; 188 + 189 + return 0; 190 + } 191 + 192 + static int da9052_ts_input_open(struct input_dev *input_dev) 193 + { 194 + struct da9052_tsi *tsi = input_get_drvdata(input_dev); 195 + 196 + tsi->stopped = false; 197 + mb(); 198 + 199 + /* Unmask PEN_DOWN event */ 200 + enable_irq(tsi->irq_pendwn); 201 + 202 + /* Enable Pen Detect Circuit */ 203 + return da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 204 + 1 << 1, 1 << 1); 205 + } 206 + 207 + static void da9052_ts_input_close(struct input_dev *input_dev) 208 + { 209 + struct da9052_tsi *tsi = input_get_drvdata(input_dev); 210 + 211 + tsi->stopped = true; 212 + mb(); 213 + disable_irq(tsi->irq_pendwn); 214 + cancel_delayed_work_sync(&tsi->ts_pen_work); 215 + 216 + if (tsi->adc_on) { 217 + disable_irq(tsi->irq_datardy); 218 + da9052_ts_adc_toggle(tsi, false); 219 + 220 + /* 221 + * If ADC was on that means that pendwn IRQ was disabled 222 + * twice and we need to enable it to keep enable/disable 223 + * counter balanced. IRQ is still off though. 224 + */ 225 + enable_irq(tsi->irq_pendwn); 226 + } 227 + 228 + /* Disable Pen Detect Circuit */ 229 + da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 1, 0); 230 + } 231 + 232 + static int __devinit da9052_ts_probe(struct platform_device *pdev) 233 + { 234 + struct da9052 *da9052; 235 + struct da9052_tsi *tsi; 236 + struct input_dev *input_dev; 237 + int irq_pendwn; 238 + int irq_datardy; 239 + int error; 240 + 241 + da9052 = dev_get_drvdata(pdev->dev.parent); 242 + if (!da9052) 243 + return -EINVAL; 244 + 245 + irq_pendwn = platform_get_irq_byname(pdev, "PENDWN"); 246 + irq_datardy = platform_get_irq_byname(pdev, "TSIRDY"); 247 + if (irq_pendwn < 0 || irq_datardy < 0) { 248 + dev_err(da9052->dev, "Unable to determine device interrupts\n"); 249 + return -ENXIO; 250 + } 251 + 252 + tsi = kzalloc(sizeof(struct da9052_tsi), GFP_KERNEL); 253 + input_dev = input_allocate_device(); 254 + if (!tsi || !input_dev) { 255 + error = -ENOMEM; 256 + goto err_free_mem; 257 + } 258 + 259 + tsi->da9052 = da9052; 260 + tsi->dev = input_dev; 261 + tsi->irq_pendwn = da9052->irq_base + irq_pendwn; 262 + tsi->irq_datardy = da9052->irq_base + irq_datardy; 263 + tsi->stopped = true; 264 + INIT_DELAYED_WORK(&tsi->ts_pen_work, da9052_ts_pen_work); 265 + 266 + input_dev->id.version = 0x0101; 267 + input_dev->id.vendor = 0x15B6; 268 + input_dev->id.product = 0x9052; 269 + input_dev->name = "Dialog DA9052 TouchScreen Driver"; 270 + input_dev->dev.parent = &pdev->dev; 271 + input_dev->open = da9052_ts_input_open; 272 + input_dev->close = da9052_ts_input_close; 273 + 274 + __set_bit(EV_ABS, input_dev->evbit); 275 + __set_bit(EV_KEY, input_dev->evbit); 276 + __set_bit(BTN_TOUCH, input_dev->keybit); 277 + 278 + input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0); 279 + input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0); 280 + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1023, 0, 0); 281 + 282 + input_set_drvdata(input_dev, tsi); 283 + 284 + /* Disable Pen Detect Circuit */ 285 + da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 1, 0); 286 + 287 + /* Disable ADC */ 288 + da9052_ts_adc_toggle(tsi, false); 289 + 290 + error = request_threaded_irq(tsi->irq_pendwn, 291 + NULL, da9052_ts_pendwn_irq, 292 + IRQF_TRIGGER_LOW | IRQF_ONESHOT, 293 + "PENDWN", tsi); 294 + if (error) { 295 + dev_err(tsi->da9052->dev, 296 + "Failed to register PENDWN IRQ %d, error = %d\n", 297 + tsi->irq_pendwn, error); 298 + goto err_free_mem; 299 + } 300 + 301 + error = request_threaded_irq(tsi->irq_datardy, 302 + NULL, da9052_ts_datardy_irq, 303 + IRQF_TRIGGER_LOW | IRQF_ONESHOT, 304 + "TSIRDY", tsi); 305 + if (error) { 306 + dev_err(tsi->da9052->dev, 307 + "Failed to register TSIRDY IRQ %d, error = %d\n", 308 + tsi->irq_datardy, error); 309 + goto err_free_pendwn_irq; 310 + } 311 + 312 + /* Mask PEN_DOWN and TSI_READY events */ 313 + disable_irq(tsi->irq_pendwn); 314 + disable_irq(tsi->irq_datardy); 315 + 316 + error = da9052_configure_tsi(tsi); 317 + if (error) 318 + goto err_free_datardy_irq; 319 + 320 + error = input_register_device(tsi->dev); 321 + if (error) 322 + goto err_free_datardy_irq; 323 + 324 + platform_set_drvdata(pdev, tsi); 325 + 326 + return 0; 327 + 328 + err_free_datardy_irq: 329 + free_irq(tsi->irq_datardy, tsi); 330 + err_free_pendwn_irq: 331 + free_irq(tsi->irq_pendwn, tsi); 332 + err_free_mem: 333 + kfree(tsi); 334 + input_free_device(input_dev); 335 + 336 + return error; 337 + } 338 + 339 + static int __devexit da9052_ts_remove(struct platform_device *pdev) 340 + { 341 + struct da9052_tsi *tsi = platform_get_drvdata(pdev); 342 + 343 + da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x19); 344 + 345 + free_irq(tsi->irq_pendwn, tsi); 346 + free_irq(tsi->irq_datardy, tsi); 347 + 348 + input_unregister_device(tsi->dev); 349 + kfree(tsi); 350 + 351 + platform_set_drvdata(pdev, NULL); 352 + 353 + return 0; 354 + } 355 + 356 + static struct platform_driver da9052_tsi_driver = { 357 + .probe = da9052_ts_probe, 358 + .remove = __devexit_p(da9052_ts_remove), 359 + .driver = { 360 + .name = "da9052-tsi", 361 + .owner = THIS_MODULE, 362 + }, 363 + }; 364 + 365 + module_platform_driver(da9052_tsi_driver); 366 + 367 + MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9052"); 368 + MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>"); 369 + MODULE_LICENSE("GPL"); 370 + MODULE_ALIAS("platform:da9052-tsi");