diff options
author | Gary King <gking@nvidia.com> | 2010-05-27 21:06:52 -0700 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2010-05-27 21:41:07 -0700 |
commit | 519db8d85af41ff3200b0421c7718a47e79d94ba (patch) | |
tree | a16be167425dc7d5b8df95ad4d1db33b48463e29 /drivers/input | |
parent | fefdb43f6397a206539ec5a2f2f218ac4d856503 (diff) |
Input: add tegra_odm generic touchscreen driver
Change-Id: Ife43077e4699e38339a637be5a21f1f75fb46543
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 8 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/tegra_odm.c | 429 |
3 files changed, 438 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 3453f1ba7ec0..24c51a52abe5 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -277,6 +277,14 @@ config TOUCHSCREEN_SYNAPTICS_I2C_RMI help This enables support for Synaptics RMI over I2C based touchscreens. +config TOUCHSCREEN_TEGRA_ODM + boolean "NVIDIA Tegra ODM-kit based touchscreen driver" + depends on ARCH_TEGRA && TEGRA_NVRM + default n + help + Adds a touchscreen input device for drivers written using NVIDIA's + Tegra ODM kit interface + config TOUCHSCREEN_TOUCHRIGHT tristate "Touchright serial touchscreen" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index b200003c1e54..126635c3f51f 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI) += synaptics_i2c_rmi.o +obj-$(CONFIG_TOUCHSCREEN_TEGRA_ODM) += tegra_odm.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o diff --git a/drivers/input/touchscreen/tegra_odm.c b/drivers/input/touchscreen/tegra_odm.c new file mode 100644 index 000000000000..3245406c8181 --- /dev/null +++ b/drivers/input/touchscreen/tegra_odm.c @@ -0,0 +1,429 @@ +/* + * drivers/input/touchscreen/tegra_odm.c + * + * Touchscreen class input driver for platforms using NVIDIA's Tegra ODM kit + * driver interface + * + * Copyright (c) 2009, NVIDIA Corporation. + * + * 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 + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#define NV_DEBUG 0 + +#include <linux/module.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/kthread.h> +#include <linux/delay.h> +#include <linux/earlysuspend.h> +#include <linux/freezer.h> + +#include <nvodm_services.h> +#include <nvodm_touch.h> + +#define TOOL_PRESSURE 100 +#define TOOL_WIDTH 8 + +struct tegra_touch_driver_data +{ + struct input_dev *input_dev; + struct task_struct *task; + NvOdmOsSemaphoreHandle semaphore; + NvOdmTouchDeviceHandle hTouchDevice; + NvBool bPollingMode; + NvU32 pollingIntervalMS; + NvOdmTouchCapabilities caps; + NvU32 MaxX; + NvU32 MinX; + NvU32 MaxY; + NvU32 MinY; + int shutdown; + struct early_suspend early_suspend; +}; + +#define NVODM_TOUCH_NAME "nvodm_touch" + +#define swapv(x, y) do { typeof(x) z = x; x = y; y = z; } while (0) + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void tegra_touch_early_suspend(struct early_suspend *es) +{ + struct tegra_touch_driver_data *touch; + touch = container_of(es, struct tegra_touch_driver_data, early_suspend); + + if (touch && touch->hTouchDevice) { + NvOdmTouchPowerOnOff(touch->hTouchDevice, NV_FALSE); + } + else { + pr_err("tegra_touch_early_suspend: NULL handles passed\n"); + } +} + +static void tegra_touch_late_resume(struct early_suspend *es) +{ + struct tegra_touch_driver_data *touch; + touch = container_of(es, struct tegra_touch_driver_data, early_suspend); + + if (touch && touch->hTouchDevice) { + NvOdmTouchPowerOnOff(touch->hTouchDevice, NV_TRUE); + } + else { + pr_err("tegra_touch_late_resume: NULL handles passed\n"); + } +} +#else +static int tegra_touch_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct tegra_touch_driver_data *touch = platform_get_drvdata(pdev); + + if (touch && touch->hTouchDevice) { + NvOdmTouchPowerOnOff(touch->hTouchDevice, NV_FALSE); + return 0; + } + pr_err("tegra_touch_suspend: NULL handles passed\n"); + return -1; +} + +static int tegra_touch_resume(struct platform_device *pdev) +{ + struct tegra_touch_driver_data *touch = platform_get_drvdata(pdev); + + if (touch && touch->hTouchDevice) { + NvOdmTouchPowerOnOff(touch->hTouchDevice, NV_TRUE); + return 0; + } + pr_err("tegra_touch_resume: NULL handles passed\n"); + return -1; +} +#endif + +static int tegra_touch_thread(void *pdata) +{ + struct tegra_touch_driver_data *touch = + (struct tegra_touch_driver_data*)pdata; + NvOdmTouchCoordinateInfo c = {0}; + NvU32 x[2] = {0}, y[2] = {0}, i = 0; + NvU32 Pressure = TOOL_PRESSURE, Width = TOOL_WIDTH; + static NvU32 prev_x[2] = {0}, prev_y[2] = {0}; + NvBool bKeepReadingSamples = NV_FALSE; + NvU32 fingers = 0; + NvOdmTouchCapabilities *caps = &touch->caps; + NvU32 max_fingers = caps->MaxNumberOfFingerCoordReported; + + /* touch event thread should be frozen before suspend */ + set_freezable_with_signal(); + + for (;;) { + if (touch->bPollingMode) + msleep(touch->pollingIntervalMS); + else + NvOdmOsSemaphoreWait(touch->semaphore); + + bKeepReadingSamples = NV_TRUE; + while (bKeepReadingSamples) { + if (!NvOdmTouchReadCoordinate(touch->hTouchDevice, &c)){ + pr_err("Couldn't read touch sample\n"); + bKeepReadingSamples = NV_FALSE; + continue; + } + + fingers = c.additionalInfo.Fingers; + + switch (fingers) { + case 0: + x[0] = prev_x[0]; + y[0] = prev_y[0]; + Pressure = 0; + break; + case 1: + x[0] = c.xcoord; + y[0] = c.ycoord; + Pressure = TOOL_PRESSURE; + break; + case 2: + for (i = 0; i < fingers; i++) { + x[i] = c.additionalInfo.multi_XYCoords[i][0]; + y[i] = c.additionalInfo.multi_XYCoords[i][1]; + } + Pressure = TOOL_PRESSURE; + break; + default: + /* can occur because of sensor errors */ + x[0] = prev_x[0]; + y[0] = prev_y[0]; + fingers = 1; + } + + /* transformation from touch to screen orientation */ + if (caps->Orientation & NvOdmTouchOrientation_V_FLIP) { + y[0] = caps->YMaxPosition + + caps->YMinPosition - y[0]; + y[1] = caps->YMaxPosition + + caps->YMinPosition - y[1]; + } + if (caps->Orientation & NvOdmTouchOrientation_H_FLIP) { + x[0] = caps->XMaxPosition + + caps->XMinPosition - x[0]; + x[1] = caps->XMaxPosition + + caps->XMinPosition - x[1]; + } + + if (caps->Orientation & NvOdmTouchOrientation_XY_SWAP) { + for (i = 0; i < max_fingers; i++) + swapv(x[i],y[i]); + } + + /* report 1st finger's co-ordinates */ + input_report_abs(touch->input_dev, ABS_X, x[0]); + input_report_abs(touch->input_dev, ABS_Y, y[0]); + prev_x[0] = x[0]; + prev_y[0] = y[0]; + + /* Report number of fingers */ + input_report_key(touch->input_dev, + BTN_TOUCH, fingers); + input_report_key(touch->input_dev, + BTN_2, fingers == 2); + + /* report co-ordinates for the 2nd finger */ + if (fingers == 2) { + input_report_abs(touch->input_dev, + ABS_HAT0X, x[1]); // x + input_report_abs(touch->input_dev, + ABS_HAT0Y, y[1]); // y + prev_x[1] = x[1]; + prev_y[1] = y[1]; + } + + input_report_abs(touch->input_dev, + ABS_MT_TOUCH_MAJOR, Pressure); + input_report_abs(touch->input_dev, + ABS_MT_WIDTH_MAJOR, Width); + input_report_abs(touch->input_dev, + ABS_MT_POSITION_X, x[0]); + input_report_abs(touch->input_dev, + ABS_MT_POSITION_Y, y[0]); + input_mt_sync(touch->input_dev); + + /* report co-ordinates for the 2nd finger */ + if (fingers == 2) { + input_report_abs(touch->input_dev, + ABS_MT_TOUCH_MAJOR, Pressure); + input_report_abs(touch->input_dev, + ABS_MT_WIDTH_MAJOR, Width); + input_report_abs(touch->input_dev, + ABS_MT_POSITION_X, x[1]); + input_report_abs(touch->input_dev, + ABS_MT_POSITION_Y, y[1]); + input_mt_sync(touch->input_dev); + } + input_sync(touch->input_dev); + + bKeepReadingSamples = NV_FALSE; + if (!touch->bPollingMode && + !NvOdmTouchHandleInterrupt(touch->hTouchDevice)) { + /* Some more data to read keep going */ + bKeepReadingSamples = NV_TRUE; + } + } + } + + return 0; +} + +static int __init tegra_touch_probe(struct platform_device *pdev) +{ + struct tegra_touch_driver_data *touch = NULL; + struct input_dev *input_dev = NULL; + int err; + NvOdmTouchCapabilities *caps; + + touch = kzalloc(sizeof(struct tegra_touch_driver_data), GFP_KERNEL); + input_dev = input_allocate_device(); + if (input_dev == NULL || touch == NULL) { + input_free_device(input_dev); + kfree(touch); + err = -ENOMEM; + pr_err("tegra_touch_probe: Failed to allocate input device\n"); + return err; + } + touch->semaphore = NvOdmOsSemaphoreCreate(0); + if (!touch->semaphore) { + err = -1; + pr_err("tegra_touch_probe: Semaphore creation failed\n"); + goto err_semaphore_create_failed; + } + + if (!NvOdmTouchDeviceOpen(&touch->hTouchDevice)) { + err = -1; + pr_err("tegra_touch_probe: NvOdmTouchDeviceOpen failed\n"); + goto err_open_failed; + } + touch->bPollingMode = NV_FALSE; + if (!NvOdmTouchEnableInterrupt(touch->hTouchDevice, touch->semaphore)) { + err = -1; + pr_err("tegra_touch_probe: Interrupt failed, polling mode\n"); + touch->bPollingMode = NV_TRUE; + touch->pollingIntervalMS = 10; + } + + touch->task = + kthread_create(tegra_touch_thread, touch, "tegra_touch_thread"); + + if(touch->task == NULL) { + err = -1; + goto err_kthread_create_failed; + } + wake_up_process( touch->task ); + + touch->input_dev = input_dev; + touch->input_dev->name = NVODM_TOUCH_NAME; + + /* Will generate sync at the end of all input */ + set_bit(EV_SYN, touch->input_dev->evbit); + /* Event is key input type */ + set_bit(EV_KEY, touch->input_dev->evbit); + /* Input values are in absoulte values */ + set_bit(EV_ABS, touch->input_dev->evbit); + /* virtual key is BTN_TOUCH */ + set_bit(BTN_TOUCH, touch->input_dev->keybit); + /* virtual key is BTN_2 */ + set_bit(BTN_2, touch->input_dev->keybit); + + /* expose multi-touch capabilities */ + set_bit(ABS_MT_TOUCH_MAJOR, touch->input_dev->keybit); + set_bit(ABS_MT_POSITION_X, touch->input_dev->keybit); + set_bit(ABS_MT_POSITION_Y, touch->input_dev->keybit); + set_bit(ABS_X, touch->input_dev->keybit); + set_bit(ABS_Y, touch->input_dev->keybit); + + NvOdmTouchDeviceGetCapabilities(touch->hTouchDevice, &touch->caps); + + caps = &touch->caps; + + if (caps->Orientation & NvOdmTouchOrientation_XY_SWAP) { + touch->MaxY = caps->XMaxPosition; + touch->MinY = caps->XMinPosition; + touch->MaxX = caps->YMaxPosition; + touch->MinX = caps->YMinPosition; + + } else { + touch->MaxX = caps->XMaxPosition; + touch->MinX = caps->XMinPosition; + touch->MaxY = caps->YMaxPosition; + touch->MinY = caps->YMinPosition; + } + + input_set_abs_params(touch->input_dev, ABS_X, touch->MinX, + touch->MaxX, 0, 0); + input_set_abs_params(touch->input_dev, ABS_Y, touch->MinY, + touch->MaxY, 0, 0); + input_set_abs_params(touch->input_dev, ABS_HAT0X, touch->MinX, + touch->MaxX, 0, 0); + input_set_abs_params(touch->input_dev, ABS_HAT0Y, touch->MinY, + touch->MaxY, 0, 0); + input_set_abs_params(touch->input_dev, ABS_MT_POSITION_X, + touch->MinX, touch->MaxX, 0, 0); + input_set_abs_params(touch->input_dev, ABS_MT_POSITION_Y, + touch->MinY, touch->MaxY, 0, 0); + + if (caps->IsPressureSupported) { + input_set_abs_params(touch->input_dev, ABS_MT_TOUCH_MAJOR, + 0, caps->MaxNumberOfPressureReported, 0, 0); + input_set_abs_params(touch->input_dev, ABS_PRESSURE, 0, + caps->MaxNumberOfPressureReported, 0, 0); + } + + if (caps->IsWidthSupported) { + input_set_abs_params(touch->input_dev, ABS_TOOL_WIDTH, 0, + caps->MaxNumberOfWidthReported, 0, 0); + input_set_abs_params(touch->input_dev, ABS_MT_WIDTH_MAJOR, 0, + caps->MaxNumberOfWidthReported, 0, 0); + } + + platform_set_drvdata(pdev, touch); + + err = input_register_device(touch->input_dev); + if (err) + { + pr_err("tegra_touch_probe: Unable to register input device\n"); + goto err_input_register_device_failed; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + touch->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + touch->early_suspend.suspend = tegra_touch_early_suspend; + touch->early_suspend.resume = tegra_touch_late_resume; + register_early_suspend(&touch->early_suspend); +#endif + printk(KERN_INFO NVODM_TOUCH_NAME + ": Successfully registered the ODM touch driver %x\n", (NvU32)touch->hTouchDevice); + return 0; + +err_input_register_device_failed: + NvOdmTouchDeviceClose(touch->hTouchDevice); +err_kthread_create_failed: + /* FIXME How to destroy the thread? Maybe we should use workqueues? */ +err_open_failed: + NvOdmOsSemaphoreDestroy(touch->semaphore); +err_semaphore_create_failed: + input_free_device(touch->input_dev); + kfree(touch); + return err; +} + +static int tegra_touch_remove(struct platform_device *pdev) +{ + struct tegra_touch_driver_data *touch = platform_get_drvdata(pdev); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&touch->early_suspend); +#endif + touch->shutdown = 1; + /* FIXME How to destroy the thread? Maybe we should use workqueues? */ + input_unregister_device(touch->input_dev); + /* NvOsSemaphoreDestroy(touch->semaphore); */ + input_unregister_device(touch->input_dev); + kfree(touch); + return 0; +} + +static struct platform_driver tegra_touch_driver = { + .probe = tegra_touch_probe, + .remove = tegra_touch_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = tegra_touch_suspend, + .resume = tegra_touch_resume, +#endif + .driver = { + .name = "tegra_touch", + }, +}; + +static int __devinit tegra_touch_init(void) +{ + return platform_driver_register(&tegra_touch_driver); +} + +static void __exit tegra_touch_exit(void) +{ + platform_driver_unregister(&tegra_touch_driver); +} + +module_init(tegra_touch_init); +module_exit(tegra_touch_exit); + +MODULE_DESCRIPTION("Tegra ODM touch driver"); |