diff options
| author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2015-09-11 12:02:36 -0400 |
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2015-09-11 12:02:36 -0400 |
| commit | 53431d0a3534263168c8ee576bad1c2c3a4cd9e0 (patch) | |
| tree | 84b3dda2e2409d1eda7c2273053c78cd6aa3a677 | |
| parent | 01b944fe1cd4e21a2a9ed51adbdbafe2d5e905ba (diff) | |
| parent | ade9c1a47c811a7ae1c874882ad9178af1ed1098 (diff) | |
Merge branch 'next' into for-linus
Prepare second round of input updates for 4.3 merge window.
| -rw-r--r-- | Documentation/devicetree/bindings/input/touchscreen/colibri-vf50-ts.txt | 36 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/input/touchscreen/imx6ul_tsc.txt | 36 | ||||
| -rw-r--r-- | drivers/input/evdev.c | 13 | ||||
| -rw-r--r-- | drivers/input/keyboard/imx_keypad.c | 2 | ||||
| -rw-r--r-- | drivers/input/misc/ab8500-ponkey.c | 1 | ||||
| -rw-r--r-- | drivers/input/misc/pwm-beeper.c | 1 | ||||
| -rw-r--r-- | drivers/input/misc/regulator-haptic.c | 1 | ||||
| -rw-r--r-- | drivers/input/misc/sparcspkr.c | 2 | ||||
| -rw-r--r-- | drivers/input/mouse/elan_i2c_core.c | 1 | ||||
| -rw-r--r-- | drivers/input/serio/i8042.c | 2 | ||||
| -rw-r--r-- | drivers/input/touchscreen/Kconfig | 24 | ||||
| -rw-r--r-- | drivers/input/touchscreen/Makefile | 2 | ||||
| -rw-r--r-- | drivers/input/touchscreen/colibri-vf50-ts.c | 386 | ||||
| -rw-r--r-- | drivers/input/touchscreen/cyttsp4_i2c.c | 1 | ||||
| -rw-r--r-- | drivers/input/touchscreen/cyttsp_i2c.c | 1 | ||||
| -rw-r--r-- | drivers/input/touchscreen/elants_i2c.c | 2 | ||||
| -rw-r--r-- | drivers/input/touchscreen/imx6ul_tsc.c | 523 |
17 files changed, 1019 insertions, 15 deletions
diff --git a/Documentation/devicetree/bindings/input/touchscreen/colibri-vf50-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/colibri-vf50-ts.txt new file mode 100644 index 000000000000..9d9e930f3251 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/colibri-vf50-ts.txt | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | * Toradex Colibri VF50 Touchscreen driver | ||
| 2 | |||
| 3 | Required Properties: | ||
| 4 | - compatible must be toradex,vf50-touchscreen | ||
| 5 | - io-channels: adc channels being used by the Colibri VF50 module | ||
| 6 | - xp-gpios: FET gate driver for input of X+ | ||
| 7 | - xm-gpios: FET gate driver for input of X- | ||
| 8 | - yp-gpios: FET gate driver for input of Y+ | ||
| 9 | - ym-gpios: FET gate driver for input of Y- | ||
| 10 | - interrupt-parent: phandle for the interrupt controller | ||
| 11 | - interrupts: pen irq interrupt for touch detection | ||
| 12 | - pinctrl-names: "idle", "default", "gpios" | ||
| 13 | - pinctrl-0: pinctrl node for pen/touch detection state pinmux | ||
| 14 | - pinctrl-1: pinctrl node for X/Y and pressure measurement (ADC) state pinmux | ||
| 15 | - pinctrl-2: pinctrl node for gpios functioning as FET gate drivers | ||
| 16 | - vf50-ts-min-pressure: pressure level at which to stop measuring X/Y values | ||
| 17 | |||
| 18 | Example: | ||
| 19 | |||
| 20 | touchctrl: vf50_touchctrl { | ||
| 21 | compatible = "toradex,vf50-touchscreen"; | ||
| 22 | io-channels = <&adc1 0>,<&adc0 0>, | ||
| 23 | <&adc0 1>,<&adc1 2>; | ||
| 24 | xp-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; | ||
| 25 | xm-gpios = <&gpio2 29 GPIO_ACTIVE_HIGH>; | ||
| 26 | yp-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>; | ||
| 27 | ym-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>; | ||
| 28 | interrupt-parent = <&gpio0>; | ||
| 29 | interrupts = <8 IRQ_TYPE_LEVEL_LOW>; | ||
| 30 | pinctrl-names = "idle","default","gpios"; | ||
| 31 | pinctrl-0 = <&pinctrl_touchctrl_idle>; | ||
| 32 | pinctrl-1 = <&pinctrl_touchctrl_default>; | ||
| 33 | pinctrl-2 = <&pinctrl_touchctrl_gpios>; | ||
| 34 | vf50-ts-min-pressure = <200>; | ||
| 35 | status = "disabled"; | ||
| 36 | }; | ||
diff --git a/Documentation/devicetree/bindings/input/touchscreen/imx6ul_tsc.txt b/Documentation/devicetree/bindings/input/touchscreen/imx6ul_tsc.txt new file mode 100644 index 000000000000..853dff96dd9f --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/imx6ul_tsc.txt | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | * Freescale i.MX6UL Touch Controller | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible: must be "fsl,imx6ul-tsc". | ||
| 5 | - reg: this touch controller address and the ADC2 address. | ||
| 6 | - interrupts: the interrupt of this touch controller and ADC2. | ||
| 7 | - clocks: the root clock of touch controller and ADC2. | ||
| 8 | - clock-names; must be "tsc" and "adc". | ||
| 9 | - xnur-gpio: the X- gpio this controller connect to. | ||
| 10 | This xnur-gpio returns to low once the finger leave the touch screen (The | ||
| 11 | last touch event the touch controller capture). | ||
| 12 | |||
| 13 | Optional properties: | ||
| 14 | - measure-delay-time: the value of measure delay time. | ||
| 15 | Before X-axis or Y-axis measurement, the screen need some time before | ||
| 16 | even potential distribution ready. | ||
| 17 | This value depends on the touch screen. | ||
| 18 | - pre-charge-time: the touch screen need some time to precharge. | ||
| 19 | This value depends on the touch screen. | ||
| 20 | |||
| 21 | Example: | ||
| 22 | tsc: tsc@02040000 { | ||
| 23 | compatible = "fsl,imx6ul-tsc"; | ||
| 24 | reg = <0x02040000 0x4000>, <0x0219c000 0x4000>; | ||
| 25 | interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>, | ||
| 26 | <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>; | ||
| 27 | clocks = <&clks IMX6UL_CLK_IPG>, | ||
| 28 | <&clks IMX6UL_CLK_ADC2>; | ||
| 29 | clock-names = "tsc", "adc"; | ||
| 30 | pinctrl-names = "default"; | ||
| 31 | pinctrl-0 = <&pinctrl_tsc>; | ||
| 32 | xnur-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; | ||
| 33 | measure-delay-time = <0xfff>; | ||
| 34 | pre-charge-time = <0xffff>; | ||
| 35 | status = "okay"; | ||
| 36 | }; | ||
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 9d35499faca4..08d496411f75 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c | |||
| @@ -290,19 +290,14 @@ static int evdev_flush(struct file *file, fl_owner_t id) | |||
| 290 | { | 290 | { |
| 291 | struct evdev_client *client = file->private_data; | 291 | struct evdev_client *client = file->private_data; |
| 292 | struct evdev *evdev = client->evdev; | 292 | struct evdev *evdev = client->evdev; |
| 293 | int retval; | ||
| 294 | 293 | ||
| 295 | retval = mutex_lock_interruptible(&evdev->mutex); | 294 | mutex_lock(&evdev->mutex); |
| 296 | if (retval) | ||
| 297 | return retval; | ||
| 298 | 295 | ||
| 299 | if (!evdev->exist || client->revoked) | 296 | if (evdev->exist && !client->revoked) |
| 300 | retval = -ENODEV; | 297 | input_flush_device(&evdev->handle, file); |
| 301 | else | ||
| 302 | retval = input_flush_device(&evdev->handle, file); | ||
| 303 | 298 | ||
| 304 | mutex_unlock(&evdev->mutex); | 299 | mutex_unlock(&evdev->mutex); |
| 305 | return retval; | 300 | return 0; |
| 306 | } | 301 | } |
| 307 | 302 | ||
| 308 | static void evdev_free(struct device *dev) | 303 | static void evdev_free(struct device *dev) |
diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index d2ea863d6a45..2165f3dd328b 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c | |||
| @@ -5,8 +5,6 @@ | |||
| 5 | * This program is free software; you can redistribute it and/or modify | 5 | * This program is free software; you can redistribute it and/or modify |
| 6 | * it under the terms of the GNU General Public License version 2 as | 6 | * it under the terms of the GNU General Public License version 2 as |
| 7 | * published by the Free Software Foundation. | 7 | * published by the Free Software Foundation. |
| 8 | * | ||
| 9 | * <<Power management needs to be implemented>>. | ||
| 10 | */ | 8 | */ |
| 11 | 9 | ||
| 12 | #include <linux/clk.h> | 10 | #include <linux/clk.h> |
diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index 1f7e15ca5fbe..4f5ef5bb535b 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c | |||
| @@ -118,6 +118,7 @@ static const struct of_device_id ab8500_ponkey_match[] = { | |||
| 118 | { .compatible = "stericsson,ab8500-ponkey", }, | 118 | { .compatible = "stericsson,ab8500-ponkey", }, |
| 119 | {} | 119 | {} |
| 120 | }; | 120 | }; |
| 121 | MODULE_DEVICE_TABLE(of, ab8500_ponkey_match); | ||
| 121 | #endif | 122 | #endif |
| 122 | 123 | ||
| 123 | static struct platform_driver ab8500_ponkey_driver = { | 124 | static struct platform_driver ab8500_ponkey_driver = { |
diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index e82edf810d1f..f2261ab54701 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c | |||
| @@ -173,6 +173,7 @@ static const struct of_device_id pwm_beeper_match[] = { | |||
| 173 | { .compatible = "pwm-beeper", }, | 173 | { .compatible = "pwm-beeper", }, |
| 174 | { }, | 174 | { }, |
| 175 | }; | 175 | }; |
| 176 | MODULE_DEVICE_TABLE(of, pwm_beeper_match); | ||
| 176 | #endif | 177 | #endif |
| 177 | 178 | ||
| 178 | static struct platform_driver pwm_beeper_driver = { | 179 | static struct platform_driver pwm_beeper_driver = { |
diff --git a/drivers/input/misc/regulator-haptic.c b/drivers/input/misc/regulator-haptic.c index 6bf3f1082f71..a804705eb04a 100644 --- a/drivers/input/misc/regulator-haptic.c +++ b/drivers/input/misc/regulator-haptic.c | |||
| @@ -249,6 +249,7 @@ static const struct of_device_id regulator_haptic_dt_match[] = { | |||
| 249 | { .compatible = "regulator-haptic" }, | 249 | { .compatible = "regulator-haptic" }, |
| 250 | { /* sentinel */ }, | 250 | { /* sentinel */ }, |
| 251 | }; | 251 | }; |
| 252 | MODULE_DEVICE_TABLE(of, regulator_haptic_dt_match); | ||
| 252 | 253 | ||
| 253 | static struct platform_driver regulator_haptic_driver = { | 254 | static struct platform_driver regulator_haptic_driver = { |
| 254 | .probe = regulator_haptic_probe, | 255 | .probe = regulator_haptic_probe, |
diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index 54116e544c96..6f997aa49183 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c | |||
| @@ -253,6 +253,7 @@ static const struct of_device_id bbc_beep_match[] = { | |||
| 253 | }, | 253 | }, |
| 254 | {}, | 254 | {}, |
| 255 | }; | 255 | }; |
| 256 | MODULE_DEVICE_TABLE(of, bbc_beep_match); | ||
| 256 | 257 | ||
| 257 | static struct platform_driver bbc_beep_driver = { | 258 | static struct platform_driver bbc_beep_driver = { |
| 258 | .driver = { | 259 | .driver = { |
| @@ -332,6 +333,7 @@ static const struct of_device_id grover_beep_match[] = { | |||
| 332 | }, | 333 | }, |
| 333 | {}, | 334 | {}, |
| 334 | }; | 335 | }; |
| 336 | MODULE_DEVICE_TABLE(of, grover_beep_match); | ||
| 335 | 337 | ||
| 336 | static struct platform_driver grover_beep_driver = { | 338 | static struct platform_driver grover_beep_driver = { |
| 337 | .driver = { | 339 | .driver = { |
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index e2b7420eed97..fa945304b9a5 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c | |||
| @@ -1170,6 +1170,7 @@ static const struct acpi_device_id elan_acpi_id[] = { | |||
| 1170 | { "ELAN0000", 0 }, | 1170 | { "ELAN0000", 0 }, |
| 1171 | { "ELAN0100", 0 }, | 1171 | { "ELAN0100", 0 }, |
| 1172 | { "ELAN0600", 0 }, | 1172 | { "ELAN0600", 0 }, |
| 1173 | { "ELAN1000", 0 }, | ||
| 1173 | { } | 1174 | { } |
| 1174 | }; | 1175 | }; |
| 1175 | MODULE_DEVICE_TABLE(acpi, elan_acpi_id); | 1176 | MODULE_DEVICE_TABLE(acpi, elan_acpi_id); |
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index c9c98f0ab284..db91de539ee3 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c | |||
| @@ -877,7 +877,7 @@ static int __init i8042_check_aux(void) | |||
| 877 | static int i8042_controller_check(void) | 877 | static int i8042_controller_check(void) |
| 878 | { | 878 | { |
| 879 | if (i8042_flush()) { | 879 | if (i8042_flush()) { |
| 880 | pr_err("No controller found\n"); | 880 | pr_info("No controller found\n"); |
| 881 | return -ENODEV; | 881 | return -ENODEV; |
| 882 | } | 882 | } |
| 883 | 883 | ||
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 059edeb7f04a..600dcceff542 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig | |||
| @@ -479,6 +479,18 @@ config TOUCHSCREEN_MTOUCH | |||
| 479 | To compile this driver as a module, choose M here: the | 479 | To compile this driver as a module, choose M here: the |
| 480 | module will be called mtouch. | 480 | module will be called mtouch. |
| 481 | 481 | ||
| 482 | config TOUCHSCREEN_IMX6UL_TSC | ||
| 483 | tristate "Freescale i.MX6UL touchscreen controller" | ||
| 484 | depends on (OF && GPIOLIB) || COMPILE_TEST | ||
| 485 | help | ||
| 486 | Say Y here if you have a Freescale i.MX6UL, and want to | ||
| 487 | use the internal touchscreen controller. | ||
| 488 | |||
| 489 | If unsure, say N. | ||
| 490 | |||
| 491 | To compile this driver as a module, choose M here: the | ||
| 492 | module will be called imx6ul_tsc. | ||
| 493 | |||
| 482 | config TOUCHSCREEN_INEXIO | 494 | config TOUCHSCREEN_INEXIO |
| 483 | tristate "iNexio serial touchscreens" | 495 | tristate "iNexio serial touchscreens" |
| 484 | select SERIO | 496 | select SERIO |
| @@ -1040,4 +1052,16 @@ config TOUCHSCREEN_ZFORCE | |||
| 1040 | To compile this driver as a module, choose M here: the | 1052 | To compile this driver as a module, choose M here: the |
| 1041 | module will be called zforce_ts. | 1053 | module will be called zforce_ts. |
| 1042 | 1054 | ||
| 1055 | config TOUCHSCREEN_COLIBRI_VF50 | ||
| 1056 | tristate "Toradex Colibri on board touchscreen driver" | ||
| 1057 | depends on GPIOLIB && IIO && VF610_ADC | ||
| 1058 | help | ||
| 1059 | Say Y here if you have a Colibri VF50 and plan to use | ||
| 1060 | the on-board provided 4-wire touchscreen driver. | ||
| 1061 | |||
| 1062 | If unsure, say N. | ||
| 1063 | |||
| 1064 | To compile this driver as a module, choose M here: the | ||
| 1065 | module will be called colibri_vf50_ts. | ||
| 1066 | |||
| 1043 | endif | 1067 | endif |
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index c85aae23e7f8..1b79cc09744a 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile | |||
| @@ -38,6 +38,7 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o | |||
| 38 | obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o | 38 | obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o |
| 39 | obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o | 39 | obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o |
| 40 | obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o | 40 | obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o |
| 41 | obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC) += imx6ul_tsc.o | ||
| 41 | obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o | 42 | obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o |
| 42 | obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o | 43 | obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o |
| 43 | obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o | 44 | obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o |
| @@ -85,3 +86,4 @@ obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o | |||
| 85 | obj-$(CONFIG_TOUCHSCREEN_SX8654) += sx8654.o | 86 | obj-$(CONFIG_TOUCHSCREEN_SX8654) += sx8654.o |
| 86 | obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o | 87 | obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o |
| 87 | obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o | 88 | obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o |
| 89 | obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o | ||
diff --git a/drivers/input/touchscreen/colibri-vf50-ts.c b/drivers/input/touchscreen/colibri-vf50-ts.c new file mode 100644 index 000000000000..5d4903a402cc --- /dev/null +++ b/drivers/input/touchscreen/colibri-vf50-ts.c | |||
| @@ -0,0 +1,386 @@ | |||
| 1 | /* | ||
| 2 | * Toradex Colibri VF50 Touchscreen driver | ||
| 3 | * | ||
| 4 | * Copyright 2015 Toradex AG | ||
| 5 | * | ||
| 6 | * Originally authored by Stefan Agner for 3.0 kernel | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License, or | ||
| 11 | * (at your option) any later version. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/delay.h> | ||
| 15 | #include <linux/err.h> | ||
| 16 | #include <linux/gpio.h> | ||
| 17 | #include <linux/gpio/consumer.h> | ||
| 18 | #include <linux/iio/consumer.h> | ||
| 19 | #include <linux/iio/types.h> | ||
| 20 | #include <linux/input.h> | ||
| 21 | #include <linux/interrupt.h> | ||
| 22 | #include <linux/kernel.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/pinctrl/consumer.h> | ||
| 25 | #include <linux/platform_device.h> | ||
| 26 | #include <linux/slab.h> | ||
| 27 | #include <linux/types.h> | ||
| 28 | |||
| 29 | #define DRIVER_NAME "colibri-vf50-ts" | ||
| 30 | #define DRV_VERSION "1.0" | ||
| 31 | |||
| 32 | #define VF_ADC_MAX ((1 << 12) - 1) | ||
| 33 | |||
| 34 | #define COLI_TOUCH_MIN_DELAY_US 1000 | ||
| 35 | #define COLI_TOUCH_MAX_DELAY_US 2000 | ||
| 36 | #define COLI_PULLUP_MIN_DELAY_US 10000 | ||
| 37 | #define COLI_PULLUP_MAX_DELAY_US 11000 | ||
| 38 | #define COLI_TOUCH_NO_OF_AVGS 5 | ||
| 39 | #define COLI_TOUCH_REQ_ADC_CHAN 4 | ||
| 40 | |||
| 41 | struct vf50_touch_device { | ||
| 42 | struct platform_device *pdev; | ||
| 43 | struct input_dev *ts_input; | ||
| 44 | struct iio_channel *channels; | ||
| 45 | struct gpio_desc *gpio_xp; | ||
| 46 | struct gpio_desc *gpio_xm; | ||
| 47 | struct gpio_desc *gpio_yp; | ||
| 48 | struct gpio_desc *gpio_ym; | ||
| 49 | int pen_irq; | ||
| 50 | int min_pressure; | ||
| 51 | bool stop_touchscreen; | ||
| 52 | }; | ||
| 53 | |||
| 54 | /* | ||
| 55 | * Enables given plates and measures touch parameters using ADC | ||
| 56 | */ | ||
| 57 | static int adc_ts_measure(struct iio_channel *channel, | ||
| 58 | struct gpio_desc *plate_p, struct gpio_desc *plate_m) | ||
| 59 | { | ||
| 60 | int i, value = 0, val = 0; | ||
| 61 | int error; | ||
| 62 | |||
| 63 | gpiod_set_value(plate_p, 1); | ||
| 64 | gpiod_set_value(plate_m, 1); | ||
| 65 | |||
| 66 | usleep_range(COLI_TOUCH_MIN_DELAY_US, COLI_TOUCH_MAX_DELAY_US); | ||
| 67 | |||
| 68 | for (i = 0; i < COLI_TOUCH_NO_OF_AVGS; i++) { | ||
| 69 | error = iio_read_channel_raw(channel, &val); | ||
| 70 | if (error < 0) { | ||
| 71 | value = error; | ||
| 72 | goto error_iio_read; | ||
| 73 | } | ||
| 74 | |||
| 75 | value += val; | ||
| 76 | } | ||
| 77 | |||
| 78 | value /= COLI_TOUCH_NO_OF_AVGS; | ||
| 79 | |||
| 80 | error_iio_read: | ||
| 81 | gpiod_set_value(plate_p, 0); | ||
| 82 | gpiod_set_value(plate_m, 0); | ||
| 83 | |||
| 84 | return value; | ||
| 85 | } | ||
| 86 | |||
| 87 | /* | ||
| 88 | * Enable touch detection using falling edge detection on XM | ||
| 89 | */ | ||
| 90 | static void vf50_ts_enable_touch_detection(struct vf50_touch_device *vf50_ts) | ||
| 91 | { | ||
| 92 | /* Enable plate YM (needs to be strong GND, high active) */ | ||
| 93 | gpiod_set_value(vf50_ts->gpio_ym, 1); | ||
| 94 | |||
| 95 | /* | ||
| 96 | * Let the platform mux to idle state in order to enable | ||
| 97 | * Pull-Up on GPIO | ||
| 98 | */ | ||
| 99 | pinctrl_pm_select_idle_state(&vf50_ts->pdev->dev); | ||
| 100 | |||
| 101 | /* Wait for the pull-up to be stable on high */ | ||
| 102 | usleep_range(COLI_PULLUP_MIN_DELAY_US, COLI_PULLUP_MAX_DELAY_US); | ||
| 103 | } | ||
| 104 | |||
| 105 | /* | ||
| 106 | * ADC touch screen sampling bottom half irq handler | ||
| 107 | */ | ||
| 108 | static irqreturn_t vf50_ts_irq_bh(int irq, void *private) | ||
| 109 | { | ||
| 110 | struct vf50_touch_device *vf50_ts = private; | ||
| 111 | struct device *dev = &vf50_ts->pdev->dev; | ||
| 112 | int val_x, val_y, val_z1, val_z2, val_p = 0; | ||
| 113 | bool discard_val_on_start = true; | ||
| 114 | |||
| 115 | /* Disable the touch detection plates */ | ||
| 116 | gpiod_set_value(vf50_ts->gpio_ym, 0); | ||
| 117 | |||
| 118 | /* Let the platform mux to default state in order to mux as ADC */ | ||
| 119 | pinctrl_pm_select_default_state(dev); | ||
| 120 | |||
| 121 | while (!vf50_ts->stop_touchscreen) { | ||
| 122 | /* X-Direction */ | ||
| 123 | val_x = adc_ts_measure(&vf50_ts->channels[0], | ||
| 124 | vf50_ts->gpio_xp, vf50_ts->gpio_xm); | ||
| 125 | if (val_x < 0) | ||
| 126 | break; | ||
| 127 | |||
| 128 | /* Y-Direction */ | ||
| 129 | val_y = adc_ts_measure(&vf50_ts->channels[1], | ||
| 130 | vf50_ts->gpio_yp, vf50_ts->gpio_ym); | ||
| 131 | if (val_y < 0) | ||
| 132 | break; | ||
| 133 | |||
| 134 | /* | ||
| 135 | * Touch pressure | ||
| 136 | * Measure on XP/YM | ||
| 137 | */ | ||
| 138 | val_z1 = adc_ts_measure(&vf50_ts->channels[2], | ||
| 139 | vf50_ts->gpio_yp, vf50_ts->gpio_xm); | ||
| 140 | if (val_z1 < 0) | ||
| 141 | break; | ||
| 142 | val_z2 = adc_ts_measure(&vf50_ts->channels[3], | ||
| 143 | vf50_ts->gpio_yp, vf50_ts->gpio_xm); | ||
| 144 | if (val_z2 < 0) | ||
| 145 | break; | ||
| 146 | |||
| 147 | /* Validate signal (avoid calculation using noise) */ | ||
| 148 | if (val_z1 > 64 && val_x > 64) { | ||
| 149 | /* | ||
| 150 | * Calculate resistance between the plates | ||
| 151 | * lower resistance means higher pressure | ||
| 152 | */ | ||
| 153 | int r_x = (1000 * val_x) / VF_ADC_MAX; | ||
| 154 | |||
| 155 | val_p = (r_x * val_z2) / val_z1 - r_x; | ||
| 156 | |||
| 157 | } else { | ||
| 158 | val_p = 2000; | ||
| 159 | } | ||
| 160 | |||
| 161 | val_p = 2000 - val_p; | ||
| 162 | dev_dbg(dev, | ||
| 163 | "Measured values: x: %d, y: %d, z1: %d, z2: %d, p: %d\n", | ||
| 164 | val_x, val_y, val_z1, val_z2, val_p); | ||
| 165 | |||
| 166 | /* | ||
| 167 | * If touch pressure is too low, stop measuring and reenable | ||
| 168 | * touch detection | ||
| 169 | */ | ||
| 170 | if (val_p < vf50_ts->min_pressure || val_p > 2000) | ||
| 171 | break; | ||
| 172 | |||
| 173 | /* | ||
| 174 | * The pressure may not be enough for the first x and the | ||
| 175 | * second y measurement, but, the pressure is ok when the | ||
| 176 | * driver is doing the third and fourth measurement. To | ||
| 177 | * take care of this, we drop the first measurement always. | ||
| 178 | */ | ||
| 179 | if (discard_val_on_start) { | ||
| 180 | discard_val_on_start = false; | ||
| 181 | } else { | ||
| 182 | /* | ||
| 183 | * Report touch position and sleep for | ||
| 184 | * the next measurement. | ||
| 185 | */ | ||
| 186 | input_report_abs(vf50_ts->ts_input, | ||
| 187 | ABS_X, VF_ADC_MAX - val_x); | ||
| 188 | input_report_abs(vf50_ts->ts_input, | ||
| 189 | ABS_Y, VF_ADC_MAX - val_y); | ||
| 190 | input_report_abs(vf50_ts->ts_input, | ||
| 191 | ABS_PRESSURE, val_p); | ||
| 192 | input_report_key(vf50_ts->ts_input, BTN_TOUCH, 1); | ||
| 193 | input_sync(vf50_ts->ts_input); | ||
| 194 | } | ||
| 195 | |||
| 196 | usleep_range(COLI_PULLUP_MIN_DELAY_US, | ||
| 197 | COLI_PULLUP_MAX_DELAY_US); | ||
| 198 | } | ||
| 199 | |||
| 200 | /* Report no more touch, re-enable touch detection */ | ||
| 201 | input_report_abs(vf50_ts->ts_input, ABS_PRESSURE, 0); | ||
| 202 | input_report_key(vf50_ts->ts_input, BTN_TOUCH, 0); | ||
| 203 | input_sync(vf50_ts->ts_input); | ||
| 204 | |||
| 205 | vf50_ts_enable_touch_detection(vf50_ts); | ||
| 206 | |||
| 207 | return IRQ_HANDLED; | ||
| 208 | } | ||
| 209 | |||
| 210 | static int vf50_ts_open(struct input_dev *dev_input) | ||
| 211 | { | ||
| 212 | struct vf50_touch_device *touchdev = input_get_drvdata(dev_input); | ||
| 213 | struct device *dev = &touchdev->pdev->dev; | ||
| 214 | |||
| 215 | dev_dbg(dev, "Input device %s opened, starting touch detection\n", | ||
| 216 | dev_input->name); | ||
| 217 | |||
| 218 | touchdev->stop_touchscreen = false; | ||
| 219 | |||
| 220 | /* Mux detection before request IRQ, wait for pull-up to settle */ | ||
| 221 | vf50_ts_enable_touch_detection(touchdev); | ||
| 222 | |||
| 223 | return 0; | ||
| 224 | } | ||
| 225 | |||
| 226 | static void vf50_ts_close(struct input_dev *dev_input) | ||
| 227 | { | ||
| 228 | struct vf50_touch_device *touchdev = input_get_drvdata(dev_input); | ||
| 229 | struct device *dev = &touchdev->pdev->dev; | ||
| 230 | |||
| 231 | touchdev->stop_touchscreen = true; | ||
| 232 | |||
| 233 | /* Make sure IRQ is not running past close */ | ||
| 234 | mb(); | ||
| 235 | synchronize_irq(touchdev->pen_irq); | ||
| 236 | |||
| 237 | gpiod_set_value(touchdev->gpio_ym, 0); | ||
| 238 | pinctrl_pm_select_default_state(dev); | ||
| 239 | |||
| 240 | dev_dbg(dev, "Input device %s closed, disable touch detection\n", | ||
| 241 | dev_input->name); | ||
| 242 | } | ||
| 243 | |||
| 244 | static int vf50_ts_get_gpiod(struct device *dev, struct gpio_desc **gpio_d, | ||
| 245 | const char *con_id, enum gpiod_flags flags) | ||
| 246 | { | ||
| 247 | int error; | ||
| 248 | |||
| 249 | *gpio_d = devm_gpiod_get(dev, con_id, flags); | ||
| 250 | if (IS_ERR(*gpio_d)) { | ||
| 251 | error = PTR_ERR(*gpio_d); | ||
| 252 | dev_err(dev, "Could not get gpio_%s %d\n", con_id, error); | ||
| 253 | return error; | ||
| 254 | } | ||
| 255 | |||
| 256 | return 0; | ||
| 257 | } | ||
| 258 | |||
| 259 | static void vf50_ts_channel_release(void *data) | ||
| 260 | { | ||
| 261 | struct iio_channel *channels = data; | ||
| 262 | |||
| 263 | iio_channel_release_all(channels); | ||
| 264 | } | ||
| 265 | |||
| 266 | static int vf50_ts_probe(struct platform_device *pdev) | ||
| 267 | { | ||
| 268 | struct input_dev *input; | ||
| 269 | struct iio_channel *channels; | ||
| 270 | struct device *dev = &pdev->dev; | ||
| 271 | struct vf50_touch_device *touchdev; | ||
| 272 | int num_adc_channels; | ||
| 273 | int error; | ||
| 274 | |||
| 275 | channels = iio_channel_get_all(dev); | ||
| 276 | if (IS_ERR(channels)) | ||
| 277 | return PTR_ERR(channels); | ||
| 278 | |||
| 279 | error = devm_add_action(dev, vf50_ts_channel_release, channels); | ||
| 280 | if (error) { | ||
| 281 | iio_channel_release_all(channels); | ||
| 282 | dev_err(dev, "Failed to register iio channel release action"); | ||
| 283 | return error; | ||
| 284 | } | ||
| 285 | |||
| 286 | num_adc_channels = 0; | ||
| 287 | while (channels[num_adc_channels].indio_dev) | ||
| 288 | num_adc_channels++; | ||
| 289 | |||
| 290 | if (num_adc_channels != COLI_TOUCH_REQ_ADC_CHAN) { | ||
| 291 | dev_err(dev, "Inadequate ADC channels specified\n"); | ||
| 292 | return -EINVAL; | ||
| 293 | } | ||
| 294 | |||
| 295 | touchdev = devm_kzalloc(dev, sizeof(*touchdev), GFP_KERNEL); | ||
| 296 | if (!touchdev) | ||
| 297 | return -ENOMEM; | ||
| 298 | |||
| 299 | touchdev->pdev = pdev; | ||
| 300 | touchdev->channels = channels; | ||
| 301 | |||
| 302 | error = of_property_read_u32(dev->of_node, "vf50-ts-min-pressure", | ||
| 303 | &touchdev->min_pressure); | ||
| 304 | if (error) | ||
| 305 | return error; | ||
| 306 | |||
| 307 | input = devm_input_allocate_device(dev); | ||
| 308 | if (!input) { | ||
| 309 | dev_err(dev, "Failed to allocate TS input device\n"); | ||
| 310 | return -ENOMEM; | ||
| 311 | } | ||
| 312 | |||
| 313 | platform_set_drvdata(pdev, touchdev); | ||
| 314 | |||
| 315 | input->name = DRIVER_NAME; | ||
| 316 | input->id.bustype = BUS_HOST; | ||
| 317 | input->dev.parent = dev; | ||
| 318 | input->open = vf50_ts_open; | ||
| 319 | input->close = vf50_ts_close; | ||
| 320 | |||
| 321 | input_set_capability(input, EV_KEY, BTN_TOUCH); | ||
| 322 | input_set_abs_params(input, ABS_X, 0, VF_ADC_MAX, 0, 0); | ||
| 323 | input_set_abs_params(input, ABS_Y, 0, VF_ADC_MAX, 0, 0); | ||
| 324 | input_set_abs_params(input, ABS_PRESSURE, 0, VF_ADC_MAX, 0, 0); | ||
| 325 | |||
| 326 | touchdev->ts_input = input; | ||
| 327 | input_set_drvdata(input, touchdev); | ||
| 328 | |||
| 329 | error = input_register_device(input); | ||
| 330 | if (error) { | ||
| 331 | dev_err(dev, "Failed to register input device\n"); | ||
| 332 | return error; | ||
| 333 | } | ||
| 334 | |||
| 335 | error = vf50_ts_get_gpiod(dev, &touchdev->gpio_xp, "xp", GPIOD_OUT_LOW); | ||
| 336 | if (error) | ||
| 337 | return error; | ||
| 338 | |||
| 339 | error = vf50_ts_get_gpiod(dev, &touchdev->gpio_xm, | ||
| 340 | "xm", GPIOD_OUT_LOW); | ||
| 341 | if (error) | ||
| 342 | return error; | ||
| 343 | |||
| 344 | error = vf50_ts_get_gpiod(dev, &touchdev->gpio_yp, "yp", GPIOD_OUT_LOW); | ||
| 345 | if (error) | ||
| 346 | return error; | ||
| 347 | |||
| 348 | error = vf50_ts_get_gpiod(dev, &touchdev->gpio_ym, "ym", GPIOD_OUT_LOW); | ||
| 349 | if (error) | ||
| 350 | return error; | ||
| 351 | |||
| 352 | touchdev->pen_irq = platform_get_irq(pdev, 0); | ||
| 353 | if (touchdev->pen_irq < 0) | ||
| 354 | return touchdev->pen_irq; | ||
| 355 | |||
| 356 | error = devm_request_threaded_irq(dev, touchdev->pen_irq, | ||
| 357 | NULL, vf50_ts_irq_bh, IRQF_ONESHOT, | ||
| 358 | "vf50 touch", touchdev); | ||
| 359 | if (error) { | ||
| 360 | dev_err(dev, "Failed to request IRQ %d: %d\n", | ||
| 361 | touchdev->pen_irq, error); | ||
| 362 | return error; | ||
| 363 | } | ||
| 364 | |||
| 365 | return 0; | ||
| 366 | } | ||
| 367 | |||
| 368 | static const struct of_device_id vf50_touch_of_match[] = { | ||
| 369 | { .compatible = "toradex,vf50-touchscreen", }, | ||
| 370 | { } | ||
| 371 | }; | ||
| 372 | MODULE_DEVICE_TABLE(of, vf50_touch_of_match); | ||
| 373 | |||
| 374 | static struct platform_driver vf50_touch_driver = { | ||
| 375 | .driver = { | ||
| 376 | .name = "toradex,vf50_touchctrl", | ||
| 377 | .of_match_table = vf50_touch_of_match, | ||
| 378 | }, | ||
| 379 | .probe = vf50_ts_probe, | ||
| 380 | }; | ||
| 381 | module_platform_driver(vf50_touch_driver); | ||
| 382 | |||
| 383 | MODULE_AUTHOR("Sanchayan Maity"); | ||
| 384 | MODULE_DESCRIPTION("Colibri VF50 Touchscreen driver"); | ||
| 385 | MODULE_LICENSE("GPL"); | ||
| 386 | MODULE_VERSION(DRV_VERSION); | ||
diff --git a/drivers/input/touchscreen/cyttsp4_i2c.c b/drivers/input/touchscreen/cyttsp4_i2c.c index 9a323dd915de..a9f95c7d3c00 100644 --- a/drivers/input/touchscreen/cyttsp4_i2c.c +++ b/drivers/input/touchscreen/cyttsp4_i2c.c | |||
| @@ -86,4 +86,3 @@ module_i2c_driver(cyttsp4_i2c_driver); | |||
| 86 | MODULE_LICENSE("GPL"); | 86 | MODULE_LICENSE("GPL"); |
| 87 | MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); | 87 | MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); |
| 88 | MODULE_AUTHOR("Cypress"); | 88 | MODULE_AUTHOR("Cypress"); |
| 89 | MODULE_ALIAS("i2c:cyttsp4"); | ||
diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c index 519e2de2f8df..eee51b3f2e3f 100644 --- a/drivers/input/touchscreen/cyttsp_i2c.c +++ b/drivers/input/touchscreen/cyttsp_i2c.c | |||
| @@ -86,4 +86,3 @@ module_i2c_driver(cyttsp_i2c_driver); | |||
| 86 | MODULE_LICENSE("GPL"); | 86 | MODULE_LICENSE("GPL"); |
| 87 | MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); | 87 | MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); |
| 88 | MODULE_AUTHOR("Cypress"); | 88 | MODULE_AUTHOR("Cypress"); |
| 89 | MODULE_ALIAS("i2c:cyttsp"); | ||
diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index ddac134b25b1..17cc20ef4923 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c | |||
| @@ -102,7 +102,7 @@ | |||
| 102 | #define ELAN_FW_PAGESIZE 132 | 102 | #define ELAN_FW_PAGESIZE 132 |
| 103 | 103 | ||
| 104 | /* calibration timeout definition */ | 104 | /* calibration timeout definition */ |
| 105 | #define ELAN_CALI_TIMEOUT_MSEC 10000 | 105 | #define ELAN_CALI_TIMEOUT_MSEC 12000 |
| 106 | 106 | ||
| 107 | #define ELAN_POWERON_DELAY_USEC 500 | 107 | #define ELAN_POWERON_DELAY_USEC 500 |
| 108 | #define ELAN_RESET_DELAY_MSEC 20 | 108 | #define ELAN_RESET_DELAY_MSEC 20 |
diff --git a/drivers/input/touchscreen/imx6ul_tsc.c b/drivers/input/touchscreen/imx6ul_tsc.c new file mode 100644 index 000000000000..ff0b75813daa --- /dev/null +++ b/drivers/input/touchscreen/imx6ul_tsc.c | |||
| @@ -0,0 +1,523 @@ | |||
| 1 | /* | ||
| 2 | * Freescale i.MX6UL touchscreen controller driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2015 Freescale Semiconductor, Inc. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License version 2 as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/errno.h> | ||
| 12 | #include <linux/kernel.h> | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/gpio/consumer.h> | ||
| 15 | #include <linux/input.h> | ||
| 16 | #include <linux/slab.h> | ||
| 17 | #include <linux/completion.h> | ||
| 18 | #include <linux/delay.h> | ||
| 19 | #include <linux/of.h> | ||
| 20 | #include <linux/interrupt.h> | ||
| 21 | #include <linux/platform_device.h> | ||
| 22 | #include <linux/clk.h> | ||
| 23 | #include <linux/io.h> | ||
| 24 | |||
| 25 | /* ADC configuration registers field define */ | ||
| 26 | #define ADC_AIEN (0x1 << 7) | ||
| 27 | #define ADC_CONV_DISABLE 0x1F | ||
| 28 | #define ADC_CAL (0x1 << 7) | ||
| 29 | #define ADC_CALF 0x2 | ||
| 30 | #define ADC_12BIT_MODE (0x2 << 2) | ||
| 31 | #define ADC_IPG_CLK 0x00 | ||
| 32 | #define ADC_CLK_DIV_8 (0x03 << 5) | ||
| 33 | #define ADC_SHORT_SAMPLE_MODE (0x0 << 4) | ||
| 34 | #define ADC_HARDWARE_TRIGGER (0x1 << 13) | ||
| 35 | #define SELECT_CHANNEL_4 0x04 | ||
| 36 | #define SELECT_CHANNEL_1 0x01 | ||
| 37 | #define DISABLE_CONVERSION_INT (0x0 << 7) | ||
| 38 | |||
| 39 | /* ADC registers */ | ||
| 40 | #define REG_ADC_HC0 0x00 | ||
| 41 | #define REG_ADC_HC1 0x04 | ||
| 42 | #define REG_ADC_HC2 0x08 | ||
| 43 | #define REG_ADC_HC3 0x0C | ||
| 44 | #define REG_ADC_HC4 0x10 | ||
| 45 | #define REG_ADC_HS 0x14 | ||
| 46 | #define REG_ADC_R0 0x18 | ||
| 47 | #define REG_ADC_CFG 0x2C | ||
| 48 | #define REG_ADC_GC 0x30 | ||
| 49 | #define REG_ADC_GS 0x34 | ||
| 50 | |||
| 51 | #define ADC_TIMEOUT msecs_to_jiffies(100) | ||
| 52 | |||
| 53 | /* TSC registers */ | ||
| 54 | #define REG_TSC_BASIC_SETING 0x00 | ||
| 55 | #define REG_TSC_PRE_CHARGE_TIME 0x10 | ||
| 56 | #define REG_TSC_FLOW_CONTROL 0x20 | ||
| 57 | #define REG_TSC_MEASURE_VALUE 0x30 | ||
| 58 | #define REG_TSC_INT_EN 0x40 | ||
| 59 | #define REG_TSC_INT_SIG_EN 0x50 | ||
| 60 | #define REG_TSC_INT_STATUS 0x60 | ||
| 61 | #define REG_TSC_DEBUG_MODE 0x70 | ||
| 62 | #define REG_TSC_DEBUG_MODE2 0x80 | ||
| 63 | |||
| 64 | /* TSC configuration registers field define */ | ||
| 65 | #define DETECT_4_WIRE_MODE (0x0 << 4) | ||
| 66 | #define AUTO_MEASURE 0x1 | ||
| 67 | #define MEASURE_SIGNAL 0x1 | ||
| 68 | #define DETECT_SIGNAL (0x1 << 4) | ||
| 69 | #define VALID_SIGNAL (0x1 << 8) | ||
| 70 | #define MEASURE_INT_EN 0x1 | ||
| 71 | #define MEASURE_SIG_EN 0x1 | ||
| 72 | #define VALID_SIG_EN (0x1 << 8) | ||
| 73 | #define DE_GLITCH_2 (0x2 << 29) | ||
| 74 | #define START_SENSE (0x1 << 12) | ||
| 75 | #define TSC_DISABLE (0x1 << 16) | ||
| 76 | #define DETECT_MODE 0x2 | ||
| 77 | |||
| 78 | struct imx6ul_tsc { | ||
| 79 | struct device *dev; | ||
| 80 | struct input_dev *input; | ||
| 81 | void __iomem *tsc_regs; | ||
| 82 | void __iomem *adc_regs; | ||
| 83 | struct clk *tsc_clk; | ||
| 84 | struct clk *adc_clk; | ||
| 85 | struct gpio_desc *xnur_gpio; | ||
| 86 | |||
| 87 | int measure_delay_time; | ||
| 88 | int pre_charge_time; | ||
| 89 | |||
| 90 | struct completion completion; | ||
| 91 | }; | ||
| 92 | |||
| 93 | /* | ||
| 94 | * TSC module need ADC to get the measure value. So | ||
| 95 | * before config TSC, we should initialize ADC module. | ||
| 96 | */ | ||
| 97 | static void imx6ul_adc_init(struct imx6ul_tsc *tsc) | ||
| 98 | { | ||
| 99 | int adc_hc = 0; | ||
| 100 | int adc_gc; | ||
| 101 | int adc_gs; | ||
| 102 | int adc_cfg; | ||
| 103 | int timeout; | ||
| 104 | |||
| 105 | reinit_completion(&tsc->completion); | ||
| 106 | |||
| 107 | adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG); | ||
| 108 | adc_cfg |= ADC_12BIT_MODE | ADC_IPG_CLK; | ||
| 109 | adc_cfg |= ADC_CLK_DIV_8 | ADC_SHORT_SAMPLE_MODE; | ||
| 110 | adc_cfg &= ~ADC_HARDWARE_TRIGGER; | ||
| 111 | writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG); | ||
| 112 | |||
| 113 | /* enable calibration interrupt */ | ||
| 114 | adc_hc |= ADC_AIEN; | ||
| 115 | adc_hc |= ADC_CONV_DISABLE; | ||
| 116 | writel(adc_hc, tsc->adc_regs + REG_ADC_HC0); | ||
| 117 | |||
| 118 | /* start ADC calibration */ | ||
| 119 | adc_gc = readl(tsc->adc_regs + REG_ADC_GC); | ||
| 120 | adc_gc |= ADC_CAL; | ||
| 121 | writel(adc_gc, tsc->adc_regs + REG_ADC_GC); | ||
| 122 | |||
| 123 | timeout = wait_for_completion_timeout | ||
| 124 | (&tsc->completion, ADC_TIMEOUT); | ||
| 125 | if (timeout == 0) | ||
| 126 | dev_err(tsc->dev, "Timeout for adc calibration\n"); | ||
| 127 | |||
| 128 | adc_gs = readl(tsc->adc_regs + REG_ADC_GS); | ||
| 129 | if (adc_gs & ADC_CALF) | ||
| 130 | dev_err(tsc->dev, "ADC calibration failed\n"); | ||
| 131 | |||
| 132 | /* TSC need the ADC work in hardware trigger */ | ||
| 133 | adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG); | ||
| 134 | adc_cfg |= ADC_HARDWARE_TRIGGER; | ||
| 135 | writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG); | ||
| 136 | } | ||
| 137 | |||
| 138 | /* | ||
| 139 | * This is a TSC workaround. Currently TSC misconnect two | ||
| 140 | * ADC channels, this function remap channel configure for | ||
| 141 | * hardware trigger. | ||
| 142 | */ | ||
| 143 | static void imx6ul_tsc_channel_config(struct imx6ul_tsc *tsc) | ||
| 144 | { | ||
| 145 | int adc_hc0, adc_hc1, adc_hc2, adc_hc3, adc_hc4; | ||
| 146 | |||
| 147 | adc_hc0 = DISABLE_CONVERSION_INT; | ||
| 148 | writel(adc_hc0, tsc->adc_regs + REG_ADC_HC0); | ||
| 149 | |||
| 150 | adc_hc1 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_4; | ||
| 151 | writel(adc_hc1, tsc->adc_regs + REG_ADC_HC1); | ||
| 152 | |||
| 153 | adc_hc2 = DISABLE_CONVERSION_INT; | ||
| 154 | writel(adc_hc2, tsc->adc_regs + REG_ADC_HC2); | ||
| 155 | |||
| 156 | adc_hc3 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_1; | ||
| 157 | writel(adc_hc3, tsc->adc_regs + REG_ADC_HC3); | ||
| 158 | |||
| 159 | adc_hc4 = DISABLE_CONVERSION_INT; | ||
| 160 | writel(adc_hc4, tsc->adc_regs + REG_ADC_HC4); | ||
| 161 | } | ||
| 162 | |||
| 163 | /* | ||
| 164 | * TSC setting, confige the pre-charge time and measure delay time. | ||
| 165 | * different touch screen may need different pre-charge time and | ||
| 166 | * measure delay time. | ||
| 167 | */ | ||
| 168 | static void imx6ul_tsc_set(struct imx6ul_tsc *tsc) | ||
| 169 | { | ||
| 170 | int basic_setting = 0; | ||
| 171 | int start; | ||
| 172 | |||
| 173 | basic_setting |= tsc->measure_delay_time << 8; | ||
| 174 | basic_setting |= DETECT_4_WIRE_MODE | AUTO_MEASURE; | ||
| 175 | writel(basic_setting, tsc->tsc_regs + REG_TSC_BASIC_SETING); | ||
| 176 | |||
| 177 | writel(DE_GLITCH_2, tsc->tsc_regs + REG_TSC_DEBUG_MODE2); | ||
| 178 | |||
| 179 | writel(tsc->pre_charge_time, tsc->tsc_regs + REG_TSC_PRE_CHARGE_TIME); | ||
| 180 | writel(MEASURE_INT_EN, tsc->tsc_regs + REG_TSC_INT_EN); | ||
| 181 | writel(MEASURE_SIG_EN | VALID_SIG_EN, | ||
| 182 | tsc->tsc_regs + REG_TSC_INT_SIG_EN); | ||
| 183 | |||
| 184 | /* start sense detection */ | ||
| 185 | start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL); | ||
| 186 | start |= START_SENSE; | ||
| 187 | start &= ~TSC_DISABLE; | ||
| 188 | writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL); | ||
| 189 | } | ||
| 190 | |||
| 191 | static void imx6ul_tsc_init(struct imx6ul_tsc *tsc) | ||
| 192 | { | ||
| 193 | imx6ul_adc_init(tsc); | ||
| 194 | imx6ul_tsc_channel_config(tsc); | ||
| 195 | imx6ul_tsc_set(tsc); | ||
| 196 | } | ||
| 197 | |||
| 198 | static void imx6ul_tsc_disable(struct imx6ul_tsc *tsc) | ||
| 199 | { | ||
| 200 | int tsc_flow; | ||
| 201 | int adc_cfg; | ||
| 202 | |||
| 203 | /* TSC controller enters to idle status */ | ||
| 204 | tsc_flow = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL); | ||
| 205 | tsc_flow |= TSC_DISABLE; | ||
| 206 | writel(tsc_flow, tsc->tsc_regs + REG_TSC_FLOW_CONTROL); | ||
| 207 | |||
| 208 | /* ADC controller enters to stop mode */ | ||
| 209 | adc_cfg = readl(tsc->adc_regs + REG_ADC_HC0); | ||
| 210 | adc_cfg |= ADC_CONV_DISABLE; | ||
| 211 | writel(adc_cfg, tsc->adc_regs + REG_ADC_HC0); | ||
| 212 | } | ||
| 213 | |||
| 214 | /* Delay some time (max 2ms), wait the pre-charge done. */ | ||
| 215 | static bool tsc_wait_detect_mode(struct imx6ul_tsc *tsc) | ||
| 216 | { | ||
| 217 | unsigned long timeout = jiffies + msecs_to_jiffies(2); | ||
| 218 | int state_machine; | ||
| 219 | int debug_mode2; | ||
| 220 | |||
| 221 | do { | ||
| 222 | if (time_after(jiffies, timeout)) | ||
| 223 | return false; | ||
| 224 | |||
| 225 | usleep_range(200, 400); | ||
| 226 | debug_mode2 = readl(tsc->tsc_regs + REG_TSC_DEBUG_MODE2); | ||
| 227 | state_machine = (debug_mode2 >> 20) & 0x7; | ||
| 228 | } while (state_machine != DETECT_MODE); | ||
| 229 | |||
| 230 | usleep_range(200, 400); | ||
| 231 | return true; | ||
| 232 | } | ||
| 233 | |||
| 234 | static irqreturn_t tsc_irq_fn(int irq, void *dev_id) | ||
| 235 | { | ||
| 236 | struct imx6ul_tsc *tsc = dev_id; | ||
| 237 | int status; | ||
| 238 | int value; | ||
| 239 | int x, y; | ||
| 240 | int start; | ||
| 241 | |||
| 242 | status = readl(tsc->tsc_regs + REG_TSC_INT_STATUS); | ||
| 243 | |||
| 244 | /* write 1 to clear the bit measure-signal */ | ||
| 245 | writel(MEASURE_SIGNAL | DETECT_SIGNAL, | ||
| 246 | tsc->tsc_regs + REG_TSC_INT_STATUS); | ||
| 247 | |||
| 248 | /* It's a HW self-clean bit. Set this bit and start sense detection */ | ||
| 249 | start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL); | ||
| 250 | start |= START_SENSE; | ||
| 251 | writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL); | ||
| 252 | |||
| 253 | if (status & MEASURE_SIGNAL) { | ||
| 254 | value = readl(tsc->tsc_regs + REG_TSC_MEASURE_VALUE); | ||
| 255 | x = (value >> 16) & 0x0fff; | ||
| 256 | y = value & 0x0fff; | ||
| 257 | |||
| 258 | /* | ||
| 259 | * In detect mode, we can get the xnur gpio value, | ||
| 260 | * otherwise assume contact is stiull active. | ||
| 261 | */ | ||
| 262 | if (!tsc_wait_detect_mode(tsc) || | ||
| 263 | gpiod_get_value_cansleep(tsc->xnur_gpio)) { | ||
| 264 | input_report_key(tsc->input, BTN_TOUCH, 1); | ||
| 265 | input_report_abs(tsc->input, ABS_X, x); | ||
| 266 | input_report_abs(tsc->input, ABS_Y, y); | ||
| 267 | } else { | ||
| 268 | input_report_key(tsc->input, BTN_TOUCH, 0); | ||
| 269 | } | ||
| 270 | |||
| 271 | input_sync(tsc->input); | ||
| 272 | } | ||
| 273 | |||
| 274 | return IRQ_HANDLED; | ||
| 275 | } | ||
| 276 | |||
| 277 | static irqreturn_t adc_irq_fn(int irq, void *dev_id) | ||
| 278 | { | ||
| 279 | struct imx6ul_tsc *tsc = dev_id; | ||
| 280 | int coco; | ||
| 281 | int value; | ||
| 282 | |||
| 283 | coco = readl(tsc->adc_regs + REG_ADC_HS); | ||
| 284 | if (coco & 0x01) { | ||
| 285 | value = readl(tsc->adc_regs + REG_ADC_R0); | ||
| 286 | complete(&tsc->completion); | ||
| 287 | } | ||
| 288 | |||
| 289 | return IRQ_HANDLED; | ||
| 290 | } | ||
| 291 | |||
| 292 | static int imx6ul_tsc_open(struct input_dev *input_dev) | ||
| 293 | { | ||
| 294 | struct imx6ul_tsc *tsc = input_get_drvdata(input_dev); | ||
| 295 | int err; | ||
| 296 | |||
| 297 | err = clk_prepare_enable(tsc->adc_clk); | ||
| 298 | if (err) { | ||
| 299 | dev_err(tsc->dev, | ||
| 300 | "Could not prepare or enable the adc clock: %d\n", | ||
| 301 | err); | ||
| 302 | return err; | ||
| 303 | } | ||
| 304 | |||
| 305 | err = clk_prepare_enable(tsc->tsc_clk); | ||
| 306 | if (err) { | ||
| 307 | dev_err(tsc->dev, | ||
| 308 | "Could not prepare or enable the tsc clock: %d\n", | ||
| 309 | err); | ||
| 310 | clk_disable_unprepare(tsc->adc_clk); | ||
| 311 | return err; | ||
| 312 | } | ||
| 313 | |||
| 314 | imx6ul_tsc_init(tsc); | ||
| 315 | |||
| 316 | return 0; | ||
| 317 | } | ||
| 318 | |||
| 319 | static void imx6ul_tsc_close(struct input_dev *input_dev) | ||
| 320 | { | ||
| 321 | struct imx6ul_tsc *tsc = input_get_drvdata(input_dev); | ||
| 322 | |||
| 323 | imx6ul_tsc_disable(tsc); | ||
| 324 | |||
| 325 | clk_disable_unprepare(tsc->tsc_clk); | ||
| 326 | clk_disable_unprepare(tsc->adc_clk); | ||
| 327 | } | ||
| 328 | |||
| 329 | static int imx6ul_tsc_probe(struct platform_device *pdev) | ||
| 330 | { | ||
| 331 | struct device_node *np = pdev->dev.of_node; | ||
| 332 | struct imx6ul_tsc *tsc; | ||
| 333 | struct input_dev *input_dev; | ||
| 334 | struct resource *tsc_mem; | ||
| 335 | struct resource *adc_mem; | ||
| 336 | int err; | ||
| 337 | int tsc_irq; | ||
| 338 | int adc_irq; | ||
| 339 | |||
| 340 | tsc = devm_kzalloc(&pdev->dev, sizeof(struct imx6ul_tsc), GFP_KERNEL); | ||
| 341 | if (!tsc) | ||
| 342 | return -ENOMEM; | ||
| 343 | |||
| 344 | input_dev = devm_input_allocate_device(&pdev->dev); | ||
| 345 | if (!input_dev) | ||
| 346 | return -ENOMEM; | ||
| 347 | |||
| 348 | input_dev->name = "iMX6UL TouchScreen Controller"; | ||
| 349 | input_dev->id.bustype = BUS_HOST; | ||
| 350 | |||
| 351 | input_dev->open = imx6ul_tsc_open; | ||
| 352 | input_dev->close = imx6ul_tsc_close; | ||
| 353 | |||
| 354 | input_set_capability(input_dev, EV_KEY, BTN_TOUCH); | ||
| 355 | input_set_abs_params(input_dev, ABS_X, 0, 0xFFF, 0, 0); | ||
| 356 | input_set_abs_params(input_dev, ABS_Y, 0, 0xFFF, 0, 0); | ||
| 357 | |||
| 358 | input_set_drvdata(input_dev, tsc); | ||
| 359 | |||
| 360 | tsc->dev = &pdev->dev; | ||
| 361 | tsc->input = input_dev; | ||
| 362 | init_completion(&tsc->completion); | ||
| 363 | |||
| 364 | tsc->xnur_gpio = devm_gpiod_get(&pdev->dev, "xnur", GPIOD_IN); | ||
| 365 | if (IS_ERR(tsc->xnur_gpio)) { | ||
| 366 | err = PTR_ERR(tsc->xnur_gpio); | ||
| 367 | dev_err(&pdev->dev, | ||
| 368 | "failed to request GPIO tsc_X- (xnur): %d\n", err); | ||
| 369 | return err; | ||
| 370 | } | ||
| 371 | |||
| 372 | tsc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 373 | tsc->tsc_regs = devm_ioremap_resource(&pdev->dev, tsc_mem); | ||
| 374 | if (IS_ERR(tsc->tsc_regs)) { | ||
| 375 | err = PTR_ERR(tsc->tsc_regs); | ||
| 376 | dev_err(&pdev->dev, "failed to remap tsc memory: %d\n", err); | ||
| 377 | return err; | ||
| 378 | } | ||
| 379 | |||
| 380 | adc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
| 381 | tsc->adc_regs = devm_ioremap_resource(&pdev->dev, adc_mem); | ||
| 382 | if (IS_ERR(tsc->adc_regs)) { | ||
| 383 | err = PTR_ERR(tsc->adc_regs); | ||
| 384 | dev_err(&pdev->dev, "failed to remap adc memory: %d\n", err); | ||
| 385 | return err; | ||
| 386 | } | ||
| 387 | |||
| 388 | tsc->tsc_clk = devm_clk_get(&pdev->dev, "tsc"); | ||
| 389 | if (IS_ERR(tsc->tsc_clk)) { | ||
| 390 | err = PTR_ERR(tsc->tsc_clk); | ||
| 391 | dev_err(&pdev->dev, "failed getting tsc clock: %d\n", err); | ||
| 392 | return err; | ||
| 393 | } | ||
| 394 | |||
| 395 | tsc->adc_clk = devm_clk_get(&pdev->dev, "adc"); | ||
| 396 | if (IS_ERR(tsc->adc_clk)) { | ||
| 397 | err = PTR_ERR(tsc->adc_clk); | ||
| 398 | dev_err(&pdev->dev, "failed getting adc clock: %d\n", err); | ||
| 399 | return err; | ||
| 400 | } | ||
| 401 | |||
| 402 | tsc_irq = platform_get_irq(pdev, 0); | ||
| 403 | if (tsc_irq < 0) { | ||
| 404 | dev_err(&pdev->dev, "no tsc irq resource?\n"); | ||
| 405 | return tsc_irq; | ||
| 406 | } | ||
| 407 | |||
| 408 | adc_irq = platform_get_irq(pdev, 1); | ||
| 409 | if (adc_irq <= 0) { | ||
| 410 | dev_err(&pdev->dev, "no adc irq resource?\n"); | ||
| 411 | return adc_irq; | ||
| 412 | } | ||
| 413 | |||
| 414 | err = devm_request_threaded_irq(tsc->dev, tsc_irq, | ||
| 415 | NULL, tsc_irq_fn, IRQF_ONESHOT, | ||
| 416 | dev_name(&pdev->dev), tsc); | ||
| 417 | if (err) { | ||
| 418 | dev_err(&pdev->dev, | ||
| 419 | "failed requesting tsc irq %d: %d\n", | ||
| 420 | tsc_irq, err); | ||
| 421 | return err; | ||
| 422 | } | ||
| 423 | |||
| 424 | err = devm_request_irq(tsc->dev, adc_irq, adc_irq_fn, 0, | ||
| 425 | dev_name(&pdev->dev), tsc); | ||
| 426 | if (err) { | ||
| 427 | dev_err(&pdev->dev, | ||
| 428 | "failed requesting adc irq %d: %d\n", | ||
| 429 | adc_irq, err); | ||
| 430 | return err; | ||
| 431 | } | ||
| 432 | |||
| 433 | err = of_property_read_u32(np, "measure-delay-time", | ||
| 434 | &tsc->measure_delay_time); | ||
| 435 | if (err) | ||
| 436 | tsc->measure_delay_time = 0xffff; | ||
| 437 | |||
| 438 | err = of_property_read_u32(np, "pre-charge-time", | ||
| 439 | &tsc->pre_charge_time); | ||
| 440 | if (err) | ||
| 441 | tsc->pre_charge_time = 0xfff; | ||
| 442 | |||
| 443 | err = input_register_device(tsc->input); | ||
| 444 | if (err) { | ||
| 445 | dev_err(&pdev->dev, | ||
| 446 | "failed to register input device: %d\n", err); | ||
| 447 | return err; | ||
| 448 | } | ||
| 449 | |||
| 450 | platform_set_drvdata(pdev, tsc); | ||
| 451 | return 0; | ||
| 452 | } | ||
| 453 | |||
| 454 | static int __maybe_unused imx6ul_tsc_suspend(struct device *dev) | ||
| 455 | { | ||
| 456 | struct platform_device *pdev = to_platform_device(dev); | ||
| 457 | struct imx6ul_tsc *tsc = platform_get_drvdata(pdev); | ||
| 458 | struct input_dev *input_dev = tsc->input; | ||
| 459 | |||
| 460 | mutex_lock(&input_dev->mutex); | ||
| 461 | |||
| 462 | if (input_dev->users) { | ||
| 463 | imx6ul_tsc_disable(tsc); | ||
| 464 | |||
| 465 | clk_disable_unprepare(tsc->tsc_clk); | ||
| 466 | clk_disable_unprepare(tsc->adc_clk); | ||
| 467 | } | ||
| 468 | |||
| 469 | mutex_unlock(&input_dev->mutex); | ||
| 470 | |||
| 471 | return 0; | ||
| 472 | } | ||
| 473 | |||
| 474 | static int __maybe_unused imx6ul_tsc_resume(struct device *dev) | ||
| 475 | { | ||
| 476 | struct platform_device *pdev = to_platform_device(dev); | ||
| 477 | struct imx6ul_tsc *tsc = platform_get_drvdata(pdev); | ||
| 478 | struct input_dev *input_dev = tsc->input; | ||
| 479 | int retval = 0; | ||
| 480 | |||
| 481 | mutex_lock(&input_dev->mutex); | ||
| 482 | |||
| 483 | if (input_dev->users) { | ||
| 484 | retval = clk_prepare_enable(tsc->adc_clk); | ||
| 485 | if (retval) | ||
| 486 | goto out; | ||
| 487 | |||
| 488 | retval = clk_prepare_enable(tsc->tsc_clk); | ||
| 489 | if (retval) { | ||
| 490 | clk_disable_unprepare(tsc->adc_clk); | ||
| 491 | goto out; | ||
| 492 | } | ||
| 493 | |||
| 494 | imx6ul_tsc_init(tsc); | ||
| 495 | } | ||
| 496 | |||
| 497 | out: | ||
| 498 | mutex_unlock(&input_dev->mutex); | ||
| 499 | return retval; | ||
| 500 | } | ||
| 501 | |||
| 502 | static SIMPLE_DEV_PM_OPS(imx6ul_tsc_pm_ops, | ||
| 503 | imx6ul_tsc_suspend, imx6ul_tsc_resume); | ||
| 504 | |||
| 505 | static const struct of_device_id imx6ul_tsc_match[] = { | ||
| 506 | { .compatible = "fsl,imx6ul-tsc", }, | ||
| 507 | { /* sentinel */ } | ||
| 508 | }; | ||
| 509 | MODULE_DEVICE_TABLE(of, imx6ul_tsc_match); | ||
| 510 | |||
| 511 | static struct platform_driver imx6ul_tsc_driver = { | ||
| 512 | .driver = { | ||
| 513 | .name = "imx6ul-tsc", | ||
| 514 | .of_match_table = imx6ul_tsc_match, | ||
| 515 | .pm = &imx6ul_tsc_pm_ops, | ||
| 516 | }, | ||
| 517 | .probe = imx6ul_tsc_probe, | ||
| 518 | }; | ||
| 519 | module_platform_driver(imx6ul_tsc_driver); | ||
| 520 | |||
| 521 | MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>"); | ||
| 522 | MODULE_DESCRIPTION("Freescale i.MX6UL Touchscreen controller driver"); | ||
| 523 | MODULE_LICENSE("GPL v2"); | ||
