内核版本:2.6.36

源码路径:drivers/input/touchscreen/s3c2410_ts.c

 
在Linux-2.6.36中,S3C2410对应的触摸屏驱动程序是drivers/input/touchscreen/s3c2410_ts.c,本文对这个文件进行分析,详细介绍相关知识点。S3C2410的触摸屏驱动和ADC驱动紧密联系在一起,在读本文之前,请大家先看我的上一篇博客《S3C2410驱动分析之ADC通用驱动》。
首先我们看模块初始化函数:

  
  
  
  
  1. 423static struct platform_driver s3c_ts_driver = {  
  2. 424    .driver         = {  
  3. 425        .name   = "samsung-ts",  
  4. 426        .owner  = THIS_MODULE,  
  5. 427#ifdef CONFIG_PM  
  6. 428        .pm = &s3c_ts_pmops,  
  7. 429#endif  
  8. 430    },  
  9. 431    .id_table   = s3cts_driver_ids,  
  10. 432    .probe      = s3c2410ts_probe,  
  11. 433    .remove     = __devexit_p(s3c2410ts_remove),  
  12. 434};  
423static 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};

这里需要关注的是platform_driver结构的id_table和probe两个成员。s3cts_driver_ids定义如下:

  
  
  1. 415static struct platform_device_id s3cts_driver_ids[] = {  
  2. 416    { "s3c2410-ts", 0 },  
  3. 417    { "s3c2440-ts", 0 },  
  4. 418    { "s3c64xx-ts", FEAT_PEN_IRQ },  
  5. 419    { }  
  6. 420};  
415static struct platform_device_id s3cts_driver_ids[] = {
416    { "s3c2410-ts", 0 },
417    { "s3c2440-ts", 0 },
418    { "s3c64xx-ts", FEAT_PEN_IRQ },
419    { }
420};

421MODULE_DEVICE_TABLE(platform, s3cts_driver_ids);
probe成员函数s3c2410ts_probe当模块被加载时就会执行,其代码如下:

  
  
  1. 234/** 
  2. 235 * s3c2410ts_probe - device core probe entry point 
  3. 236 * @pdev: The device we are being bound to. 
  4. 237 * 
  5. 238 * Initialise, find and allocate any resources we need to run and then 
  6. 239 * register with the ADC and input systems. 
  7. 240 */  
  8. 241static int __devinit s3c2410ts_probe(struct platform_device *pdev)  
  9. 242{  
  10. 243    struct s3c2410_ts_mach_info *info;  
  11. 244    struct device *dev = &pdev->dev;  
  12. 245    struct input_dev *input_dev;  
  13. 246    struct resource *res;  
  14. 247    int ret = -EINVAL;  
  15. 248  
  16. 249    /* Initialise input stuff */  
  17. 250    memset(&ts, 0, sizeof(struct s3c2410ts));  
  18. 251  
  19. 252    ts.dev = dev;  
  20. 253  
  21. 254    info = pdev->dev.platform_data;  
  22. 255    if (!info) {  
  23. 256        dev_err(dev, "no platform data, cannot attach\n");  
  24. 257        return -EINVAL;  
  25. 258    }  
  26. 259  
  27. 260    dev_dbg(dev, "initialising touchscreen\n");  
  28. 261  
  29. 262    ts.clock = clk_get(dev, "adc");  
  30. 263    if (IS_ERR(ts.clock)) {  
  31. 264        dev_err(dev, "cannot get adc clock source\n");  
  32. 265        return -ENOENT;  
  33. 266    }  
  34. 267  
  35. 268    clk_enable(ts.clock);  
  36. 269    dev_dbg(dev, "got and enabled clocks\n");  
  37. 270  
  38. 271    ts.irq_tc = ret = platform_get_irq(pdev, 0);  
  39. 272    if (ret < 0) {  
  40. 273        dev_err(dev, "no resource for interrupt\n");  
  41. 274        goto err_clk;  
  42. 275    }  
  43. 276  
  44. 277    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  45. 278    if (!res) {  
  46. 279        dev_err(dev, "no resource for registers\n");  
  47. 280        ret = -ENOENT;  
  48. 281        goto err_clk;  
  49. 282    }  
  50. 283  
  51. 284    ts.io = ioremap(res->start, resource_size(res));  
  52. 285    if (ts.io == NULL) {  
  53. 286        dev_err(dev, "cannot map registers\n");  
  54. 287        ret = -ENOMEM;  
  55. 288        goto err_clk;  
  56. 289    }  
  57. 290  
  58. 291    /* inititalise the gpio */  
  59. 292    if (info->cfg_gpio)  
  60. 293        info->cfg_gpio(to_platform_device(ts.dev));  
  61. 294  
  62. 295    ts.client = s3c_adc_register(pdev, s3c24xx_ts_select,  
  63. 296                     s3c24xx_ts_conversion, 1);  
  64. 297    if (IS_ERR(ts.client)) {  
  65. 298        dev_err(dev, "failed to register adc client\n");  
  66. 299        ret = PTR_ERR(ts.client);  
  67. 300        goto err_iomap;  
  68. 301    }  
  69. 302  
  70. 303    /* Initialise registers */  
  71. 304    if ((info->delay & 0xffff) > 0)  
  72. 305        writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);  
  73. 306  
  74. 307    writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);  
  75. 308  
  76. 309    input_dev = input_allocate_device();  
  77. 310    if (!input_dev) {  
  78. 311        dev_err(dev, "Unable to allocate the input device !!\n");  
  79. 312        ret = -ENOMEM;  
  80. 313        goto err_iomap;  
  81. 314    }  
  82. 315  
  83. 316    ts.input = input_dev;  
  84. 317    ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);  
  85. 318    ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);  
  86. 319    input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);  
  87. 320    input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);  
  88. 321  
  89. 322    ts.input->name = "S3C24XX TouchScreen";  
  90. 323    ts.input->id.bustype = BUS_HOST;  
  91. 324    ts.input->id.vendor = 0xDEAD;  
  92. 325    ts.input->id.product = 0xBEEF;  
  93. 326    ts.input->id.version = 0x0102;  
  94. 327  
  95. 328    ts.shift = info->oversampling_shift;  
  96. 329    ts.features = platform_get_device_id(pdev)->driver_data;  
  97. 330  
  98. 331    ret = request_irq(ts.irq_tc, stylus_irq, IRQF_DISABLED,  
  99. 332              "s3c2410_ts_pen", ts.input);  
  100. 333    if (ret) {  
  101. 334        dev_err(dev, "cannot get TC interrupt\n");  
  102. 335        goto err_inputdev;  
  103. 336    }  
  104. 337  
  105. 338    dev_info(dev, "driver attached, registering input device\n");  
  106. 339  
  107. 340    /* All went ok, so register to the input system */  
  108. 341    ret = input_register_device(ts.input);  
  109. 342    if (ret < 0) {  
  110. 343        dev_err(dev, "failed to register input device\n");  
  111. 344        ret = -EIO;  
  112. 345        goto err_tcirq;  
  113. 346    }  
  114. 347  
  115. 348    return 0;  
  116. 349  
  117. 350 err_tcirq:  
  118. 351    free_irq(ts.irq_tc, ts.input);  
  119. 352 err_inputdev:  
  120. 353    input_unregister_device(ts.input);  
  121. 354 err_iomap:  
  122. 355    iounmap(ts.io);  
  123. 356 err_clk:  
  124. 357    del_timer_sync(&touch_timer);  
  125. 358    clk_put(ts.clock);  
  126. 359    return ret;  
  127. 360}  
234/**
235 * s3c2410ts_probe - device core probe entry point
236 * @pdev: The device we are being bound to.
237 *
238 * Initialise, find and allocate any resources we need to run and then
239 * register with the ADC and input systems.
240 */
241static 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
307    writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
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, IRQF_DISABLED,
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_unregister_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}

243行,定义s3c2410_ts_mach_info结构体变量info,该结构体定义在arch/arm/plat-samsung/include/plat/ts.h文件中:

  
  
  1. 13struct s3c2410_ts_mach_info {  
  2. 14       int             delay;  
  3. 15       int             presc;  
  4. 16       int             oversampling_shift;  
  5. 17    void    (*cfg_gpio)(struct platform_device *dev);  
  6. 18};  
13struct s3c2410_ts_mach_info {
14       int             delay;
15       int             presc;
16       int             oversampling_shift;
17    void    (*cfg_gpio)(struct platform_device *dev);
18};

delay保存ADC的延迟时间。
presc保存ADC的预分频系数。
oversampling_shift保存采样次数log2值。
cfg_gpio函数用于设置gpio。
250行,将ts清0,ts是s3c2410ts结构体类型变量,其定义如下:

  
  
  1. 62/** 
  2. 63 * struct s3c2410ts - driver touchscreen state. 
  3. 64 * @client: The ADC client we registered with the core driver. 
  4. 65 * @dev: The device we are bound to. 
  5. 66 * @input: The input device we registered with the input subsystem. 
  6. 67 * @clock: The clock for the adc. 
  7. 68 * @io: Pointer to the IO base. 
  8. 69 * @xp: The accumulated X position data. 
  9. 70 * @yp: The accumulated Y position data. 
  10. 71 * @irq_tc: The interrupt number for pen up/down interrupt 
  11. 72 * @count: The number of samples collected. 
  12. 73 * @shift: The log2 of the maximum count to read in one go. 
  13. 74 * @features: The features supported by the TSADC MOdule. 
  14. 75 */  
  15. 76struct s3c2410ts {  
  16. 77    struct s3c_adc_client *client;  
  17. 78    struct device *dev;  
  18. 79    struct input_dev *input;  
  19. 80    struct clk *clock;  
  20. 81    void __iomem *io;  
  21. 82    unsigned long xp;  
  22. 83    unsigned long yp;  
  23. 84    int irq_tc;  
  24. 85    int count;  
  25. 86    int shift;  
  26. 87    int features;  
  27. 88};  
  28. 89  
  29. 90static struct s3c2410ts ts;  
62/**
63 * struct s3c2410ts - driver touchscreen state.
64 * @client: The ADC client we registered with the core driver.
65 * @dev: The device we are bound to.
66 * @input: The input device we registered with the input subsystem.
67 * @clock: The clock for the adc.
68 * @io: Pointer to the IO base.
69 * @xp: The accumulated X position data.
70 * @yp: The accumulated Y position data.
71 * @irq_tc: The interrupt number for pen up/down interrupt
72 * @count: The number of samples collected.
73 * @shift: The log2 of the maximum count to read in one go.
74 * @features: The features supported by the TSADC MOdule.
75 */
76struct s3c2410ts {
77    struct s3c_adc_client *client;
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
90static struct s3c2410ts ts;

s3c2410ts结构体的各个成员的含义在注册中已经说的很清楚,这里不再解释。
回到s3c2410ts_probe函数,接着往下看
262 - 268行,获取并使能触摸屏adc时钟,因为AD转换频率与时钟有关。
271行,取得中断号保存在ts.irq_tc中。
277行,取得触摸屏设备的I/O内存保存在res中。
284行,调用ioremap得到触摸屏的I/O内存对应的虚拟地址,保存在ts.io中。
292 - 293行,如果设备定义了cfg_gpio函数,则调用cfg_gpio函数初始化GPIO。
295 - 296行,调用s3c_adc_register函数注册ADC客户(client)。
s3c_adc_register函数定义在arch/arm/plat-samsung/adc.c文件中:

  
  
  1. 207struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev,  
  2. 208                    void (*select)(struct s3c_adc_client *client,  
  3. 209                               unsigned int selected),  
  4. 210                    void (*conv)(struct s3c_adc_client *client,  
  5. 211                             unsigned d0, unsigned d1,  
  6. 212                             unsigned *samples_left),  
  7. 213                    unsigned int is_ts)  
  8. 214{  
  9. 215    struct s3c_adc_client *client;  
  10. 216  
  11. 217    WARN_ON(!pdev);  
  12. 218  
  13. 219    if (!select)  
  14. 220        select = s3c_adc_default_select;  
  15. 221  
  16. 222    if (!pdev)  
  17. 223        return ERR_PTR(-EINVAL);  
  18. 224  
  19. 225    client = kzalloc(sizeof(struct s3c_adc_client), GFP_KERNEL);  
  20. 226    if (!client) {  
  21. 227        dev_err(&pdev->dev, "no memory for adc client\n");  
  22. 228        return ERR_PTR(-ENOMEM);  
  23. 229    }  
  24. 230  
  25. 231    client->pdev = pdev;  
  26. 232    client->is_ts = is_ts;  
  27. 233    client->select_cb = select;  
  28. 234    client->convert_cb = conv;  
  29. 235  
  30. 236    return client;  
  31. 237}  
207struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev,
208                    void (*select)(struct s3c_adc_client *client,
209                               unsigned int selected),
210                    void (*conv)(struct s3c_adc_client *client,
211                             unsigned d0, unsigned d1,
212                             unsigned *samples_left),
213                    unsigned int is_ts)
214{
215    struct s3c_adc_client *client;
216
217    WARN_ON(!pdev);
218
219    if (!select)
220        select = s3c_adc_default_select;
221
222    if (!pdev)
223        return ERR_PTR(-EINVAL);
224
225    client = kzalloc(sizeof(struct s3c_adc_client), GFP_KERNEL);
226    if (!client) {
227        dev_err(&pdev->dev, "no memory for adc client\n");
228        return ERR_PTR(-ENOMEM);
229    }
230
231    client->pdev = pdev;
232    client->is_ts = is_ts;
233    client->select_cb = select;
234    client->convert_cb = conv;
235
236    return client;
237}

s3c_adc_client结构体定义在arch/arm/plat-samsung/adc.c中:

  
  
  1. 46struct s3c_adc_client {  
  2. 47    struct platform_device  *pdev;  
  3. 48    struct list_head     pend;  
  4. 49    wait_queue_head_t   *wait;  
  5. 50  
  6. 51    unsigned int         nr_samples;  
  7. 52    int          result;  
  8. 53    unsigned char        is_ts;  
  9. 54    unsigned char        channel;  
  10. 55  
  11. 56    void    (*select_cb)(struct s3c_adc_client *c, unsigned selected);  
  12. 57    void    (*convert_cb)(struct s3c_adc_client *c,  
  13. 58                  unsigned val1, unsigned val2,  
  14. 59                  unsigned *samples_left);  
  15. 60};  
46struct s3c_adc_client {
47    struct platform_device  *pdev;
48    struct list_head     pend;
49    wait_queue_head_t   *wait;
50
51    unsigned int         nr_samples;
52    int          result;
53    unsigned char        is_ts;
54    unsigned char        channel;
55
56    void    (*select_cb)(struct s3c_adc_client *c, unsigned selected);
57    void    (*convert_cb)(struct s3c_adc_client *c,
58                  unsigned val1, unsigned val2,
59                  unsigned *samples_left);
60};

再回到s3c2410ts_probe函数:
304 - 305行,设定ADC延迟时间,

  
  
  1. 304    if ((info->delay & 0xffff) > 0)  
  2. 305        writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);  
304    if ((info->delay & 0xffff) > 0)
305        writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);

info->delay & 0xffff表示取延迟时间的低32位值。
在arch/arm/plat-samsung/include/plat/regs-adc.h文件中,S3C2410_ADCDLY定义如下:

  
  
  1. #define S3C2410_ADCREG(x) (x)   
  2. #define S3C2410_ADCDLY     S3C2410_ADCREG(0x08)  
#define S3C2410_ADCREG(x) (x)
#define S3C2410_ADCDLY     S3C2410_ADCREG(0x08)

所以S3C2410_ADCDLY的值就是0x08。
查S3C2410 Datasheet,可以发现ADC相关寄存器的起始地址是0x58000000,而ADC start delay(ADCDLY)寄存器的地址是0x58000008。ADC相关寄存器的虚拟地址保存在ts.io中,所以ADCDLY寄存器的虚拟地址是ts.io + 0x08。
再回到s3c2410ts_probe函数,307行,设置ADC touch screen control(ADCTSC)寄存器,进入等待中断模式。

  
  
  1. 307    writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);  
307    writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);

S3C2410_ADCTSC宏定义如下:

  
  
  1. #define S3C2410_ADCREG(x) (x)   
  2. #define S3C2410_ADCTSC     S3C2410_ADCREG(0x04)  
#define S3C2410_ADCREG(x) (x)
#define S3C2410_ADCTSC     S3C2410_ADCREG(0x04)

INT_DOWN宏定义如下:

  
  
  1. 44#define INT_DOWN    (0)  
44#define INT_DOWN    (0)

WAIT4INT宏定义如下:

  
  
  1. 47#define WAIT4INT    (S3C2410_ADCTSC_YM_SEN | \  
  2. 48             S3C2410_ADCTSC_YP_SEN | \  
  3. 49             S3C2410_ADCTSC_XP_SEN | \  
  4. 50             S3C2410_ADCTSC_XY_PST(3))  
47#define WAIT4INT    (S3C2410_ADCTSC_YM_SEN | \
48             S3C2410_ADCTSC_YP_SEN | \
49             S3C2410_ADCTSC_XP_SEN | \
50             S3C2410_ADCTSC_XY_PST(3))

在arch/arm/plat-samsung/include/plat/regs-adc.h文件中,有如下宏定义:

  
  
  1. 41/* ADCTSC Register Bits */  
  2. 42#define S3C2410_ADCTSC_YM_SEN       (1<<7)  
  3. 43#define S3C2410_ADCTSC_YP_SEN       (1<<6)  
  4. 44#define S3C2410_ADCTSC_XM_SEN       (1<<5)  
  5. 45#define S3C2410_ADCTSC_XP_SEN       (1<<4)  
  6. 46#define S3C2410_ADCTSC_PULL_UP_DISABLE  (1<<3)  
  7. 47#define S3C2410_ADCTSC_AUTO_PST     (1<<2)  
  8. 48#define S3C2410_ADCTSC_XY_PST(x)    (((x)&0x3)<<0)  
41/* ADCTSC Register Bits */
42#define S3C2410_ADCTSC_YM_SEN       (1<<7)
43#define S3C2410_ADCTSC_YP_SEN       (1<<6)
44#define S3C2410_ADCTSC_XM_SEN       (1<<5)
45#define S3C2410_ADCTSC_XP_SEN       (1<<4)
46#define S3C2410_ADCTSC_PULL_UP_DISABLE  (1<<3)
47#define S3C2410_ADCTSC_AUTO_PST     (1<<2)
48#define S3C2410_ADCTSC_XY_PST(x)    (((x)&0x3)<<0)

所以用WAIT4INT宏设置ADCTSC寄存器就是把ADCTSC寄存器的第0,1,4,6,7位设置为1。查看S3C2410数据手册可知,把ADCTSC寄存器设置为0xD3表示进入“Waiting for interrupt Mode”。
再回到s3c2410ts_probe函数,309 - 326行分配input_dev并进行初始化:

  
  
  1. 309    input_dev = input_allocate_device();  
  2. 310    if (!input_dev) {  
  3. 311        dev_err(dev, "Unable to allocate the input device !!\n");  
  4. 312        ret = -ENOMEM;  
  5. 313        goto err_iomap;  
  6. 314    }  
  7. 315  
  8. 316    ts.input = input_dev;  
  9. 317    ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);  
  10. 318    ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);  
  11. 319    input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);  
  12. 320    input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);  
  13. 321  
  14. 322    ts.input->name = "S3C24XX TouchScreen";  
  15. 323    ts.input->id.bustype = BUS_HOST;  
  16. 324    ts.input->id.vendor = 0xDEAD;  
  17. 325    ts.input->id.product = 0xBEEF;  
  18. 326    ts.input->id.version = 0x0102;  
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;

309行,调用input_allocate_device分配input_dev设备,并保存在input_dev变量中。
317行,设置input_dev的evbit成员,evbit代表一个input_dev设备支持的事件类型,这里支持按键事件和绝对坐标事件。
318行,设置input_dev的keybit成员,keybit代表按键的类型,这里设置为触摸屏。
319行,设置X坐标,最小值为0,最大值为0x3FF(1023)。
320行,设置Y坐标,最小值为0,最大值为1023。
322 - 326行,设置触摸屏信息,这些信息以后可以在/proc/bus/input/devices中查到。注意323行,设置总线类型为BUS_HOST,根据触摸屏不同,总线可以是I2C,SPI,RS232等各种总线类型。
328行,设置采样次数log2值。
331 - 332行,申请中断,设置中断处理函数为stylus_irq。
341行,注册input_dev设备。
至此,模块初始化及probe过程就完成了。
当触摸屏被按下时,将触发触摸屏中断IRQ_TC,下面看中断处理函数stylus_irq:

  
  
  1. 151/** 
  2. 152 * stylus_irq - touchscreen stylus event interrupt 
  3. 153 * @irq: The interrupt number 
  4. 154 * @dev_id: The device ID. 
  5. 155 * 
  6. 156 * Called when the IRQ_TC is fired for a pen up or down event. 
  7. 157 */  
  8. 158static irqreturn_t stylus_irq(int irq, void *dev_id)  
  9. 159{  
  10. 160    unsigned long data0;  
  11. 161    unsigned long data1;  
  12. 162    bool down;  
  13. 163  
  14. 164    data0 = readl(ts.io + S3C2410_ADCDAT0);  
  15. 165    data1 = readl(ts.io + S3C2410_ADCDAT1);  
  16. 166  
  17. 167    down = get_down(data0, data1);  
  18. 168  
  19. 169    /* TODO we should never get an interrupt with down set while 
  20. 170     * the timer is running, but maybe we ought to verify that the 
  21. 171     * timer isn't running anyways. */  
  22. 172  
  23. 173    if (down)  
  24. 174        s3c_adc_start(ts.client, 0, 1 << ts.shift);  
  25. 175    else  
  26. 176        dev_dbg(ts.dev, "%s: count=%d\n", __func__, ts.count);  
  27. 177  
  28. 178    if (ts.features & FEAT_PEN_IRQ) {  
  29. 179        /* Clear pen down/up interrupt */  
  30. 180        writel(0x0, ts.io + S3C64XX_ADCCLRINTPNDNUP);  
  31. 181    }  
  32. 182  
  33. 183    return IRQ_HANDLED;  
  34. 184}  
151/**
152 * stylus_irq - touchscreen stylus event interrupt
153 * @irq: The interrupt number
154 * @dev_id: The device ID.
155 *
156 * Called when the IRQ_TC is fired for a pen up or down event.
157 */
158static 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}

164 - 165行,读取ADCDAT0和ADCDAT1两个寄存器。注意,如果S3C2410处理器的ADCCON寄存器的READ_START位被置为1,则在读ADCDATA0和ADCDATA1时即会启动下一次AD转换。
167行,调用get_down函数获取触摸屏是否被按下,如果是被按下的,则返回1,否则返回0。get_down函数定义如下:

  
  
  1.  92/** 
  2.  93 * get_down - return the down state of the pen 
  3.  94 * @data0: The data read from ADCDAT0 register. 
  4.  95 * @data1: The data read from ADCDAT1 register. 
  5.  96 * 
  6.  97 * Return non-zero if both readings show that the pen is down. 
  7.  98 */  
  8.  99static inline bool get_down(unsigned long data0, unsigned long data1)  
  9. 100{  
  10. 101    /* returns true if both data values show stylus down */  
  11. 102    return (!(data0 & S3C2410_ADCDAT0_UPDOWN) &&  
  12. 103        !(data1 & S3C2410_ADCDAT0_UPDOWN));  
  13. 104}  
 92/**
 93 * get_down - return the down state of the pen
 94 * @data0: The data read from ADCDAT0 register.
 95 * @data1: The data read from ADCDAT1 register.
 96 *
 97 * Return non-zero if both readings show that the pen is down.
 98 */
 99static 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}
  1. #define S3C2410_ADCDAT0_UPDOWN      (1<<15)   
  2. #define S3C2410_ADCDAT1_UPDOWN      (1<<15)  
#define S3C2410_ADCDAT0_UPDOWN      (1<<15)
#define S3C2410_ADCDAT1_UPDOWN      (1<<15)

ADCDAT0和ADCDAT1寄存器的第15位为0,表示触摸屏被按下,如果为1,表示触摸屏抬起。
回到触摸屏中断处理函数stylus_irq,
173 - 174行,如果触摸屏是被按下的,则调用s3c_adc_start函数,该函数在arch/arm/plat-samsung/adc.c文件中定义,我在《S3C2410驱动分析之ADC通用驱动》中对s3c_adc_start函数已经进行过详细分析,具体函数内容,这里不再重复,这里列出其函数调用逻辑:
s3c_adc_start -> 
s3c_adc_try -> 
s3c_adc_select -> 
client->select_cb(client, 1) 
s3c_adc_convert -> 
s3c_adc_irq -> 
(client->convert_cb)(client, data0, data1, &client->nr_samples)
(client->select_cb)(client, 0)
首先s3c_adc_start完成一些初始化工作后,调用s3c_adc_try,该函数检测等待处理的客户(client),然后调用s3c_adc_select,该函数会调用客户定义的select回调函数选择客户(client)。s3c_adc_try执行完s3c_adc_select后,又会调用s3c_adc_convert,该函数启动AD转换,转换结束后,触发中断,ADC中断处理函数s3c_adc_irq执行,在s3c_adc_irq函数中,会调用客户定义的convert回调函数对转换结果进行处理,然后调用select回调函数取消对客户的选择。
根据上面的调用逻辑,下面我们要分析的就是触摸屏的select和convert回调函数。先看convert回调函数,因为它比较简单,其定义如下:

  
  
  1. 186/** 
  2. 187 * s3c24xx_ts_conversion - ADC conversion callback 
  3. 188 * @client: The client that was registered with the ADC core. 
  4. 189 * @data0: The reading from ADCDAT0. 
  5. 190 * @data1: The reading from ADCDAT1. 
  6. 191 * @left: The number of samples left. 
  7. 192 * 
  8. 193 * Called when a conversion has finished. 
  9. 194 */  
  10. 195static void s3c24xx_ts_conversion(struct s3c_adc_client *client,  
  11. 196                  unsigned data0, unsigned data1,  
  12. 197                  unsigned *left)  
  13. 198{  
  14. 199    dev_dbg(ts.dev, "%s: %d,%d\n", __func__, data0, data1);  
  15. 200  
  16. 201    ts.xp += data0;  
  17. 202    ts.yp += data1;  
  18. 203  
  19. 204    ts.count++;  
  20. 205  
  21. 206    /* From tests, it seems that it is unlikely to get a pen-up 
  22. 207     * event during the conversion process which means we can 
  23. 208     * ignore any pen-up events with less than the requisite 
  24. 209     * count done. 
  25. 210     * 
  26. 211     * In several thousand conversions, no pen-ups where detected 
  27. 212     * before count completed. 
  28. 213     */  
  29. 214}  
186/**
187 * s3c24xx_ts_conversion - ADC conversion callback
188 * @client: The client that was registered with the ADC core.
189 * @data0: The reading from ADCDAT0.
190 * @data1: The reading from ADCDAT1.
191 * @left: The number of samples left.
192 *
193 * Called when a conversion has finished.
194 */
195static 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}

201行,将从ADCDAT0寄存器中读得的x坐标值累加到ts.xp上。
202行,将从ADCDAT1寄存器中读得的y坐标值累加到ts.yp上。
204行,ts.count累加1。
所以,convert回调函数s3c24xx_ts_conversion的作用就是累加每次采样后得到的x,y坐标值,并记录采样次数,在其它函数中,可以取ts.xp和ts.yp的平均值,得到传递给用户空间的x,y坐标值。
下面看触摸屏的select回调函数,其定义如下:

  
  
  1. 216/** 
  2. 217 * s3c24xx_ts_select - ADC selection callback. 
  3. 218 * @client: The client that was registered with the ADC core. 
  4. 219 * @select: The reason for select. 
  5. 220 * 
  6. 221 * Called when the ADC core selects (or deslects) us as a client. 
  7. 222 */  
  8. 223static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select)  
  9. 224{  
  10. 225    if (select) {  
  11. 226        writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,  
  12. 227               ts.io + S3C2410_ADCTSC);  
  13. 228    } else {  
  14. 229        mod_timer(&touch_timer, jiffies+1);  
  15. 230        writel(WAIT4INT | INT_UP, ts.io + S3C2410_ADCTSC);  
  16. 231    }  
  17. 232}  
216/**
217 * s3c24xx_ts_select - ADC selection callback.
218 * @client: The client that was registered with the ADC core.
219 * @select: The reason for select.
220 *
221 * Called when the ADC core selects (or deslects) us as a client.
222 */
223static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select)
224{
225    if (select) {
226        writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
227               ts.io + S3C2410_ADCTSC);
228    } else {
229        mod_timer(&touch_timer, jiffies+1);
230        writel(WAIT4INT | INT_UP, ts.io + S3C2410_ADCTSC);
231    }
232}

S3C2410_ADCTSC_PULL_UP_DISABLE宏表示禁止XP上拉,其定义如下:

  
  
  1. #define S3C2410_ADCTSC_PULL_UP_DISABLE  (1<<3)  
#define S3C2410_ADCTSC_PULL_UP_DISABLE  (1<<3)

AUTOPST宏表示启用自动(连续)x/y轴坐标转换模式,其定义如下:

  
  
  1. 52#define AUTOPST     (S3C2410_ADCTSC_YM_SEN | \  
  2. 53             S3C2410_ADCTSC_YP_SEN | \  
  3. 54             S3C2410_ADCTSC_XP_SEN | \  
  4. 55             S3C2410_ADCTSC_AUTO_PST | \  
  5. 56             S3C2410_ADCTSC_XY_PST(0))  
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))

我们来看select回调函数s3c24xx_ts_select,
225 - 227行,如果select参数为1,则启用自动(连续)x/y轴坐标转换模式。
228 - 231行,如果select参数为0,将touch_timer定时器的到时时间设置为此后一个时钟滴答,并进入中断等待模式。
再次回忆一下前面介绍的s3c_adc_start执行流程:
s3c_adc_start -> 
s3c_adc_try -> 
s3c_adc_select -> 
client->select_cb(client, 1) 
s3c_adc_convert -> 
s3c_adc_irq -> 
(client->convert_cb)(client, data0, data1, &client->nr_samples)
(client->select_cb)(client, 0)
重点看select和convert回调函数的调用。s3c_adc_try首先是调用client->select_cb(client, 1),即启用自动(连续)x/y轴坐标转换模式,然后调用s3c_adc_convert进行AD转换,AD转换结束后,触发AD中断,中断处理函数s3c_adc_irq会首先调用s3c24xx_ts_conversion,累加x,y坐标值及采样次数,然后再调用client->select_cb(client, 0)设置定时器和重新进入等待中断模式。
下面要分析的是定时器touch_timer,其定义如下:

  
  
  1. 149static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0);  
149static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0);

定时器函数touch_timer_fire定义如下:

  
  
  1. 106static void touch_timer_fire(unsigned long data)  
  2. 107{  
  3. 108    unsigned long data0;  
  4. 109    unsigned long data1;  
  5. 110    bool down;  
  6. 111  
  7. 112    data0 = readl(ts.io + S3C2410_ADCDAT0);  
  8. 113    data1 = readl(ts.io + S3C2410_ADCDAT1);  
  9. 114  
  10. 115    down = get_down(data0, data1);  
  11. 116  
  12. 117    if (down) {  
  13. 118        if (ts.count == (1 << ts.shift)) {  
  14. 119            ts.xp >>= ts.shift;  
  15. 120            ts.yp >>= ts.shift;  
  16. 121  
  17. 122            dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n",  
  18. 123                __func__, ts.xp, ts.yp, ts.count);  
  19. 124  
  20. 125            input_report_abs(ts.input, ABS_X, ts.xp);  
  21. 126            input_report_abs(ts.input, ABS_Y, ts.yp);  
  22. 127  
  23. 128            input_report_key(ts.input, BTN_TOUCH, 1);  
  24. 129            input_sync(ts.input);  
  25. 130  
  26. 131            ts.xp = 0;  
  27. 132            ts.yp = 0;  
  28. 133            ts.count = 0;  
  29. 134        }  
  30. 135  
  31. 136        s3c_adc_start(ts.client, 0, 1 << ts.shift);  
  32. 137    } else {  
  33. 138        ts.xp = 0;  
  34. 139        ts.yp = 0;  
  35. 140        ts.count = 0;  
  36. 141  
  37. 142        input_report_key(ts.input, BTN_TOUCH, 0);  
  38. 143        input_sync(ts.input);  
  39. 144  
  40. 145        writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);  
  41. 146    }  
  42. 147}  
106static 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
145        writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
146    }
147}

212 - 215行,取得ADCDAT0和ADCDAT1的值保存在data0和data1中,并由get_down函数判断触摸屏是按下还是松开,如果是按下的,down为1,否则down为0。
117 - 118行,如果触摸屏是按下的,再判断采样次数是否是用户指定的次数。
119行,取ts.xp的平均值,即x坐标平均值。
120行,取ts.yp的平均值,即y坐标平均值。
125行,使用input子系统函数input_report_abs提交x绝对坐标值。
126行,使用input子系统函数input_report_abs提交y绝对坐标值。
128行, 使用input子系统函数input_report_key提交按键事件为触摸屏按下。
129行,使用input子系统函数input_sync(ts.input)同步所有事件,将所有事件组成一个evdev包,并通过/dev/input/eventX发送出去。input子系统要求提交各种事件后必须执行同步。
131 - 133行,将ts.xp,ts.yp和ts.count清0,准备一下次采样。
136行,如果采样次数与用户指定的采样次数不同,则重新执行s3c_adc_start进行采样。但是这里我有个疑问,再次采样,ts.count并没有清0,那么采样计数可以会超过用户指定的次数,这是怎么回事呢?
137 - 146行,如果触摸屏是抬起的,则将ts.xp,ts.yp和ts.count清0,准备一下次采样。然后提交触摸屏抬起事件,并将触摸屏设置为等待中断模式。
至此,整个S3C2410的触摸屏驱动我们就分析完了。
Logo

更多推荐