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

Commit 7b939544 authored by Ritesh Kumar's avatar Ritesh Kumar Committed by Gerrit - the friendly Code Review server
Browse files

input: touchscreen: synaptics_dsx: Add secure touch support



Add secure touch support to the synaptics_dsx driver.
Expose the new interface through sysfs, to enable/disable
secure touch, and to wait for touch events. Also add
support for QUP ownership transfer to other subsystems to
support the secure touch usecase.

Change-Id: I9ff28dc193ab3c1eb7b425961ebc10a9cf75b8be
Signed-off-by: default avatarRitesh Kumar <riteshk@codeaurora.org>
parent 70fe4cf2
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -856,6 +856,17 @@ config TOUCHSCREEN_WM97XX_ZYLONITE
	  To compile this driver as a module, choose M here: the
	  module will be called zylonite-wm97xx.

config SECURE_TOUCH_SYNAPTICS_DSX
	bool "Secure Touch"
	depends on TOUCHSCREEN_SYNAPTICS_DSX_I2C
	help
	  Say Y here to enable Secure Touch in supported driver.

	  If unsure, say N.

	  To compile the supported driver with Secure Touch enabled,
	  choose M here.

config TOUCHSCREEN_USB_COMPOSITE
	tristate "USB Touchscreen Driver"
	depends on USB_ARCH_HAS_HCD
+270 −0
Original line number Diff line number Diff line
@@ -45,6 +45,16 @@
#ifdef KERNEL_ABOVE_2_6_38
#include <linux/input/mt.h>
#endif
#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
#include <linux/errno.h>
#include <soc/qcom/scm.h>
enum subsystem {
	TZ = 1,
	APSS = 3
};

#define TZ_BLSP_MODIFY_OWNERSHIP_ID 3
#endif

#include <linux/completion.h>

@@ -182,6 +192,19 @@ static ssize_t synaptics_rmi4_synad_pid_store(struct device *dev,
static ssize_t synaptics_rmi4_virtual_key_map_show(struct kobject *kobj,
		struct kobj_attribute *attr, char *buf);

static irqreturn_t synaptics_rmi4_irq(int irq, void *data);

#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
static ssize_t synaptics_rmi4_secure_touch_enable_show(struct device *dev,
		struct device_attribute *attr, char *buf);

static ssize_t synaptics_rmi4_secure_touch_enable_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count);

static ssize_t synaptics_rmi4_secure_touch_show(struct device *dev,
		struct device_attribute *attr, char *buf);
#endif

struct synaptics_rmi4_f01_device_status {
	union {
		struct {
@@ -705,6 +728,14 @@ static struct device_attribute attrs[] = {
	__ATTR(wake_gesture, 0664,
			synaptics_rmi4_wake_gesture_show,
			synaptics_rmi4_wake_gesture_store),
#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
	__ATTR(secure_touch_enable, 0664,
			synaptics_rmi4_secure_touch_enable_show,
			synaptics_rmi4_secure_touch_enable_store),
	__ATTR(secure_touch, 0444,
			synaptics_rmi4_secure_touch_show,
			NULL),
#endif
#ifdef USE_DATA_SERVER
	__ATTR(synad_pid, 0220,
			synaptics_rmi4_show_error,
@@ -720,6 +751,224 @@ static struct kobj_attribute virtual_key_map_attr = {
	.show = synaptics_rmi4_virtual_key_map_show,
};

#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
static int synaptics_rmi4_i2c_change_pipe_owner(
		struct synaptics_rmi4_data *rmi4_data, enum subsystem subsystem)
{
	struct scm_desc desc;
	struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
	int ret = 0;

	desc.arginfo = SCM_ARGS(2);
	desc.args[0] = i2c->adapter->nr;
	desc.args[1] = subsystem;

	ret = scm_call2(SCM_SIP_FNID(SCM_SVC_TZ, TZ_BLSP_MODIFY_OWNERSHIP_ID),
			&desc);
	if (ret) {
		dev_err(rmi4_data->pdev->dev.parent, "%s: failed\n",
				__func__);
		return ret;
	}

	return desc.ret[0];
}

static void synaptics_rmi4_secure_touch_init(struct synaptics_rmi4_data *data)
{
	data->st_initialized = false;
	init_completion(&data->st_powerdown);
	init_completion(&data->st_irq_processed);

	/* Get clocks */
	data->core_clk = devm_clk_get(data->pdev->dev.parent, "core_clk");
	if (IS_ERR(data->core_clk)) {
		dev_dbg(data->pdev->dev.parent,
				"%s: error on clk_get(core_clk): %ld\n",
				__func__, PTR_ERR(data->core_clk));
		data->core_clk = NULL;
	}

	data->iface_clk = devm_clk_get(data->pdev->dev.parent, "iface_clk");
	if (IS_ERR(data->iface_clk)) {
		dev_dbg(data->pdev->dev.parent,
				"%s: error on clk_get(iface_clk): %ld\n",
				__func__, PTR_ERR(data->iface_clk));
		data->iface_clk = NULL;
	}
	data->st_initialized = true;
}

static void synaptics_rmi4_secure_touch_notify(
		struct synaptics_rmi4_data *rmi4_data)
{
	sysfs_notify(&rmi4_data->input_dev->dev.kobj, NULL, "secure_touch");
}

static irqreturn_t synaptics_rmi4_filter_interrupt(
		struct synaptics_rmi4_data *rmi4_data)
{
	if (atomic_read(&rmi4_data->st_enabled)) {
		if (atomic_cmpxchg(&rmi4_data->st_pending_irqs, 0, 1) == 0) {
			reinit_completion(&rmi4_data->st_irq_processed);
			synaptics_rmi4_secure_touch_notify(rmi4_data);
			wait_for_completion_interruptible(
					&rmi4_data->st_irq_processed);
		}
		return IRQ_HANDLED;
	}
	return IRQ_NONE;
}

/*
 * 'blocking' variable will have value 'true' when we want to prevent the driver
 * from accessing the xPU/SMMU protected HW resources while the session is
 * active.
 */
static void synaptics_rmi4_secure_touch_stop(
		struct synaptics_rmi4_data *rmi4_data, bool blocking)
{
	if (atomic_read(&rmi4_data->st_enabled)) {
		atomic_set(&rmi4_data->st_pending_irqs, -1);
		synaptics_rmi4_secure_touch_notify(rmi4_data);
		if (blocking)
			wait_for_completion_interruptible(
					&rmi4_data->st_powerdown);
	}
}

#else
static void synaptics_rmi4_secure_touch_init(
		struct synaptics_rmi4_data *rmi4_data)
{
}

static irqreturn_t synaptics_rmi4_filter_interrupt(
		struct synaptics_rmi4_data *rmi4_data)
{
	return IRQ_NONE;
}

static void synaptics_rmi4_secure_touch_stop(
		struct synaptics_rmi4_data *rmi4_data, bool blocking)
{
}
#endif

#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
static ssize_t synaptics_rmi4_secure_touch_enable_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);

	return scnprintf(buf, PAGE_SIZE, "%d",
			atomic_read(&rmi4_data->st_enabled));
}

/*
 * Accept only "0" and "1" valid values.
 * "0" will reset the st_enabled flag, then wake up the reading process and
 * the interrupt handler.
 * The bus driver is notified via pm_runtime that it is not required to stay
 * awake anymore.
 * It will also make sure the queue of events is emptied in the controller,
 * in case a touch happened in between the secure touch being disabled and
 * the local ISR being ungated.
 * "1" will set the st_enabled flag and clear the st_pending_irqs flag.
 * The bus driver is requested via pm_runtime to stay awake.
 */
static ssize_t synaptics_rmi4_secure_touch_enable_store(struct device *dev,
		struct device_attribute *attr,
		const char *buf, size_t count)
{
	struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
	unsigned long value;
	int err = 0;

	if (count > 2)
		return -EINVAL;

	if (!rmi4_data->st_initialized)
		return -EIO;

	err = kstrtoul(buf, 10, &value);
	if (err)
		return err;

	err = count;

	switch (value) {
	case 0:
		if (atomic_read(&rmi4_data->st_enabled) == 0)
			break;
		synaptics_rmi4_i2c_change_pipe_owner(rmi4_data, APSS);
		synaptics_rmi4_bus_put(rmi4_data);
		atomic_set(&rmi4_data->st_enabled, 0);
		synaptics_rmi4_secure_touch_notify(rmi4_data);
		complete(&rmi4_data->st_irq_processed);
		synaptics_rmi4_irq(rmi4_data->irq, rmi4_data);
		complete(&rmi4_data->st_powerdown);

		break;
	case 1:
		if (atomic_read(&rmi4_data->st_enabled)) {
			err = -EBUSY;
			break;
		}
		synchronize_irq(rmi4_data->irq);

		if (synaptics_rmi4_bus_get(rmi4_data) < 0) {
			dev_err(rmi4_data->pdev->dev.parent,
					"synaptics_rmi4_bus_get failed\n");
			err = -EIO;
			break;
		}
		synaptics_rmi4_i2c_change_pipe_owner(rmi4_data, TZ);
		reinit_completion(&rmi4_data->st_powerdown);
		reinit_completion(&rmi4_data->st_irq_processed);
		atomic_set(&rmi4_data->st_enabled, 1);
		atomic_set(&rmi4_data->st_pending_irqs,  0);
		break;
	default:
		dev_err(rmi4_data->pdev->dev.parent,
				"unsupported value: %lu\n", value);
		err = -EINVAL;
		break;
	}
	return err;
}

/*
 * This function returns whether there are pending interrupts, or
 * other error conditions that need to be signaled to the userspace library,
 * according tot he following logic:
 * - st_enabled is 0 if secure touch is not enabled, returning -EBADF
 * - st_pending_irqs is -1 to signal that secure touch is in being stopped,
 *   returning -EINVAL
 * - st_pending_irqs is 1 to signal that there is a pending irq, returning
 *   the value "1" to the sysfs read operation
 * - st_pending_irqs is 0 (only remaining case left) if the pending interrupt
 *   has been processed, so the interrupt handler can be allowed to continue.
 */
static ssize_t synaptics_rmi4_secure_touch_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
	int val = 0;

	if (atomic_read(&rmi4_data->st_enabled) == 0)
		return -EBADF;
	if (atomic_cmpxchg(&rmi4_data->st_pending_irqs, -1, 0) == -1)
		return -EINVAL;
	if (atomic_cmpxchg(&rmi4_data->st_pending_irqs, 1, 0) == 1)
		val = 1;
	else
		complete(&rmi4_data->st_irq_processed);

	return scnprintf(buf, PAGE_SIZE, "%u", val);
}
#endif

static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
@@ -1768,6 +2017,9 @@ static irqreturn_t synaptics_rmi4_irq(int irq, void *data)
	const struct synaptics_dsx_board_data *bdata =
			rmi4_data->hw_if->board_data;

	if (synaptics_rmi4_filter_interrupt(data) == IRQ_HANDLED)
		return IRQ_HANDLED;

	if (gpio_get_value(bdata->irq_gpio) != bdata->irq_on_state)
		goto exit;

@@ -4291,6 +4543,11 @@ static int synaptics_rmi4_probe(struct platform_device *pdev)
			goto err_drm_reg;
		}
	}

	/* Initialize secure touch */
	synaptics_rmi4_secure_touch_init(rmi4_data);
	synaptics_rmi4_secure_touch_stop(rmi4_data, true);

	rmi4_data->rmi4_probe_wq = create_singlethread_workqueue(
						"Synaptics_rmi4_probe_wq");
	if (!rmi4_data->rmi4_probe_wq) {
@@ -4641,6 +4898,7 @@ static int synaptics_rmi4_dsi_panel_notifier_cb(struct notifier_block *self,

	if (evdata && evdata->data && rmi4_data) {
		if (event == DRM_PANEL_EARLY_EVENT_BLANK) {
			synaptics_rmi4_secure_touch_stop(rmi4_data, false);
			transition = *(int *)evdata->data;
			if (transition == DRM_PANEL_BLANK_POWERDOWN) {
				if (rmi4_data->initialized)
@@ -4679,6 +4937,13 @@ static int synaptics_rmi4_early_suspend(struct early_suspend *h)

	if (rmi4_data->stay_awake)
		return retval;
	/*
	 * During early suspend/late resume, the driver doesn't access xPU/SMMU
	 * protected HW resources. So, there is no compelling need to block,
	 * but notifying the userspace that a power event has occurred is
	 * enough. Hence 'blocking' variable can be set to false.
	 */
	synaptics_rmi4_secure_touch_stop(rmi4_data, false);

	if (rmi4_data->enable_wakeup_gesture) {
		if (rmi4_data->no_sleep_setting) {
@@ -4743,6 +5008,8 @@ static int synaptics_rmi4_late_resume(struct early_suspend *h)
	if (rmi4_data->stay_awake)
		return retval;

	synaptics_rmi4_secure_touch_stop(rmi4_data, false);

	if (rmi4_data->enable_wakeup_gesture) {
		disable_irq_wake(rmi4_data->irq);
		goto exit;
@@ -4791,6 +5058,8 @@ static int synaptics_rmi4_suspend(struct device *dev)
	if (rmi4_data->stay_awake)
		return 0;

	synaptics_rmi4_secure_touch_stop(rmi4_data, true);

	if (rmi4_data->enable_wakeup_gesture) {
		if (rmi4_data->no_sleep_setting) {
			synaptics_rmi4_reg_read(rmi4_data,
@@ -4862,6 +5131,7 @@ static int synaptics_rmi4_resume(struct device *dev)
			rmi4_data->hw_if->board_data;
	if (rmi4_data->stay_awake)
		return 0;
	synaptics_rmi4_secure_touch_stop(rmi4_data, true);

	if (rmi4_data->enable_wakeup_gesture) {
		disable_irq_wake(rmi4_data->irq);
+28 −0
Original line number Diff line number Diff line
@@ -51,6 +51,13 @@

#include <drm/drm_panel.h>

#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
#include <linux/completion.h>
#include <linux/atomic.h>
#include <linux/pm_runtime.h>
#include <linux/clk.h>
#endif

#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38))
#define KERNEL_ABOVE_2_6_38
#endif
@@ -431,6 +438,15 @@ struct synaptics_rmi4_data {
			bool enable);
	void (*report_touch)(struct synaptics_rmi4_data *rmi4_data,
			struct synaptics_rmi4_fn *fhandler);
#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
	atomic_t st_enabled;
	atomic_t st_pending_irqs;
	struct completion st_powerdown;
	struct completion st_irq_processed;
	bool st_initialized;
	struct clk *core_clk;
	struct clk *iface_clk;
#endif
};

struct synaptics_dsx_bus_access {
@@ -439,6 +455,8 @@ struct synaptics_dsx_bus_access {
		unsigned char *data, unsigned int length);
	int (*write)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr,
		unsigned char *data, unsigned int length);
	int (*get)(struct synaptics_rmi4_data *rmi4_data);
	void (*put)(struct synaptics_rmi4_data *rmi4_data);
};

struct synaptics_dsx_hw_interface {
@@ -489,6 +507,16 @@ static inline int synaptics_rmi4_reg_write(
	return rmi4_data->hw_if->bus_access->write(rmi4_data, addr, data, len);
}

static inline int synaptics_rmi4_bus_get(struct synaptics_rmi4_data *rmi4_data)
{
	return rmi4_data->hw_if->bus_access->get(rmi4_data);
}

static inline void synaptics_rmi4_bus_put(struct synaptics_rmi4_data *rmi4_data)
{
	rmi4_data->hw_if->bus_access->put(rmi4_data);
}

static inline ssize_t synaptics_rmi4_show_error(struct device *dev,
		struct device_attribute *attr, char *buf)
{
+63 −0
Original line number Diff line number Diff line
@@ -504,10 +504,73 @@ static int check_default_tp(struct device_node *dt, const char *prop)
	return ret;
}

#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
static int synaptics_rmi4_clk_prepare_enable(
		struct synaptics_rmi4_data *rmi4_data)
{
	int ret;

	ret = clk_prepare_enable(rmi4_data->iface_clk);
	if (ret) {
		dev_err(rmi4_data->pdev->dev.parent,
				"error on clk_prepare_enable(iface_clk):%d\n",
				ret);
		return ret;
	}
	ret = clk_prepare_enable(rmi4_data->core_clk);
	if (ret) {
		clk_disable_unprepare(rmi4_data->iface_clk);
		dev_err(rmi4_data->pdev->dev.parent,
				"error clk_prepare_enable(core_clk):%d\n", ret);
	}
	return ret;
}

static void synaptics_rmi4_clk_disable_unprepare(
		struct synaptics_rmi4_data *rmi4_data)
{
	clk_disable_unprepare(rmi4_data->core_clk);
	clk_disable_unprepare(rmi4_data->iface_clk);
}

static int synaptics_rmi4_i2c_get(struct synaptics_rmi4_data *rmi4_data)
{
	int retval;
	struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);

	mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
	retval = pm_runtime_get_sync(i2c->adapter->dev.parent);
	if (retval >= 0 && rmi4_data->core_clk != NULL &&
			rmi4_data->iface_clk != NULL) {
		retval = synaptics_rmi4_clk_prepare_enable(rmi4_data);
		if (retval)
			pm_runtime_put_sync(i2c->adapter->dev.parent);
	}
	mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);

	return retval;
}

static void synaptics_rmi4_i2c_put(struct synaptics_rmi4_data *rmi4_data)
{
	struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);

	mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
	if (rmi4_data->core_clk != NULL && rmi4_data->iface_clk != NULL)
		synaptics_rmi4_clk_disable_unprepare(rmi4_data);
	pm_runtime_put_sync(i2c->adapter->dev.parent);
	mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
}
#endif

static struct synaptics_dsx_bus_access bus_access = {
	.type = BUS_I2C,
	.read = synaptics_rmi4_i2c_read,
	.write = synaptics_rmi4_i2c_write,
#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
	.get = synaptics_rmi4_i2c_get,
	.put = synaptics_rmi4_i2c_put,
#endif
};

static void synaptics_rmi4_i2c_dev_release(struct device *dev)