原创 linux2.6基于yl2440(s3c2440)开发板touchscreen驱动移植

2006-11-13 12:30 5630 6 6 分类: MCU/ 嵌入式

linux 2.6 下基于yl2440板(s3c2440)touch screen 驱动移植过程



    本移植是先从 Ben Dooks 的网站上下载了部分代码,同时在网下载了一个2410的
 touch screen 驱动程序,做了简单修改完成的.这个简单的触摸屏驱动程序还不是
 很完善,还需要改进.
 
简单触摸屏驱动移植过程如下:



一. 如果你的kernel里面没有 reg-adc.h 则需要到 Ben Dooks 的网站下载相关补丁
    文件位置在 include/asm-arm/arch-s3c2410/regs-adc.h, 并在其添加如下内容


#define S3C2410_ADCTSC_XY_PST_N     (0x0<<0)
#define S3C2410_ADCTSC_XY_PST_X     (0x1<<0)
#define S3C2410_ADCTSC_XY_PST_Y  (0x2<<0)
#define S3C2410_ADCTSC_XY_PST_W   (0x3<<0)


二.将下载的2410的驱动程序代码复制到 drivers/input/touchscreen/ 下名为
    s3c2440_ts.c .
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/irq.h>


#include <asm/arch/regs-adc.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/s3c2440_ts.h>
//#include <asm/hardware/clock.h>
#include <asm/arch/regs-clock.h>
#include <linux/clk.h>


/* For ts.dev.id.version */
#define S3C2440TSVERSION 0x0101


#define WAIT4INT(x)  (((x)<<8) | \
       S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
       S3C2410_ADCTSC_XY_PST_W)


#define AUTOPST      (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
       S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST_N)


#define DEBUG_LVL    KERN_INFO


#ifdef CONFIG_TOUCHSCREEN_S3C2440_DEBUG
#define DBG(x...)       printk(KERN_INFO x)
#else
#define DBG(x...)       do { } while (0)
#endif


/*
 * Definitions & global arrays.
 */



static char *s3c2440ts_name = "s3c2440 TouchScreen";


/*
 * Per-touchscreen data.
 */


struct s3c2440ts {
 struct input_dev *dev;
 long xp;
 long yp;
 int count;
 int shift;
 char phys[32];
};


static struct s3c2440ts ts;
static void __iomem *base_addr;


static void touch_timer_fire(unsigned long data)
{
   unsigned long data0;
   unsigned long data1;
 int updown;


   data0 = readl(base_addr+S3C2410_ADCDAT0);
   data1 = readl(base_addr+S3C2410_ADCDAT1);


  updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
 
 DBG(" data0 = %x, data1 = %x updown = %d  %s : %s : line = %d\n", data0, data1, updown,
   __FUNCTION__, __FILE__, __LINE__);


  if (updown) {
   if (ts.count != 0) {
    ts.xp >>= ts.shift;
    ts.yp >>= ts.shift;
 
#ifdef CONFIG_TOUCHSCREEN_S3C2440_DEBUG
    {
     struct timeval tv;
     do_gettimeofday(&tv);
     printk(DEBUG_LVL "T: %06d, X: %03ld, Y: %03ld\n", (int)tv.tv_usec, ts.xp, ts.yp);
    }
#endif
   
    input_report_abs(ts.dev, ABS_X, ts.xp);
    input_report_abs(ts.dev, ABS_Y, ts.yp);
 
    input_report_key(ts.dev, BTN_TOUCH, 1);
    input_report_abs(ts.dev, ABS_PRESSURE, 1);
    input_sync(ts.dev);
   }
  
   ts.xp = 0;
   ts.yp = 0;
   ts.count = 0;
 
   writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
   writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
  } else {
   ts.count = 0;
  
   input_report_key(ts.dev, BTN_TOUCH, 0);
   input_report_abs(ts.dev, ABS_PRESSURE, 0);
   input_sync(ts.dev);
  
   writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
  }
}


static struct timer_list touch_timer = TIMER_INITIALIZER(touch_timer_fire, 0, 0);


static irqreturn_t stylus_updown(int irq, void *dev_id, struct pt_regs *regs)
{
 unsigned long data0;
 unsigned long data1;
 int updown;


 data0 = readl(base_addr+S3C2410_ADCDAT0);
 data1 = readl(base_addr+S3C2410_ADCDAT1);


 updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));


 DBG("\n data0 = %x, data1 = %x updown = %d  %s : %s : line = %d\n", data0, data1, updown,
   __FUNCTION__, __FILE__, __LINE__);


 /* TODO we should never get an interrupt with updown set while
  * the timer is running, but maybe we ought to verify that the
  * timer isn't running anyways. */
 
 if (updown)
  touch_timer_fire(0);


 return IRQ_HANDLED;
}



static irqreturn_t stylus_action(int irq, void *dev_id, struct pt_regs *regs)
{
 unsigned long data0;
 unsigned long data1;
 
 data0 = readl(base_addr+S3C2410_ADCDAT0);
 data1 = readl(base_addr+S3C2410_ADCDAT1);


 ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
 ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
 ts.count++;


    if (ts.count < (1<< ts.shift)) {
  writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
  writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
 } else {


  DBG(" data0 = %x, data1 = %x, %s : %s : line = %d\n", data0, data1, __FUNCTION__, __FILE__, __LINE__);
  mod_timer(&touch_timer, jiffies+1);
  writel(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
 }


 return IRQ_HANDLED;
}


static struct clk *adc_clock;


/*
 * The functions for inserting/removing us as a module.
 */


static int __init s3c2440ts_probe(struct device *dev)
{
 struct s3c2440_ts_mach_info *info;
 
 info = ( struct s3c2440_ts_mach_info *)dev->platform_data;


 if (!info)
 {
  printk(KERN_ERR "Hm... too bad : no platform data for ts\n");
  return -EINVAL;
 }
 
#ifdef CONFIG_TOUCHSCREEN_S3C2440_DEBUG
 printk(DEBUG_LVL "Entering s3c2440ts_init\n");
#endif


 memset(&ts, 0, sizeof(struct s3c2440ts));
 ts.dev = input_allocate_device();
 if (!ts.dev)
 {
  printk(KERN_ERR "%s:%s allocate_device\n", __FUNCTION__, __FILE__);
  return -ENOMEM;
 }


 adc_clock = clk_get(NULL, "adc");
 if (!adc_clock) {
  printk(KERN_ERR "failed to get adc clock source\n");
  return -ENOENT;
 }
 //clk_use(adc_clock);
 clk_enable(adc_clock);


#ifdef CONFIG_TOUCHSCREEN_S3C2440_DEBUG
 printk(DEBUG_LVL "got and enabled clock\n");
#endif


 base_addr = ioremap(S3C2410_PA_ADC, 0x20);
 if (base_addr == NULL) {
  printk(KERN_ERR "Failed to remap register block\n");
  return -ENOMEM;
 }
  


 if ((info->presc&0xff) > 0)
  writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(info->presc&0xFF), base_addr + S3C2410_ADCCON);
 else
  writel(0,base_addr + S3C2410_ADCCON);



 /* Initialise registers */
 if ((info->delay&0xffff) > 0)
  writel(info->delay & 0xffff,  base_addr + S3C2410_ADCDLY);
 
 writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
 
 /* Initialise input stuff */
 //memset(&ts, 0, sizeof(struct s3c2440ts));
 //init_input_dev(&ts.dev);


 ts.dev->evbit[0] = ts.dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
 ts.dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
 input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);
 input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0);
 input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0);


 sprintf(ts.phys, "ts0");


 ts.dev->private = &ts;
 ts.dev->name = s3c2440ts_name;
 ts.dev->phys = ts.phys;
 ts.dev->id.bustype = BUS_RS232;
 ts.dev->id.vendor = 0xDEAD;
 ts.dev->id.product = 0xBEEF;
 ts.dev->id.version = S3C2440TSVERSION;


 ts.shift = info->oversampling_shift;


 /* Get irqs */
 if (request_irq(IRQ_ADC, stylus_action, SA_SAMPLE_RANDOM, "s3c2440_action", ts.dev)) {
  printk(KERN_ERR "s3c2440_ts.c: Could not allocate ts IRQ_ADC !\n");
  iounmap(base_addr);
  return -EIO;
 }
 if (request_irq(IRQ_TC, stylus_updown, SA_SAMPLE_RANDOM, "s3c2440_updown", ts.dev)) {
  printk(KERN_ERR "s3c2440_ts.c: Could not allocate ts IRQ_TC !\n");
  disable_irq(IRQ_ADC);
  free_irq(IRQ_ADC, ts.dev);
  iounmap(base_addr);
  return -EIO;
 }


 printk(KERN_INFO "%s successfully loaded\n", s3c2440ts_name);


 /* All went ok, so register to the input system */
 input_register_device(ts.dev);
 
 return 0;
}


static int s3c2440ts_remove(struct device *dev)
{
 disable_irq(IRQ_ADC);
 disable_irq(IRQ_TC);
 free_irq(IRQ_TC, ts.dev);
 free_irq(IRQ_ADC, ts.dev);


 if (adc_clock) {
  clk_disable(adc_clock);
  //clk_unuse(adc_clock);
  clk_put(adc_clock);
  adc_clock = NULL;
 }
 
 input_unregister_device(ts.dev);
 iounmap(base_addr);


 return 0;
}


static struct device_driver s3c2440ts_driver = {
       .name           = "s3c2440-ts",
       .bus            = &platform_bus_type,
       .probe          = s3c2440ts_probe,
       .remove         = s3c2440ts_remove,
};



int __init s3c2440ts_init(void)
{
 return driver_register(&s3c2440ts_driver);
}


void __exit s3c2440ts_exit(void)
{
 driver_unregister(&s3c2440ts_driver);
}


module_init(s3c2440ts_init);
module_exit(s3c2440ts_exit);


MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("s3c2440 touchscreen driver");
MODULE_LICENSE("GPL");
 


三.将s3c2440_ts.h文件拷贝到include/asm/arch-s3c2410/目录下,其内容为:


#ifndef __ASM_ARM_S3C2440_TS_H
#define __ASM_ARM_S3C2440_TS_H


struct s3c2440_ts_mach_info {
       int             delay;
       int             presc;
       int             oversampling_shift;
};


void __init set_s3c2440ts_info(struct s3c2440_ts_mach_info *hard_s3c2440ts_info);


#endif /* __ASM_ARM_S3C2440_TS_H */


四.在 arch/arm/mach-s3c2410/devs.c文件中添加如下几行


#include <asm/arch/s3c2440_ts.h>


/* Touchscreen */
static struct s3c2440_ts_mach_info s3c2440ts_info;


void __init set_s3c2440ts_info(struct s3c2440_ts_mach_info *hard_s3c2440ts_info)
{
 memcpy(&s3c2440ts_info, hard_s3c2440ts_info, sizeof(struct s3c2440_ts_mach_info));
}


EXPORT_SYMBOL(set_s3c2440ts_info);


struct platform_device s3c_device_ts = {
 .name    = "s3c2440-ts",
 .id    = -1,
 .dev              = {
   .platform_data = &s3c2440ts_info,
 }
};
EXPORT_SYMBOL(s3c_device_ts);


五.在arch/arm/mach-s3c2410/devs.h文件中添加:


extern struct platform_device s3c_device_ts;


六.在arch/arm/mach-s3c2410/mach-smdk2410.c中添加:


#include <asm/arch/s3c2440_ts.h>


static struct s3c2440_ts_mach_info s3c2440_ts_cfg __initdata = {
 .delay = 10000,
 //.delay = 200000,
 .presc = 49,
 .oversampling_shift = 2,
};


 在 smdk2440_devices[] 增加 &s3c_device_ts,
 在 smdk2440_machine_init()函数中调用 set_s3c2440ts_info(&s3c2440_ts_cfg);


七.在drivers/input/touchscreen/Kconfig中添加


config TOUCHSCREEN_S3C2440
 tristate "Samsung S3C2440 touchscreen input driver"
 depends on ARCH_S3C2440 && INPUT && INPUT_TOUCHSCREEN
 select SERIO
 help
   Say Y here if you have the s3c2440 touchscreen.


   If unsure, say N.


   To compile this driver as a module, choose M here: the
   module will be called s3c2440_ts.


config TOUCHSCREEN_S3C2440_DEBUG
 boolean "Samsung S3C2440 touchscreen debug messages"
 depends on TOUCHSCREEN_S3C2440
 help
   Select this if you want debug messages



八.在drivers/input/touchscreen/Makefile中添加:


 obj-$(CONFIG_TOUCHSCREEN_S3C2440) += s3c2440_ts.o



九.配置编译内核


点击看大图


点击看大图



十.创建设备文件
 根据你的配置情况创建相应的设备文件例如:
 mknod -m 666 ts -c 13 128 等
 
注: 因为这个驱动相对校简单,在实际应用中需要对其准确性和效率需要自己验证和改进.

文章评论0条评论)

登录后参与讨论
相关推荐阅读
kenny_95_240432243 2008-09-25 22:20
Linux 文件系统中元数据使用计数的机制
Linux 文件系统中元数据使用计数的机制文档选项将此页作为电子邮件发送未显示需要 JavaScript 的文档选项级别: 初级陈 撰 (zhuanchen@gmail.com), 硕士研究生, 中国...
kenny_95_240432243 2008-09-25 22:14
Linux 时钟处理机制
Linux 时钟处理机制文档选项打印本页将此页作为电子邮件发送未显示需要 JavaScript 的文档选项级别: 初级赵 健博 (zhaojianbo@ncic.ac.cn), 硕士, 中国科学院计算...
kenny_95_240432243 2008-08-29 21:39
深入理解低功耗无线传感器网络标准 (蓝牙、Wi-Fi、ZigBee )
蓝牙、Wi-Fi和ZigBee在无线通信领域都有一席之地。但基于不同原因,上述几种技术都不太适合无线传感器网络应用。用于传感和控制应用的无线产品和技术正迅速变为现实。无线技术的大规模普及只是时间早晚的...
kenny_95_240432243 2008-08-29 21:34
嵌入式开发如何选择MCU
    本文将描述一下嵌入式开发中选择主控 mcu 时要考虑的几个因素。    芯片是否稳定这个因素是首先要考虑的,这个关系到产品开发成败。如果有些 mcu已知的问题是你能够规避和接受的,也可以考虑,...
kenny_95_240432243 2008-08-18 15:34
用vim编辑c文件
手把手教你把Vim改装成一个IDE编程环境(图文)By:吴垠Date:2007-09-07Version:0.5Email:lazy.fox.wu#gmail.comHomepage:http://b...
kenny_95_240432243 2008-08-18 15:31
patch 命令使用
patch 命令用于打补丁,补丁文件是使用diff产生的 patch 命令语法patch [  -b [ -B Prefix ] ] [  -f ] [ -l ] [ -N ] [ -R ] [  -...
我要评论
0
6
1
2
3
4
5
6
7
8
9
0
关闭 热点推荐上一条 /3 下一条