From: sakura Date: Thu, 13 Jan 2011 08:03:08 +0000 (+0800) Subject: modify sdk touchscreen driver form hann to eeti_egalax X-Git-Tag: firefly_0821_release~10815 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=5fd66919183347062a8514f3d269595ef3cdaab9;p=firefly-linux-kernel-4.4.55.git modify sdk touchscreen driver form hann to eeti_egalax --- diff --git a/arch/arm/mach-rk29/board-rk29sdk.c b/arch/arm/mach-rk29/board-rk29sdk.c index 3a6b7af28345..1c03f403a8b3 100755 --- a/arch/arm/mach-rk29/board-rk29sdk.c +++ b/arch/arm/mach-rk29/board-rk29sdk.c @@ -345,7 +345,40 @@ struct p1003_platform_data p1003_info = { }; #endif +#if defined (CONFIG_EETI_EGALAX) +#define TOUCH_RESET_PIN RK29_PIN6_PC3 +#define TOUCH_INT_PIN RK29_PIN0_PA2 + +int EETI_EGALAX_init_platform_hw(void) +{ + if(gpio_request(TOUCH_RESET_PIN,NULL) != 0){ + gpio_free(TOUCH_RESET_PIN); + printk("p1003_init_platform_hw gpio_request error\n"); + return -EIO; + } + if(gpio_request(TOUCH_INT_PIN,NULL) != 0){ + gpio_free(TOUCH_INT_PIN); + printk("p1003_init_platform_hw gpio_request error\n"); + return -EIO; + } + gpio_pull_updown(TOUCH_INT_PIN, 1); + gpio_direction_output(TOUCH_RESET_PIN, 0); + msleep(500); + gpio_set_value(TOUCH_RESET_PIN,GPIO_LOW); + msleep(500); + gpio_set_value(TOUCH_RESET_PIN,GPIO_HIGH); + + return 0; +} + + +struct eeti_egalax_platform_data eeti_egalax_info = { + .model= 1003, + .init_platform_hw= EETI_EGALAX_init_platform_hw, + +}; +#endif /*MMA8452 gsensor*/ #if defined (CONFIG_GS_MMA8452) #define MMA8452_INT_PIN RK29_PIN0_PA3 @@ -545,6 +578,15 @@ static struct i2c_board_info __initdata board_i2c2_devices[] = { .platform_data = &p1003_info, }, #endif +#if defined (CONFIG_EETI_EGALAX) + { + .type = "egalax_i2c", + .addr = 0x04, + .flags = 0, + .irq = RK29_PIN0_PA2, + .platform_data = &eeti_egalax_info, + }, +#endif }; #endif diff --git a/arch/arm/mach-rk29/include/mach/board.h b/arch/arm/mach-rk29/include/mach/board.h index 875c08f02b09..9c5f57bcaa36 100755 --- a/arch/arm/mach-rk29/include/mach/board.h +++ b/arch/arm/mach-rk29/include/mach/board.h @@ -131,7 +131,15 @@ struct p1003_platform_data { int (*p1003_platform_wakeup)(void); void (*exit_platform_hw)(void); }; +struct eeti_egalax_platform_data{ + u16 model; + int (*get_pendown_state)(void); + int (*init_platform_hw)(void); + int (*eeti_egalax_platform_sleep)(void); + int (*eeti_egalax_platform_wakeup)(void); + void (*exit_platform_hw)(void); +}; struct mma8452_platform_data { u16 model; u16 swap_xy; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index d50ddfec9054..4c211075e2bf 100755 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -596,7 +596,6 @@ config HANNSTAR_P1003 help RK29 hannstar touch max Y size - config HANNSTAR_DEBUG bool "hannstar debug" depends on HANNSTAR_P1003 @@ -604,6 +603,16 @@ config HANNSTAR_P1003 help RK29 hannstar touch debug +config EETI_EGALAX + tristate "EETI_EGALAX touchscreen panel support" + depends on I2C + help + Say Y here to enable support for I2C connected EETI touch panels. + + To compile this driver as a module, choose M here: the + module will be called eeti_egalax_ts. + + config TOUCHSCREEN_IT7260 tristate "IT7260 based touchscreens: IT7260 Interface" depends on I2C2_RK29 diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 591322ce10b2..e336151393eb 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -51,3 +51,4 @@ obj-$(CONFIG_TOUCHSCREEN_IT7250) += ctp_it7250.o obj-$(CONFIG_RK28_I2C_TS_NTP070) += ntp070.o obj-$(CONFIG_HANNSTAR_P1003) += hannstar_p1003.o obj-$(CONFIG_TOUCHSCREEN_IT7260) += it7260_ts.o +obj-$(CONFIG_EETI_EGALAX) += eeti_egalax_i2c.o diff --git a/drivers/input/touchscreen/eeti_egalax_i2c.c b/drivers/input/touchscreen/eeti_egalax_i2c.c new file mode 100644 index 000000000000..a59fcaa4cff6 --- /dev/null +++ b/drivers/input/touchscreen/eeti_egalax_i2c.c @@ -0,0 +1,846 @@ +/* + * + * Touch Screen I2C Driver for EETI Controller + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +// Release Date: 2010/11/08 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define DEBUG +#ifdef DEBUG + #define TS_DEBUG(fmt,args...) printk( KERN_DEBUG "[egalax_i2c]: " fmt, ## args) + #define DBG() printk("[%s]:%d => \n",__FUNCTION__,__LINE__) +#else + #define TS_DEBUG(fmt,args...) + #define DBG() +#endif + +//#define _NON_INPUT_DEV // define this to disable register input device + +static int global_major = 0; // dynamic major by default +static int global_minor = 0; + +#define MAX_I2C_LEN 10 +#define FIFO_SIZE PAGE_SIZE +#define MAX_SUPPORT_POINT 5 +#define REPORTID_MOUSE 0x01 +#define REPORTID_VENDOR 0x03 +#define REPORTID_MTOUCH 0x04 + +/// ioctl command /// +#define EGALAX_IOC_MAGIC 0x72 +#define EGALAX_IOCWAKEUP _IO(EGALAX_IOC_MAGIC, 1) +#define EGALAX_IOC_MAXNR 1 + +struct point_data { + short Status; + short X; + short Y; +}; + +struct _egalax_i2c { + struct workqueue_struct *ktouch_wq; + struct work_struct work; + struct mutex mutex_wq; + struct i2c_client *client; + char work_state; + char skip_packet; + int irq; +}; + +struct egalax_char_dev +{ + int OpenCnts; + struct cdev cdev; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) + struct kfifo* pDataKFiFo; +#else + struct kfifo DataKFiFo; +#endif + unsigned char *pFiFoBuf; + spinlock_t FiFoLock; + struct semaphore sem; + wait_queue_head_t fifo_inq; +}; + +static struct _egalax_i2c *p_egalax_i2c_dev = NULL; // allocated in egalax_i2c_probe +static struct egalax_char_dev *p_char_dev = NULL; // allocated in init_module +static atomic_t egalax_char_available = ATOMIC_INIT(1); +static struct class *egalax_class; +#ifndef _NON_INPUT_DEV +static struct input_dev *input_dev = NULL; +static struct point_data PointBuf[MAX_SUPPORT_POINT]; +#endif //#ifndef _NON_INPUT_DEV + +static int egalax_cdev_open(struct inode *inode, struct file *filp) +{ + struct egalax_char_dev *cdev; + + DBG(); + + cdev = container_of(inode->i_cdev, struct egalax_char_dev, cdev); + if( cdev == NULL ) + { + TS_DEBUG(" No such char device node \n"); + return -ENODEV; + } + + if( !atomic_dec_and_test(&egalax_char_available) ) + { + atomic_inc(&egalax_char_available); + return -EBUSY; /* already open */ + } + + cdev->OpenCnts++; + filp->private_data = cdev;// Used by the read and write metheds + + TS_DEBUG(" egalax_cdev_open done \n"); + try_module_get(THIS_MODULE); + return 0; +} + +static int egalax_cdev_release(struct inode *inode, struct file *filp) +{ + struct egalax_char_dev *cdev; // device information + + DBG(); + + cdev = container_of(inode->i_cdev, struct egalax_char_dev, cdev); + if( cdev == NULL ) + { + TS_DEBUG(" No such char device node \n"); + return -ENODEV; + } + + atomic_inc(&egalax_char_available); /* release the device */ + + filp->private_data = NULL; + cdev->OpenCnts--; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) + kfifo_reset( cdev->pDataKFiFo ); +#else + kfifo_reset( &cdev->DataKFiFo ); +#endif + + TS_DEBUG(" egalax_cdev_release done \n"); + module_put(THIS_MODULE); + return 0; +} + +#define MAX_READ_BUF_LEN 50 +static char fifo_read_buf[MAX_READ_BUF_LEN]; +static ssize_t egalax_cdev_read(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + int read_cnt, ret, fifoLen; + struct egalax_char_dev *cdev = file->private_data; + + DBG(); + + if( down_interruptible(&cdev->sem) ) + return -ERESTARTSYS; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) + fifoLen = kfifo_len(cdev->pDataKFiFo); +#else + fifoLen = kfifo_len(&cdev->DataKFiFo); +#endif + + while( fifoLen<1 ) /* nothing to read */ + { + up(&cdev->sem); /* release the lock */ + if( file->f_flags & O_NONBLOCK ) + return -EAGAIN; + + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) + if( wait_event_interruptible(cdev->fifo_inq, kfifo_len( cdev->pDataKFiFo )>0) ) + #else + if( wait_event_interruptible(cdev->fifo_inq, kfifo_len( &cdev->DataKFiFo )>0) ) + #endif + { + return -ERESTARTSYS; /* signal: tell the fs layer to handle it */ + } + + if( down_interruptible(&cdev->sem) ) + return -ERESTARTSYS; + } + + if(count > MAX_READ_BUF_LEN) + count = MAX_READ_BUF_LEN; + + TS_DEBUG("\"%s\" reading: real fifo data\n", current->comm); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) + read_cnt = kfifo_get(cdev->pDataKFiFo, fifo_read_buf, count); +#else + read_cnt = kfifo_out_locked(&cdev->DataKFiFo, fifo_read_buf, count, &cdev->FiFoLock); +#endif + + ret = copy_to_user(buf, fifo_read_buf, read_cnt)?-EFAULT:read_cnt; + + up(&cdev->sem); + + return ret; +} + +static ssize_t egalax_cdev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) +{ + char *tmp; + struct egalax_char_dev *cdev = file->private_data; + int ret=0; + + DBG(); + + if( down_interruptible(&cdev->sem) ) + return -ERESTARTSYS; + + if (count > MAX_I2C_LEN) + count = MAX_I2C_LEN; + + tmp = kmalloc(count,GFP_KERNEL); + if(tmp==NULL) + { + up(&cdev->sem); + return -ENOMEM; + } + + if(copy_from_user(tmp, buf, count)) + { + up(&cdev->sem); + kfree(tmp); + return -EFAULT; + } + + ret = i2c_master_send(p_egalax_i2c_dev->client, tmp, count); + TS_DEBUG("I2C writing %zu bytes.\n", count); + + kfree(tmp); + + up(&cdev->sem); + + return ret; +} + +static int wakeup_controller(int gpio) +{ + int ret=0, i; + + gpio_free(gpio); + + if( (ret=gpio_request(gpio, "Touch Wakeup GPIO"))!=0 ) + { + printk(KERN_ERR "[egalax_i2c]: Failed to request GPIO for Touch Wakeup GPIO. Err:%d\n", ret); + ret = -EFAULT; + } + else + { + gpio_direction_output(gpio, 0); + for(i=0; i<100; i++); + gpio_direction_input(gpio); + printk(KERN_ERR "[egalax_i2c]: INT wakeup touch controller done\n"); + } + + return ret; +} + +static int egalax_cdev_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long args) +{ + //struct egalax_char_dev *cdev = file->private_data; + int ret=0; + + if(_IOC_TYPE(cmd) != EGALAX_IOC_MAGIC) + return -ENOTTY; + if(_IOC_NR(cmd) > EGALAX_IOC_MAXNR) + return -ENOTTY; + + if(_IOC_DIR(cmd) & _IOC_READ) + ret = !access_ok(VERIFY_WRITE, (void __user*)args, _IOC_SIZE(cmd)); + else if(_IOC_DIR(cmd) & _IOC_WRITE) + ret = !access_ok(VERIFY_READ, (void __user*)args, _IOC_SIZE(cmd)); + + if(ret) + return -EFAULT; + + //printk(KERN_ERR "Handle device ioctl command\n"); + switch (cmd) + { + case EGALAX_IOCWAKEUP: + ret = wakeup_controller(irq_to_gpio(p_egalax_i2c_dev->irq)); + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +static unsigned int egalax_cdev_poll(struct file *filp, struct poll_table_struct *wait) +{ + struct egalax_char_dev *cdev = filp->private_data; + unsigned int mask = 0; + int fifoLen; + + down(&cdev->sem); + poll_wait(filp, &cdev->fifo_inq, wait); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) + fifoLen = kfifo_len(cdev->pDataKFiFo); +#else + fifoLen = kfifo_len(&cdev->DataKFiFo); +#endif + + if( fifoLen > 0 ) + mask |= POLLIN | POLLRDNORM; /* readable */ + if( (FIFO_SIZE - fifoLen) > MAX_I2C_LEN ) + mask |= POLLOUT | POLLWRNORM; /* writable */ + + up(&cdev->sem); + return mask; +} + +#ifndef _NON_INPUT_DEV +static int LastUpdateID = 0; +static void ProcessReport(unsigned char *buf, int buflen) +{ + int i; + short X=0, Y=0, ContactID=0, Status=0; + + if(buflen!=MAX_I2C_LEN || buf[0]!=0x04) // check buffer len & header + return; + + Status = buf[1]&0x01; + ContactID = (buf[1]&0x7C)>>2; + X = ((buf[3]<<8) + buf[2])>>4; + Y = ((buf[5]<<8) + buf[4])>>4; + + PointBuf[ContactID].Status = Status; + PointBuf[ContactID].X = X; + PointBuf[ContactID].Y = Y; + + TS_DEBUG("Get Point[%d] Update: Status=%d X=%d Y=%d\n", ContactID, Status, X, Y); + + // Send point report + if( !Status || (ContactID <= LastUpdateID) ) + { + for(i=0; i= 0) + { + input_report_abs(input_dev, ABS_MT_TRACKING_ID, i); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, PointBuf[i].Status); + input_report_abs(input_dev, ABS_MT_WIDTH_MAJOR, 0); + input_report_abs(input_dev, ABS_MT_POSITION_X, PointBuf[i].X); + input_report_abs(input_dev, ABS_MT_POSITION_Y, PointBuf[i].Y); + + input_mt_sync(input_dev); + + if(PointBuf[i].Status == 0) + PointBuf[i].Status--; + } + } + input_sync(input_dev); + TS_DEBUG("Input sync point data done!\n"); + } + + LastUpdateID = ContactID; +} + +static struct input_dev * allocate_Input_Dev(void) +{ + int ret; + struct input_dev *pInputDev=NULL; + + pInputDev = input_allocate_device(); + if(pInputDev == NULL) + { + TS_DEBUG("Failed to allocate input device\n"); + return NULL;//-ENOMEM; + } + + pInputDev->name = "eGalax Touch Screen"; + pInputDev->phys = "I2C"; + pInputDev->id.bustype = BUS_I2C; + pInputDev->id.vendor = 0x0EEF; + pInputDev->id.product = 0x0020; + + set_bit(EV_ABS, pInputDev->evbit); + + input_set_abs_params(pInputDev, ABS_MT_POSITION_X, 0, 1080, 0, 0); + input_set_abs_params(pInputDev, ABS_MT_POSITION_Y, 0, 800, 0, 0); + input_set_abs_params(pInputDev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(pInputDev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(pInputDev, ABS_MT_TRACKING_ID, 0, 10, 0, 0); + + ret = input_register_device(pInputDev); + if(ret) + { + TS_DEBUG("Unable to register input device.\n"); + input_free_device(pInputDev); + return NULL; + } + + return pInputDev; +} +#endif //#ifndef _NON_INPUT_DEV + +static int egalax_i2c_measure(struct i2c_client *client, char skip_packet) +{ + u8 x_buf[MAX_I2C_LEN]; + int count, loop=3; + + DBG(); + do{ + count = i2c_master_recv(client, x_buf, MAX_I2C_LEN); + }while(count==EAGAIN && --loop); + + if( count<0 || (x_buf[0]!=REPORTID_VENDOR && x_buf[0]!=REPORTID_MTOUCH) ) + { + TS_DEBUG("I2C read error data with Len=%d hedaer=%d\n", count, x_buf[0]); + return -1; + } + + TS_DEBUG("egalax_i2c read data with Len=%d\n", count); + if(x_buf[0]==REPORTID_VENDOR) + TS_DEBUG("egalax_i2c get command packet\n"); + + if( skip_packet > 0 ) + return count; + +#ifndef _NON_INPUT_DEV + if( count>0 && x_buf[0]==REPORTID_MTOUCH ) + { + ProcessReport(x_buf, count); + + return count; + } +#endif //#ifndef _NON_INPUT_DEV + + if( count>0 && p_char_dev->OpenCnts>0 ) // If someone reading now! put the data into the buffer! + { + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) + kfifo_put(p_char_dev->pDataKFiFo, x_buf, count); + #else + kfifo_in_locked(&p_char_dev->DataKFiFo, x_buf, count, &p_char_dev->FiFoLock); + #endif + wake_up_interruptible( &p_char_dev->fifo_inq ); + } + + return count; +} + +static void egalax_i2c_wq(struct work_struct *work) +{ + struct _egalax_i2c *egalax_i2c = container_of(work, struct _egalax_i2c, work); + struct i2c_client *client = egalax_i2c->client; + int gpio = client->irq; + + TS_DEBUG("egalax_i2c_wq run\n"); + mutex_lock(&egalax_i2c->mutex_wq); + + /*continue recv data*/ + while( !gpio_get_value(gpio) && egalax_i2c->work_state>0 ) + { + egalax_i2c_measure(client, egalax_i2c->skip_packet); + schedule(); + } + + if( egalax_i2c->skip_packet > 0 ) + egalax_i2c->skip_packet = 0; + + mutex_unlock(&egalax_i2c->mutex_wq); + + if( egalax_i2c->work_state > 0 ) + enable_irq(p_egalax_i2c_dev->irq); + + TS_DEBUG("egalax_i2c_wq leave\n"); +} + +static irqreturn_t egalax_i2c_interrupt(int irq, void *dev_id) +{ + struct _egalax_i2c *egalax_i2c = (struct _egalax_i2c *)dev_id; + + TS_DEBUG("egalax_i2c_interrupt with irq:%d\n", irq); + disable_irq_nosync(irq); + queue_work(egalax_i2c->ktouch_wq, &egalax_i2c->work); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM +static int egalax_i2c_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct _egalax_i2c *egalax_i2c = i2c_get_clientdata(client); + u8 cmdbuf[MAX_I2C_LEN]={0x03, 0x05, 0x0A, 0x03, 0x36, 0x3F, 0x02, 0, 0, 0}; + + i2c_master_send(client, cmdbuf, MAX_I2C_LEN); + + egalax_i2c->work_state = 0; + disable_irq(p_egalax_i2c_dev->irq); + cancel_work_sync(&egalax_i2c->work); + + printk(KERN_DEBUG "[egalax_i2c]: device suspend done\n"); + + if(device_may_wakeup(&client->dev)) + { + enable_irq_wake(p_egalax_i2c_dev->irq); + } + else + { + printk(KERN_DEBUG "[egalax_i2c]: device_may_wakeup false\n"); + } + + return 0; +} + +static int egalax_i2c_resume(struct i2c_client *client) +{ + struct _egalax_i2c *egalax_i2c = i2c_get_clientdata(client); + + if(device_may_wakeup(&client->dev)) + { + disable_irq_wake(p_egalax_i2c_dev->irq); + } + else + { + printk(KERN_DEBUG "[egalax_i2c]: device_may_wakeup false\n"); + } + + wakeup_controller(irq_to_gpio(p_egalax_i2c_dev->irq)); + egalax_i2c->work_state = 1; + enable_irq(p_egalax_i2c_dev->irq); + + printk(KERN_DEBUG "[egalax_i2c]: device wakeup done\n"); + + return 0; +} +#else +#define egalax_i2c_suspend NULL +#define egalax_i2c_resume NULL +#endif + +static int __devinit egalax_i2c_probe(struct i2c_client *client) +{ + int ret; + int gpio = client->irq; + struct eeti_egalax_platform_data *pdata = pdata = client->dev.platform_data; + DBG(); + printk(KERN_DEBUG "[egalax_i2c]: start probe\n"); + + p_egalax_i2c_dev = (struct _egalax_i2c *)kzalloc(sizeof(struct _egalax_i2c), GFP_KERNEL); + if (!p_egalax_i2c_dev) + { + printk(KERN_ERR "[egalax_i2c]: request memory failed\n"); + ret = -ENOMEM; + goto fail1; + } + +#ifndef _NON_INPUT_DEV + input_dev = allocate_Input_Dev(); + if(input_dev==NULL) + { + printk(KERN_ERR "[egalax_i2c]: allocate_Input_Dev failed\n"); + ret = -EINVAL; + goto fail2; + } + TS_DEBUG("egalax_i2c register input device done\n"); + memset(PointBuf, 0, sizeof(struct point_data)*MAX_SUPPORT_POINT); +#endif //#ifndef _NON_INPUT_DEV + +if (pdata->init_platform_hw) + pdata->init_platform_hw(); + + p_egalax_i2c_dev->client = client; + mutex_init(&p_egalax_i2c_dev->mutex_wq); + + p_egalax_i2c_dev->ktouch_wq = create_workqueue("egalax_touch_wq"); + INIT_WORK(&p_egalax_i2c_dev->work, egalax_i2c_wq); + + i2c_set_clientdata(client, p_egalax_i2c_dev); + + if( gpio_get_value(gpio) ) + p_egalax_i2c_dev->skip_packet = 0; + else + p_egalax_i2c_dev->skip_packet = 1; + + p_egalax_i2c_dev->work_state = 1; + + p_egalax_i2c_dev->irq = gpio_to_irq(client->irq); + + ret = request_irq(p_egalax_i2c_dev->irq, egalax_i2c_interrupt, 0, + client->name, p_egalax_i2c_dev); + if( ret ) + { + printk(KERN_ERR "[egalax_i2c]: request irq(%d) failed\n", p_egalax_i2c_dev->irq); + goto fail3; + } + TS_DEBUG("egalax_i2c request irq(%d) gpio(%d) with result:%d\n", p_egalax_i2c_dev->irq, gpio, ret); + +#ifdef CONFIG_PM + device_init_wakeup(&client->dev, 1); +#endif + + printk(KERN_DEBUG "[egalax_i2c]: probe done\n"); + return 0; + +fail3: + i2c_set_clientdata(client, NULL); + destroy_workqueue(p_egalax_i2c_dev->ktouch_wq); + free_irq(p_egalax_i2c_dev->irq, p_egalax_i2c_dev); +#ifndef _NON_INPUT_DEV + input_unregister_device(input_dev); + input_free_device(input_dev); + input_dev = NULL; +#endif //#ifndef _NON_INPUT_DEV +fail2: +fail1: + kfree(p_egalax_i2c_dev); + p_egalax_i2c_dev = NULL; + + printk(KERN_DEBUG "[egalax_i2c]: probe failed\n"); + return ret; +} + +static int __devexit egalax_i2c_remove(struct i2c_client *client) +{ + struct _egalax_i2c *egalax_i2c = i2c_get_clientdata(client); + + DBG(); + +#ifndef _NON_INPUT_DEV + if(input_dev) + { + TS_DEBUG("unregister input device\n"); + input_unregister_device(input_dev); + input_free_device(input_dev); + input_dev = NULL; + } +#endif //#ifndef _NON_INPUT_DEV + + if(p_egalax_i2c_dev->ktouch_wq) + { + destroy_workqueue(p_egalax_i2c_dev->ktouch_wq); + } + + if(p_egalax_i2c_dev->irq) + { + free_irq(p_egalax_i2c_dev->irq, egalax_i2c); + } + + i2c_set_clientdata(client, NULL); + kfree(egalax_i2c); + p_egalax_i2c_dev = NULL; + + return 0; +} + +static struct i2c_device_id egalax_i2c_idtable[] = { + { "egalax_i2c", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, egalax_i2c_idtable); + +static struct i2c_driver egalax_i2c_driver = { + .driver = { + .name = "egalax_i2c", + }, + .id_table = egalax_i2c_idtable, + .probe = egalax_i2c_probe, + .remove = __devexit_p(egalax_i2c_remove), + .suspend = egalax_i2c_suspend, + .resume = egalax_i2c_resume, +}; + +static const struct file_operations egalax_cdev_fops = { + .owner = THIS_MODULE, + .read = egalax_cdev_read, + .write = egalax_cdev_write, + .ioctl = egalax_cdev_ioctl, + .poll = egalax_cdev_poll, + .open = egalax_cdev_open, + .release= egalax_cdev_release, +}; + +static void egalax_i2c_ts_exit(void) +{ + dev_t devno = MKDEV(global_major, global_minor); + DBG(); + + if(p_char_dev) + { + TS_DEBUG("unregister character device\n"); + if( p_char_dev->pFiFoBuf ) + kfree(p_char_dev->pFiFoBuf); + + cdev_del(&p_char_dev->cdev); + kfree(p_char_dev); + p_char_dev = NULL; + } + + unregister_chrdev_region( devno, 1); + + if(!IS_ERR(egalax_class)) + { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) + class_device_destroy(egalax_class, devno); +#else + device_destroy(egalax_class, devno); +#endif + class_destroy(egalax_class); + } + + i2c_del_driver(&egalax_i2c_driver); + + printk(KERN_DEBUG "[egalax_i2c]: driver exit\n"); +} + +static struct egalax_char_dev* setup_chardev(dev_t dev) +{ + struct egalax_char_dev *pCharDev; + int result; + + pCharDev = kmalloc(1*sizeof(struct egalax_char_dev), GFP_KERNEL); + if(!pCharDev) + goto fail_cdev; + memset(pCharDev, 0, sizeof(struct egalax_char_dev)); + + spin_lock_init( &pCharDev->FiFoLock ); + pCharDev->pFiFoBuf = kmalloc(sizeof(unsigned char)*FIFO_SIZE, GFP_KERNEL); + if(!pCharDev->pFiFoBuf) + goto fail_fifobuf; + memset(pCharDev->pFiFoBuf, 0, sizeof(unsigned char)*FIFO_SIZE); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) + pCharDev->pDataKFiFo = kfifo_init(pCharDev->pFiFoBuf, FIFO_SIZE, GFP_KERNEL, &pCharDev->FiFoLock); + if( pCharDev->pDataKFiFo==NULL ) + goto fail_kfifo; +#else + kfifo_init(&pCharDev->DataKFiFo, pCharDev->pFiFoBuf, FIFO_SIZE); + if( !kfifo_initialized(&pCharDev->DataKFiFo) ) + goto fail_kfifo; +#endif + + pCharDev->OpenCnts = 0; + cdev_init(&pCharDev->cdev, &egalax_cdev_fops); + pCharDev->cdev.owner = THIS_MODULE; + sema_init(&pCharDev->sem, 1); + init_waitqueue_head(&pCharDev->fifo_inq); + + result = cdev_add(&pCharDev->cdev, dev, 1); + if(result) + { + TS_DEBUG(KERN_ERR "Error cdev ioctldev added\n"); + goto fail_kfifo; + } + + return pCharDev; + +fail_kfifo: + kfree(pCharDev->pFiFoBuf); +fail_fifobuf: + kfree(pCharDev); +fail_cdev: + return NULL; +} + +static void __init egalax_i2c_ts_init_async(void *unused, async_cookie_t cookie) +{ + int result; + dev_t devno = 0; +printk("--------> %s <-------------\n",__func__); + DBG(); + + // Asking for a dynamic major unless directed otherwise at load time. + if(global_major) + { + devno = MKDEV(global_major, global_minor); + result = register_chrdev_region(devno, 1, "egalax_i2c"); + } + else + { + result = alloc_chrdev_region(&devno, global_minor, 1, "egalax_i2c"); + global_major = MAJOR(devno); + } + + if (result < 0) + { + TS_DEBUG(" egalax_i2c cdev can't get major number\n"); + return 0; + } + + // allocate the character device + p_char_dev = setup_chardev(devno); + if(!p_char_dev) + { + result = -ENOMEM; + goto fail; + } + + egalax_class = class_create(THIS_MODULE, "egalax_i2c"); + if(IS_ERR(egalax_class)) + { + TS_DEBUG("Err: failed in creating class.\n"); + result = -EFAULT; + goto fail; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) + class_device_create(egalax_class, NULL, devno, NULL, "egalax_i2c"); +#else + device_create(egalax_class, NULL, devno, NULL, "egalax_i2c"); +#endif + TS_DEBUG("register egalax_i2c cdev, major: %d \n",global_major); +printk("--------> %s <-------------\n",__func__); + printk(KERN_DEBUG "[egalax_i2c]: init done\n"); + return i2c_add_driver(&egalax_i2c_driver); + +fail: + egalax_i2c_ts_exit(); + return result; +} + +static int __init egalax_i2c_ts_init(void) +{ + async_schedule(egalax_i2c_ts_init_async, NULL); + return 0; +} +module_init(egalax_i2c_ts_init); +module_exit(egalax_i2c_ts_exit); + +MODULE_DESCRIPTION("egalax touch screen i2c driver"); +MODULE_LICENSE("GPL"); +