Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.9.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Driver for MStar msg2638 touchscreens
  4 *
  5 * Copyright (c) 2021 Vincent Knecht <vincent.knecht@mailoo.org>
  6 *
  7 * Checksum and IRQ handler based on mstar_drv_common.c and
  8 * mstar_drv_mutual_fw_control.c
  9 * Copyright (c) 2006-2012 MStar Semiconductor, Inc.
 10 *
 11 * Driver structure based on zinitix.c by Michael Srba <Michael.Srba@seznam.cz>
 12 */
 13
 14#include <linux/delay.h>
 15#include <linux/gpio/consumer.h>
 16#include <linux/i2c.h>
 17#include <linux/input.h>
 18#include <linux/input/mt.h>
 19#include <linux/input/touchscreen.h>
 20#include <linux/interrupt.h>
 21#include <linux/kernel.h>
 22#include <linux/mod_devicetable.h>
 23#include <linux/module.h>
 24#include <linux/regulator/consumer.h>
 25#include <linux/slab.h>
 26
 27#define MODE_DATA_RAW			0x5A
 28
 29#define MAX_SUPPORTED_FINGER_NUM	5
 30
 31#define CHIP_ON_DELAY_MS		15
 32#define FIRMWARE_ON_DELAY_MS		50
 33#define RESET_DELAY_MIN_US		10000
 34#define RESET_DELAY_MAX_US		11000
 35
 36struct packet {
 37	u8	xy_hi; /* higher bits of x and y coordinates */
 38	u8	x_low;
 39	u8	y_low;
 40	u8	pressure;
 41};
 42
 43struct touch_event {
 44	u8	mode;
 45	struct	packet pkt[MAX_SUPPORTED_FINGER_NUM];
 46	u8	proximity;
 47	u8	checksum;
 48};
 49
 50struct msg2638_ts_data {
 51	struct i2c_client *client;
 52	struct input_dev *input_dev;
 53	struct touchscreen_properties prop;
 54	struct regulator_bulk_data supplies[2];
 55	struct gpio_desc *reset_gpiod;
 56};
 57
 58static u8 msg2638_checksum(u8 *data, u32 length)
 59{
 60	s32 sum = 0;
 61	u32 i;
 62
 63	for (i = 0; i < length; i++)
 64		sum += data[i];
 65
 66	return (u8)((-sum) & 0xFF);
 67}
 68
 69static irqreturn_t msg2638_ts_irq_handler(int irq, void *msg2638_handler)
 70{
 71	struct msg2638_ts_data *msg2638 = msg2638_handler;
 72	struct i2c_client *client = msg2638->client;
 73	struct input_dev *input = msg2638->input_dev;
 74	struct touch_event touch_event;
 75	u32 len = sizeof(touch_event);
 76	struct i2c_msg msg[] = {
 77		{
 78			.addr	= client->addr,
 79			.flags	= I2C_M_RD,
 80			.len	= sizeof(touch_event),
 81			.buf	= (u8 *)&touch_event,
 82		},
 83	};
 84	struct packet *p;
 85	u16 x, y;
 86	int ret;
 87	int i;
 88
 89	ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
 90	if (ret != ARRAY_SIZE(msg)) {
 91		dev_err(&client->dev,
 92			"Failed I2C transfer in irq handler: %d\n",
 93			ret < 0 ? ret : -EIO);
 94		goto out;
 95	}
 96
 97	if (touch_event.mode != MODE_DATA_RAW)
 98		goto out;
 99
100	if (msg2638_checksum((u8 *)&touch_event, len - 1) !=
101						touch_event.checksum) {
102		dev_err(&client->dev, "Failed checksum!\n");
103		goto out;
104	}
105
106	for (i = 0; i < MAX_SUPPORTED_FINGER_NUM; i++) {
107		p = &touch_event.pkt[i];
108
109		/* Ignore non-pressed finger data */
110		if (p->xy_hi == 0xFF && p->x_low == 0xFF && p->y_low == 0xFF)
111			continue;
112
113		x = (((p->xy_hi & 0xF0) << 4) | p->x_low);
114		y = (((p->xy_hi & 0x0F) << 8) | p->y_low);
115
116		input_mt_slot(input, i);
117		input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
118		touchscreen_report_pos(input, &msg2638->prop, x, y, true);
119	}
120
121	input_mt_sync_frame(msg2638->input_dev);
122	input_sync(msg2638->input_dev);
123
124out:
125	return IRQ_HANDLED;
126}
127
128static void msg2638_reset(struct msg2638_ts_data *msg2638)
129{
130	gpiod_set_value_cansleep(msg2638->reset_gpiod, 1);
131	usleep_range(RESET_DELAY_MIN_US, RESET_DELAY_MAX_US);
132	gpiod_set_value_cansleep(msg2638->reset_gpiod, 0);
133	msleep(FIRMWARE_ON_DELAY_MS);
134}
135
136static int msg2638_start(struct msg2638_ts_data *msg2638)
137{
138	int error;
139
140	error = regulator_bulk_enable(ARRAY_SIZE(msg2638->supplies),
141				      msg2638->supplies);
142	if (error) {
143		dev_err(&msg2638->client->dev,
144			"Failed to enable regulators: %d\n", error);
145		return error;
146	}
147
148	msleep(CHIP_ON_DELAY_MS);
149
150	msg2638_reset(msg2638);
151
152	enable_irq(msg2638->client->irq);
153
154	return 0;
155}
156
157static int msg2638_stop(struct msg2638_ts_data *msg2638)
158{
159	int error;
160
161	disable_irq(msg2638->client->irq);
162
163	error = regulator_bulk_disable(ARRAY_SIZE(msg2638->supplies),
164				       msg2638->supplies);
165	if (error) {
166		dev_err(&msg2638->client->dev,
167			"Failed to disable regulators: %d\n", error);
168		return error;
169	}
170
171	return 0;
172}
173
174static int msg2638_input_open(struct input_dev *dev)
175{
176	struct msg2638_ts_data *msg2638 = input_get_drvdata(dev);
177
178	return msg2638_start(msg2638);
179}
180
181static void msg2638_input_close(struct input_dev *dev)
182{
183	struct msg2638_ts_data *msg2638 = input_get_drvdata(dev);
184
185	msg2638_stop(msg2638);
186}
187
188static int msg2638_init_input_dev(struct msg2638_ts_data *msg2638)
189{
190	struct device *dev = &msg2638->client->dev;
191	struct input_dev *input_dev;
192	int error;
193
194	input_dev = devm_input_allocate_device(dev);
195	if (!input_dev) {
196		dev_err(dev, "Failed to allocate input device.\n");
197		return -ENOMEM;
198	}
199
200	input_set_drvdata(input_dev, msg2638);
201	msg2638->input_dev = input_dev;
202
203	input_dev->name = "MStar TouchScreen";
204	input_dev->phys = "input/ts";
205	input_dev->id.bustype = BUS_I2C;
206	input_dev->open = msg2638_input_open;
207	input_dev->close = msg2638_input_close;
208
209	input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X);
210	input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y);
211
212	touchscreen_parse_properties(input_dev, true, &msg2638->prop);
213	if (!msg2638->prop.max_x || !msg2638->prop.max_y) {
214		dev_err(dev, "touchscreen-size-x and/or touchscreen-size-y not set in properties\n");
215		return -EINVAL;
216	}
217
218	error = input_mt_init_slots(input_dev, MAX_SUPPORTED_FINGER_NUM,
219				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
220	if (error) {
221		dev_err(dev, "Failed to initialize MT slots: %d\n", error);
222		return error;
223	}
224
225	error = input_register_device(input_dev);
226	if (error) {
227		dev_err(dev, "Failed to register input device: %d\n", error);
228		return error;
229	}
230
231	return 0;
232}
233
234static int msg2638_ts_probe(struct i2c_client *client)
235{
236	struct device *dev = &client->dev;
237	struct msg2638_ts_data *msg2638;
238	int error;
239
240	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
241		dev_err(dev, "Failed to assert adapter's support for plain I2C.\n");
242		return -ENXIO;
243	}
244
245	msg2638 = devm_kzalloc(dev, sizeof(*msg2638), GFP_KERNEL);
246	if (!msg2638)
247		return -ENOMEM;
248
249	msg2638->client = client;
250	i2c_set_clientdata(client, msg2638);
251
252	msg2638->supplies[0].supply = "vdd";
253	msg2638->supplies[1].supply = "vddio";
254	error = devm_regulator_bulk_get(dev, ARRAY_SIZE(msg2638->supplies),
255					msg2638->supplies);
256	if (error) {
257		dev_err(dev, "Failed to get regulators: %d\n", error);
258		return error;
259	}
260
261	msg2638->reset_gpiod = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
262	if (IS_ERR(msg2638->reset_gpiod)) {
263		error = PTR_ERR(msg2638->reset_gpiod);
264		dev_err(dev, "Failed to request reset GPIO: %d\n", error);
265		return error;
266	}
267
268	error = msg2638_init_input_dev(msg2638);
269	if (error) {
270		dev_err(dev, "Failed to initialize input device: %d\n", error);
271		return error;
272	}
273
274	error = devm_request_threaded_irq(dev, client->irq,
275					  NULL, msg2638_ts_irq_handler,
276					  IRQF_ONESHOT | IRQF_NO_AUTOEN,
277					  client->name, msg2638);
278	if (error) {
279		dev_err(dev, "Failed to request IRQ: %d\n", error);
280		return error;
281	}
282
283	return 0;
284}
285
286static int __maybe_unused msg2638_suspend(struct device *dev)
287{
288	struct i2c_client *client = to_i2c_client(dev);
289	struct msg2638_ts_data *msg2638 = i2c_get_clientdata(client);
290
291	mutex_lock(&msg2638->input_dev->mutex);
292
293	if (input_device_enabled(msg2638->input_dev))
294		msg2638_stop(msg2638);
295
296	mutex_unlock(&msg2638->input_dev->mutex);
297
298	return 0;
299}
300
301static int __maybe_unused msg2638_resume(struct device *dev)
302{
303	struct i2c_client *client = to_i2c_client(dev);
304	struct msg2638_ts_data *msg2638 = i2c_get_clientdata(client);
305	int ret = 0;
306
307	mutex_lock(&msg2638->input_dev->mutex);
308
309	if (input_device_enabled(msg2638->input_dev))
310		ret = msg2638_start(msg2638);
311
312	mutex_unlock(&msg2638->input_dev->mutex);
313
314	return ret;
315}
316
317static SIMPLE_DEV_PM_OPS(msg2638_pm_ops, msg2638_suspend, msg2638_resume);
318
319static const struct of_device_id msg2638_of_match[] = {
320	{ .compatible = "mstar,msg2638" },
321	{ }
322};
323MODULE_DEVICE_TABLE(of, msg2638_of_match);
324
325static struct i2c_driver msg2638_ts_driver = {
326	.probe_new = msg2638_ts_probe,
327	.driver = {
328		.name = "MStar-TS",
329		.pm = &msg2638_pm_ops,
330		.of_match_table = msg2638_of_match,
331	},
332};
333module_i2c_driver(msg2638_ts_driver);
334
335MODULE_AUTHOR("Vincent Knecht <vincent.knecht@mailoo.org>");
336MODULE_DESCRIPTION("MStar MSG2638 touchscreen driver");
337MODULE_LICENSE("GPL v2");