138e783b3SJoonyoung Shim /*
238e783b3SJoonyoung Shim  * mcs5000_ts.c - Touchscreen driver for MELFAS MCS-5000 controller
338e783b3SJoonyoung Shim  *
438e783b3SJoonyoung Shim  * Copyright (C) 2009 Samsung Electronics Co.Ltd
538e783b3SJoonyoung Shim  * Author: Joonyoung Shim <jy0922.shim@samsung.com>
638e783b3SJoonyoung Shim  *
738e783b3SJoonyoung Shim  * Based on wm97xx-core.c
838e783b3SJoonyoung Shim  *
938e783b3SJoonyoung Shim  *  This program is free software; you can redistribute  it and/or modify it
1038e783b3SJoonyoung Shim  *  under  the terms of  the GNU General  Public License as published by the
1138e783b3SJoonyoung Shim  *  Free Software Foundation;  either version 2 of the  License, or (at your
1238e783b3SJoonyoung Shim  *  option) any later version.
1338e783b3SJoonyoung Shim  *
1438e783b3SJoonyoung Shim  */
1538e783b3SJoonyoung Shim 
1638e783b3SJoonyoung Shim #include <linux/module.h>
1738e783b3SJoonyoung Shim #include <linux/i2c.h>
18312e8e8aSJoonyoung Shim #include <linux/i2c/mcs.h>
1938e783b3SJoonyoung Shim #include <linux/interrupt.h>
2038e783b3SJoonyoung Shim #include <linux/input.h>
2138e783b3SJoonyoung Shim #include <linux/irq.h>
225a0e3ad6STejun Heo #include <linux/slab.h>
2338e783b3SJoonyoung Shim 
2438e783b3SJoonyoung Shim /* Registers */
2538e783b3SJoonyoung Shim #define MCS5000_TS_STATUS		0x00
2638e783b3SJoonyoung Shim #define STATUS_OFFSET			0
2738e783b3SJoonyoung Shim #define STATUS_NO			(0 << STATUS_OFFSET)
2838e783b3SJoonyoung Shim #define STATUS_INIT			(1 << STATUS_OFFSET)
2938e783b3SJoonyoung Shim #define STATUS_SENSING			(2 << STATUS_OFFSET)
3038e783b3SJoonyoung Shim #define STATUS_COORD			(3 << STATUS_OFFSET)
3138e783b3SJoonyoung Shim #define STATUS_GESTURE			(4 << STATUS_OFFSET)
3238e783b3SJoonyoung Shim #define ERROR_OFFSET			4
3338e783b3SJoonyoung Shim #define ERROR_NO			(0 << ERROR_OFFSET)
3438e783b3SJoonyoung Shim #define ERROR_POWER_ON_RESET		(1 << ERROR_OFFSET)
3538e783b3SJoonyoung Shim #define ERROR_INT_RESET			(2 << ERROR_OFFSET)
3638e783b3SJoonyoung Shim #define ERROR_EXT_RESET			(3 << ERROR_OFFSET)
3738e783b3SJoonyoung Shim #define ERROR_INVALID_REG_ADDRESS	(8 << ERROR_OFFSET)
3838e783b3SJoonyoung Shim #define ERROR_INVALID_REG_VALUE		(9 << ERROR_OFFSET)
3938e783b3SJoonyoung Shim 
4038e783b3SJoonyoung Shim #define MCS5000_TS_OP_MODE		0x01
4138e783b3SJoonyoung Shim #define RESET_OFFSET			0
4238e783b3SJoonyoung Shim #define RESET_NO			(0 << RESET_OFFSET)
4338e783b3SJoonyoung Shim #define RESET_EXT_SOFT			(1 << RESET_OFFSET)
4438e783b3SJoonyoung Shim #define OP_MODE_OFFSET			1
4538e783b3SJoonyoung Shim #define OP_MODE_SLEEP			(0 << OP_MODE_OFFSET)
4638e783b3SJoonyoung Shim #define OP_MODE_ACTIVE			(1 << OP_MODE_OFFSET)
4738e783b3SJoonyoung Shim #define GESTURE_OFFSET			4
4838e783b3SJoonyoung Shim #define GESTURE_DISABLE			(0 << GESTURE_OFFSET)
4938e783b3SJoonyoung Shim #define GESTURE_ENABLE			(1 << GESTURE_OFFSET)
5038e783b3SJoonyoung Shim #define PROXIMITY_OFFSET		5
5138e783b3SJoonyoung Shim #define PROXIMITY_DISABLE		(0 << PROXIMITY_OFFSET)
5238e783b3SJoonyoung Shim #define PROXIMITY_ENABLE		(1 << PROXIMITY_OFFSET)
5338e783b3SJoonyoung Shim #define SCAN_MODE_OFFSET		6
5438e783b3SJoonyoung Shim #define SCAN_MODE_INTERRUPT		(0 << SCAN_MODE_OFFSET)
5538e783b3SJoonyoung Shim #define SCAN_MODE_POLLING		(1 << SCAN_MODE_OFFSET)
5638e783b3SJoonyoung Shim #define REPORT_RATE_OFFSET		7
5738e783b3SJoonyoung Shim #define REPORT_RATE_40			(0 << REPORT_RATE_OFFSET)
5838e783b3SJoonyoung Shim #define REPORT_RATE_80			(1 << REPORT_RATE_OFFSET)
5938e783b3SJoonyoung Shim 
6038e783b3SJoonyoung Shim #define MCS5000_TS_SENS_CTL		0x02
6138e783b3SJoonyoung Shim #define MCS5000_TS_FILTER_CTL		0x03
6238e783b3SJoonyoung Shim #define PRI_FILTER_OFFSET		0
6338e783b3SJoonyoung Shim #define SEC_FILTER_OFFSET		4
6438e783b3SJoonyoung Shim 
6538e783b3SJoonyoung Shim #define MCS5000_TS_X_SIZE_UPPER		0x08
6638e783b3SJoonyoung Shim #define MCS5000_TS_X_SIZE_LOWER		0x09
6738e783b3SJoonyoung Shim #define MCS5000_TS_Y_SIZE_UPPER		0x0A
6838e783b3SJoonyoung Shim #define MCS5000_TS_Y_SIZE_LOWER		0x0B
6938e783b3SJoonyoung Shim 
7038e783b3SJoonyoung Shim #define MCS5000_TS_INPUT_INFO		0x10
7138e783b3SJoonyoung Shim #define INPUT_TYPE_OFFSET		0
7238e783b3SJoonyoung Shim #define INPUT_TYPE_NONTOUCH		(0 << INPUT_TYPE_OFFSET)
7338e783b3SJoonyoung Shim #define INPUT_TYPE_SINGLE		(1 << INPUT_TYPE_OFFSET)
7438e783b3SJoonyoung Shim #define INPUT_TYPE_DUAL			(2 << INPUT_TYPE_OFFSET)
7538e783b3SJoonyoung Shim #define INPUT_TYPE_PALM			(3 << INPUT_TYPE_OFFSET)
7638e783b3SJoonyoung Shim #define INPUT_TYPE_PROXIMITY		(7 << INPUT_TYPE_OFFSET)
7738e783b3SJoonyoung Shim #define GESTURE_CODE_OFFSET		3
7838e783b3SJoonyoung Shim #define GESTURE_CODE_NO			(0 << GESTURE_CODE_OFFSET)
7938e783b3SJoonyoung Shim 
8038e783b3SJoonyoung Shim #define MCS5000_TS_X_POS_UPPER		0x11
8138e783b3SJoonyoung Shim #define MCS5000_TS_X_POS_LOWER		0x12
8238e783b3SJoonyoung Shim #define MCS5000_TS_Y_POS_UPPER		0x13
8338e783b3SJoonyoung Shim #define MCS5000_TS_Y_POS_LOWER		0x14
8438e783b3SJoonyoung Shim #define MCS5000_TS_Z_POS		0x15
8538e783b3SJoonyoung Shim #define MCS5000_TS_WIDTH		0x16
8638e783b3SJoonyoung Shim #define MCS5000_TS_GESTURE_VAL		0x17
8738e783b3SJoonyoung Shim #define MCS5000_TS_MODULE_REV		0x20
8838e783b3SJoonyoung Shim #define MCS5000_TS_FIRMWARE_VER		0x21
8938e783b3SJoonyoung Shim 
9038e783b3SJoonyoung Shim /* Touchscreen absolute values */
9138e783b3SJoonyoung Shim #define MCS5000_MAX_XC			0x3ff
9238e783b3SJoonyoung Shim #define MCS5000_MAX_YC			0x3ff
9338e783b3SJoonyoung Shim 
9438e783b3SJoonyoung Shim enum mcs5000_ts_read_offset {
9538e783b3SJoonyoung Shim 	READ_INPUT_INFO,
9638e783b3SJoonyoung Shim 	READ_X_POS_UPPER,
9738e783b3SJoonyoung Shim 	READ_X_POS_LOWER,
9838e783b3SJoonyoung Shim 	READ_Y_POS_UPPER,
9938e783b3SJoonyoung Shim 	READ_Y_POS_LOWER,
10038e783b3SJoonyoung Shim 	READ_BLOCK_SIZE,
10138e783b3SJoonyoung Shim };
10238e783b3SJoonyoung Shim 
10338e783b3SJoonyoung Shim /* Each client has this additional data */
10438e783b3SJoonyoung Shim struct mcs5000_ts_data {
10538e783b3SJoonyoung Shim 	struct i2c_client *client;
10638e783b3SJoonyoung Shim 	struct input_dev *input_dev;
107312e8e8aSJoonyoung Shim 	const struct mcs_platform_data *platform_data;
10838e783b3SJoonyoung Shim };
10938e783b3SJoonyoung Shim 
11038e783b3SJoonyoung Shim static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id)
11138e783b3SJoonyoung Shim {
11238e783b3SJoonyoung Shim 	struct mcs5000_ts_data *data = dev_id;
11338e783b3SJoonyoung Shim 	struct i2c_client *client = data->client;
11438e783b3SJoonyoung Shim 	u8 buffer[READ_BLOCK_SIZE];
11538e783b3SJoonyoung Shim 	int err;
11638e783b3SJoonyoung Shim 	int x;
11738e783b3SJoonyoung Shim 	int y;
11838e783b3SJoonyoung Shim 
11938e783b3SJoonyoung Shim 	err = i2c_smbus_read_i2c_block_data(client, MCS5000_TS_INPUT_INFO,
12038e783b3SJoonyoung Shim 			READ_BLOCK_SIZE, buffer);
12138e783b3SJoonyoung Shim 	if (err < 0) {
12238e783b3SJoonyoung Shim 		dev_err(&client->dev, "%s, err[%d]\n", __func__, err);
12338e783b3SJoonyoung Shim 		goto out;
12438e783b3SJoonyoung Shim 	}
12538e783b3SJoonyoung Shim 
12638e783b3SJoonyoung Shim 	switch (buffer[READ_INPUT_INFO]) {
12738e783b3SJoonyoung Shim 	case INPUT_TYPE_NONTOUCH:
12838e783b3SJoonyoung Shim 		input_report_key(data->input_dev, BTN_TOUCH, 0);
12938e783b3SJoonyoung Shim 		input_sync(data->input_dev);
13038e783b3SJoonyoung Shim 		break;
13138e783b3SJoonyoung Shim 
13238e783b3SJoonyoung Shim 	case INPUT_TYPE_SINGLE:
13338e783b3SJoonyoung Shim 		x = (buffer[READ_X_POS_UPPER] << 8) | buffer[READ_X_POS_LOWER];
13438e783b3SJoonyoung Shim 		y = (buffer[READ_Y_POS_UPPER] << 8) | buffer[READ_Y_POS_LOWER];
13538e783b3SJoonyoung Shim 
13638e783b3SJoonyoung Shim 		input_report_key(data->input_dev, BTN_TOUCH, 1);
13738e783b3SJoonyoung Shim 		input_report_abs(data->input_dev, ABS_X, x);
13838e783b3SJoonyoung Shim 		input_report_abs(data->input_dev, ABS_Y, y);
13938e783b3SJoonyoung Shim 		input_sync(data->input_dev);
14038e783b3SJoonyoung Shim 		break;
14138e783b3SJoonyoung Shim 
14238e783b3SJoonyoung Shim 	case INPUT_TYPE_DUAL:
14338e783b3SJoonyoung Shim 		/* TODO */
14438e783b3SJoonyoung Shim 		break;
14538e783b3SJoonyoung Shim 
14638e783b3SJoonyoung Shim 	case INPUT_TYPE_PALM:
14738e783b3SJoonyoung Shim 		/* TODO */
14838e783b3SJoonyoung Shim 		break;
14938e783b3SJoonyoung Shim 
15038e783b3SJoonyoung Shim 	case INPUT_TYPE_PROXIMITY:
15138e783b3SJoonyoung Shim 		/* TODO */
15238e783b3SJoonyoung Shim 		break;
15338e783b3SJoonyoung Shim 
15438e783b3SJoonyoung Shim 	default:
15538e783b3SJoonyoung Shim 		dev_err(&client->dev, "Unknown ts input type %d\n",
15638e783b3SJoonyoung Shim 				buffer[READ_INPUT_INFO]);
15738e783b3SJoonyoung Shim 		break;
15838e783b3SJoonyoung Shim 	}
15938e783b3SJoonyoung Shim 
16038e783b3SJoonyoung Shim  out:
16138e783b3SJoonyoung Shim 	return IRQ_HANDLED;
16238e783b3SJoonyoung Shim }
16338e783b3SJoonyoung Shim 
164f5189d07SBeomho Seo static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data,
165f5189d07SBeomho Seo 				 const struct mcs_platform_data *platform_data)
16638e783b3SJoonyoung Shim {
16738e783b3SJoonyoung Shim 	struct i2c_client *client = data->client;
16838e783b3SJoonyoung Shim 
16938e783b3SJoonyoung Shim 	/* Touch reset & sleep mode */
17038e783b3SJoonyoung Shim 	i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE,
17138e783b3SJoonyoung Shim 			RESET_EXT_SOFT | OP_MODE_SLEEP);
17238e783b3SJoonyoung Shim 
17338e783b3SJoonyoung Shim 	/* Touch size */
17438e783b3SJoonyoung Shim 	i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_UPPER,
17538e783b3SJoonyoung Shim 			platform_data->x_size >> 8);
17638e783b3SJoonyoung Shim 	i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_LOWER,
17738e783b3SJoonyoung Shim 			platform_data->x_size & 0xff);
17838e783b3SJoonyoung Shim 	i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_UPPER,
17938e783b3SJoonyoung Shim 			platform_data->y_size >> 8);
18038e783b3SJoonyoung Shim 	i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_LOWER,
18138e783b3SJoonyoung Shim 			platform_data->y_size & 0xff);
18238e783b3SJoonyoung Shim 
18338e783b3SJoonyoung Shim 	/* Touch active mode & 80 report rate */
18438e783b3SJoonyoung Shim 	i2c_smbus_write_byte_data(data->client, MCS5000_TS_OP_MODE,
18538e783b3SJoonyoung Shim 			OP_MODE_ACTIVE | REPORT_RATE_80);
18638e783b3SJoonyoung Shim }
18738e783b3SJoonyoung Shim 
1885298cc4cSBill Pemberton static int mcs5000_ts_probe(struct i2c_client *client,
18938e783b3SJoonyoung Shim 			    const struct i2c_device_id *id)
19038e783b3SJoonyoung Shim {
191f5189d07SBeomho Seo 	const struct mcs_platform_data *pdata;
19238e783b3SJoonyoung Shim 	struct mcs5000_ts_data *data;
19338e783b3SJoonyoung Shim 	struct input_dev *input_dev;
194f5189d07SBeomho Seo 	int error;
19538e783b3SJoonyoung Shim 
196f5189d07SBeomho Seo 	pdata = dev_get_platdata(&client->dev);
197f5189d07SBeomho Seo 	if (!pdata)
19838e783b3SJoonyoung Shim 		return -EINVAL;
19938e783b3SJoonyoung Shim 
200f5189d07SBeomho Seo 	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
201f5189d07SBeomho Seo 	if (!data) {
20238e783b3SJoonyoung Shim 		dev_err(&client->dev, "Failed to allocate memory\n");
203f5189d07SBeomho Seo 		return -ENOMEM;
20438e783b3SJoonyoung Shim 	}
20538e783b3SJoonyoung Shim 
20638e783b3SJoonyoung Shim 	data->client = client;
207f5189d07SBeomho Seo 
208f5189d07SBeomho Seo 	input_dev = devm_input_allocate_device(&client->dev);
209f5189d07SBeomho Seo 	if (!input_dev) {
210f5189d07SBeomho Seo 		dev_err(&client->dev, "Failed to allocate input device\n");
211f5189d07SBeomho Seo 		return -ENOMEM;
212f5189d07SBeomho Seo 	}
21338e783b3SJoonyoung Shim 
21421d128a7SBeomho Seo 	input_dev->name = "MELFAS MCS-5000 Touchscreen";
21538e783b3SJoonyoung Shim 	input_dev->id.bustype = BUS_I2C;
21638e783b3SJoonyoung Shim 	input_dev->dev.parent = &client->dev;
21738e783b3SJoonyoung Shim 
21838e783b3SJoonyoung Shim 	__set_bit(EV_ABS, input_dev->evbit);
21938e783b3SJoonyoung Shim 	__set_bit(EV_KEY, input_dev->evbit);
22038e783b3SJoonyoung Shim 	__set_bit(BTN_TOUCH, input_dev->keybit);
22138e783b3SJoonyoung Shim 	input_set_abs_params(input_dev, ABS_X, 0, MCS5000_MAX_XC, 0, 0);
22238e783b3SJoonyoung Shim 	input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0);
22338e783b3SJoonyoung Shim 
22438e783b3SJoonyoung Shim 	input_set_drvdata(input_dev, data);
225f5189d07SBeomho Seo 	data->input_dev = input_dev;
22638e783b3SJoonyoung Shim 
227f5189d07SBeomho Seo 	if (pdata->cfg_pin)
228f5189d07SBeomho Seo 		pdata->cfg_pin();
22938e783b3SJoonyoung Shim 
230f5189d07SBeomho Seo 	error = devm_request_threaded_irq(&client->dev, client->irq,
231f5189d07SBeomho Seo 					  NULL, mcs5000_ts_interrupt,
232f5189d07SBeomho Seo 					  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
233f5189d07SBeomho Seo 					  "mcs5000_ts", data);
234f5189d07SBeomho Seo 	if (error) {
23538e783b3SJoonyoung Shim 		dev_err(&client->dev, "Failed to register interrupt\n");
236f5189d07SBeomho Seo 		return error;
23738e783b3SJoonyoung Shim 	}
23838e783b3SJoonyoung Shim 
239f5189d07SBeomho Seo 	error = input_register_device(data->input_dev);
240f5189d07SBeomho Seo 	if (error) {
241f5189d07SBeomho Seo 		dev_err(&client->dev, "Failed to register input device\n");
242f5189d07SBeomho Seo 		return error;
243f5189d07SBeomho Seo 	}
24438e783b3SJoonyoung Shim 
245f5189d07SBeomho Seo 	mcs5000_ts_phys_init(data, pdata);
24638e783b3SJoonyoung Shim 	i2c_set_clientdata(client, data);
24738e783b3SJoonyoung Shim 
24838e783b3SJoonyoung Shim 	return 0;
24938e783b3SJoonyoung Shim }
25038e783b3SJoonyoung Shim 
25138e783b3SJoonyoung Shim #ifdef CONFIG_PM
25292b672e2SMark Brown static int mcs5000_ts_suspend(struct device *dev)
25338e783b3SJoonyoung Shim {
25492b672e2SMark Brown 	struct i2c_client *client = to_i2c_client(dev);
25592b672e2SMark Brown 
25638e783b3SJoonyoung Shim 	/* Touch sleep mode */
25738e783b3SJoonyoung Shim 	i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP);
25838e783b3SJoonyoung Shim 
25938e783b3SJoonyoung Shim 	return 0;
26038e783b3SJoonyoung Shim }
26138e783b3SJoonyoung Shim 
26292b672e2SMark Brown static int mcs5000_ts_resume(struct device *dev)
26338e783b3SJoonyoung Shim {
26492b672e2SMark Brown 	struct i2c_client *client = to_i2c_client(dev);
26538e783b3SJoonyoung Shim 	struct mcs5000_ts_data *data = i2c_get_clientdata(client);
266f5189d07SBeomho Seo 	const struct mcs_platform_data *pdata = dev_get_platdata(dev);
26738e783b3SJoonyoung Shim 
268f5189d07SBeomho Seo 	mcs5000_ts_phys_init(data, pdata);
26938e783b3SJoonyoung Shim 
27038e783b3SJoonyoung Shim 	return 0;
27138e783b3SJoonyoung Shim }
272f5189d07SBeomho Seo #endif
27392b672e2SMark Brown 
27492b672e2SMark Brown static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume);
27538e783b3SJoonyoung Shim 
27638e783b3SJoonyoung Shim static const struct i2c_device_id mcs5000_ts_id[] = {
27738e783b3SJoonyoung Shim 	{ "mcs5000_ts", 0 },
27838e783b3SJoonyoung Shim 	{ }
27938e783b3SJoonyoung Shim };
28038e783b3SJoonyoung Shim MODULE_DEVICE_TABLE(i2c, mcs5000_ts_id);
28138e783b3SJoonyoung Shim 
28238e783b3SJoonyoung Shim static struct i2c_driver mcs5000_ts_driver = {
28338e783b3SJoonyoung Shim 	.probe		= mcs5000_ts_probe,
28438e783b3SJoonyoung Shim 	.driver = {
28538e783b3SJoonyoung Shim 		.name = "mcs5000_ts",
28692b672e2SMark Brown 		.pm   = &mcs5000_ts_pm,
28738e783b3SJoonyoung Shim 	},
28838e783b3SJoonyoung Shim 	.id_table	= mcs5000_ts_id,
28938e783b3SJoonyoung Shim };
29038e783b3SJoonyoung Shim 
2911b92c1cfSAxel Lin module_i2c_driver(mcs5000_ts_driver);
29238e783b3SJoonyoung Shim 
29338e783b3SJoonyoung Shim /* Module information */
29438e783b3SJoonyoung Shim MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
29538e783b3SJoonyoung Shim MODULE_DESCRIPTION("Touchscreen driver for MELFAS MCS-5000 controller");
29638e783b3SJoonyoung Shim MODULE_LICENSE("GPL");
297