?? s3c2410_ts.c
字號:
/* * drivers/char/s3c2410-ts.c * * touchScreen driver for SAMSUNG S3C2410 * */#include <linux/config.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/serio.h>#include <linux/delay.h>#include <asm/io.h>#include <asm/irq.h>#include <linux/miscdevice.h>#include <linux/sched.h>#include <linux/poll.h>#include <linux/spinlock.h>#include <linux/coda.h>#include <linux/cdev.h>#include <linux/interrupt.h>#include <linux/devfs_fs_kernel.h>#include <asm/io.h>#include <asm/hardware/clock.h>#include <asm/arch/regs-adc.h>#include <asm/arch/regs-gpio.h>#include <asm/irq.h>#include <linux/delay.h>#include <asm/hardware.h>/* debug macros */#undef DEBUG#ifdef DEBUG#define DPRINTK( x... ) printk("s3c2410-ts: " ##x)#else#define DPRINTK( x... )#endif#define PEN_UP 0 #define PEN_DOWN 1#define PEN_FLEETING 2#define MAX_TS_BUF 8 /* how many do we want to buffer */#define DEVICE_NAME "s3c2410-ts"static struct cdev ts = { .kobj = {.name = "ts", }, .owner = THIS_MODULE,};typedef struct { unsigned short pressure; unsigned short x; unsigned short y; unsigned short pad;} TS_RET;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; struct fasync_struct *aq;} 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))unsigned long tmp;dev_t chrdev;static void (*tsEvent)(void);#define TS_TIMER_DELAY (HZ/100) /* 10 ms */static struct timer_list ts_timer;#define wait_down_int() { S3C2410_DOWN_INT | S3C2410_ADCTSC_XP_PULL_UP_EN | \ S3C2410_ADCTSC_XP_AIN | S3C2410_ADCTSC_XM_HIZ | \ S3C2410_ADCTSC_YP_AIN | S3C2410_ADCTSC_YM_GND | \ S3C2410_ADCTSC_XY_PST(S3C2410_WAIT_INT_MODE); }#define wait_up_int() { S3C2410_UP_INT | S3C2410_ADCTSC_XP_PULL_UP_EN | \ S3C2410_ADCTSC_XP_AIN | S3C2410_ADCTSC_XM_HIZ | \ S3C2410_ADCTSC_YP_AIN | S3C2410_ADCTSC_YM_GND | \ S3C2410_ADCTSC_XY_PST(S3C2410_WAIT_INT_MODE); }#define mode_x_axis() { S3C2410_ADCTSC_XP_EXTVLT | S3C2410_ADCTSC_XM_GND | \ S3C2410_ADCTSC_YP_AIN | S3C2410_ADCTSC_YM_HIZ | \ S3C2410_ADCTSC_XP_PULL_UP_DIS | \ S3C2410_ADCTSC_XY_PST(S3C2410_X_AXIS_MODE); }#define mode_y_axis() { S3C2410_ADCTSC_XP_AIN | S3C2410_ADCTSC_XM_HIZ | \ S3C2410_ADCTSC_YP_EXTVLT | S3C2410_ADCTSC_YM_GND | \ S3C2410_ADCTSC_XP_PULL_UP_DIS | \ S3C2410_ADCTSC_XY_PST(S3C2410_Y_AXIS_MODE); }#define start_adc_x() { S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(49) | \ S3C2410_ADCCON_SELMUX(S3C2410_ADC_IN5) | \ S3C2410_ADCCON_READ_START; }#define start_adc_y() { S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(49) | \ S3C2410_ADCCON_SELMUX(S3C2410_ADC_IN7) | \ S3C2410_ADCCON_READ_START; } static int adc_state = 0;static int x, y;//touch screen coorinatesstatic void __iomem *base_addr;static void tsEvent_raw(void){ if (tsdev.penStatus == PEN_DOWN) { BUF_HEAD.x = x; BUF_HEAD.y = y; BUF_HEAD.pressure = PEN_DOWN; ts_timer.expires = jiffies + TS_TIMER_DELAY; add_timer(&ts_timer); } else { del_timer(&ts_timer); BUF_HEAD.x = 0; BUF_HEAD.y = 0; BUF_HEAD.pressure = PEN_UP; } tsdev.head = INCBUF(tsdev.head, MAX_TS_BUF); wake_up_interruptible(&(tsdev.wq)); if (tsdev.aq) kill_fasync(&(tsdev.aq), SIGIO, POLL_IN);}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) 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);}static int s3c2410_ts_fasync(int fd, struct file *filp, int mode) { return fasync_helper(fd, filp, mode, &(tsdev.aq));}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; writel(mode_x_axis(), base_addr+S3C2410_ADCTSC); //mode_x_axis(); writel(start_adc_x(), base_addr+S3C2410_ADCCON); // start_adc_x(); tmp=readl(base_addr+S3C2410_ADCCON); tmp &=~(S3C2410_ADCCON_STDBM); writel(tmp, base_addr+S3C2410_ADCCON); readl(base_addr+S3C2410_ADCDAT0);}static inline void s3c2410_get_XY(void){ if (adc_state == 0) { adc_state = 1; tmp = readl(base_addr+S3C2410_ADCCON); tmp &= ~(S3C2410_ADCCON_READ_START); writel(tmp, base_addr+S3C2410_ADCCON); // disable_ts_adc(); y = (readl(base_addr+S3C2410_ADCDAT0) & 0x3ff); // y = (ADCDAT0 & 0x3ff); writel(mode_y_axis(), base_addr+S3C2410_ADCTSC);// mode_y_axis(); writel(start_adc_y(), base_addr+S3C2410_ADCCON);// start_adc_y(); tmp=readl(base_addr+S3C2410_ADCCON); tmp &=~(S3C2410_ADCCON_STDBM); writel(tmp, base_addr+S3C2410_ADCCON); readl(base_addr+S3C2410_ADCDAT1); } else if (adc_state == 1) { adc_state = 0; tmp = readl(base_addr+S3C2410_ADCCON); tmp &= ~(S3C2410_ADCCON_READ_START); writel(tmp, base_addr+S3C2410_ADCCON); // disable_ts_adc(); x = (readl(base_addr+S3C2410_ADCDAT1) & 0x3ff); // x = (ADCDAT1 & 0x3ff); tsdev.penStatus = PEN_DOWN; DPRINTK("PEN DOWN: x: %08d, y: %08d\n", x, y); writel(wait_up_int(), base_addr+S3C2410_ADCTSC);// wait_up_int(); tsEvent(); }}static irqreturn_t s3c2410_isr_adc(int irq, void *dev_id, struct pt_regs *reg){ spin_lock_irq(&(tsdev.lock)); s3c2410_get_XY(); spin_unlock_irq(&(tsdev.lock)); return IRQ_HANDLED;}static irqreturn_t s3c2410_isr_tc(int irq, void *dev_id, struct pt_regs *reg){ spin_lock_irq(&(tsdev.lock)); if (tsdev.penStatus == PEN_UP) { start_ts_adc(); } else { tsdev.penStatus = PEN_UP; DPRINTK("PEN UP: x: %08d, y: %08d\n", x, y); writel(wait_down_int(), base_addr+S3C2410_ADCTSC);//wait_down_int(); tsEvent(); } spin_unlock_irq(&(tsdev.lock)); return IRQ_HANDLED;}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));}static int s3c2410_ts_open(struct inode *inode, struct file *filp){ tsdev.head = tsdev.tail = 0; tsdev.penStatus = PEN_UP; init_timer(&ts_timer); ts_timer.function = ts_timer_handler; tsEvent = tsEvent_raw; init_waitqueue_head(&(tsdev.wq)); return 0;}static int s3c2410_ts_release(struct inode *inode, struct file *filp){ del_timer(&ts_timer); return 0;}static struct file_operations s3c2410_fops = { owner: THIS_MODULE, open: s3c2410_ts_open, read: s3c2410_ts_read, release: s3c2410_ts_release, fasync: s3c2410_ts_fasync, poll: s3c2410_ts_poll,};void tsEvent_dummy(void) {}static struct clk *adc_clock;static int __init s3c2410_ts_probe(struct device *dev){ int ret = 0; tsEvent = tsEvent_dummy; 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); if (base_addr == NULL) { printk(KERN_ERR "Failed to remap register block\n"); return -ENOMEM; } if(alloc_chrdev_region(&chrdev,0,1,"ts")){ printk(KERN_ERR"Couldn't alloc chrdev region\n"); return 1; } cdev_init(&ts,&s3c2410_fops); if(cdev_add(&ts, chrdev, 1)){ unregister_chrdev_region(chrdev,1); printk(KERN_ERR"Couldn't register ts driver\n"); return 1; } s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON); s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON); s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON); s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON); if (request_irq(IRQ_ADC, s3c2410_isr_adc, SA_SAMPLE_RANDOM, "s3c2410_action", &chrdev)) { printk(KERN_ERR "Could not allocate ts IRQ_ADC !\n"); iounmap(base_addr); return -EIO; } if (request_irq(IRQ_TC, s3c2410_isr_tc, SA_SAMPLE_RANDOM, "s3c2410_action", &chrdev)) { printk(KERN_ERR "Could not allocate ts IRQ_TC !\n"); iounmap(base_addr); free_irq(IRQ_ADC,&chrdev); return -EIO; } writel(wait_down_int(), base_addr+S3C2410_ADCTSC);//wait_down_int(); ret = devfs_mk_cdev(chrdev,S_IFCHR | S_IRUGO | S_IWUSR, DEVICE_NAME); if(ret) goto out_chrdev; writel(0xFFFF, base_addr+S3C2410_ADCDLY); printk(KERN_INFO "Tochu screen successfully loaded\n"); goto out;out_chrdev: unregister_chrdev(chrdev, DEVICE_NAME);out: return ret;}static int s3c2410_ts_remove(struct device *dev){ unregister_chrdev(chrdev, DEVICE_NAME); disable_irq(IRQ_ADC); disable_irq(IRQ_TC); free_irq(IRQ_TC,&chrdev); free_irq(IRQ_ADC,&chrdev); if (adc_clock) { clk_disable(adc_clock); clk_unuse(adc_clock); clk_put(adc_clock); adc_clock = NULL; } iounmap(base_addr); return 0;}static struct device_driver s3c2410_ts_driver = { .name = DEVICE_NAME, .bus = &platform_bus_type, .probe = s3c2410_ts_probe, .remove = s3c2410_ts_remove,};int __init s3c2410_ts_init(void){ return driver_register(&s3c2410_ts_driver);}void __exit s3c2410_ts_exit(void){ driver_unregister(&s3c2410_ts_driver);}module_init(s3c2410_ts_init);module_exit(s3c2410_ts_exit);MODULE_LICENSE("GPL");
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -