From 533badde3dbb348df9a992015fa8f3db6bdb134d Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 14 Jan 2013 15:38:20 -0800 Subject: [PATCH] input: touchscreen: Add MSM legacy touchscreen driver Signed-off-by: Stephen Boyd --- arch/arm/mach-msm/include/mach/msm_touch.h | 26 ++ drivers/input/touchscreen/Kconfig | 21 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/msm_touch.c | 317 +++++++++++++++++++++ 4 files changed, 365 insertions(+) create mode 100644 arch/arm/mach-msm/include/mach/msm_touch.h create mode 100644 drivers/input/touchscreen/msm_touch.c diff --git a/arch/arm/mach-msm/include/mach/msm_touch.h b/arch/arm/mach-msm/include/mach/msm_touch.h new file mode 100644 index 00000000000..763d6a8f111 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_touch.h @@ -0,0 +1,26 @@ +/* arch/arm/mach-msm/include/mach/msm_touch.h + * + * Platform data for MSM touchscreen driver. + * + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#ifndef _MACH_MSM_TOUCH_H_ +#define _MACH_MSM_TOUCH_H_ + +struct msm_ts_platform_data { + unsigned int x_max; + unsigned int y_max; + unsigned int pressure_max; +}; + +#endif diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 74694f1f4fd..77a0affd0c2 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -831,6 +831,27 @@ config TOUCHSCREEN_TSC2007 To compile this driver as a module, choose M here: the module will be called tsc2007. +config TOUCHSCREEN_MSM_LEGACY + default n + tristate "MSM Touchscreen" + depends on ARCH_MSM && !ARCH_MSM7X30 + help + Say Y here if you have a touchscreen interface using MSM + touchscreen controller. + + To compile this driver as a module, choose M here: the + module will be called msm_touch. + +config ANDROID_TOUCHSCREEN_MSM_HACKS + default y + depends on TOUCHSCREEN_MSM_LEGACY + bool "Android MSM Touchscreen hacks" + help + Say Y here if you are running Android framework on Qualcomm + MSM/QSD based Surf or FFAs. These hacks are required inorder + to Android framework to receive adjusted x, y co-ordinates + until proper calibration framework is in place. + config TOUCHSCREEN_W90X900 tristate "W90P910 touchscreen driver" depends on HAVE_CLK diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 06ab77d3d47..bf4154cd388 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -74,5 +74,6 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o +obj-$(CONFIG_TOUCHSCREEN_MSM_LEGACY) += msm_touch.o obj-$(CONFIG_TOUCHSCREEN_CY8C_TS) += cy8c_ts.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC) += cyttsp-i2c-qc.o diff --git a/drivers/input/touchscreen/msm_touch.c b/drivers/input/touchscreen/msm_touch.c new file mode 100644 index 00000000000..7ba896a3888 --- /dev/null +++ b/drivers/input/touchscreen/msm_touch.c @@ -0,0 +1,317 @@ +/* drivers/input/touchscreen/msm_touch.c + * + * Copyright (c) 2008-2009, 2011, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* HW register map */ +#define TSSC_CTL_REG 0x100 +#define TSSC_SI_REG 0x108 +#define TSSC_OPN_REG 0x104 +#define TSSC_STATUS_REG 0x10C +#define TSSC_AVG12_REG 0x110 + +/* status bits */ +#define TSSC_STS_OPN_SHIFT 0x6 +#define TSSC_STS_OPN_BMSK 0x1C0 +#define TSSC_STS_NUMSAMP_SHFT 0x1 +#define TSSC_STS_NUMSAMP_BMSK 0x3E + +/* CTL bits */ +#define TSSC_CTL_EN (0x1 << 0) +#define TSSC_CTL_SW_RESET (0x1 << 2) +#define TSSC_CTL_MASTER_MODE (0x3 << 3) +#define TSSC_CTL_AVG_EN (0x1 << 5) +#define TSSC_CTL_DEB_EN (0x1 << 6) +#define TSSC_CTL_DEB_12_MS (0x2 << 7) /* 1.2 ms */ +#define TSSC_CTL_DEB_16_MS (0x3 << 7) /* 1.6 ms */ +#define TSSC_CTL_DEB_2_MS (0x4 << 7) /* 2 ms */ +#define TSSC_CTL_DEB_3_MS (0x5 << 7) /* 3 ms */ +#define TSSC_CTL_DEB_4_MS (0x6 << 7) /* 4 ms */ +#define TSSC_CTL_DEB_6_MS (0x7 << 7) /* 6 ms */ +#define TSSC_CTL_INTR_FLAG1 (0x1 << 10) +#define TSSC_CTL_DATA (0x1 << 11) +#define TSSC_CTL_SSBI_CTRL_EN (0x1 << 13) + +/* control reg's default state */ +#define TSSC_CTL_STATE ( \ + TSSC_CTL_DEB_12_MS | \ + TSSC_CTL_DEB_EN | \ + TSSC_CTL_AVG_EN | \ + TSSC_CTL_MASTER_MODE | \ + TSSC_CTL_EN) + +#define TSSC_NUMBER_OF_OPERATIONS 2 +#define TS_PENUP_TIMEOUT_MS 20 + +#define TS_DRIVER_NAME "msm_touchscreen" + +#define X_MAX 1024 +#define Y_MAX 1024 +#define P_MAX 256 + +struct ts { + struct input_dev *input; + struct timer_list timer; + int irq; + unsigned int x_max; + unsigned int y_max; +}; + +static void __iomem *virt; +#define TSSC_REG(reg) (virt + TSSC_##reg##_REG) + +static void ts_update_pen_state(struct ts *ts, int x, int y, int pressure) +{ + if (pressure) { + input_report_abs(ts->input, ABS_X, x); + input_report_abs(ts->input, ABS_Y, y); + input_report_abs(ts->input, ABS_PRESSURE, pressure); + input_report_key(ts->input, BTN_TOUCH, !!pressure); + } else { + input_report_abs(ts->input, ABS_PRESSURE, 0); + input_report_key(ts->input, BTN_TOUCH, 0); + } + + input_sync(ts->input); +} + +static void ts_timer(unsigned long arg) +{ + struct ts *ts = (struct ts *)arg; + + ts_update_pen_state(ts, 0, 0, 0); +} + +static irqreturn_t ts_interrupt(int irq, void *dev_id) +{ + u32 avgs, x, y, lx, ly; + u32 num_op, num_samp; + u32 status; + + struct ts *ts = dev_id; + + status = readl_relaxed(TSSC_REG(STATUS)); + avgs = readl_relaxed(TSSC_REG(AVG12)); + x = avgs & 0xFFFF; + y = avgs >> 16; + + /* For pen down make sure that the data just read is still valid. + * The DATA bit will still be set if the ARM9 hasn't clobbered + * the TSSC. If it's not set, then it doesn't need to be cleared + * here, so just return. + */ + if (!(readl_relaxed(TSSC_REG(CTL)) & TSSC_CTL_DATA)) + goto out; + + /* Data has been read, OK to clear the data flag */ + writel_relaxed(TSSC_CTL_STATE, TSSC_REG(CTL)); + /* barrier: Write to complete before the next sample */ + mb(); + /* Valid samples are indicated by the sample number in the status + * register being the number of expected samples and the number of + * samples collected being zero (this check is due to ADC contention). + */ + num_op = (status & TSSC_STS_OPN_BMSK) >> TSSC_STS_OPN_SHIFT; + num_samp = (status & TSSC_STS_NUMSAMP_BMSK) >> TSSC_STS_NUMSAMP_SHFT; + + if ((num_op == TSSC_NUMBER_OF_OPERATIONS) && (num_samp == 0)) { + /* TSSC can do Z axis measurment, but driver doesn't support + * this yet. + */ + + /* + * REMOVE THIS: + * These x, y co-ordinates adjustments will be removed once + * Android framework adds calibration framework. + */ +#ifdef CONFIG_ANDROID_TOUCHSCREEN_MSM_HACKS + lx = ts->x_max - x; + ly = ts->y_max - y; +#else + lx = x; + ly = y; +#endif + ts_update_pen_state(ts, lx, ly, 255); + /* kick pen up timer - to make sure it expires again(!) */ + mod_timer(&ts->timer, + jiffies + msecs_to_jiffies(TS_PENUP_TIMEOUT_MS)); + + } else + printk(KERN_INFO "Ignored interrupt: {%3d, %3d}," + " op = %3d samp = %3d\n", + x, y, num_op, num_samp); + +out: + return IRQ_HANDLED; +} + +static int __devinit ts_probe(struct platform_device *pdev) +{ + int result; + struct input_dev *input_dev; + struct resource *res, *ioarea; + struct ts *ts; + unsigned int x_max, y_max, pressure_max; + struct msm_ts_platform_data *pdata = pdev->dev.platform_data; + + /* The primary initialization of the TS Hardware + * is taken care of by the ADC code on the modem side + */ + + ts = kzalloc(sizeof(struct ts), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!input_dev || !ts) { + result = -ENOMEM; + goto fail_alloc_mem; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + result = -ENOENT; + goto fail_alloc_mem; + } + + ts->irq = platform_get_irq(pdev, 0); + if (!ts->irq) { + dev_err(&pdev->dev, "Could not get IORESOURCE_IRQ\n"); + result = -ENODEV; + goto fail_alloc_mem; + } + + ioarea = request_mem_region(res->start, resource_size(res), pdev->name); + if (!ioarea) { + dev_err(&pdev->dev, "Could not allocate io region\n"); + result = -EBUSY; + goto fail_alloc_mem; + } + + virt = ioremap(res->start, resource_size(res)); + if (!virt) { + dev_err(&pdev->dev, "Could not ioremap region\n"); + result = -ENOMEM; + goto fail_ioremap; + } + + input_dev->name = TS_DRIVER_NAME; + input_dev->phys = "msm_touch/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0002; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &pdev->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE); + input_dev->absbit[BIT_WORD(ABS_MISC)] = BIT_MASK(ABS_MISC); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + if (pdata) { + x_max = pdata->x_max ? : X_MAX; + y_max = pdata->y_max ? : Y_MAX; + pressure_max = pdata->pressure_max ? : P_MAX; + } else { + x_max = X_MAX; + y_max = Y_MAX; + pressure_max = P_MAX; + } + + ts->x_max = x_max; + ts->y_max = y_max; + + input_set_abs_params(input_dev, ABS_X, 0, x_max, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, y_max, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, pressure_max, 0, 0); + + result = input_register_device(input_dev); + if (result) + goto fail_ip_reg; + + ts->input = input_dev; + + setup_timer(&ts->timer, ts_timer, (unsigned long)ts); + result = request_irq(ts->irq, ts_interrupt, IRQF_TRIGGER_RISING, + "touchscreen", ts); + if (result) + goto fail_req_irq; + + platform_set_drvdata(pdev, ts); + + return 0; + +fail_req_irq: + input_unregister_device(input_dev); + input_dev = NULL; +fail_ip_reg: + iounmap(virt); +fail_ioremap: + release_mem_region(res->start, resource_size(res)); +fail_alloc_mem: + input_free_device(input_dev); + kfree(ts); + return result; +} + +static int __devexit ts_remove(struct platform_device *pdev) +{ + struct resource *res; + struct ts *ts = platform_get_drvdata(pdev); + + free_irq(ts->irq, ts); + del_timer_sync(&ts->timer); + + input_unregister_device(ts->input); + iounmap(virt); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + platform_set_drvdata(pdev, NULL); + kfree(ts); + + return 0; +} + +static struct platform_driver ts_driver = { + .probe = ts_probe, + .remove = __devexit_p(ts_remove), + .driver = { + .name = TS_DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init ts_init(void) +{ + return platform_driver_register(&ts_driver); +} +module_init(ts_init); + +static void __exit ts_exit(void) +{ + platform_driver_unregister(&ts_driver); +} +module_exit(ts_exit); + +MODULE_DESCRIPTION("MSM Touch Screen driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:msm_touchscreen");