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