Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a5df8a5b authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "input: it7258_ts_i2c: add debugfs support for suspend/resume"

parents 5b649be8 af2218dd
Loading
Loading
Loading
Loading
+208 −66
Original line number Diff line number Diff line
@@ -24,11 +24,14 @@
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
#include <linux/of_gpio.h>
#include <linux/fb.h>
#include <linux/debugfs.h>

#define MAX_BUFFER_SIZE			144
#define DEVICE_NAME			"IT7260"
#define SCREEN_X_RESOLUTION		320
#define SCREEN_Y_RESOLUTION		320
#define DEBUGFS_DIR_NAME		"ts_debug"

/* all commands writes go to this idx */
#define BUF_COMMAND			0x20
@@ -99,6 +102,11 @@
/* use this to include integers in commands */
#define CMD_UINT16(v)		((uint8_t)(v)) , ((uint8_t)((v) >> 8))

/* Function declarations */
static int fb_notifier_callback(struct notifier_block *self,
			unsigned long event, void *data);
static int IT7260_ts_resume(struct device *dev);
static int IT7260_ts_suspend(struct device *dev);

struct FingerData {
	uint8_t xLo;
@@ -148,13 +156,15 @@ struct IT7260_ts_data {
	const struct IT7260_ts_platform_data *pdata;
	struct regulator *vdd;
	struct regulator *avdd;
#ifdef CONFIG_FB
	struct notifier_block fb_notif;
#endif
	struct dentry *dir;
};

static int8_t fwUploadResult;
static int8_t calibrationWasSuccessful;
static bool devicePresent;
static DEFINE_MUTEX(sleepModeMutex);
static bool chipAwake;
static bool hadFingerDown;
static bool isDeviceSuspend;
static struct input_dev *input_dev;
@@ -163,6 +173,26 @@ static struct IT7260_ts_data *gl_ts;
#define LOGE(...)	pr_err(DEVICE_NAME ": " __VA_ARGS__)
#define LOGI(...)	printk(DEVICE_NAME ": " __VA_ARGS__)

static int IT7260_debug_suspend_set(void *_data, u64 val)
{
	if (val)
		IT7260_ts_suspend(&gl_ts->client->dev);
	else
		IT7260_ts_resume(&gl_ts->client->dev);

	return 0;
}

static int IT7260_debug_suspend_get(void *_data, u64 *val)
{
	*val = isDeviceSuspend;

	return 0;
}

DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, IT7260_debug_suspend_get,
				IT7260_debug_suspend_set, "%lld\n");

/* internal use func - does not make sure chip is ready before read */
static bool i2cReadNoReadyCheck(uint8_t bufferIndex, uint8_t *dataBuffer,
							uint16_t dataLength)
@@ -414,6 +444,21 @@ static bool chipGetVersions(uint8_t *verFw, uint8_t *verCfg, bool logIt)
	return ret;
}

static int IT7260_ts_chipLowPowerMode(bool low)
{
	static const uint8_t cmdGoSleep[] = {CMD_PWR_CTL,
					0x00, PWR_CTL_SLEEP_MODE};
	uint8_t dummy;

	if (low)
		i2cWriteNoReadyCheck(BUF_COMMAND, cmdGoSleep,
					sizeof(cmdGoSleep));
	else
		i2cReadNoReadyCheck(BUF_QUERY, &dummy, sizeof(dummy));

	return 0;
}

static ssize_t sysfsUpgradeStore(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
@@ -586,48 +631,33 @@ static ssize_t sysfsSleepShow(struct device *dev,
	 * leaking a byte of kernel data (by claiming to return a byte but not
	 * writing to buf. To fix this now we actually return the sleep status
	 */
	if (!mutex_lock_interruptible(&sleepModeMutex)) {
		*buf = chipAwake ? '1' : '0';
		mutex_unlock(&sleepModeMutex);
	*buf = isDeviceSuspend ? '1' : '0';
	return 1;
	} else {
		return -EINTR;
	}
}

static ssize_t sysfsSleepStore(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	static const uint8_t cmdGoSleep[] = {CMD_PWR_CTL,
					0x00, PWR_CTL_SLEEP_MODE};
	int goToSleepVal, ret;
	bool goToWake;
	uint8_t dummy;

	ret = kstrtoint(buf, 10, &goToSleepVal);
	/* convert to bool of proper polarity */
	goToWake = !goToSleepVal;

	if (!mutex_lock_interruptible(&sleepModeMutex)) {
		if ((chipAwake && goToWake) || (!chipAwake && !goToWake))
			LOGE("duplicate request to %s chip\n",
				goToWake ? "wake" : "sleep");
		else if (goToWake) {
			i2cReadNoReadyCheck(BUF_QUERY, &dummy, sizeof(dummy));
			enable_irq(gl_ts->client->irq);
			LOGI("touch is going to wake!\n");
		} else {

	if ((isDeviceSuspend && goToSleepVal > 0) || (!isDeviceSuspend &&
							goToSleepVal == 0))
		dev_err(dev, "duplicate request to %s chip\n",
			goToSleepVal ? "sleep" : "wake");
	else if (goToSleepVal) {
		disable_irq(gl_ts->client->irq);
			i2cWriteNoReadyCheck(BUF_COMMAND, cmdGoSleep,
						sizeof(cmdGoSleep));
			LOGI("touch is going to sleep...\n");
		}
		chipAwake = goToWake;
		mutex_unlock(&sleepModeMutex);
		return count;
		IT7260_ts_chipLowPowerMode(true);
		dev_dbg(dev, "touch is going to sleep...\n");
	} else {
		return -EINTR;
		IT7260_ts_chipLowPowerMode(false);
		enable_irq(gl_ts->client->irq);
		dev_dbg(dev, "touch is going to wake!\n");
	}
	isDeviceSuspend = goToSleepVal;

	return count;
}


@@ -684,6 +714,13 @@ void sendCalibrationCmd(void)
}
EXPORT_SYMBOL(sendCalibrationCmd);

static void IT7260_ts_release_all(void)
{
	input_report_key(gl_ts->input_dev, BTN_TOUCH, 0);
	input_mt_sync(gl_ts->input_dev);
	input_sync(gl_ts->input_dev);
}

static void readFingerData(uint16_t *xP, uint16_t *yP, uint8_t *pressureP,
						const struct FingerData *fd)
{
@@ -701,7 +738,7 @@ static void readFingerData(uint16_t *xP, uint16_t *yP, uint8_t *pressureP,
		*pressureP = fd->pressure & FD_PRESSURE_BITS;
}

static void readTouchDataPoint(void)
static irqreturn_t IT7260_ts_threaded_handler(int irq, void *devid)
{
	struct PointData pointData;
	uint8_t devStatus;
@@ -711,49 +748,46 @@ static void readTouchDataPoint(void)
	/* verify there is point data to read & it is readable and valid */
	i2cReadNoReadyCheck(BUF_QUERY, &devStatus, sizeof(devStatus));
	if (!((devStatus & PT_INFO_BITS) & PT_INFO_YES)) {
		pr_err("readTouchDataPoint() called when no data available (0x%02X)\n",
								devStatus);
		return;
		return IRQ_HANDLED;
	}
	if (!i2cReadNoReadyCheck(BUF_POINT_INFO, (void *)&pointData,
						sizeof(pointData))) {
		pr_err("readTouchDataPoint() failed to read point data buffer\n");
		return;
		dev_err(&gl_ts->client->dev,
			"readTouchDataPoint() failed to read point data buffer\n");
		return IRQ_HANDLED;
	}
	if ((pointData.flags & PD_FLAGS_DATA_TYPE_BITS) !=
					PD_FLAGS_DATA_TYPE_TOUCH) {
		pr_err("readTouchDataPoint() dropping non-point data of type 0x%02X\n",
		dev_err(&gl_ts->client->dev,
			"readTouchDataPoint() dropping non-point data of type 0x%02X\n",
							pointData.flags);
		return;
		return IRQ_HANDLED;
	}

	if ((pointData.flags & PD_FLAGS_HAVE_FINGERS) & 1)
		readFingerData(&x, &y, &pressure, pointData.fd);

	if (pressure >= FD_PRESSURE_LIGHT) {

		if (!hadFingerDown)
			hadFingerDown = true;

		readFingerData(&x, &y, &pressure, pointData.fd);

		input_report_abs(gl_ts->input_dev, ABS_X, x);
		input_report_abs(gl_ts->input_dev, ABS_Y, y);
		input_report_key(gl_ts->input_dev, BTN_TOUCH, 1);
		input_report_abs(gl_ts->input_dev, ABS_MT_POSITION_X, x);
		input_report_abs(gl_ts->input_dev, ABS_MT_POSITION_Y, y);
		input_mt_sync(gl_ts->input_dev);
		input_sync(gl_ts->input_dev);


	} else if (hadFingerDown) {
		hadFingerDown = false;

		input_report_key(gl_ts->input_dev, BTN_TOUCH, 0);
		input_mt_sync(gl_ts->input_dev);
		input_sync(gl_ts->input_dev);
	}

}

static irqreturn_t IT7260_ts_threaded_handler(int irq, void *devid)
{
	readTouchDataPoint();
	return IRQ_HANDLED;
}

@@ -803,6 +837,7 @@ static int IT7260_ts_probe(struct i2c_client *client,
	uint8_t rsp[2];
	int ret = -1;
	int rc;
	struct dentry *temp;

	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
		LOGE("need I2C_FUNC_I2C\n");
@@ -940,8 +975,11 @@ static int IT7260_ts_probe(struct i2c_client *client,
	set_bit(KEY_SLEEP,input_dev->keybit);
	set_bit(KEY_WAKEUP,input_dev->keybit);
	set_bit(KEY_POWER,input_dev->keybit);
	input_set_abs_params(input_dev, ABS_X, 0, SCREEN_X_RESOLUTION, 0, 0);
	input_set_abs_params(input_dev, ABS_Y, 0, SCREEN_Y_RESOLUTION, 0, 0);
	input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
				SCREEN_X_RESOLUTION, 0, 0);
	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
				SCREEN_Y_RESOLUTION, 0, 0);
	input_set_drvdata(gl_ts->input_dev, gl_ts);

	if (input_register_device(input_dev)) {
		LOGE("failed to register input device\n");
@@ -959,6 +997,15 @@ static int IT7260_ts_probe(struct i2c_client *client,
		goto err_sysfs_grp_create_2;
	}

#if defined(CONFIG_FB)
	gl_ts->fb_notif.notifier_call = fb_notifier_callback;

	ret = fb_register_client(&gl_ts->fb_notif);
	if (ret)
		dev_err(&client->dev, "Unable to register fb_notifier: %d\n",
									ret);
#endif
	
	devicePresent = true;

	i2cWriteNoReadyCheck(BUF_COMMAND, cmdStart, sizeof(cmdStart));
@@ -966,8 +1013,36 @@ static int IT7260_ts_probe(struct i2c_client *client,
	i2cReadNoReadyCheck(BUF_RESPONSE, rsp, sizeof(rsp));
	mdelay(10);

	gl_ts->dir = debugfs_create_dir(DEBUGFS_DIR_NAME, NULL);
	if (gl_ts->dir == NULL || IS_ERR(gl_ts->dir)) {
		dev_err(&client->dev,
			"%s: Failed to create debugfs directory, rc = %ld\n",
			__func__, PTR_ERR(gl_ts->dir));
		ret = PTR_ERR(gl_ts->dir);
		goto err_create_debugfs_dir;
	}

	temp = debugfs_create_file("suspend", S_IRUSR | S_IWUSR, gl_ts->dir,
					gl_ts, &debug_suspend_fops);
	if (temp == NULL || IS_ERR(temp)) {
		dev_err(&client->dev,
			"%s: Failed to create suspend debugfs file, rc = %ld\n",
			__func__, PTR_ERR(temp));
		ret = PTR_ERR(temp);
		goto err_create_debugfs_file;
	}

	return 0;

err_create_debugfs_file:
	debugfs_remove_recursive(gl_ts->dir);
err_create_debugfs_dir:
#if defined(CONFIG_FB)
	if (fb_unregister_client(&gl_ts->fb_notif))
		dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n");
#endif
	sysfs_remove_group(&(client->dev.kobj), &it7260_attr_group);

err_sysfs_grp_create_2:
	free_irq(client->irq, gl_ts);

@@ -991,45 +1066,112 @@ err_out:

static int IT7260_ts_remove(struct i2c_client *client)
{
	debugfs_remove_recursive(gl_ts->dir);
#if defined(CONFIG_FB)
	if (fb_unregister_client(&gl_ts->fb_notif))
		dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n");
#endif
	sysfs_remove_group(&(client->dev.kobj), &it7260_attr_group);
	devicePresent = false;
	return 0;
}

static const struct i2c_device_id IT7260_ts_id[] = {
	{ DEVICE_NAME, 0},
	{}
};
#if defined(CONFIG_FB)
static int fb_notifier_callback(struct notifier_block *self,
			unsigned long event, void *data)
{
	struct fb_event *evdata = data;
	int *blank;

MODULE_DEVICE_TABLE(i2c, IT7260_ts_id);
	if (evdata && evdata->data && gl_ts && gl_ts->client) {
		if (event == FB_EVENT_BLANK) {
			blank = evdata->data;
			if (*blank == FB_BLANK_UNBLANK)
				IT7260_ts_resume(&(gl_ts->input_dev->dev));
			else if (*blank == FB_BLANK_POWERDOWN ||
					*blank == FB_BLANK_VSYNC_SUSPEND)
				IT7260_ts_suspend(&(gl_ts->input_dev->dev));
		}
	}

static const struct of_device_id IT7260_match_table[] = {
	{ .compatible = "ite,it7260_ts",},
	{},
};
	return 0;
}
#endif

static int IT7260_ts_resume(struct i2c_client *i2cdev)
#ifdef CONFIG_PM
static int IT7260_ts_resume(struct device *dev)
{
	if (!isDeviceSuspend) {
		dev_info(dev, "Already in resume state\n");
		return 0;
	}

	/* put the device in active powr mode */
	IT7260_ts_chipLowPowerMode(false);

	enable_irq(gl_ts->client->irq);
	isDeviceSuspend	= false;
	return 0;
}

static int IT7260_ts_suspend(struct i2c_client *i2cdev, pm_message_t pmesg)
static int IT7260_ts_suspend(struct device *dev)
{
	if (isDeviceSuspend) {
		dev_info(dev, "Already in suspend state\n");
		return 0;
	}

	disable_irq(gl_ts->client->irq);

	/* put the device in active powr mode */
	IT7260_ts_chipLowPowerMode(true);

	IT7260_ts_release_all();
	isDeviceSuspend = true;

	return 0;
}

static const struct dev_pm_ops IT7260_ts_dev_pm_ops = {
	.suspend = IT7260_ts_suspend,
	.resume  = IT7260_ts_resume,
};
#else
static int IT7260_ts_resume(struct device *dev)
{
	return 0;
}

static int IT7260_ts_suspend(struct device *dev)
{
	return 0;
}
#endif

static const struct i2c_device_id IT7260_ts_id[] = {
	{ DEVICE_NAME, 0},
	{}
};

MODULE_DEVICE_TABLE(i2c, IT7260_ts_id);

static const struct of_device_id IT7260_match_table[] = {
	{ .compatible = "ite,it7260_ts",},
	{},
};

static struct i2c_driver IT7260_ts_driver = {
	.driver = {
		.owner = THIS_MODULE,
		.name = DEVICE_NAME,
		.of_match_table = IT7260_match_table,
#ifdef CONFIG_PM
		.pm = &IT7260_ts_dev_pm_ops,
#endif
	},
	.probe = IT7260_ts_probe,
	.remove = IT7260_ts_remove,
	.id_table = IT7260_ts_id,
	.resume   = IT7260_ts_resume,
	.suspend = IT7260_ts_suspend,
};

module_i2c_driver(IT7260_ts_driver);