18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * mcs5000_ts.c - Touchscreen driver for MELFAS MCS-5000 controller 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Samsung Electronics Co.Ltd 68c2ecf20Sopenharmony_ci * Author: Joonyoung Shim <jy0922.shim@samsung.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on wm97xx-core.c 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/i2c.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/input.h> 158c2ecf20Sopenharmony_ci#include <linux/irq.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_data/mcs.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* Registers */ 208c2ecf20Sopenharmony_ci#define MCS5000_TS_STATUS 0x00 218c2ecf20Sopenharmony_ci#define STATUS_OFFSET 0 228c2ecf20Sopenharmony_ci#define STATUS_NO (0 << STATUS_OFFSET) 238c2ecf20Sopenharmony_ci#define STATUS_INIT (1 << STATUS_OFFSET) 248c2ecf20Sopenharmony_ci#define STATUS_SENSING (2 << STATUS_OFFSET) 258c2ecf20Sopenharmony_ci#define STATUS_COORD (3 << STATUS_OFFSET) 268c2ecf20Sopenharmony_ci#define STATUS_GESTURE (4 << STATUS_OFFSET) 278c2ecf20Sopenharmony_ci#define ERROR_OFFSET 4 288c2ecf20Sopenharmony_ci#define ERROR_NO (0 << ERROR_OFFSET) 298c2ecf20Sopenharmony_ci#define ERROR_POWER_ON_RESET (1 << ERROR_OFFSET) 308c2ecf20Sopenharmony_ci#define ERROR_INT_RESET (2 << ERROR_OFFSET) 318c2ecf20Sopenharmony_ci#define ERROR_EXT_RESET (3 << ERROR_OFFSET) 328c2ecf20Sopenharmony_ci#define ERROR_INVALID_REG_ADDRESS (8 << ERROR_OFFSET) 338c2ecf20Sopenharmony_ci#define ERROR_INVALID_REG_VALUE (9 << ERROR_OFFSET) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define MCS5000_TS_OP_MODE 0x01 368c2ecf20Sopenharmony_ci#define RESET_OFFSET 0 378c2ecf20Sopenharmony_ci#define RESET_NO (0 << RESET_OFFSET) 388c2ecf20Sopenharmony_ci#define RESET_EXT_SOFT (1 << RESET_OFFSET) 398c2ecf20Sopenharmony_ci#define OP_MODE_OFFSET 1 408c2ecf20Sopenharmony_ci#define OP_MODE_SLEEP (0 << OP_MODE_OFFSET) 418c2ecf20Sopenharmony_ci#define OP_MODE_ACTIVE (1 << OP_MODE_OFFSET) 428c2ecf20Sopenharmony_ci#define GESTURE_OFFSET 4 438c2ecf20Sopenharmony_ci#define GESTURE_DISABLE (0 << GESTURE_OFFSET) 448c2ecf20Sopenharmony_ci#define GESTURE_ENABLE (1 << GESTURE_OFFSET) 458c2ecf20Sopenharmony_ci#define PROXIMITY_OFFSET 5 468c2ecf20Sopenharmony_ci#define PROXIMITY_DISABLE (0 << PROXIMITY_OFFSET) 478c2ecf20Sopenharmony_ci#define PROXIMITY_ENABLE (1 << PROXIMITY_OFFSET) 488c2ecf20Sopenharmony_ci#define SCAN_MODE_OFFSET 6 498c2ecf20Sopenharmony_ci#define SCAN_MODE_INTERRUPT (0 << SCAN_MODE_OFFSET) 508c2ecf20Sopenharmony_ci#define SCAN_MODE_POLLING (1 << SCAN_MODE_OFFSET) 518c2ecf20Sopenharmony_ci#define REPORT_RATE_OFFSET 7 528c2ecf20Sopenharmony_ci#define REPORT_RATE_40 (0 << REPORT_RATE_OFFSET) 538c2ecf20Sopenharmony_ci#define REPORT_RATE_80 (1 << REPORT_RATE_OFFSET) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define MCS5000_TS_SENS_CTL 0x02 568c2ecf20Sopenharmony_ci#define MCS5000_TS_FILTER_CTL 0x03 578c2ecf20Sopenharmony_ci#define PRI_FILTER_OFFSET 0 588c2ecf20Sopenharmony_ci#define SEC_FILTER_OFFSET 4 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define MCS5000_TS_X_SIZE_UPPER 0x08 618c2ecf20Sopenharmony_ci#define MCS5000_TS_X_SIZE_LOWER 0x09 628c2ecf20Sopenharmony_ci#define MCS5000_TS_Y_SIZE_UPPER 0x0A 638c2ecf20Sopenharmony_ci#define MCS5000_TS_Y_SIZE_LOWER 0x0B 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define MCS5000_TS_INPUT_INFO 0x10 668c2ecf20Sopenharmony_ci#define INPUT_TYPE_OFFSET 0 678c2ecf20Sopenharmony_ci#define INPUT_TYPE_NONTOUCH (0 << INPUT_TYPE_OFFSET) 688c2ecf20Sopenharmony_ci#define INPUT_TYPE_SINGLE (1 << INPUT_TYPE_OFFSET) 698c2ecf20Sopenharmony_ci#define INPUT_TYPE_DUAL (2 << INPUT_TYPE_OFFSET) 708c2ecf20Sopenharmony_ci#define INPUT_TYPE_PALM (3 << INPUT_TYPE_OFFSET) 718c2ecf20Sopenharmony_ci#define INPUT_TYPE_PROXIMITY (7 << INPUT_TYPE_OFFSET) 728c2ecf20Sopenharmony_ci#define GESTURE_CODE_OFFSET 3 738c2ecf20Sopenharmony_ci#define GESTURE_CODE_NO (0 << GESTURE_CODE_OFFSET) 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define MCS5000_TS_X_POS_UPPER 0x11 768c2ecf20Sopenharmony_ci#define MCS5000_TS_X_POS_LOWER 0x12 778c2ecf20Sopenharmony_ci#define MCS5000_TS_Y_POS_UPPER 0x13 788c2ecf20Sopenharmony_ci#define MCS5000_TS_Y_POS_LOWER 0x14 798c2ecf20Sopenharmony_ci#define MCS5000_TS_Z_POS 0x15 808c2ecf20Sopenharmony_ci#define MCS5000_TS_WIDTH 0x16 818c2ecf20Sopenharmony_ci#define MCS5000_TS_GESTURE_VAL 0x17 828c2ecf20Sopenharmony_ci#define MCS5000_TS_MODULE_REV 0x20 838c2ecf20Sopenharmony_ci#define MCS5000_TS_FIRMWARE_VER 0x21 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* Touchscreen absolute values */ 868c2ecf20Sopenharmony_ci#define MCS5000_MAX_XC 0x3ff 878c2ecf20Sopenharmony_ci#define MCS5000_MAX_YC 0x3ff 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cienum mcs5000_ts_read_offset { 908c2ecf20Sopenharmony_ci READ_INPUT_INFO, 918c2ecf20Sopenharmony_ci READ_X_POS_UPPER, 928c2ecf20Sopenharmony_ci READ_X_POS_LOWER, 938c2ecf20Sopenharmony_ci READ_Y_POS_UPPER, 948c2ecf20Sopenharmony_ci READ_Y_POS_LOWER, 958c2ecf20Sopenharmony_ci READ_BLOCK_SIZE, 968c2ecf20Sopenharmony_ci}; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* Each client has this additional data */ 998c2ecf20Sopenharmony_cistruct mcs5000_ts_data { 1008c2ecf20Sopenharmony_ci struct i2c_client *client; 1018c2ecf20Sopenharmony_ci struct input_dev *input_dev; 1028c2ecf20Sopenharmony_ci const struct mcs_platform_data *platform_data; 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct mcs5000_ts_data *data = dev_id; 1088c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 1098c2ecf20Sopenharmony_ci u8 buffer[READ_BLOCK_SIZE]; 1108c2ecf20Sopenharmony_ci int err; 1118c2ecf20Sopenharmony_ci int x; 1128c2ecf20Sopenharmony_ci int y; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci err = i2c_smbus_read_i2c_block_data(client, MCS5000_TS_INPUT_INFO, 1158c2ecf20Sopenharmony_ci READ_BLOCK_SIZE, buffer); 1168c2ecf20Sopenharmony_ci if (err < 0) { 1178c2ecf20Sopenharmony_ci dev_err(&client->dev, "%s, err[%d]\n", __func__, err); 1188c2ecf20Sopenharmony_ci goto out; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci switch (buffer[READ_INPUT_INFO]) { 1228c2ecf20Sopenharmony_ci case INPUT_TYPE_NONTOUCH: 1238c2ecf20Sopenharmony_ci input_report_key(data->input_dev, BTN_TOUCH, 0); 1248c2ecf20Sopenharmony_ci input_sync(data->input_dev); 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci case INPUT_TYPE_SINGLE: 1288c2ecf20Sopenharmony_ci x = (buffer[READ_X_POS_UPPER] << 8) | buffer[READ_X_POS_LOWER]; 1298c2ecf20Sopenharmony_ci y = (buffer[READ_Y_POS_UPPER] << 8) | buffer[READ_Y_POS_LOWER]; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci input_report_key(data->input_dev, BTN_TOUCH, 1); 1328c2ecf20Sopenharmony_ci input_report_abs(data->input_dev, ABS_X, x); 1338c2ecf20Sopenharmony_ci input_report_abs(data->input_dev, ABS_Y, y); 1348c2ecf20Sopenharmony_ci input_sync(data->input_dev); 1358c2ecf20Sopenharmony_ci break; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci case INPUT_TYPE_DUAL: 1388c2ecf20Sopenharmony_ci /* TODO */ 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci case INPUT_TYPE_PALM: 1428c2ecf20Sopenharmony_ci /* TODO */ 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci case INPUT_TYPE_PROXIMITY: 1468c2ecf20Sopenharmony_ci /* TODO */ 1478c2ecf20Sopenharmony_ci break; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci default: 1508c2ecf20Sopenharmony_ci dev_err(&client->dev, "Unknown ts input type %d\n", 1518c2ecf20Sopenharmony_ci buffer[READ_INPUT_INFO]); 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci out: 1568c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic void mcs5000_ts_phys_init(struct mcs5000_ts_data *data, 1608c2ecf20Sopenharmony_ci const struct mcs_platform_data *platform_data) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* Touch reset & sleep mode */ 1658c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, 1668c2ecf20Sopenharmony_ci RESET_EXT_SOFT | OP_MODE_SLEEP); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* Touch size */ 1698c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_UPPER, 1708c2ecf20Sopenharmony_ci platform_data->x_size >> 8); 1718c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_LOWER, 1728c2ecf20Sopenharmony_ci platform_data->x_size & 0xff); 1738c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_UPPER, 1748c2ecf20Sopenharmony_ci platform_data->y_size >> 8); 1758c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_LOWER, 1768c2ecf20Sopenharmony_ci platform_data->y_size & 0xff); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Touch active mode & 80 report rate */ 1798c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(data->client, MCS5000_TS_OP_MODE, 1808c2ecf20Sopenharmony_ci OP_MODE_ACTIVE | REPORT_RATE_80); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic int mcs5000_ts_probe(struct i2c_client *client, 1848c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci const struct mcs_platform_data *pdata; 1878c2ecf20Sopenharmony_ci struct mcs5000_ts_data *data; 1888c2ecf20Sopenharmony_ci struct input_dev *input_dev; 1898c2ecf20Sopenharmony_ci int error; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&client->dev); 1928c2ecf20Sopenharmony_ci if (!pdata) 1938c2ecf20Sopenharmony_ci return -EINVAL; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); 1968c2ecf20Sopenharmony_ci if (!data) { 1978c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to allocate memory\n"); 1988c2ecf20Sopenharmony_ci return -ENOMEM; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci data->client = client; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci input_dev = devm_input_allocate_device(&client->dev); 2048c2ecf20Sopenharmony_ci if (!input_dev) { 2058c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to allocate input device\n"); 2068c2ecf20Sopenharmony_ci return -ENOMEM; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci input_dev->name = "MELFAS MCS-5000 Touchscreen"; 2108c2ecf20Sopenharmony_ci input_dev->id.bustype = BUS_I2C; 2118c2ecf20Sopenharmony_ci input_dev->dev.parent = &client->dev; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci __set_bit(EV_ABS, input_dev->evbit); 2148c2ecf20Sopenharmony_ci __set_bit(EV_KEY, input_dev->evbit); 2158c2ecf20Sopenharmony_ci __set_bit(BTN_TOUCH, input_dev->keybit); 2168c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, 0, MCS5000_MAX_XC, 0, 0); 2178c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci data->input_dev = input_dev; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (pdata->cfg_pin) 2228c2ecf20Sopenharmony_ci pdata->cfg_pin(); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci error = devm_request_threaded_irq(&client->dev, client->irq, 2258c2ecf20Sopenharmony_ci NULL, mcs5000_ts_interrupt, 2268c2ecf20Sopenharmony_ci IRQF_TRIGGER_LOW | IRQF_ONESHOT, 2278c2ecf20Sopenharmony_ci "mcs5000_ts", data); 2288c2ecf20Sopenharmony_ci if (error) { 2298c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to register interrupt\n"); 2308c2ecf20Sopenharmony_ci return error; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci error = input_register_device(data->input_dev); 2348c2ecf20Sopenharmony_ci if (error) { 2358c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to register input device\n"); 2368c2ecf20Sopenharmony_ci return error; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci mcs5000_ts_phys_init(data, pdata); 2408c2ecf20Sopenharmony_ci i2c_set_clientdata(client, data); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int __maybe_unused mcs5000_ts_suspend(struct device *dev) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* Touch sleep mode */ 2508c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci return 0; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic int __maybe_unused mcs5000_ts_resume(struct device *dev) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 2588c2ecf20Sopenharmony_ci struct mcs5000_ts_data *data = i2c_get_clientdata(client); 2598c2ecf20Sopenharmony_ci const struct mcs_platform_data *pdata = dev_get_platdata(dev); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci mcs5000_ts_phys_init(data, pdata); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic const struct i2c_device_id mcs5000_ts_id[] = { 2698c2ecf20Sopenharmony_ci { "mcs5000_ts", 0 }, 2708c2ecf20Sopenharmony_ci { } 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mcs5000_ts_id); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic struct i2c_driver mcs5000_ts_driver = { 2758c2ecf20Sopenharmony_ci .probe = mcs5000_ts_probe, 2768c2ecf20Sopenharmony_ci .driver = { 2778c2ecf20Sopenharmony_ci .name = "mcs5000_ts", 2788c2ecf20Sopenharmony_ci .pm = &mcs5000_ts_pm, 2798c2ecf20Sopenharmony_ci }, 2808c2ecf20Sopenharmony_ci .id_table = mcs5000_ts_id, 2818c2ecf20Sopenharmony_ci}; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cimodule_i2c_driver(mcs5000_ts_driver); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/* Module information */ 2868c2ecf20Sopenharmony_ciMODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); 2878c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Touchscreen driver for MELFAS MCS-5000 controller"); 2888c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 289