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 
mcs5000_ts_interrupt(int irq,void * dev_id)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 
mcs5000_ts_phys_init(struct mcs5000_ts_data * data,const struct mcs_platform_data * platform_data)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 
mcs5000_ts_probe(struct i2c_client * client)183e0715e3cSUwe Kleine-König static int mcs5000_ts_probe(struct i2c_client *client)
18438e783b3SJoonyoung Shim {
185f5189d07SBeomho Seo 	const struct mcs_platform_data *pdata;
18638e783b3SJoonyoung Shim 	struct mcs5000_ts_data *data;
18738e783b3SJoonyoung Shim 	struct input_dev *input_dev;
188f5189d07SBeomho Seo 	int error;
18938e783b3SJoonyoung Shim 
190f5189d07SBeomho Seo 	pdata = dev_get_platdata(&client->dev);
191f5189d07SBeomho Seo 	if (!pdata)
19238e783b3SJoonyoung Shim 		return -EINVAL;
19338e783b3SJoonyoung Shim 
194f5189d07SBeomho Seo 	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
195f5189d07SBeomho Seo 	if (!data) {
19638e783b3SJoonyoung Shim 		dev_err(&client->dev, "Failed to allocate memory\n");
197f5189d07SBeomho Seo 		return -ENOMEM;
19838e783b3SJoonyoung Shim 	}
19938e783b3SJoonyoung Shim 
20038e783b3SJoonyoung Shim 	data->client = client;
201f5189d07SBeomho Seo 
202f5189d07SBeomho Seo 	input_dev = devm_input_allocate_device(&client->dev);
203f5189d07SBeomho Seo 	if (!input_dev) {
204f5189d07SBeomho Seo 		dev_err(&client->dev, "Failed to allocate input device\n");
205f5189d07SBeomho Seo 		return -ENOMEM;
206f5189d07SBeomho Seo 	}
20738e783b3SJoonyoung Shim 
20821d128a7SBeomho Seo 	input_dev->name = "MELFAS MCS-5000 Touchscreen";
20938e783b3SJoonyoung Shim 	input_dev->id.bustype = BUS_I2C;
21038e783b3SJoonyoung Shim 	input_dev->dev.parent = &client->dev;
21138e783b3SJoonyoung Shim 
21238e783b3SJoonyoung Shim 	__set_bit(EV_ABS, input_dev->evbit);
21338e783b3SJoonyoung Shim 	__set_bit(EV_KEY, input_dev->evbit);
21438e783b3SJoonyoung Shim 	__set_bit(BTN_TOUCH, input_dev->keybit);
21538e783b3SJoonyoung Shim 	input_set_abs_params(input_dev, ABS_X, 0, MCS5000_MAX_XC, 0, 0);
21638e783b3SJoonyoung Shim 	input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0);
21738e783b3SJoonyoung Shim 
218f5189d07SBeomho Seo 	data->input_dev = input_dev;
21938e783b3SJoonyoung Shim 
220f5189d07SBeomho Seo 	if (pdata->cfg_pin)
221f5189d07SBeomho Seo 		pdata->cfg_pin();
22238e783b3SJoonyoung Shim 
223f5189d07SBeomho Seo 	error = devm_request_threaded_irq(&client->dev, client->irq,
224f5189d07SBeomho Seo 					  NULL, mcs5000_ts_interrupt,
225f5189d07SBeomho Seo 					  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
226f5189d07SBeomho Seo 					  "mcs5000_ts", data);
227f5189d07SBeomho Seo 	if (error) {
22838e783b3SJoonyoung Shim 		dev_err(&client->dev, "Failed to register interrupt\n");
229f5189d07SBeomho Seo 		return error;
23038e783b3SJoonyoung Shim 	}
23138e783b3SJoonyoung Shim 
232f5189d07SBeomho Seo 	error = input_register_device(data->input_dev);
233f5189d07SBeomho Seo 	if (error) {
234f5189d07SBeomho Seo 		dev_err(&client->dev, "Failed to register input device\n");
235f5189d07SBeomho Seo 		return error;
236f5189d07SBeomho Seo 	}
23738e783b3SJoonyoung Shim 
238f5189d07SBeomho Seo 	mcs5000_ts_phys_init(data, pdata);
23938e783b3SJoonyoung Shim 	i2c_set_clientdata(client, data);
24038e783b3SJoonyoung Shim 
24138e783b3SJoonyoung Shim 	return 0;
24238e783b3SJoonyoung Shim }
24338e783b3SJoonyoung Shim 
mcs5000_ts_suspend(struct device * dev)24470f36d0cSJonathan Cameron static int mcs5000_ts_suspend(struct device *dev)
24538e783b3SJoonyoung Shim {
24692b672e2SMark Brown 	struct i2c_client *client = to_i2c_client(dev);
24792b672e2SMark Brown 
24838e783b3SJoonyoung Shim 	/* Touch sleep mode */
24938e783b3SJoonyoung Shim 	i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP);
25038e783b3SJoonyoung Shim 
25138e783b3SJoonyoung Shim 	return 0;
25238e783b3SJoonyoung Shim }
25338e783b3SJoonyoung Shim 
mcs5000_ts_resume(struct device * dev)25470f36d0cSJonathan Cameron static int mcs5000_ts_resume(struct device *dev)
25538e783b3SJoonyoung Shim {
25692b672e2SMark Brown 	struct i2c_client *client = to_i2c_client(dev);
25738e783b3SJoonyoung Shim 	struct mcs5000_ts_data *data = i2c_get_clientdata(client);
258f5189d07SBeomho Seo 	const struct mcs_platform_data *pdata = dev_get_platdata(dev);
25938e783b3SJoonyoung Shim 
260f5189d07SBeomho Seo 	mcs5000_ts_phys_init(data, pdata);
26138e783b3SJoonyoung Shim 
26238e783b3SJoonyoung Shim 	return 0;
26338e783b3SJoonyoung Shim }
26492b672e2SMark Brown 
26570f36d0cSJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(mcs5000_ts_pm,
26670f36d0cSJonathan Cameron 				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 = {
275*d8bde56dSUwe Kleine-König 	.probe		= mcs5000_ts_probe,
27638e783b3SJoonyoung Shim 	.driver = {
27738e783b3SJoonyoung Shim 		.name = "mcs5000_ts",
27870f36d0cSJonathan Cameron 		.pm   = pm_sleep_ptr(&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