S3C2440驱动简析——触摸屏驱动

S3C2440驱动简析——触摸屏驱动

    本文将简要分析2440的触摸屏驱动,其驱动程序内核自带。在浏览本文之前,如果对Linux 驱动的input 子系统没有认识的话,请先回头参考鄙人之前的博文《input子系统》http://blog.csdn.net/jarvis_xian/article/details/6552579

 

    事不宜迟,马上进入本文的正题。有经验的朋友都知道,看驱动,先找入口、出口!

[cpp] view plain copy
  1. <span style="font-size:18px;">static int __init s3c2410ts_init(void)  
  2. {  
  3.     return platform_driver_register(&s3c_ts_driver);//注册platform驱动,注意参数s3c_ts_driver  
  4. }  
  5.   
  6. static void __exit s3c2410ts_exit(void)  
  7. {  
  8.     platform_driver_unregister(&s3c_ts_driver);   //注销platform驱动  
  9. }</span>  

platform_driver_register 的参数s3c_ts_driver 定义如下

[cpp] view plain copy
  1. <span style="font-size:18px;">static struct platform_driver s3c_ts_driver = {  
  2.     .driver         = {  
  3.         .name   = "samsung-ts",  
  4.         .owner  = THIS_MODULE,  
  5. #ifdef CONFIG_PM  
  6.         .pm = &s3c_ts_pmops,  
  7. #endif  
  8.     },  
  9.     .id_table   = s3cts_driver_ids,  
  10.     .probe      = s3c2410ts_probe,  
  11.     .remove     = __devexit_p(s3c2410ts_remove),  
  12. };</span>  

在上述结构体中,我们应该把精力放在.probe、.remove和.pm上

探讨probe 函数前,我们先看一下结构体ts 的定义,其统筹了整个驱动所用到的资源

[cpp] view plain copy
  1. <span style="font-size:18px;">struct s3c2410ts {  
  2.     struct s3c_adc_client *client;  
  3.     struct device *dev;  
  4.     struct input_dev *input;  
  5.     struct clk *clock;  
  6.     void __iomem *io;  
  7.     unsigned long xp;  
  8.     unsigned long yp;  
  9.     int irq_tc;  
  10.     int count;  
  11.     int shift;  
  12.     int features;  
  13. };</span>  


s3c2410ts_probe代码如下:

[cpp] view plain copy
  1. <span style="font-size:18px;">static int __devinit s3c2410ts_probe(struct platform_device *pdev)  
  2. {  
  3.     struct s3c2410_ts_mach_info *info;  
  4.     struct device *dev = &pdev->dev;  
  5.     struct input_dev *input_dev;  
  6.     struct resource *res;  
  7.     int ret = -EINVAL;  
  8.   
  9.     /* Initialise input stuff */  
  10.     memset(&ts, 0, sizeof(struct s3c2410ts));  
  11.   
  12.     ts.dev = dev;   //把platform的设备挂到ts 结构体  
  13.   
  14.     info = pdev->dev.platform_data;  
  15.     if (!info) {  
  16.         dev_err(dev, "no platform data, cannot attach\n");  
  17.         return -EINVAL;  
  18.     }  
  19.   
  20.     dev_dbg(dev, "initialising touchscreen\n");  
  21.   
  22.     ts.clock = clk_get(dev, "adc");   //得到adc 时钟资源  
  23.     if (IS_ERR(ts.clock)) {  
  24.         dev_err(dev, "cannot get adc clock source\n");  
  25.         return -ENOENT;  
  26.     }  
  27.   
  28.     clk_enable(ts.clock);   //使能ts 时钟  
  29.     dev_dbg(dev, "got and enabled clocks\n");  
  30.   
  31.     ts.irq_tc = ret = platform_get_irq(pdev, 0);   //获取中断资源  
  32.     if (ret < 0) {  
  33.         dev_err(dev, "no resource for interrupt\n");  
  34.         goto err_clk;  
  35.     }  
  36.   
  37.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);   //获取占用端口的大小  
  38.     if (!res) {  
  39.         dev_err(dev, "no resource for registers\n");  
  40.         ret = -ENOENT;  
  41.         goto err_clk;  
  42.     }  
  43.   
  44.     ts.io = ioremap(res->start, resource_size(res));   //物理地址映射到虚拟地址  
  45.     if (ts.io == NULL) {  
  46.         dev_err(dev, "cannot map registers\n");  
  47.         ret = -ENOMEM;  
  48.         goto err_clk;  
  49.     }  
  50.   
  51.     /* inititalise the gpio */  
  52.     if (info->cfg_gpio)  
  53.         info->cfg_gpio(to_platform_device(ts.dev));  
  54.   
  55.     ts.client = s3c_adc_register(pdev, s3c24xx_ts_select,  
  56.                      s3c24xx_ts_conversion, 1);  
  57.     if (IS_ERR(ts.client)) {  
  58.         dev_err(dev, "failed to register adc client\n");  
  59.         ret = PTR_ERR(ts.client);  
  60.         goto err_iomap;  
  61.     }  
  62.   
  63.     /* Initialise registers */  
  64.     if ((info->delay & 0xffff) > 0)  
  65.         writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);   //初始化延时寄存器  
  66.   
  67.     writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);  
  68.   
  69.     input_dev = input_allocate_device();   //为input_dev 分配空间并初始化  
  70.     if (!input_dev) {  
  71.         dev_err(dev, "Unable to allocate the input device !!\n");  
  72.         ret = -ENOMEM;  
  73.         goto err_iomap;  
  74.     }  
  75.   
  76.     ts.input = input_dev;   //把input_dev 挂接到ts 结构体  
  77.     ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); //设置事件类型  
  78.     ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);  
  79.     input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);   //设置边界条件等  
  80.     input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);  
  81.   
  82.     ts.input->name = "S3C24XX TouchScreen";  
  83.     ts.input->id.bustype = BUS_HOST;  
  84.     ts.input->id.vendor = 0xDEAD;  
  85.     ts.input->id.product = 0xBEEF;  
  86.     ts.input->id.version = 0x0102;  
  87.   
  88.     ts.shift = info->oversampling_shift;  
  89.     ts.features = platform_get_device_id(pdev)->driver_data;  
  90.   
  91.     ret = request_irq(ts.irq_tc, stylus_irq, IRQF_DISABLED,  
  92.               "s3c2410_ts_pen", ts.input);  
  93.     if (ret) {  
  94.         dev_err(dev, "cannot get TC interrupt\n");  
  95.         goto err_inputdev;  
  96.     }  
  97.   
  98.     dev_info(dev, "driver attached, registering input device\n");  
  99.   
  100.     /* All went ok, so register to the input system */  
  101.     ret = input_register_device(ts.input);   //初始化完毕后,注册input 子系统  
  102.     if (ret < 0) {  
  103.         dev_err(dev, "failed to register input device\n");  
  104.         ret = -EIO;  
  105.         goto err_tcirq;  
  106.     }  
  107.   
  108.     return 0;  
  109.   
  110.  err_tcirq:  
  111.     free_irq(ts.irq_tc, ts.input);  
  112.  err_inputdev:  
  113.     input_free_device(ts.input);  
  114.  err_iomap:  
  115.     iounmap(ts.io);  
  116.  err_clk:  
  117.     del_timer_sync(&touch_timer);  
  118.     clk_put(ts.clock);  
  119.     return ret;  
  120. }</span>  
[cpp] view plain copy
  1. <span style="font-size:18px;"> </span>  
[cpp] view plain copy
  1. <span style="font-size:18px;">相对于probe,remove的代码如下</span><pre class="cpp" name="code"><span style="font-size:18px;">static int __devexit s3c2410ts_remove(struct platform_device *pdev)  
  2. {  
  3.     free_irq(ts.irq_tc, ts.input);   //释放中断资源  
  4.     del_timer_sync(&touch_timer);   //卸载软件时钟  
  5.   
  6.     clk_disable(ts.clock);   //禁能  
  7.     clk_put(ts.clock);  
  8.   
  9.     input_unregister_device(ts.input);   //注销输入子系统  
  10.     iounmap(ts.io);   //解除地址映射  
  11.   
  12.     return 0;  
  13. }</span>  
[cpp] view plain copy
  1. <span style="font-size:18px;"> </span>  
[cpp] view plain copy
  1. <span style="font-size:18px;">至于上文提及的.pm,实则也是一个结构体,如下</span><pre class="cpp" name="code"><span style="font-size:18px;">static struct dev_pm_ops s3c_ts_pmops = {  
  2.     .suspend    = s3c2410ts_suspend,  
  3.     .resume     = s3c2410ts_resume,  
  4. };  
  5. 包含了suspend 和resume 两个操作。</span>  
[cpp] view plain copy
  1. <span style="font-size:18px;"> </span>  
[cpp] view plain copy
  1. <span style="font-size:18px;">s3c2410ts_suspend函数代码:</span><pre class="cpp" name="code"><span style="font-size:18px;">static int s3c2410ts_suspend(struct device *dev)  
  2. {  
  3.     writel(TSC_SLEEP, ts.io + S3C2410_ADCTSC);   //把挂起状态写入硬件寄存器  
  4.     disable_irq(ts.irq_tc);     
  5.     clk_disable(ts.clock);  
  6.   
  7.     return 0;  
  8. }</span>  
[cpp] view plain copy
  1. <span style="font-size:18px;"> </span>  
[cpp] view plain copy
  1. <span style="font-size:18px;">s3c2410ts_resume函数代码:</span><pre class="cpp" name="code"><span style="font-size:18px;">static int s3c2410ts_resume(struct device *dev)  
  2. {  
  3.     struct platform_device *pdev = to_platform_device(dev);  
  4.     struct s3c2410_ts_mach_info *info = pdev->dev.platform_data;  
  5.   
  6.     clk_enable(ts.clock);  
  7.     enable_irq(ts.irq_tc);  
  8.   
  9.     /* Initialise registers */  
  10.     if ((info->delay & 0xffff) > 0)   //写入延时初值  
  11.         writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);  
  12.   
  13.     writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);   //写入工作状态  
  14.   
  15.     return 0;  
  16. }</span>  
[cpp] view plain copy
  1. <span style="font-size:18px;"> </span>  
[cpp] view plain copy
  1. <span style="font-size:18px;">经过上述代码的铺垫,我们已经有了一个舒适的环境,剩下的工作,就在与中断相关的函数里完成了。</span>  
[cpp] view plain copy
  1. <pre class="cpp" name="code"><span style="font-size:18px;">static irqreturn_t stylus_irq(int irq, void *dev_id)  
  2. {  
  3.     unsigned long data0;  
  4.     unsigned long data1;  
  5.     bool down;  
  6.   
  7.     data0 = readl(ts.io + S3C2410_ADCDAT0);   //读取X、Y轴坐标  
  8.     data1 = readl(ts.io + S3C2410_ADCDAT1);  
  9.   
  10.     down = get_down(data0, data1);   //关于get_down函数查看下文  
  11.   
  12.     /* TODO we should never get an interrupt with down set while 
  13.      * the timer is running, but maybe we ought to verify that the 
  14.      * timer isn't running anyways. */  
  15.   
  16.     if (down)  
  17.         s3c_adc_start(ts.client, 0, 1 << ts.shift);   //开始ADC转换  
  18.     else  
  19.         dev_dbg(ts.dev, "%s: count=%d\n", __func__, ts.count);  
  20.   
  21.     if (ts.features & FEAT_PEN_IRQ) {   //清除标志位(等待下一次中断的到来)  
  22.         /* Clear pen down/up interrupt */  
  23.         writel(0x0, ts.io + S3C64XX_ADCCLRINTPNDNUP);  
  24.     }  
  25.   
  26.     return IRQ_HANDLED;  
  27. }</span><pre class="cpp" name="code"><span style="font-size:18px;">static inline bool get_down(unsigned long data0, unsigned long data1)  
  28. {   //检测触摸屏有没有被按下  
  29.     /* returns true if both data values show stylus down */  
  30.     return (!(data0 & S3C2410_ADCDAT0_UPDOWN) &&  
  31.         !(data1 & S3C2410_ADCDAT0_UPDOWN));  
  32. }</span>  
[cpp] view plain copy
  1. <span style="font-size:18px;"> </span>  
[cpp] view plain copy
  1. <span style="font-size:18px;">touch_timer_fire函数将触摸屏的数据报告给内核</span>  
[cpp] view plain copy
  1. <pre class="cpp" name="code"><span style="font-size:18px;">static void touch_timer_fire(unsigned long data)  
  2. {  
  3.     unsigned long data0;  
  4.     unsigned long data1;  
  5.     bool down;  
  6.   
  7.     data0 = readl(ts.io + S3C2410_ADCDAT0);  
  8.     data1 = readl(ts.io + S3C2410_ADCDAT1);  
  9.   
  10.     down = get_down(data0, data1);  
  11.   
  12.     if (down) {  
  13.         if (ts.count == (1 << ts.shift)) {  
  14.             ts.xp >>= ts.shift;  
  15.             ts.yp >>= ts.shift;  
  16.   
  17.             dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n",  
  18.                 __func__, ts.xp, ts.yp, ts.count);  
  19.   
  20.             input_report_abs(ts.input, ABS_X, ts.xp);   //熟悉的操作~~~  
  21.             input_report_abs(ts.input, ABS_Y, ts.yp);  
  22.   
  23.             input_report_key(ts.input, BTN_TOUCH, 1);  
  24.             input_sync(ts.input);   //结束同步  
  25.   
  26.             ts.xp = 0;  
  27.             ts.yp = 0;  
  28.             ts.count = 0;  
  29.         }  
  30.   
  31.         s3c_adc_start(ts.client, 0, 1 << ts.shift);  
  32.     } else {  
  33.         ts.xp = 0;  
  34.         ts.yp = 0;  
  35.         ts.count = 0;  
  36.   
  37.         input_report_key(ts.input, BTN_TOUCH, 0);  
  38.         input_sync(ts.input);  
  39.   
  40.         writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);  
  41.     }  
  42. }</span>  

  1.  最后,我们可以看到触摸屏驱动是基于platform + input子系统 搭建起来的,正如搭积木一样,只要理解了每一个系统、部件的特点和接口等等,再稍加努力消化,就可把各式简单驱动程序的架构辨清,继而为自己编写驱动或者修改驱动打下了一个良好的基础。以上仅是菜鸟之愚见,还请前辈多多指教!

你可能感兴趣的:(c,timer,工作,report,Module,input)