12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 238e783b3SJoonyoung Shim /* 338e783b3SJoonyoung Shim * mcs5000_ts.c - Touchscreen driver for MELFAS MCS-5000 controller 438e783b3SJoonyoung Shim * 538e783b3SJoonyoung Shim * Copyright (C) 2009 Samsung Electronics Co.Ltd 638e783b3SJoonyoung Shim * Author: Joonyoung Shim <jy0922.shim@samsung.com> 738e783b3SJoonyoung Shim * 838e783b3SJoonyoung Shim * Based on wm97xx-core.c 938e783b3SJoonyoung Shim */ 1038e783b3SJoonyoung Shim 1138e783b3SJoonyoung Shim #include <linux/module.h> 1238e783b3SJoonyoung Shim #include <linux/i2c.h> 1338e783b3SJoonyoung Shim #include <linux/interrupt.h> 1438e783b3SJoonyoung Shim #include <linux/input.h> 1538e783b3SJoonyoung Shim #include <linux/irq.h> 168cd9ab9eSWolfram Sang #include <linux/platform_data/mcs.h> 175a0e3ad6STejun Heo #include <linux/slab.h> 1838e783b3SJoonyoung Shim 1938e783b3SJoonyoung Shim /* Registers */ 2038e783b3SJoonyoung Shim #define MCS5000_TS_STATUS 0x00 2138e783b3SJoonyoung Shim #define STATUS_OFFSET 0 2238e783b3SJoonyoung Shim #define STATUS_NO (0 << STATUS_OFFSET) 2338e783b3SJoonyoung Shim #define STATUS_INIT (1 << STATUS_OFFSET) 2438e783b3SJoonyoung Shim #define STATUS_SENSING (2 << STATUS_OFFSET) 2538e783b3SJoonyoung Shim #define STATUS_COORD (3 << STATUS_OFFSET) 2638e783b3SJoonyoung Shim #define STATUS_GESTURE (4 << STATUS_OFFSET) 2738e783b3SJoonyoung Shim #define ERROR_OFFSET 4 2838e783b3SJoonyoung Shim #define ERROR_NO (0 << ERROR_OFFSET) 2938e783b3SJoonyoung Shim #define ERROR_POWER_ON_RESET (1 << ERROR_OFFSET) 3038e783b3SJoonyoung Shim #define ERROR_INT_RESET (2 << ERROR_OFFSET) 3138e783b3SJoonyoung Shim #define ERROR_EXT_RESET (3 << ERROR_OFFSET) 3238e783b3SJoonyoung Shim #define ERROR_INVALID_REG_ADDRESS (8 << ERROR_OFFSET) 3338e783b3SJoonyoung Shim #define ERROR_INVALID_REG_VALUE (9 << ERROR_OFFSET) 3438e783b3SJoonyoung Shim 3538e783b3SJoonyoung Shim #define MCS5000_TS_OP_MODE 0x01 3638e783b3SJoonyoung Shim #define RESET_OFFSET 0 3738e783b3SJoonyoung Shim #define RESET_NO (0 << RESET_OFFSET) 3838e783b3SJoonyoung Shim #define RESET_EXT_SOFT (1 << RESET_OFFSET) 3938e783b3SJoonyoung Shim #define OP_MODE_OFFSET 1 4038e783b3SJoonyoung Shim #define OP_MODE_SLEEP (0 << OP_MODE_OFFSET) 4138e783b3SJoonyoung Shim #define OP_MODE_ACTIVE (1 << OP_MODE_OFFSET) 4238e783b3SJoonyoung Shim #define GESTURE_OFFSET 4 4338e783b3SJoonyoung Shim #define GESTURE_DISABLE (0 << GESTURE_OFFSET) 4438e783b3SJoonyoung Shim #define GESTURE_ENABLE (1 << GESTURE_OFFSET) 4538e783b3SJoonyoung Shim #define PROXIMITY_OFFSET 5 4638e783b3SJoonyoung Shim #define PROXIMITY_DISABLE (0 << PROXIMITY_OFFSET) 4738e783b3SJoonyoung Shim #define PROXIMITY_ENABLE (1 << PROXIMITY_OFFSET) 4838e783b3SJoonyoung Shim #define SCAN_MODE_OFFSET 6 4938e783b3SJoonyoung Shim #define SCAN_MODE_INTERRUPT (0 << SCAN_MODE_OFFSET) 5038e783b3SJoonyoung Shim #define SCAN_MODE_POLLING (1 << SCAN_MODE_OFFSET) 5138e783b3SJoonyoung Shim #define REPORT_RATE_OFFSET 7 5238e783b3SJoonyoung Shim #define REPORT_RATE_40 (0 << REPORT_RATE_OFFSET) 5338e783b3SJoonyoung Shim #define REPORT_RATE_80 (1 << REPORT_RATE_OFFSET) 5438e783b3SJoonyoung Shim 5538e783b3SJoonyoung Shim #define MCS5000_TS_SENS_CTL 0x02 5638e783b3SJoonyoung Shim #define MCS5000_TS_FILTER_CTL 0x03 5738e783b3SJoonyoung Shim #define PRI_FILTER_OFFSET 0 5838e783b3SJoonyoung Shim #define SEC_FILTER_OFFSET 4 5938e783b3SJoonyoung Shim 6038e783b3SJoonyoung Shim #define MCS5000_TS_X_SIZE_UPPER 0x08 6138e783b3SJoonyoung Shim #define MCS5000_TS_X_SIZE_LOWER 0x09 6238e783b3SJoonyoung Shim #define MCS5000_TS_Y_SIZE_UPPER 0x0A 6338e783b3SJoonyoung Shim #define MCS5000_TS_Y_SIZE_LOWER 0x0B 6438e783b3SJoonyoung Shim 6538e783b3SJoonyoung Shim #define MCS5000_TS_INPUT_INFO 0x10 6638e783b3SJoonyoung Shim #define INPUT_TYPE_OFFSET 0 6738e783b3SJoonyoung Shim #define INPUT_TYPE_NONTOUCH (0 << INPUT_TYPE_OFFSET) 6838e783b3SJoonyoung Shim #define INPUT_TYPE_SINGLE (1 << INPUT_TYPE_OFFSET) 6938e783b3SJoonyoung Shim #define INPUT_TYPE_DUAL (2 << INPUT_TYPE_OFFSET) 7038e783b3SJoonyoung Shim #define INPUT_TYPE_PALM (3 << INPUT_TYPE_OFFSET) 7138e783b3SJoonyoung Shim #define INPUT_TYPE_PROXIMITY (7 << INPUT_TYPE_OFFSET) 7238e783b3SJoonyoung Shim #define GESTURE_CODE_OFFSET 3 7338e783b3SJoonyoung Shim #define GESTURE_CODE_NO (0 << GESTURE_CODE_OFFSET) 7438e783b3SJoonyoung Shim 7538e783b3SJoonyoung Shim #define MCS5000_TS_X_POS_UPPER 0x11 7638e783b3SJoonyoung Shim #define MCS5000_TS_X_POS_LOWER 0x12 7738e783b3SJoonyoung Shim #define MCS5000_TS_Y_POS_UPPER 0x13 7838e783b3SJoonyoung Shim #define MCS5000_TS_Y_POS_LOWER 0x14 7938e783b3SJoonyoung Shim #define MCS5000_TS_Z_POS 0x15 8038e783b3SJoonyoung Shim #define MCS5000_TS_WIDTH 0x16 8138e783b3SJoonyoung Shim #define MCS5000_TS_GESTURE_VAL 0x17 8238e783b3SJoonyoung Shim #define MCS5000_TS_MODULE_REV 0x20 8338e783b3SJoonyoung Shim #define MCS5000_TS_FIRMWARE_VER 0x21 8438e783b3SJoonyoung Shim 8538e783b3SJoonyoung Shim /* Touchscreen absolute values */ 8638e783b3SJoonyoung Shim #define MCS5000_MAX_XC 0x3ff 8738e783b3SJoonyoung Shim #define MCS5000_MAX_YC 0x3ff 8838e783b3SJoonyoung Shim 8938e783b3SJoonyoung Shim enum mcs5000_ts_read_offset { 9038e783b3SJoonyoung Shim READ_INPUT_INFO, 9138e783b3SJoonyoung Shim READ_X_POS_UPPER, 9238e783b3SJoonyoung Shim READ_X_POS_LOWER, 9338e783b3SJoonyoung Shim READ_Y_POS_UPPER, 9438e783b3SJoonyoung Shim READ_Y_POS_LOWER, 9538e783b3SJoonyoung Shim READ_BLOCK_SIZE, 9638e783b3SJoonyoung Shim }; 9738e783b3SJoonyoung Shim 9838e783b3SJoonyoung Shim /* Each client has this additional data */ 9938e783b3SJoonyoung Shim struct mcs5000_ts_data { 10038e783b3SJoonyoung Shim struct i2c_client *client; 10138e783b3SJoonyoung Shim struct input_dev *input_dev; 102312e8e8aSJoonyoung Shim const struct mcs_platform_data *platform_data; 10338e783b3SJoonyoung Shim }; 10438e783b3SJoonyoung Shim 10538e783b3SJoonyoung Shim static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id) 10638e783b3SJoonyoung Shim { 10738e783b3SJoonyoung Shim struct mcs5000_ts_data *data = dev_id; 10838e783b3SJoonyoung Shim struct i2c_client *client = data->client; 10938e783b3SJoonyoung Shim u8 buffer[READ_BLOCK_SIZE]; 11038e783b3SJoonyoung Shim int err; 11138e783b3SJoonyoung Shim int x; 11238e783b3SJoonyoung Shim int y; 11338e783b3SJoonyoung Shim 11438e783b3SJoonyoung Shim err = i2c_smbus_read_i2c_block_data(client, MCS5000_TS_INPUT_INFO, 11538e783b3SJoonyoung Shim READ_BLOCK_SIZE, buffer); 11638e783b3SJoonyoung Shim if (err < 0) { 11738e783b3SJoonyoung Shim dev_err(&client->dev, "%s, err[%d]\n", __func__, err); 11838e783b3SJoonyoung Shim goto out; 11938e783b3SJoonyoung Shim } 12038e783b3SJoonyoung Shim 12138e783b3SJoonyoung Shim switch (buffer[READ_INPUT_INFO]) { 12238e783b3SJoonyoung Shim case INPUT_TYPE_NONTOUCH: 12338e783b3SJoonyoung Shim input_report_key(data->input_dev, BTN_TOUCH, 0); 12438e783b3SJoonyoung Shim input_sync(data->input_dev); 12538e783b3SJoonyoung Shim break; 12638e783b3SJoonyoung Shim 12738e783b3SJoonyoung Shim case INPUT_TYPE_SINGLE: 12838e783b3SJoonyoung Shim x = (buffer[READ_X_POS_UPPER] << 8) | buffer[READ_X_POS_LOWER]; 12938e783b3SJoonyoung Shim y = (buffer[READ_Y_POS_UPPER] << 8) | buffer[READ_Y_POS_LOWER]; 13038e783b3SJoonyoung Shim 13138e783b3SJoonyoung Shim input_report_key(data->input_dev, BTN_TOUCH, 1); 13238e783b3SJoonyoung Shim input_report_abs(data->input_dev, ABS_X, x); 13338e783b3SJoonyoung Shim input_report_abs(data->input_dev, ABS_Y, y); 13438e783b3SJoonyoung Shim input_sync(data->input_dev); 13538e783b3SJoonyoung Shim break; 13638e783b3SJoonyoung Shim 13738e783b3SJoonyoung Shim case INPUT_TYPE_DUAL: 13838e783b3SJoonyoung Shim /* TODO */ 13938e783b3SJoonyoung Shim break; 14038e783b3SJoonyoung Shim 14138e783b3SJoonyoung Shim case INPUT_TYPE_PALM: 14238e783b3SJoonyoung Shim /* TODO */ 14338e783b3SJoonyoung Shim break; 14438e783b3SJoonyoung Shim 14538e783b3SJoonyoung Shim case INPUT_TYPE_PROXIMITY: 14638e783b3SJoonyoung Shim /* TODO */ 14738e783b3SJoonyoung Shim break; 14838e783b3SJoonyoung Shim 14938e783b3SJoonyoung Shim default: 15038e783b3SJoonyoung Shim dev_err(&client->dev, "Unknown ts input type %d\n", 15138e783b3SJoonyoung Shim buffer[READ_INPUT_INFO]); 15238e783b3SJoonyoung Shim break; 15338e783b3SJoonyoung Shim } 15438e783b3SJoonyoung Shim 15538e783b3SJoonyoung Shim out: 15638e783b3SJoonyoung Shim return IRQ_HANDLED; 15738e783b3SJoonyoung Shim } 15838e783b3SJoonyoung Shim 159f5189d07SBeomho Seo static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data, 160f5189d07SBeomho Seo const struct mcs_platform_data *platform_data) 16138e783b3SJoonyoung Shim { 16238e783b3SJoonyoung Shim struct i2c_client *client = data->client; 16338e783b3SJoonyoung Shim 16438e783b3SJoonyoung Shim /* Touch reset & sleep mode */ 16538e783b3SJoonyoung Shim i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, 16638e783b3SJoonyoung Shim RESET_EXT_SOFT | OP_MODE_SLEEP); 16738e783b3SJoonyoung Shim 16838e783b3SJoonyoung Shim /* Touch size */ 16938e783b3SJoonyoung Shim i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_UPPER, 17038e783b3SJoonyoung Shim platform_data->x_size >> 8); 17138e783b3SJoonyoung Shim i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_LOWER, 17238e783b3SJoonyoung Shim platform_data->x_size & 0xff); 17338e783b3SJoonyoung Shim i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_UPPER, 17438e783b3SJoonyoung Shim platform_data->y_size >> 8); 17538e783b3SJoonyoung Shim i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_LOWER, 17638e783b3SJoonyoung Shim platform_data->y_size & 0xff); 17738e783b3SJoonyoung Shim 17838e783b3SJoonyoung Shim /* Touch active mode & 80 report rate */ 17938e783b3SJoonyoung Shim i2c_smbus_write_byte_data(data->client, MCS5000_TS_OP_MODE, 18038e783b3SJoonyoung Shim OP_MODE_ACTIVE | REPORT_RATE_80); 18138e783b3SJoonyoung Shim } 18238e783b3SJoonyoung Shim 1835298cc4cSBill Pemberton static int mcs5000_ts_probe(struct i2c_client *client, 18438e783b3SJoonyoung Shim const struct i2c_device_id *id) 18538e783b3SJoonyoung Shim { 186f5189d07SBeomho Seo const struct mcs_platform_data *pdata; 18738e783b3SJoonyoung Shim struct mcs5000_ts_data *data; 18838e783b3SJoonyoung Shim struct input_dev *input_dev; 189f5189d07SBeomho Seo int error; 19038e783b3SJoonyoung Shim 191f5189d07SBeomho Seo pdata = dev_get_platdata(&client->dev); 192f5189d07SBeomho Seo if (!pdata) 19338e783b3SJoonyoung Shim return -EINVAL; 19438e783b3SJoonyoung Shim 195f5189d07SBeomho Seo data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); 196f5189d07SBeomho Seo if (!data) { 19738e783b3SJoonyoung Shim dev_err(&client->dev, "Failed to allocate memory\n"); 198f5189d07SBeomho Seo return -ENOMEM; 19938e783b3SJoonyoung Shim } 20038e783b3SJoonyoung Shim 20138e783b3SJoonyoung Shim data->client = client; 202f5189d07SBeomho Seo 203f5189d07SBeomho Seo input_dev = devm_input_allocate_device(&client->dev); 204f5189d07SBeomho Seo if (!input_dev) { 205f5189d07SBeomho Seo dev_err(&client->dev, "Failed to allocate input device\n"); 206f5189d07SBeomho Seo return -ENOMEM; 207f5189d07SBeomho Seo } 20838e783b3SJoonyoung Shim 20921d128a7SBeomho Seo input_dev->name = "MELFAS MCS-5000 Touchscreen"; 21038e783b3SJoonyoung Shim input_dev->id.bustype = BUS_I2C; 21138e783b3SJoonyoung Shim input_dev->dev.parent = &client->dev; 21238e783b3SJoonyoung Shim 21338e783b3SJoonyoung Shim __set_bit(EV_ABS, input_dev->evbit); 21438e783b3SJoonyoung Shim __set_bit(EV_KEY, input_dev->evbit); 21538e783b3SJoonyoung Shim __set_bit(BTN_TOUCH, input_dev->keybit); 21638e783b3SJoonyoung Shim input_set_abs_params(input_dev, ABS_X, 0, MCS5000_MAX_XC, 0, 0); 21738e783b3SJoonyoung Shim input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0); 21838e783b3SJoonyoung Shim 219f5189d07SBeomho Seo data->input_dev = input_dev; 22038e783b3SJoonyoung Shim 221f5189d07SBeomho Seo if (pdata->cfg_pin) 222f5189d07SBeomho Seo pdata->cfg_pin(); 22338e783b3SJoonyoung Shim 224f5189d07SBeomho Seo error = devm_request_threaded_irq(&client->dev, client->irq, 225f5189d07SBeomho Seo NULL, mcs5000_ts_interrupt, 226f5189d07SBeomho Seo IRQF_TRIGGER_LOW | IRQF_ONESHOT, 227f5189d07SBeomho Seo "mcs5000_ts", data); 228f5189d07SBeomho Seo if (error) { 22938e783b3SJoonyoung Shim dev_err(&client->dev, "Failed to register interrupt\n"); 230f5189d07SBeomho Seo return error; 23138e783b3SJoonyoung Shim } 23238e783b3SJoonyoung Shim 233f5189d07SBeomho Seo error = input_register_device(data->input_dev); 234f5189d07SBeomho Seo if (error) { 235f5189d07SBeomho Seo dev_err(&client->dev, "Failed to register input device\n"); 236f5189d07SBeomho Seo return error; 237f5189d07SBeomho Seo } 23838e783b3SJoonyoung Shim 239f5189d07SBeomho Seo mcs5000_ts_phys_init(data, pdata); 24038e783b3SJoonyoung Shim i2c_set_clientdata(client, data); 24138e783b3SJoonyoung Shim 24238e783b3SJoonyoung Shim return 0; 24338e783b3SJoonyoung Shim } 24438e783b3SJoonyoung Shim 2459634c152SFabio Estevam static int __maybe_unused mcs5000_ts_suspend(struct device *dev) 24638e783b3SJoonyoung Shim { 24792b672e2SMark Brown struct i2c_client *client = to_i2c_client(dev); 24892b672e2SMark Brown 24938e783b3SJoonyoung Shim /* Touch sleep mode */ 25038e783b3SJoonyoung Shim i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP); 25138e783b3SJoonyoung Shim 25238e783b3SJoonyoung Shim return 0; 25338e783b3SJoonyoung Shim } 25438e783b3SJoonyoung Shim 2559634c152SFabio Estevam static int __maybe_unused mcs5000_ts_resume(struct device *dev) 25638e783b3SJoonyoung Shim { 25792b672e2SMark Brown struct i2c_client *client = to_i2c_client(dev); 25838e783b3SJoonyoung Shim struct mcs5000_ts_data *data = i2c_get_clientdata(client); 259f5189d07SBeomho Seo const struct mcs_platform_data *pdata = dev_get_platdata(dev); 26038e783b3SJoonyoung Shim 261f5189d07SBeomho Seo mcs5000_ts_phys_init(data, pdata); 26238e783b3SJoonyoung Shim 26338e783b3SJoonyoung Shim return 0; 26438e783b3SJoonyoung Shim } 26592b672e2SMark Brown 26692b672e2SMark Brown static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume); 26738e783b3SJoonyoung Shim 26838e783b3SJoonyoung Shim static const struct i2c_device_id mcs5000_ts_id[] = { 26938e783b3SJoonyoung Shim { "mcs5000_ts", 0 }, 27038e783b3SJoonyoung Shim { } 27138e783b3SJoonyoung Shim }; 27238e783b3SJoonyoung Shim MODULE_DEVICE_TABLE(i2c, mcs5000_ts_id); 27338e783b3SJoonyoung Shim 27438e783b3SJoonyoung Shim static struct i2c_driver mcs5000_ts_driver = { 27538e783b3SJoonyoung Shim .probe = mcs5000_ts_probe, 27638e783b3SJoonyoung Shim .driver = { 27738e783b3SJoonyoung Shim .name = "mcs5000_ts", 27892b672e2SMark Brown .pm = &mcs5000_ts_pm, 27938e783b3SJoonyoung Shim }, 28038e783b3SJoonyoung Shim .id_table = mcs5000_ts_id, 28138e783b3SJoonyoung Shim }; 28238e783b3SJoonyoung Shim 2831b92c1cfSAxel Lin module_i2c_driver(mcs5000_ts_driver); 28438e783b3SJoonyoung Shim 28538e783b3SJoonyoung Shim /* Module information */ 28638e783b3SJoonyoung Shim MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); 28738e783b3SJoonyoung Shim MODULE_DESCRIPTION("Touchscreen driver for MELFAS MCS-5000 controller"); 28838e783b3SJoonyoung Shim MODULE_LICENSE("GPL"); 289