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