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 * Copyright (C) 2012 Scott Lin - * + * 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 #endif +#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26) +#include +#include +#include +#include +#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 * Copyright (C) 2012 Scott Lin - * + * 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 * Copyright (C) 2012 Scott Lin + * 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;