Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/input/touchscreen/migor_ts.c
15111 views
1
/*
2
* Touch Screen driver for Renesas MIGO-R Platform
3
*
4
* Copyright (c) 2008 Magnus Damm
5
* Copyright (c) 2007 Ujjwal Pande <[email protected]>,
6
* Kenati Technologies Pvt Ltd.
7
*
8
* This file is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU General Public
10
* License as published by the Free Software Foundation; either
11
* version 2 of the License, or (at your option) any later version.
12
*
13
* This file is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
* General Public License for more details.
17
*
18
* You should have received a copy of the GNU General Public
19
* License along with this library; if not, write to the Free Software
20
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
*/
22
#include <linux/module.h>
23
#include <linux/kernel.h>
24
#include <linux/input.h>
25
#include <linux/interrupt.h>
26
#include <linux/pm.h>
27
#include <linux/slab.h>
28
#include <asm/io.h>
29
#include <linux/i2c.h>
30
#include <linux/timer.h>
31
32
#define EVENT_PENDOWN 1
33
#define EVENT_REPEAT 2
34
#define EVENT_PENUP 3
35
36
struct migor_ts_priv {
37
struct i2c_client *client;
38
struct input_dev *input;
39
struct delayed_work work;
40
int irq;
41
};
42
43
static const u_int8_t migor_ts_ena_seq[17] = { 0x33, 0x22, 0x11,
44
0x01, 0x06, 0x07, };
45
static const u_int8_t migor_ts_dis_seq[17] = { };
46
47
static void migor_ts_poscheck(struct work_struct *work)
48
{
49
struct migor_ts_priv *priv = container_of(work,
50
struct migor_ts_priv,
51
work.work);
52
unsigned short xpos, ypos;
53
unsigned char event;
54
u_int8_t buf[16];
55
56
memset(buf, 0, sizeof(buf));
57
58
/* Set Index 0 */
59
buf[0] = 0;
60
if (i2c_master_send(priv->client, buf, 1) != 1) {
61
dev_err(&priv->client->dev, "Unable to write i2c index\n");
62
goto out;
63
}
64
65
/* Now do Page Read */
66
if (i2c_master_recv(priv->client, buf, sizeof(buf)) != sizeof(buf)) {
67
dev_err(&priv->client->dev, "Unable to read i2c page\n");
68
goto out;
69
}
70
71
ypos = ((buf[9] & 0x03) << 8 | buf[8]);
72
xpos = ((buf[11] & 0x03) << 8 | buf[10]);
73
event = buf[12];
74
75
if (event == EVENT_PENDOWN || event == EVENT_REPEAT) {
76
input_report_key(priv->input, BTN_TOUCH, 1);
77
input_report_abs(priv->input, ABS_X, ypos); /*X-Y swap*/
78
input_report_abs(priv->input, ABS_Y, xpos);
79
input_sync(priv->input);
80
} else if (event == EVENT_PENUP) {
81
input_report_key(priv->input, BTN_TOUCH, 0);
82
input_sync(priv->input);
83
}
84
out:
85
enable_irq(priv->irq);
86
}
87
88
static irqreturn_t migor_ts_isr(int irq, void *dev_id)
89
{
90
struct migor_ts_priv *priv = dev_id;
91
92
/* the touch screen controller chip is hooked up to the cpu
93
* using i2c and a single interrupt line. the interrupt line
94
* is pulled low whenever someone taps the screen. to deassert
95
* the interrupt line we need to acknowledge the interrupt by
96
* communicating with the controller over the slow i2c bus.
97
*
98
* we can't acknowledge from interrupt context since the i2c
99
* bus controller may sleep, so we just disable the interrupt
100
* here and handle the acknowledge using delayed work.
101
*/
102
103
disable_irq_nosync(irq);
104
schedule_delayed_work(&priv->work, HZ / 20);
105
106
return IRQ_HANDLED;
107
}
108
109
110
static int migor_ts_open(struct input_dev *dev)
111
{
112
struct migor_ts_priv *priv = input_get_drvdata(dev);
113
struct i2c_client *client = priv->client;
114
int count;
115
116
/* enable controller */
117
count = i2c_master_send(client, migor_ts_ena_seq,
118
sizeof(migor_ts_ena_seq));
119
if (count != sizeof(migor_ts_ena_seq)) {
120
dev_err(&client->dev, "Unable to enable touchscreen.\n");
121
return -ENXIO;
122
}
123
124
return 0;
125
}
126
127
static void migor_ts_close(struct input_dev *dev)
128
{
129
struct migor_ts_priv *priv = input_get_drvdata(dev);
130
struct i2c_client *client = priv->client;
131
132
disable_irq(priv->irq);
133
134
/* cancel pending work and wait for migor_ts_poscheck() to finish */
135
if (cancel_delayed_work_sync(&priv->work)) {
136
/*
137
* if migor_ts_poscheck was canceled we need to enable IRQ
138
* here to balance disable done in migor_ts_isr.
139
*/
140
enable_irq(priv->irq);
141
}
142
143
/* disable controller */
144
i2c_master_send(client, migor_ts_dis_seq, sizeof(migor_ts_dis_seq));
145
146
enable_irq(priv->irq);
147
}
148
149
static int migor_ts_probe(struct i2c_client *client,
150
const struct i2c_device_id *idp)
151
{
152
struct migor_ts_priv *priv;
153
struct input_dev *input;
154
int error;
155
156
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
157
if (!priv) {
158
dev_err(&client->dev, "failed to allocate driver data\n");
159
error = -ENOMEM;
160
goto err0;
161
}
162
163
dev_set_drvdata(&client->dev, priv);
164
165
input = input_allocate_device();
166
if (!input) {
167
dev_err(&client->dev, "Failed to allocate input device.\n");
168
error = -ENOMEM;
169
goto err1;
170
}
171
172
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
173
input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
174
175
input_set_abs_params(input, ABS_X, 95, 955, 0, 0);
176
input_set_abs_params(input, ABS_Y, 85, 935, 0, 0);
177
178
input->name = client->name;
179
input->id.bustype = BUS_I2C;
180
input->dev.parent = &client->dev;
181
182
input->open = migor_ts_open;
183
input->close = migor_ts_close;
184
185
input_set_drvdata(input, priv);
186
187
priv->client = client;
188
priv->input = input;
189
INIT_DELAYED_WORK(&priv->work, migor_ts_poscheck);
190
priv->irq = client->irq;
191
192
error = input_register_device(input);
193
if (error)
194
goto err1;
195
196
error = request_irq(priv->irq, migor_ts_isr, IRQF_TRIGGER_LOW,
197
client->name, priv);
198
if (error) {
199
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
200
goto err2;
201
}
202
203
device_init_wakeup(&client->dev, 1);
204
return 0;
205
206
err2:
207
input_unregister_device(input);
208
input = NULL; /* so we dont try to free it below */
209
err1:
210
input_free_device(input);
211
kfree(priv);
212
err0:
213
dev_set_drvdata(&client->dev, NULL);
214
return error;
215
}
216
217
static int migor_ts_remove(struct i2c_client *client)
218
{
219
struct migor_ts_priv *priv = dev_get_drvdata(&client->dev);
220
221
free_irq(priv->irq, priv);
222
input_unregister_device(priv->input);
223
kfree(priv);
224
225
dev_set_drvdata(&client->dev, NULL);
226
227
return 0;
228
}
229
230
static int migor_ts_suspend(struct device *dev)
231
{
232
struct i2c_client *client = to_i2c_client(dev);
233
struct migor_ts_priv *priv = dev_get_drvdata(&client->dev);
234
235
if (device_may_wakeup(&client->dev))
236
enable_irq_wake(priv->irq);
237
238
return 0;
239
}
240
241
static int migor_ts_resume(struct device *dev)
242
{
243
struct i2c_client *client = to_i2c_client(dev);
244
struct migor_ts_priv *priv = dev_get_drvdata(&client->dev);
245
246
if (device_may_wakeup(&client->dev))
247
disable_irq_wake(priv->irq);
248
249
return 0;
250
}
251
252
static SIMPLE_DEV_PM_OPS(migor_ts_pm, migor_ts_suspend, migor_ts_resume);
253
254
static const struct i2c_device_id migor_ts_id[] = {
255
{ "migor_ts", 0 },
256
{ }
257
};
258
MODULE_DEVICE_TABLE(i2c, migor_ts);
259
260
static struct i2c_driver migor_ts_driver = {
261
.driver = {
262
.name = "migor_ts",
263
.pm = &migor_ts_pm,
264
},
265
.probe = migor_ts_probe,
266
.remove = migor_ts_remove,
267
.id_table = migor_ts_id,
268
};
269
270
static int __init migor_ts_init(void)
271
{
272
return i2c_add_driver(&migor_ts_driver);
273
}
274
275
static void __exit migor_ts_exit(void)
276
{
277
i2c_del_driver(&migor_ts_driver);
278
}
279
280
MODULE_DESCRIPTION("MigoR Touchscreen driver");
281
MODULE_AUTHOR("Magnus Damm <[email protected]>");
282
MODULE_LICENSE("GPL");
283
284
module_init(migor_ts_init);
285
module_exit(migor_ts_exit);
286
287