summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2016-08-26 22:22:51 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2016-08-26 22:22:50 -0700
commit9a7c65cabed11146b6522bcf13574eb061e58a64 (patch)
tree34b4edcf97c32e855dae05f9c4154b134771b3e1
parentf6b34d14bd1cae77540cb285f83c92324c101d2d (diff)
parent29e0ce3c544d39b5f438a79f7949b55c59aeaece (diff)
Merge "input: synaptics_dsx_2.6: fix issues raised by static analyzer"
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt7
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig10
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c341
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h34
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c70
-rw-r--r--include/linux/input/synaptics_dsx_v2_6.h4
6 files changed, 449 insertions, 17 deletions
diff --git a/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt b/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt
index 7dece8e06240..c10fd981df2b 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt
@@ -25,6 +25,9 @@ Optional property:
- synaptics,y-flip : modify orientation of the y axis.
- synaptics,reset-delay-ms : reset delay for controller (ms), default 100.
- synaptics,max-y-for-2d : maximal y value of the panel.
+ - clock-names : Clock names used for secure touch. They are: "iface_clk", "core_clk"
+ - clocks : Defined if 'clock-names' DT property is defined. These clocks
+ are associated with the underlying I2C bus.
Example:
i2c@78b7000 {
@@ -46,5 +49,9 @@ Example:
synaptics,max-y-for-2d = <1919>; /* remove if no virtual buttons */
synaptics,cap-button-codes = <139 172 158>;
synaptics,vir-button-codes = <139 180 2000 320 160 172 540 2000 320 160 158 900 2000 320 160>;
+ /* Underlying clocks used by secure touch */
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>,
+ <&clock_gcc clk_gcc_blsp1_qup3_i2c_apps_clk>;
};
};
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig b/drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig
index 78b995ec7c8a..53896288ba77 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig
@@ -114,4 +114,14 @@ config TOUCHSCREEN_SYNAPTICS_DSX_VIDEO_v26
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_video.
+config SECURE_TOUCH_SYNAPTICS_DSX_V26
+ bool "Secure Touch support for Synaptics V2.6 Touchscreen"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_I2C_v26
+ help
+ Say Y here
+ -Synaptics DSX V2.6 touch driver is connected
+ -To enable secure touch for Synaptics DSX V2.6 touch driver
+
+ If unsure, say N.
+
endif
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c
index 07d2c479aa39..a0303ec3c26e 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c
@@ -122,6 +122,7 @@ static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data,
bool rebuild);
#ifdef CONFIG_FB
+static void synaptics_rmi4_fb_notify_resume_work(struct work_struct *work);
static int synaptics_rmi4_fb_notifier_cb(struct notifier_block *self,
unsigned long event, void *data);
#endif
@@ -172,6 +173,19 @@ static ssize_t synaptics_rmi4_wake_gesture_store(struct device *dev,
static ssize_t synaptics_rmi4_virtual_key_map_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf);
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+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
+
+static irqreturn_t synaptics_rmi4_irq(int irq, void *data);
+
struct synaptics_rmi4_f01_device_status {
union {
struct {
@@ -617,6 +631,14 @@ static struct device_attribute attrs[] = {
__ATTR(wake_gesture, (S_IRUGO | S_IWUSR | S_IWGRP),
synaptics_rmi4_wake_gesture_show,
synaptics_rmi4_wake_gesture_store),
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+ __ATTR(secure_touch_enable, (S_IRUGO | S_IWUSR | S_IWGRP),
+ synaptics_rmi4_secure_touch_enable_show,
+ synaptics_rmi4_secure_touch_enable_store),
+ __ATTR(secure_touch, S_IRUGO,
+ synaptics_rmi4_secure_touch_show,
+ NULL),
+#endif
};
static struct kobj_attribute virtual_key_map_attr = {
@@ -627,6 +649,210 @@ static struct kobj_attribute virtual_key_map_attr = {
.show = synaptics_rmi4_virtual_key_map_show,
};
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+static void synaptics_secure_touch_init(struct synaptics_rmi4_data *data)
+{
+ int ret = 0;
+
+ data->st_initialized = 0;
+ 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)) {
+ ret = PTR_ERR(data->core_clk);
+ data->core_clk = NULL;
+ dev_warn(data->pdev->dev.parent,
+ "%s: error on clk_get(core_clk): %d\n", __func__, ret);
+ return;
+ }
+
+ data->iface_clk = devm_clk_get(data->pdev->dev.parent, "iface_clk");
+ if (IS_ERR(data->iface_clk)) {
+ ret = PTR_ERR(data->iface_clk);
+ data->iface_clk = NULL;
+ dev_warn(data->pdev->dev.parent,
+ "%s: error on clk_get(iface_clk): %d\n", __func__, ret);
+ return;
+ }
+
+ data->st_initialized = 1;
+ return;
+}
+
+static void synaptics_secure_touch_notify(struct synaptics_rmi4_data *rmi4_data)
+{
+ sysfs_notify(&rmi4_data->input_dev->dev.kobj, NULL, "secure_touch");
+}
+
+static irqreturn_t synaptics_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_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_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_secure_touch_notify(rmi4_data);
+ if (blocking)
+ wait_for_completion_interruptible(
+ &rmi4_data->st_powerdown);
+ }
+}
+
+#else
+static void synaptics_secure_touch_init(struct synaptics_rmi4_data *rmi4_data)
+{
+}
+
+static irqreturn_t synaptics_filter_interrupt(
+ struct synaptics_rmi4_data *rmi4_data)
+{
+ return IRQ_NONE;
+}
+
+static void synaptics_secure_touch_stop(struct synaptics_rmi4_data *rmi4_data,
+ bool blocking)
+{
+}
+#endif
+
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+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;
+
+ err = kstrtoul(buf, 10, &value);
+ if (err != 0)
+ return err;
+
+ if (!rmi4_data->st_initialized)
+ return -EIO;
+
+ err = count;
+
+ switch (value) {
+ case 0:
+ if (atomic_read(&rmi4_data->st_enabled) == 0)
+ break;
+
+ synaptics_rmi4_bus_put(rmi4_data);
+ atomic_set(&rmi4_data->st_enabled, 0);
+ synaptics_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;
+ }
+ 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)
{
@@ -1173,7 +1399,6 @@ static int synaptics_rmi4_f12_abs_report(struct synaptics_rmi4_data *rmi4_data,
#ifndef TYPE_B_PROTOCOL
input_mt_sync(rmi4_data->input_dev);
#endif
- input_sync(rmi4_data->input_dev);
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Finger %d: status = 0x%02x, x = %d, y = %d, wx = %d, wy = %d\n",
@@ -1245,7 +1470,6 @@ static int synaptics_rmi4_f12_abs_report(struct synaptics_rmi4_data *rmi4_data,
#ifndef TYPE_B_PROTOCOL
input_mt_sync(rmi4_data->input_dev);
#endif
- input_sync(rmi4_data->input_dev);
if (rmi4_data->stylus_enable) {
stylus_presence = 0;
@@ -1261,6 +1485,8 @@ static int synaptics_rmi4_f12_abs_report(struct synaptics_rmi4_data *rmi4_data,
}
}
+ input_sync(rmi4_data->input_dev);
+
mutex_unlock(&(rmi4_data->rmi4_report_mutex));
return touch_count;
@@ -1512,6 +1738,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_filter_interrupt(data) == IRQ_HANDLED)
+ return IRQ_HANDLED;
+
if (gpio_get_value(bdata->irq_gpio) != bdata->irq_on_state)
goto exit;
@@ -2911,7 +3140,9 @@ static int synaptics_rmi4_gpio_setup(int gpio, bool config, int dir, int state)
unsigned char buf[16];
if (config) {
- snprintf(buf, PAGE_SIZE, "dsx_gpio_%u\n", gpio);
+ retval = snprintf(buf, ARRAY_SIZE(buf), "dsx_gpio_%u\n", gpio);
+ if (retval >= 16)
+ return -EINVAL;
retval = gpio_request(gpio, buf);
if (retval) {
@@ -3784,6 +4015,8 @@ static int synaptics_rmi4_probe(struct platform_device *pdev)
}
#ifdef CONFIG_FB
+ INIT_WORK(&rmi4_data->fb_notify_work,
+ synaptics_rmi4_fb_notify_resume_work);
rmi4_data->fb_notifier.notifier_call = synaptics_rmi4_fb_notifier_cb;
retval = fb_register_client(&rmi4_data->fb_notifier);
if (retval < 0) {
@@ -3849,25 +4082,52 @@ static int synaptics_rmi4_probe(struct platform_device *pdev)
rmi4_data->rb_workqueue =
create_singlethread_workqueue("dsx_rebuild_workqueue");
+ if (!rmi4_data->rb_workqueue) {
+ retval = -ENOMEM;
+ goto err_rb_workqueue;
+ }
INIT_DELAYED_WORK(&rmi4_data->rb_work, synaptics_rmi4_rebuild_work);
exp_data.workqueue = create_singlethread_workqueue("dsx_exp_workqueue");
+ if (!exp_data.workqueue) {
+ retval = -ENOMEM;
+ goto err_exp_data_workqueue;
+ }
INIT_DELAYED_WORK(&exp_data.work, synaptics_rmi4_exp_fn_work);
exp_data.rmi4_data = rmi4_data;
exp_data.queue_work = true;
- queue_delayed_work(exp_data.workqueue,
- &exp_data.work,
- 0);
+ queue_delayed_work(exp_data.workqueue, &exp_data.work, 0);
#ifdef FB_READY_RESET
rmi4_data->reset_workqueue =
create_singlethread_workqueue("dsx_reset_workqueue");
+ if (!rmi4_data->reset_workqueue) {
+ retval = -ENOMEM;
+ goto err_reset_workqueue;
+ }
INIT_WORK(&rmi4_data->reset_work, synaptics_rmi4_reset_work);
queue_work(rmi4_data->reset_workqueue, &rmi4_data->reset_work);
#endif
+ /* Initialize secure touch */
+ synaptics_secure_touch_init(rmi4_data);
+ synaptics_secure_touch_stop(rmi4_data, true);
+
return retval;
+#ifdef FB_READY_RESET
+err_reset_workqueue:
+#endif
+ cancel_delayed_work_sync(&exp_data.work);
+ flush_workqueue(exp_data.workqueue);
+ destroy_workqueue(exp_data.workqueue);
+
+err_exp_data_workqueue:
+ cancel_delayed_work_sync(&rmi4_data->rb_work);
+ flush_workqueue(rmi4_data->rb_workqueue);
+ destroy_workqueue(rmi4_data->rb_workqueue);
+
+err_rb_workqueue:
err_sysfs:
for (attr_count--; attr_count >= 0; attr_count--) {
sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
@@ -4096,6 +4356,14 @@ static void synaptics_rmi4_wakeup_gesture(struct synaptics_rmi4_data *rmi4_data,
}
#ifdef CONFIG_FB
+static void synaptics_rmi4_fb_notify_resume_work(struct work_struct *work)
+{
+ struct synaptics_rmi4_data *rmi4_data =
+ container_of(work, struct synaptics_rmi4_data, fb_notify_work);
+ synaptics_rmi4_resume(&(rmi4_data->input_dev->dev));
+ rmi4_data->fb_ready = true;
+}
+
static int synaptics_rmi4_fb_notifier_cb(struct notifier_block *self,
unsigned long event, void *data)
{
@@ -4106,14 +4374,36 @@ static int synaptics_rmi4_fb_notifier_cb(struct notifier_block *self,
fb_notifier);
if (evdata && evdata->data && rmi4_data) {
- if (event == FB_EVENT_BLANK) {
- transition = evdata->data;
- if (*transition == FB_BLANK_POWERDOWN) {
- synaptics_rmi4_suspend(&rmi4_data->pdev->dev);
- rmi4_data->fb_ready = false;
- } else if (*transition == FB_BLANK_UNBLANK) {
- synaptics_rmi4_resume(&rmi4_data->pdev->dev);
- rmi4_data->fb_ready = true;
+ if (rmi4_data->hw_if->board_data->resume_in_workqueue) {
+ if (event == FB_EARLY_EVENT_BLANK) {
+ synaptics_secure_touch_stop(rmi4_data, false);
+ } else if (event == FB_EVENT_BLANK) {
+ transition = evdata->data;
+ if (*transition == FB_BLANK_POWERDOWN) {
+ flush_work(
+ &(rmi4_data->fb_notify_work));
+ synaptics_rmi4_suspend(
+ &rmi4_data->pdev->dev);
+ rmi4_data->fb_ready = false;
+ } else if (*transition == FB_BLANK_UNBLANK) {
+ schedule_work(
+ &(rmi4_data->fb_notify_work));
+ }
+ }
+ } else {
+ if (event == FB_EARLY_EVENT_BLANK) {
+ synaptics_secure_touch_stop(rmi4_data, false);
+ } else if (event == FB_EVENT_BLANK) {
+ transition = evdata->data;
+ if (*transition == FB_BLANK_POWERDOWN) {
+ synaptics_rmi4_suspend(
+ &rmi4_data->pdev->dev);
+ rmi4_data->fb_ready = false;
+ } else if (*transition == FB_BLANK_UNBLANK) {
+ synaptics_rmi4_resume(
+ &rmi4_data->pdev->dev);
+ rmi4_data->fb_ready = true;
+ }
}
}
}
@@ -4133,6 +4423,14 @@ static void synaptics_rmi4_early_suspend(struct early_suspend *h)
if (rmi4_data->stay_awake)
return;
+ /*
+ * 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_secure_touch_stop(rmi4_data, false);
+
if (rmi4_data->enable_wakeup_gesture) {
synaptics_rmi4_wakeup_gesture(rmi4_data, true);
enable_irq_wake(rmi4_data->irq);
@@ -4170,6 +4468,8 @@ static void synaptics_rmi4_late_resume(struct early_suspend *h)
if (rmi4_data->stay_awake)
return;
+ synaptics_secure_touch_stop(rmi4_data, false);
+
if (rmi4_data->enable_wakeup_gesture) {
synaptics_rmi4_wakeup_gesture(rmi4_data, false);
disable_irq_wake(rmi4_data->irq);
@@ -4216,6 +4516,8 @@ static int synaptics_rmi4_suspend(struct device *dev)
if (rmi4_data->stay_awake)
return 0;
+ synaptics_secure_touch_stop(rmi4_data, true);
+
if (rmi4_data->enable_wakeup_gesture) {
synaptics_rmi4_wakeup_gesture(rmi4_data, true);
enable_irq_wake(rmi4_data->irq);
@@ -4237,6 +4539,10 @@ exit:
}
mutex_unlock(&exp_data.mutex);
+ if (!rmi4_data->suspend) {
+ synaptics_rmi4_enable_reg(rmi4_data, false);
+ synaptics_rmi4_get_reg(rmi4_data, false);
+ }
rmi4_data->suspend = true;
return 0;
@@ -4253,6 +4559,8 @@ static int synaptics_rmi4_resume(struct device *dev)
if (rmi4_data->stay_awake)
return 0;
+ synaptics_secure_touch_stop(rmi4_data, true);
+
if (rmi4_data->enable_wakeup_gesture) {
synaptics_rmi4_wakeup_gesture(rmi4_data, false);
disable_irq_wake(rmi4_data->irq);
@@ -4261,6 +4569,11 @@ static int synaptics_rmi4_resume(struct device *dev)
rmi4_data->current_page = MASK_8BIT;
+ if (rmi4_data->suspend) {
+ synaptics_rmi4_get_reg(rmi4_data, true);
+ synaptics_rmi4_enable_reg(rmi4_data, true);
+ }
+
synaptics_rmi4_sleep_enable(rmi4_data, false);
synaptics_rmi4_irq_enable(rmi4_data, true, false);
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h
index 0de0e9905cb4..b860a553b334 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h
@@ -5,7 +5,7 @@
*
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
- *
+ * Copyright (C) 2016 The Linux Foundation. All rights reserved.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -48,6 +48,13 @@
#include <linux/earlysuspend.h>
#endif
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+#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
@@ -324,6 +331,7 @@ struct synaptics_rmi4_data {
struct delayed_work rb_work;
struct workqueue_struct *rb_workqueue;
#ifdef CONFIG_FB
+ struct work_struct fb_notify_work;
struct notifier_block fb_notifier;
struct work_struct reset_work;
struct workqueue_struct *reset_workqueue;
@@ -374,6 +382,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_V26)
+ 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 {
@@ -382,6 +399,10 @@ struct synaptics_dsx_bus_access {
unsigned char *data, unsigned short length);
int (*write)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr,
unsigned char *data, unsigned short length);
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+ int (*get)(struct synaptics_rmi4_data *rmi4_data);
+ void (*put)(struct synaptics_rmi4_data *rmi4_data);
+#endif
};
struct synaptics_dsx_hw_interface {
@@ -432,6 +453,17 @@ static inline int synaptics_rmi4_reg_write(
return rmi4_data->hw_if->bus_access->write(rmi4_data, addr, data, len);
}
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+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);
+}
+#endif
+
static inline ssize_t synaptics_rmi4_show_error(struct device *dev,
struct device_attribute *attr, char *buf)
{
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c
index 784de607aa08..f936f3c2ebb1 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c
@@ -5,7 +5,7 @@
*
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
- *
+ * Copyright (C) 2016, The Linux Foundation. All rights reserved.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -78,6 +78,9 @@ static int parse_dt(struct device *dev, struct synaptics_dsx_board_data *bdata)
else
bdata->irq_on_state = value;
+ bdata->resume_in_workqueue = of_property_read_bool(np,
+ "synaptics,resume-in-workqueue");
+
retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name);
if (retval < 0)
bdata->pwr_reg_name = NULL;
@@ -288,7 +291,7 @@ static void synaptics_rmi4_i2c_check_addr(struct synaptics_rmi4_data *rmi4_data,
static int synaptics_rmi4_i2c_set_page(struct synaptics_rmi4_data *rmi4_data,
unsigned short addr)
{
- int retval;
+ int retval = 0;
unsigned char retry;
unsigned char buf[PAGE_SELECT_LEN];
unsigned char page;
@@ -497,10 +500,73 @@ exit:
return retval;
}
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+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_V26)
+ .get = synaptics_rmi4_i2c_get,
+ .put = synaptics_rmi4_i2c_put,
+#endif
};
static void synaptics_rmi4_i2c_dev_release(struct device *dev)
diff --git a/include/linux/input/synaptics_dsx_v2_6.h b/include/linux/input/synaptics_dsx_v2_6.h
index 2b91bc043f6f..5d4bbedb5f1a 100644
--- a/include/linux/input/synaptics_dsx_v2_6.h
+++ b/include/linux/input/synaptics_dsx_v2_6.h
@@ -5,6 +5,7 @@
*
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -57,6 +58,7 @@ struct synaptics_dsx_button_map {
* @x_flip: x flip flag
* @y_flip: y flip flag
* @swap_axes: swap axes flag
+ * @resume_in_workqueue: defer resume function to workqueue
* @irq_gpio: attention interrupt GPIO
* @irq_on_state: attention interrupt active state
* @power_gpio: power switch GPIO
@@ -79,11 +81,13 @@ struct synaptics_dsx_button_map {
* @bus_reg_name: pointer to name of regulator for bus pullup control
* @cap_button_map: pointer to 0D button map
* @vir_button_map: pointer to virtual button map
+ * @resume_in_workqueue: defer resume function to workqueue
*/
struct synaptics_dsx_board_data {
bool x_flip;
bool y_flip;
bool swap_axes;
+ bool resume_in_workqueue;
int irq_gpio;
int irq_on_state;
int power_gpio;