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