1 // SPDX-License-Identifier: GPL-2.0-only
3 * Driver for Motorola PCAP2 touchscreen as found in the EZX phone platform.
5 * Copyright (C) 2006 Harald Welte <laforge@openezx.org>
6 * Copyright (C) 2009 Daniel Ribeiro <drwyrm@gmail.com>
9 #include <linux/module.h>
11 #include <linux/string.h>
12 #include <linux/slab.h>
14 #include <linux/timer.h>
15 #include <linux/interrupt.h>
16 #include <linux/platform_device.h>
17 #include <linux/input.h>
18 #include <linux/mfd/ezx-pcap.h>
21 struct pcap_chip
*pcap
;
22 struct input_dev
*input
;
23 struct delayed_work work
;
29 #define SAMPLE_DELAY 20 /* msecs */
32 #define X_AXIS_MAX 1023
33 #define Y_AXIS_MAX X_AXIS_MAX
34 #define Y_AXIS_MIN X_AXIS_MIN
35 #define PRESSURE_MAX X_AXIS_MAX
36 #define PRESSURE_MIN X_AXIS_MIN
38 static void pcap_ts_read_xy(void *data
, u16 res
[2])
40 struct pcap_ts
*pcap_ts
= data
;
42 switch (pcap_ts
->read_state
) {
43 case PCAP_ADC_TS_M_PRESSURE
:
44 /* pressure reading is unreliable */
45 if (res
[0] > PRESSURE_MIN
&& res
[0] < PRESSURE_MAX
)
46 pcap_ts
->pressure
= res
[0];
47 pcap_ts
->read_state
= PCAP_ADC_TS_M_XY
;
48 schedule_delayed_work(&pcap_ts
->work
, 0);
50 case PCAP_ADC_TS_M_XY
:
53 if (pcap_ts
->x
<= X_AXIS_MIN
|| pcap_ts
->x
>= X_AXIS_MAX
||
54 pcap_ts
->y
<= Y_AXIS_MIN
|| pcap_ts
->y
>= Y_AXIS_MAX
) {
55 /* pen has been released */
56 input_report_abs(pcap_ts
->input
, ABS_PRESSURE
, 0);
57 input_report_key(pcap_ts
->input
, BTN_TOUCH
, 0);
59 pcap_ts
->read_state
= PCAP_ADC_TS_M_STANDBY
;
60 schedule_delayed_work(&pcap_ts
->work
, 0);
62 /* pen is touching the screen */
63 input_report_abs(pcap_ts
->input
, ABS_X
, pcap_ts
->x
);
64 input_report_abs(pcap_ts
->input
, ABS_Y
, pcap_ts
->y
);
65 input_report_key(pcap_ts
->input
, BTN_TOUCH
, 1);
66 input_report_abs(pcap_ts
->input
, ABS_PRESSURE
,
69 /* switch back to pressure read mode */
70 pcap_ts
->read_state
= PCAP_ADC_TS_M_PRESSURE
;
71 schedule_delayed_work(&pcap_ts
->work
,
72 msecs_to_jiffies(SAMPLE_DELAY
));
74 input_sync(pcap_ts
->input
);
77 dev_warn(&pcap_ts
->input
->dev
,
78 "pcap_ts: Warning, unhandled read_state %d\n",
84 static void pcap_ts_work(struct work_struct
*work
)
86 struct delayed_work
*dw
= to_delayed_work(work
);
87 struct pcap_ts
*pcap_ts
= container_of(dw
, struct pcap_ts
, work
);
90 pcap_set_ts_bits(pcap_ts
->pcap
,
91 pcap_ts
->read_state
<< PCAP_ADC_TS_M_SHIFT
);
93 if (pcap_ts
->read_state
== PCAP_ADC_TS_M_STANDBY
)
96 /* start adc conversion */
97 ch
[0] = PCAP_ADC_CH_TS_X1
;
98 ch
[1] = PCAP_ADC_CH_TS_Y1
;
99 pcap_adc_async(pcap_ts
->pcap
, PCAP_ADC_BANK_1
, 0, ch
,
100 pcap_ts_read_xy
, pcap_ts
);
103 static irqreturn_t
pcap_ts_event_touch(int pirq
, void *data
)
105 struct pcap_ts
*pcap_ts
= data
;
107 if (pcap_ts
->read_state
== PCAP_ADC_TS_M_STANDBY
) {
108 pcap_ts
->read_state
= PCAP_ADC_TS_M_PRESSURE
;
109 schedule_delayed_work(&pcap_ts
->work
, 0);
114 static int pcap_ts_open(struct input_dev
*dev
)
116 struct pcap_ts
*pcap_ts
= input_get_drvdata(dev
);
118 pcap_ts
->read_state
= PCAP_ADC_TS_M_STANDBY
;
119 schedule_delayed_work(&pcap_ts
->work
, 0);
124 static void pcap_ts_close(struct input_dev
*dev
)
126 struct pcap_ts
*pcap_ts
= input_get_drvdata(dev
);
128 cancel_delayed_work_sync(&pcap_ts
->work
);
130 pcap_ts
->read_state
= PCAP_ADC_TS_M_NONTS
;
131 pcap_set_ts_bits(pcap_ts
->pcap
,
132 pcap_ts
->read_state
<< PCAP_ADC_TS_M_SHIFT
);
135 static int pcap_ts_probe(struct platform_device
*pdev
)
137 struct input_dev
*input_dev
;
138 struct pcap_ts
*pcap_ts
;
141 pcap_ts
= kzalloc(sizeof(*pcap_ts
), GFP_KERNEL
);
145 pcap_ts
->pcap
= dev_get_drvdata(pdev
->dev
.parent
);
146 platform_set_drvdata(pdev
, pcap_ts
);
148 input_dev
= input_allocate_device();
152 INIT_DELAYED_WORK(&pcap_ts
->work
, pcap_ts_work
);
154 pcap_ts
->read_state
= PCAP_ADC_TS_M_NONTS
;
155 pcap_set_ts_bits(pcap_ts
->pcap
,
156 pcap_ts
->read_state
<< PCAP_ADC_TS_M_SHIFT
);
158 pcap_ts
->input
= input_dev
;
159 input_set_drvdata(input_dev
, pcap_ts
);
161 input_dev
->name
= "pcap-touchscreen";
162 input_dev
->phys
= "pcap_ts/input0";
163 input_dev
->id
.bustype
= BUS_HOST
;
164 input_dev
->id
.vendor
= 0x0001;
165 input_dev
->id
.product
= 0x0002;
166 input_dev
->id
.version
= 0x0100;
167 input_dev
->dev
.parent
= &pdev
->dev
;
168 input_dev
->open
= pcap_ts_open
;
169 input_dev
->close
= pcap_ts_close
;
171 input_dev
->evbit
[0] = BIT_MASK(EV_KEY
) | BIT_MASK(EV_ABS
);
172 input_dev
->keybit
[BIT_WORD(BTN_TOUCH
)] = BIT_MASK(BTN_TOUCH
);
173 input_set_abs_params(input_dev
, ABS_X
, X_AXIS_MIN
, X_AXIS_MAX
, 0, 0);
174 input_set_abs_params(input_dev
, ABS_Y
, Y_AXIS_MIN
, Y_AXIS_MAX
, 0, 0);
175 input_set_abs_params(input_dev
, ABS_PRESSURE
, PRESSURE_MIN
,
178 err
= input_register_device(pcap_ts
->input
);
182 err
= request_irq(pcap_to_irq(pcap_ts
->pcap
, PCAP_IRQ_TS
),
183 pcap_ts_event_touch
, 0, "Touch Screen", pcap_ts
);
190 input_unregister_device(input_dev
);
193 input_free_device(input_dev
);
200 static int pcap_ts_remove(struct platform_device
*pdev
)
202 struct pcap_ts
*pcap_ts
= platform_get_drvdata(pdev
);
204 free_irq(pcap_to_irq(pcap_ts
->pcap
, PCAP_IRQ_TS
), pcap_ts
);
205 cancel_delayed_work_sync(&pcap_ts
->work
);
207 input_unregister_device(pcap_ts
->input
);
215 static int pcap_ts_suspend(struct device
*dev
)
217 struct pcap_ts
*pcap_ts
= dev_get_drvdata(dev
);
219 pcap_set_ts_bits(pcap_ts
->pcap
, PCAP_ADC_TS_REF_LOWPWR
);
223 static int pcap_ts_resume(struct device
*dev
)
225 struct pcap_ts
*pcap_ts
= dev_get_drvdata(dev
);
227 pcap_set_ts_bits(pcap_ts
->pcap
,
228 pcap_ts
->read_state
<< PCAP_ADC_TS_M_SHIFT
);
232 static const struct dev_pm_ops pcap_ts_pm_ops
= {
233 .suspend
= pcap_ts_suspend
,
234 .resume
= pcap_ts_resume
,
236 #define PCAP_TS_PM_OPS (&pcap_ts_pm_ops)
238 #define PCAP_TS_PM_OPS NULL
241 static struct platform_driver pcap_ts_driver
= {
242 .probe
= pcap_ts_probe
,
243 .remove
= pcap_ts_remove
,
246 .pm
= PCAP_TS_PM_OPS
,
249 module_platform_driver(pcap_ts_driver
);
251 MODULE_DESCRIPTION("Motorola PCAP2 touchscreen driver");
252 MODULE_AUTHOR("Daniel Ribeiro / Harald Welte");
253 MODULE_LICENSE("GPL");
254 MODULE_ALIAS("platform:pcap_ts");