1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * drivers/input/touchscreen/jornada720_ts.c |
4 | * |
5 | * Copyright (C) 2007 Kristoffer Ericson <Kristoffer.Ericson@gmail.com> |
6 | * |
7 | * Copyright (C) 2006 Filip Zyzniewski <filip.zyzniewski@tefnet.pl> |
8 | * based on HP Jornada 56x touchscreen driver by Alex Lange <chicken@handhelds.org> |
9 | * |
10 | * HP Jornada 710/720/729 Touchscreen Driver |
11 | */ |
12 | |
13 | #include <linux/gpio/consumer.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/input.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/module.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/io.h> |
20 | |
21 | #include <mach/jornada720.h> |
22 | |
23 | MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>" ); |
24 | MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver" ); |
25 | MODULE_LICENSE("GPL v2" ); |
26 | |
27 | struct jornada_ts { |
28 | struct input_dev *dev; |
29 | struct gpio_desc *gpio; |
30 | int x_data[4]; /* X sample values */ |
31 | int y_data[4]; /* Y sample values */ |
32 | }; |
33 | |
34 | static void jornada720_ts_collect_data(struct jornada_ts *jornada_ts) |
35 | { |
36 | /* 3 low word X samples */ |
37 | jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY); |
38 | jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY); |
39 | jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY); |
40 | |
41 | /* 3 low word Y samples */ |
42 | jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY); |
43 | jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY); |
44 | jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY); |
45 | |
46 | /* combined x samples bits */ |
47 | jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY); |
48 | |
49 | /* combined y samples bits */ |
50 | jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY); |
51 | } |
52 | |
53 | static int jornada720_ts_average(int coords[4]) |
54 | { |
55 | int coord, high_bits = coords[3]; |
56 | |
57 | coord = coords[0] | ((high_bits & 0x03) << 8); |
58 | coord += coords[1] | ((high_bits & 0x0c) << 6); |
59 | coord += coords[2] | ((high_bits & 0x30) << 4); |
60 | |
61 | return coord / 3; |
62 | } |
63 | |
64 | static irqreturn_t jornada720_ts_interrupt(int irq, void *dev_id) |
65 | { |
66 | struct platform_device *pdev = dev_id; |
67 | struct jornada_ts *jornada_ts = platform_get_drvdata(pdev); |
68 | struct input_dev *input = jornada_ts->dev; |
69 | int x, y; |
70 | |
71 | /* If gpio is high then report pen up */ |
72 | if (gpiod_get_value(desc: jornada_ts->gpio)) { |
73 | input_report_key(dev: input, BTN_TOUCH, value: 0); |
74 | input_sync(dev: input); |
75 | } else { |
76 | jornada_ssp_start(); |
77 | |
78 | /* proper reply to request is always TXDUMMY */ |
79 | if (jornada_ssp_inout(GETTOUCHSAMPLES) == TXDUMMY) { |
80 | jornada720_ts_collect_data(jornada_ts); |
81 | |
82 | x = jornada720_ts_average(coords: jornada_ts->x_data); |
83 | y = jornada720_ts_average(coords: jornada_ts->y_data); |
84 | |
85 | input_report_key(dev: input, BTN_TOUCH, value: 1); |
86 | input_report_abs(dev: input, ABS_X, value: x); |
87 | input_report_abs(dev: input, ABS_Y, value: y); |
88 | input_sync(dev: input); |
89 | } |
90 | |
91 | jornada_ssp_end(); |
92 | } |
93 | |
94 | return IRQ_HANDLED; |
95 | } |
96 | |
97 | static int jornada720_ts_probe(struct platform_device *pdev) |
98 | { |
99 | struct jornada_ts *jornada_ts; |
100 | struct input_dev *input_dev; |
101 | int error, irq; |
102 | |
103 | jornada_ts = devm_kzalloc(dev: &pdev->dev, size: sizeof(*jornada_ts), GFP_KERNEL); |
104 | if (!jornada_ts) |
105 | return -ENOMEM; |
106 | |
107 | input_dev = devm_input_allocate_device(&pdev->dev); |
108 | if (!input_dev) |
109 | return -ENOMEM; |
110 | |
111 | platform_set_drvdata(pdev, data: jornada_ts); |
112 | |
113 | jornada_ts->gpio = devm_gpiod_get(dev: &pdev->dev, con_id: "penup" , flags: GPIOD_IN); |
114 | if (IS_ERR(ptr: jornada_ts->gpio)) |
115 | return PTR_ERR(ptr: jornada_ts->gpio); |
116 | |
117 | irq = gpiod_to_irq(desc: jornada_ts->gpio); |
118 | if (irq <= 0) |
119 | return irq < 0 ? irq : -EINVAL; |
120 | |
121 | jornada_ts->dev = input_dev; |
122 | |
123 | input_dev->name = "HP Jornada 7xx Touchscreen" ; |
124 | input_dev->phys = "jornadats/input0" ; |
125 | input_dev->id.bustype = BUS_HOST; |
126 | input_dev->dev.parent = &pdev->dev; |
127 | |
128 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); |
129 | input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); |
130 | input_set_abs_params(dev: input_dev, ABS_X, min: 270, max: 3900, fuzz: 0, flat: 0); |
131 | input_set_abs_params(dev: input_dev, ABS_Y, min: 180, max: 3700, fuzz: 0, flat: 0); |
132 | |
133 | error = devm_request_irq(dev: &pdev->dev, irq, handler: jornada720_ts_interrupt, |
134 | IRQF_TRIGGER_RISING, |
135 | devname: "HP7XX Touchscreen driver" , dev_id: pdev); |
136 | if (error) { |
137 | dev_err(&pdev->dev, "HP7XX TS : Unable to acquire irq!\n" ); |
138 | return error; |
139 | } |
140 | |
141 | error = input_register_device(jornada_ts->dev); |
142 | if (error) |
143 | return error; |
144 | |
145 | return 0; |
146 | } |
147 | |
148 | /* work with hotplug and coldplug */ |
149 | MODULE_ALIAS("platform:jornada_ts" ); |
150 | |
151 | static struct platform_driver jornada720_ts_driver = { |
152 | .probe = jornada720_ts_probe, |
153 | .driver = { |
154 | .name = "jornada_ts" , |
155 | }, |
156 | }; |
157 | module_platform_driver(jornada720_ts_driver); |
158 | |