s3c2440 linux 触摸屏驱动

主机:VM - redhat 9.0

开发板:FL2440,linux-2.6.12

arm-linux-gcc:3.4.1


代码中的第90行,若不支持拖拽,则可以获取到笔触坐标,但是LCD上的图标没有响应,不知什么原因。

#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/devfs_fs_kernel.h>

#include <asm/hardware/clock.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>

// YM_SEN输出驱动器使能,XP_SEN,YP_SEN输出驱动器禁止
// S3C2410_ADCTSC_XY_PST(3) -- 手动测量X、Y方向,等待中断模式
// x=0, 将ADCTSC[8]设置为0,即检测笔尖落下中断信号
#define WAIT4INT(x)  (((x)<<8) | \
		     S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
		     S3C2410_ADCTSC_XY_PST(3))
		     
/* ADCTSC Register Bits */
#define S3C2410_ADCTSC_YM_SEN		(1<<7)
#define S3C2410_ADCTSC_YP_SEN		(1<<6)
#define S3C2410_ADCTSC_XM_SEN		(1<<5)
#define S3C2410_ADCTSC_XP_SEN		(1<<4)
#define S3C2410_ADCTSC_PULL_UP_DISABLE	(1<<3)
#define S3C2410_ADCTSC_AUTO_PST		(1<<2)
#define S3C2410_ADCTSC_XY_PST(x)	(((x)&0x3)<<0)

/* ADCCON Register Bits */
#define S3C2410_ADCCON_ECFLG		(1<<15)
#define S3C2410_ADCCON_PRSCEN		(1<<14)
#define S3C2410_ADCCON_PRSCVL(x)	(((x)&0xFF)<<6)
#define S3C2410_ADCCON_PRSCVLMASK	(0xFF<<6)
#define S3C2410_ADCCON_SELMUX(x)	(((x)&0x7)<<3)
#define S3C2410_ADCCON_MUXMASK		(0x7<<3)
#define S3C2410_ADCCON_STDBM		(1<<2)
#define S3C2410_ADCCON_READ_START	(1<<1)
#define S3C2410_ADCCON_ENABLE_START	(1<<0)
#define S3C2410_ADCCON_STARTMASK	(0x3<<0)

#ifdef S3C2410_ADCCON
#undef S3C2410_ADCCON
#endif

#ifdef S3C2410_ADCTSC
#undef S3C2410_ADCTSC
#endif

#ifdef S3C2410_ADCDLY
#undef S3C2410_ADCDLY
#endif

#ifdef S3C2410_ADCDAT0
#undef S3C2410_ADCDAT0
#endif

#ifdef S3C2410_ADCDAT1
#undef S3C2410_ADCDAT1
#endif

#ifdef S3C2410_PA_ADC
#undef S3C2410_PA_ADC
#endif

#define S3C2410_PA_ADC	   (0x58000000) 

static void __iomem *base_addr;
#define S3C2410_ADCCON (base_addr+(0x00))
#define S3C2410_ADCTSC (base_addr+(0x04))
#define S3C2410_ADCDLY (base_addr+(0x08))
#define S3C2410_ADCDAT0 (base_addr+(0x0c))
#define S3C2410_ADCDAT1 (base_addr+(0x10))


#ifdef CONFIG_PM
#include <linux/pm.h>
#endif

// 支持拖拽
// 若不支持拖拽,则可以获取到笔触坐标,但是LCD上的图标没有响应。
// 可以看到read接口被调用到,不知为何图标没有响应。
#define HOOK_FOR_DRAG


//#define	DEBUG
#ifdef DEBUG
#define DPRINTK	printk
#else
#define DPRINTK
#endif

typedef struct {
  unsigned short pressure;
  unsigned short x;
  unsigned short y;
  unsigned short pad;
} TS_RET;

typedef struct {
  int xscale;
  int xtrans;
  int yscale;
  int ytrans;
  int xyswap;
} TS_CAL;

#define PEN_UP	        0		
#define PEN_DOWN	1
#define PEN_FLEETING	2
#define MAX_TS_BUF	16	/* how many do we want to buffer */

#undef USE_ASYNC	
#define DEVICE_NAME	"s3c2410-ts"
#define TSRAW_MINOR	1

typedef struct {
	unsigned int penStatus;		/* PEN_UP, PEN_DOWN, PEN_SAMPLE */
	TS_RET buf[MAX_TS_BUF];		/* protect against overrun */
	unsigned int head, tail;	/* head and tail for queued events */
	wait_queue_head_t wq;
	spinlock_t lock;
#ifdef USE_ASYNC
	struct fasync_struct *aq;
#endif
#ifdef CONFIG_PM
	struct pm_dev *pm_dev;
#endif
} TS_DEV;

static TS_DEV tsdev;

#define BUF_HEAD	(tsdev.buf[tsdev.head])
#define BUF_TAIL	(tsdev.buf[tsdev.tail])
#define INCBUF(x,mod) 	((++(x)) & ((mod) - 1))

static int tsMajor = 0;

static void (*tsEvent)(void);

#ifdef HOOK_FOR_DRAG
#define TS_TIMER_DELAY  (HZ/100) /* 10 ms */
static struct timer_list ts_timer;
#endif

// pick-up regs val from 2.4.18&2440
// YM_SEN输出驱动器使能,XP上拉使能,正常ADC转换,检测笔尖落下中断信号,等待中断模式
#define wait_down_int()	__raw_writel(0x000000d3,S3C2410_ADCTSC)

// YM_SEN输出驱动器使能,XP上拉使能,正常ADC转换,检测笔尖抬起中断信号,等待中断模式
#define wait_up_int()	__raw_writel(0x000001d3, S3C2410_ADCTSC)

// XM_SEN、XP_SEN输出驱动器使能,XP上拉禁止,正常ADC转换,X方向测量模式
#define mode_x_axis()	__raw_writel(0x00000069, S3C2410_ADCTSC)

// 相当于__raw_writel(0x00000068, S3C2410_ADCTSC),即
// XM_SEN、YP_SEN使能,XP上拉禁止,正常ADC转换,无操作模式
#define mode_x_axis_n()	__raw_writel(XP_EXTVLT | XM_GND | YP_AIN | YM_HIZ | \
				XP_PULL_UP_DIS | XP_PST(NOP_MODE), S3C2410_ADCTSC)
				
// YM_SEN、YP_SEN使能,XP上拉禁止,正常ADC转换,Y方向测量模式
#define mode_y_axis()	__raw_writel(0x0000009a, S3C2410_ADCTSC)

// __raw_writel(0x00007ffa, S3C2410_ADCCON); -- A/D转换器预分频器使能,预分频值0xff,模拟输入通道SEL_MUX为XP,正常工作模式,使能读启动操作
// __raw_readl(S3C2410_ADCDAT0); -- 读取ADCDAT)的值
#define start_adc_x()	do {__raw_writel(0x00007ffa, S3C2410_ADCCON); \
				__raw_readl(S3C2410_ADCDAT0); } while(0)
				
// 怎么跟start_adc_x()配置一样?应该为0x00007fea?
#define start_adc_y()	do {__raw_writel(0x00007ffa, S3C2410_ADCCON); \
				__raw_readl(S3C2410_ADCDAT1); } while(0)
				

// 禁止读启动操作,即READ_START位置0
#define disable_ts_adc()	__raw_writel(__raw_readl(S3C2410_ADCCON)&0xfffffffd, S3C2410_ADCCON)


static int adc_state = 0;
static int x, y;	/* touch screen coorinates */

#ifdef HOOK_FOR_DRAG
    #define	RT_BT_EMU_TM	((HZ>>1)+(HZ>>2))	//0.75S
    //static u16 ts_r_x[5];
    //static u16 ts_r_y[5];
    //static u16 ts_r_idx;
    //static u16 ts_r_beg;
    static u32 dn_start;
#endif

static void tsEvent_raw(void)
{
DPRINTK("@@@@@@@@ tvEvent_raw() @@@@@@@@@\n");
	if (tsdev.penStatus == PEN_DOWN)
	{
		//u16 i, j;
#ifdef HOOK_FOR_DRAG 
		ts_timer.expires = jiffies + TS_TIMER_DELAY;
		add_timer(&ts_timer);
#endif
		BUF_HEAD.x = x;
		BUF_HEAD.y = y;
		
#ifdef HOOK_FOR_DRAG
                BUF_HEAD.pressure = ((jiffies - dn_start) >= RT_BT_EMU_TM) ? PEN_FLEETING : PEN_DOWN;
#else
                BUF_HEAD.pressure = PEN_DOWN;
#endif
		
		
	}/* if (tsdev.penStatus == PEN_DOWN) */
	else
	{
#ifdef HOOK_FOR_DRAG 
		del_timer(&ts_timer);
#endif
		BUF_HEAD.x = x;
		BUF_HEAD.y = y;
		BUF_HEAD.pressure = PEN_UP;
	}

	tsdev.head = INCBUF(tsdev.head, MAX_TS_BUF);
DPRINTK("@@@@@@@@ wake_up_interruptible() in tvEvent_raw() @@@@@@@@@\n");
	wake_up_interruptible(&(tsdev.wq));

#ifdef USE_ASYNC
	if (tsdev.aq)
		kill_fasync(&(tsdev.aq), SIGIO, POLL_IN);
#endif

#ifdef CONFIG_PM
	pm_access(tsdev.pm_dev);
#endif
}

static int tsRead(TS_RET * ts_ret)
{
	spin_lock_irq(&(tsdev.lock));
	ts_ret->x = BUF_TAIL.x;
	ts_ret->y = BUF_TAIL.y;
	ts_ret->pressure = BUF_TAIL.pressure;
	tsdev.tail = INCBUF(tsdev.tail, MAX_TS_BUF);
	spin_unlock_irq(&(tsdev.lock));

	return sizeof(TS_RET);
}

static ssize_t s3c2410_ts_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
	TS_RET ts_ret;

retry: 
	if (tsdev.head != tsdev.tail)
	{
		int count;
		count = tsRead(&ts_ret);
		if (count)
		{
printk("@@@@@ ts_ret.x: %hd, ts_ret.y: %hd @@@@@@@\n", ts_ret.x, ts_ret.y);
		    copy_to_user(buffer, (char *)&ts_ret, count);
		}
		return count;
	} 
	else
	{
		if (filp->f_flags & O_NONBLOCK)
			return -EAGAIN;
		interruptible_sleep_on(&(tsdev.wq));
		if (signal_pending(current))
			return -ERESTARTSYS;
		goto retry;
	}

	return sizeof(TS_RET);
}

#ifdef USE_ASYNC
static int s3c2410_ts_fasync(int fd, struct file *filp, int mode) 
{
	return fasync_helper(fd, filp, mode, &(tsdev.aq));
}
#endif

static unsigned int s3c2410_ts_poll(struct file *filp, struct poll_table_struct *wait)
{
	poll_wait(filp, &(tsdev.wq), wait);
	return (tsdev.head == tsdev.tail) ? 0 : (POLLIN | POLLRDNORM); 
}

static inline void start_ts_adc(void)
{
	adc_state = 0;
	mode_x_axis();
	start_adc_x();
}

static inline void s3c2410_get_XY(void)
{
	if (adc_state == 0)
	{ 
		adc_state = 1;
		disable_ts_adc();
		y = __raw_readl(S3C2410_ADCDAT1) & 0x3ff;       //x:f04 y:f0e dh  by pht.
		mode_y_axis();
		start_adc_y();
	}
	else if (adc_state == 1)
	{ 
		adc_state = 0;
		disable_ts_adc();
		x = __raw_readl(S3C2410_ADCDAT0) & 0x3ff;         //y:f04 x:f0e dh by pht.
printk("@@@@@@ PEN DOWN: x: %d, y: %d @@@@@@\n", x, y);
		wait_up_int();
		tsdev.penStatus = PEN_DOWN;
		tsEvent();
	}
}

static irqreturn_t s3c2410_isr_adc(int irq, void *dev_id, struct pt_regs *reg)
{
DPRINTK("@@@@@@@@ s3c2410_isr_adc() @@@@@@@@\n");
	spin_lock_irq(&(tsdev.lock));
	if (tsdev.penStatus == PEN_UP)
	{
DPRINTK("@@@@@@ s3c2410_isr_adc 1 @@@@@@\n");
            s3c2410_get_XY();
	}
#ifdef HOOK_FOR_DRAG
	else
	{
DPRINTK("@@@@@@ s3c2410_isr_adc 2 @@@@@@\n");
            s3c2410_get_XY();
	}
#endif
	spin_unlock_irq(&(tsdev.lock));

	return IRQ_HANDLED;
}

static irqreturn_t s3c2410_isr_tc(int irq, void *dev_id, struct pt_regs *reg)
{
DPRINTK("@@@@@@@@ s3c2410_isr_tc() @@@@@@@@\n");
	spin_lock_irq(&(tsdev.lock));
	if (tsdev.penStatus == PEN_UP)
	{
DPRINTK("@@@@@@@@ s3c2410_isr_tc 1 @@@@@@@@\n");
#ifdef HOOK_FOR_DRAG
	  dn_start = jiffies; // add by gzliu
#endif
	  start_ts_adc();
	}
	else
	{
	  tsdev.penStatus = PEN_UP;
DPRINTK("@@@@@@@@ s3c2410_isr_tc 2 @@@@@@@@\n");
printk("@@@@@@@ PEN UP: x: %d, y: %d @@@@@@@\n", x, y);
	  wait_down_int();
	  tsEvent();
	}
	spin_unlock_irq(&(tsdev.lock));

	return IRQ_HANDLED;
}

#ifdef HOOK_FOR_DRAG
static void ts_timer_handler(unsigned long data)
{
	spin_lock_irq(&(tsdev.lock));
	if (tsdev.penStatus == PEN_DOWN)
	{
		start_ts_adc();
	}
	spin_unlock_irq(&(tsdev.lock));
}
#endif

static int s3c2410_ts_open(struct inode *inode, struct file *filp)
{
	tsdev.head = tsdev.tail = 0;
	tsdev.penStatus = PEN_UP;
#ifdef HOOK_FOR_DRAG 
	init_timer(&ts_timer);
	ts_timer.function = ts_timer_handler;
#endif
	tsEvent = tsEvent_raw;
	init_waitqueue_head(&(tsdev.wq));

	//MOD_INC_USE_COUNT;
	return 0;
}

static int s3c2410_ts_release(struct inode *inode, struct file *filp)
{
#ifdef HOOK_FOR_DRAG
	del_timer(&ts_timer);
#endif
	//MOD_DEC_USE_COUNT;
	return 0;
}

static struct file_operations s3c2410_fops = {
	owner:	THIS_MODULE,
	open:	s3c2410_ts_open,
	read:	s3c2410_ts_read,	
	release:	s3c2410_ts_release,
#ifdef USE_ASYNC
	fasync:	s3c2410_ts_fasync,
#endif
	poll:	s3c2410_ts_poll,
};

void tsEvent_dummy(void) {}
#ifdef CONFIG_PM
static int s3c2410_ts_pm_callback(struct pm_dev *pm_dev, pm_request_t req, 
								   void *data) 
{
    switch (req) {
		case PM_SUSPEND:
			tsEvent = tsEvent_dummy;
			break;
		case PM_RESUME:
			tsEvent = tsEvent_raw;
			wait_down_int();
			break;
    }
    return 0;
}
#endif


static struct clk	*adc_clock;
static int __init s3c2410ts_probe(struct device *dev)
{
	int ret;
 
	tsEvent = tsEvent_dummy;
DPRINTK("@@@@@@@@@@ s3c2410ts_probe @@@@@@@@@@\n");
	ret = register_chrdev(0, DEVICE_NAME, &s3c2410_fops);
	if (ret < 0) {
	  printk(DEVICE_NAME " can't get major number\n");
	  return ret;
	}

	tsMajor = ret;

	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);
	
	base_addr = ioremap(S3C2410_PA_ADC, 0x20);
	
	__raw_writel(WAIT4INT(0), S3C2410_ADCTSC);

	__raw_writel(30000, S3C2410_ADCDLY);	// 30000--20000

	ret = request_irq(IRQ_ADC, s3c2410_isr_adc, SA_INTERRUPT, DEVICE_NAME, s3c2410_isr_adc);
	if (ret)
	    goto adc_failed;
	ret = request_irq(IRQ_TC, s3c2410_isr_tc, SA_INTERRUPT, DEVICE_NAME, s3c2410_isr_tc);
	if (ret)
	    goto tc_failed;

	/* Wait for touch screen interrupts */
	wait_down_int();
        
#ifdef CONFIG_DEVFS_FS
	devfs_mk_dir("touchscreen");
	devfs_mk_cdev(MKDEV(tsMajor, TSRAW_MINOR), S_IFCHR|S_IRUGO|S_IWUSR, "touchscreen/%s", "0raw");
#endif

#ifdef CONFIG_PM
	tsdev.pm_dev = pm_register(PM_DEBUG_DEV, PM_USER_INPUT, s3c2410_ts_pm_callback);
#endif

	return 0;
 tc_failed:
    	{
            printk(KERN_ERR "tc failed!!!!!!!!!!!!!\n");
	     	free_irq(IRQ_ADC, s3c2410_isr_adc);
		}
 adc_failed:
     	{
            printk(KERN_ERR "adc failed!!!!!!!!!!!!!\n");
	     	return ret;
	 	}
}

static struct device_driver s3c2410ts_driver = {
	.name		= DEVICE_NAME,
	.bus		= &platform_bus_type,
	.probe		= s3c2410ts_probe,
#ifdef CONFIG_PM
	.suspend	= s3c2410ts_suspend,
	.resume		= s3c2410ts_resume,
#endif
};

static int __init s3c2410ts_init(void)
{
	int ret;

	printk("@@@@@@@@ s3c2410ts init() @@@@@@@\n");
	ret = driver_register(&s3c2410ts_driver);
	if(ret)
	{
            printk("register %s driver failed, return code is %d\n", DEVICE_NAME, ret);
        }
	else
	{
	    printk("@@@@@@@ register %s driver success, return code is %d @@@@@@@@@@\n", DEVICE_NAME, ret);
	}
	return ret;
}


static void __exit s3c2410ts_exit(void)
{
#ifdef CONFIG_DEVFS_FS	
	devfs_remove("touchscreen/%d", 0);
	devfs_remove("touchscreen");
#endif	
	unregister_chrdev(tsMajor, DEVICE_NAME);
#ifdef CONFIG_PM
	pm_unregister(tsdev.pm_dev);
#endif
	free_irq(IRQ_ADC, s3c2410_isr_adc);
	free_irq(IRQ_TC, s3c2410_isr_tc);
	driver_unregister(&s3c2410ts_driver);
}

module_init(s3c2410ts_init);
module_exit(s3c2410ts_exit);


你可能感兴趣的:(c,linux,timer,struct,XP,hook)