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"); 371