crucial_oj: add oj driver and modifications needed in gpio_input/event/matrix
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 2c66a8c..2af54c9 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -188,6 +188,8 @@
 
 source "drivers/input/misc/Kconfig"
 
+source "drivers/input/opticaljoystick/Kconfig"
+
 endif
 
 menu "Hardware I/O ports"
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index cf643be..58002f6 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -26,3 +26,5 @@
 obj-$(CONFIG_INPUT_APMPOWER)	+= apm-power.o
 obj-$(CONFIG_INPUT_OF_MATRIX_KEYMAP) += of_keymap.o
 obj-$(CONFIG_INPUT_KEYRESET)	+= keyreset.o
+
+obj-$(CONFIG_INPUT_OPTICALJOYSTICK)		+= opticaljoystick/
diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c
index 975f4d1..548b5b5 100644
--- a/drivers/input/misc/gpio_input.c
+++ b/drivers/input/misc/gpio_input.c
@@ -21,6 +21,9 @@
 #include <linux/interrupt.h>
 #include <linux/slab.h>
 #include <linux/wakelock.h>
+#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL
+#include <linux/curcial_oj.h>
+#endif
 
 #ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_3K
 static uint8_t power_key_state;
@@ -250,9 +253,20 @@
 #ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_3K
 		handle_power_key_state(key_entry->code, pressed);
 #endif
+#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL
+		if (key_entry->code == BTN_MOUSE) {
+		  pr_info("gpio_keys_scan_keys: OJ action key %d-%d, %d (%d) "
+			  "changed to %d\n", ds->info->type,
+			  key_entry->code, i, key_entry->gpio, pressed);
+		  curcial_oj_send_key(BTN_MOUSE, pressed);
+		} else {
+#endif
 		input_event(ds->input_devs->dev[key_entry->dev], ds->info->type,
 			    key_entry->code, pressed);
 		sync_needed = true;
+#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL
+                }
+#endif
 	}
 	if (sync_needed) {
 		for (i = 0; i < ds->input_devs->count; i++)
diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c
index eaa9e89..cfa6170 100644
--- a/drivers/input/misc/gpio_matrix.c
+++ b/drivers/input/misc/gpio_matrix.c
@@ -20,6 +20,10 @@
 #include <linux/interrupt.h>
 #include <linux/slab.h>
 #include <linux/wakelock.h>
+#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL
+#include <asm/mach-types.h>
+#include <linux/curcial_oj.h>
+#endif
 
 struct gpio_kp {
 	struct gpio_event_input_devs *input_devs;
@@ -111,6 +115,9 @@
 	unsigned short keyentry = mi->keymap[key_index];
 	unsigned short keycode = keyentry & MATRIX_KEY_MASK;
 	unsigned short dev = keyentry >> MATRIX_CODE_BITS;
+#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL
+	static unsigned need_send_spec_key = 1;
+#endif
 
 	if (pressed != test_bit(keycode, kp->input_devs->dev[dev]->key)) {
 		if (keycode == KEY_RESERVED) {
@@ -125,9 +132,22 @@
 					"changed to %d\n", keycode,
 					out, in, mi->output_gpios[out],
 					mi->input_gpios[in], pressed);
+#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL
+			if (!(mi->info.oj_btn && keycode == BTN_MOUSE))
+#endif
 			input_report_key(kp->input_devs->dev[dev], keycode, pressed);
 		}
 	}
+#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL
+	if (mi->info.oj_btn && keycode == BTN_MOUSE) {
+	  if (need_send_spec_key == pressed) {
+	    curcial_oj_send_key(keycode, pressed);
+	    need_send_spec_key = !pressed;
+	    pr_info("%s: send OJ action key, pressed: %d\n",
+		    __func__, pressed);
+	  }
+	}
+#endif
 }
 
 static void report_sync(struct gpio_kp *kp)
diff --git a/drivers/input/opticaljoystick/Kconfig b/drivers/input/opticaljoystick/Kconfig
new file mode 100644
index 0000000..9f54841
--- /dev/null
+++ b/drivers/input/opticaljoystick/Kconfig
@@ -0,0 +1,34 @@
+#
+# opticaljoystick driver configuration
+#
+menuconfig INPUT_OPTICALJOYSTICK
+	bool "Opticaljoystick"
+	help
+	  Say Y here, and a list of supported optical joystick will be displayed.
+	  This option doesn't affect the kernel.
+
+	  If unsure, say Y.
+
+if INPUT_OPTICALJOYSTICK
+
+config OPTICALJOYSTICK_CRUCIAL
+	boolean
+
+choice
+	prompt "Interface"
+
+config OPTICALJOYSTICK_CRUCIAL_uP
+	boolean "Crucial Optical Joystick (microP)"
+	depends on MICROP_COMMON
+	select OPTICALJOYSTICK_CRUCIAL
+	help
+
+config OPTICALJOYSTICK_CRUCIAL_SPI
+	boolean "Crucial Optical Joystick (SPI)"
+	depends on SPI_CRUCIAL_OJ
+	select OPTICALJOYSTICK_CRUCIAL
+	help
+
+endchoice
+
+endif
diff --git a/drivers/input/opticaljoystick/Makefile b/drivers/input/opticaljoystick/Makefile
new file mode 100644
index 0000000..67bd9be
--- /dev/null
+++ b/drivers/input/opticaljoystick/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the opticaljoystick drivers.
+#
+
+# Each configuration option enables a list of files.
+obj-$(CONFIG_OPTICALJOYSTICK_CRUCIAL_uP)	+= curcial.o
+obj-$(CONFIG_OPTICALJOYSTICK_CRUCIAL_SPI)	+= curcial_spi.o
diff --git a/drivers/input/opticaljoystick/curcial.c b/drivers/input/opticaljoystick/curcial.c
new file mode 100644
index 0000000..458322e
--- /dev/null
+++ b/drivers/input/opticaljoystick/curcial.c
@@ -0,0 +1,780 @@
+/* drivers/input/opticaljoystick/curcial.c
+ *
+ * Copyright (C) 2009 HTC Corporation.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ *
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <mach/atmega_microp.h>
+#include <linux/earlysuspend.h>
+#include <linux/curcial_oj.h>
+#include <mach/vreg.h>
+#include <asm/mach-types.h>
+#include "curcial.h"
+
+#define OJ_POWERON                  1
+#define OJ_POWEROFF                 0
+#define CURCIAL_OJ_POWER            85
+#define BURST_DATA_SIZE             7
+#define OJ_DEVICE_ID                0x0D
+#define OJ_REGISTER_WRITE           0x7B
+#define OJ_REGISTER_REQUEST         0x7C
+#define OJ_REGISTER_READ            0x7D
+#define OJ_REGISTER_BURST_REQUEST   0x7E
+#define OJ_REGISTER_BURST_READ      0x7F
+#define OJ_REGISTER_OJ_POLLING      0xA8
+#define OJ_MOTION                   0x02
+#define OJ_DELTA_Y                  0x03
+#define OJ_DELTA_X                  0x04
+#define OJ_SQUAL                    0x05
+#define OJ_SHU_HIGH                 0x06
+#define OJ_SHU_LOW                  0x07
+#define OJ_OBSERVATION              0x2E
+#define OJ_POLLING_ENABLE           1
+#define OJ_POLLING_DISABLE          0
+#define OJ_POLLING_INTERVAL         10
+#define OJ_POLLING_COUNT            10
+
+#define DELTA_SUM_TIME      40
+#define DELTA_SUM_CP        0
+#define OJ_RETRY		        5
+static const unsigned short keymap[] = {
+	 KEY_RIGHT,
+	 KEY_LEFT,
+	 KEY_UP,
+	 KEY_DOWN /*,
+	 KEY_REPLY*/
+};
+
+enum {
+	MOTION = 0,
+	Y,
+	X,
+	SQUAL,
+	SHUTTER_UPPER,
+	SHUTTER_LOWER,
+	MAXIMUM_PIXEL
+};
+extern unsigned int system_rev;
+static struct proc_dir_entry *oj_proc_entry;
+static struct workqueue_struct *curcial_wq;
+static struct curcial_oj_platform_data *my_oj;
+static uint8_t  polling_delay;/* use msleep*/
+static uint8_t  interval;
+static uint8_t  debugflag;
+static uint8_t	ap_code;
+static int16_t	mSumDeltaX;
+static int16_t	mSumDeltaY;
+static int8_t	DeltaX[64];
+static int8_t	DeltaY[64];
+static int16_t	mDeltaX;
+static int16_t	mDeltaY;
+static int8_t	normal_th;
+static int8_t	xy_ratio;
+
+static atomic_t suspend_flag = ATOMIC_INIT(0);
+static uint16_t	index;
+
+static int __devinit curcial_oj_probe(struct platform_device *pdev);
+static int __devexit curcial_oj_remove(struct platform_device *pdev);
+
+static struct platform_driver curcial_oj_device_driver = {
+	.probe    = curcial_oj_probe,
+	.remove   = __devexit_p(curcial_oj_remove),
+	.driver   = {
+		.name   = CURCIAL_OJ_NAME,
+		.owner  = THIS_MODULE,
+	}
+};
+
+static uint8_t curcial_oj_register_read(uint8_t reg)
+{
+	uint8_t cmd[2];
+
+	cmd[0] = 0;
+	cmd[1] = reg;
+	microp_i2c_write(OJ_REGISTER_REQUEST, cmd, 2);
+	microp_i2c_read(OJ_REGISTER_READ, cmd, 2);
+
+	return cmd[1];
+}
+
+static void curcial_oj_burst_read(uint8_t *data)
+{
+	uint8_t cmd[2];
+
+	cmd[0] = 0x01;
+	microp_i2c_write(OJ_REGISTER_BURST_REQUEST, cmd, 1);
+	microp_i2c_read(OJ_REGISTER_BURST_READ, data, BURST_DATA_SIZE);
+}
+
+static void curcial_oj_polling_mode(uint8_t mode)
+{
+	uint8_t cmd[2];
+
+	cmd[0] = mode;
+	microp_i2c_write(OJ_REGISTER_OJ_POLLING, cmd, 1);
+}
+
+static irqreturn_t curcial_oj_irq_handler(int irq, void *data)
+{
+ 	queue_work(curcial_wq, &my_oj->work);
+	return IRQ_HANDLED;
+}
+
+static int curcial_oj_init(void)
+{
+	uint8_t data[BURST_DATA_SIZE];
+	uint8_t id;
+	uint8_t version;
+	uint8_t i;
+
+	microp_i2c_read(MICROP_I2C_RCMD_VERSION, data, 2);
+	version = my_oj->microp_version;
+
+
+	if (data[0] < version) {
+		printk("Microp firmware version:%d have to large than %d !\n\
+						Stop OJ driver loading!\n", data[0], version);
+		return 0;
+	}
+
+	if (!my_oj->oj_poweron(OJ_POWERON))
+		return 0;
+
+	mdelay(10);
+	microp_spi_vote_enable(SPI_OJ, 1);
+
+	/*microp_i2c_read(0x24, data, 2);*/
+	my_oj->oj_shutdown(0);
+	/* Write 0x5a to register 0x3a */
+	data[0] = 0x3a;
+	data[1] = 0x5a;
+	microp_i2c_write(OJ_REGISTER_WRITE, data, 2);
+	mdelay(23);
+
+	/* Read from register 0x02,0x03 and 0x04 one time regardless the state of the motion pin */
+	curcial_oj_register_read(OJ_MOTION);
+	curcial_oj_register_read(OJ_DELTA_Y);
+	curcial_oj_register_read(OJ_DELTA_X);
+
+	for (i = 0;i < OJ_RETRY; i++ ) {
+	id = curcial_oj_register_read(0x00);
+	if (id == OJ_DEVICE_ID) {
+		printk(KERN_INFO"OpticalJoystick Device ID: %02x\n", OJ_DEVICE_ID);
+		id = curcial_oj_register_read(0x01);
+		printk(KERN_INFO"OJ Driver: Revision : %02x\n", id);
+			break;
+	} else {
+			printk("probe OpticalJoystick Device:retry =%d\n", i);
+		}
+	}
+	if (i == OJ_RETRY) {
+		printk("Can't probe OpticalJoystick Device: %02x!\n", id);
+		return 0;
+	}
+
+	/* Write 0x10 to register 0x1C. This will activate burst mode. */
+	data[0] = 0x1C;
+	data[1] = 0x10;
+	microp_i2c_write(OJ_REGISTER_WRITE, data, 2);
+
+	curcial_oj_polling_mode(OJ_POLLING_ENABLE);
+
+	return 1;
+}
+static OJKeyEvt_T OJ_ProcessNavi(int Ratio, int DeltaMin, int16_t SumDeltaX, int16_t SumDeltaY)
+{
+	OJKeyEvt_T	tmpKey;
+
+	if ((10*abs(SumDeltaY) > (Ratio*abs(SumDeltaX)))
+			&& (abs(SumDeltaY) > DeltaMin)) {
+		if (SumDeltaY > 0)
+			tmpKey = OJ_KEY_UP;
+		else
+			tmpKey = OJ_KEY_DOWN;
+	} else	if (abs(SumDeltaX) > DeltaMin) {
+		if (SumDeltaX > 0)
+			tmpKey = OJ_KEY_RIGHT;
+		else
+			tmpKey = OJ_KEY_LEFT;
+	} else
+		tmpKey = OJ_KEY_NONE;
+
+	return tmpKey;
+}
+
+static void curcial_oj_work_func(struct work_struct *work)
+{
+	struct curcial_oj_platform_data *oj = container_of(work, struct curcial_oj_platform_data, work);
+	OJData_T  OJData;
+	uint16_t i, j;
+	uint8_t data[BURST_DATA_SIZE];
+	uint32_t click_time = 0;
+	uint32_t delta_time = 0;
+	uint32_t entry_time = 0;
+	OJKeyEvt_T	evtKey = OJ_KEY_NONE;
+	uint8_t	x_count = 0;
+	uint8_t	y_count = 0;
+	bool out = false;
+	uint8_t pxsum;
+	uint16_t sht;
+	int16_t	x_sum, x_idx;
+	int16_t	y_sum, y_idx;
+
+	curcial_oj_polling_mode(OJ_POLLING_DISABLE);
+
+	mDeltaX = 0;
+	mDeltaY = 0;
+	oj->interval = interval;
+	entry_time = jiffies_to_msecs(jiffies);
+	x_sum = 0;
+	y_sum = 0;
+
+		do {
+			memset(data, 0x00, sizeof(data));
+			out = false;
+			curcial_oj_burst_read(data);
+			OJData.squal = data[SQUAL];
+			pxsum = curcial_oj_register_read(0x09);
+			sht =	((data[SHUTTER_UPPER] << 8)|data[SHUTTER_LOWER]);
+			if (debugflag) {
+				printk(KERN_INFO"OJ1:M=0x%02x Y=0x%02x X=0x%02x SQUAL=0x%02x "
+				"SHU_U=0x%02x SHU_L=0x%02x pxsum=%d sht=%d  \n", data[MOTION], data[Y], data[X],
+				 data[SQUAL], data[SHUTTER_UPPER], data[SHUTTER_LOWER], pxsum, sht);
+			}
+			if (ap_code) {
+				for (i = 1; i < oj->degree; i++) {
+					if (((oj->sht_tbl[i-1] < sht) && (sht <= oj->sht_tbl[i])) && (oj->pxsum_tbl[i] < pxsum)) {
+						if (debugflag)
+							printk("OJ:A.code_condition:%d\n", i);
+						out = true;
+						break;
+					}
+				}
+			if (!out)
+				goto exit;
+			}
+			oj->oj_adjust_xy(data, &mDeltaX, &mDeltaY);
+
+
+				DeltaX[index] = (int8_t)mDeltaX;
+				DeltaY[index] = (int8_t)mDeltaY;
+				/*printk(KERN_INFO"index=%d: DeltaX[]  = %d DeltaY[] = %d \n",index, DeltaX[index] , DeltaY[index]);*/
+				if (++index == 64)
+					index = 0;
+
+			x_sum = x_sum + mDeltaX;
+			y_sum = y_sum + mDeltaY;
+			mSumDeltaX = mSumDeltaX + mDeltaX;
+			mSumDeltaY = mSumDeltaY + mDeltaY;
+			if (debugflag)
+				printk(KERN_INFO"check:OJ:mSumDeltaX = %d mSumDeltaY = %d \n", mSumDeltaX, mSumDeltaY);
+
+			evtKey = OJ_ProcessNavi(xy_ratio, normal_th, x_sum, y_sum);
+
+			if (evtKey != OJ_KEY_NONE) {
+				click_time = jiffies_to_msecs(jiffies);
+				if (debugflag)
+					printk(KERN_INFO"click_time=%x last_click_time=%x, %x\n", click_time, oj->last_click_time, click_time-oj->last_click_time);
+
+			if (oj->last_click_time == 0) {
+				oj->last_click_time = entry_time - oj->interval;
+				oj->key = evtKey;
+				}
+
+			delta_time = 	click_time - entry_time;
+
+			/*printk(KERN_INFO"x_sum=%d y_sum=%d, delta time=%dms\n", x_sum, y_sum, delta_time);*/
+
+				if (click_time - oj->last_click_time < oj->interval) {
+					evtKey = OJ_KEY_NONE;
+
+				if (debugflag)
+						printk(KERN_INFO"interval blocking < %d\n", oj->interval);
+				}else if (click_time - oj->last_click_time < 80 && evtKey != oj->key) {
+					evtKey = OJ_KEY_NONE;
+					printk(KERN_INFO"sudden key ignore \n");
+				}
+			}
+
+			x_idx = abs(x_sum) / normal_th;
+			y_idx = abs(y_sum) / normal_th;
+			if (x_idx >= ARRAY_SIZE(oj->Xsteps))
+				x_idx = ARRAY_SIZE(oj->Xsteps) - 1;
+			if (y_idx >= ARRAY_SIZE(oj->Ysteps))
+				y_idx = ARRAY_SIZE(oj->Ysteps) - 1;
+
+			x_count = oj->Xsteps[x_idx];
+			y_count = oj->Ysteps[y_idx];
+			if (evtKey == OJ_KEY_LEFT) {
+				for (j = 0; j < x_count; j++) {
+					input_report_rel(oj->input_dev, REL_X, -1);
+					input_sync(oj->input_dev);
+				}
+				if (debugflag)
+					printk(KERN_INFO"OJ:KEY_LEFT:%d\n", x_count);
+
+			} else if (evtKey == OJ_KEY_RIGHT) {
+				for (j = 0; j < x_count; j++) {
+					input_report_rel(oj->input_dev, REL_X, 1);
+					input_sync(oj->input_dev);
+				}
+				if (debugflag)
+					printk(KERN_INFO"OJ:KEY_RIGHT:%d\n", x_count);
+
+			} else if (evtKey == OJ_KEY_DOWN) {
+				for (j = 0; j < y_count; j++) {
+					input_report_rel(oj->input_dev, REL_Y, 1);
+					input_sync(oj->input_dev);
+				}
+				if (debugflag)
+					printk(KERN_INFO"OJ:KEY_DOWN:%d\n", y_count);
+
+			} else if (evtKey == OJ_KEY_UP) {
+				for (j = 0; j < y_count; j++) {
+					input_report_rel(oj->input_dev, REL_Y, -1);
+					input_sync(oj->input_dev);
+				}
+				if (debugflag)
+					printk(KERN_INFO"OJ:KEY_UP:%d\n", y_count);
+			}
+
+			if (evtKey != OJ_KEY_NONE) {
+				oj->key = evtKey;
+				oj->last_click_time = click_time;
+				x_sum = 0;
+				y_sum = 0;
+				/*goto exit;*/
+			}
+		mDeltaX = 0;
+		mDeltaY = 0;
+		if (polling_delay)
+			hr_msleep(polling_delay);
+			} while ((data[0] & 0x80) && (!atomic_read(&suspend_flag)));
+
+
+exit:
+
+	if (debugflag)
+		printk(KERN_INFO"%s:-\n", __func__);
+	if (!atomic_read(&suspend_flag))
+		curcial_oj_polling_mode(OJ_POLLING_ENABLE);
+	else
+		curcial_oj_polling_mode(OJ_POLLING_DISABLE);
+}
+
+
+static ssize_t oj_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+
+	return sprintf(buf,
+				"interval=%d normal_th=%d system_rev=%d"
+				" debugflag=%d polling_delay=%d xy_ratio=%d  ap_code=%d",
+				 interval, normal_th, system_rev, debugflag,
+				  polling_delay, xy_ratio, ap_code);
+
+}
+
+static ssize_t oj_ap_code_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+
+	ap_code = simple_strtoull(buf, NULL, 10);
+
+	return count;
+}
+static ssize_t oj_interval_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+
+	interval = simple_strtoull(buf, NULL, 10);
+
+	return count;
+}
+
+static ssize_t oj_normal_th_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+
+	normal_th = simple_strtoull(buf, NULL, 10);
+
+	return count;
+}
+static ssize_t oj_polling_delay_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+
+	polling_delay = simple_strtoull(buf, NULL, 10);
+
+	return count;
+}
+
+static ssize_t oj_xy_ratio_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+
+	xy_ratio = simple_strtoull(buf, NULL, 10);
+
+	return count;
+}
+static ssize_t oj_debugflag_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+
+	debugflag = simple_strtoull(buf, NULL, 10);
+
+	return count;
+}
+
+
+static ssize_t oj_xtable_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+
+	char *buffer,*endptr;
+	int i;
+	buffer = (char *)buf;
+
+	i= simple_strtoull(buffer, &endptr, 10);
+	buffer = endptr+1;
+
+	if (i <= 30)
+		my_oj->Xsteps[i-1] = simple_strtoull(buffer, &endptr, 10);
+
+	return count;
+}
+static ssize_t oj_ytable_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	char *buffer,*endptr;
+	int i;
+	buffer = (char *)buf;;
+
+
+	i= simple_strtoull(buffer, &endptr, 10);
+	buffer = endptr+1;
+
+	if (i <= 30)
+		my_oj->Ysteps[i-1] = simple_strtoull(buffer, &endptr, 10);
+
+	return count;
+}
+static ssize_t oj_xtable_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+	char log[128];
+	int i,p;
+
+	for (i = 0, p = 0; i < 30 ; i++)	{
+		p += sprintf(log+p, "%d,",  my_oj->Xsteps[i]);
+	}
+	return sprintf(buf,"X_table:%s\n",log);
+
+}
+static ssize_t oj_ytable_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+	char log[128];
+	int i,p;
+
+	for (i = 0, p = 0; i < 30 ; i++)	{
+		p += sprintf(log+p, "%d,",  my_oj->Ysteps[i]);
+	}
+	return sprintf(buf,"Y_table:%s\n",log);
+
+}
+static ssize_t oj_deltax_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+	char log[512];
+	uint8_t i,p;
+
+	for (i = 0, p = 0; i < 64 ; i++)	{
+		if (i == 63)
+			p += sprintf(log+p, "%d",  DeltaX[i]);
+		else
+			p += sprintf(log+p, "%d,",  DeltaX[i]);
+	}
+
+	return sprintf(buf,"%s\n", log);
+
+}
+static ssize_t oj_deltay_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+	char log[512];
+	uint8_t i,p;
+
+	for (i = 0, p = 0; i < 64 ; i++)	{
+		if (i == 63)
+			p += sprintf(log+p, "%d",  DeltaY[i]);
+		else
+			p += sprintf(log+p, "%d,",  DeltaY[i]);
+	}
+
+	return sprintf(buf,"%s\n", log);
+
+}
+static ssize_t oj_SumDeltaX_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+
+	return sprintf(buf,"%d\n", mSumDeltaX);
+
+}
+static ssize_t oj_SumDeltaY_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+
+	return sprintf(buf,"%d\n", mSumDeltaY);
+
+}
+static ssize_t oj_reset_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+		index = 0;
+		mSumDeltaX = 0;
+		mSumDeltaY = 0;
+		memset(DeltaX, 0x00, sizeof(DeltaX));
+		memset(DeltaY, 0x00, sizeof(DeltaY));
+
+	return count;
+}
+static DEVICE_ATTR(reset, 0664, oj_show, oj_reset_store);
+static DEVICE_ATTR(deltax, 0444, oj_deltax_show, NULL);
+static DEVICE_ATTR(deltay, 0444, oj_deltay_show, NULL);
+static DEVICE_ATTR(SumDeltaX, 0444, oj_SumDeltaX_show, NULL);
+static DEVICE_ATTR(SumDeltaY, 0444, oj_SumDeltaY_show, NULL);
+static DEVICE_ATTR(ap_code, 0644, oj_show, oj_ap_code_store);
+static DEVICE_ATTR(interval, 0644, oj_show, oj_interval_store);
+static DEVICE_ATTR(normal_th, 0644, oj_show, oj_normal_th_store);
+static DEVICE_ATTR(polling_delay, 0644, oj_show, oj_polling_delay_store);
+static DEVICE_ATTR(xy_ratio, 0644, oj_show, oj_xy_ratio_store);
+static DEVICE_ATTR(debugflag, 0644, oj_show, oj_debugflag_store);
+static DEVICE_ATTR(xtable, 0644, oj_xtable_show, oj_xtable_store);
+static DEVICE_ATTR(ytable, 0644, oj_ytable_show, oj_ytable_store);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void curcial_oj_early_suspend(struct early_suspend *h)
+{
+	struct curcial_oj_platform_data *oj;
+	atomic_set(&suspend_flag, 1);
+	oj = container_of(h, struct curcial_oj_platform_data, early_suspend);
+	printk(KERN_ERR"%s: enter\n", __func__);
+	oj->oj_shutdown(1);
+	curcial_oj_polling_mode(OJ_POLLING_DISABLE);
+	if (oj->share_power == false) {
+		oj->oj_poweron(OJ_POWEROFF);
+	}
+	microp_spi_vote_enable(SPI_OJ, 0);
+
+}
+
+static void curcial_oj_late_resume(struct early_suspend *h)
+{
+	struct curcial_oj_platform_data	*oj;
+	atomic_set(&suspend_flag, 0);
+	oj = container_of(h, struct curcial_oj_platform_data, early_suspend);
+	printk(KERN_ERR"%s: enter\n", __func__);
+	if (!curcial_oj_init())
+		microp_spi_vote_enable(SPI_OJ, 0);
+}
+#endif
+
+static int __devinit curcial_oj_probe(struct platform_device *pdev)
+{
+	struct curcial_oj_platform_data *oj = pdev->dev.platform_data;
+	int err;
+	int i;
+
+	err = -ENOMEM;
+	my_oj = oj;
+
+
+	INIT_WORK(&oj->work, curcial_oj_work_func);
+
+	curcial_wq = create_singlethread_workqueue("curcial_wq");
+	if (!curcial_wq) {
+		err = -ENOMEM;
+		goto fail;
+	}
+
+	oj->input_dev = input_allocate_device();
+	if (!oj->input_dev) {
+		printk(KERN_ERR "Unable to allocate device for OJ\n");
+		err = -ENOMEM;
+		goto fail;
+	}
+
+	oj->input_dev->name = "curcial-oj";
+
+
+	oj->input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+	input_set_capability(oj->input_dev, EV_KEY, BTN_MOUSE);
+	oj->input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+
+
+	for(i = 0; i < ARRAY_SIZE(keymap); i++)
+		set_bit(keymap[i], oj->input_dev->keybit);
+
+	err = input_register_device(oj->input_dev);
+	if (err) {
+		printk(KERN_ERR "Unable to register %s input device\n", oj->input_dev->name);
+		goto fail;
+	}
+
+	if (!curcial_oj_init())
+		goto fail;
+
+	err = request_irq(my_oj->irq, curcial_oj_irq_handler,
+			  IRQF_TRIGGER_NONE, CURCIAL_OJ_NAME, oj);
+	if (err < 0) {
+		err = -ENOMEM;
+		printk(KERN_ERR "request_irq failed\n");
+		goto fail;
+	}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	oj->early_suspend.suspend = curcial_oj_early_suspend;
+	oj->early_suspend.resume = curcial_oj_late_resume;
+/*	oj->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1;*/
+	register_early_suspend(&oj->early_suspend);
+#endif
+	err = 	device_create_file(&(pdev->dev), &dev_attr_reset);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_deltax);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_deltay);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_SumDeltaX);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_SumDeltaY);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_ap_code);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_interval);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_normal_th);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_polling_delay);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_xy_ratio);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_debugflag);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_xtable);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_ytable);
+
+	normal_th = my_oj->normal_th;
+	xy_ratio = my_oj->xy_ratio;
+	interval = my_oj->interval;
+	polling_delay = my_oj->mdelay_time;
+	debugflag = my_oj->debugflag;
+	ap_code = my_oj->ap_code;
+
+	printk(KERN_INFO "OJ: driver loaded\n");
+	return 0;
+
+fail:
+	microp_spi_vote_enable(SPI_OJ, 0);
+
+	if (oj->share_power == false) {
+		oj->oj_poweron(OJ_POWEROFF);
+	}
+
+	if (oj->input_dev) {
+		input_free_device(oj->input_dev);
+	}
+
+	if (curcial_wq)
+		destroy_workqueue(curcial_wq);
+
+	if (oj_proc_entry)
+		remove_proc_entry("oj", NULL);
+
+	return err;
+}
+
+static int __devexit curcial_oj_remove(struct platform_device *pdev)
+{
+	struct curcial_oj_platform_data *oj = pdev->dev.platform_data;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	if (oj->early_suspend.suspend && oj->early_suspend.resume)
+		unregister_early_suspend(&oj->early_suspend);
+#endif
+	if (oj->share_power == false) {
+		oj->oj_poweron(OJ_POWEROFF);
+	}
+	microp_spi_vote_enable(SPI_OJ, 0);
+
+	if (oj->input_dev) {
+		input_unregister_device(oj->input_dev);
+		input_free_device(oj->input_dev);
+	}
+
+	if (curcial_wq)
+		destroy_workqueue(curcial_wq);
+
+	if (oj_proc_entry)
+		remove_proc_entry("oj", NULL);
+
+	device_remove_file(&(pdev->dev), &dev_attr_reset);
+	device_remove_file(&(pdev->dev), &dev_attr_deltax);
+	device_remove_file(&(pdev->dev), &dev_attr_deltay);
+	device_remove_file(&(pdev->dev), &dev_attr_SumDeltaX);
+	device_remove_file(&(pdev->dev), &dev_attr_SumDeltaY);
+	device_remove_file(&(pdev->dev), &dev_attr_ap_code);
+	device_remove_file(&(pdev->dev), &dev_attr_interval);
+	device_remove_file(&(pdev->dev), &dev_attr_normal_th);
+	device_remove_file(&(pdev->dev), &dev_attr_polling_delay);
+	device_remove_file(&(pdev->dev), &dev_attr_xy_ratio);
+	device_remove_file(&(pdev->dev), &dev_attr_debugflag);
+	device_remove_file(&(pdev->dev), &dev_attr_xtable);
+	device_remove_file(&(pdev->dev), &dev_attr_ytable);
+	printk(KERN_INFO "OJ: driver unloaded\n");
+	return 0;
+}
+
+static int __init curcial_oj_module_init(void)
+{
+	return platform_driver_register(&curcial_oj_device_driver);
+}
+
+static void __exit curcial_oj_module_exit(void)
+{
+	platform_driver_unregister(&curcial_oj_device_driver);
+}
+
+module_init(curcial_oj_module_init);
+module_exit(curcial_oj_module_exit);
+
+void curcial_oj_send_key(unsigned int code, int value)
+{
+	if ((my_oj != NULL) && (my_oj->input_dev != NULL))
+		input_report_key(my_oj->input_dev, code, value);
+	else
+		printk(KERN_WARNING "%s: device not ready...\n", __func__);
+}
+
+MODULE_DESCRIPTION("Crucial OpticalJoystick Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/opticaljoystick/curcial.h b/drivers/input/opticaljoystick/curcial.h
new file mode 100644
index 0000000..c5f7eda
--- /dev/null
+++ b/drivers/input/opticaljoystick/curcial.h
@@ -0,0 +1,62 @@
+/* drivers/input/opticaljoystick/curcial.h
+ *
+ * Copyright (C) 2009 HTC Corporation.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+#ifndef _CURCIAL_H
+#define _CURCIAL_H
+
+#include <linux/input.h>
+
+typedef enum {
+	OJ_KEY_RIGHT = KEY_RIGHT,
+	OJ_KEY_LEFT = KEY_LEFT,
+	OJ_KEY_UP = KEY_UP,
+	OJ_KEY_DOWN = KEY_DOWN,
+/*	OJ_KEY_CLICK = KEY_REPLY,*/
+	OJ_KEY_NONE
+}OJKeyEvt_T;
+
+typedef enum {
+	OJ_TOUCH_NONE_EVT = 0,
+	OJ_TOUCH_PRESS_EVT,
+	OJ_TOUCH_RELEASE_EVT,
+	OJ_TOUCH_CLICK_EVT
+}OJTouchEvt_T;
+
+typedef struct {
+	int8_t 	deltaX;
+	int8_t 	deltaY;
+	int8_t	shtHi;
+	int8_t	shtLo;
+	uint8_t squal;
+	uint16_t	key;
+}OJData_T;
+
+enum {
+  OJ_QUEUE_01 = 0,
+  OJ_QUEUE_02,
+  OJ_QUEUE_03,
+  OJ_QUEUE_04,
+  OJ_QUEUE_05,
+  OJ_QUEUE_MAX
+};
+
+extern OJTouchEvt_T 	OJ_SoftClick_Event(OJData_T* OJData);
+extern OJTouchEvt_T		gTouchEvt;
+extern uint8_t  gSqRatio;
+extern uint8_t  gdeltamod;
+extern uint8_t	gPressBufCnt;
+extern uint8_t  softclick;
+extern uint8_t  gDeltaU;
+extern uint8_t  gDeltaL;
+#endif
diff --git a/drivers/input/opticaljoystick/curcial_spi.c b/drivers/input/opticaljoystick/curcial_spi.c
new file mode 100644
index 0000000..dad1738
--- /dev/null
+++ b/drivers/input/opticaljoystick/curcial_spi.c
@@ -0,0 +1,824 @@
+/* drivers/input/opticaljoystick/curcial.c
+ *
+ * Copyright (C) 2009 HTC Corporation.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ *
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/earlysuspend.h>
+#include <linux/curcial_oj.h>
+#include <mach/vreg.h>
+#include <asm/mach-types.h>
+#include "curcial.h"
+
+#define OJ_POWERON                  1
+#define OJ_POWEROFF                 0
+#define CURCIAL_OJ_POWER            85
+#define BURST_DATA_SIZE             10
+#define OJ_DEVICE_ID                0x83
+#define OJ_REGISTER_WRITE           0x7B
+#define OJ_REGISTER_REQUEST         0x7C
+#define OJ_REGISTER_READ            0x7D
+#define OJ_REGISTER_BURST_REQUEST   0x7E
+#define OJ_REGISTER_BURST_READ      0x7F
+#define OJ_REGISTER_OJ_POLLING      0xA8
+#define OJ_MOTION                   0x02
+#define OJ_DELTA_Y                  0x03
+#define OJ_DELTA_X                  0x04
+#define OJ_SQUAL                    0x05
+#define OJ_SHU_HIGH                 0x06
+#define OJ_SHU_LOW                  0x07
+#define OJ_OBSERVATION              0x2E
+#define OJ_POLLING_ENABLE           1
+#define OJ_POLLING_DISABLE          0
+#define OJ_POLLING_INTERVAL         10
+#define OJ_POLLING_COUNT            10
+
+#define DELTA_SUM_TIME      40
+#define DELTA_SUM_CP        0
+#define OJ_RETRY		        5
+static const unsigned short keymap[] = {
+	 KEY_RIGHT,
+	 KEY_LEFT,
+	 KEY_UP,
+	 KEY_DOWN /*,
+	 KEY_REPLY*/
+};
+
+enum {
+	MOTION = 3,
+	Y,
+	X,
+	SQUAL,
+	SHUTTER_UPPER,
+	SHUTTER_LOWER,
+	MAXIMUM_PIXEL
+};
+extern unsigned int system_rev;
+static struct proc_dir_entry *oj_proc_entry;
+static struct workqueue_struct *curcial_wq;
+static struct curcial_oj_platform_data *my_oj;
+static uint8_t  polling_delay;/* use msleep*/
+static uint8_t  interval;
+static uint8_t  debugflag;
+static uint8_t	ap_code;
+static int16_t	mSumDeltaX;
+static int16_t	mSumDeltaY;
+static int8_t	DeltaX[64];
+static int8_t	DeltaY[64];
+static int16_t	mDeltaX;
+static int16_t	mDeltaY;
+static int8_t	normal_th;
+static int8_t	xy_ratio;
+static int8_t	ledreg;
+
+static atomic_t suspend_flag = ATOMIC_INIT(0);
+static uint16_t	index;
+static int8_t   recoveryesd;
+static unsigned OJ_RSTz;
+
+static int __devinit curcial_oj_probe(struct platform_device *pdev);
+static int __devexit curcial_oj_remove(struct platform_device *pdev);
+
+static struct platform_driver curcial_oj_device_driver = {
+  .probe    = curcial_oj_probe,
+  .remove   = __devexit_p(curcial_oj_remove),
+  .driver   = {
+		.name   = CURCIAL_OJ_NAME,
+    		.owner  = THIS_MODULE,
+	}
+};
+
+extern u8 oj_reg_read(u8 reg);
+extern u8 oj_reg_write(u8 reg, u8 val);
+extern int oj_burst_read(char *buf, int len);
+
+
+static uint8_t curcial_oj_register_read(uint8_t reg)
+{
+	uint8_t cmd;
+	cmd = oj_reg_read(reg);
+	return cmd;
+}
+
+static void curcial_oj_burst_read(uint8_t *data)
+{
+	oj_burst_read(data, BURST_DATA_SIZE);
+	oj_reg_write(0x02, 0);
+}
+
+static irqreturn_t oj_interrupt(int irq, void *data)
+{
+	struct curcial_oj_platform_data *oj =
+		(struct curcial_oj_platform_data *) data;
+
+	disable_irq_nosync(oj->irq);
+	queue_work(curcial_wq, &oj->work);
+	return IRQ_HANDLED;
+}
+
+static int curcial_oj_init(void)
+{
+	uint8_t id;
+	uint8_t i;
+
+	my_oj->oj_shutdown(0);
+	if (recoveryesd) {
+		pr_info("%s recoveryesd=1\n", __func__);
+		recoveryesd = 0;
+		mdelay(10);
+		gpio_set_value(OJ_RSTz, 0);
+		mdelay(1);
+		gpio_set_value(OJ_RSTz, 1);
+		mdelay(4);
+	}
+	mdelay(1);
+	oj_reg_write(0x3a, 0x5a);
+	mdelay(23);
+
+	/* Read from register 0x02,0x03 and 0x04 one time regardless the state of the motion pin */
+	curcial_oj_register_read(OJ_MOTION);
+	curcial_oj_register_read(OJ_DELTA_Y);
+	curcial_oj_register_read(OJ_DELTA_X);
+
+
+	for (i = 0; i < OJ_RETRY; i++) {
+		id = curcial_oj_register_read(0x00);
+		if (id == OJ_DEVICE_ID) {
+			printk(KERN_INFO"OpticalJoystick Device ID: %02x\n", OJ_DEVICE_ID);
+			id = curcial_oj_register_read(0x01);
+			printk(KERN_INFO"OJ Driver: Revision : %02x\n", id);
+			break;
+		} else {
+			printk("probe OpticalJoystick Device:retry =%d\n", i);
+		}
+	}
+	if (i == OJ_RETRY) {
+			printk("Can't probe OpticalJoystick Device: %02x!\n", id);
+			return 0;
+	}
+
+	/* Write 0x10 to register 0x1C. This will activate burst mode. */
+	oj_reg_write(0x1C, 0x10);
+
+	if (ledreg) {
+		pr_info("%s ledreg = %d\n", __func__, ledreg);
+		oj_reg_write(0x1a, ledreg);
+	}
+
+	return 1;
+}
+
+static OJKeyEvt_T OJ_ProcessNavi(int Ratio, int DeltaMin, int16_t SumDeltaX, int16_t SumDeltaY)
+{
+	OJKeyEvt_T	tmpKey;
+
+
+	if ((10*abs(SumDeltaY) > (Ratio*abs(SumDeltaX)))
+			&& (abs(SumDeltaY) > DeltaMin)) {
+		if (SumDeltaY > 0)
+			tmpKey = OJ_KEY_UP;
+		else
+			tmpKey = OJ_KEY_DOWN;
+	} else	if (abs(SumDeltaX) > DeltaMin) {
+		if (SumDeltaX > 0)
+			tmpKey = OJ_KEY_RIGHT;
+		else
+			tmpKey = OJ_KEY_LEFT;
+	} else
+		tmpKey = OJ_KEY_NONE;
+
+	return tmpKey;
+}
+
+static void curcial_oj_work_func(struct work_struct *work)
+{
+	struct curcial_oj_platform_data *oj = container_of(work, struct curcial_oj_platform_data, work);
+	OJData_T  OJData;
+	uint16_t i, j;
+	uint8_t data[BURST_DATA_SIZE];
+	uint32_t click_time = 0;
+	uint32_t delta_time = 0;
+	uint32_t entry_time = 0;
+	OJKeyEvt_T	evtKey = OJ_KEY_NONE;
+	uint8_t	x_count = 0;
+	uint8_t	y_count = 0;
+	bool out = false;
+	uint8_t pxsum;
+	uint16_t sht;
+	int16_t	x_sum, x_idx;
+	int16_t	y_sum, y_idx;
+
+
+
+	mDeltaX = 0;
+	mDeltaY = 0;
+	oj->interval = interval;
+	entry_time = jiffies_to_msecs(jiffies);
+	x_sum = 0;
+	y_sum = 0;
+
+		do {
+			memset(data, 0x00, sizeof(data));
+			out = false;
+			curcial_oj_burst_read(data);
+
+			if (data[MOTION] != 0x60 && data[MOTION] != 0xe0) {
+				pr_info("%s: OJ re-init enter\n", __func__);
+				recoveryesd = 1;
+				curcial_oj_init();
+				break;
+			}
+			OJData.squal = data[SQUAL];
+			pxsum = curcial_oj_register_read(0x09);
+			sht =	((data[SHUTTER_UPPER] << 8)|data[SHUTTER_LOWER]);
+			if (debugflag) {
+				printk(KERN_INFO"OJ1:M=0x%02x Y=0x%02x X=0x%02x SQUAL=0x%02x "
+				"SHU_U=0x%02x SHU_L=0x%02x pxsum=%d sht=%d  \n", data[MOTION], data[Y], data[X],
+				 data[SQUAL], data[SHUTTER_UPPER], data[SHUTTER_LOWER], pxsum, sht);
+			}
+			if (ap_code) {
+				for (i = 1; i < oj->degree; i++) {
+					if (((oj->sht_tbl[i-1] < sht) && (sht <= oj->sht_tbl[i])) && (oj->pxsum_tbl[i] < pxsum)) {
+						if (debugflag)
+							printk("OJ:A.code_condition:%d\n", i);
+						out = true;
+						break;
+					}
+				}
+			if (!out)
+				goto exit;
+			}
+			oj->oj_adjust_xy(&data[MOTION], &mDeltaX, &mDeltaY);
+
+
+				DeltaX[index] = (int8_t)mDeltaX;
+				DeltaY[index] = (int8_t)mDeltaY;
+			/*	printk(KERN_INFO"index=%d: DeltaX[]  = %d DeltaY[] = %d \n",index, DeltaX[index] , DeltaY[index]);*/
+				if (++index == 64)
+					index = 0;
+
+			x_sum = x_sum + mDeltaX;
+			y_sum = y_sum + mDeltaY;
+			mSumDeltaX = mSumDeltaX + mDeltaX;
+			mSumDeltaY = mSumDeltaY + mDeltaY;
+			if (debugflag)
+				printk(KERN_INFO"check:OJ:mSumDeltaX = %d mSumDeltaY = %d \n", mSumDeltaX, mSumDeltaY);
+
+			evtKey = OJ_ProcessNavi(xy_ratio, normal_th, x_sum, y_sum);
+
+			if (evtKey != OJ_KEY_NONE) {
+				click_time = jiffies_to_msecs(jiffies);
+				if (debugflag)
+					printk(KERN_INFO"click_time=%x last_click_time=%x, %x\n", click_time, oj->last_click_time, click_time-oj->last_click_time);
+
+			if (oj->last_click_time == 0) {
+				oj->last_click_time = entry_time - oj->interval;
+				oj->key = evtKey;
+				}
+
+			delta_time = 	click_time - entry_time;
+
+			/*printk(KERN_INFO"x_sum=%d y_sum=%d, delta time=%dms\n", x_sum, y_sum, delta_time);*/
+
+				if (click_time - oj->last_click_time < oj->interval) {
+					evtKey = OJ_KEY_NONE;
+
+				if (debugflag)
+						printk(KERN_INFO"interval blocking < %d\n", oj->interval);
+				} else if (click_time - oj->last_click_time < 80 && evtKey != oj->key) {
+					evtKey = OJ_KEY_NONE;
+					printk(KERN_INFO"sudden key ignore \n");
+				}
+			}
+
+			x_idx = abs(x_sum) / normal_th;
+			y_idx = abs(y_sum) / normal_th;
+			if (x_idx >= ARRAY_SIZE(oj->Xsteps))
+				x_idx = ARRAY_SIZE(oj->Xsteps) - 1;
+			if (y_idx >= ARRAY_SIZE(oj->Ysteps))
+				y_idx = ARRAY_SIZE(oj->Ysteps) - 1;
+
+			x_count = oj->Xsteps[x_idx];
+			y_count = oj->Ysteps[y_idx];
+			if (evtKey == OJ_KEY_LEFT) {
+				for (j = 0; j < x_count; j++) {
+					input_report_rel(oj->input_dev, REL_X, -1);
+					input_sync(oj->input_dev);
+				}
+				if (debugflag)
+					printk(KERN_INFO"OJ:KEY_LEFT:%d\n", x_count);
+
+			} else if (evtKey == OJ_KEY_RIGHT) {
+				for (j = 0; j < x_count; j++) {
+					input_report_rel(oj->input_dev, REL_X, 1);
+					input_sync(oj->input_dev);
+				}
+				if (debugflag)
+					printk(KERN_INFO"OJ:KEY_RIGHT:%d\n", x_count);
+
+			} else if (evtKey == OJ_KEY_DOWN) {
+				for (j = 0; j < y_count; j++) {
+					input_report_rel(oj->input_dev, REL_Y, 1);
+					input_sync(oj->input_dev);
+				}
+				if (debugflag)
+					printk(KERN_INFO"OJ:KEY_DOWN:%d\n", y_count);
+
+			} else if (evtKey == OJ_KEY_UP) {
+				for (j = 0; j < y_count; j++) {
+					input_report_rel(oj->input_dev, REL_Y, -1);
+					input_sync(oj->input_dev);
+				}
+				if (debugflag)
+					printk(KERN_INFO"OJ:KEY_UP:%d\n", y_count);
+			}
+
+			if (evtKey != OJ_KEY_NONE) {
+				oj->key = evtKey;
+				oj->last_click_time = click_time;
+				x_sum = 0;
+				y_sum = 0;
+				/*goto exit;*/
+			}
+		mDeltaX = 0;
+		mDeltaY = 0;
+		if (polling_delay)
+			hr_msleep(polling_delay);
+			} while ((data[0] & 0x80) && (!atomic_read(&suspend_flag)));
+
+
+exit:
+
+	if (debugflag)
+		printk(KERN_INFO"%s:-\n", __func__);
+
+	enable_irq(oj->irq);
+}
+
+
+static ssize_t oj_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+
+	return sprintf(buf,
+				"interval=%d normal_th=%d system_rev=%d"
+				" debugflag=%d polling_delay=%d xy_ratio=%d  ap_code=%d",
+				 interval, normal_th, system_rev, debugflag,
+				  polling_delay, xy_ratio, ap_code);
+
+}
+
+static ssize_t oj_ap_code_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+
+	ap_code = simple_strtoull(buf, NULL, 10);
+
+	return count;
+}
+static ssize_t oj_interval_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+
+	interval = simple_strtoull(buf, NULL, 10);
+
+	return count;
+}
+
+static ssize_t oj_normal_th_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+
+	normal_th = simple_strtoull(buf, NULL, 10);
+
+	return count;
+}
+static ssize_t oj_polling_delay_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+
+	polling_delay = simple_strtoull(buf, NULL, 10);
+
+	return count;
+}
+
+static ssize_t oj_xy_ratio_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+
+	xy_ratio = simple_strtoull(buf, NULL, 10);
+
+	return count;
+}
+static ssize_t oj_debugflag_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+
+	debugflag = simple_strtoull(buf, NULL, 10);
+
+	return count;
+}
+
+
+static ssize_t oj_xtable_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+
+	char *buffer, *endptr;
+	int i;
+	buffer = (char *)buf;
+
+	i = simple_strtoull(buffer, &endptr, 10);
+	buffer = endptr+1;
+
+	if (i <= 30)
+		my_oj->Xsteps[i-1] = simple_strtoull(buffer, &endptr, 10);
+
+	return count;
+}
+static ssize_t oj_ytable_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	char *buffer, *endptr;
+	int i;
+	buffer = (char *)buf;;
+
+
+	i = simple_strtoull(buffer, &endptr, 10);
+	buffer = endptr+1;
+
+	if (i <= 30)
+		my_oj->Ysteps[i-1] = simple_strtoull(buffer, &endptr, 10);
+
+	return count;
+}
+static ssize_t oj_xtable_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+	char log[128];
+	int i, p;
+
+	for (i = 0, p = 0; i < 30 ; i++)	{
+		p += sprintf(log+p, "%d,",  my_oj->Xsteps[i]);
+	}
+	return sprintf(buf, "X_table:%s\n", log);
+
+}
+static ssize_t oj_ytable_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+	char log[128];
+	int i, p;
+
+	for (i = 0, p = 0; i < 30 ; i++)	{
+		p += sprintf(log+p, "%d,",  my_oj->Ysteps[i]);
+	}
+	return sprintf(buf, "Y_table:%s\n", log);
+
+}
+static ssize_t oj_deltax_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+	char log[512];
+	uint8_t i, p;
+
+	for (i = 0, p = 0; i < 64 ; i++)	{
+		if (i == 63)
+			p += sprintf(log+p, "%d",  DeltaX[i]);
+		else
+			p += sprintf(log+p, "%d,",  DeltaX[i]);
+	}
+
+	return sprintf(buf, "%s\n", log);
+
+}
+static ssize_t oj_deltay_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+	char log[512];
+	uint8_t i, p;
+
+	for (i = 0, p = 0; i < 64 ; i++)	{
+		if (i == 63)
+			p += sprintf(log+p, "%d",  DeltaY[i]);
+		else
+			p += sprintf(log+p, "%d,",  DeltaY[i]);
+	}
+
+	return sprintf(buf, "%s\n", log);
+
+}
+static ssize_t oj_SumDeltaX_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+
+	return sprintf(buf, "%d\n", mSumDeltaX);
+
+}
+static ssize_t oj_SumDeltaY_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+
+	return sprintf(buf, "%d\n", mSumDeltaY);
+
+}
+static ssize_t oj_reset_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+		index = 0;
+		mSumDeltaX = 0;
+		mSumDeltaY = 0;
+		memset(DeltaX, 0x00, sizeof(DeltaX));
+		memset(DeltaY, 0x00, sizeof(DeltaY));
+
+	return count;
+}
+static ssize_t oj_ledreg_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+
+	ledreg = simple_strtoull(buf, NULL, 10);
+	pr_info("OJ:: ledreg change to %d\n", ledreg);
+	oj_reg_write(0x1a, ledreg);
+
+	return count;
+}
+static ssize_t oj_ledreg_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+	uint8_t regval;
+
+	regval = curcial_oj_register_read(0x1a);
+	return sprintf(buf, "ledreg:%d\n", regval);
+
+}
+static DEVICE_ATTR(reset, 0664, oj_show, oj_reset_store);
+static DEVICE_ATTR(deltax, 0444, oj_deltax_show, NULL);
+static DEVICE_ATTR(deltay, 0444, oj_deltay_show, NULL);
+static DEVICE_ATTR(SumDeltaX, 0444, oj_SumDeltaX_show, NULL);
+static DEVICE_ATTR(SumDeltaY, 0444, oj_SumDeltaY_show, NULL);
+static DEVICE_ATTR(ap_code, 0644, oj_show, oj_ap_code_store);
+static DEVICE_ATTR(interval, 0644, oj_show, oj_interval_store);
+static DEVICE_ATTR(normal_th, 0644, oj_show, oj_normal_th_store);
+static DEVICE_ATTR(polling_delay, 0644, oj_show, oj_polling_delay_store);
+static DEVICE_ATTR(xy_ratio, 0644, oj_show, oj_xy_ratio_store);
+static DEVICE_ATTR(debugflag, 0644, oj_show, oj_debugflag_store);
+static DEVICE_ATTR(xtable, 0644, oj_xtable_show, oj_xtable_store);
+static DEVICE_ATTR(ytable, 0644, oj_ytable_show, oj_ytable_store);
+static DEVICE_ATTR(ledreg, 0644, oj_ledreg_show, oj_ledreg_store);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void curcial_oj_early_suspend(struct early_suspend *h)
+{
+	struct curcial_oj_platform_data *oj;
+	atomic_set(&suspend_flag, 1);
+	oj = container_of(h, struct curcial_oj_platform_data, early_suspend);
+	printk(KERN_ERR"%s: enter\n", __func__);
+	disable_irq(oj->irq);
+	oj->oj_shutdown(1);
+
+	if (oj->share_power == false) {
+		oj->oj_poweron(OJ_POWEROFF);
+	}
+
+
+}
+
+static void curcial_oj_late_resume(struct early_suspend *h)
+{
+	struct curcial_oj_platform_data	*oj;
+	atomic_set(&suspend_flag, 0);
+	oj = container_of(h, struct curcial_oj_platform_data, early_suspend);
+	printk(KERN_ERR"%s: enter\n", __func__);
+	if (curcial_oj_init())
+		enable_irq(oj->irq);
+}
+#endif
+
+static int __devinit curcial_oj_probe(struct platform_device *pdev)
+{
+	struct curcial_oj_platform_data *oj = pdev->dev.platform_data;
+	int err;
+	int i;
+	int irq_oj = 0;
+	my_oj = oj;
+	err = -ENOMEM;
+
+	INIT_WORK(&oj->work, curcial_oj_work_func);
+
+	curcial_wq = create_singlethread_workqueue("curcial_wq");
+	if (!curcial_wq) {
+		err = -ENOMEM;
+		printk(KERN_ERR "%s: create_singlethread_worqqueue failed\n", __func__);
+		goto fail;
+	}
+
+
+	oj->input_dev = input_allocate_device();
+	if (!oj->input_dev) {
+		printk(KERN_ERR "Unable to allocate device for OJ\n");
+		err = -ENOMEM;
+		goto fail;
+	}
+
+	oj->input_dev->name = "curcial-oj";
+
+
+	oj->input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+	input_set_capability(oj->input_dev, EV_KEY, BTN_MOUSE);
+	oj->input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+
+
+	for (i = 0; i < ARRAY_SIZE(keymap); i++)
+		set_bit(keymap[i], oj->input_dev->keybit);
+
+	err = input_register_device(oj->input_dev);
+	if (err) {
+		printk(KERN_ERR "Unable to register %s input device\n", oj->input_dev->name);
+		goto fail;
+	}
+
+	OJ_RSTz = oj->rst_gpio;
+	ledreg = my_oj->ledval;
+	recoveryesd = 0;
+	if (!curcial_oj_init()) {
+	  printk(KERN_ERR "%s: curcial_oj_init failed\n", __func__);
+		goto fail;
+	}
+	err = gpio_request(oj->irq_gpio, "OJ irq");
+	if (err < 0) {
+		printk(KERN_ERR "oj: gpio_request error\n");
+		goto fail;
+	}
+
+	err = gpio_direction_input(oj->irq_gpio);
+	if (err < 0) {
+		printk(KERN_ERR "oj: gpio_drection_input eror\n");
+		gpio_free(oj->irq_gpio);
+		goto fail;
+	}
+
+	oj->irq = gpio_to_irq(oj->irq_gpio);
+	if (irq_oj < 0) {
+		printk(KERN_ERR "oj: gpio_to_irq error\n");
+		gpio_free(oj->irq_gpio);
+		goto fail;
+	}
+
+	err = request_irq(oj->irq, oj_interrupt,
+				IRQF_TRIGGER_LOW, "OJ_interrupt", oj);
+	if (err < 0) {
+		printk(KERN_ERR "%s(): request_irq  fail\n", __func__);
+		gpio_free(oj->irq_gpio);
+		goto fail;
+	}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	oj->early_suspend.suspend = curcial_oj_early_suspend;
+	oj->early_suspend.resume = curcial_oj_late_resume;
+/*	oj->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1;*/
+	register_early_suspend(&oj->early_suspend);
+#endif
+	err = 	device_create_file(&(pdev->dev), &dev_attr_reset);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_deltax);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_deltay);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_SumDeltaX);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_SumDeltaY);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_ap_code);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_interval);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_normal_th);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_polling_delay);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_xy_ratio);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_debugflag);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_xtable);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_ytable);
+	err = 	device_create_file(&(pdev->dev), &dev_attr_ledreg);
+
+	normal_th = my_oj->normal_th;
+	xy_ratio = my_oj->xy_ratio;
+	interval = my_oj->interval;
+	polling_delay = my_oj->mdelay_time;
+	debugflag = my_oj->debugflag;
+	ap_code = my_oj->ap_code;
+
+	printk(KERN_INFO "OJ: driver loaded\n");
+	return 0;
+
+fail:
+	if (oj->share_power == false)
+		oj->oj_poweron(OJ_POWEROFF);
+
+	if (irq_oj > 0)
+		free_irq(irq_oj, 0);
+
+	if (oj->input_dev)
+		input_free_device(oj->input_dev);
+
+
+	if (curcial_wq)
+		destroy_workqueue(curcial_wq);
+
+	if (oj_proc_entry)
+		remove_proc_entry("oj", NULL);
+
+	return err;
+}
+
+static int __devexit curcial_oj_remove(struct platform_device *pdev)
+{
+	struct curcial_oj_platform_data *oj = pdev->dev.platform_data;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	if (oj->early_suspend.suspend && oj->early_suspend.resume)
+		unregister_early_suspend(&oj->early_suspend);
+#endif
+	if (oj->share_power == false)
+		oj->oj_poweron(OJ_POWEROFF);
+
+	free_irq(gpio_to_irq(oj->irq_gpio), 0);
+
+	if (oj->input_dev) {
+		input_unregister_device(oj->input_dev);
+		input_free_device(oj->input_dev);
+	}
+
+	if (curcial_wq)
+		destroy_workqueue(curcial_wq);
+
+	if (oj_proc_entry)
+		remove_proc_entry("oj", NULL);
+
+
+	device_remove_file(&(pdev->dev), &dev_attr_reset);
+	device_remove_file(&(pdev->dev), &dev_attr_deltax);
+	device_remove_file(&(pdev->dev), &dev_attr_deltay);
+	device_remove_file(&(pdev->dev), &dev_attr_SumDeltaX);
+	device_remove_file(&(pdev->dev), &dev_attr_SumDeltaY);
+	device_remove_file(&(pdev->dev), &dev_attr_ap_code);
+	device_remove_file(&(pdev->dev), &dev_attr_interval);
+	device_remove_file(&(pdev->dev), &dev_attr_normal_th);
+	device_remove_file(&(pdev->dev), &dev_attr_polling_delay);
+	device_remove_file(&(pdev->dev), &dev_attr_xy_ratio);
+	device_remove_file(&(pdev->dev), &dev_attr_debugflag);
+	device_remove_file(&(pdev->dev), &dev_attr_xtable);
+	device_remove_file(&(pdev->dev), &dev_attr_ytable);
+	device_remove_file(&(pdev->dev), &dev_attr_ledreg);
+	printk(KERN_INFO "OJ: driver unloaded\n");
+	return 0;
+}
+static int __init curcial_oj_module_init(void)
+{
+	return platform_driver_register(&curcial_oj_device_driver);
+}
+
+static void __exit curcial_oj_module_exit(void)
+{
+	platform_driver_unregister(&curcial_oj_device_driver);
+}
+
+module_init(curcial_oj_module_init);
+module_exit(curcial_oj_module_exit);
+
+void curcial_oj_send_key(unsigned int code, int value)
+{
+  if ((my_oj != NULL) && (my_oj->input_dev != NULL)) {
+		input_report_key(my_oj->input_dev, code, value);
+		if (code == BTN_MOUSE)
+		  input_sync(my_oj->input_dev);
+  }
+  else
+    printk(KERN_WARNING "%s: device not ready...\n", __func__);
+}
+
+MODULE_DESCRIPTION("Crucial OpticalJoystick Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 016a618..5dc9333 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -430,6 +430,14 @@
 
 	  This driver can also be built as a module.  If so, the module
 	  will be called spi_qsd.
+
+config SPI_CRUCIAL_OJ
+        tristate "SPI support for CRUCIAL OJ"
+        default n
+	depends on ARCH_MSM7X30
+        help
+          SPI driver for CRUCIAL OJ
+
 #
 # Add new SPI master controllers in alphabetical order above this line
 #
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 5615f05..4f2b63b 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -47,6 +47,7 @@
 obj-$(CONFIG_SPI_PXA2XX_PCI)		+= spi-pxa2xx-pci.o
 obj-$(CONFIG_SPI_QSD)			+= spi_qsd.o
 obj-$(CONFIG_SPI_QUP)			+= spi_qsd.o
+obj-$(CONFIG_SPI_CRUCIAL_OJ)		+= spi_oj.o
 obj-$(CONFIG_SPI_RSPI)			+= spi-rspi.o
 obj-$(CONFIG_SPI_S3C24XX)		+= spi-s3c24xx-hw.o
 spi-s3c24xx-hw-y			:= spi-s3c24xx.o
diff --git a/drivers/spi/spi_oj.c b/drivers/spi/spi_oj.c
new file mode 100644
index 0000000..d626d89
--- /dev/null
+++ b/drivers/spi/spi_oj.c
@@ -0,0 +1,239 @@
+/* linux/driver/spi/spi_oj.c
+ *
+ *
+ * Copyright (C) 2009 HTC Corporation.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ *
+ *
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#define tSRAD	160
+#define tSWW	30
+
+#define OJ_MOTION              (26)
+#define OJ_SHUTDOWN            (35)
+#define OJ_RST                 (36)
+
+// #define OJ_DEBUG 1
+static struct spi_device        *spidev;
+struct work_struct oj_work;
+
+u8 oj_reg_read(u8 reg)
+{
+	u8 val;
+	struct spi_msg msg;
+	int err = 0;
+	if (!spidev)
+          {
+            printk(KERN_ERR "%s: spidev = 0\n", __func__);
+		return 0;
+          }
+	udelay(1);
+
+	msg.buffer[0] = reg;
+	msg.len = 0;
+	err += spi_read_write_lock(spidev, &msg, NULL, 1, 1);
+	udelay(tSRAD);
+	err += spi_read_write_lock(spidev, NULL, &val, 1, 0);
+#ifdef OJ_DEBUG
+	if (err != 0)
+		printk("oj_reg_read fail\n");
+#endif
+	return val;
+}
+EXPORT_SYMBOL(oj_reg_read);
+
+int oj_reg_write(u8 reg, u8 val)
+{
+	struct spi_msg msg;
+	int err = 0;
+	if (!spidev)
+		return 0;
+#ifdef OJ_DEBUG
+	printk(KERN_INFO "write %x to reg%x\n", val, reg);
+#endif
+	msg.buffer[0] = reg | 0x80;
+	msg.buffer[1] = val;
+	msg.len = 0;
+	udelay(1);
+	err += spi_read_write_lock(spidev, &msg, NULL, 2, 1);
+#ifdef OJ_DEBUG
+	if (err != 0)
+		printk("oj_reg_write fail\n");
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(oj_reg_write);
+
+int oj_burst_read(char *buf, int len)
+{
+	struct spi_msg msg;
+	int err = 0;
+	if (!spidev)
+		return 0;
+
+	msg.buffer[0] = 0x02;
+	msg.len = 0;
+	udelay(1);
+	err += spi_read_write_lock(spidev, &msg, NULL, 1, 1);
+	udelay(tSRAD);
+	err += spi_read_write_lock(spidev, NULL, buf, len, 0);
+#ifdef OJ_DEBUG
+	if (err != 0)
+		printk("oj_burst_read fail\n");
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(oj_burst_read);
+
+#ifdef OJ_DEBUG
+static irqreturn_t oj_interrupt(int irq, void *data)
+{
+	int motion;
+	/* printk("oj interrupt\n");*/
+	motion = gpio_get_value(OJ_MOTION);
+	if (motion)
+		return IRQ_HANDLED;
+	schedule_work(&oj_work);
+	return IRQ_HANDLED;
+}
+
+static void oj_do_work(struct work_struct *w)
+{
+	char motion_val;
+	printk(KERN_INFO "%s\n", __func__);
+	printk(KERN_INFO "reg02 = 0x%x\n", oj_reg_read(0x02));
+	printk(KERN_INFO "reg03 = 0x%x\n", oj_reg_read(0x03));
+	printk(KERN_INFO "reg04 = 0x%x\n", oj_reg_read(0x04));
+	motion_val = oj_reg_read(0x02);
+	if (motion_val & (1 << 7)) {
+		printk(KERN_INFO "reg02 = 0x%x\n", oj_reg_read(0x02));
+		printk(KERN_INFO "reg03 = 0x%x\n", oj_reg_read(0x03));
+		printk(KERN_INFO "reg04 = 0x%x\n", oj_reg_read(0x04));
+	}
+
+	printk(KERN_INFO "gpio26 = %d\n", gpio_get_value(26));
+}
+#endif
+
+static int oj_spi_probe(struct spi_device *spi)
+{
+#ifdef OJ_DEBUG
+	int irq_oj;
+	int ret;
+#endif
+	printk(KERN_INFO "%s \n", __func__);
+	spidev = spi;
+
+#ifdef OJ_DEBUG
+	gpio_set_value(OJ_RST, 0);
+	udelay(20);
+	gpio_set_value(OJ_RST, 1);
+	mdelay(10);
+	oj_reg_write(0x3a, 0x5a);
+	mdelay(23);
+	printk(KERN_INFO "reg02 = 0x%x\n", oj_reg_read(0x02));
+	printk(KERN_INFO "reg03 = 0x%x\n", oj_reg_read(0x03));
+	printk(KERN_INFO "reg04 = 0x%x\n", oj_reg_read(0x04));
+	printk(KERN_INFO "reg00 = 0x%x\n", oj_reg_read(0x00));
+	printk(KERN_INFO "gpio26 = %d\n", gpio_get_value(26));
+
+	ret = gpio_request(OJ_MOTION, "OJ irq");
+	if (ret < 0) {
+		printk(KERN_ERR "gpio_request error\n");
+		return 0;
+	}
+
+	ret = gpio_direction_input(OJ_MOTION);
+	if (ret < 0) {
+		printk(KERN_ERR "gpio_drection_input eror\n");
+		gpio_free(OJ_MOTION);
+		return 0;
+	}
+
+	irq_oj = gpio_to_irq(OJ_MOTION);
+	if (irq_oj < 0) {
+		printk(KERN_ERR "gpio_to_irq error\n");
+		gpio_free(OJ_MOTION);
+		return 0;
+	}
+
+	ret = request_irq(irq_oj, oj_interrupt,
+				IRQF_TRIGGER_FALLING, "OJ irq", NULL);
+	if (ret < 0) {
+		printk(KERN_ERR "%s(): request_irq  fail\n", __func__);
+		gpio_free(OJ_MOTION);
+	}
+	INIT_WORK(&oj_work, oj_do_work);
+#endif
+	return 0 ;
+}
+
+static int __exit oj_spi_remove(struct spi_device *spi)
+{
+	spidev = NULL;
+	return 0;
+}
+
+
+static struct spi_driver spi_oj = {
+	.driver = {
+		.name  = "spi_oj",
+		.owner = THIS_MODULE,
+	},
+	.probe         = oj_spi_probe,
+	.remove        = __exit_p(oj_spi_remove),
+};
+
+static int __init spi_oj_init(void)
+{
+	int rc;
+	rc = spi_register_driver(&spi_oj);
+	return rc;
+}
+module_init(spi_oj_init);
+
+static void __exit spi_oj_exit(void)
+{
+	spi_unregister_driver(&spi_oj);
+}
+module_exit(spi_oj_exit);
+
+#ifdef OJ_DEBUG
+static int oj_set_debug(const char *val, struct kernel_param *kp)
+{
+	printk(KERN_INFO "reg02 = 0x%x\n", oj_reg_read(0x02));
+	printk(KERN_INFO "reg03 = 0x%x\n", oj_reg_read(0x03));
+	printk(KERN_INFO "reg04 = 0x%x\n", oj_reg_read(0x04));
+	printk(KERN_INFO "reg00 = 0x%x\n", oj_reg_read(0x00));
+	printk(KERN_INFO "gpio26 = %d\n", gpio_get_value(26));
+	return 0;
+}
+
+static int oj_get_debug(char *buffer, struct kernel_param *kp)
+{
+	oj_reg_write(0x02, 0);
+	return 1;
+}
+
+//module_param_call(debug, oj_set_debug, oj_get_debug, 0, 0664);
+#endif /*OJ_DEBUG*/
diff --git a/include/linux/curcial_oj.h b/include/linux/curcial_oj.h
new file mode 100644
index 0000000..7fce58f
--- /dev/null
+++ b/include/linux/curcial_oj.h
@@ -0,0 +1,47 @@
+#ifndef _CURCIAL_OJ_H
+#define _CURCIAL_OJ_H
+#include<linux/earlysuspend.h>
+
+#define CURCIAL_OJ_NAME "curcial_oj"
+
+struct curcial_oj_platform_data {
+	struct input_dev *input_dev;
+	struct work_struct work;
+	bool click;
+	uint8_t key;
+	uint32_t last_key_time;
+	bool ap_code;
+	uint8_t degree;
+	uint8_t  debugflag;
+	uint32_t last_click_time;
+	uint16_t interval;
+	uint8_t mdelay_time;
+	int8_t normal_th;
+	int8_t xy_ratio;
+	void (*oj_shutdown)(int);
+	int (*oj_poweron)(int);
+	void(*oj_adjust_xy)(uint8_t *, int16_t *, int16_t *);
+	void (*oj_reset)(int);
+	int microp_version;
+	bool share_power;
+	bool reset_pin;
+	bool swap;
+	int x;
+	int y;
+	uint8_t Xsteps[30];
+	uint8_t Ysteps[30];
+	uint16_t sht_tbl[10];
+	uint8_t pxsum_tbl[10];
+	int irq;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct early_suspend early_suspend;
+#endif
+
+	unsigned irq_gpio;
+	unsigned rst_gpio;
+	uint8_t  ledval;
+	int device_id;
+};
+void curcial_oj_send_key(unsigned int code, int value);
+
+#endif
\ No newline at end of file
diff --git a/include/linux/gpio_event.h b/include/linux/gpio_event.h
index 2613fc5..71ee16a 100644
--- a/include/linux/gpio_event.h
+++ b/include/linux/gpio_event.h
@@ -37,6 +37,9 @@
 		     void **data, unsigned int dev, unsigned int type,
 		     unsigned int code, int value); /* out events */
 	bool no_suspend;
+#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL
+	bool oj_btn;
+#endif
 };
 
 struct gpio_event_platform_data {