--- /dev/null
+/*
+ * 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);
+
+
--- /dev/null
+\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 = ®\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