summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen/crtouch_ts.c
blob: e5e23b8d5d78fc896df0fe659b57cb88388fbbb2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/*
 * Driver for Freescale Semiconductor CRTOUCH - A Resistive and Capacitive
 * touch device with i2c interface
 *
 * Copyright 2012 Freescale Semiconductor, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <linux/gpio.h>

/* Resistive touch sense status registers */
#define RES_STA_ERROR			0x00
#define RES_STA_STATUS1			0x01
#define	RES_STA_STATUS2			0x02
#define RES_STA_X_MSB			0x03
#define RES_STA_X_LSB			0x04
#define RES_STA_Y_MSB			0x05
#define RES_STA_Y_LSB			0x06
#define RES_STA_PRES_MSB		0x07
#define RES_STA_RRES_LSB		0x08
#define RES_STA_FIFO_STATUS		0x09
#define RES_STA_FIFO_X_MSB		0x0a
#define RES_STA_FIFO_X_LSB		0x0b
#define RES_STA_FIFO_Y_MSB		0x0c
#define RES_STA_FIFO_Y_LSB		0x0d
#define RES_STA_FIFO_PRES_MSB		0x0e
#define RES_STA_FIFO_PRES_LSB		0x0f
#define RES_STA_UART_BRATE_MSB		0x10
#define RES_STA_UART_BRATE_MID		0x11
#define RES_STA_UART_BRATE_LSB		0x12
#define RES_STA_DEV_IDEN		0x13
#define RES_STA_SLIDE_DISPLACE		0x14
#define RES_STA_ROTATE_ANGLE		0x15

/* Resistive touch configuration registers */
#define CR_CON_SYSTEM			0x40
#define CR_CON_TRIG_EVENT		0x41
#define CR_CON_FIFO_SETUP		0x42
#define CR_CON_SAMPLING_RATE		0x43
#define CR_CON_X_DELAY_MSB		0x44
#define CR_CON_X_DELAY_LSB		0x45
#define CR_CON_Y_DELAY_MSB		0x46
#define CR_CON_Y_DELAY_LSB		0x47
#define CR_CON_Z_DELAY_MSB		0x48
#define CR_CON_Z_DELAY_LSB		0x49
#define CR_CON_DIS_HOR_MSB		0x4a
#define CR_CON_DIS_HOR_LSB		0x4b
#define CR_CON_DIS_VER_MSB		0x4c
#define CR_CON_DIS_VER_LSB		0x4d
#define CR_CON_SLIDE_STEPS		0x4e


#define RTST_EVENT			(1 << 7)
#define RTS2T_EVENT			(1 << 6)
#define RTSZ_EVENT			(1 << 5)
#define RTSR_EVENT			(1 << 4)
#define RTSS_EVENT			(1 << 3)
#define	RTSF_EVENT			(1 << 2)
#define RTSRDY_EVENT			(1 << 0)

#define RTSSD_MASK			(1 << 2)
#define RTSSD_H_POS			(0 << 2)
#define RTSSD_H_NEG			(1 << 2)
#define RTSSD_V_POS			(1 << 3)
#define RTSSD_V_NEG			(1 << 4)

#define RTSRD_MASK			(1 << 4)
#define RTSRD_CLK_WISE			(0 << 4)
#define RTSRD_COUNTER_CLK_WISE		(1 << 4)

#define RTSZD_MASK			(1 << 5)
#define RTSZD_ZOOM_IN			(0 << 5)
#define RTSZD_ZOOM_OUT			(1 << 5)

#define CRTOUCH_MAX_FINGER		2
#define CRTOUCH_MAX_AREA		0xfff
#define CRTOUCH_MAX_X			0x01df
#define CRTOUCH_MAX_Y			0x010f


struct crtouch_ts_data {
	struct i2c_client	*client;
	struct input_dev	*input_dev;
};

static u8 crtouch_read_reg(struct i2c_client *client, int addr)
{
	return i2c_smbus_read_byte_data(client, addr);
}

static int crtouch_write_reg(struct i2c_client *client, int addr, int data)
{
	return i2c_smbus_write_byte_data(client, addr, data);
}

static void calibration_pointer(u16 *x_orig, u16 *y_orig)
{
	u16 x, y;

	x = CRTOUCH_MAX_X - *x_orig;
	*x_orig = x;

	y = CRTOUCH_MAX_Y - *y_orig;
	*y_orig = y;
}

static irqreturn_t crtouch_ts_interrupt(int irq, void *dev_id)
{
	struct crtouch_ts_data *data = dev_id;
	struct i2c_client *client = data->client;
	u8 status1;
	u16 valuep, valuex, valuey;

	status1 = crtouch_read_reg(client, RES_STA_STATUS1);

	/* For single touch */
	if (status1 & RTST_EVENT) {
		valuep = crtouch_read_reg(client, RES_STA_PRES_MSB);
		valuep = ((valuep << 8) |
			crtouch_read_reg(client, RES_STA_RRES_LSB));
		valuex = crtouch_read_reg(client, RES_STA_X_MSB);
		valuex = ((valuex << 8) |
			crtouch_read_reg(client, RES_STA_X_LSB));
		valuey = crtouch_read_reg(client, RES_STA_Y_MSB);
		valuey = ((valuey << 8) |
			crtouch_read_reg(client, RES_STA_Y_LSB));
		calibration_pointer(&valuex, &valuey);
		input_report_key(data->input_dev, BTN_TOUCH, 1);
		input_report_abs(data->input_dev, ABS_X, valuex);
		input_report_abs(data->input_dev, ABS_Y, valuey);
		input_report_abs(data->input_dev, ABS_PRESSURE, valuep);
		input_sync(data->input_dev);
	} else {
		input_report_abs(data->input_dev, ABS_PRESSURE, 0);
		input_event(data->input_dev, EV_KEY, BTN_TOUCH, 0);
		input_sync(data->input_dev);
	}

	return IRQ_HANDLED;
}

static void __devinit crtouch_ts_reg_init(struct crtouch_ts_data *data)
{
	struct i2c_client *client = data->client;

	crtouch_write_reg(client, CR_CON_SYSTEM, 0x9c);
	crtouch_write_reg(client, CR_CON_TRIG_EVENT, 0xf9);
	crtouch_write_reg(client, CR_CON_FIFO_SETUP, 0x1f);
	crtouch_write_reg(client, CR_CON_SAMPLING_RATE, 0x08);
	crtouch_write_reg(client, CR_CON_DIS_HOR_MSB, 0x01);
	crtouch_write_reg(client, CR_CON_DIS_HOR_LSB, 0xdf);
	crtouch_write_reg(client, CR_CON_DIS_VER_MSB, 0x01);
	crtouch_write_reg(client, CR_CON_DIS_VER_LSB, 0x0f);
}

static int __devinit crtouch_ts_probe(struct i2c_client *client,
		const struct i2c_device_id *id)
{
	struct crtouch_ts_data *data;
	struct input_dev *input_dev;
	int error;

	data = kzalloc(sizeof(struct crtouch_ts_data), GFP_KERNEL);
	input_dev = input_allocate_device();
	if (!data || !input_dev) {
		dev_err(&client->dev, "Failed to allocate memory\n");
		error = -ENOMEM;
		goto err_free_mem;
	}

	data->client = client;
	data->input_dev = input_dev;

	input_dev->name = "crtouch_ts";
	input_dev->id.bustype = BUS_I2C;
	input_dev->dev.parent = &client->dev;

	/* For single touch */
	__set_bit(EV_KEY, input_dev->evbit);
	__set_bit(BTN_TOUCH, input_dev->keybit);
	__set_bit(EV_ABS, input_dev->evbit);
	__set_bit(ABS_X, input_dev->absbit);
	__set_bit(ABS_Y, input_dev->absbit);
	__set_bit(ABS_PRESSURE, input_dev->absbit);

	input_set_abs_params(input_dev, ABS_X, 0, CRTOUCH_MAX_X, 0, 0);
	input_set_abs_params(input_dev, ABS_Y, 0, CRTOUCH_MAX_Y, 0, 0);
	input_set_abs_params(input_dev, ABS_PRESSURE, 0,
		CRTOUCH_MAX_AREA, 0, 0);

	input_set_drvdata(input_dev, data);

	crtouch_ts_reg_init(data);

	error = gpio_request_one(21, GPIOF_IN, "TS_IRQ");
	if (error) {
		dev_err(&client->dev, "Failed to request gpio\n");
		goto err_free_mem;
	}

	error = request_threaded_irq(gpio_to_irq(21), NULL,
		crtouch_ts_interrupt, IRQF_TRIGGER_FALLING, "crtouch_ts", data);
	if (error) {
		dev_err(&client->dev, "Failed to register interrupt\n");
		goto err_free_mem;
	}

	error = input_register_device(data->input_dev);
	if (error)
		goto err_free_irq;

	i2c_set_clientdata(client, data);
	return 0;

err_free_irq:
	free_irq(client->irq, data);
err_free_mem:
	input_free_device(input_dev);
	kfree(data);
	return error;
}

static __devexit int crtouch_ts_remove(struct i2c_client *client)
{
	struct crtouch_ts_data *data = i2c_get_clientdata(client);

	free_irq(client->irq, data);
	input_unregister_device(data->input_dev);
	kfree(data);

	return 0;
}

static const struct i2c_device_id crtouch_ts_id[] = {
	{"crtouch_ts", 0},
	{ }
};
MODULE_DEVICE_TABLE(i2c, crtouch_ts_id);

static struct i2c_driver crtouch_ts_driver = {
	.driver = {
		.name	= "crtouch_ts",
		.owner	= THIS_MODULE,
	},
	.id_table	= crtouch_ts_id,
	.probe		= crtouch_ts_probe,
	.remove		= __devexit_p(crtouch_ts_remove),
};

static int __init crtouch_ts_init(void)
{
	return i2c_add_driver(&crtouch_ts_driver);
}

static void __exit crtouch_ts_exit(void)
{
	i2c_del_driver(&crtouch_ts_driver);
}

module_init(crtouch_ts_init);
module_exit(crtouch_ts_exit);

MODULE_AUTHOR("Alison Wang <b18965@freescale.com>");
MODULE_DESCRIPTION("Touchscreen driver for Freescale CRTOUCH");
MODULE_LICENSE("GPL");