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

Commit a0e10471 authored by Abinaya P's avatar Abinaya P
Browse files

misc: hbtp_input: add secure touch support for hbtp input driver



Add support for secure touch for hbtp input driver. For hbtp input
driver to work with the TrustZone Secure Touch, all the touch
interrupts must be forwarded to the TrustZone input driver.
Add APIs and configuration to support this.

CRs-Fixed: 1036946
Change-Id: Ia6be1cda88646a16f444899929d12d596674961f
Signed-off-by: default avatarShantanu Jain <shjain@codeaurora.org>
Signed-off-by: default avatarAbinaya P <abinayap@codeaurora.org>
parent ad8cf1bb
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -29,12 +29,21 @@ Optional properties if qcom,use-scale DT property is defined:
 - qcom,des-maxy	: desired Y-resolution of the touch panel.
			(Above two properties should be defined in pairs only)

Optional properties if secure touch is enabled for hbtp input driver
 - interrupt-parent	: Parent of interrupt
 - interrupts		: Touch sample interrupt to indicate presense or release
			  of fingers on the panel.
 - hbtp,irq-gpio	: GPIO used for interrupt.

Example:
	&soc {
		hbtp {
			compatible = "qcom,hbtp-input";
			vcc_ana-supply = <&pm8941_l17>;
			vcc_dig-supply = <&pm8950_l16>;
			interrupt-parent = <&tlmm>;
			interrupts = <65 0>;
			hbtp,irq-gpio = <&tlmm 65 0x00>;
			qcom,afe-load = <50000>;
			qcom,afe-vtg-min = <2850000>;
			qcom,afe-vtg-max = <2850000>;
+10 −0
Original line number Diff line number Diff line
@@ -104,6 +104,16 @@ config INPUT_HBTP_INPUT
	  To compile this driver as a module, choose M here: the
	  module will be called hbtp_input.

config HBTP_INPUT_SECURE_TOUCH
	bool "Secure Touch support for HBTP Input driver"
	depends on INPUT_HBTP_INPUT
	help
	  This option enables the Secure Touch functionality of the HBTP
	  Input driver.

	  Say Y to enable this functionality.
	  If unsure, say N.

config INPUT_PCSPKR
	tristate "PC Speaker support"
	depends on PCSPKR_PLATFORM
+294 −3
Original line number Diff line number Diff line
@@ -23,6 +23,11 @@
#include <linux/regulator/consumer.h>
#include <uapi/linux/hbtp_input.h>
#include "../input-compat.h"
#if defined(CONFIG_HBTP_INPUT_SECURE_TOUCH)
#include <linux/gpio.h>
#include <linux/interrupt.h>
#endif
#include <linux/of_gpio.h>

#if defined(CONFIG_FB)
#include <linux/notifier.h>
@@ -55,6 +60,17 @@ struct hbtp_data {
	int def_maxy;		/* Default Max Y */
	int des_maxx;		/* Desired Max X */
	int des_maxy;		/* Desired Max Y */
	int gpio_irq;
	u32 irq_gpio_flags;
#if defined(CONFIG_HBTP_INPUT_SECURE_TOUCH)
	int irq_num;
	atomic_t st_enabled;
	atomic_t st_pending_irqs;
	struct completion st_powerdown;
	struct completion st_irq_processed;
	struct completion st_userspace_task;
	bool st_initialized;
#endif
	bool use_scaling;
	bool override_disp_coords;
	bool manage_afe_power_ana;
@@ -63,6 +79,260 @@ struct hbtp_data {

static struct hbtp_data *hbtp;

#if defined(CONFIG_HBTP_INPUT_SECURE_TOUCH)
static irqreturn_t hbtp_filter_interrupt(int irq, void *context)
{
	if (atomic_read(&hbtp->st_enabled)) {
		if (atomic_cmpxchg(&hbtp->st_pending_irqs, 0, 1) == 0) {
			reinit_completion(&hbtp->st_irq_processed);
			sysfs_notify(&hbtp->pdev->dev.kobj, NULL,
							"secure_touch");
			wait_for_completion_interruptible(
					&hbtp->st_irq_processed);
		}
		return IRQ_HANDLED;
	}
	return IRQ_NONE;
}

static void hbtp_secure_touch_init(struct hbtp_data *hbtp)
{
	hbtp->st_initialized = 0;
	init_completion(&hbtp->st_powerdown);
	init_completion(&hbtp->st_irq_processed);
	init_completion(&hbtp->st_userspace_task);
	hbtp->st_initialized = 1;
}

/*
 * '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 hbtp_secure_touch_stop(struct hbtp_data *hbtp, bool blocking)
{
	if (atomic_read(&hbtp->st_enabled)) {
		atomic_set(&hbtp->st_pending_irqs, -1);
		sysfs_notify(&hbtp->pdev->dev.kobj, NULL, "secure_touch");
		if (blocking)
			wait_for_completion_interruptible(
					&hbtp->st_powerdown);
	}
	dev_dbg(hbtp->pdev->dev.parent, "Secure Touch session stopped\n");
}

static int hbtp_gpio_configure(struct hbtp_data *hbtp, bool on)
{
	int retval = 0;

	if (on) {
		if (gpio_is_valid(hbtp->gpio_irq)) {
			retval = gpio_request(hbtp->gpio_irq, "hbtp_irq");
			if (retval) {
				dev_err(hbtp->pdev->dev.parent,
					"unable to request GPIO [%d]\n",
					hbtp->gpio_irq);
				goto err_gpio_irq_req;
			}

			retval = gpio_direction_input(hbtp->gpio_irq);
			if (retval) {
				dev_err(hbtp->pdev->dev.parent,
					"unable to set dir for GPIO[%d]\n",
					hbtp->gpio_irq);
				goto err_gpio_irq_dir;
			}
		} else {
			dev_err(hbtp->pdev->dev.parent,
					"GPIO [%d] is not valid\n",
					hbtp->gpio_irq);
		}
	} else {
		if (gpio_is_valid(hbtp->gpio_irq))
			gpio_free(hbtp->gpio_irq);
	}

	return 0;

err_gpio_irq_dir:
	if (gpio_is_valid(hbtp->gpio_irq))
		gpio_free(hbtp->gpio_irq);
err_gpio_irq_req:
	return retval;
}

static ssize_t hbtp_secure_touch_enable_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	return scnprintf(buf, PAGE_SIZE, "%d", atomic_read(&hbtp->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 hbtp_secure_touch_enable_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	u8 value;
	int err = 0, retval;

	if (!hbtp->input_dev || !hbtp->st_initialized)
		return -EIO;

	if (count > 2)
		return -EINVAL;
	err = kstrtou8(buf, 10, &value);
	if (err != 0)
		return err;

	err = count;

	switch (value) {
	case 0:
		if (atomic_read(&hbtp->st_enabled) == 0)
			break;
		atomic_set(&hbtp->st_enabled, 0);
		complete(&hbtp->st_irq_processed);
		complete(&hbtp->st_powerdown);
		disable_irq(hbtp->irq_num);
		free_irq(hbtp->irq_num, hbtp);
		hbtp_gpio_configure(hbtp, false);
		reinit_completion(&hbtp->st_userspace_task);
		sysfs_notify(&hbtp->pdev->dev.kobj, NULL,
							"secure_touch_enable");
		wait_for_completion_interruptible(&hbtp->st_userspace_task);
		sysfs_notify(&hbtp->pdev->dev.kobj, NULL, "secure_touch");
		break;
	case 1:
		if (atomic_read(&hbtp->st_enabled)) {
			err = -EBUSY;
			break;
		}
		reinit_completion(&hbtp->st_powerdown);
		reinit_completion(&hbtp->st_irq_processed);
		reinit_completion(&hbtp->st_userspace_task);
		atomic_set(&hbtp->st_enabled, 1);
		sysfs_notify(&hbtp->pdev->dev.kobj, NULL,
							"secure_touch_enable");
		atomic_set(&hbtp->st_pending_irqs,  0);
		wait_for_completion_interruptible(&hbtp->st_userspace_task);
		retval = hbtp_gpio_configure(hbtp, true);
		if (retval)
			return retval;

		hbtp->irq_num = gpio_to_irq(hbtp->gpio_irq);
		retval = request_threaded_irq(hbtp->irq_num, NULL,
			hbtp_filter_interrupt,
			IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
			"hbtp", hbtp);
		if (retval < 0) {
			dev_err(hbtp->pdev->dev.parent,
				"%s: Failed to create irq thread ", __func__);
			return retval;
		}
		break;
	default:
		dev_err(hbtp->pdev->dev.parent,
			"unsupported value: %d\n", value);
		err = -EINVAL;
		break;
	}
	return err;
}

/*
 * 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 hbtp_secure_touch_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	int val = 0;

	if (atomic_read(&hbtp->st_enabled) == 0)
		return -EBADF;
	if (atomic_cmpxchg(&hbtp->st_pending_irqs, -1, 0) == -1)
		return -EINVAL;
	if (atomic_cmpxchg(&hbtp->st_pending_irqs, 1, 0) == 1)
		val = 1;
	else
		complete(&hbtp->st_irq_processed);
	return scnprintf(buf, PAGE_SIZE, "%u", val);
}

static ssize_t hbtp_secure_touch_userspace_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	u8 value;
	int err = 0;

	if (!hbtp->input_dev)
		return -EIO;

	if (count > 2)
		return -EINVAL;
	err = kstrtou8(buf, 10, &value);
	if (err != 0)
		return err;

	if (value == 0 || value == 1) {
		dev_dbg(hbtp->pdev->dev.parent,
			"%s: Userspace is %s\n", __func__,
			(value == 0) ? "started" : "stopped");
		complete(&hbtp->st_userspace_task);
	} else {
		dev_err(hbtp->pdev->dev.parent,
			"%s: Wrong value sent\n", __func__);
	}

	return count;
}

static DEVICE_ATTR(secure_touch_enable, S_IRUGO | S_IWUSR | S_IWGRP,
		hbtp_secure_touch_enable_show, hbtp_secure_touch_enable_store);
static DEVICE_ATTR(secure_touch, S_IRUGO, hbtp_secure_touch_show, NULL);
static DEVICE_ATTR(secure_touch_userspace, S_IRUGO | S_IWUSR | S_IWGRP,
		hbtp_secure_touch_enable_show,
		hbtp_secure_touch_userspace_store);
#else
static void hbtp_secure_touch_init(struct hbtp_data *hbtp)
{
}
static void hbtp_secure_touch_stop(struct hbtp_data *data, bool blocking)
{
}
#endif

static struct attribute *secure_touch_attrs[] = {
#if defined(CONFIG_HBTP_INPUT_SECURE_TOUCH)
	&dev_attr_secure_touch_enable.attr,
	&dev_attr_secure_touch.attr,
	&dev_attr_secure_touch_userspace.attr,
	NULL
#endif
};

static const struct attribute_group secure_touch_attr_group = {
	.attrs = secure_touch_attrs,
};

#if defined(CONFIG_FB)
static int fb_notifier_callback(struct notifier_block *self,
				 unsigned long event, void *data)
@@ -79,10 +349,12 @@ static int fb_notifier_callback(struct notifier_block *self,
		if (blank == FB_BLANK_UNBLANK)
			kobject_uevent_env(&hbtp_data->input_dev->dev.kobj,
					KOBJ_ONLINE, envp);
		else if (blank == FB_BLANK_POWERDOWN)
		else if (blank == FB_BLANK_POWERDOWN) {
			hbtp_secure_touch_stop(hbtp, true);
			kobject_uevent_env(&hbtp_data->input_dev->dev.kobj,
					KOBJ_OFFLINE, envp);
		}
	}

	return 0;
}
@@ -120,8 +392,7 @@ static int hbtp_input_create_input_dev(struct hbtp_input_absinfo *absinfo)
{
	struct input_dev *input_dev;
	struct hbtp_input_absinfo *abs;
	int error;
	int i;
	int i, error;

	input_dev = input_allocate_device();
	if (!input_dev) {
@@ -538,6 +809,9 @@ static int hbtp_parse_dt(struct device *dev)
		hbtp->override_disp_coords = true;
	}

	hbtp->gpio_irq = of_get_named_gpio_flags(np,
				"hbtp,irq-gpio", 0, &hbtp->irq_gpio_flags);

	hbtp->use_scaling = of_property_read_bool(np, "qcom,use-scale");
	if (hbtp->use_scaling) {
		rc = of_property_read_u32(np, "qcom,default-max-x", &temp_val);
@@ -663,12 +937,29 @@ static int hbtp_pdev_probe(struct platform_device *pdev)
	}

	hbtp->pdev = pdev;
	error = sysfs_create_group(&hbtp->pdev->dev.kobj,
					&secure_touch_attr_group);
	if (error) {
		dev_err(&pdev->dev, "Failed to create sysfs entries\n");
		goto err_sysfs_create_group;
	}

	hbtp_secure_touch_init(hbtp);
	hbtp_secure_touch_stop(hbtp, true);

	return 0;

err_sysfs_create_group:
	if (hbtp->manage_power_dig)
		regulator_put(vcc_dig);
	if (hbtp->manage_afe_power_ana)
		regulator_put(vcc_ana);
	return error;
}

static int hbtp_pdev_remove(struct platform_device *pdev)
{
	sysfs_remove_group(&hbtp->pdev->dev.kobj, &secure_touch_attr_group);
	if (hbtp->vcc_ana || hbtp->vcc_dig) {
		hbtp_pdev_power_on(hbtp, false);
		if (hbtp->vcc_ana)