rk2928-phonepad-sdk: add ap321xx l/p sensor and byd693x tp support
authorlinjh <linjh@rock-chips.com>
Wed, 10 Oct 2012 11:14:05 +0000 (19:14 +0800)
committerlinjh <linjh@rock-chips.com>
Wed, 10 Oct 2012 11:14:05 +0000 (19:14 +0800)
[reference files]

modified:
arch/arm/configs/rk2928_phonepad_defconfig
arch/arm/mach-rk2928/board-rk2928-phonepad.c
arch/arm/mach-rk2928/include/mach/board.h
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
new file:
drivers/input/misc/ap321xx.c
drivers/input/touchscreen/byd693x_ts.c
drivers/input/touchscreen/byd693x_ts.h

arch/arm/configs/rk2928_phonepad_defconfig
arch/arm/mach-rk2928/board-rk2928-phonepad.c
arch/arm/mach-rk2928/include/mach/board.h
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/ap321xx.c [new file with mode: 0644]
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/byd693x_ts.c [new file with mode: 0644]
drivers/input/touchscreen/byd693x_ts.h [new file with mode: 0644]

index 64551a783d2a1ef8ccba92d4db11dcade369db77..72ad4b792c3854b59b58a0c8f039cb359a7e1091 100644 (file)
@@ -239,7 +239,9 @@ CONFIG_TABLET_USB_KBTAB=y
 CONFIG_TABLET_USB_WACOM=y
 CONFIG_INPUT_TOUCHSCREEN=y
 CONFIG_TOUCHSCREEN_I30=y
+CONFIG_TOUCHSCREEN_BYD693X=y
 CONFIG_INPUT_MISC=y
+CONFIG_INPUT_AP321XX=y
 CONFIG_INPUT_KEYCHORD=y
 CONFIG_INPUT_UINPUT=y
 CONFIG_SENSOR_DEVICE=y
index 26762b3dd21250a5fc52f42b58695104cc8b91e0..a0ee44695c6d77418b535a8038cb481ad7df449b 100755 (executable)
@@ -378,6 +378,18 @@ struct ft5306_platform_data ft5306_info = {
 };
 #endif
 
+#if defined(CONFIG_TOUCHSCREEN_BYD693X)
+
+#define TOUCH_RESET_PIN RK2928_PIN3_PD5
+#define TOUCH_INT_PIN   RK2928_PIN3_PC7
+struct byd_platform_data byd693x_info = {
+       .int_pin = TOUCH_INT_PIN,
+       .rst_pin = TOUCH_RESET_PIN,
+       .screen_max_x = 800,
+       .screen_max_y = 480,
+       .xpol = -1,
+};
+#endif
 
 /*MMA7660 gsensor*/
 #if defined (CONFIG_GS_MMA7660)
@@ -883,6 +895,15 @@ static struct i2c_board_info __initdata i2c2_info[] = {
                .platform_data = &ft5306_info,
        },
 #endif
+#if defined(CONFIG_TOUCHSCREEN_BYD693X)
+       {
+               .type          = "byd693x-ts",
+               .addr          = 0x52,
+               .flags         = 0,
+               .irq           = TOUCH_INT_PIN,
+               .platform_data = &byd693x_info,
+       },
+#endif
 };
 #endif
 #ifdef CONFIG_I2C3_RK30
index 2040eb35fa47103154c55b12c3e568250655cae2..2f012fd005d5942ebb70e0353933b37a8f31c80d 100644 (file)
@@ -99,6 +99,23 @@ struct ft5306_platform_data {
     void    (*exit_platform_hw)(void);
 };
 #endif
+#if defined(CONFIG_TOUCHSCREEN_BYD693X)
+struct byd_platform_data {
+       u16     model;
+       int     pwr_pin;
+       int       int_pin;
+       int     rst_pin;
+       int             pwr_on_value;
+       int     *tp_flag;
+
+       uint16_t screen_max_x;
+       uint16_t screen_max_y;
+       u8 swap_xy :1;
+       u8 xpol :1;
+       u8 ypol :1;     
+};
+#endif
+
 
 #if defined (CONFIG_GPIOEXP_AW9523B)
 struct gpio_exp_platform_data {
@@ -107,6 +124,13 @@ struct gpio_exp_platform_data {
 };
 #endif
 
+#ifdef CONFIG_INPUT_AP321XX
+struct ap321xx_platform_data {
+       int (*init_platform_hw)(void);
+       void (*exit_platform_hw)(void);
+};
+#endif
+
 enum _periph_pll {
        periph_pll_1485mhz = 148500000,
        periph_pll_297mhz = 297000000,
index b2d165feab474acadeb1c7add2aa992f054a3043..f5ab923449ecc91a669deb374171b4192f011969 100755 (executable)
@@ -21,6 +21,12 @@ config INPUT_LPSENSOR_CM3602
 config INPUT_LPSENSOR_AL3006
        tristate "al3006 l/p sensor input support"
 
+config INPUT_AP321XX
+       tristate "ap321xx light and proximity sensor support"
+       default n
+       help     
+         To have support for AP321XX serial light and proximity sensor.
+
 config INPUT_88PM860X_ONKEY
        tristate "88PM860x ONKEY support"
        depends on MFD_88PM860X
index 93e4103aa35fb1ead2f896e791442f913705e511..1619523e8e58c6eeb7d7120b834a5ce03ce6a188 100755 (executable)
@@ -52,4 +52,4 @@ obj-$(CONFIG_INPUT_WM831X_ON)         += wm831x-on.o
 obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND)        += xen-kbdfront.o
 obj-$(CONFIG_INPUT_YEALINK)            += yealink.o
 obj-$(CONFIG_INPUT_LPSENSOR_AL3006)    += al3006.o
-
+obj-$(CONFIG_INPUT_AP321XX)            += ap321xx.o
diff --git a/drivers/input/misc/ap321xx.c b/drivers/input/misc/ap321xx.c
new file mode 100644 (file)
index 0000000..495123b
--- /dev/null
@@ -0,0 +1,1395 @@
+/*
+ * This file is part of the AP3212B, AP3212C and AP3216C sensor driver.
+ * AP3212B is combined proximity and ambient light sensor.
+ * AP3216C is combined proximity, ambient light sensor and IRLED.
+ *
+ * Contact: YC Hou <yc.hou@liteonsemi.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ *
+ * Filename: ap321XX.c
+ *
+ * Summary:
+ *     AP3212B v1.5-1.8 and AP3212C/AP3216C v2.0-v2.3 sensor dirver.
+ *
+ * Modification History:
+ * Date     By       Summary
+ * -------- -------- -------------------------------------------------------
+ * 06/28/11 YC          Original Creation (Test version:1.0)
+ * 06/28/11 YC       Change dev name to dyna for demo purpose (ver 1.5).
+ * 08/29/11 YC       Add engineer mode. Change version to 1.6.
+ * 09/26/11 YC       Add calibration compensation function and add not power up 
+ *                   prompt. Change version to 1.7.
+ * 02/02/12 YC       1. Modify irq function to seperate two interrupt routine. 
+ *                                      2. Fix the index of reg array error in em write. 
+ * 02/22/12 YC       3. Merge AP3212B and AP3216C into the same driver. (ver 1.8)
+ * 03/01/12 YC       Add AP3212C into the driver. (ver 1.8)
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/string.h>
+#include <mach/gpio.h>
+#include <linux/miscdevice.h>
+#include <asm/uaccess.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <mach/board.h>
+
+#define AP3212B_DRV_NAME               "ap321xx"
+#define DRIVER_VERSION         "1.8"
+
+#define AP3212B_NUM_CACHABLE_REGS      23
+#define AP3216C_NUM_CACHABLE_REGS      26
+
+#define AP3212B_RAN_COMMAND    0x10
+#define AP3212B_RAN_MASK               0x30
+#define AP3212B_RAN_SHIFT      (4)
+
+#define AP3212B_MODE_COMMAND   0x00
+#define AP3212B_MODE_SHIFT     (0)
+#define AP3212B_MODE_MASK      0x07
+
+#define        AL3212_ADC_LSB          0x0c
+#define        AL3212_ADC_MSB          0x0d
+
+#define        AL3212_PX_LSB           0x0e
+#define        AL3212_PX_MSB           0x0f
+#define        AL3212_PX_LSB_MASK      0x0f
+#define        AL3212_PX_MSB_MASK      0x3f
+
+#define AP3212B_OBJ_COMMAND    0x0f
+#define AP3212B_OBJ_MASK               0x80
+#define AP3212B_OBJ_SHIFT      (7)
+
+#define AP3212B_INT_COMMAND    0x01
+#define AP3212B_INT_SHIFT      (0)
+#define AP3212B_INT_MASK               0x03
+#define AP3212B_INT_PMASK              0x02
+#define AP3212B_INT_AMASK              0x01
+
+#define AP3212B_ALS_LTHL                       0x1a
+#define AP3212B_ALS_LTHL_SHIFT (0)
+#define AP3212B_ALS_LTHL_MASK  0xff
+
+#define AP3212B_ALS_LTHH                       0x1b
+#define AP3212B_ALS_LTHH_SHIFT (0)
+#define AP3212B_ALS_LTHH_MASK  0xff
+
+#define AP3212B_ALS_HTHL                       0x1c
+#define AP3212B_ALS_HTHL_SHIFT (0)
+#define AP3212B_ALS_HTHL_MASK  0xff
+
+#define AP3212B_ALS_HTHH                       0x1d
+#define AP3212B_ALS_HTHH_SHIFT (0)
+#define AP3212B_ALS_HTHH_MASK  0xff
+
+#define AP3212B_PX_LTHL                        0x2a
+#define AP3212B_PX_LTHL_SHIFT  (0)
+#define AP3212B_PX_LTHL_MASK           0x03
+
+#define AP3212B_PX_LTHH                        0x2b
+#define AP3212B_PX_LTHH_SHIFT  (0)
+#define AP3212B_PX_LTHH_MASK           0xff
+
+#define AP3212B_PX_HTHL                        0x2c
+#define AP3212B_PX_HTHL_SHIFT  (0)
+#define AP3212B_PX_HTHL_MASK           0x03
+
+#define AP3212B_PX_HTHH                        0x2d
+#define AP3212B_PX_HTHH_SHIFT  (0)
+#define AP3212B_PX_HTHH_MASK           0xff
+
+#define AP3212B_PX_CONFIGURE   0x20
+
+#define PSENSOR_IOCTL_MAGIC 'c'
+#define PSENSOR_IOCTL_GET_ENABLED _IOR(PSENSOR_IOCTL_MAGIC, 1, int *)
+#define PSENSOR_IOCTL_ENABLE _IOW(PSENSOR_IOCTL_MAGIC, 2, int *)
+
+#define LIGHTSENSOR_IOCTL_MAGIC 'l'
+#define LIGHTSENSOR_IOCTL_GET_ENABLED _IOR(LIGHTSENSOR_IOCTL_MAGIC, 1, int *)
+#define LIGHTSENSOR_IOCTL_ENABLE _IOW(LIGHTSENSOR_IOCTL_MAGIC, 2, int *)
+
+
+#define LSC_DBG
+#ifdef LSC_DBG
+#define LDBG(s,args...)        {printk("LDBG: func [%s], line [%d], ",__func__,__LINE__); printk(s,## args);}
+#else
+#define LDBG(s,args...) {}
+#endif
+
+struct ap321xx_data {
+       struct i2c_client *client;
+       u8 reg_cache[AP3216C_NUM_CACHABLE_REGS];
+       u8 power_state_before_suspend;
+       int irq;
+       struct input_dev        *psensor_input_dev;
+       struct input_dev        *lsensor_input_dev;
+};
+
+// AP3216C / AP3212C register
+static u8 ap3216c_reg[AP3216C_NUM_CACHABLE_REGS] = 
+       {0x00,0x01,0x02,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
+        0x10,0x19,0x1a,0x1b,0x1c,0x1d,
+        0x20,0x21,0x22,0x23,0x24,0x28,0x29,0x2a,0x2b,0x2c,0x2d};
+
+// AP3216C / AP3212C range
+static int ap3216c_range[4] = {23360,5840,1460,265};
+
+// AP3212B register
+static u8 ap3212b_reg[AP3212B_NUM_CACHABLE_REGS] = 
+       {0x00,0x01,0x02,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
+        0x10,0x11,0x1a,0x1b,0x1c,0x1d,
+        0x20,0x21,0x22,0x23,0x2a,0x2b,0x2c,0x2d};
+
+// AP3212B range
+static int ap3212b_range[4] = {65535,16383,4095,1023};
+
+static u16 ap321xx_threshole[8] = {28,444,625,888,1778,3555,7222,0xffff};
+
+static u8 *reg_array;
+static int *range;
+static int reg_num = 0;
+
+static int cali = 100;
+
+#define ADD_TO_IDX(addr,idx)   {                                                                                                               \
+                                                                       int i;                                                                                          \
+                                                                       for(i = 0; i < reg_num; i++)                                            \
+                                                                       {                                                                                                       \
+                                                                               if (addr == reg_array[i])                                               \
+                                                                               {                                                                                               \
+                                                                                       idx = i;                                                                        \
+                                                                                       break;                                                                          \
+                                                                               }                                                                                               \
+                                                                       }                                                                                                       \
+                                                               }
+
+
+/*
+ * register access helpers
+ */
+
+static int __ap321xx_read_reg(struct i2c_client *client,
+                              u32 reg, u8 mask, u8 shift)
+{
+       struct ap321xx_data *data = i2c_get_clientdata(client);
+       u8 idx = 0xff;
+
+       ADD_TO_IDX(reg,idx)
+       return (data->reg_cache[idx] & mask) >> shift;
+}
+
+static int __ap321xx_write_reg(struct i2c_client *client,
+                               u32 reg, u8 mask, u8 shift, u8 val)
+{
+       struct ap321xx_data *data = i2c_get_clientdata(client);
+       int ret = 0;
+       u8 tmp;
+       u8 idx = 0xff;
+
+       ADD_TO_IDX(reg,idx)
+       if (idx >= reg_num)
+               return -EINVAL;
+
+       tmp = data->reg_cache[idx];
+       tmp &= ~mask;
+       tmp |= val << shift;
+
+       ret = i2c_smbus_write_byte_data(client, reg, tmp);
+       if (!ret)
+               data->reg_cache[idx] = tmp;
+
+       return ret;
+}
+
+/*
+ * internally used functions
+ */
+
+/* range */
+static int ap321xx_get_range(struct i2c_client *client)
+{
+       u8 idx = __ap321xx_read_reg(client, AP3212B_RAN_COMMAND,
+               AP3212B_RAN_MASK, AP3212B_RAN_SHIFT); 
+       return range[idx];
+}
+
+static int ap321xx_set_range(struct i2c_client *client, int range)
+{
+       return __ap321xx_write_reg(client, AP3212B_RAN_COMMAND,
+               AP3212B_RAN_MASK, AP3212B_RAN_SHIFT, range);;
+}
+
+
+/* mode */
+static int ap321xx_get_mode(struct i2c_client *client)
+{
+       int ret;
+
+       ret = __ap321xx_read_reg(client, AP3212B_MODE_COMMAND,
+                       AP3212B_MODE_MASK, AP3212B_MODE_SHIFT);
+       return ret;
+}
+
+static int ap321xx_set_mode(struct i2c_client *client, int mode)
+{
+       int ret;
+
+       ret = __ap321xx_write_reg(client, AP3212B_MODE_COMMAND,
+                               AP3212B_MODE_MASK, AP3212B_MODE_SHIFT, mode);
+       return ret;
+}
+
+/* ALS low threshold */
+static int ap321xx_get_althres(struct i2c_client *client)
+{
+       int lsb, msb;
+       lsb = __ap321xx_read_reg(client, AP3212B_ALS_LTHL,
+                               AP3212B_ALS_LTHL_MASK, AP3212B_ALS_LTHL_SHIFT);
+       msb = __ap321xx_read_reg(client, AP3212B_ALS_LTHH,
+                               AP3212B_ALS_LTHH_MASK, AP3212B_ALS_LTHH_SHIFT);
+       return ((msb << 8) | lsb);
+}
+
+static int ap321xx_set_althres(struct i2c_client *client, int val)
+{
+       int lsb, msb, err;
+       
+       msb = val >> 8;
+       lsb = val & AP3212B_ALS_LTHL_MASK;
+
+       err = __ap321xx_write_reg(client, AP3212B_ALS_LTHL,
+               AP3212B_ALS_LTHL_MASK, AP3212B_ALS_LTHL_SHIFT, lsb);
+       if (err)
+               return err;
+
+       err = __ap321xx_write_reg(client, AP3212B_ALS_LTHH,
+               AP3212B_ALS_LTHH_MASK, AP3212B_ALS_LTHH_SHIFT, msb);
+
+       return err;
+}
+
+/* ALS high threshold */
+static int ap321xx_get_ahthres(struct i2c_client *client)
+{
+       int lsb, msb;
+       lsb = __ap321xx_read_reg(client, AP3212B_ALS_HTHL,
+                               AP3212B_ALS_HTHL_MASK, AP3212B_ALS_HTHL_SHIFT);
+       msb = __ap321xx_read_reg(client, AP3212B_ALS_HTHH,
+                               AP3212B_ALS_HTHH_MASK, AP3212B_ALS_HTHH_SHIFT);
+       return ((msb << 8) | lsb);
+}
+
+static int ap321xx_set_ahthres(struct i2c_client *client, int val)
+{
+       int lsb, msb, err;
+       
+       msb = val >> 8;
+       lsb = val & AP3212B_ALS_HTHL_MASK;
+       
+       err = __ap321xx_write_reg(client, AP3212B_ALS_HTHL,
+               AP3212B_ALS_HTHL_MASK, AP3212B_ALS_HTHL_SHIFT, lsb);
+       if (err)
+               return err;
+
+       err = __ap321xx_write_reg(client, AP3212B_ALS_HTHH,
+               AP3212B_ALS_HTHH_MASK, AP3212B_ALS_HTHH_SHIFT, msb);
+
+       return err;
+}
+
+/* PX low threshold */
+static int ap321xx_get_plthres(struct i2c_client *client)
+{
+       int lsb, msb;
+       lsb = __ap321xx_read_reg(client, AP3212B_PX_LTHL,
+                               AP3212B_PX_LTHL_MASK, AP3212B_PX_LTHL_SHIFT);
+       msb = __ap321xx_read_reg(client, AP3212B_PX_LTHH,
+                               AP3212B_PX_LTHH_MASK, AP3212B_PX_LTHH_SHIFT);
+       return ((msb << 2) | lsb);
+}
+
+static int ap321xx_set_plthres(struct i2c_client *client, int val)
+{
+       int lsb, msb, err;
+       
+       msb = val >> 2;
+       lsb = val & AP3212B_PX_LTHL_MASK;
+       
+       err = __ap321xx_write_reg(client, AP3212B_PX_LTHL,
+               AP3212B_PX_LTHL_MASK, AP3212B_PX_LTHL_SHIFT, lsb);
+       if (err)
+               return err;
+
+       err = __ap321xx_write_reg(client, AP3212B_PX_LTHH,
+               AP3212B_PX_LTHH_MASK, AP3212B_PX_LTHH_SHIFT, msb);
+
+       return err;
+}
+
+/* PX high threshold */
+static int ap321xx_get_phthres(struct i2c_client *client)
+{
+       int lsb, msb;
+       lsb = __ap321xx_read_reg(client, AP3212B_PX_HTHL,
+                               AP3212B_PX_HTHL_MASK, AP3212B_PX_HTHL_SHIFT);
+       msb = __ap321xx_read_reg(client, AP3212B_PX_HTHH,
+                               AP3212B_PX_HTHH_MASK, AP3212B_PX_HTHH_SHIFT);
+       return ((msb << 2) | lsb);
+}
+
+static int ap321xx_set_phthres(struct i2c_client *client, int val)
+{
+       int lsb, msb, err;
+       
+       msb = val >> 2;
+       lsb = val & AP3212B_ALS_HTHL_MASK;
+       
+       err = __ap321xx_write_reg(client, AP3212B_PX_HTHL,
+               AP3212B_PX_HTHL_MASK, AP3212B_PX_HTHL_SHIFT, lsb);
+       if (err)
+               return err;
+
+       err = __ap321xx_write_reg(client, AP3212B_PX_HTHH,
+               AP3212B_PX_HTHH_MASK, AP3212B_PX_HTHH_SHIFT, msb);
+
+       return err;
+}
+
+static int ap321xx_get_adc_value(struct i2c_client *client)
+{
+       unsigned int lsb, msb, val;
+#ifdef LSC_DBG
+       unsigned int tmp,range;
+#endif
+       unsigned char index=0;
+
+       lsb = i2c_smbus_read_byte_data(client, AL3212_ADC_LSB);
+
+       if (lsb < 0) {
+               return lsb;
+       }
+
+       msb = i2c_smbus_read_byte_data(client, AL3212_ADC_MSB);
+
+       if (msb < 0)
+               return msb;
+
+#ifdef LSC_DBG
+       range = ap321xx_get_range(client);
+       tmp = (((msb << 8) | lsb) * range) >> 16;
+       tmp = tmp * cali / 100;
+       LDBG("ALS val=%d lux\n",tmp);
+#endif
+       val = msb << 8 | lsb;
+       for(index = 0; index < 7 && val > ap321xx_threshole[index];index++)
+               ;
+
+       return index;
+}
+
+static int ap321xx_get_object(struct i2c_client *client)
+{
+       int val;
+
+       val = i2c_smbus_read_byte_data(client, AP3212B_OBJ_COMMAND);
+       val &= AP3212B_OBJ_MASK;
+
+       return val >> AP3212B_OBJ_SHIFT;
+}
+
+static int ap321xx_get_intstat(struct i2c_client *client)
+{
+       int val;
+       
+       val = i2c_smbus_read_byte_data(client, AP3212B_INT_COMMAND);
+       val &= AP3212B_INT_MASK;
+
+       return val >> AP3212B_INT_SHIFT;
+}
+
+
+static int ap321xx_get_px_value(struct i2c_client *client)
+{
+       int lsb, msb;
+
+       lsb = i2c_smbus_read_byte_data(client, AL3212_PX_LSB);
+
+       if (lsb < 0) {
+               return lsb;
+       }
+
+       msb = i2c_smbus_read_byte_data(client, AL3212_PX_MSB);
+
+       if (msb < 0)
+               return msb;
+
+       return (u32)(((msb & AL3212_PX_MSB_MASK) << 4) | (lsb & AL3212_PX_LSB_MASK));
+}
+
+#if 0
+/*
+ * sysfs layer
+ */
+static int ap321xx_input_init(struct ap321xx_data *data)
+{
+    struct input_dev *dev;
+    int err;
+
+    dev = input_allocate_device();
+    if (!dev) {
+        return -ENOMEM;
+    }
+    dev->name = "lightsensor-level";
+    dev->id.bustype = BUS_I2C;
+
+    input_set_capability(dev, EV_ABS, ABS_MISC);
+    input_set_capability(dev, EV_ABS, ABS_RUDDER);
+    input_set_drvdata(dev, data);
+
+    err = input_register_device(dev);
+    if (err < 0) {
+        input_free_device(dev);
+        return err;
+    }
+    data->input = dev;
+
+    return 0;
+}
+
+static void ap321xx_input_fini(struct ap321xx_data *data)
+{
+    struct input_dev *dev = data->input;
+
+    input_unregister_device(dev);
+    input_free_device(dev);
+}
+#else
+static int ap321xx_lsensor_open(struct inode *inode, struct file *file);
+static int ap321xx_lsensor_release(struct inode *inode, struct file *file);
+static long ap321xx_lsensor_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+static void ap321xx_change_ls_threshold(struct i2c_client *client);
+
+static int misc_ls_opened = 0;
+static struct file_operations ap321xx_lsensor_fops = {
+       .owner = THIS_MODULE,
+       .open = ap321xx_lsensor_open,
+       .release = ap321xx_lsensor_release,
+       .unlocked_ioctl = ap321xx_lsensor_ioctl
+};
+
+static struct miscdevice ap321xx_lsensor_misc = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "lightsensor",
+       .fops = &ap321xx_lsensor_fops
+};
+
+static int ap321xx_lsensor_open(struct inode *inode, struct file *file)
+{
+       LDBG("\n");
+       if (misc_ls_opened)
+               return -EBUSY;
+       misc_ls_opened = 1;
+       return 0;
+}
+
+static int ap321xx_lsensor_release(struct inode *inode, struct file *file)
+{
+       LDBG("\n");
+       misc_ls_opened = 0;
+       return 0;
+}
+
+static int ap321xx_lsensor_enable(struct i2c_client *client)
+{
+       int ret = 0,mode;
+       
+       mode = ap321xx_get_mode(client);
+       if((mode & 0x01) == 0){
+               mode |= 0x01;
+               ret = ap321xx_set_mode(client,mode);
+       }
+       
+       return ret;
+}
+
+static int ap321xx_lsensor_disable(struct i2c_client *client)
+{
+       int ret = 0,mode;
+       
+       mode = ap321xx_get_mode(client);
+       if(mode & 0x01){
+               mode &= ~0x01;
+               if(mode == 0x04)
+                       mode = 0;
+               ret = ap321xx_set_mode(client,mode);
+       }
+       
+       return ret;
+}
+
+static long ap321xx_lsensor_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       char val;
+       int ret;
+       struct i2c_client *client = container_of(ap321xx_lsensor_misc.parent, struct i2c_client, dev);
+
+       LDBG("%s cmd %d\n", __FUNCTION__, _IOC_NR(cmd));
+       
+       switch (cmd) {
+       case LIGHTSENSOR_IOCTL_ENABLE:
+               if (get_user(val, (unsigned long __user *)arg))
+                       return -EFAULT;
+               if (val){
+                       ret = ap321xx_lsensor_enable(client);
+                       if(!ret){
+                               msleep(200);
+                               ap321xx_change_ls_threshold(client);
+                       }
+                       return ret;
+               }
+               else
+                       return ap321xx_lsensor_disable(client);
+               break;
+       case LIGHTSENSOR_IOCTL_GET_ENABLED:
+               val = ap321xx_get_mode(client);
+               val &= 0x01;
+               return put_user(val, (unsigned long __user *)arg);
+               break;
+       default:
+               pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd));
+               return -EINVAL;
+       }
+}
+
+static int ap321xx_register_lsensor_device(struct i2c_client *client, struct ap321xx_data *data)
+{
+       struct input_dev *input_dev;
+       int rc;
+
+       LDBG("allocating input device lsensor\n");
+       input_dev = input_allocate_device();
+       if (!input_dev) {
+               dev_err(&client->dev,"%s: could not allocate input device for lsensor\n", __FUNCTION__);
+               rc = -ENOMEM;
+               goto done;
+       }
+       data->lsensor_input_dev = input_dev;
+       input_set_drvdata(input_dev, data);
+       input_dev->name = "lightsensor-level";
+       input_dev->dev.parent = &client->dev;
+       set_bit(EV_ABS, input_dev->evbit);
+       input_set_abs_params(input_dev, ABS_MISC, 0, 8, 0, 0);
+
+       rc = input_register_device(input_dev);
+       if (rc < 0) {
+               pr_err("%s: could not register input device for lsensor\n", __FUNCTION__);
+               goto done;
+       }
+       rc = misc_register(&ap321xx_lsensor_misc);
+       if (rc < 0) {
+               pr_err("%s: could not register misc device lsensor\n", __FUNCTION__);
+               goto err_unregister_input_device;
+       }
+
+       ap321xx_lsensor_misc.parent = &client->dev;
+       return 0;
+
+err_unregister_input_device:
+       input_unregister_device(input_dev);
+done:
+       return rc;
+}
+
+static void ap321xx_unregister_lsensor_device(struct i2c_client *client, struct ap321xx_data *data)
+{
+       misc_deregister(&ap321xx_lsensor_misc);
+       input_unregister_device(data->lsensor_input_dev);
+}
+
+static void ap321xx_change_ls_threshold(struct i2c_client *client)
+{
+       struct ap321xx_data *data = i2c_get_clientdata(client);
+       int value;
+
+       value = ap321xx_get_adc_value(client);
+       LDBG("ALS lux index: %u\n", value);
+       if(value > 0){
+               ap321xx_set_althres(client,ap321xx_threshole[value-1]);
+               ap321xx_set_ahthres(client,ap321xx_threshole[value]);
+       }
+       else{
+               ap321xx_set_althres(client,0);
+               ap321xx_set_ahthres(client,ap321xx_threshole[value]);
+       }
+       
+       input_report_abs(data->lsensor_input_dev, ABS_MISC, value);
+       input_sync(data->lsensor_input_dev);
+       
+}
+
+
+static int misc_ps_opened = 0;
+static int ap321xx_psensor_open(struct inode *inode, struct file *file);
+static int ap321xx_psensor_release(struct inode *inode, struct file *file);
+static long ap321xx_psensor_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+
+static struct file_operations ap321xx_psensor_fops = {
+       .owner = THIS_MODULE,
+       .open = ap321xx_psensor_open,
+       .release = ap321xx_psensor_release,
+       .unlocked_ioctl = ap321xx_psensor_ioctl
+};
+
+static struct miscdevice ap321xx_psensor_misc = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "psensor",
+       .fops = &ap321xx_psensor_fops
+};
+
+static int ap321xx_psensor_open(struct inode *inode, struct file *file)
+{
+       LDBG("\n");
+       if (misc_ps_opened)
+               return -EBUSY;
+       misc_ps_opened = 1;
+       return 0;
+}
+
+static int ap321xx_psensor_release(struct inode *inode, struct file *file)
+{
+       LDBG("\n");
+       misc_ps_opened = 0;
+       return 0;
+}
+
+static int ap321xx_psensor_enable(struct i2c_client *client)
+{
+       int ret = 0,mode;
+       
+       mode = ap321xx_get_mode(client);
+       if((mode & 0x02) == 0){
+               mode |= 0x02;
+               ret = ap321xx_set_mode(client,mode);
+       }
+       
+       return ret;
+}
+
+static int ap321xx_psensor_disable(struct i2c_client *client)
+{
+       int ret = 0,mode;
+       
+       mode = ap321xx_get_mode(client);
+       if(mode & 0x02){
+               mode &= ~0x02;
+               if(mode == 0x04)
+                       mode = 0x00;
+               ret = ap321xx_set_mode(client,mode);
+       }
+       return ret;
+}
+
+
+static long ap321xx_psensor_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       char val;
+       struct i2c_client *client = container_of(ap321xx_psensor_misc.parent,struct i2c_client,dev);
+
+       LDBG("%s cmd %d\n", __func__, _IOC_NR(cmd));
+       
+       switch (cmd) {
+       case PSENSOR_IOCTL_ENABLE:
+               if (get_user(val, (unsigned long __user *)arg))
+                       return -EFAULT;
+               if (val)
+                       return ap321xx_psensor_enable(client);
+               else
+                       return ap321xx_psensor_disable(client);
+               break;
+       case PSENSOR_IOCTL_GET_ENABLED:
+               val = ap321xx_get_mode(client);
+               val = (val >> 1) & 0x01;
+               return put_user(val, (unsigned long __user *)arg);
+               break;
+       default:
+               pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd));
+               return -EINVAL;
+       }
+}
+
+static int ap321xx_register_psensor_device(struct i2c_client *client, struct ap321xx_data *data)
+{
+       struct input_dev *input_dev;
+       int rc;
+
+       LDBG("allocating input device psensor\n");
+       input_dev = input_allocate_device();
+       if (!input_dev) {
+               dev_err(&client->dev,"%s: could not allocate input device for psensor\n", __FUNCTION__);
+               rc = -ENOMEM;
+               goto done;
+       }
+       data->psensor_input_dev = input_dev;
+       input_set_drvdata(input_dev, data);
+       input_dev->name = "proximity";
+       input_dev->dev.parent = &client->dev;
+       set_bit(EV_ABS, input_dev->evbit);
+       input_set_abs_params(input_dev, ABS_DISTANCE, 0, 1, 0, 0);
+
+       rc = input_register_device(input_dev);
+       if (rc < 0) {
+               pr_err("%s: could not register input device for psensor\n", __FUNCTION__);
+               goto done;
+       }
+
+       rc = misc_register(&ap321xx_psensor_misc);
+       if (rc < 0) {
+               pr_err("%s: could not register misc device psensor\n", __FUNCTION__);
+               goto err_unregister_input_device;
+       }
+       ap321xx_psensor_misc.parent = &client->dev;
+       return 0;
+
+err_unregister_input_device:
+       input_unregister_device(input_dev);
+done:
+       return rc;
+}
+
+static void ap321xx_unregister_psensor_device(struct i2c_client *client, struct ap321xx_data *data)
+{
+       misc_deregister(&ap321xx_psensor_misc);
+       input_unregister_device(data->psensor_input_dev);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static struct early_suspend ap321xx_early_suspend;
+static void ap321xx_suspend(struct early_suspend *h)
+{
+       struct i2c_client *client = container_of(ap321xx_lsensor_misc.parent, struct i2c_client, dev);
+
+       if (misc_ps_opened)
+               ap321xx_psensor_disable(client);
+       if (misc_ls_opened)
+               ap321xx_lsensor_disable(client);
+}
+
+static void ap321xx_resume(struct early_suspend *h)
+{
+       struct i2c_client *client = container_of(ap321xx_lsensor_misc.parent, struct i2c_client, dev);
+
+       if (misc_ls_opened)
+               ap321xx_lsensor_enable(client);
+       if (misc_ps_opened)
+               ap321xx_psensor_enable(client);
+}
+#endif
+
+#endif
+
+/* range */
+static ssize_t ap321xx_show_range(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       struct input_dev *input = to_input_dev(dev);
+       struct ap321xx_data *data = input_get_drvdata(input);
+       return sprintf(buf, "%i\n", ap321xx_get_range(data->client));
+}
+
+static ssize_t ap321xx_store_range(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       struct input_dev *input = to_input_dev(dev);
+       struct ap321xx_data *data = input_get_drvdata(input);
+       unsigned long val;
+       int ret;
+
+       if ((strict_strtoul(buf, 10, &val) < 0) || (val > 3))
+               return -EINVAL;
+
+       ret = ap321xx_set_range(data->client, val);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static DEVICE_ATTR(range, S_IWUSR | S_IRUGO,
+                  ap321xx_show_range, ap321xx_store_range);
+
+
+/* mode */
+static ssize_t ap321xx_show_mode(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct input_dev *input = to_input_dev(dev);
+       struct ap321xx_data *data = input_get_drvdata(input);
+       return sprintf(buf, "%d\n", ap321xx_get_mode(data->client));
+}
+
+static ssize_t ap321xx_store_mode(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct input_dev *input = to_input_dev(dev);
+       struct ap321xx_data *data = input_get_drvdata(input);
+       unsigned long val;
+       int ret;
+
+       if ((strict_strtoul(buf, 10, &val) < 0) || (val > 7))
+               return -EINVAL;
+
+       ret = ap321xx_set_mode(data->client, val);
+       
+       if (ret < 0)
+               return ret;
+       return count;
+}
+
+static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO,
+                  ap321xx_show_mode, ap321xx_store_mode);
+
+
+/* lux */
+static ssize_t ap321xx_show_lux(struct device *dev,
+                                struct device_attribute *attr, char *buf)
+{
+       struct input_dev *input = to_input_dev(dev);
+       struct ap321xx_data *data = input_get_drvdata(input);
+
+       /* No LUX data if power down */
+       if (ap321xx_get_mode(data->client) == 0x00)
+               return sprintf((char*) buf, "%s\n", "Please power up first!");
+
+       return sprintf(buf, "%d\n", ap321xx_get_adc_value(data->client));
+}
+
+static DEVICE_ATTR(lux, S_IRUGO, ap321xx_show_lux, NULL);
+
+
+/* Px data */
+static ssize_t ap321xx_show_pxvalue(struct device *dev,
+                                struct device_attribute *attr, char *buf)
+{
+       struct input_dev *input = to_input_dev(dev);
+       struct ap321xx_data *data = input_get_drvdata(input);
+
+       /* No Px data if power down */
+       if (ap321xx_get_mode(data->client) == 0x00)
+               return -EBUSY;
+
+       return sprintf(buf, "%d\n", ap321xx_get_px_value(data->client));
+}
+
+static DEVICE_ATTR(pxvalue, S_IRUGO, ap321xx_show_pxvalue, NULL);
+
+
+/* proximity object detect */
+static ssize_t ap321xx_show_object(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+       struct input_dev *input = to_input_dev(dev);
+       struct ap321xx_data *data = input_get_drvdata(input);
+       return sprintf(buf, "%d\n", ap321xx_get_object(data->client));
+}
+
+static DEVICE_ATTR(object, S_IRUGO, ap321xx_show_object, NULL);
+
+
+/* ALS low threshold */
+static ssize_t ap321xx_show_althres(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct input_dev *input = to_input_dev(dev);
+       struct ap321xx_data *data = input_get_drvdata(input);
+       return sprintf(buf, "%d\n", ap321xx_get_althres(data->client));
+}
+
+static ssize_t ap321xx_store_althres(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct input_dev *input = to_input_dev(dev);
+       struct ap321xx_data *data = input_get_drvdata(input);
+       unsigned long val;
+       int ret;
+
+       if (strict_strtoul(buf, 10, &val) < 0)
+               return -EINVAL;
+
+       ret = ap321xx_set_althres(data->client, val);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static DEVICE_ATTR(althres, S_IWUSR | S_IRUGO,
+                  ap321xx_show_althres, ap321xx_store_althres);
+
+
+/* ALS high threshold */
+static ssize_t ap321xx_show_ahthres(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct input_dev *input = to_input_dev(dev);
+       struct ap321xx_data *data = input_get_drvdata(input);
+       return sprintf(buf, "%d\n", ap321xx_get_ahthres(data->client));
+}
+
+static ssize_t ap321xx_store_ahthres(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct input_dev *input = to_input_dev(dev);
+       struct ap321xx_data *data = input_get_drvdata(input);
+       unsigned long val;
+       int ret;
+
+       if (strict_strtoul(buf, 10, &val) < 0)
+               return -EINVAL;
+
+       ret = ap321xx_set_ahthres(data->client, val);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static DEVICE_ATTR(ahthres, S_IWUSR | S_IRUGO,
+                  ap321xx_show_ahthres, ap321xx_store_ahthres);
+
+/* Px low threshold */
+static ssize_t ap321xx_show_plthres(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct input_dev *input = to_input_dev(dev);
+       struct ap321xx_data *data = input_get_drvdata(input);
+       return sprintf(buf, "%d\n", ap321xx_get_plthres(data->client));
+}
+
+static ssize_t ap321xx_store_plthres(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct input_dev *input = to_input_dev(dev);
+       struct ap321xx_data *data = input_get_drvdata(input);
+       unsigned long val;
+       int ret;
+
+       if (strict_strtoul(buf, 10, &val) < 0)
+               return -EINVAL;
+
+       ret = ap321xx_set_plthres(data->client, val);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static DEVICE_ATTR(plthres, S_IWUSR | S_IRUGO,
+                  ap321xx_show_plthres, ap321xx_store_plthres);
+
+/* Px high threshold */
+static ssize_t ap321xx_show_phthres(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct input_dev *input = to_input_dev(dev);
+       struct ap321xx_data *data = input_get_drvdata(input);
+       return sprintf(buf, "%d\n", ap321xx_get_phthres(data->client));
+}
+
+static ssize_t ap321xx_store_phthres(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct input_dev *input = to_input_dev(dev);
+       struct ap321xx_data *data = input_get_drvdata(input);
+       unsigned long val;
+       int ret;
+
+       if (strict_strtoul(buf, 10, &val) < 0)
+               return -EINVAL;
+
+       ret = ap321xx_set_phthres(data->client, val);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static DEVICE_ATTR(phthres, S_IWUSR | S_IRUGO,
+                  ap321xx_show_phthres, ap321xx_store_phthres);
+
+
+/* calibration */
+static ssize_t ap321xx_show_calibration_state(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+       return sprintf(buf, "%d\n", cali);
+}
+
+static ssize_t ap321xx_store_calibration_state(struct device *dev,
+                                         struct device_attribute *attr,
+                                         const char *buf, size_t count)
+{
+       struct input_dev *input = to_input_dev(dev);
+       struct ap321xx_data *data = input_get_drvdata(input);
+       int stdls, lux; 
+       char tmp[10];
+
+       /* No LUX data if not operational */
+       if (ap321xx_get_mode(data->client) == 0x00)
+       {
+               printk("Please power up first!");
+               return -EINVAL;
+       }
+
+       cali = 100;
+       sscanf(buf, "%d %s", &stdls, tmp);
+
+       if (!strncmp(tmp, "-setcv", 6))
+       {
+               cali = stdls;
+               return -EBUSY;
+       }
+
+       if (stdls < 0)
+       {
+               printk("Std light source: [%d] < 0 !!!\nCheck again, please.\n\
+               Set calibration factor to 100.\n", stdls);
+               return -EBUSY;
+       }
+
+       lux = ap321xx_get_adc_value(data->client);
+       cali = stdls * 100 / lux;
+
+       return -EBUSY;
+}
+
+static DEVICE_ATTR(calibration, S_IWUSR | S_IRUGO,
+                  ap321xx_show_calibration_state, ap321xx_store_calibration_state);
+
+#ifdef LSC_DBG
+/* engineer mode */
+static ssize_t ap321xx_em_read(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct ap321xx_data *data = i2c_get_clientdata(client);
+       int i;
+       u8 tmp;
+       
+       for (i = 0; i < reg_num; i++)
+       {
+               tmp = i2c_smbus_read_byte_data(data->client, reg_array[i]);
+
+               printk("Reg[0x%x] Val[0x%x]\n", reg_array[i], tmp);
+       }
+
+       return 0;
+}
+
+static ssize_t ap321xx_em_write(struct device *dev,
+                                         struct device_attribute *attr,
+                                         const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct ap321xx_data *data = i2c_get_clientdata(client);
+       u32 addr,val,idx=0;
+       int ret = 0;
+
+       sscanf(buf, "%x%x", &addr, &val);
+
+       printk("Write [%x] to Reg[%x]...\n",val,addr);
+
+       ret = i2c_smbus_write_byte_data(data->client, addr, val);
+       ADD_TO_IDX(addr,idx)
+       if (!ret)
+               data->reg_cache[idx] = val;
+
+       return count;
+}
+static DEVICE_ATTR(em, S_IWUSR |S_IRUGO,
+                                  ap321xx_em_read, ap321xx_em_write);
+#endif
+
+static struct attribute *ap321xx_attributes[] = {
+       &dev_attr_range.attr,
+       &dev_attr_mode.attr,
+       &dev_attr_lux.attr,
+       &dev_attr_object.attr,
+       &dev_attr_pxvalue.attr,
+       &dev_attr_althres.attr,
+       &dev_attr_ahthres.attr,
+       &dev_attr_plthres.attr,
+       &dev_attr_phthres.attr,
+       &dev_attr_calibration.attr,
+#ifdef LSC_DBG
+       &dev_attr_em.attr,
+#endif
+       NULL
+};
+
+static const struct attribute_group ap321xx_attr_group = {
+       .attrs = ap321xx_attributes,
+};
+
+static int Product_Detect(struct i2c_client *client)
+{
+       int mid = i2c_smbus_read_byte_data(client, 0x03);
+       int pid = i2c_smbus_read_byte_data(client, 0x04);
+       int rid = i2c_smbus_read_byte_data(client, 0x05);
+
+       if ( mid == 0x01 && pid == 0x01 && 
+           (rid == 0x03 || rid == 0x04) )
+       {
+               LDBG("RevID [%d], ==> DA3212 v1.5~1.8 ...... AP3212B detected\n", rid)
+               reg_array = ap3212b_reg;
+               range = ap3212b_range;
+               reg_num = AP3212B_NUM_CACHABLE_REGS;
+       }
+       else if ( (mid == 0x01 && pid == 0x02 && rid == 0x00) || 
+                     (mid == 0x02 && pid == 0x02 && rid == 0x01))
+       {
+               LDBG("RevID [%d], ==> DA3212 v2.0 ...... AP3212C/AP3216C detected\n", rid)
+               reg_array = ap3216c_reg;
+               range = ap3216c_range;
+               reg_num = AP3216C_NUM_CACHABLE_REGS;
+       }
+       else
+       {
+               LDBG("MakeID[%d] ProductID[%d] RevID[%d] .... can't detect ... bad reversion!!!\n", mid, pid, rid)
+               return -EIO;
+       }
+               
+
+       return 0;
+}
+
+static int ap321xx_init_client(struct i2c_client *client)
+{
+       struct ap321xx_data *data = i2c_get_clientdata(client);
+       int i;
+
+       /* read all the registers once to fill the cache.
+        * if one of the reads fails, we consider the init failed */
+       for (i = 0; i < reg_num; i++) {
+               int v = i2c_smbus_read_byte_data(client, reg_array[i]);
+               if (v < 0)
+                       return -ENODEV;
+
+               data->reg_cache[i] = v;
+       }
+
+       /* set defaults */
+       ap321xx_set_range(client, 0);
+       ap321xx_set_mode(client, 0);
+
+       return 0;
+}
+
+/*
+ * I2C layer
+ */
+
+static irqreturn_t ap321xx_irq(int irq, void *data_)
+{
+       struct ap321xx_data *data = data_;
+       u8 int_stat;
+       int Pval;
+       int_stat = ap321xx_get_intstat(data->client);
+
+       // ALS int
+       if (int_stat & AP3212B_INT_AMASK)
+       {
+               ap321xx_change_ls_threshold(data->client);
+       }
+       
+       // PX int
+       if (int_stat & AP3212B_INT_PMASK)
+       {
+               Pval = ap321xx_get_object(data->client);
+               LDBG("%s\n", Pval ? "obj near":"obj far");
+               input_report_abs(data->psensor_input_dev, ABS_DISTANCE, Pval);
+               input_sync(data->psensor_input_dev);
+       }
+
+    return IRQ_HANDLED;
+}
+
+static int __devinit ap321xx_probe(struct i2c_client *client,
+                                   const struct i2c_device_id *id)
+{
+       const struct ap321xx_platform_data *pdata = client->dev.platform_data;
+       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+       struct ap321xx_data *data;
+       int err = 0;
+
+       LDBG("ap321xx_probe\n");
+       
+       if (pdata->init_platform_hw) {
+               err = pdata->init_platform_hw();
+               if (err < 0)
+                       goto exit_free_gpio;
+       }
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)){
+               err = -EIO;
+               goto exit_free_gpio;
+       }
+       err = Product_Detect(client);
+       if (err)
+       {
+               dev_err(&client->dev, "ret: %d, product version detect failed.\n",err);
+               err = -EIO;
+               goto exit_free_gpio;
+       }
+
+       data = kzalloc(sizeof(struct ap321xx_data), GFP_KERNEL);
+       if (!data){
+               err = -ENOMEM;
+               goto exit_free_gpio;
+       }
+       
+       data->client = client;
+       i2c_set_clientdata(client, data);
+       data->irq = client->irq;
+
+       /* initialize the AP3212B chip */
+       err = ap321xx_init_client(client);
+       if (err)
+               goto exit_kfree;
+
+       err = ap321xx_register_lsensor_device(client,data);
+       if (err){
+               dev_err(&client->dev, "failed to register_lsensor_device\n");
+               goto exit_kfree;
+       }
+               
+       err = ap321xx_register_psensor_device(client, data);
+       if (err) {
+               dev_err(&client->dev, "failed to register_psensor_device\n");
+               goto exit_free_ls_device;
+       }
+
+#if 0
+       /* register sysfs hooks */
+       err = sysfs_create_group(&data->input->dev.kobj, &ap321xx_attr_group);
+       if (err)
+               goto exit_free_ps_device;
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+       ap321xx_early_suspend.suspend = ap321xx_suspend;
+       ap321xx_early_suspend.resume  = ap321xx_resume;
+       ap321xx_early_suspend.level   = 0x02;
+       register_early_suspend(&ap321xx_early_suspend);
+#endif
+
+
+       err = request_threaded_irq(client->irq, NULL, ap321xx_irq,
+                               IRQF_TRIGGER_FALLING,
+                               "ap321xx", data);
+    if (err) {
+               dev_err(&client->dev, "ret: %d, could not get IRQ %d\n",err,client->irq);
+            goto exit_free_ps_device;
+    }
+
+       dev_info(&client->dev, "Driver version %s enabled\n", DRIVER_VERSION);
+       return 0;
+
+exit_free_ps_device:
+       ap321xx_unregister_psensor_device(client,data);
+
+exit_free_ls_device:
+       ap321xx_unregister_lsensor_device(client,data);
+
+exit_kfree:
+       kfree(data);
+exit_free_gpio:
+       if (pdata->exit_platform_hw) {
+               pdata->exit_platform_hw();
+       }
+       return err;
+}
+
+static int __devexit ap321xx_remove(struct i2c_client *client)
+{
+       const struct ap321xx_platform_data *pdata = client->dev.platform_data;
+       struct ap321xx_data *data = i2c_get_clientdata(client);
+       free_irq(data->irq, data);
+
+//     sysfs_remove_group(&data->input->dev.kobj, &ap321xx_attr_group);
+       ap321xx_unregister_psensor_device(client,data);
+       ap321xx_unregister_lsensor_device(client,data);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+    unregister_early_suspend(&ap321xx_early_suspend);
+#endif
+
+       ap321xx_set_mode(client, 0);
+       kfree(i2c_get_clientdata(client));
+       if (pdata->exit_platform_hw) {
+               pdata->exit_platform_hw();
+       }
+       return 0;
+}
+
+static const struct i2c_device_id ap321xx_id[] = {
+       { AP3212B_DRV_NAME, 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, ap321xx_id);
+
+static struct i2c_driver ap321xx_driver = {
+       .driver = {
+               .name   = AP3212B_DRV_NAME,
+               .owner  = THIS_MODULE,
+       },
+       .probe  = ap321xx_probe,
+       .remove = __devexit_p(ap321xx_remove),
+       .id_table = ap321xx_id,
+};
+
+static int __init ap321xx_init(void)
+{
+       LDBG("ap321xx_init\n");
+       return i2c_add_driver(&ap321xx_driver);
+}
+
+static void __exit ap321xx_exit(void)
+{
+       i2c_del_driver(&ap321xx_driver);
+}
+
+MODULE_AUTHOR("YC Hou, LiteOn-semi corporation.");
+MODULE_DESCRIPTION("Test AP3212B, AP3212C and AP3216C driver on mini6410.");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRIVER_VERSION);
+
+module_init(ap321xx_init);
+module_exit(ap321xx_exit);
+
+
index f845d0cef81dca1c4c13c8ba90d52bbe586e0936..58ef753851912ca806415c69c10c2486f9f0b5f2 100644 (file)
@@ -1016,6 +1016,10 @@ config TOUCHSCREEN_I30
        tristate "i30 based touchscreens: i30(ft5306) Interface"
        depends on I2C2_RK29 || I2C2_RK30
 
+config TOUCHSCREEN_BYD693X
+       tristate "touchscreen BYD693X I2C Interface"
+       depends on I2C2_RK29 || I2C2_RK30
+
 config TOUCHSCREEN_SITRONIX_A720
        tristate "SITRONIX based touchscreens: SITRONIX Interface for a720"
        depends on I2C2_RK29 || I2C2_RK30 
index c16a52bc175e2742be613ed7c6af3a2d8ff4d084..159f846c813a557475a853f57f18c5c1c3f164da 100644 (file)
@@ -93,3 +93,5 @@ obj-$(CONFIG_LAIBAO_TS)               += ft5x0x_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PIXCIR)      += pixcir_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_RMI4_I2C_RK)      += rmi4/
 obj-$(CONFIG_TOUCHSCREEN_I30)  += i30_ts.o
+obj-$(CONFIG_TOUCHSCREEN_BYD693X)      += byd693x_ts.o
+
diff --git a/drivers/input/touchscreen/byd693x_ts.c b/drivers/input/touchscreen/byd693x_ts.c
new file mode 100644 (file)
index 0000000..02b4230
--- /dev/null
@@ -0,0 +1,720 @@
+\r
+\r
+#include <linux/i2c.h>\r
+#include <linux/input.h>\r
+\r
+#include <linux/interrupt.h>\r
+#include <linux/delay.h>\r
+#include <linux/interrupt.h>\r
+#include <linux/errno.h>\r
+#include <linux/kernel.h>\r
+#include <linux/module.h>\r
+#include <linux/slab.h>\r
+#include <linux/input/mt.h>            //use slot B protocol, Android 4.0 system\r
+#include <linux/platform_device.h>\r
+#include <linux/async.h>\r
+#include <linux/hrtimer.h>\r
+#include <linux/init.h>\r
+#include <linux/ioport.h>\r
+#include <asm/irq.h>\r
+#include <asm/io.h>\r
+#include <asm/uaccess.h>\r
+#include <linux/gpio.h>\r
+\r
+#include <mach/irqs.h>\r
+//#include <mach/system.h>\r
+//#include <mach/hardware.h>\r
+//#include <mach/sys_config.h>\r
+#include <mach/board.h>\r
+\r
+#ifdef CONFIG_HAS_EARLYSUSPEND\r
+    #include <linux/pm.h>\r
+    #include <linux/earlysuspend.h>\r
+#endif\r
+\r
+\r
+//#define      CONFIG_TS_FUNCTION_CALLED_DEBUG                 //Display the debug information whitch function is called\r
+//#define CONFIG_TS_PROBE_DEBUG                //Display the debug information in byd693x_ts_probe function\r
+//#define CONFIG_TS_I2C_TRANSFER_DEBUG         //Display the debug information of IIC transfer\r
+//#define CONFIG_TPKEY_STATUS_DEBUG                    //Display the debug information of Touch Key status\r
+//#define CONFIG_TS_WORKQUEUE_DEBUG            //Display the debug ihnformation of creating work queue\r
+//#define CONFIG_TS_COORDIATE_DEBUG            //\r
+//#define CONFIG_TS_CUTEDGE_DEBUG                      //\r
+\r
+//----------------------------------------//\r
+#define        TOUCH_INT_NO    SW_INT_IRQNO_PIO    //GPIO :set the interrupt \r
+#define byd693x_I2C_NAME       "byd693x-ts"\r
+\r
+//----------------------------------------//\r
+struct ChipSetting {\r
+       char No;\r
+       char Reg;\r
+       char Data1;\r
+       char Data2;\r
+};\r
+\r
+#include "byd693x_ts.h"\r
+\r
+#define VERSION        "byd693x_20120731_16:52_V1.2_Charles@Raysens@Zed"\r
+#define CTP_NAME       "byd693x-ts"\r
+\r
+struct byd_platform_data *byd6932_pdata; \r
+\r
+\r
+#define FINGER_NO_MAX          10              //Define the max finger number, but the really finger number: fetch from .fex file\r
+#define BYD_COORD_READ_ADDR 0x5c\r
+\r
+static int SCREEN_MAX_X = 1024;\r
+static int SCREEN_MAX_Y = 600;\r
+static int Get_Finger_Num = 5;\r
+\r
+#define        RESO_X_NO               0\r
+#define        RESO_Y_NO               1\r
+\r
+struct ChipSetting byd693xcfg_Resolution[]={                                                   \r
+//{ 2,0x08,    200/256,        200%256},       //      1       FTHD_H;FTHD_L   //¨º???¡ã¡ä?¨¹?D?¦Ì\r
+//{ 2,0x0A,    120/256,        120%256},       //      2       NTHD_H;NTHD_L   //??¨¦¨´?D?¦Ì\r
+{ 2,0x0C,      800/256,        800%256},       //      3 RESX_H;RESX_L //X¡¤?¡À??¨º\r
+{ 2,0x0E,      480/256,        480%256},       //      4       RESY_H;RESY_L   //Y¡¤?¡À??¨º\r
+};\r
+\r
+static void deviceResume(struct i2c_client *client);\r
+static void deviceSuspend(struct i2c_client *client);\r
+void byd693xdeviceInit(struct i2c_client *client); \r
+\r
+//static int byd693x_ts_open(struct input_dev *dev);\r
+//static void byd693x_ts_close(struct input_dev *dev);\r
+static int byd693x_ts_suspend(struct i2c_client *client, pm_message_t mesg);\r
+static int byd693x_ts_resume(struct i2c_client *client);\r
+#ifdef CONFIG_HAS_EARLYSUSPEND\r
+static void byd693x_ts_early_suspend(struct early_suspend *h);\r
+static void byd693x_ts_late_resume(struct early_suspend *h);\r
+#endif /* CONFIG_HAS_EARLYSUSPEND */\r
+\r
+static irqreturn_t byd693x_ts_isr(int irq, void *dev_id);\r
+static struct workqueue_struct *byd693x_wq;\r
+\r
+\r
+struct byd_ts_priv {\r
+       struct i2c_client *client;\r
+       struct input_dev *input;\r
+       struct hrtimer timer;\r
+       struct work_struct  byd_work;\r
+#ifdef CONFIG_HAS_EARLYSUSPEND\r
+       struct early_suspend early_suspend;\r
+#endif \r
+\r
+       int irq;\r
+       int FingerNo;\r
+       int FingerDetect;\r
+       u8 btn_pre_TPKey;\r
+       int suspend_opend;\r
+};\r
+\r
+/***********************************************************\r
+Read Data from TP through IIC\r
+***********************************************************/\r
+static int ReadRegister(struct i2c_client *client,uint8_t reg,unsigned char *buf, int ByteLen)\r
+{\r
+//     unsigned char buf[4];\r
+       struct i2c_msg msg[2];\r
+       int ret;\r
+\r
+//     memset(buf, 0xFF, sizeof(buf));\r
+       msg[0].addr = client->addr;\r
+       msg[0].flags = 0;\r
+       msg[0].len = 1;\r
+       msg[0].buf = &reg;\r
+       msg[0].scl_rate=byd693x_I2C_RATE;\r
+\r
+       msg[1].addr = client->addr;\r
+       msg[1].flags = I2C_M_RD;\r
+       msg[1].len = ByteLen;\r
+       msg[1].buf = buf;\r
+       msg[1].scl_rate=byd693x_I2C_RATE;\r
+\r
+       ret = i2c_transfer(client->adapter, msg, 2);\r
+\r
+       #ifdef CONFIG_TS_I2C_TRANSFER_DEBUG\r
+       if(ret<0)       printk("                ReadRegister: i2c_transfer Error !\n");\r
+       else            printk("                ReadRegister: i2c_transfer OK !\n");\r
+       #endif\r
+       if(ret<0)               {       return 0;       }\r
+               else            {       return 1;       }\r
+}\r
+\r
+/***********************************************************\r
+Write Data to TP through IIC\r
+***********************************************************/\r
+static void WriteRegister(struct i2c_client *client,uint8_t Reg,unsigned char Data1,unsigned char Data2,int ByteNo)\r
+{      \r
+       struct i2c_msg msg;\r
+       unsigned char buf[4];\r
+       int ret;\r
+\r
+       buf[0]=Reg;\r
+       buf[1]=Data1;\r
+       buf[2]=Data2;\r
+       buf[3]=0;\r
+\r
+       msg.addr = client->addr;\r
+       msg.flags = 0;\r
+       msg.len = ByteNo+1;\r
+       msg.buf = (char *)buf;\r
+       msg.scl_rate=byd693x_I2C_RATE;\r
+       ret = i2c_transfer(client->adapter, &msg, 1);\r
+\r
+       #ifdef CONFIG_TS_I2C_TRANSFER_DEBUG\r
+       if(ret<0)       printk("                WriteRegister: i2c_master_send Error !\n");\r
+       else            printk("                WriteRegister: i2c_master_send OK !\n");\r
+       #endif\r
+}\r
+\r
+void byd693xdeviceInit(struct i2c_client *client)\r
+{      \r
+       deviceSuspend(client);\r
+       deviceResume(client);\r
+mdelay(30);\r
+}\r
+\r
+static void deviceResume(struct i2c_client *client)\r
+{      \r
+       int i;\r
+\r
+       for(i=0;i<sizeof(Resume)/sizeof(Resume[0]);i++)\r
+       {\r
+               WriteRegister(  client,Resume[i].Reg,\r
+                               Resume[i].Data1,Resume[i].Data2,\r
+                               Resume[i].No);\r
+       }\r
+       mdelay(20);\r
+       //Config the resolution of CTP\r
+       for(i=0;i<sizeof(byd693xcfg_Resolution)/sizeof(byd693xcfg_Resolution[0]);i++)\r
+       {\r
+               WriteRegister(  client,byd693xcfg_Resolution[i].Reg,\r
+                               byd693xcfg_Resolution[i].Data1,byd693xcfg_Resolution[i].Data2,\r
+                               byd693xcfg_Resolution[i].No);\r
+       }\r
+       mdelay(20);\r
+}\r
+\r
+static void deviceSuspend(struct i2c_client *client)\r
+{      \r
+       int i;\r
+       \r
+       for(i=0;i<sizeof(Suspend)/sizeof(Suspend[0]);i++)\r
+       {\r
+               WriteRegister(  client,Suspend[i].Reg,\r
+                               Suspend[i].Data1,Suspend[i].Data2,\r
+                               Suspend[i].No);\r
+       }\r
+       mdelay(50);\r
+}\r
+\r
+static void bf693x_ts_send_keyevent(struct byd_ts_priv *byd_priv,u8 btn_status)\r
+{\r
+       \r
+       switch(btn_status & 0xf0)\r
+       {\r
+               case 0x90:\r
+                       byd_priv->btn_pre_TPKey = TPKey_code[0];\r
+                       break;\r
+               case 0xa0:\r
+                       byd_priv->btn_pre_TPKey = TPKey_code[1];\r
+                       break;\r
+               case 0xb0:\r
+                       byd_priv->btn_pre_TPKey = TPKey_code[2];\r
+                       break;\r
+               case 0xc0:\r
+                       byd_priv->btn_pre_TPKey = TPKey_code[3];\r
+                       break;\r
+               case 0xf0:\r
+                       input_report_key(byd_priv->input, byd_priv->btn_pre_TPKey, REPORT_TPKEY_UP);\r
+                       input_sync(byd_priv->input);\r
+                       return;\r
+               default:\r
+                       return;\r
+       }\r
+       input_report_key(byd_priv->input, byd_priv->btn_pre_TPKey, REPORT_TPKEY_DOWN);\r
+       input_sync(byd_priv->input);\r
+}\r
+       \r
+static void byd693x_ts_work(struct work_struct *work)\r
+{\r
+       int i;\r
+       unsigned short xpos=0, ypos=0;\r
+       unsigned char Coord_Buf[4*FINGER_NO_MAX +1];            //Define the max finger data\r
+       u8 btn_status;\r
+       u8 Finger_ID,Finger_Status,Report_Status;\r
+\r
+       struct byd_ts_priv *byd_priv = container_of(work,struct byd_ts_priv,byd_work);\r
+\r
+       #ifdef CONFIG_TS_FUNCTION_CALLED_DEBUG\r
+       printk("+-----------------------------------------+\n");\r
+       printk("|       byd693x_ts_work!                  |\n");\r
+       printk("+-----------------------------------------+\n");\r
+       #endif\r
+       if (byd_priv->suspend_opend == 1)\r
+               return ;\r
+               \r
+       ReadRegister(byd_priv->client,BYD_COORD_READ_ADDR,Coord_Buf,(4 * Get_Finger_Num +1));           //read only the used finger number data\r
+               \r
+       btn_status = Coord_Buf[0];\r
+#ifdef CONFIG_TS_COORDIATE_DEBUG\r
+       printk("btn_status is: 0x%x\n",btn_status);\r
+#endif\r
+       \r
+       if ( 0x00 == (btn_status & 0x80))\r
+       {\r
+               return;\r
+       }\r
+\r
+       bf693x_ts_send_keyevent(byd_priv,btn_status);\r
+\r
+       byd_priv->FingerDetect = 0;\r
+       Report_Status = 0;\r
+       if ((btn_status & 0x0f))\r
+               {\r
+                       for(i=0;i< (btn_status & 0x0f);i++)\r
+                       {\r
+                               Finger_ID = (Coord_Buf[i*4 + 1]>>4)-1;\r
+                               Finger_Status = Coord_Buf[i*4 + 3] & 0xf0;\r
+                               xpos = Coord_Buf[i*4 + 1] & 0x0f;\r
+                               xpos = (xpos <<8) | Coord_Buf[i*4 + 2];\r
+                               \r
+                               ypos = Coord_Buf[i*4 + 3] & 0x0f;\r
+                               ypos = (ypos <<8) | Coord_Buf[i*4 + 4];\r
+                       \r
+                       if (byd6932_pdata ->swap_xy)\r
+                               swap(xpos, ypos);\r
+                       if (byd6932_pdata ->xpol)\r
+                               xpos = byd6932_pdata ->screen_max_x -xpos;\r
+                       if (byd6932_pdata ->ypol)\r
+                               ypos = byd6932_pdata ->screen_max_y -ypos;\r
+\r
+                               if((0xa0 == Finger_Status) || (0x90 == Finger_Status))          //0xa0:The first Touch;  0x90: Hold Finger Touch\r
+                               {\r
+                                       byd_priv->FingerDetect++;\r
+                                       Report_Status = 1;\r
+//                                     printk("Finger_ID = 0x%x, DOWN\n", Finger_ID);\r
+                                       input_mt_slot(byd_priv->input, Finger_ID);              //Slot B protocol\r
+                                       input_report_abs(byd_priv->input, ABS_MT_TRACKING_ID, Finger_ID);\r
+                                       input_report_abs(byd_priv->input, ABS_MT_TOUCH_MAJOR, REPORT_TOUCH_MAJOR); //Finger Size\r
+                                       input_report_abs(byd_priv->input, ABS_MT_POSITION_X, xpos);\r
+                                       input_report_abs(byd_priv->input, ABS_MT_POSITION_Y, ypos);\r
+                                       input_report_abs(byd_priv->input, ABS_MT_WIDTH_MAJOR, REPORT_WIDTH_MAJOR); //Touch Size\r
+               \r
+                                       #ifdef CONFIG_TS_COORDIATE_DEBUG\r
+                                               printk("  Finger Touch X = %d , Y = %d, State = 0x%x,Finger_ID=0x%x\n\n",xpos,ypos,Finger_Status,Finger_ID);\r
+                                       #endif\r
+                               }\r
+                               \r
+                               if (Finger_Status == 0xc0)\r
+                               {\r
+                                       Report_Status = 1;\r
+                                       input_mt_slot(byd_priv->input, Finger_ID);\r
+                                       input_report_abs(byd_priv->input, ABS_MT_TRACKING_ID, -1);\r
+                               #ifdef CONFIG_TS_COORDIATE_DEBUG\r
+                                       printk("        Touch release  X = %d , Y = %d, State = 0x%x,Finger_ID=0x%x\n\n",xpos,ypos,Finger_Status,Finger_ID);\r
+                               #endif\r
+                               }\r
+                       }\r
+               }\r
+       if (Report_Status)\r
+       {\r
+                       input_sync(byd_priv->input);\r
+       }\r
+}\r
+\r
+static int byd693x_init_platform_hw(void)\r
+{\r
+    if(gpio_request(byd6932_pdata->rst_pin,NULL) != 0){\r
+      gpio_free(byd6932_pdata->rst_pin);\r
+      printk("byd693x_init_platform_hw gpio_request error\n");\r
+      return -EIO;\r
+    }\r
+\r
+    if(gpio_request(byd6932_pdata->int_pin, NULL) != 0){\r
+      gpio_free(byd6932_pdata->int_pin);\r
+      printk("byd693x_init_platform_hw gpio_request error\n");\r
+      return -EIO;\r
+    }\r
+    gpio_pull_updown(byd6932_pdata->int_pin, 1);\r
+    gpio_direction_output(byd6932_pdata->rst_pin, 1);\r
+    return 0;\r
+}\r
+\r
+\r
+static int byd693x_ts_probe(struct i2c_client *client,const struct i2c_device_id *idp)\r
+{\r
+       struct byd_ts_priv *byd_priv;\r
+       struct input_dev *byd_input = NULL;\r
+       struct byd_platform_data *pdata = client->dev.platform_data;\r
+       unsigned char tp_buf[1];\r
+       int error = -1;\r
+\r
+\r
+       byd6932_pdata = client->dev.platform_data;\r
+       #ifdef CONFIG_TS_FUNCTION_CALLED_DEBUG\r
+               printk("+-----------------------------------------+\n");\r
+               printk("|       byd693x_ts_probe!                 |\n");\r
+               printk("+-----------------------------------------+\n");\r
+       #endif\r
+       \r
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))\r
+       {\r
+               #ifdef CONFIG_TS_PROBE_DEBUG\r
+                       printk("                byd693x_ts_probe: need I2C_FUNC_I2C\n");\r
+               #endif\r
+               return -ENODEV;\r
+       }\r
+       else\r
+       {\r
+               #ifdef CONFIG_TS_PROBE_DEBUG\r
+                       printk("                byd693x_ts_probe: i2c Check OK!\n");\r
+                       printk("                byd693x_ts_probe: i2c_client name : %s\n",client->name);\r
+               #endif\r
+       }\r
+\r
+       byd_priv = kzalloc(sizeof(*byd_priv), GFP_KERNEL);\r
+       if (!byd_priv)\r
+       {\r
+               #ifdef CONFIG_TS_PROBE_DEBUG\r
+                       printk("                byd693x_ts_probe: kzalloc Error!\n");\r
+               #endif\r
+               error=-ENODEV;\r
+               goto    err0;\r
+       }\r
+       else\r
+       {\r
+               #ifdef CONFIG_TS_PROBE_DEBUG\r
+                       printk("                byd693x_ts_probe: kzalloc OK!\n");\r
+               #endif\r
+       }\r
+\r
+\r
+\r
+       dev_set_drvdata(&client->dev, byd_priv);\r
+       byd_input = input_allocate_device();\r
+       if (!byd_input)\r
+       {\r
+               #ifdef CONFIG_TS_PROBE_DEBUG\r
+                       printk("                byd693x_ts_probe: input_allocate_device Error\n");\r
+               #endif\r
+               error=-ENODEV;\r
+               goto    err1;\r
+       }\r
+       else\r
+       {\r
+               #ifdef CONFIG_TS_PROBE_DEBUG\r
+                       printk("                byd693x_ts_probe: input_allocate_device OK\n");\r
+               #endif\r
+       }\r
+\r
+       //only check BYD BF6932\r
+       error = ReadRegister(client, BYD_COORD_READ_ADDR, tp_buf, sizeof(tp_buf));\r
+       if (error <= 0)\r
+       {\r
+               printk(KERN_ALERT "BYD BF6932 Touchscreen not found \n");\r
+               gpio_free(pdata->rst_pin);\r
+               gpio_free(pdata->int_pin);\r
+               goto err1;\r
+       }\r
+\r
+//     byd_input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) | BIT_MASK(EV_SYN)|BIT_MASK(EV_REP) ;\r
+//     byd_input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) | BIT_MASK(BTN_2);\r
+       byd_input->name = client->name;\r
+       byd_input->id.bustype = BUS_I2C;\r
+       byd_input->id.vendor  = 0x2878; // Modify for Vendor ID\r
+       byd_input->dev.parent = &client->dev;\r
+//     byd_input->open = byd693x_ts_open;\r
+//     byd_input->close = byd693x_ts_close;\r
+       input_set_drvdata(byd_input, byd_priv);\r
+       byd_priv->client = client;\r
+       byd_priv->input = byd_input;\r
+       byd_priv->irq = pdata->int_pin;\r
+       byd_priv->FingerNo=FINGER_NO_MAX;\r
+\r
+       byd_input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);\r
+       byd_input->absbit[0] = BIT(ABS_X) | BIT(ABS_Y); // for android\r
+\r
+       __set_bit(EV_ABS, byd_input->evbit);\r
+       __set_bit(INPUT_PROP_DIRECT, byd_input->propbit);\r
+       set_bit(ABS_MT_POSITION_X, byd_input->absbit);\r
+       set_bit(ABS_MT_POSITION_Y, byd_input->absbit);\r
+       set_bit(ABS_MT_TOUCH_MAJOR, byd_input->absbit);\r
+       set_bit(ABS_MT_WIDTH_MAJOR, byd_input->absbit);\r
+\r
+//     deviceReset(client);\r
+//     printk("BYD Touchscreen I2C Address: 0x%02X\n",client->addr);\r
+//     printk("BYD Touchscreen Device ID  : BF6932\n");\r
+       \r
+       //config the resolution of CTP\r
+       byd693xcfg_Resolution[RESO_X_NO].Data1 = (char)(pdata->screen_max_x >>8);\r
+       byd693xcfg_Resolution[RESO_X_NO].Data2 = (char)(pdata->screen_max_x & 0xff);\r
+       \r
+       byd693xcfg_Resolution[RESO_Y_NO].Data1 = (char)(pdata->screen_max_y >>8);\r
+       byd693xcfg_Resolution[RESO_Y_NO].Data2 = (char)(pdata->screen_max_y & 0xff);\r
+       \r
+       byd693xdeviceInit(client);\r
\r
+       input_mt_init_slots(byd_input, MAX_TRACKID_ITEM);\r
+\r
+\r
+       input_set_abs_params(byd_input, ABS_MT_TOUCH_MAJOR, 0, MAX_TOUCH_MAJOR, 0, 0);\r
+       input_set_abs_params(byd_input, ABS_MT_WIDTH_MAJOR, 0, MAX_WIDTH_MAJOR, 0, 0);\r
+       input_set_abs_params(byd_input, ABS_MT_POSITION_X,  0,pdata->screen_max_x + 1, 0, 0);\r
+       input_set_abs_params(byd_input, ABS_MT_POSITION_Y,  0,pdata->screen_max_y + 1, 0, 0);\r
+\r
+#ifdef USE_TOUCH_KEY\r
+       set_bit(KEY_MENU, byd_input->keybit);\r
+       set_bit(KEY_HOME, byd_input->keybit);\r
+       set_bit(KEY_BACK, byd_input->keybit);\r
+       set_bit(KEY_SEARCH, byd_input->keybit);\r
+#endif\r
+       \r
+       INIT_WORK(&byd_priv->byd_work, byd693x_ts_work);\r
+\r
+       error = input_register_device(byd_input);\r
+\r
+       if(error)\r
+       {\r
+               #ifdef CONFIG_TS_PROBE_DEBUG\r
+                       printk("                byd693x_ts_probe: input_register_device input Error!\n");\r
+               #endif\r
+               error=-ENODEV;\r
+               goto    err1;\r
+       }\r
+       else\r
+       {\r
+               #ifdef CONFIG_TS_PROBE_DEBUG\r
+                       printk("                byd693x_ts_probe: input_register_device input OK!\n");\r
+               #endif\r
+       }\r
+\r
+               error = byd693x_init_platform_hw();             //Init RK29 GPIO\r
+               if(0 != error)\r
+               {\r
+                       printk("%s:Init_INT set_irq_mode err. \n", __func__);\r
+                       goto exit_set_irq_mode;\r
+               }\r
+               // Options for different interrupt system \r
+//             error = request_irq(byd_priv->irq, byd693x_ts_isr, IRQF_DISABLED|IRQF_TRIGGER_FALLING, client->name,byd_priv);\r
+//             error = request_irq(byd_priv->irq, byd693x_ts_isr, IRQF_TRIGGER_FALLING, client->name,byd_priv);\r
+               error = request_irq(byd_priv->irq, byd693x_ts_isr, IRQF_TRIGGER_FALLING, client->name,byd_priv);\r
+               if(error)\r
+               {\r
+               #ifdef CONFIG_TS_PROBE_DEBUG\r
+                       printk("                byd693x_ts_probe: request_irq Error!\n");\r
+               #endif\r
+                       error=-ENODEV;\r
+                       goto err2;\r
+               }\r
+               else\r
+               {\r
+               #ifdef CONFIG_TS_PROBE_DEBUG\r
+                       printk("                byd693x_ts_probe: request_irq OK!\n");\r
+                       #endif\r
+               }       \r
+\r
+       printk("Install BYD BF6932 Touchscreen driver successfully\n");\r
+       \r
+#ifdef CONFIG_HAS_EARLYSUSPEND\r
+       byd_priv->early_suspend.suspend = byd693x_ts_early_suspend;\r
+       byd_priv->early_suspend.resume  = byd693x_ts_late_resume;\r
+       byd_priv->early_suspend.level   = EARLY_SUSPEND_LEVEL_DISABLE_FB+1;\r
+       register_early_suspend(&byd_priv->early_suspend);\r
+#endif \r
+       return 0;\r
+exit_set_irq_mode:\r
+err2:  input_unregister_device(byd_input);     \r
+err1:  input_free_device(byd_input);\r
+       kfree(byd_priv);\r
+//exit_gpio_wakeup_request_failed:     \r
+err0:  dev_set_drvdata(&client->dev, NULL);\r
+       return error;\r
+}\r
+\r
+/*\r
+static int byd693x_ts_open(struct input_dev *dev)\r
+{\r
+       struct byd_ts_priv *byd_priv = input_get_drvdata(dev);\r
+       #ifdef CONFIG_TS_FUNCTION_CALLED_DEBUG\r
+       printk("+-----------------------------------------+\n");\r
+       printk("|       byd693x_ts_open!                  |\n");\r
+       printk("+-----------------------------------------+\n");\r
+       #endif  \r
+       deviceResume(byd_priv->client);\r
+       enable_irq(byd_priv->irq);\r
+       byd_priv->suspend_opend = 0;\r
+       return 0;\r
+}\r
+\r
+static void byd693x_ts_close(struct input_dev *dev)\r
+{\r
+       struct byd_ts_priv *byd_priv = input_get_drvdata(dev);\r
+       #ifdef CONFIG_TS_FUNCTION_CALLED_DEBUG\r
+       printk("+-----------------------------------------+\n");\r
+       printk("|       byd693x_ts_close!                 |\n");\r
+       printk("+-----------------------------------------+\n");\r
+       #endif\r
+       deviceSuspend(byd_priv->client);        \r
+       byd_priv->suspend_opend = 1;\r
+       disable_irq(byd_priv->irq);\r
+}\r
+*/\r
+\r
+static int byd693x_ts_resume(struct i2c_client *client)\r
+{\r
+       struct byd_ts_priv *byd_priv = dev_get_drvdata(&client->dev);\r
+       #ifdef CONFIG_TS_FUNCTION_CALLED_DEBUG\r
+       printk("+-----------------------------------------+\n");\r
+       printk("|       byd693x_ts_resume!                |\n");\r
+       printk("+-----------------------------------------+\n");\r
+       #endif\r
+\r
+       deviceResume(client);\r
+       byd_priv->suspend_opend = 0;            \r
+       enable_irq(byd_priv->irq);\r
+       return 0;\r
+}\r
+\r
+static int byd693x_ts_suspend(struct i2c_client *client, pm_message_t mesg)\r
+{\r
+       struct byd_ts_priv *byd_priv = dev_get_drvdata(&client->dev);\r
+       #ifdef CONFIG_TS_FUNCTION_CALLED_DEBUG\r
+       printk("+-----------------------------------------+\n");\r
+       printk("|       byd693x_ts_suspend!               |\n");\r
+       printk("+-----------------------------------------+\n");\r
+       #endif\r
+       byd_priv->suspend_opend = 1;\r
+\r
+       disable_irq(byd_priv->irq);\r
+       deviceSuspend(client);\r
+       return 0;\r
+}\r
+\r
+#ifdef CONFIG_HAS_EARLYSUSPEND\r
+static void byd693x_ts_late_resume(struct early_suspend *h)\r
+{\r
+       struct byd_ts_priv *byd_priv = container_of(h, struct byd_ts_priv, early_suspend);\r
+       #ifdef CONFIG_TS_FUNCTION_CALLED_DEBUG\r
+       printk("+-----------------------------------------+\n");\r
+       printk("|       byd693x_ts_late_resume!           |\n");\r
+       printk("+-----------------------------------------+\n");\r
+       #endif\r
+       byd693x_ts_resume(byd_priv->client);\r
+}\r
+static void byd693x_ts_early_suspend(struct early_suspend *h)\r
+{\r
+       struct byd_ts_priv *byd_priv = container_of(h, struct byd_ts_priv, early_suspend);\r
+       #ifdef CONFIG_TS_FUNCTION_CALLED_DEBUG\r
+       printk("+-----------------------------------------+\n");\r
+       printk("|       byd693x_ts_early_suspend!         |\n");\r
+       printk("+-----------------------------------------+\n");\r
+       #endif\r
+       byd693x_ts_suspend(byd_priv->client, PMSG_SUSPEND);\r
+}\r
+#endif\r
+\r
+static int byd693x_ts_remove(struct i2c_client *client)\r
+{\r
+       struct byd_ts_priv *byd_priv = dev_get_drvdata(&client->dev);\r
+       #ifdef CONFIG_TS_FUNCTION_CALLED_DEBUG\r
+       printk("+-----------------------------------------+\n");\r
+       printk("|       byd693x_ts_remove !               |\n");\r
+       printk("+-----------------------------------------+\n");\r
+       #endif\r
+       free_irq(byd_priv->irq, byd_priv);\r
+       input_unregister_device(byd_priv->input);\r
+       input_free_device(byd_priv->input);\r
+       kfree(byd_priv);\r
+       dev_set_drvdata(&client->dev, NULL);\r
+       return 0;\r
+}\r
+\r
+static irqreturn_t byd693x_ts_isr(int irq, void *dev_id)\r
+{\r
+       struct byd_ts_priv *byd_priv = dev_id;\r
+       #ifdef CONFIG_TS_FUNCTION_CALLED_DEBUG\r
+       printk("+-----------------------------------------+\n");\r
+       printk("|       byd693x_ts_isr!                   |\n");\r
+       printk("+-----------------------------------------+\n");\r
+       #endif  \r
+       disable_irq_nosync(byd_priv->irq);\r
+       queue_work(byd693x_wq, &byd_priv->byd_work);\r
+       enable_irq(byd_priv->irq);\r
+       return IRQ_HANDLED;\r
+}\r
+\r
+static const struct i2c_device_id byd693x_ts_id[] = {\r
+       { CTP_NAME, 0 },\r
+       { }\r
+};\r
+\r
+MODULE_DEVICE_TABLE(i2c, byd693x_ts_id);\r
+\r
+static struct i2c_driver byd693x_ts_driver = {\r
+       .driver = {\r
+               .name = CTP_NAME,\r
+       },\r
+       .probe = byd693x_ts_probe,\r
+       .remove = byd693x_ts_remove,\r
+#ifndef        CONFIG_HAS_EARLYSUSPEND\r
+       .suspend = byd693x_ts_suspend,\r
+       .resume = byd693x_ts_resume,\r
+#endif\r
+       .id_table = byd693x_ts_id,\r
+};\r
+\r
+static char banner[] __initdata = KERN_INFO "BYD Touchscreen driver, (c) 2012 BYD Systech Ltd.\n";\r
+static int __init byd693x_ts_init(void)\r
+{\r
+       int ret;\r
+       #ifdef CONFIG_TS_FUNCTION_CALLED_DEBUG\r
+       printk("+-----------------------------------------+\n");\r
+       printk("|       byd_ts_init!                      |\n");\r
+       printk("+-----------------------------------------+\n");\r
+       #endif\r
+       printk(banner);\r
+       printk("==================byd693x_ts_init===========================\n");\r
+       printk("Version =%s\n",VERSION);\r
+       byd693x_wq = create_singlethread_workqueue("byd693x_wq");\r
+       if (!byd693x_wq)\r
+       {\r
+               #ifdef CONFIG_TS_WORKQUEUE_DEBUG\r
+               printk("                byd693x_ts_init: create_singlethread_workqueue Error!\n");\r
+               #endif\r
+               return -ENOMEM;\r
+       }\r
+       else\r
+       {\r
+               #ifdef CONFIG_TS_WORKQUEUE_DEBUG\r
+               printk("                byd693x_ts_init: create_singlethread_workqueue OK!\n");\r
+               #endif\r
+       }\r
+       ret=i2c_add_driver(&byd693x_ts_driver);\r
+       #ifdef CONFIG_TS_I2C_TRANSFER_DEBUG\r
+       if(ret) printk("                byd693x_ts_init: i2c_add_driver Error! \n");\r
+       else    printk("                byd693x_ts_init: i2c_add_driver OK! \n");\r
+       #endif\r
+       return ret;\r
+}\r
+\r
+static void __exit byd693x_ts_exit(void)\r
+{\r
+       #ifdef CONFIG_TS_FUNCTION_CALLED_DEBUG\r
+       printk("+-----------------------------------------+\n");\r
+       printk("|       byd693x_ts_exit!                  |\n");\r
+       printk("+-----------------------------------------+\n");\r
+       #endif\r
+       i2c_del_driver(&byd693x_ts_driver);\r
+       if (byd693x_wq) destroy_workqueue(byd693x_wq);\r
+}\r
+\r
+module_init(byd693x_ts_init);\r
+module_exit(byd693x_ts_exit);\r
+\r
+MODULE_AUTHOR("BYD Systech Ltd - Raysens Design Technology, Charles Chen.");\r
+MODULE_LICENSE("GPL v2");\r
+MODULE_DESCRIPTION("byd693x Touchscreen Driver 1.2_Charles@Raysens@20120731");\r
diff --git a/drivers/input/touchscreen/byd693x_ts.h b/drivers/input/touchscreen/byd693x_ts.h
new file mode 100644 (file)
index 0000000..d176772
--- /dev/null
@@ -0,0 +1,43 @@
+//a4, 52
+#define MAX_TOUCH_MAJOR                10              //Charles added
+#define MAX_WIDTH_MAJOR                15              //Charles added
+#define MAX_TRACKID_ITEM               10      //Charles added
+
+#define REPORT_TOUCH_MAJOR             5               //Charles added
+#define REPORT_WIDTH_MAJOR             8               //Charles added
+
+#define REPORT_TPKEY_DOWN              1
+#define REPORT_TPKEY_UP                        0
+
+//#define RK29xx_ANDROID2_3_REPORT             //if the Android system is V2.3
+//#undef RK29xx_ANDROID2_3_REPORT
+#define RK29xx_ANDROID4_0_REPORT               //if the Android system is V4.0
+//#undef RK29xx_ANDROID4_0_REPORT
+
+//----------------------------------------//
+//#define TOUCH_INT_PIN                                RK29_PINx_PAx           //define INT Pin        Should be changed to the INT GPIO Port and Pin
+//#define TOUCH_RESET_PIN                      RK29_PINx_PAx                   //define Reset Pin  Should be changed to the Reset GPIO Port and Pin
+//#define SW_INT_IRQNO_PIO    TOUCH_INT_PIN
+
+#define        byd693x_I2C_RATE        100*1000   //400KHz
+
+#define USE_TOUCH_KEY
+
+#ifdef USE_TOUCH_KEY
+static const uint32_t TPKey_code[4] ={ KEY_SEARCH,KEY_MENU,KEY_HOME,KEY_BACK };
+#endif
+
+//struct ChipSetting byd693xcfg_Table1[]={                                                     
+//{ 2,0x08,    200/256,        200%256},       //      1       FTHD_H;FTHD_L   //ÊÖÖ¸°´¼üãÐÖµ
+//{ 2,0x0A,    120/256,        120%256},       //      2       NTHD_H;NTHD_L   //ÔëÉùãÐÖµ
+//{ 2,0x0C,    SCREEN_MAX_X/256,       SCREEN_MAX_X%256},      //      3 RESX_H;RESX_L //X·Ö±æÂÊ
+//{ 2,0x0E,    SCREEN_MAX_Y/256,       SCREEN_MAX_Y%256},      //      4       RESY_H;RESY_L   //Y·Ö±æÂÊ
+//};
+
+static struct ChipSetting Resume[]={
+{ 1, 0x07, 0x01, 0x00},        // Wakeup TP from Sleep mode
+};
+
+static struct ChipSetting Suspend[] ={
+{ 1, 0x07, 0x00, 0x00}, // Enter Sleep mode
+};