Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 1 | /* drivers/input/touchscreen/msm_touch.c |
| 2 | * |
| 3 | * Copyright (c) 2008-2009, 2011, Code Aurora Forum. All rights reserved. |
| 4 | * |
| 5 | * This software is licensed under the terms of the GNU General Public |
| 6 | * License version 2, as published by the Free Software Foundation, and |
| 7 | * may be copied, distributed, and modified under those terms. |
| 8 | * |
| 9 | * This program is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | * GNU General Public License for more details. |
| 13 | * |
| 14 | */ |
| 15 | |
| 16 | #include <linux/slab.h> |
| 17 | #include <linux/kernel.h> |
| 18 | #include <linux/module.h> |
| 19 | #include <linux/interrupt.h> |
| 20 | #include <linux/input.h> |
| 21 | #include <linux/platform_device.h> |
| 22 | #include <linux/jiffies.h> |
| 23 | #include <linux/io.h> |
| 24 | |
| 25 | #include <mach/msm_touch.h> |
| 26 | |
| 27 | /* HW register map */ |
| 28 | #define TSSC_CTL_REG 0x100 |
| 29 | #define TSSC_SI_REG 0x108 |
| 30 | #define TSSC_OPN_REG 0x104 |
| 31 | #define TSSC_STATUS_REG 0x10C |
| 32 | #define TSSC_AVG12_REG 0x110 |
| 33 | |
| 34 | /* status bits */ |
| 35 | #define TSSC_STS_OPN_SHIFT 0x6 |
| 36 | #define TSSC_STS_OPN_BMSK 0x1C0 |
| 37 | #define TSSC_STS_NUMSAMP_SHFT 0x1 |
| 38 | #define TSSC_STS_NUMSAMP_BMSK 0x3E |
| 39 | |
| 40 | /* CTL bits */ |
| 41 | #define TSSC_CTL_EN (0x1 << 0) |
| 42 | #define TSSC_CTL_SW_RESET (0x1 << 2) |
| 43 | #define TSSC_CTL_MASTER_MODE (0x3 << 3) |
| 44 | #define TSSC_CTL_AVG_EN (0x1 << 5) |
| 45 | #define TSSC_CTL_DEB_EN (0x1 << 6) |
| 46 | #define TSSC_CTL_DEB_12_MS (0x2 << 7) /* 1.2 ms */ |
| 47 | #define TSSC_CTL_DEB_16_MS (0x3 << 7) /* 1.6 ms */ |
| 48 | #define TSSC_CTL_DEB_2_MS (0x4 << 7) /* 2 ms */ |
| 49 | #define TSSC_CTL_DEB_3_MS (0x5 << 7) /* 3 ms */ |
| 50 | #define TSSC_CTL_DEB_4_MS (0x6 << 7) /* 4 ms */ |
| 51 | #define TSSC_CTL_DEB_6_MS (0x7 << 7) /* 6 ms */ |
| 52 | #define TSSC_CTL_INTR_FLAG1 (0x1 << 10) |
| 53 | #define TSSC_CTL_DATA (0x1 << 11) |
| 54 | #define TSSC_CTL_SSBI_CTRL_EN (0x1 << 13) |
| 55 | |
| 56 | /* control reg's default state */ |
| 57 | #define TSSC_CTL_STATE ( \ |
| 58 | TSSC_CTL_DEB_12_MS | \ |
| 59 | TSSC_CTL_DEB_EN | \ |
| 60 | TSSC_CTL_AVG_EN | \ |
| 61 | TSSC_CTL_MASTER_MODE | \ |
| 62 | TSSC_CTL_EN) |
| 63 | |
| 64 | #define TSSC_NUMBER_OF_OPERATIONS 2 |
| 65 | #define TS_PENUP_TIMEOUT_MS 20 |
| 66 | |
| 67 | #define TS_DRIVER_NAME "msm_touchscreen" |
| 68 | |
| 69 | #define X_MAX 1024 |
| 70 | #define Y_MAX 1024 |
| 71 | #define P_MAX 256 |
| 72 | |
| 73 | struct ts { |
| 74 | struct input_dev *input; |
| 75 | struct timer_list timer; |
| 76 | int irq; |
| 77 | unsigned int x_max; |
| 78 | unsigned int y_max; |
| 79 | }; |
| 80 | |
| 81 | static void __iomem *virt; |
| 82 | #define TSSC_REG(reg) (virt + TSSC_##reg##_REG) |
| 83 | |
| 84 | static void ts_update_pen_state(struct ts *ts, int x, int y, int pressure) |
| 85 | { |
| 86 | if (pressure) { |
| 87 | input_report_abs(ts->input, ABS_X, x); |
| 88 | input_report_abs(ts->input, ABS_Y, y); |
| 89 | input_report_abs(ts->input, ABS_PRESSURE, pressure); |
| 90 | input_report_key(ts->input, BTN_TOUCH, !!pressure); |
| 91 | } else { |
| 92 | input_report_abs(ts->input, ABS_PRESSURE, 0); |
| 93 | input_report_key(ts->input, BTN_TOUCH, 0); |
| 94 | } |
| 95 | |
| 96 | input_sync(ts->input); |
| 97 | } |
| 98 | |
| 99 | static void ts_timer(unsigned long arg) |
| 100 | { |
| 101 | struct ts *ts = (struct ts *)arg; |
| 102 | |
| 103 | ts_update_pen_state(ts, 0, 0, 0); |
| 104 | } |
| 105 | |
| 106 | static irqreturn_t ts_interrupt(int irq, void *dev_id) |
| 107 | { |
| 108 | u32 avgs, x, y, lx, ly; |
| 109 | u32 num_op, num_samp; |
| 110 | u32 status; |
| 111 | |
| 112 | struct ts *ts = dev_id; |
| 113 | |
| 114 | status = readl_relaxed(TSSC_REG(STATUS)); |
| 115 | avgs = readl_relaxed(TSSC_REG(AVG12)); |
| 116 | x = avgs & 0xFFFF; |
| 117 | y = avgs >> 16; |
| 118 | |
| 119 | /* For pen down make sure that the data just read is still valid. |
| 120 | * The DATA bit will still be set if the ARM9 hasn't clobbered |
| 121 | * the TSSC. If it's not set, then it doesn't need to be cleared |
| 122 | * here, so just return. |
| 123 | */ |
| 124 | if (!(readl_relaxed(TSSC_REG(CTL)) & TSSC_CTL_DATA)) |
| 125 | goto out; |
| 126 | |
| 127 | /* Data has been read, OK to clear the data flag */ |
| 128 | writel_relaxed(TSSC_CTL_STATE, TSSC_REG(CTL)); |
| 129 | /* barrier: Write to complete before the next sample */ |
| 130 | mb(); |
| 131 | /* Valid samples are indicated by the sample number in the status |
| 132 | * register being the number of expected samples and the number of |
| 133 | * samples collected being zero (this check is due to ADC contention). |
| 134 | */ |
| 135 | num_op = (status & TSSC_STS_OPN_BMSK) >> TSSC_STS_OPN_SHIFT; |
| 136 | num_samp = (status & TSSC_STS_NUMSAMP_BMSK) >> TSSC_STS_NUMSAMP_SHFT; |
| 137 | |
| 138 | if ((num_op == TSSC_NUMBER_OF_OPERATIONS) && (num_samp == 0)) { |
| 139 | /* TSSC can do Z axis measurment, but driver doesn't support |
| 140 | * this yet. |
| 141 | */ |
| 142 | |
| 143 | /* |
| 144 | * REMOVE THIS: |
| 145 | * These x, y co-ordinates adjustments will be removed once |
| 146 | * Android framework adds calibration framework. |
| 147 | */ |
| 148 | #ifdef CONFIG_ANDROID_TOUCHSCREEN_MSM_HACKS |
| 149 | lx = ts->x_max - x; |
| 150 | ly = ts->y_max - y; |
| 151 | #else |
| 152 | lx = x; |
| 153 | ly = y; |
| 154 | #endif |
| 155 | ts_update_pen_state(ts, lx, ly, 255); |
| 156 | /* kick pen up timer - to make sure it expires again(!) */ |
| 157 | mod_timer(&ts->timer, |
| 158 | jiffies + msecs_to_jiffies(TS_PENUP_TIMEOUT_MS)); |
| 159 | |
| 160 | } else |
| 161 | printk(KERN_INFO "Ignored interrupt: {%3d, %3d}," |
| 162 | " op = %3d samp = %3d\n", |
| 163 | x, y, num_op, num_samp); |
| 164 | |
| 165 | out: |
| 166 | return IRQ_HANDLED; |
| 167 | } |
| 168 | |
| 169 | static int __devinit ts_probe(struct platform_device *pdev) |
| 170 | { |
| 171 | int result; |
| 172 | struct input_dev *input_dev; |
| 173 | struct resource *res, *ioarea; |
| 174 | struct ts *ts; |
| 175 | unsigned int x_max, y_max, pressure_max; |
| 176 | struct msm_ts_platform_data *pdata = pdev->dev.platform_data; |
| 177 | |
| 178 | /* The primary initialization of the TS Hardware |
| 179 | * is taken care of by the ADC code on the modem side |
| 180 | */ |
| 181 | |
| 182 | ts = kzalloc(sizeof(struct ts), GFP_KERNEL); |
| 183 | input_dev = input_allocate_device(); |
| 184 | if (!input_dev || !ts) { |
| 185 | result = -ENOMEM; |
| 186 | goto fail_alloc_mem; |
| 187 | } |
| 188 | |
| 189 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 190 | if (!res) { |
| 191 | dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); |
| 192 | result = -ENOENT; |
| 193 | goto fail_alloc_mem; |
| 194 | } |
| 195 | |
| 196 | ts->irq = platform_get_irq(pdev, 0); |
| 197 | if (!ts->irq) { |
| 198 | dev_err(&pdev->dev, "Could not get IORESOURCE_IRQ\n"); |
| 199 | result = -ENODEV; |
| 200 | goto fail_alloc_mem; |
| 201 | } |
| 202 | |
| 203 | ioarea = request_mem_region(res->start, resource_size(res), pdev->name); |
| 204 | if (!ioarea) { |
| 205 | dev_err(&pdev->dev, "Could not allocate io region\n"); |
| 206 | result = -EBUSY; |
| 207 | goto fail_alloc_mem; |
| 208 | } |
| 209 | |
| 210 | virt = ioremap(res->start, resource_size(res)); |
| 211 | if (!virt) { |
| 212 | dev_err(&pdev->dev, "Could not ioremap region\n"); |
| 213 | result = -ENOMEM; |
| 214 | goto fail_ioremap; |
| 215 | } |
| 216 | |
| 217 | input_dev->name = TS_DRIVER_NAME; |
| 218 | input_dev->phys = "msm_touch/input0"; |
| 219 | input_dev->id.bustype = BUS_HOST; |
| 220 | input_dev->id.vendor = 0x0001; |
| 221 | input_dev->id.product = 0x0002; |
| 222 | input_dev->id.version = 0x0100; |
| 223 | input_dev->dev.parent = &pdev->dev; |
| 224 | |
| 225 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); |
| 226 | input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE); |
| 227 | input_dev->absbit[BIT_WORD(ABS_MISC)] = BIT_MASK(ABS_MISC); |
| 228 | input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); |
| 229 | |
| 230 | if (pdata) { |
| 231 | x_max = pdata->x_max ? : X_MAX; |
| 232 | y_max = pdata->y_max ? : Y_MAX; |
| 233 | pressure_max = pdata->pressure_max ? : P_MAX; |
| 234 | } else { |
| 235 | x_max = X_MAX; |
| 236 | y_max = Y_MAX; |
| 237 | pressure_max = P_MAX; |
| 238 | } |
| 239 | |
| 240 | ts->x_max = x_max; |
| 241 | ts->y_max = y_max; |
| 242 | |
| 243 | input_set_abs_params(input_dev, ABS_X, 0, x_max, 0, 0); |
| 244 | input_set_abs_params(input_dev, ABS_Y, 0, y_max, 0, 0); |
| 245 | input_set_abs_params(input_dev, ABS_PRESSURE, 0, pressure_max, 0, 0); |
| 246 | |
| 247 | result = input_register_device(input_dev); |
| 248 | if (result) |
| 249 | goto fail_ip_reg; |
| 250 | |
| 251 | ts->input = input_dev; |
| 252 | |
| 253 | setup_timer(&ts->timer, ts_timer, (unsigned long)ts); |
| 254 | result = request_irq(ts->irq, ts_interrupt, IRQF_TRIGGER_RISING, |
| 255 | "touchscreen", ts); |
| 256 | if (result) |
| 257 | goto fail_req_irq; |
| 258 | |
| 259 | platform_set_drvdata(pdev, ts); |
| 260 | |
| 261 | return 0; |
| 262 | |
| 263 | fail_req_irq: |
| 264 | input_unregister_device(input_dev); |
| 265 | input_dev = NULL; |
| 266 | fail_ip_reg: |
| 267 | iounmap(virt); |
| 268 | fail_ioremap: |
| 269 | release_mem_region(res->start, resource_size(res)); |
| 270 | fail_alloc_mem: |
| 271 | input_free_device(input_dev); |
| 272 | kfree(ts); |
| 273 | return result; |
| 274 | } |
| 275 | |
| 276 | static int __devexit ts_remove(struct platform_device *pdev) |
| 277 | { |
| 278 | struct resource *res; |
| 279 | struct ts *ts = platform_get_drvdata(pdev); |
| 280 | |
| 281 | free_irq(ts->irq, ts); |
| 282 | del_timer_sync(&ts->timer); |
| 283 | |
| 284 | input_unregister_device(ts->input); |
| 285 | iounmap(virt); |
| 286 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 287 | release_mem_region(res->start, resource_size(res)); |
| 288 | platform_set_drvdata(pdev, NULL); |
| 289 | kfree(ts); |
| 290 | |
| 291 | return 0; |
| 292 | } |
| 293 | |
| 294 | static struct platform_driver ts_driver = { |
| 295 | .probe = ts_probe, |
| 296 | .remove = __devexit_p(ts_remove), |
| 297 | .driver = { |
| 298 | .name = TS_DRIVER_NAME, |
| 299 | .owner = THIS_MODULE, |
| 300 | }, |
| 301 | }; |
| 302 | |
| 303 | static int __init ts_init(void) |
| 304 | { |
| 305 | return platform_driver_register(&ts_driver); |
| 306 | } |
| 307 | module_init(ts_init); |
| 308 | |
| 309 | static void __exit ts_exit(void) |
| 310 | { |
| 311 | platform_driver_unregister(&ts_driver); |
| 312 | } |
| 313 | module_exit(ts_exit); |
| 314 | |
| 315 | MODULE_DESCRIPTION("MSM Touch Screen driver"); |
| 316 | MODULE_LICENSE("GPL v2"); |
| 317 | MODULE_ALIAS("platform:msm_touchscreen"); |