da9034-ts.c (8293B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Touchscreen driver for Dialog Semiconductor DA9034 4 * 5 * Copyright (C) 2006-2008 Marvell International Ltd. 6 * Fengwei Yin <fengwei.yin@marvell.com> 7 * Bin Yang <bin.yang@marvell.com> 8 * Eric Miao <eric.miao@marvell.com> 9 */ 10 11#include <linux/module.h> 12#include <linux/kernel.h> 13#include <linux/delay.h> 14#include <linux/platform_device.h> 15#include <linux/input.h> 16#include <linux/workqueue.h> 17#include <linux/mfd/da903x.h> 18#include <linux/slab.h> 19 20#define DA9034_MANUAL_CTRL 0x50 21#define DA9034_LDO_ADC_EN (1 << 4) 22 23#define DA9034_AUTO_CTRL1 0x51 24 25#define DA9034_AUTO_CTRL2 0x52 26#define DA9034_AUTO_TSI_EN (1 << 3) 27#define DA9034_PEN_DETECT (1 << 4) 28 29#define DA9034_TSI_CTRL1 0x53 30#define DA9034_TSI_CTRL2 0x54 31#define DA9034_TSI_X_MSB 0x6c 32#define DA9034_TSI_Y_MSB 0x6d 33#define DA9034_TSI_XY_LSB 0x6e 34 35enum { 36 STATE_IDLE, /* wait for pendown */ 37 STATE_BUSY, /* TSI busy sampling */ 38 STATE_STOP, /* sample available */ 39 STATE_WAIT, /* Wait to start next sample */ 40}; 41 42enum { 43 EVENT_PEN_DOWN, 44 EVENT_PEN_UP, 45 EVENT_TSI_READY, 46 EVENT_TIMEDOUT, 47}; 48 49struct da9034_touch { 50 struct device *da9034_dev; 51 struct input_dev *input_dev; 52 53 struct delayed_work tsi_work; 54 struct notifier_block notifier; 55 56 int state; 57 58 int interval_ms; 59 int x_inverted; 60 int y_inverted; 61 62 int last_x; 63 int last_y; 64}; 65 66static inline int is_pen_down(struct da9034_touch *touch) 67{ 68 return da903x_query_status(touch->da9034_dev, DA9034_STATUS_PEN_DOWN); 69} 70 71static inline int detect_pen_down(struct da9034_touch *touch, int on) 72{ 73 if (on) 74 return da903x_set_bits(touch->da9034_dev, 75 DA9034_AUTO_CTRL2, DA9034_PEN_DETECT); 76 else 77 return da903x_clr_bits(touch->da9034_dev, 78 DA9034_AUTO_CTRL2, DA9034_PEN_DETECT); 79} 80 81static int read_tsi(struct da9034_touch *touch) 82{ 83 uint8_t _x, _y, _v; 84 int ret; 85 86 ret = da903x_read(touch->da9034_dev, DA9034_TSI_X_MSB, &_x); 87 if (ret) 88 return ret; 89 90 ret = da903x_read(touch->da9034_dev, DA9034_TSI_Y_MSB, &_y); 91 if (ret) 92 return ret; 93 94 ret = da903x_read(touch->da9034_dev, DA9034_TSI_XY_LSB, &_v); 95 if (ret) 96 return ret; 97 98 touch->last_x = ((_x << 2) & 0x3fc) | (_v & 0x3); 99 touch->last_y = ((_y << 2) & 0x3fc) | ((_v & 0xc) >> 2); 100 101 return 0; 102} 103 104static inline int start_tsi(struct da9034_touch *touch) 105{ 106 return da903x_set_bits(touch->da9034_dev, 107 DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN); 108} 109 110static inline int stop_tsi(struct da9034_touch *touch) 111{ 112 return da903x_clr_bits(touch->da9034_dev, 113 DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN); 114} 115 116static inline void report_pen_down(struct da9034_touch *touch) 117{ 118 int x = touch->last_x; 119 int y = touch->last_y; 120 121 x &= 0xfff; 122 if (touch->x_inverted) 123 x = 1024 - x; 124 y &= 0xfff; 125 if (touch->y_inverted) 126 y = 1024 - y; 127 128 input_report_abs(touch->input_dev, ABS_X, x); 129 input_report_abs(touch->input_dev, ABS_Y, y); 130 input_report_key(touch->input_dev, BTN_TOUCH, 1); 131 132 input_sync(touch->input_dev); 133} 134 135static inline void report_pen_up(struct da9034_touch *touch) 136{ 137 input_report_key(touch->input_dev, BTN_TOUCH, 0); 138 input_sync(touch->input_dev); 139} 140 141static void da9034_event_handler(struct da9034_touch *touch, int event) 142{ 143 int err; 144 145 switch (touch->state) { 146 case STATE_IDLE: 147 if (event != EVENT_PEN_DOWN) 148 break; 149 150 /* Enable auto measurement of the TSI, this will 151 * automatically disable pen down detection 152 */ 153 err = start_tsi(touch); 154 if (err) 155 goto err_reset; 156 157 touch->state = STATE_BUSY; 158 break; 159 160 case STATE_BUSY: 161 if (event != EVENT_TSI_READY) 162 break; 163 164 err = read_tsi(touch); 165 if (err) 166 goto err_reset; 167 168 /* Disable auto measurement of the TSI, so that 169 * pen down status will be available 170 */ 171 err = stop_tsi(touch); 172 if (err) 173 goto err_reset; 174 175 touch->state = STATE_STOP; 176 177 /* FIXME: PEN_{UP/DOWN} events are expected to be 178 * available by stopping TSI, but this is found not 179 * always true, delay and simulate such an event 180 * here is more reliable 181 */ 182 mdelay(1); 183 da9034_event_handler(touch, 184 is_pen_down(touch) ? EVENT_PEN_DOWN : 185 EVENT_PEN_UP); 186 break; 187 188 case STATE_STOP: 189 if (event == EVENT_PEN_DOWN) { 190 report_pen_down(touch); 191 schedule_delayed_work(&touch->tsi_work, 192 msecs_to_jiffies(touch->interval_ms)); 193 touch->state = STATE_WAIT; 194 } 195 196 if (event == EVENT_PEN_UP) { 197 report_pen_up(touch); 198 touch->state = STATE_IDLE; 199 } 200 break; 201 202 case STATE_WAIT: 203 if (event != EVENT_TIMEDOUT) 204 break; 205 206 if (is_pen_down(touch)) { 207 start_tsi(touch); 208 touch->state = STATE_BUSY; 209 } else { 210 report_pen_up(touch); 211 touch->state = STATE_IDLE; 212 } 213 break; 214 } 215 return; 216 217err_reset: 218 touch->state = STATE_IDLE; 219 stop_tsi(touch); 220 detect_pen_down(touch, 1); 221} 222 223static void da9034_tsi_work(struct work_struct *work) 224{ 225 struct da9034_touch *touch = 226 container_of(work, struct da9034_touch, tsi_work.work); 227 228 da9034_event_handler(touch, EVENT_TIMEDOUT); 229} 230 231static int da9034_touch_notifier(struct notifier_block *nb, 232 unsigned long event, void *data) 233{ 234 struct da9034_touch *touch = 235 container_of(nb, struct da9034_touch, notifier); 236 237 if (event & DA9034_EVENT_TSI_READY) 238 da9034_event_handler(touch, EVENT_TSI_READY); 239 240 if ((event & DA9034_EVENT_PEN_DOWN) && touch->state == STATE_IDLE) 241 da9034_event_handler(touch, EVENT_PEN_DOWN); 242 243 return 0; 244} 245 246static int da9034_touch_open(struct input_dev *dev) 247{ 248 struct da9034_touch *touch = input_get_drvdata(dev); 249 int ret; 250 251 ret = da903x_register_notifier(touch->da9034_dev, &touch->notifier, 252 DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY); 253 if (ret) 254 return -EBUSY; 255 256 /* Enable ADC LDO */ 257 ret = da903x_set_bits(touch->da9034_dev, 258 DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN); 259 if (ret) 260 return ret; 261 262 /* TSI_DELAY: 3 slots, TSI_SKIP: 3 slots */ 263 ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL1, 0x1b); 264 if (ret) 265 return ret; 266 267 ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL2, 0x00); 268 if (ret) 269 return ret; 270 271 touch->state = STATE_IDLE; 272 detect_pen_down(touch, 1); 273 274 return 0; 275} 276 277static void da9034_touch_close(struct input_dev *dev) 278{ 279 struct da9034_touch *touch = input_get_drvdata(dev); 280 281 da903x_unregister_notifier(touch->da9034_dev, &touch->notifier, 282 DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY); 283 284 cancel_delayed_work_sync(&touch->tsi_work); 285 286 touch->state = STATE_IDLE; 287 stop_tsi(touch); 288 detect_pen_down(touch, 0); 289 290 /* Disable ADC LDO */ 291 da903x_clr_bits(touch->da9034_dev, 292 DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN); 293} 294 295 296static int da9034_touch_probe(struct platform_device *pdev) 297{ 298 struct da9034_touch_pdata *pdata = dev_get_platdata(&pdev->dev); 299 struct da9034_touch *touch; 300 struct input_dev *input_dev; 301 int error; 302 303 touch = devm_kzalloc(&pdev->dev, sizeof(struct da9034_touch), 304 GFP_KERNEL); 305 if (!touch) { 306 dev_err(&pdev->dev, "failed to allocate driver data\n"); 307 return -ENOMEM; 308 } 309 310 touch->da9034_dev = pdev->dev.parent; 311 312 if (pdata) { 313 touch->interval_ms = pdata->interval_ms; 314 touch->x_inverted = pdata->x_inverted; 315 touch->y_inverted = pdata->y_inverted; 316 } else { 317 /* fallback into default */ 318 touch->interval_ms = 10; 319 } 320 321 INIT_DELAYED_WORK(&touch->tsi_work, da9034_tsi_work); 322 touch->notifier.notifier_call = da9034_touch_notifier; 323 324 input_dev = devm_input_allocate_device(&pdev->dev); 325 if (!input_dev) { 326 dev_err(&pdev->dev, "failed to allocate input device\n"); 327 return -ENOMEM; 328 } 329 330 input_dev->name = pdev->name; 331 input_dev->open = da9034_touch_open; 332 input_dev->close = da9034_touch_close; 333 input_dev->dev.parent = &pdev->dev; 334 335 __set_bit(EV_ABS, input_dev->evbit); 336 __set_bit(ABS_X, input_dev->absbit); 337 __set_bit(ABS_Y, input_dev->absbit); 338 input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0); 339 input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0); 340 341 __set_bit(EV_KEY, input_dev->evbit); 342 __set_bit(BTN_TOUCH, input_dev->keybit); 343 344 touch->input_dev = input_dev; 345 input_set_drvdata(input_dev, touch); 346 347 error = input_register_device(input_dev); 348 if (error) 349 return error; 350 351 return 0; 352} 353 354static struct platform_driver da9034_touch_driver = { 355 .driver = { 356 .name = "da9034-touch", 357 }, 358 .probe = da9034_touch_probe, 359}; 360module_platform_driver(da9034_touch_driver); 361 362MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9034"); 363MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>, Bin Yang <bin.yang@marvell.com>"); 364MODULE_LICENSE("GPL"); 365MODULE_ALIAS("platform:da9034-touch");