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

2006-11-13 12:30 6181 10 10 分类: 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 等
 
注: 因为这个驱动相对校简单,在实际应用中需要对其准确性和效率需要自己验证和改进.

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
10
关闭 站长推荐上一条 /3 下一条