cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

msg2638.c (8132B)


      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");