Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.19-rc5 445 lines 11 kB view raw
1/* 2 * Samsung S3C24XX touchscreen driver 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the term of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 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 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 * 18 * Copyright 2004 Arnaud Patard <arnaud.patard@rtp-net.org> 19 * Copyright 2008 Ben Dooks <ben-linux@fluff.org> 20 * Copyright 2009 Simtec Electronics <linux@simtec.co.uk> 21 * 22 * Additional work by Herbert Pötzl <herbert@13thfloor.at> and 23 * Harald Welte <laforge@openmoko.org> 24 */ 25 26#include <linux/errno.h> 27#include <linux/kernel.h> 28#include <linux/module.h> 29#include <linux/gpio.h> 30#include <linux/input.h> 31#include <linux/delay.h> 32#include <linux/interrupt.h> 33#include <linux/platform_device.h> 34#include <linux/clk.h> 35#include <linux/io.h> 36 37#include <plat/adc.h> 38#include <plat/regs-adc.h> 39#include <linux/platform_data/touchscreen-s3c2410.h> 40 41#define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0)) 42 43#define INT_DOWN (0) 44#define INT_UP (1 << 8) 45 46#define WAIT4INT (S3C2410_ADCTSC_YM_SEN | \ 47 S3C2410_ADCTSC_YP_SEN | \ 48 S3C2410_ADCTSC_XP_SEN | \ 49 S3C2410_ADCTSC_XY_PST(3)) 50 51#define AUTOPST (S3C2410_ADCTSC_YM_SEN | \ 52 S3C2410_ADCTSC_YP_SEN | \ 53 S3C2410_ADCTSC_XP_SEN | \ 54 S3C2410_ADCTSC_AUTO_PST | \ 55 S3C2410_ADCTSC_XY_PST(0)) 56 57#define FEAT_PEN_IRQ (1 << 0) /* HAS ADCCLRINTPNDNUP */ 58 59/* Per-touchscreen data. */ 60 61/** 62 * struct s3c2410ts - driver touchscreen state. 63 * @client: The ADC client we registered with the core driver. 64 * @dev: The device we are bound to. 65 * @input: The input device we registered with the input subsystem. 66 * @clock: The clock for the adc. 67 * @io: Pointer to the IO base. 68 * @xp: The accumulated X position data. 69 * @yp: The accumulated Y position data. 70 * @irq_tc: The interrupt number for pen up/down interrupt 71 * @count: The number of samples collected. 72 * @shift: The log2 of the maximum count to read in one go. 73 * @features: The features supported by the TSADC MOdule. 74 */ 75struct s3c2410ts { 76 struct s3c_adc_client *client; 77 struct device *dev; 78 struct input_dev *input; 79 struct clk *clock; 80 void __iomem *io; 81 unsigned long xp; 82 unsigned long yp; 83 int irq_tc; 84 int count; 85 int shift; 86 int features; 87}; 88 89static struct s3c2410ts ts; 90 91/** 92 * get_down - return the down state of the pen 93 * @data0: The data read from ADCDAT0 register. 94 * @data1: The data read from ADCDAT1 register. 95 * 96 * Return non-zero if both readings show that the pen is down. 97 */ 98static inline bool get_down(unsigned long data0, unsigned long data1) 99{ 100 /* returns true if both data values show stylus down */ 101 return (!(data0 & S3C2410_ADCDAT0_UPDOWN) && 102 !(data1 & S3C2410_ADCDAT0_UPDOWN)); 103} 104 105static void touch_timer_fire(struct timer_list *unused) 106{ 107 unsigned long data0; 108 unsigned long data1; 109 bool down; 110 111 data0 = readl(ts.io + S3C2410_ADCDAT0); 112 data1 = readl(ts.io + S3C2410_ADCDAT1); 113 114 down = get_down(data0, data1); 115 116 if (down) { 117 if (ts.count == (1 << ts.shift)) { 118 ts.xp >>= ts.shift; 119 ts.yp >>= ts.shift; 120 121 dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n", 122 __func__, ts.xp, ts.yp, ts.count); 123 124 input_report_abs(ts.input, ABS_X, ts.xp); 125 input_report_abs(ts.input, ABS_Y, ts.yp); 126 127 input_report_key(ts.input, BTN_TOUCH, 1); 128 input_sync(ts.input); 129 130 ts.xp = 0; 131 ts.yp = 0; 132 ts.count = 0; 133 } 134 135 s3c_adc_start(ts.client, 0, 1 << ts.shift); 136 } else { 137 ts.xp = 0; 138 ts.yp = 0; 139 ts.count = 0; 140 141 input_report_key(ts.input, BTN_TOUCH, 0); 142 input_sync(ts.input); 143 144 writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC); 145 } 146} 147 148static DEFINE_TIMER(touch_timer, touch_timer_fire); 149 150/** 151 * stylus_irq - touchscreen stylus event interrupt 152 * @irq: The interrupt number 153 * @dev_id: The device ID. 154 * 155 * Called when the IRQ_TC is fired for a pen up or down event. 156 */ 157static irqreturn_t stylus_irq(int irq, void *dev_id) 158{ 159 unsigned long data0; 160 unsigned long data1; 161 bool down; 162 163 data0 = readl(ts.io + S3C2410_ADCDAT0); 164 data1 = readl(ts.io + S3C2410_ADCDAT1); 165 166 down = get_down(data0, data1); 167 168 /* TODO we should never get an interrupt with down set while 169 * the timer is running, but maybe we ought to verify that the 170 * timer isn't running anyways. */ 171 172 if (down) 173 s3c_adc_start(ts.client, 0, 1 << ts.shift); 174 else 175 dev_dbg(ts.dev, "%s: count=%d\n", __func__, ts.count); 176 177 if (ts.features & FEAT_PEN_IRQ) { 178 /* Clear pen down/up interrupt */ 179 writel(0x0, ts.io + S3C64XX_ADCCLRINTPNDNUP); 180 } 181 182 return IRQ_HANDLED; 183} 184 185/** 186 * s3c24xx_ts_conversion - ADC conversion callback 187 * @client: The client that was registered with the ADC core. 188 * @data0: The reading from ADCDAT0. 189 * @data1: The reading from ADCDAT1. 190 * @left: The number of samples left. 191 * 192 * Called when a conversion has finished. 193 */ 194static void s3c24xx_ts_conversion(struct s3c_adc_client *client, 195 unsigned data0, unsigned data1, 196 unsigned *left) 197{ 198 dev_dbg(ts.dev, "%s: %d,%d\n", __func__, data0, data1); 199 200 ts.xp += data0; 201 ts.yp += data1; 202 203 ts.count++; 204 205 /* From tests, it seems that it is unlikely to get a pen-up 206 * event during the conversion process which means we can 207 * ignore any pen-up events with less than the requisite 208 * count done. 209 * 210 * In several thousand conversions, no pen-ups where detected 211 * before count completed. 212 */ 213} 214 215/** 216 * s3c24xx_ts_select - ADC selection callback. 217 * @client: The client that was registered with the ADC core. 218 * @select: The reason for select. 219 * 220 * Called when the ADC core selects (or deslects) us as a client. 221 */ 222static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select) 223{ 224 if (select) { 225 writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, 226 ts.io + S3C2410_ADCTSC); 227 } else { 228 mod_timer(&touch_timer, jiffies+1); 229 writel(WAIT4INT | INT_UP, ts.io + S3C2410_ADCTSC); 230 } 231} 232 233/** 234 * s3c2410ts_probe - device core probe entry point 235 * @pdev: The device we are being bound to. 236 * 237 * Initialise, find and allocate any resources we need to run and then 238 * register with the ADC and input systems. 239 */ 240static int s3c2410ts_probe(struct platform_device *pdev) 241{ 242 struct s3c2410_ts_mach_info *info; 243 struct device *dev = &pdev->dev; 244 struct input_dev *input_dev; 245 struct resource *res; 246 int ret = -EINVAL; 247 248 /* Initialise input stuff */ 249 memset(&ts, 0, sizeof(struct s3c2410ts)); 250 251 ts.dev = dev; 252 253 info = dev_get_platdata(dev); 254 if (!info) { 255 dev_err(dev, "no platform data, cannot attach\n"); 256 return -EINVAL; 257 } 258 259 dev_dbg(dev, "initialising touchscreen\n"); 260 261 ts.clock = clk_get(dev, "adc"); 262 if (IS_ERR(ts.clock)) { 263 dev_err(dev, "cannot get adc clock source\n"); 264 return -ENOENT; 265 } 266 267 ret = clk_prepare_enable(ts.clock); 268 if (ret) { 269 dev_err(dev, "Failed! to enabled clocks\n"); 270 goto err_clk_get; 271 } 272 dev_dbg(dev, "got and enabled clocks\n"); 273 274 ts.irq_tc = ret = platform_get_irq(pdev, 0); 275 if (ret < 0) { 276 dev_err(dev, "no resource for interrupt\n"); 277 goto err_clk; 278 } 279 280 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 281 if (!res) { 282 dev_err(dev, "no resource for registers\n"); 283 ret = -ENOENT; 284 goto err_clk; 285 } 286 287 ts.io = ioremap(res->start, resource_size(res)); 288 if (ts.io == NULL) { 289 dev_err(dev, "cannot map registers\n"); 290 ret = -ENOMEM; 291 goto err_clk; 292 } 293 294 /* inititalise the gpio */ 295 if (info->cfg_gpio) 296 info->cfg_gpio(to_platform_device(ts.dev)); 297 298 ts.client = s3c_adc_register(pdev, s3c24xx_ts_select, 299 s3c24xx_ts_conversion, 1); 300 if (IS_ERR(ts.client)) { 301 dev_err(dev, "failed to register adc client\n"); 302 ret = PTR_ERR(ts.client); 303 goto err_iomap; 304 } 305 306 /* Initialise registers */ 307 if ((info->delay & 0xffff) > 0) 308 writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY); 309 310 writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC); 311 312 input_dev = input_allocate_device(); 313 if (!input_dev) { 314 dev_err(dev, "Unable to allocate the input device !!\n"); 315 ret = -ENOMEM; 316 goto err_iomap; 317 } 318 319 ts.input = input_dev; 320 ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 321 ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 322 input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0); 323 input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0); 324 325 ts.input->name = "S3C24XX TouchScreen"; 326 ts.input->id.bustype = BUS_HOST; 327 ts.input->id.vendor = 0xDEAD; 328 ts.input->id.product = 0xBEEF; 329 ts.input->id.version = 0x0102; 330 331 ts.shift = info->oversampling_shift; 332 ts.features = platform_get_device_id(pdev)->driver_data; 333 334 ret = request_irq(ts.irq_tc, stylus_irq, 0, 335 "s3c2410_ts_pen", ts.input); 336 if (ret) { 337 dev_err(dev, "cannot get TC interrupt\n"); 338 goto err_inputdev; 339 } 340 341 dev_info(dev, "driver attached, registering input device\n"); 342 343 /* All went ok, so register to the input system */ 344 ret = input_register_device(ts.input); 345 if (ret < 0) { 346 dev_err(dev, "failed to register input device\n"); 347 ret = -EIO; 348 goto err_tcirq; 349 } 350 351 return 0; 352 353 err_tcirq: 354 free_irq(ts.irq_tc, ts.input); 355 err_inputdev: 356 input_free_device(ts.input); 357 err_iomap: 358 iounmap(ts.io); 359 err_clk: 360 clk_disable_unprepare(ts.clock); 361 del_timer_sync(&touch_timer); 362 err_clk_get: 363 clk_put(ts.clock); 364 return ret; 365} 366 367/** 368 * s3c2410ts_remove - device core removal entry point 369 * @pdev: The device we are being removed from. 370 * 371 * Free up our state ready to be removed. 372 */ 373static int s3c2410ts_remove(struct platform_device *pdev) 374{ 375 free_irq(ts.irq_tc, ts.input); 376 del_timer_sync(&touch_timer); 377 378 clk_disable_unprepare(ts.clock); 379 clk_put(ts.clock); 380 381 input_unregister_device(ts.input); 382 iounmap(ts.io); 383 384 return 0; 385} 386 387#ifdef CONFIG_PM 388static int s3c2410ts_suspend(struct device *dev) 389{ 390 writel(TSC_SLEEP, ts.io + S3C2410_ADCTSC); 391 disable_irq(ts.irq_tc); 392 clk_disable(ts.clock); 393 394 return 0; 395} 396 397static int s3c2410ts_resume(struct device *dev) 398{ 399 struct platform_device *pdev = to_platform_device(dev); 400 struct s3c2410_ts_mach_info *info = dev_get_platdata(&pdev->dev); 401 402 clk_enable(ts.clock); 403 enable_irq(ts.irq_tc); 404 405 /* Initialise registers */ 406 if ((info->delay & 0xffff) > 0) 407 writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY); 408 409 writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC); 410 411 return 0; 412} 413 414static const struct dev_pm_ops s3c_ts_pmops = { 415 .suspend = s3c2410ts_suspend, 416 .resume = s3c2410ts_resume, 417}; 418#endif 419 420static const struct platform_device_id s3cts_driver_ids[] = { 421 { "s3c2410-ts", 0 }, 422 { "s3c2440-ts", 0 }, 423 { "s3c64xx-ts", FEAT_PEN_IRQ }, 424 { } 425}; 426MODULE_DEVICE_TABLE(platform, s3cts_driver_ids); 427 428static struct platform_driver s3c_ts_driver = { 429 .driver = { 430 .name = "samsung-ts", 431#ifdef CONFIG_PM 432 .pm = &s3c_ts_pmops, 433#endif 434 }, 435 .id_table = s3cts_driver_ids, 436 .probe = s3c2410ts_probe, 437 .remove = s3c2410ts_remove, 438}; 439module_platform_driver(s3c_ts_driver); 440 441MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>, " 442 "Ben Dooks <ben@simtec.co.uk>, " 443 "Simtec Electronics <linux@simtec.co.uk>"); 444MODULE_DESCRIPTION("S3C24XX Touchscreen driver"); 445MODULE_LICENSE("GPL v2");