1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * TSI driver for Dialog DA9052
5 * Copyright(c) 2012 Dialog Semiconductor Ltd.
7 * Author: David Dajun Chen <dchen@diasemi.com>
9 #include <linux/module.h>
10 #include <linux/input.h>
11 #include <linux/delay.h>
12 #include <linux/platform_device.h>
13 #include <linux/interrupt.h>
15 #include <linux/mfd/da9052/reg.h>
16 #include <linux/mfd/da9052/da9052.h>
18 #define TSI_PEN_DOWN_STATUS 0x40
21 struct da9052
*da9052
;
22 struct input_dev
*dev
;
23 struct delayed_work ts_pen_work
;
28 static void da9052_ts_adc_toggle(struct da9052_tsi
*tsi
, bool on
)
30 da9052_reg_update(tsi
->da9052
, DA9052_TSI_CONT_A_REG
, 1 << 0, on
);
34 static irqreturn_t
da9052_ts_pendwn_irq(int irq
, void *data
)
36 struct da9052_tsi
*tsi
= data
;
39 /* Mask PEN_DOWN event and unmask TSI_READY event */
40 da9052_disable_irq_nosync(tsi
->da9052
, DA9052_IRQ_PENDOWN
);
41 da9052_enable_irq(tsi
->da9052
, DA9052_IRQ_TSIREADY
);
43 da9052_ts_adc_toggle(tsi
, true);
45 schedule_delayed_work(&tsi
->ts_pen_work
, HZ
/ 50);
51 static void da9052_ts_read(struct da9052_tsi
*tsi
)
53 struct input_dev
*input
= tsi
->dev
;
58 ret
= da9052_reg_read(tsi
->da9052
, DA9052_TSI_X_MSB_REG
);
64 ret
= da9052_reg_read(tsi
->da9052
, DA9052_TSI_Y_MSB_REG
);
70 ret
= da9052_reg_read(tsi
->da9052
, DA9052_TSI_Z_MSB_REG
);
76 ret
= da9052_reg_read(tsi
->da9052
, DA9052_TSI_LSB_REG
);
82 x
= ((x
<< 2) & 0x3fc) | (v
& 0x3);
83 y
= ((y
<< 2) & 0x3fc) | ((v
& 0xc) >> 2);
84 z
= ((z
<< 2) & 0x3fc) | ((v
& 0x30) >> 4);
86 input_report_key(input
, BTN_TOUCH
, 1);
87 input_report_abs(input
, ABS_X
, x
);
88 input_report_abs(input
, ABS_Y
, y
);
89 input_report_abs(input
, ABS_PRESSURE
, z
);
93 static irqreturn_t
da9052_ts_datardy_irq(int irq
, void *data
)
95 struct da9052_tsi
*tsi
= data
;
102 static void da9052_ts_pen_work(struct work_struct
*work
)
104 struct da9052_tsi
*tsi
= container_of(work
, struct da9052_tsi
,
107 int ret
= da9052_reg_read(tsi
->da9052
, DA9052_TSI_LSB_REG
);
108 if (ret
< 0 || (ret
& TSI_PEN_DOWN_STATUS
)) {
109 /* Pen is still DOWN (or read error) */
110 schedule_delayed_work(&tsi
->ts_pen_work
, HZ
/ 50);
112 struct input_dev
*input
= tsi
->dev
;
115 da9052_ts_adc_toggle(tsi
, false);
118 input_report_key(input
, BTN_TOUCH
, 0);
119 input_report_abs(input
, ABS_PRESSURE
, 0);
123 * FIXME: Fixes the unhandled irq issue when quick
124 * pen down and pen up events occurs
126 ret
= da9052_reg_update(tsi
->da9052
,
127 DA9052_EVENT_B_REG
, 0xC0, 0xC0);
131 /* Mask TSI_READY event and unmask PEN_DOWN event */
132 da9052_disable_irq(tsi
->da9052
, DA9052_IRQ_TSIREADY
);
133 da9052_enable_irq(tsi
->da9052
, DA9052_IRQ_PENDOWN
);
138 static int da9052_ts_configure_gpio(struct da9052
*da9052
)
142 error
= da9052_reg_update(da9052
, DA9052_GPIO_2_3_REG
, 0x30, 0);
146 error
= da9052_reg_update(da9052
, DA9052_GPIO_4_5_REG
, 0x33, 0);
150 error
= da9052_reg_update(da9052
, DA9052_GPIO_6_7_REG
, 0x33, 0);
157 static int da9052_configure_tsi(struct da9052_tsi
*tsi
)
161 error
= da9052_ts_configure_gpio(tsi
->da9052
);
165 /* Measure TSI sample every 1ms */
166 error
= da9052_reg_update(tsi
->da9052
, DA9052_ADC_CONT_REG
,
171 /* TSI_DELAY: 3 slots, TSI_SKIP: 0 slots, TSI_MODE: XYZP */
172 error
= da9052_reg_update(tsi
->da9052
, DA9052_TSI_CONT_A_REG
, 0xFC, 0xC0);
176 /* Supply TSIRef through LD09 */
177 error
= da9052_reg_write(tsi
->da9052
, DA9052_LDO9_REG
, 0x59);
184 static int da9052_ts_input_open(struct input_dev
*input_dev
)
186 struct da9052_tsi
*tsi
= input_get_drvdata(input_dev
);
188 tsi
->stopped
= false;
191 /* Unmask PEN_DOWN event */
192 da9052_enable_irq(tsi
->da9052
, DA9052_IRQ_PENDOWN
);
194 /* Enable Pen Detect Circuit */
195 return da9052_reg_update(tsi
->da9052
, DA9052_TSI_CONT_A_REG
,
199 static void da9052_ts_input_close(struct input_dev
*input_dev
)
201 struct da9052_tsi
*tsi
= input_get_drvdata(input_dev
);
205 da9052_disable_irq(tsi
->da9052
, DA9052_IRQ_PENDOWN
);
206 cancel_delayed_work_sync(&tsi
->ts_pen_work
);
209 da9052_disable_irq(tsi
->da9052
, DA9052_IRQ_TSIREADY
);
210 da9052_ts_adc_toggle(tsi
, false);
213 * If ADC was on that means that pendwn IRQ was disabled
214 * twice and we need to enable it to keep enable/disable
215 * counter balanced. IRQ is still off though.
217 da9052_enable_irq(tsi
->da9052
, DA9052_IRQ_PENDOWN
);
220 /* Disable Pen Detect Circuit */
221 da9052_reg_update(tsi
->da9052
, DA9052_TSI_CONT_A_REG
, 1 << 1, 0);
224 static int da9052_ts_probe(struct platform_device
*pdev
)
226 struct da9052
*da9052
;
227 struct da9052_tsi
*tsi
;
228 struct input_dev
*input_dev
;
231 da9052
= dev_get_drvdata(pdev
->dev
.parent
);
235 tsi
= kzalloc(sizeof(*tsi
), GFP_KERNEL
);
236 input_dev
= input_allocate_device();
237 if (!tsi
|| !input_dev
) {
242 tsi
->da9052
= da9052
;
243 tsi
->dev
= input_dev
;
245 INIT_DELAYED_WORK(&tsi
->ts_pen_work
, da9052_ts_pen_work
);
247 input_dev
->id
.version
= 0x0101;
248 input_dev
->id
.vendor
= 0x15B6;
249 input_dev
->id
.product
= 0x9052;
250 input_dev
->name
= "Dialog DA9052 TouchScreen Driver";
251 input_dev
->dev
.parent
= &pdev
->dev
;
252 input_dev
->open
= da9052_ts_input_open
;
253 input_dev
->close
= da9052_ts_input_close
;
255 __set_bit(EV_ABS
, input_dev
->evbit
);
256 __set_bit(EV_KEY
, input_dev
->evbit
);
257 __set_bit(BTN_TOUCH
, input_dev
->keybit
);
259 input_set_abs_params(input_dev
, ABS_X
, 0, 1023, 0, 0);
260 input_set_abs_params(input_dev
, ABS_Y
, 0, 1023, 0, 0);
261 input_set_abs_params(input_dev
, ABS_PRESSURE
, 0, 1023, 0, 0);
263 input_set_drvdata(input_dev
, tsi
);
265 /* Disable Pen Detect Circuit */
266 da9052_reg_update(tsi
->da9052
, DA9052_TSI_CONT_A_REG
, 1 << 1, 0);
269 da9052_ts_adc_toggle(tsi
, false);
271 error
= da9052_request_irq(tsi
->da9052
, DA9052_IRQ_PENDOWN
,
272 "pendown-irq", da9052_ts_pendwn_irq
, tsi
);
274 dev_err(tsi
->da9052
->dev
,
275 "Failed to register PENDWN IRQ: %d\n", error
);
279 error
= da9052_request_irq(tsi
->da9052
, DA9052_IRQ_TSIREADY
,
280 "tsiready-irq", da9052_ts_datardy_irq
, tsi
);
282 dev_err(tsi
->da9052
->dev
,
283 "Failed to register TSIRDY IRQ :%d\n", error
);
284 goto err_free_pendwn_irq
;
287 /* Mask PEN_DOWN and TSI_READY events */
288 da9052_disable_irq(tsi
->da9052
, DA9052_IRQ_PENDOWN
);
289 da9052_disable_irq(tsi
->da9052
, DA9052_IRQ_TSIREADY
);
291 error
= da9052_configure_tsi(tsi
);
293 goto err_free_datardy_irq
;
295 error
= input_register_device(tsi
->dev
);
297 goto err_free_datardy_irq
;
299 platform_set_drvdata(pdev
, tsi
);
303 err_free_datardy_irq
:
304 da9052_free_irq(tsi
->da9052
, DA9052_IRQ_TSIREADY
, tsi
);
306 da9052_free_irq(tsi
->da9052
, DA9052_IRQ_PENDOWN
, tsi
);
309 input_free_device(input_dev
);
314 static void da9052_ts_remove(struct platform_device
*pdev
)
316 struct da9052_tsi
*tsi
= platform_get_drvdata(pdev
);
318 da9052_reg_write(tsi
->da9052
, DA9052_LDO9_REG
, 0x19);
320 da9052_free_irq(tsi
->da9052
, DA9052_IRQ_TSIREADY
, tsi
);
321 da9052_free_irq(tsi
->da9052
, DA9052_IRQ_PENDOWN
, tsi
);
323 input_unregister_device(tsi
->dev
);
327 static struct platform_driver da9052_tsi_driver
= {
328 .probe
= da9052_ts_probe
,
329 .remove
= da9052_ts_remove
,
331 .name
= "da9052-tsi",
335 module_platform_driver(da9052_tsi_driver
);
337 MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9052");
338 MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>");
339 MODULE_LICENSE("GPL");
340 MODULE_ALIAS("platform:da9052-tsi");