18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci// Melfas MMS114/MMS152 touchscreen device driver
38c2ecf20Sopenharmony_ci//
48c2ecf20Sopenharmony_ci// Copyright (c) 2012 Samsung Electronics Co., Ltd.
58c2ecf20Sopenharmony_ci// Author: Joonyoung Shim <jy0922.shim@samsung.com>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/delay.h>
98c2ecf20Sopenharmony_ci#include <linux/of.h>
108c2ecf20Sopenharmony_ci#include <linux/of_device.h>
118c2ecf20Sopenharmony_ci#include <linux/i2c.h>
128c2ecf20Sopenharmony_ci#include <linux/input/mt.h>
138c2ecf20Sopenharmony_ci#include <linux/input/touchscreen.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/* Write only registers */
198c2ecf20Sopenharmony_ci#define MMS114_MODE_CONTROL		0x01
208c2ecf20Sopenharmony_ci#define MMS114_OPERATION_MODE_MASK	0xE
218c2ecf20Sopenharmony_ci#define MMS114_ACTIVE			BIT(1)
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define MMS114_XY_RESOLUTION_H		0x02
248c2ecf20Sopenharmony_ci#define MMS114_X_RESOLUTION		0x03
258c2ecf20Sopenharmony_ci#define MMS114_Y_RESOLUTION		0x04
268c2ecf20Sopenharmony_ci#define MMS114_CONTACT_THRESHOLD	0x05
278c2ecf20Sopenharmony_ci#define MMS114_MOVING_THRESHOLD		0x06
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* Read only registers */
308c2ecf20Sopenharmony_ci#define MMS114_PACKET_SIZE		0x0F
318c2ecf20Sopenharmony_ci#define MMS114_INFORMATION		0x10
328c2ecf20Sopenharmony_ci#define MMS114_TSP_REV			0xF0
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define MMS152_FW_REV			0xE1
358c2ecf20Sopenharmony_ci#define MMS152_COMPAT_GROUP		0xF2
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* Minimum delay time is 50us between stop and start signal of i2c */
388c2ecf20Sopenharmony_ci#define MMS114_I2C_DELAY		50
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/* 200ms needs after power on */
418c2ecf20Sopenharmony_ci#define MMS114_POWERON_DELAY		200
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* Touchscreen absolute values */
448c2ecf20Sopenharmony_ci#define MMS114_MAX_AREA			0xff
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define MMS114_MAX_TOUCH		10
478c2ecf20Sopenharmony_ci#define MMS114_PACKET_NUM		8
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* Touch type */
508c2ecf20Sopenharmony_ci#define MMS114_TYPE_NONE		0
518c2ecf20Sopenharmony_ci#define MMS114_TYPE_TOUCHSCREEN		1
528c2ecf20Sopenharmony_ci#define MMS114_TYPE_TOUCHKEY		2
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cienum mms_type {
558c2ecf20Sopenharmony_ci	TYPE_MMS114	= 114,
568c2ecf20Sopenharmony_ci	TYPE_MMS152	= 152,
578c2ecf20Sopenharmony_ci	TYPE_MMS345L	= 345,
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistruct mms114_data {
618c2ecf20Sopenharmony_ci	struct i2c_client	*client;
628c2ecf20Sopenharmony_ci	struct input_dev	*input_dev;
638c2ecf20Sopenharmony_ci	struct regulator	*core_reg;
648c2ecf20Sopenharmony_ci	struct regulator	*io_reg;
658c2ecf20Sopenharmony_ci	struct touchscreen_properties props;
668c2ecf20Sopenharmony_ci	enum mms_type		type;
678c2ecf20Sopenharmony_ci	unsigned int		contact_threshold;
688c2ecf20Sopenharmony_ci	unsigned int		moving_threshold;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/* Use cache data for mode control register(write only) */
718c2ecf20Sopenharmony_ci	u8			cache_mode_control;
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistruct mms114_touch {
758c2ecf20Sopenharmony_ci	u8 id:4, reserved_bit4:1, type:2, pressed:1;
768c2ecf20Sopenharmony_ci	u8 x_hi:4, y_hi:4;
778c2ecf20Sopenharmony_ci	u8 x_lo;
788c2ecf20Sopenharmony_ci	u8 y_lo;
798c2ecf20Sopenharmony_ci	u8 width;
808c2ecf20Sopenharmony_ci	u8 strength;
818c2ecf20Sopenharmony_ci	u8 reserved[2];
828c2ecf20Sopenharmony_ci} __packed;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int __mms114_read_reg(struct mms114_data *data, unsigned int reg,
858c2ecf20Sopenharmony_ci			     unsigned int len, u8 *val)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
888c2ecf20Sopenharmony_ci	struct i2c_msg xfer[2];
898c2ecf20Sopenharmony_ci	u8 buf = reg & 0xff;
908c2ecf20Sopenharmony_ci	int error;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (reg <= MMS114_MODE_CONTROL && reg + len > MMS114_MODE_CONTROL)
938c2ecf20Sopenharmony_ci		BUG();
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	/* Write register */
968c2ecf20Sopenharmony_ci	xfer[0].addr = client->addr;
978c2ecf20Sopenharmony_ci	xfer[0].flags = client->flags & I2C_M_TEN;
988c2ecf20Sopenharmony_ci	xfer[0].len = 1;
998c2ecf20Sopenharmony_ci	xfer[0].buf = &buf;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/* Read data */
1028c2ecf20Sopenharmony_ci	xfer[1].addr = client->addr;
1038c2ecf20Sopenharmony_ci	xfer[1].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
1048c2ecf20Sopenharmony_ci	xfer[1].len = len;
1058c2ecf20Sopenharmony_ci	xfer[1].buf = val;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	error = i2c_transfer(client->adapter, xfer, 2);
1088c2ecf20Sopenharmony_ci	if (error != 2) {
1098c2ecf20Sopenharmony_ci		dev_err(&client->dev,
1108c2ecf20Sopenharmony_ci			"%s: i2c transfer failed (%d)\n", __func__, error);
1118c2ecf20Sopenharmony_ci		return error < 0 ? error : -EIO;
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci	udelay(MMS114_I2C_DELAY);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return 0;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int mms114_read_reg(struct mms114_data *data, unsigned int reg)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	u8 val;
1218c2ecf20Sopenharmony_ci	int error;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (reg == MMS114_MODE_CONTROL)
1248c2ecf20Sopenharmony_ci		return data->cache_mode_control;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	error = __mms114_read_reg(data, reg, 1, &val);
1278c2ecf20Sopenharmony_ci	return error < 0 ? error : val;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int mms114_write_reg(struct mms114_data *data, unsigned int reg,
1318c2ecf20Sopenharmony_ci			    unsigned int val)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
1348c2ecf20Sopenharmony_ci	u8 buf[2];
1358c2ecf20Sopenharmony_ci	int error;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	buf[0] = reg & 0xff;
1388c2ecf20Sopenharmony_ci	buf[1] = val & 0xff;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	error = i2c_master_send(client, buf, 2);
1418c2ecf20Sopenharmony_ci	if (error != 2) {
1428c2ecf20Sopenharmony_ci		dev_err(&client->dev,
1438c2ecf20Sopenharmony_ci			"%s: i2c send failed (%d)\n", __func__, error);
1448c2ecf20Sopenharmony_ci		return error < 0 ? error : -EIO;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci	udelay(MMS114_I2C_DELAY);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (reg == MMS114_MODE_CONTROL)
1498c2ecf20Sopenharmony_ci		data->cache_mode_control = val;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	return 0;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic void mms114_process_mt(struct mms114_data *data, struct mms114_touch *touch)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
1578c2ecf20Sopenharmony_ci	struct input_dev *input_dev = data->input_dev;
1588c2ecf20Sopenharmony_ci	unsigned int id;
1598c2ecf20Sopenharmony_ci	unsigned int x;
1608c2ecf20Sopenharmony_ci	unsigned int y;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (touch->id > MMS114_MAX_TOUCH) {
1638c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Wrong touch id (%d)\n", touch->id);
1648c2ecf20Sopenharmony_ci		return;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	if (touch->type != MMS114_TYPE_TOUCHSCREEN) {
1688c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Wrong touch type (%d)\n", touch->type);
1698c2ecf20Sopenharmony_ci		return;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	id = touch->id - 1;
1738c2ecf20Sopenharmony_ci	x = touch->x_lo | touch->x_hi << 8;
1748c2ecf20Sopenharmony_ci	y = touch->y_lo | touch->y_hi << 8;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	dev_dbg(&client->dev,
1778c2ecf20Sopenharmony_ci		"id: %d, type: %d, pressed: %d, x: %d, y: %d, width: %d, strength: %d\n",
1788c2ecf20Sopenharmony_ci		id, touch->type, touch->pressed,
1798c2ecf20Sopenharmony_ci		x, y, touch->width, touch->strength);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	input_mt_slot(input_dev, id);
1828c2ecf20Sopenharmony_ci	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, touch->pressed);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (touch->pressed) {
1858c2ecf20Sopenharmony_ci		touchscreen_report_pos(input_dev, &data->props, x, y, true);
1868c2ecf20Sopenharmony_ci		input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, touch->width);
1878c2ecf20Sopenharmony_ci		input_report_abs(input_dev, ABS_MT_PRESSURE, touch->strength);
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic irqreturn_t mms114_interrupt(int irq, void *dev_id)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct mms114_data *data = dev_id;
1948c2ecf20Sopenharmony_ci	struct input_dev *input_dev = data->input_dev;
1958c2ecf20Sopenharmony_ci	struct mms114_touch touch[MMS114_MAX_TOUCH];
1968c2ecf20Sopenharmony_ci	int packet_size;
1978c2ecf20Sopenharmony_ci	int touch_size;
1988c2ecf20Sopenharmony_ci	int index;
1998c2ecf20Sopenharmony_ci	int error;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	mutex_lock(&input_dev->mutex);
2028c2ecf20Sopenharmony_ci	if (!input_dev->users) {
2038c2ecf20Sopenharmony_ci		mutex_unlock(&input_dev->mutex);
2048c2ecf20Sopenharmony_ci		goto out;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci	mutex_unlock(&input_dev->mutex);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	packet_size = mms114_read_reg(data, MMS114_PACKET_SIZE);
2098c2ecf20Sopenharmony_ci	if (packet_size <= 0)
2108c2ecf20Sopenharmony_ci		goto out;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	touch_size = packet_size / MMS114_PACKET_NUM;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size,
2158c2ecf20Sopenharmony_ci			(u8 *)touch);
2168c2ecf20Sopenharmony_ci	if (error < 0)
2178c2ecf20Sopenharmony_ci		goto out;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	for (index = 0; index < touch_size; index++)
2208c2ecf20Sopenharmony_ci		mms114_process_mt(data, touch + index);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	input_mt_report_pointer_emulation(data->input_dev, true);
2238c2ecf20Sopenharmony_ci	input_sync(data->input_dev);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ciout:
2268c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic int mms114_set_active(struct mms114_data *data, bool active)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	int val;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	val = mms114_read_reg(data, MMS114_MODE_CONTROL);
2348c2ecf20Sopenharmony_ci	if (val < 0)
2358c2ecf20Sopenharmony_ci		return val;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	val &= ~MMS114_OPERATION_MODE_MASK;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* If active is false, sleep mode */
2408c2ecf20Sopenharmony_ci	if (active)
2418c2ecf20Sopenharmony_ci		val |= MMS114_ACTIVE;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	return mms114_write_reg(data, MMS114_MODE_CONTROL, val);
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic int mms114_get_version(struct mms114_data *data)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct device *dev = &data->client->dev;
2498c2ecf20Sopenharmony_ci	u8 buf[6];
2508c2ecf20Sopenharmony_ci	int group;
2518c2ecf20Sopenharmony_ci	int error;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	switch (data->type) {
2548c2ecf20Sopenharmony_ci	case TYPE_MMS345L:
2558c2ecf20Sopenharmony_ci		error = __mms114_read_reg(data, MMS152_FW_REV, 3, buf);
2568c2ecf20Sopenharmony_ci		if (error)
2578c2ecf20Sopenharmony_ci			return error;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci		dev_info(dev, "TSP FW Rev: bootloader 0x%x / core 0x%x / config 0x%x\n",
2608c2ecf20Sopenharmony_ci			 buf[0], buf[1], buf[2]);
2618c2ecf20Sopenharmony_ci		break;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	case TYPE_MMS152:
2648c2ecf20Sopenharmony_ci		error = __mms114_read_reg(data, MMS152_FW_REV, 3, buf);
2658c2ecf20Sopenharmony_ci		if (error)
2668c2ecf20Sopenharmony_ci			return error;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci		group = i2c_smbus_read_byte_data(data->client,
2698c2ecf20Sopenharmony_ci						  MMS152_COMPAT_GROUP);
2708c2ecf20Sopenharmony_ci		if (group < 0)
2718c2ecf20Sopenharmony_ci			return group;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		dev_info(dev, "TSP FW Rev: bootloader 0x%x / core 0x%x / config 0x%x, Compat group: %c\n",
2748c2ecf20Sopenharmony_ci			 buf[0], buf[1], buf[2], group);
2758c2ecf20Sopenharmony_ci		break;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	case TYPE_MMS114:
2788c2ecf20Sopenharmony_ci		error = __mms114_read_reg(data, MMS114_TSP_REV, 6, buf);
2798c2ecf20Sopenharmony_ci		if (error)
2808c2ecf20Sopenharmony_ci			return error;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci		dev_info(dev, "TSP Rev: 0x%x, HW Rev: 0x%x, Firmware Ver: 0x%x\n",
2838c2ecf20Sopenharmony_ci			 buf[0], buf[1], buf[3]);
2848c2ecf20Sopenharmony_ci		break;
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	return 0;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic int mms114_setup_regs(struct mms114_data *data)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	const struct touchscreen_properties *props = &data->props;
2938c2ecf20Sopenharmony_ci	int val;
2948c2ecf20Sopenharmony_ci	int error;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	error = mms114_get_version(data);
2978c2ecf20Sopenharmony_ci	if (error < 0)
2988c2ecf20Sopenharmony_ci		return error;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	/* Only MMS114 has configuration and power on registers */
3018c2ecf20Sopenharmony_ci	if (data->type != TYPE_MMS114)
3028c2ecf20Sopenharmony_ci		return 0;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	error = mms114_set_active(data, true);
3058c2ecf20Sopenharmony_ci	if (error < 0)
3068c2ecf20Sopenharmony_ci		return error;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	val = (props->max_x >> 8) & 0xf;
3098c2ecf20Sopenharmony_ci	val |= ((props->max_y >> 8) & 0xf) << 4;
3108c2ecf20Sopenharmony_ci	error = mms114_write_reg(data, MMS114_XY_RESOLUTION_H, val);
3118c2ecf20Sopenharmony_ci	if (error < 0)
3128c2ecf20Sopenharmony_ci		return error;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	val = props->max_x & 0xff;
3158c2ecf20Sopenharmony_ci	error = mms114_write_reg(data, MMS114_X_RESOLUTION, val);
3168c2ecf20Sopenharmony_ci	if (error < 0)
3178c2ecf20Sopenharmony_ci		return error;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	val = props->max_x & 0xff;
3208c2ecf20Sopenharmony_ci	error = mms114_write_reg(data, MMS114_Y_RESOLUTION, val);
3218c2ecf20Sopenharmony_ci	if (error < 0)
3228c2ecf20Sopenharmony_ci		return error;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (data->contact_threshold) {
3258c2ecf20Sopenharmony_ci		error = mms114_write_reg(data, MMS114_CONTACT_THRESHOLD,
3268c2ecf20Sopenharmony_ci				data->contact_threshold);
3278c2ecf20Sopenharmony_ci		if (error < 0)
3288c2ecf20Sopenharmony_ci			return error;
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	if (data->moving_threshold) {
3328c2ecf20Sopenharmony_ci		error = mms114_write_reg(data, MMS114_MOVING_THRESHOLD,
3338c2ecf20Sopenharmony_ci				data->moving_threshold);
3348c2ecf20Sopenharmony_ci		if (error < 0)
3358c2ecf20Sopenharmony_ci			return error;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	return 0;
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic int mms114_start(struct mms114_data *data)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
3448c2ecf20Sopenharmony_ci	int error;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	error = regulator_enable(data->core_reg);
3478c2ecf20Sopenharmony_ci	if (error) {
3488c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Failed to enable avdd: %d\n", error);
3498c2ecf20Sopenharmony_ci		return error;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	error = regulator_enable(data->io_reg);
3538c2ecf20Sopenharmony_ci	if (error) {
3548c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Failed to enable vdd: %d\n", error);
3558c2ecf20Sopenharmony_ci		regulator_disable(data->core_reg);
3568c2ecf20Sopenharmony_ci		return error;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	msleep(MMS114_POWERON_DELAY);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	error = mms114_setup_regs(data);
3628c2ecf20Sopenharmony_ci	if (error < 0) {
3638c2ecf20Sopenharmony_ci		regulator_disable(data->io_reg);
3648c2ecf20Sopenharmony_ci		regulator_disable(data->core_reg);
3658c2ecf20Sopenharmony_ci		return error;
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	enable_irq(client->irq);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	return 0;
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic void mms114_stop(struct mms114_data *data)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
3768c2ecf20Sopenharmony_ci	int error;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	disable_irq(client->irq);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	error = regulator_disable(data->io_reg);
3818c2ecf20Sopenharmony_ci	if (error)
3828c2ecf20Sopenharmony_ci		dev_warn(&client->dev, "Failed to disable vdd: %d\n", error);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	error = regulator_disable(data->core_reg);
3858c2ecf20Sopenharmony_ci	if (error)
3868c2ecf20Sopenharmony_ci		dev_warn(&client->dev, "Failed to disable avdd: %d\n", error);
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic int mms114_input_open(struct input_dev *dev)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct mms114_data *data = input_get_drvdata(dev);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	return mms114_start(data);
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic void mms114_input_close(struct input_dev *dev)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	struct mms114_data *data = input_get_drvdata(dev);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	mms114_stop(data);
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic int mms114_parse_legacy_bindings(struct mms114_data *data)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct device *dev = &data->client->dev;
4068c2ecf20Sopenharmony_ci	struct touchscreen_properties *props = &data->props;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	if (device_property_read_u32(dev, "x-size", &props->max_x)) {
4098c2ecf20Sopenharmony_ci		dev_dbg(dev, "failed to get legacy x-size property\n");
4108c2ecf20Sopenharmony_ci		return -EINVAL;
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (device_property_read_u32(dev, "y-size", &props->max_y)) {
4148c2ecf20Sopenharmony_ci		dev_dbg(dev, "failed to get legacy y-size property\n");
4158c2ecf20Sopenharmony_ci		return -EINVAL;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	device_property_read_u32(dev, "contact-threshold",
4198c2ecf20Sopenharmony_ci				&data->contact_threshold);
4208c2ecf20Sopenharmony_ci	device_property_read_u32(dev, "moving-threshold",
4218c2ecf20Sopenharmony_ci				&data->moving_threshold);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	if (device_property_read_bool(dev, "x-invert"))
4248c2ecf20Sopenharmony_ci		props->invert_x = true;
4258c2ecf20Sopenharmony_ci	if (device_property_read_bool(dev, "y-invert"))
4268c2ecf20Sopenharmony_ci		props->invert_y = true;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	props->swap_x_y = false;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	return 0;
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic int mms114_probe(struct i2c_client *client,
4348c2ecf20Sopenharmony_ci				  const struct i2c_device_id *id)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	struct mms114_data *data;
4378c2ecf20Sopenharmony_ci	struct input_dev *input_dev;
4388c2ecf20Sopenharmony_ci	const void *match_data;
4398c2ecf20Sopenharmony_ci	int error;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
4428c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Not supported I2C adapter\n");
4438c2ecf20Sopenharmony_ci		return -ENODEV;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	data = devm_kzalloc(&client->dev, sizeof(struct mms114_data),
4478c2ecf20Sopenharmony_ci			    GFP_KERNEL);
4488c2ecf20Sopenharmony_ci	input_dev = devm_input_allocate_device(&client->dev);
4498c2ecf20Sopenharmony_ci	if (!data || !input_dev) {
4508c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Failed to allocate memory\n");
4518c2ecf20Sopenharmony_ci		return -ENOMEM;
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	data->client = client;
4558c2ecf20Sopenharmony_ci	data->input_dev = input_dev;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	match_data = device_get_match_data(&client->dev);
4588c2ecf20Sopenharmony_ci	if (!match_data)
4598c2ecf20Sopenharmony_ci		return -EINVAL;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	data->type = (enum mms_type)match_data;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X);
4648c2ecf20Sopenharmony_ci	input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y);
4658c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
4668c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
4678c2ecf20Sopenharmony_ci			     0, MMS114_MAX_AREA, 0, 0);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	touchscreen_parse_properties(input_dev, true, &data->props);
4708c2ecf20Sopenharmony_ci	if (!data->props.max_x || !data->props.max_y) {
4718c2ecf20Sopenharmony_ci		dev_dbg(&client->dev,
4728c2ecf20Sopenharmony_ci			"missing X/Y size properties, trying legacy bindings\n");
4738c2ecf20Sopenharmony_ci		error = mms114_parse_legacy_bindings(data);
4748c2ecf20Sopenharmony_ci		if (error)
4758c2ecf20Sopenharmony_ci			return error;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci		input_set_abs_params(input_dev, ABS_MT_POSITION_X,
4788c2ecf20Sopenharmony_ci				     0, data->props.max_x, 0, 0);
4798c2ecf20Sopenharmony_ci		input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
4808c2ecf20Sopenharmony_ci				     0, data->props.max_y, 0, 0);
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	if (data->type == TYPE_MMS114) {
4848c2ecf20Sopenharmony_ci		/*
4858c2ecf20Sopenharmony_ci		 * The firmware handles movement and pressure fuzz, so
4868c2ecf20Sopenharmony_ci		 * don't duplicate that in software.
4878c2ecf20Sopenharmony_ci		 */
4888c2ecf20Sopenharmony_ci		data->moving_threshold = input_abs_get_fuzz(input_dev,
4898c2ecf20Sopenharmony_ci							    ABS_MT_POSITION_X);
4908c2ecf20Sopenharmony_ci		data->contact_threshold = input_abs_get_fuzz(input_dev,
4918c2ecf20Sopenharmony_ci							     ABS_MT_PRESSURE);
4928c2ecf20Sopenharmony_ci		input_abs_set_fuzz(input_dev, ABS_MT_POSITION_X, 0);
4938c2ecf20Sopenharmony_ci		input_abs_set_fuzz(input_dev, ABS_MT_POSITION_Y, 0);
4948c2ecf20Sopenharmony_ci		input_abs_set_fuzz(input_dev, ABS_MT_PRESSURE, 0);
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	input_dev->name = devm_kasprintf(&client->dev, GFP_KERNEL,
4988c2ecf20Sopenharmony_ci					 "MELFAS MMS%d Touchscreen",
4998c2ecf20Sopenharmony_ci					 data->type);
5008c2ecf20Sopenharmony_ci	if (!input_dev->name)
5018c2ecf20Sopenharmony_ci		return -ENOMEM;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	input_dev->id.bustype = BUS_I2C;
5048c2ecf20Sopenharmony_ci	input_dev->dev.parent = &client->dev;
5058c2ecf20Sopenharmony_ci	input_dev->open = mms114_input_open;
5068c2ecf20Sopenharmony_ci	input_dev->close = mms114_input_close;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	error = input_mt_init_slots(input_dev, MMS114_MAX_TOUCH,
5098c2ecf20Sopenharmony_ci				    INPUT_MT_DIRECT);
5108c2ecf20Sopenharmony_ci	if (error)
5118c2ecf20Sopenharmony_ci		return error;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	input_set_drvdata(input_dev, data);
5148c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, data);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	data->core_reg = devm_regulator_get(&client->dev, "avdd");
5178c2ecf20Sopenharmony_ci	if (IS_ERR(data->core_reg)) {
5188c2ecf20Sopenharmony_ci		error = PTR_ERR(data->core_reg);
5198c2ecf20Sopenharmony_ci		dev_err(&client->dev,
5208c2ecf20Sopenharmony_ci			"Unable to get the Core regulator (%d)\n", error);
5218c2ecf20Sopenharmony_ci		return error;
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	data->io_reg = devm_regulator_get(&client->dev, "vdd");
5258c2ecf20Sopenharmony_ci	if (IS_ERR(data->io_reg)) {
5268c2ecf20Sopenharmony_ci		error = PTR_ERR(data->io_reg);
5278c2ecf20Sopenharmony_ci		dev_err(&client->dev,
5288c2ecf20Sopenharmony_ci			"Unable to get the IO regulator (%d)\n", error);
5298c2ecf20Sopenharmony_ci		return error;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	error = devm_request_threaded_irq(&client->dev, client->irq,
5338c2ecf20Sopenharmony_ci					  NULL, mms114_interrupt, IRQF_ONESHOT,
5348c2ecf20Sopenharmony_ci					  dev_name(&client->dev), data);
5358c2ecf20Sopenharmony_ci	if (error) {
5368c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Failed to register interrupt\n");
5378c2ecf20Sopenharmony_ci		return error;
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci	disable_irq(client->irq);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	error = input_register_device(data->input_dev);
5428c2ecf20Sopenharmony_ci	if (error) {
5438c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Failed to register input device\n");
5448c2ecf20Sopenharmony_ci		return error;
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	return 0;
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic int __maybe_unused mms114_suspend(struct device *dev)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
5538c2ecf20Sopenharmony_ci	struct mms114_data *data = i2c_get_clientdata(client);
5548c2ecf20Sopenharmony_ci	struct input_dev *input_dev = data->input_dev;
5558c2ecf20Sopenharmony_ci	int id;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	/* Release all touch */
5588c2ecf20Sopenharmony_ci	for (id = 0; id < MMS114_MAX_TOUCH; id++) {
5598c2ecf20Sopenharmony_ci		input_mt_slot(input_dev, id);
5608c2ecf20Sopenharmony_ci		input_mt_report_slot_inactive(input_dev);
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	input_mt_report_pointer_emulation(input_dev, true);
5648c2ecf20Sopenharmony_ci	input_sync(input_dev);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	mutex_lock(&input_dev->mutex);
5678c2ecf20Sopenharmony_ci	if (input_dev->users)
5688c2ecf20Sopenharmony_ci		mms114_stop(data);
5698c2ecf20Sopenharmony_ci	mutex_unlock(&input_dev->mutex);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	return 0;
5728c2ecf20Sopenharmony_ci}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_cistatic int __maybe_unused mms114_resume(struct device *dev)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
5778c2ecf20Sopenharmony_ci	struct mms114_data *data = i2c_get_clientdata(client);
5788c2ecf20Sopenharmony_ci	struct input_dev *input_dev = data->input_dev;
5798c2ecf20Sopenharmony_ci	int error;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	mutex_lock(&input_dev->mutex);
5828c2ecf20Sopenharmony_ci	if (input_dev->users) {
5838c2ecf20Sopenharmony_ci		error = mms114_start(data);
5848c2ecf20Sopenharmony_ci		if (error < 0) {
5858c2ecf20Sopenharmony_ci			mutex_unlock(&input_dev->mutex);
5868c2ecf20Sopenharmony_ci			return error;
5878c2ecf20Sopenharmony_ci		}
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci	mutex_unlock(&input_dev->mutex);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	return 0;
5928c2ecf20Sopenharmony_ci}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(mms114_pm_ops, mms114_suspend, mms114_resume);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic const struct i2c_device_id mms114_id[] = {
5978c2ecf20Sopenharmony_ci	{ "mms114", 0 },
5988c2ecf20Sopenharmony_ci	{ }
5998c2ecf20Sopenharmony_ci};
6008c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mms114_id);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
6038c2ecf20Sopenharmony_cistatic const struct of_device_id mms114_dt_match[] = {
6048c2ecf20Sopenharmony_ci	{
6058c2ecf20Sopenharmony_ci		.compatible = "melfas,mms114",
6068c2ecf20Sopenharmony_ci		.data = (void *)TYPE_MMS114,
6078c2ecf20Sopenharmony_ci	}, {
6088c2ecf20Sopenharmony_ci		.compatible = "melfas,mms152",
6098c2ecf20Sopenharmony_ci		.data = (void *)TYPE_MMS152,
6108c2ecf20Sopenharmony_ci	}, {
6118c2ecf20Sopenharmony_ci		.compatible = "melfas,mms345l",
6128c2ecf20Sopenharmony_ci		.data = (void *)TYPE_MMS345L,
6138c2ecf20Sopenharmony_ci	},
6148c2ecf20Sopenharmony_ci	{ }
6158c2ecf20Sopenharmony_ci};
6168c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mms114_dt_match);
6178c2ecf20Sopenharmony_ci#endif
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic struct i2c_driver mms114_driver = {
6208c2ecf20Sopenharmony_ci	.driver = {
6218c2ecf20Sopenharmony_ci		.name	= "mms114",
6228c2ecf20Sopenharmony_ci		.pm	= &mms114_pm_ops,
6238c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(mms114_dt_match),
6248c2ecf20Sopenharmony_ci	},
6258c2ecf20Sopenharmony_ci	.probe		= mms114_probe,
6268c2ecf20Sopenharmony_ci	.id_table	= mms114_id,
6278c2ecf20Sopenharmony_ci};
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_cimodule_i2c_driver(mms114_driver);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci/* Module information */
6328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
6338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MELFAS mms114 Touchscreen driver");
6348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
635