Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
s3c2410_ts.c
Go to the documentation of this file.
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 <[email protected]>
19  * Copyright 2008 Ben Dooks <[email protected]>
20  * Copyright 2009 Simtec Electronics <[email protected]>
21  *
22  * Additional work by Herbert Pƶtzl <[email protected]> and
23  * Harald Welte <[email protected]>
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/init.h>
32 #include <linux/delay.h>
33 #include <linux/interrupt.h>
34 #include <linux/platform_device.h>
35 #include <linux/clk.h>
36 #include <linux/io.h>
37 
38 #include <plat/adc.h>
39 #include <plat/regs-adc.h>
41 
42 #define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0))
43 
44 #define INT_DOWN (0)
45 #define INT_UP (1 << 8)
46 
47 #define WAIT4INT (S3C2410_ADCTSC_YM_SEN | \
48  S3C2410_ADCTSC_YP_SEN | \
49  S3C2410_ADCTSC_XP_SEN | \
50  S3C2410_ADCTSC_XY_PST(3))
51 
52 #define AUTOPST (S3C2410_ADCTSC_YM_SEN | \
53  S3C2410_ADCTSC_YP_SEN | \
54  S3C2410_ADCTSC_XP_SEN | \
55  S3C2410_ADCTSC_AUTO_PST | \
56  S3C2410_ADCTSC_XY_PST(0))
57 
58 #define FEAT_PEN_IRQ (1 << 0) /* HAS ADCCLRINTPNDNUP */
59 
60 /* Per-touchscreen data. */
61 
76 struct s3c2410ts {
78  struct device *dev;
79  struct input_dev *input;
80  struct clk *clock;
81  void __iomem *io;
82  unsigned long xp;
83  unsigned long yp;
84  int irq_tc;
85  int count;
86  int shift;
87  int features;
88 };
89 
90 static struct s3c2410ts ts;
91 
99 static inline bool get_down(unsigned long data0, unsigned long data1)
100 {
101  /* returns true if both data values show stylus down */
102  return (!(data0 & S3C2410_ADCDAT0_UPDOWN) &&
103  !(data1 & S3C2410_ADCDAT0_UPDOWN));
104 }
105 
106 static void touch_timer_fire(unsigned long data)
107 {
108  unsigned long data0;
109  unsigned long data1;
110  bool down;
111 
112  data0 = readl(ts.io + S3C2410_ADCDAT0);
113  data1 = readl(ts.io + S3C2410_ADCDAT1);
114 
115  down = get_down(data0, data1);
116 
117  if (down) {
118  if (ts.count == (1 << ts.shift)) {
119  ts.xp >>= ts.shift;
120  ts.yp >>= ts.shift;
121 
122  dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n",
123  __func__, ts.xp, ts.yp, ts.count);
124 
125  input_report_abs(ts.input, ABS_X, ts.xp);
126  input_report_abs(ts.input, ABS_Y, ts.yp);
127 
128  input_report_key(ts.input, BTN_TOUCH, 1);
129  input_sync(ts.input);
130 
131  ts.xp = 0;
132  ts.yp = 0;
133  ts.count = 0;
134  }
135 
136  s3c_adc_start(ts.client, 0, 1 << ts.shift);
137  } else {
138  ts.xp = 0;
139  ts.yp = 0;
140  ts.count = 0;
141 
142  input_report_key(ts.input, BTN_TOUCH, 0);
143  input_sync(ts.input);
144 
146  }
147 }
148 
149 static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0);
150 
158 static irqreturn_t stylus_irq(int irq, void *dev_id)
159 {
160  unsigned long data0;
161  unsigned long data1;
162  bool down;
163 
164  data0 = readl(ts.io + S3C2410_ADCDAT0);
165  data1 = readl(ts.io + S3C2410_ADCDAT1);
166 
167  down = get_down(data0, data1);
168 
169  /* TODO we should never get an interrupt with down set while
170  * the timer is running, but maybe we ought to verify that the
171  * timer isn't running anyways. */
172 
173  if (down)
174  s3c_adc_start(ts.client, 0, 1 << ts.shift);
175  else
176  dev_dbg(ts.dev, "%s: count=%d\n", __func__, ts.count);
177 
178  if (ts.features & FEAT_PEN_IRQ) {
179  /* Clear pen down/up interrupt */
180  writel(0x0, ts.io + S3C64XX_ADCCLRINTPNDNUP);
181  }
182 
183  return IRQ_HANDLED;
184 }
185 
195 static void s3c24xx_ts_conversion(struct s3c_adc_client *client,
196  unsigned data0, unsigned data1,
197  unsigned *left)
198 {
199  dev_dbg(ts.dev, "%s: %d,%d\n", __func__, data0, data1);
200 
201  ts.xp += data0;
202  ts.yp += data1;
203 
204  ts.count++;
205 
206  /* From tests, it seems that it is unlikely to get a pen-up
207  * event during the conversion process which means we can
208  * ignore any pen-up events with less than the requisite
209  * count done.
210  *
211  * In several thousand conversions, no pen-ups where detected
212  * before count completed.
213  */
214 }
215 
223 static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select)
224 {
225  if (select) {
227  ts.io + S3C2410_ADCTSC);
228  } else {
229  mod_timer(&touch_timer, jiffies+1);
231  }
232 }
233 
241 static int __devinit s3c2410ts_probe(struct platform_device *pdev)
242 {
243  struct s3c2410_ts_mach_info *info;
244  struct device *dev = &pdev->dev;
245  struct input_dev *input_dev;
246  struct resource *res;
247  int ret = -EINVAL;
248 
249  /* Initialise input stuff */
250  memset(&ts, 0, sizeof(struct s3c2410ts));
251 
252  ts.dev = dev;
253 
254  info = pdev->dev.platform_data;
255  if (!info) {
256  dev_err(dev, "no platform data, cannot attach\n");
257  return -EINVAL;
258  }
259 
260  dev_dbg(dev, "initialising touchscreen\n");
261 
262  ts.clock = clk_get(dev, "adc");
263  if (IS_ERR(ts.clock)) {
264  dev_err(dev, "cannot get adc clock source\n");
265  return -ENOENT;
266  }
267 
268  clk_enable(ts.clock);
269  dev_dbg(dev, "got and enabled clocks\n");
270 
271  ts.irq_tc = ret = platform_get_irq(pdev, 0);
272  if (ret < 0) {
273  dev_err(dev, "no resource for interrupt\n");
274  goto err_clk;
275  }
276 
277  res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
278  if (!res) {
279  dev_err(dev, "no resource for registers\n");
280  ret = -ENOENT;
281  goto err_clk;
282  }
283 
284  ts.io = ioremap(res->start, resource_size(res));
285  if (ts.io == NULL) {
286  dev_err(dev, "cannot map registers\n");
287  ret = -ENOMEM;
288  goto err_clk;
289  }
290 
291  /* inititalise the gpio */
292  if (info->cfg_gpio)
293  info->cfg_gpio(to_platform_device(ts.dev));
294 
295  ts.client = s3c_adc_register(pdev, s3c24xx_ts_select,
296  s3c24xx_ts_conversion, 1);
297  if (IS_ERR(ts.client)) {
298  dev_err(dev, "failed to register adc client\n");
299  ret = PTR_ERR(ts.client);
300  goto err_iomap;
301  }
302 
303  /* Initialise registers */
304  if ((info->delay & 0xffff) > 0)
305  writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);
306 
308 
309  input_dev = input_allocate_device();
310  if (!input_dev) {
311  dev_err(dev, "Unable to allocate the input device !!\n");
312  ret = -ENOMEM;
313  goto err_iomap;
314  }
315 
316  ts.input = input_dev;
317  ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
318  ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
319  input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);
320  input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);
321 
322  ts.input->name = "S3C24XX TouchScreen";
323  ts.input->id.bustype = BUS_HOST;
324  ts.input->id.vendor = 0xDEAD;
325  ts.input->id.product = 0xBEEF;
326  ts.input->id.version = 0x0102;
327 
328  ts.shift = info->oversampling_shift;
329  ts.features = platform_get_device_id(pdev)->driver_data;
330 
331  ret = request_irq(ts.irq_tc, stylus_irq, 0,
332  "s3c2410_ts_pen", ts.input);
333  if (ret) {
334  dev_err(dev, "cannot get TC interrupt\n");
335  goto err_inputdev;
336  }
337 
338  dev_info(dev, "driver attached, registering input device\n");
339 
340  /* All went ok, so register to the input system */
341  ret = input_register_device(ts.input);
342  if (ret < 0) {
343  dev_err(dev, "failed to register input device\n");
344  ret = -EIO;
345  goto err_tcirq;
346  }
347 
348  return 0;
349 
350  err_tcirq:
351  free_irq(ts.irq_tc, ts.input);
352  err_inputdev:
353  input_free_device(ts.input);
354  err_iomap:
355  iounmap(ts.io);
356  err_clk:
357  del_timer_sync(&touch_timer);
358  clk_put(ts.clock);
359  return ret;
360 }
361 
368 static int __devexit s3c2410ts_remove(struct platform_device *pdev)
369 {
370  free_irq(ts.irq_tc, ts.input);
371  del_timer_sync(&touch_timer);
372 
373  clk_disable(ts.clock);
374  clk_put(ts.clock);
375 
376  input_unregister_device(ts.input);
377  iounmap(ts.io);
378 
379  return 0;
380 }
381 
382 #ifdef CONFIG_PM
383 static int s3c2410ts_suspend(struct device *dev)
384 {
386  disable_irq(ts.irq_tc);
387  clk_disable(ts.clock);
388 
389  return 0;
390 }
391 
392 static int s3c2410ts_resume(struct device *dev)
393 {
394  struct platform_device *pdev = to_platform_device(dev);
395  struct s3c2410_ts_mach_info *info = pdev->dev.platform_data;
396 
397  clk_enable(ts.clock);
398  enable_irq(ts.irq_tc);
399 
400  /* Initialise registers */
401  if ((info->delay & 0xffff) > 0)
402  writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);
403 
405 
406  return 0;
407 }
408 
409 static const struct dev_pm_ops s3c_ts_pmops = {
410  .suspend = s3c2410ts_suspend,
411  .resume = s3c2410ts_resume,
412 };
413 #endif
414 
415 static struct platform_device_id s3cts_driver_ids[] = {
416  { "s3c2410-ts", 0 },
417  { "s3c2440-ts", 0 },
418  { "s3c64xx-ts", FEAT_PEN_IRQ },
419  { }
420 };
421 MODULE_DEVICE_TABLE(platform, s3cts_driver_ids);
422 
423 static struct platform_driver s3c_ts_driver = {
424  .driver = {
425  .name = "samsung-ts",
426  .owner = THIS_MODULE,
427 #ifdef CONFIG_PM
428  .pm = &s3c_ts_pmops,
429 #endif
430  },
431  .id_table = s3cts_driver_ids,
432  .probe = s3c2410ts_probe,
433  .remove = __devexit_p(s3c2410ts_remove),
434 };
435 module_platform_driver(s3c_ts_driver);
436 
437 MODULE_AUTHOR("Arnaud Patard <[email protected]>, "
438  "Ben Dooks <[email protected]>, "
439  "Simtec Electronics <[email protected]>");
440 MODULE_DESCRIPTION("S3C24XX Touchscreen driver");
441 MODULE_LICENSE("GPL v2");