一个基于linux2.6内核下S3C2410触摸屏驱动

  1. //*********************************************************************************************/
  2. //* Purpose:this is a s3c2410 touch screen drive
  3. //*                                                                                        
  4. //* Author: alvin.zhang                                                                                         
  5. //*
  6. //* Date: 2008/04/06
  7. //* 
  8. //*  Revision: 2009/01/12
  9. //*                                                                                                 
  10. //* Description : the code about s3c2410 touch screen , you can reference it in your code                                                                                          
  11. //**********************************************************************************************/
  12. #include <linux/platform_device.h>
  13. //#include <linux/config.h>
  14. #include <linux/errno.h>
  15. #include <linux/kernel.h>
  16. #include <linux/module.h>
  17. #include <linux/slab.h>
  18. #include <linux/input.h>
  19. #include <linux/init.h>
  20. #include <linux/serio.h>
  21. #include <linux/delay.h>
  22. #include <asm/io.h>
  23. #include <asm/irq.h>
  24. #include <asm/arch/regs-adc.h>
  25. #include <asm/arch/regs-gpio.h>
  26. #include <asm/arch/s3c2410_ts.h>
  27. #include <asm/arch/regs-clock.h>
  28. #include <linux/clk.h>
  29. /* 定义版本*/
  30. #define S3C2410TSVERSION    0x0101
  31. /*IT4INT(1) 为UP_INT   WAIT4INT(0)为 DOWN_INT*/
  32. #define WAIT4INT(x) (((x)<<8) | S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | S3C2410_ADCTSC_XY_PST(3))
  33. /* 设置触摸屏的模式为AUTOPST */
  34. #define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
  35. #define DEBUG_LVL    KERN_DEBUG
  36. #define DEBUG   1 
  37. MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
  38. MODULE_DESCRIPTION("s3c2410 touchscreen driver");
  39. MODULE_LICENSE("GPL");
  40. /*
  41. * Definitions & global arrays.
  42. */
  43. static char *s3c2410ts_name = "s3c2410 TouchScreen";
  44. /*
  45. * Per-touchscreen data.
  46. */
  47. struct s3c2410ts {
  48. /*修改前
  49.     struct input_dev dev;
  50. */
  51. /*add by alvin*/
  52.     struct input_dev *dev;
  53. /*add by alvin*/
  54.     long xp;
  55.     long yp;
  56.     int count;
  57. /*驱动内取样的频率定义在oversampling_shift 作法是取樣
  58. (1<<oversampling_shift) 次,然後取平均值當作最後抓到的座標。  */
  59.     int shift;
  60.     char phys[32];
  61. };
  62. static struct s3c2410ts ts;
  63. static void __iomem *base_addr;
  64. /* 配置GPIO PIN*/
  65. static inline void s3c2410_ts_connect(void)
  66. {
  67.     s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);
  68.     s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON);
  69.     s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON);
  70.     s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);
  71. }
  72. /*
  73.   *   touch_timer_fire这个函数主要实现以下功能:
  74.   *    1、stylus down的时候,在中断函数stylus_updown里面被调用,
  75.   *     此时缓存区没有数据,ts.count为0,所以只是简单的设
  76.   *     置ad转换的模式,然后开启ad转换。
  77.   *    2、但ADC中断函数stylus_action把缓冲区填满的时候,作为中
  78.         断后半段函数稍后被调用,此时ts.count为4,算出其平
  79.         均值后,交给事件处理层(Event Handler)处理,主要是填写
  80.         缓冲,然后唤醒等待输入数据的进程。
  81.   *    3、stylus抬起,等到缓冲区填满后(可能会包含一些无用的数据)被调用,
  82.   *       这时候判断出stylus up,报告stylus up事件,重新等待stylus down。
  83. */
  84. static void touch_timer_fire(unsigned long data)
  85. {
  86.     unsigned long data0;
  87.     unsigned long data1;
  88.     int updown;
  89.     
  90.     data0 = readl(base_addr+S3C2410_ADCDAT0);
  91.     data1 = readl(base_addr+S3C2410_ADCDAT1);
  92.     
  93.     updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT1_UPDOWN));
  94.     
  95.     if (updown) {//注意updown变量只在stylus_updown中更新
  96.         if (ts.count != 0) {/*<功能2>*/
  97.             ts.xp >>= ts.shift;
  98.             ts.yp >>= ts.shift;
  99.             
  100. #ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
  101.             {
  102.                 struct timeval tv;
  103.                 do_gettimeofday(&tv);
  104.                 printk(DEBUG_LVL "T: %06d, X: %03ld, Y: %03ld/n", (int)tv.tv_usec, ts.xp, ts.yp);
  105.                 printk(KERN_INFO "T: %06d, X: %03ld, Y: %03ld/n", (int)tv.tv_usec, ts.xp, ts.yp);
  106.             }
  107. #endif
  108.             /* 报告x、y的绝对坐标值 */
  109.             input_report_abs(ts.dev/*&ts.dev*/, ABS_X, ts.xp);
  110.             input_report_abs(ts.dev/*&ts.dev*/, ABS_Y, ts.yp);
  111.              /* 报告按键事件,键值为1(代表触摸屏对应的按键被按下) */
  112.             input_report_key(ts.dev/*&ts.dev*/, BTN_TOUCH, 1);
  113.              /* 报告触摸屏的状态,1表明触摸屏被按下 */
  114.             input_report_abs(ts.dev/*&ts.dev*/, ABS_PRESSURE, 1);
  115.              /* 等待接收方受到数据后回复确认,用于同步 */
  116.             input_sync(ts.dev/*&ts.dev*/);
  117.         }
  118.         /*<功能1>*/
  119.         ts.xp = 0;
  120.         ts.yp = 0;
  121.         ts.count = 0;
  122.         /* 设置触摸屏的模式为AUTOPST */
  123.         writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
  124.         /* 启动ADC转换 */
  125.         writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
  126.     } else {/*<功能3>*/
  127.         ts.count = 0;
  128.         
  129.         input_report_key(ts.dev/*&ts.dev*/, BTN_TOUCH, 0);
  130.         input_report_abs(ts.dev/*&ts.dev*/, ABS_PRESSURE, 0);
  131.         input_sync(ts.dev/*&ts.dev*/);
  132.         
  133.         writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
  134.     }
  135. }
  136. /*这里初始化定时器,并把中断处理函数指向touch_timer_fire*/
  137. static struct timer_list touch_timer = TIMER_INITIALIZER(touch_timer_fire, 0, 0);
  138. static irqreturn_t stylus_updown(int irq, void *dev_id, struct pt_regs *regs)
  139. {
  140.     unsigned long data0;
  141.     unsigned long data1;
  142.     int updown;
  143.     /********************************debug************************************/
  144.     printk(KERN_INFO "You touch the screen/n");
  145.     /*************************************************************************/
  146.     data0 = readl(base_addr+S3C2410_ADCDAT0);
  147.     data1 = readl(base_addr+S3C2410_ADCDAT1);
  148.     /*更新stylus状态寄变量updown:
  149.        1 = down
  150.        0 = up                                                   */
  151.     updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT1_UPDOWN));
  152.     
  153.     /* TODO we should never get an interrupt with updown set while
  154.     * the timer is running, but maybe we ought to verify that the
  155.     * timer isn't running anyways. */
  156.     
  157.     if (updown)
  158.         touch_timer_fire(0);
  159.     
  160.     return IRQ_HANDLED;
  161. }
  162. /*ADC中断服务程序*/
  163. static irqreturn_t stylus_action(int irq, void *dev_id, struct pt_regs *regs)
  164. {
  165.     unsigned long data0;
  166.     unsigned long data1;
  167.     data0 = readl(base_addr+S3C2410_ADCDAT0);
  168.     data1 = readl(base_addr+S3C2410_ADCDAT1);
  169.     
  170.     ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
  171.     ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
  172.     ts.count++;
  173.     
  174.     if (ts.count < (1<<ts.shift)) {/* 缓冲区未满,再次激活ADC转换 */
  175.         writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
  176.         writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
  177.     } else {/* 缓冲区满,激活下半部处理程序touch_timer_fire,处理接收数据 */
  178.         mod_timer(&touch_timer, jiffies+1);//修改定时器的expire
  179.         writel(WAIT4INT(1), base_addr+S3C2410_ADCTSC);//等待提笔中断
  180.     }
  181.     
  182.     return IRQ_HANDLED;
  183. }
  184. static struct clk   *adc_clock;
  185. /*
  186. * The functions for inserting/removing us as a module.
  187. */
  188. static int __init s3c2410ts_probe(struct device *dev)
  189. {
  190.     struct s3c2410_ts_mach_info *info;
  191.     /*s3c2410_ts_mach_info这个结构需要我们去填充,里面存放的是触摸屏需要的一些配置参数,见下面的附录部分*/
  192.     info = ( struct s3c2410_ts_mach_info *)dev->platform_data;
  193.     
  194.     if (!info)
  195.     {
  196.         printk(KERN_ERR "Hm... too bad : no platform data for ts/n");
  197.         return -EINVAL;
  198.     }
  199.     
  200. #ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
  201.     printk(DEBUG_LVL "Entering s3c2410ts_init/n");
  202. #endif
  203.     
  204.     adc_clock = clk_get(NULL, "adc");//可以获得adc时钟
  205.     if (!adc_clock) {
  206.         printk(KERN_ERR "failed to get adc clock source/n");
  207.         return -ENOENT;
  208.     }
  209.     //clk_use(adc_clock);//这个在高版本下已经不需要了
  210.     clk_enable(adc_clock);// 使能时钟
  211.     
  212. #ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
  213.     printk(DEBUG_LVL "got and enabled clock/n");
  214. #endif
  215.     
  216.     base_addr=ioremap(S3C2410_PA_ADC,0x20);//映射触摸屏的控制寄存器
  217.     if (base_addr == NULL) {
  218.         printk(KERN_ERR "Failed to remap register block/n");
  219.         return -ENOMEM;
  220.     }
  221.     
  222.     
  223.     /* Configure GPIOs */
  224.     s3c2410_ts_connect();
  225.     /* Set the prscvl*/
  226.     if ((info->presc&0xff) > 0)
  227.         writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(info->presc&0xFF),/
  228.                  base_addr+S3C2410_ADCCON);
  229.     else
  230.         writel(0,base_addr+S3C2410_ADCCON);
  231.     
  232.     
  233.     /* Initialise the adcdly registers */
  234.     if ((info->delay&0xffff) > 0)
  235.         writel(info->delay & 0xffff,  base_addr+S3C2410_ADCDLY);
  236.     
  237.     writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
  238.     
  239.     /* Initialise input stuff */
  240.     memset(&ts, 0, sizeof(struct s3c2410ts));
  241.      /* 以下配置2.6内核划分出来的输入设备 */
  242. /* 修改前
  243.     init_input_dev(&ts.dev);//这是低版本linux函数,新版本中已经被取替
  244.     ts.dev.evbit[0] = ts.dev.evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
  245.     ts.dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
  246.     input_set_abs_params(&ts.dev, ABS_X, 0, 0x3FF, 0, 0);
  247.     input_set_abs_params(&ts.dev, ABS_Y, 0, 0x3FF, 0, 0);
  248.     input_set_abs_params(&ts.dev, ABS_PRESSURE, 0, 1, 0, 0);
  249.     
  250.     sprintf(ts.phys, "ts0");
  251.     
  252.     ts.dev.private = &ts;
  253.     ts.dev.name = s3c2410ts_name;
  254.     ts.dev.phys = ts.phys;
  255.     ts.dev.id.bustype = BUS_RS232;
  256.     ts.dev.id.vendor = 0xDEAD;
  257.     ts.dev.id.product = 0xBEEF;
  258.     ts.dev.id.version = S3C2410TSVERSION;
  259. */  
  260. /*add by alvin*/
  261. //  int err;
  262.     if (ts.dev) {
  263.         return -ENODEV; /* already initialized */
  264.     }
  265.     ts.dev = input_allocate_device();
  266.     if (!ts.dev)
  267.         return -ENOMEM;
  268.     /* evbit字段用来定义该输入设备可以支持的(产生和响应)的事件的类型,
  269.        在此触摸屏设置为支持同步(EN_SYN)、按键(EN_KEY)、绝对坐标(EV_ABS)事件*/
  270.     ts.dev->evbit[0] = ts.dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
  271.     /* 设置所支持的按键(键值),触摸屏可以看成只有一个按键的设备 */
  272.     ts.dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
  273.     /* 设置绝对坐标x和y的最小最大值,在这是0-0x3FF */
  274.     input_set_abs_params(ts.dev, ABS_X, 0, 320/*0x3FF*/, 0, 0);
  275.     input_set_abs_params(ts.dev, ABS_Y, 0, 240/*0x3FF*/, 0, 0);
  276.     input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0);
  277.     
  278.     sprintf(ts.phys, "ts0");
  279.     
  280.     ts.dev->private = &ts;
  281.     ts.dev->name = s3c2410ts_name;
  282.     ts.dev->phys = ts.phys;
  283.     ts.dev->id.bustype = BUS_RS232;
  284.     ts.dev->id.vendor = 0xDEAD;
  285.     ts.dev->id.product = 0xBEEF;
  286.     ts.dev->id.version = S3C2410TSVERSION;
  287. /*add by alvin*/     
  288.     ts.shift = info->oversampling_shift;
  289. /* 这个比较重要,配置输入数据的缓存区大小,
  290.        在这里oversampling_shift设为2,也就是缓存区的大小为4(1<<2) */
  291.     
  292.     /* Get irqs */
  293. /* ADC转换中断,转换结束后触发 */
  294.     if (request_irq(IRQ_ADC, stylus_action, SA_SAMPLE_RANDOM,
  295.         "s3c2410_action", ts.dev/*&ts.dev*/)) {
  296.         printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_ADC !/n");
  297.         iounmap(base_addr);
  298.         return -EIO;
  299.     }
  300. /*   触摸屏的中断,按下或提笔触发*/
  301.     if (request_irq(IRQ_TC, stylus_updown, SA_SAMPLE_RANDOM,
  302.         "s3c2410_action", ts.dev/*&ts.dev*/)) {
  303.         printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !/n");
  304.         iounmap(base_addr);
  305.         return -EIO;
  306.     }
  307.     
  308.     printk(KERN_INFO "%s successfully loaded/n", s3c2410ts_name);
  309.     
  310.     /* All went ok, so register to the input system */
  311.     input_register_device(ts.dev/*&ts.dev*/);
  312.     
  313.     return 0;
  314. }
  315. static int s3c2410ts_remove(struct device *dev)
  316. {
  317.     disable_irq(IRQ_ADC);
  318.     disable_irq(IRQ_TC);
  319.     free_irq(IRQ_TC,ts.dev/*&ts.dev*/);
  320.     free_irq(IRQ_ADC,ts.dev/*&ts.dev*/);
  321.     
  322.     if (adc_clock) {
  323.         clk_disable(adc_clock);
  324. //      clk_unuse(adc_clock);
  325.         clk_put(adc_clock);
  326.         adc_clock = NULL;
  327.     }
  328.     
  329.     input_unregister_device(ts.dev/*&ts.dev*/);
  330.     iounmap(base_addr);
  331.     
  332.     return 0;
  333. }
  334. static struct device_driver s3c2410ts_driver = {
  335.     .name           = "s3c2410-ts",
  336.     .bus            = &platform_bus_type,
  337.     .probe          = s3c2410ts_probe,
  338.     .remove         = s3c2410ts_remove,
  339. };
  340. int __init s3c2410ts_init(void)
  341. {
  342.     return driver_register(&s3c2410ts_driver);
  343. }
  344. void __exit s3c2410ts_exit(void)
  345. {
  346.     driver_unregister(&s3c2410ts_driver);
  347. }
  348. module_init(s3c2410ts_init);
  349. module_exit(s3c2410ts_exit);
  350. /*
  351. mach-smdk2410.c中
  352. static struct s3c2410_ts_mach_info s3c2410_ts_cfg __initdata = {
  353.     .delay = 10000,
  354.     .presc = 49,
  355.     .oversampling_shift = 2,
  356. };
  357. 在devs.c中定义
  358. struct platform_device s3c_device_ts = {
  359.         .name = "s3c2410-ts",
  360.         .id = -1,
  361.         .dev = {
  362.                 .platform_data = &s3c2410ts_info,
  363.         }
  364. };
  365. */

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值