summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Bouyack <mbouyack@google.com>2020-10-21 13:06:03 -0700
committerAndrew Evans <andrewevans@google.com>2022-03-09 15:04:44 -0800
commitd295937aa126eb1454151605e4bb969ab880a47b (patch)
tree62ebee2194384002d418c965e3029abcc1ecccf5
parent6666507205b70f528a0b843d57e0bccd9a3f9a6b (diff)
downloadrotary-encoders-d295937aa126eb1454151605e4bb969ab880a47b.tar.gz
Pixart 9126 driver integration
Bug: 168725669 Change-Id: I934c3793cb2f494a21ed88e3f85dd22d96aa79e2
-rw-r--r--ots_pat9126/Kbuild3
-rw-r--r--ots_pat9126/Kconfig14
-rw-r--r--ots_pat9126/Makefile13
-rw-r--r--ots_pat9126/pat9126.c705
-rw-r--r--ots_pat9126/pat9126.h115
5 files changed, 850 insertions, 0 deletions
diff --git a/ots_pat9126/Kbuild b/ots_pat9126/Kbuild
new file mode 100644
index 0000000..e826c43
--- /dev/null
+++ b/ots_pat9126/Kbuild
@@ -0,0 +1,3 @@
+obj-$(CONFIG_INPUT_PIXART_OTS_PAT9126_SWITCH) += ots-pat9126.o
+
+ots-pat9126-y += pat9126.o \ No newline at end of file
diff --git a/ots_pat9126/Kconfig b/ots_pat9126/Kconfig
new file mode 100644
index 0000000..607dd54
--- /dev/null
+++ b/ots_pat9126/Kconfig
@@ -0,0 +1,14 @@
+#
+# PixArt OTS switch driver configuration
+#
+
+config INPUT_PIXART_OTS_PAT9126_SWITCH
+ tristate "PixArt PAT9126 Rotating Switch driver"
+ depends on INPUT && I2C && GPIOLIB
+ help
+ Say Y to enable support for the PixArt OTS pat9126
+ rotating switch driver.
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ots_pat9126.
diff --git a/ots_pat9126/Makefile b/ots_pat9126/Makefile
new file mode 100644
index 0000000..cf34037
--- /dev/null
+++ b/ots_pat9126/Makefile
@@ -0,0 +1,13 @@
+default: all
+
+KBUILD_OPTIONS := CONFIG_INPUT_PIXART_OTS_PAT9126_SWITCH=m
+
+all:
+ $(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS)
+
+modules_install:
+ $(MAKE) INSTALL_MOD_STRIP=1 M=$(M) -C $(KERNEL_SRC) modules_install
+
+clean::
+ rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers
+ rm -rf .tmp_versions
diff --git a/ots_pat9126/pat9126.c b/ots_pat9126/pat9126.c
new file mode 100644
index 0000000..752ddba
--- /dev/null
+++ b/ots_pat9126/pat9126.c
@@ -0,0 +1,705 @@
+/* drivers/input/misc/ots_pat9126/pat9126_linux_driver.c
+ *
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#include <linux/input.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include "pat9126.h"
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include<linux/time.h>
+#include <linux/rtc.h>
+#include <linux/string.h>
+
+struct pixart_pat9126_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+ int irq;
+ u32 irq_flags;
+ struct device *pat9126_device;
+ u32 press_keycode;
+ bool press_en;
+ bool inverse_x;
+ bool inverse_y;
+ struct work_struct work;
+ struct workqueue_struct *workqueue;
+ struct delayed_work polling_work;
+};
+
+/*IRQ Flags*/
+bool is_initialized = false;
+
+/*Persist Flags*/
+static int already_calibrated = 0;
+extern char *saved_command_line;
+
+struct mutex irq_mutex;
+int en_irq_cnt = 0; /*Calculate times of enable irq*/
+int dis_irq_cnt = 0;/*Calculate times of disable irq*/
+
+struct rtc_time pat9126_tm;
+
+struct rw_reg_info {
+ char flag; /*R/W char*/
+ long w_addr;
+ long r_addr;
+ long r_data;
+};
+
+struct rw_reg_info pat9126_reg_info;
+
+/* Declaration of suspend and resume functions */
+static int pat9126_suspend(struct device *dev);
+static int pat9126_resume(struct device *dev);
+
+static int pat9126_write(struct i2c_client *client, u8 addr, u8 data)
+{
+ u8 buf[BUF_SIZE];
+ struct device *dev = &client->dev;
+
+ buf[0] = addr;
+ buf[1] = data;
+
+ /* Returns negative errno, or else the number of bytes written. */
+ if (i2c_master_send(client, buf, BUF_SIZE) < 0) {
+ dev_err(dev, "%s Failed: writing to reg 0x%x\n", __func__, addr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int pat9126_read(struct i2c_client *client, u8 addr, u8 *data)
+{
+ struct device *dev = &client->dev;
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &addr,
+ }, {
+ .addr = client->addr,
+ .flags = 1,
+ .len = 1,
+ .buf = data,
+ }
+ };
+
+ if (i2c_transfer(client->adapter, msg, 2) != 2) {
+ dev_err(dev, "%s Failed: writing to reg 0x%x\n", __func__,
+ addr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int pat9126_write_verified(struct i2c_client *client, u8 address, u8 data)
+{
+ int i, ret;
+ u8 read_value;
+
+ for (i = 0; i < PAT9150_I2C_RETRY; i++) {
+ ret = pat9126_write(client, address, data);
+ if (ret < 0)
+ return ret;
+ ret = pat9126_read(client, address, &read_value);
+ if (ret < 0)
+ return ret;
+
+ if (read_value == data)
+ return 0;
+
+ msleep(1);
+ }
+
+ return -EIO;
+}
+
+void delay(int ms)
+{
+ msleep(ms);
+}
+
+bool pat9126_sensor_init(struct i2c_client *client)
+{
+ u8 id;
+ int ret = 0;
+
+ /*
+ * Read sensor_pid in address 0x00 to check if the
+ * serial link is valid, read value should be 0x31.
+ */
+ pat9126_read(client, PIXART_PAT9126_PRODUCT_ID1_REG, &id);
+ if (id != PIXART_PAT9126_SENSOR_ID) {
+ return false;
+ }
+
+ /*
+ * PAT9126 sensor recommended settings:
+ * switch to bank0, not allowed to perform pat9126_write_verified
+ */
+ pat9126_write(client, PIXART_PAT9126_SELECT_BANK_REG,
+ PIXART_PAT9126_BANK0);
+ /*
+ * software reset (i.e. set bit7 to 1).
+ * It will reset to 0 automatically
+ * so perform OTS_RegWriteRead is not allowed.
+ */
+ pat9126_write(client, PIXART_PAT9126_CONFIG_REG,
+ PIXART_PAT9126_RESET);
+
+ /* delay 1ms */
+ usleep_range(RESET_DELAY_US, RESET_DELAY_US + 1);
+
+ /* disable write protect */
+ if (pat9126_write_verified(client, PIXART_PAT9126_WRITE_PROTECT_REG,
+ PIXART_PAT9126_DISABLE_WRITE_PROTECT) < 0)
+ return false;
+ /* set X-axis resolution (depends on application) */
+ if (pat9126_write_verified(client, PIXART_PAT9126_SET_CPI_RES_X_REG,
+ PIXART_PAT9126_CPI_RESOLUTION_X) < 0)
+ return false;
+ /* set Y-axis resolution (depends on application) */
+ if (pat9126_write_verified(client, PIXART_PAT9126_SET_CPI_RES_Y_REG,
+ PIXART_PAT9126_CPI_RESOLUTION_Y) < 0)
+ return false;
+ /* set 12-bit X/Y data format (depends on application) */
+ if (pat9126_write_verified(client, PIXART_PAT9126_ORIENTATION_REG,
+ PIXART_PAT9126_MOTION_DATA_LENGTH) < 0)
+ return false;
+ /* ONLY for VDD=VDDA=1.7~1.9V: for power saving */
+ if (pat9126_write_verified(client, PIXART_PAT9126_VOLTAGE_SEGMENT_SEL_REG,
+ PIXART_PAT9126_LOW_VOLTAGE_SEGMENT) < 0)
+ return false;
+
+ pat9126_write_verified(client, PIXART_PAT9126_SENSOR_MODE_SELECT_REG,
+ PIXART_PAT9126_SENSOR_SET_MODE2);
+
+ pat9126_write_verified(client, PIXART_PAT9126_AE_ENABLE, PIXART_PAT9126_AE_ENABLE_VAL);
+ pat9126_write_verified(client, PIXART_PAT9126_NY_MIN, PIXART_PAT9126_NY_MIN_VAL);
+
+ ret = pat9126_enable_mot(client);
+ if (ret < 0) {
+ pr_err("[PAT9126]: Enable Motion FAIL.");
+ }
+
+ /* enable write protect */
+ pat9126_write_verified(client, PIXART_PAT9126_WRITE_PROTECT_REG,
+ PIXART_PAT9126_ENABLE_WRITE_PROTECT);
+ return ret;
+}
+
+int pat9126_disable_mot(struct i2c_client *client, int16_t detect_freq)
+{
+ uint8_t tmp_1 = 0;
+ uint8_t sensor_pid = 0;
+
+ pat9126_read(client, PIXART_PAT9126_PRODUCT_ID1_REG, &sensor_pid);
+ if (sensor_pid != PIXART_PAT9126_SENSOR_ID) {
+ return (-1);
+ }
+
+ pat9126_write_verified(client, PIXART_PAT9126_SENSOR_MODE_SELECT_REG,
+ PIXART_PAT9126_SENSOR_DEFAULT_MODE2); // Set motion to open drain
+ pat9126_read(client, PIXART_PAT9126_SENSOR_MODE_SELECT_REG, &tmp_1);
+ pr_debug("[PAT9126]: Open drain mode motion: 0x%2x. \n", tmp_1);
+
+ /*Switch to bank1*/
+ pat9126_write(client, PIXART_PAT9126_SELECT_BANK_REG,
+ PIXART_PAT9126_SELECT_BANK_VAL2);
+
+ pat9126_write_verified(client, PIXART_PAT9126_BANK_FTWK,
+ PIXART_PAT9126_BANK_FTWK_VAL1);
+
+ pat9126_write_verified(client, PIXART_PAT9126_BANK_FTWK_D2,
+ PIXART_PAT9126_BANK_FTWK_D2_VAL1);
+
+ pat9126_write_verified(client, PIXART_PAT9126_BANK_CTB,
+ PIXART_PAT9126_BANK_CTB_VAL1);
+
+ pat9126_write_verified(client, PIXART_PAT9126_BANK_HI_SAD_K,
+ PIXART_PAT9126_BANK_HI_SAD_K_VAL1);
+
+ pat9126_write(client, PIXART_PAT9126_SELECT_BANK_REG,
+ PIXART_PAT9126_SELECT_BANK_VAL1);
+
+ delay(1); /* delay 1ms */
+
+ pat9126_write_verified(client, PIXART_PAT9126_SLEEP2_MODE_FREQ_REG,
+ detect_freq);
+
+ pat9126_write(client, PIXART_PAT9126_SLEEP_MODE_SELECT_REG,
+ PIXART_PAT9126_FORCE_ENTER_SLEEP2_MODE);
+
+ return 0;
+}
+
+int pat9126_enable_mot(struct i2c_client *client)
+{
+ uint8_t tmp_1 = 0;
+ uint8_t sensor_pid = 0;
+
+ pat9126_read(client, PIXART_PAT9126_PRODUCT_ID1_REG, &sensor_pid);
+ if (sensor_pid != PIXART_PAT9126_SENSOR_ID) {
+ return (-1);
+ }
+
+ pat9126_write_verified(client, PIXART_PAT9126_SENSOR_MODE_SELECT_REG,
+ PIXART_PAT9126_SENSOR_SET_MODE2); // Set motion to drive mode
+ pat9126_read(client, PIXART_PAT9126_SENSOR_MODE_SELECT_REG, &tmp_1);
+ pr_debug("[PAT9126]: Drive mode motion: 0x%2x. \n", tmp_1);
+
+ delay(1); /* delay 1ms */
+
+ /*Read Register for Pulling Up Motion IRQ*/
+ pat9126_read(client, PIXART_PAT9126_MOTION_STATUS_REG, &tmp_1);
+ pat9126_read(client, PIXART_PAT9126_DELTA_X_LO_REG, &tmp_1);
+ pat9126_read(client, PIXART_PAT9126_DELTA_Y_LO_REG, &tmp_1);
+ pat9126_read(client, PIXART_PAT9126_DELTA_XY_HI_REG, &tmp_1);
+
+ /*Write Register for Active Mode*/
+ /*Switch to bank1*/
+ pat9126_write(client, PIXART_PAT9126_SELECT_BANK_REG,
+ PIXART_PAT9126_SELECT_BANK_VAL2);
+
+ pat9126_write_verified(client, PIXART_PAT9126_BANK_FTWK,
+ PIXART_PAT9126_BANK_FTWK_DEFAULT_VAL);
+
+ pat9126_write_verified(client, PIXART_PAT9126_BANK_FTWK_D2,
+ PIXART_PAT9126_BANK_FTWK_D2_DEFAULT_VAL);
+
+ pat9126_write_verified(client, PIXART_PAT9126_BANK_CTB,
+ PIXART_PAT9126_BANK_CTB_DEFAULT_VAL);
+
+ pat9126_write_verified(client, PIXART_PAT9126_BANK_HI_SAD_K,
+ PIXART_PAT9126_BANK_HI_SAD_K_DEFAULT_VAL);
+
+ pat9126_write(client, PIXART_PAT9126_SELECT_BANK_REG,
+ PIXART_PAT9126_SELECT_BANK_VAL1);
+
+ delay(1); /* delay 1ms */
+
+ pat9126_write_verified(client, PIXART_PAT9126_SLEEP_MODE_SELECT_REG,
+ PIXART_PAT9126_WAKEUP_MODE);
+ pat9126_read(client, PIXART_PAT9126_SLEEP_MODE_SELECT_REG, &tmp_1);
+ pr_debug("[PAT9126]: Enable sleep1 and disable sleep2 mode: 0x%2x. \n", tmp_1);
+ return 0;
+}
+
+/* Read motion */
+void pat9126_read_motion(struct i2c_client *client, int16_t *dx16, int16_t *dy16)
+{
+ int8_t deltaX_l = 0, deltaY_l = 0, deltaXY_h = 0;
+ int16_t deltaX_h = 0, deltaY_h = 0;
+ uint8_t motion = 0;
+
+ pat9126_read(client, PIXART_PAT9126_MOTION_STATUS_REG, &motion);
+ pr_debug("[pat9126]: Motion BIT: 0x%2x\n", motion);
+
+ if (motion & PIXART_PAT9126_VALID_MOTION_DATA) {
+ pat9126_read(client, PIXART_PAT9126_DELTA_X_LO_REG, &deltaX_l);
+ pat9126_read(client, PIXART_PAT9126_DELTA_Y_LO_REG, &deltaY_l);
+ pat9126_read(client, PIXART_PAT9126_DELTA_XY_HI_REG, &deltaXY_h);
+
+ deltaX_h = (deltaXY_h & 0xF0);
+ deltaX_h <<= 4;
+ /* 12-bit data convert to 16-bit */
+ if (deltaX_h & 0x800)
+ deltaX_h |= 0xf000;
+
+ deltaY_h = (deltaXY_h & 0xF);
+ deltaY_h <<= 8;
+ /* 12-bit data convert to 16-bit */
+ if (deltaY_h & 0x800)
+ deltaY_h |= 0xf000;
+ }
+
+ *dx16 = deltaX_h | deltaX_l;
+ *dy16 = deltaY_h | deltaY_l;
+}
+
+static void pat9126_work_handler(struct work_struct *work)
+{
+ int16_t delta_x = 0, delta_y = 0;
+ struct delayed_work *dw = to_delayed_work(work);
+ struct pixart_pat9126_data *data = \
+ container_of(dw, struct pixart_pat9126_data, polling_work);
+ struct input_dev *ipdev = data->input;
+ struct device *dev = &data->client->dev;
+
+ /* check if MOTION bit is set or not */
+ pat9126_read_motion(data->client, &delta_x, &delta_y);
+ pr_debug("[PAT9126]delta_x: %d, delta_y: %d\n", delta_x, delta_y);
+
+ /* Inverse x depending upon the device orientation */
+ delta_x = (data->inverse_x) ? -delta_x : delta_x;
+ /* Inverse y depending upon the device orientation */
+ delta_y = (data->inverse_y) ? -delta_y : delta_y;
+
+ dev_dbg(dev, "delta_x = 0x%2x, delta_y = 0x%2x\n",
+ delta_x, delta_y);
+
+ if (delta_x != 0) {
+ /* Send delta_x as REL_WHEEL for rotation */
+ input_report_rel(ipdev, REL_WHEEL, (s8) delta_x);
+ input_sync(ipdev);
+ }
+ enable_irq(data->client->irq);
+}
+
+static irqreturn_t pat9126_irq(int irq, void *dev_data)
+{
+ struct pixart_pat9126_data *data = dev_data;
+ bool result = false;
+
+ disable_irq_nosync(irq);
+ if (!work_pending(&data->work)) {
+ result = schedule_delayed_work(&data->polling_work, msecs_to_jiffies(10));
+ if (result == false) {
+ /* queue_work fail */
+ pr_err("%s:queue_work fail.\n",__func__);
+ }
+ } else {
+ /* work pending */
+ pr_err("%s:queue_work pending.\n",__func__);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t pat9126_sensitivity_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count) {
+ u8 sensitivity;
+
+ struct pixart_pat9126_data *data =
+ (struct pixart_pat9126_data *) dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ if (!kstrtou8(buf, 0, &sensitivity)) {
+ dev_dbg(dev, "RES_X: %d, 0x%x\n", sensitivity, sensitivity);
+ pat9126_write(client, PIXART_PAT9126_SET_CPI_RES_X_REG, sensitivity);
+ }
+
+ return count;
+}
+
+static ssize_t pat9126_sensitivity_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf) {
+ int count = 0;
+ uint8_t tmp;
+
+ struct pixart_pat9126_data *data =
+ (struct pixart_pat9126_data *) dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ pat9126_read(client, PIXART_PAT9126_SET_CPI_RES_X_REG, &tmp);
+ count += sprintf(buf, "0x%2x\n", tmp);
+
+ return count;
+}
+
+static DEVICE_ATTR
+ (crown_sensitivity, S_IRUGO | S_IWUSR | S_IWGRP, pat9126_sensitivity_show, pat9126_sensitivity_store);
+
+static struct attribute *pat9126_attr_list[] = {
+ &dev_attr_crown_sensitivity.attr,
+ NULL,
+};
+
+static struct attribute_group pat9126_attr_grp = {
+ .attrs = pat9126_attr_list,
+};
+
+static int pat9126_parse_dt(struct device *dev,
+ struct pixart_pat9126_data *data)
+{
+ struct device_node *np = dev->of_node;
+ u32 temp_val;
+ int ret;
+
+ data->inverse_x = of_property_read_bool(np, "pixart,inverse-x");
+ data->inverse_y = of_property_read_bool(np, "pixart,inverse-y");
+ data->press_en = of_property_read_bool(np, "pixart,press-enabled");
+ if (data->press_en) {
+ ret = of_property_read_u32(np, "pixart,press-keycode",
+ &temp_val);
+ if (!ret) {
+ data->press_keycode = temp_val;
+ } else {
+ dev_err(dev, "Unable to parse press-keycode\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int pat9126_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct pixart_pat9126_data *data;
+ struct input_dev *input;
+ struct device *dev = &client->dev;
+
+ ret = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE);
+ if (ret < 0) {
+ dev_err(dev, "I2C not supported\n");
+ return -ENXIO;
+ }
+
+ data = devm_kzalloc(dev, sizeof(struct pixart_pat9126_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ ret = pat9126_parse_dt(dev, data);
+ if (ret) {
+ dev_err(dev, "DT parsing failed, errno:%d\n", ret);
+ return ret;
+ }
+ data->client = client;
+
+ input = devm_input_allocate_device(dev);
+ if (!input) {
+ dev_err(dev, "Failed to alloc input device\n");
+ return -ENOMEM;
+ }
+
+ input_set_capability(input, EV_REL, REL_WHEEL);
+ if (data->press_en)
+ input_set_capability(input, EV_KEY, data->press_keycode);
+
+ i2c_set_clientdata(client, data);
+ input_set_drvdata(input, data);
+ input->name = PAT9126_DEV_NAME;
+
+ data->input = input;
+ ret = input_register_device(data->input);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register input device\n");
+ return ret;
+ }
+
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret < 0)
+ dev_err(dev, "Could not set pin to active state %d\n", ret);
+
+ usleep_range(DELAY_BETWEEN_REG_US, DELAY_BETWEEN_REG_US + 1);
+
+ /*
+ * Initialize pixart sensor after some delay, when vdd
+ * regulator is enabled
+ */
+ if (pat9126_sensor_init(data->client) < 0) {
+ ret = -ENODEV;
+ dev_err(dev, "Failed to initialize sensor %d\n", ret);
+ return ret;
+ }
+ INIT_DELAYED_WORK(&data->polling_work, pat9126_work_handler);
+ if (already_calibrated == 1 && en_irq_cnt == 0) {
+ pr_err("[PAT9126]: Probe Enable Irq. \n");
+ ret = devm_request_threaded_irq(dev, client->irq, NULL, pat9126_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "pixart_pat9126_irq", data);
+ is_initialized = true;
+
+ if (ret) {
+ dev_err(dev, "Req irq %d failed, errno:%d\n", client->irq, ret);
+ goto err_request_threaded_irq;
+ }
+ en_irq_cnt++;
+ } else
+ en_irq_cnt = 0;
+
+ ret = sysfs_create_group(&client->dev.kobj, &pat9126_attr_grp);
+ if (ret) {
+ dev_err(dev, "Failed to create sysfs group, errno:%d\n", ret);
+ goto err_sysfs_create;
+ }
+
+ return 0;
+
+err_sysfs_create:
+err_request_threaded_irq:
+ cancel_work_sync(&data->work);
+ destroy_workqueue(data->workqueue);
+
+ return ret;
+}
+
+static int pat9126_i2c_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static int pat9126_suspend(struct device *dev)
+{
+ int ret = 0;
+ struct pixart_pat9126_data *data =
+ (struct pixart_pat9126_data *) dev_get_drvdata(dev);
+
+ flush_scheduled_work();
+
+ printk(KERN_DEBUG "[PAT9126]%s, start\n", __func__);
+ ret = pinctrl_pm_select_sleep_state(dev);
+ if (ret < 0)
+ dev_err(dev, "Could not set pin to sleep state %d\n", ret);
+
+ if(!is_initialized) {
+ printk(KERN_DEBUG "[PAT9126]%s: Not initialize yet. \n", __func__);
+ /* disable write protect */
+ pat9126_write_verified(data->client, PIXART_PAT9126_WRITE_PROTECT_REG,
+ PIXART_PAT9126_DISABLE_WRITE_PROTECT);
+
+ /*Write Register for Suspend Mode*/
+ ret = pat9126_disable_mot(data->client,
+ PIXART_PAT9126_SLEEP_MODE_DETECT_FREQ_DEFAULT);
+ if (ret != 0){
+ pr_err("[PAT9126]: Disable Motion FAIL.");
+ }
+
+ /* enable write protect */
+ pat9126_write_verified(data->client, PIXART_PAT9126_WRITE_PROTECT_REG,
+ PIXART_PAT9126_ENABLE_WRITE_PROTECT);
+
+ return 0;
+ }
+ else
+ printk(KERN_DEBUG "[PAT9126]%s: Already initialized. \n", __func__);
+
+ if (dis_irq_cnt == 0){
+ disable_irq(data->client->irq);
+ dis_irq_cnt++;
+ }
+ else {
+ dev_info(dev, "Already in suspend state\n");
+ return 0;
+ }
+ en_irq_cnt = 0;
+
+ /* disable write protect */
+ pat9126_write_verified(data->client, PIXART_PAT9126_WRITE_PROTECT_REG,
+ PIXART_PAT9126_DISABLE_WRITE_PROTECT);
+
+ /*Write Register for Suspend Mode*/
+ ret = pat9126_disable_mot(data->client,
+ PIXART_PAT9126_SLEEP_MODE_DETECT_FREQ_DEFAULT);
+ if (ret != 0){
+ pr_err("[PAT9126]: Disable Motion FAIL.");
+ }
+
+ /* enable write protect */
+ pat9126_write_verified(data->client, PIXART_PAT9126_WRITE_PROTECT_REG,
+ PIXART_PAT9126_ENABLE_WRITE_PROTECT);
+ return 0;
+}
+
+static int pat9126_resume(struct device *dev)
+{
+ int ret = 0;
+ struct pixart_pat9126_data *data =
+ (struct pixart_pat9126_data *) dev_get_drvdata(dev);
+
+ printk(KERN_DEBUG "[PAT9126]%s, start\n", __func__);
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret < 0)
+ dev_err(dev, "Could not set pin to active state %d\n", ret);
+ if(!is_initialized) {
+ printk(KERN_DEBUG "[PAT9126]%s: Not initialize yet. \n", __func__);
+ /* disable write protect */
+ pat9126_write_verified(data->client, PIXART_PAT9126_WRITE_PROTECT_REG,
+ PIXART_PAT9126_DISABLE_WRITE_PROTECT);
+
+ ret = pat9126_enable_mot(data->client);
+ if (ret != 0){
+ pr_err("[PAT9126]: Enable Motion FAIL. \n");
+ return 0;
+ }
+
+ /* enable write protect */
+ pat9126_write_verified(data->client, PIXART_PAT9126_WRITE_PROTECT_REG,
+ PIXART_PAT9126_ENABLE_WRITE_PROTECT);
+
+ return 0;
+ }
+ else
+ printk(KERN_DEBUG "[PAT9126]%s: Already initialized. \n", __func__);
+
+ /* disable write protect */
+ pat9126_write_verified(data->client, PIXART_PAT9126_WRITE_PROTECT_REG,
+ PIXART_PAT9126_DISABLE_WRITE_PROTECT);
+
+ ret = pat9126_enable_mot(data->client);
+ if (ret != 0){
+ pr_err("[PAT9126]: Enable Motion FAIL. \n");
+ return 0;
+ }
+
+ /* enable write protect */
+ pat9126_write_verified(data->client, PIXART_PAT9126_WRITE_PROTECT_REG,
+ PIXART_PAT9126_ENABLE_WRITE_PROTECT);
+
+ if (en_irq_cnt == 0){
+ enable_irq(data->client->irq);
+ en_irq_cnt++;
+ }
+ else {
+ dev_info(dev, "Already in wake state \n");
+ return 0;
+ }
+ dis_irq_cnt = 0;
+
+ return 0;
+}
+
+static const struct i2c_device_id pat9126_device_id[] = {
+ {PAT9126_DEV_NAME, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, pat9126_device_id);
+
+static const struct dev_pm_ops pat9126_pm_ops = {
+ .suspend = pat9126_suspend,
+ .resume = pat9126_resume
+};
+
+static const struct of_device_id pixart_pat9126_match_table[] = {
+ { .compatible = "pixart,pat9126",},
+ { },
+};
+
+static struct i2c_driver pat9126_i2c_driver = {
+ .driver = {
+ .name = PAT9126_DEV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &pat9126_pm_ops,
+ .of_match_table = pixart_pat9126_match_table,
+ },
+ .probe = pat9126_i2c_probe,
+ .remove = pat9126_i2c_remove,
+ .id_table = pat9126_device_id,
+};
+module_i2c_driver(pat9126_i2c_driver);
+
+MODULE_AUTHOR("pixart");
+MODULE_DESCRIPTION("pixart pat9126 driver");
+MODULE_LICENSE("GPL");
diff --git a/ots_pat9126/pat9126.h b/ots_pat9126/pat9126.h
new file mode 100644
index 0000000..0c153b5
--- /dev/null
+++ b/ots_pat9126/pat9126.h
@@ -0,0 +1,115 @@
+/* drivers/input/misc/ots_pat9126/pixart_ots.h
+ *
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#ifndef __PIXART_OTS_H_
+#define __PIXART_OTS_H_
+
+#define PAT9126_DEV_NAME "pixart_pat9126"
+#define BUF_SIZE 2
+#define RESET_DELAY_US 1000
+#define PINCTRL_STATE_ACTIVE "pmx_rot_switch_active"
+#define PINCTRL_STATE_SUSPEND "pmx_rot_switch_suspend"
+#define PINCTRL_STATE_RELEASE "pmx_rot_switch_release"
+#define VDD_VTG_MIN_UV 1800000
+#define VDD_VTG_MAX_UV 1800000
+#define VDD_ACTIVE_LOAD_UA 10000
+#define VLD_VTG_MIN_UV 2800000
+#define VLD_VTG_MAX_UV 3300000
+#define VLD_ACTIVE_LOAD_UA 10000
+#define DELAY_BETWEEN_REG_US 20000
+#define PAT9150_I2C_RETRY 4
+
+/* Register addresses */
+#define PIXART_PAT9126_PRODUCT_ID1_REG 0x00
+#define PIXART_PAT9126_PRODUCT_ID2_REG 0x01
+#define PIXART_PAT9126_MOTION_STATUS_REG 0x02
+#define PIXART_PAT9126_DELTA_X_LO_REG 0x03
+#define PIXART_PAT9126_DELTA_Y_LO_REG 0x04
+#define PIXART_PAT9126_SLEEP_MODE_SELECT_REG 0x05
+#define PIXART_PAT9126_CONFIG_REG 0x06
+#define PIXART_PAT9126_PIXEL_GRAB_STATUS_REG 0x08
+#define PIXART_PAT9126_WRITE_PROTECT_REG 0x09
+#define PIXART_PAT9126_SLEEP2_MODE_FREQ_REG 0x0B
+#define PIXART_PAT9126_SET_CPI_RES_X_REG 0x0D
+#define PIXART_PAT9126_SET_CPI_RES_Y_REG 0x0E
+#define PIXART_PAT9126_PIXEL_GRAB_VAL_REG 0x0F
+#define PIXART_PAT9126_DELTA_XY_HI_REG 0x12
+#define PIXART_PAT9126_FRAME_AVG_REG 0x17
+#define PIXART_PAT9126_ORIENTATION_REG 0x19
+#define PIXART_PAT9126_PIXEL_GRAB_CLOCK_REG 0x19
+#define PIXART_PAT9126_PIXEL_GRAB_FRAME_SKIP0_REG 0x20
+#define PIXART_PAT9126_AE_ENABLE 0x2B
+#define PIXART_PAT9126_BANK_FTWK 0x2B
+#define PIXART_PAT9126_BANK_FTWK_D2 0x2C
+#define PIXART_PAT9126_NY_MIN 0x2D
+#define PIXART_PAT9126_BANK_CTB 0x30
+#define PIXART_PAT9126_BANK_HI_SAD_K 0x38
+#define PIXART_PAT9126_VOLTAGE_SEGMENT_SEL_REG 0x4B
+#define PIXART_PAT9126_PIXEL_GRAB_FRAME_SKIP1_REG 0x58
+#define PIXART_PAT9126_MISC1_REG 0x5D
+#define PIXART_PAT9126_MISC2_REG 0x5E
+#define PIXART_PAT9126_SENSOR_MODE_SELECT_REG 0x7C
+#define PIXART_PAT9126_SELECT_BANK_REG 0x7F
+
+/*Register configuration data */
+#define PIXART_PAT9126_SENSOR_ID 0x31
+#define PIXART_PAT9126_RESET 0x97
+#define PIXART_PAT9126_MOTION_DATA_LENGTH 0x04
+#define PIXART_PAT9126_BANK0 0x00
+#define PIXART_PAT9126_DISABLE_WRITE_PROTECT 0x5A
+#define PIXART_PAT9126_ENABLE_WRITE_PROTECT 0x00
+#define PIXART_PAT9126_CPI_RESOLUTION_X 0xFF
+#define PIXART_PAT9126_CPI_RESOLUTION_Y 0x00
+#define PIXART_PAT9126_CPI_RES_X_MAX 0xFF
+#define PIXART_PAT9126_CPI_RES_X_MIN 0x00
+#define PIXART_PAT9126_SENSOR_DEFAULT_MODE 0xC8
+#define PIXART_PAT9126_SENSOR_DEFAULT_MODE2 0x80
+#define PIXART_PAT9126_SENSOR_SET_MODE 0xCC
+#define PIXART_PAT9126_SENSOR_SET_MODE2 0x82
+#define PIXART_PAT9126_WAKEUP_MODE 0xA0
+#define PIXART_PAT9126_SLEEP1_MODE_EN 0xA1
+#define PIXART_PAT9126_SLEEP2_MODE_EN 0xB8
+#define PIXART_PAT9126_SLEEP2_MODE_DIS 0xB0
+#define PIXART_PAT9126_FORCE_ENTER_SLEEP2_MODE 0xBC
+#define PIXART_PAT9126_SLEEP_MODE_DETECT_FREQ_DEFAULT 0x10
+#define PIXART_PAT9126_SLEEP_MODE_DETECT_FREQ 0xF0
+#define PIXART_PAT9126_SELECT_BANK_VAL1 0x00
+#define PIXART_PAT9126_SELECT_BANK_VAL2 0x01
+#define PIXART_PAT9126_AE_ENABLE_VAL 0x6D
+#define PIXART_PAT9126_NY_MIN_VAL 0x00
+#define PIXART_PAT9126_BANK_FTWK_DEFAULT_VAL 0xAE
+#define PIXART_PAT9126_BANK_FTWK_VAL1 0x00
+#define PIXART_PAT9126_BANK_FTWK_D2_DEFAULT_VAL 0x97
+#define PIXART_PAT9126_BANK_FTWK_D2_VAL1 0x00
+#define PIXART_PAT9126_BANK_CTB_DEFAULT_VAL 0xAC
+#define PIXART_PAT9126_BANK_CTB_VAL1 0x00
+#define PIXART_PAT9126_BANK_HI_SAD_K_DEFAULT_VAL 0x01
+#define PIXART_PAT9126_BANK_HI_SAD_K_VAL1 0xF1
+#define PIXART_PAT9126_LOW_VOLTAGE_SEGMENT 0x04
+#define PIXART_PAT9126_VALID_MOTION_DATA 0x80
+#define PIXART_PAT9126_PIXEL_GRAB_RESET_VAL 0x00
+#define PIXART_PAT9126_PIXEL_GRAB_FRAME_SKIP0_VAL 0x64
+#define PIXART_PAT9126_PIXEL_GRAB_FRAME_SKIP0_VAL1 0xE4
+#define PIXART_PAT9126_PIXEL_GRAB_FRAME_SKIP1_VAL 0x98
+#define PIXART_PAT9126_PIXEL_GRAB_FRAME_SKIP1_VAL1 0xD8
+#define PIXART_PAT9126_PIXEL_GRAB_CLOCK_VAL 0x06
+#define PIXART_PAT9126_PIXEL_GRAB_CLOCK_VAL1 0x04
+#define PIXART_PAT9126_MOTION_COUNT_MAX 385
+#define PIXART_PAT9126_MOTION_COUNT_DEFAULT 350
+#define PIXART_PAT9126_MOTION_COUNT_MIN 315
+
+#define PIXART_SAMPLING_PERIOD_US_MIN 4000
+#define PIXART_SAMPLING_PERIOD_US_MAX 8000
+
+/*define pixel grab flag*/
+#define OTS_FRAME_VALID 0x80
+#define OTS_PIXEL_VALID 0x40
+
+/* Export functions */
+int pat9126_disable_mot(struct i2c_client *client, int16_t detect_freq);
+int pat9126_enable_mot(struct i2c_client *client);
+
+#endif