/* drivers/input/touchscreen/msm_touch.c * * Copyright (c) 2008-2009, 2011, The Linux Foundation. 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");