diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index e9ae3d5..170681b 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -28,13 +28,6 @@
 #include <linux/cdev.h>
 #include "input-compat.h"
 
-enum evdev_clock_type {
-	EV_CLK_REAL = 0,
-	EV_CLK_MONO,
-	EV_CLK_BOOT,
-	EV_CLK_MAX
-};
-
 struct evdev {
 	int open;
 	struct input_handle handle;
@@ -57,10 +50,11 @@ struct evdev_client {
 	struct evdev *evdev;
 	struct list_head node;
 	unsigned int clk_type;
+	unsigned int if_type;
 	bool revoked;
 	unsigned long *evmasks[EV_CNT];
 	unsigned int bufsize;
-	struct input_event buffer[];
+	struct input_value buffer[];
 };
 
 static size_t evdev_get_mask_cnt(unsigned int type)
@@ -113,7 +107,7 @@ static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
 	unsigned int i, head, num;
 	unsigned int mask = client->bufsize - 1;
 	bool is_report;
-	struct input_event *ev;
+	struct input_value *ev;
 
 	BUG_ON(type == EV_SYN);
 
@@ -135,7 +129,6 @@ static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
 			continue;
 		} else if (head != i) {
 			/* move entry to fill the gap */
-			client->buffer[head].time = ev->time;
 			client->buffer[head].type = ev->type;
 			client->buffer[head].code = ev->code;
 			client->buffer[head].value = ev->value;
@@ -155,16 +148,8 @@ static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
 
 static void __evdev_queue_syn_dropped(struct evdev_client *client)
 {
-	struct input_event ev;
-	ktime_t time;
+	struct input_value ev;
 
-	time = client->clk_type == EV_CLK_REAL ?
-			ktime_get_real() :
-			client->clk_type == EV_CLK_MONO ?
-				ktime_get() :
-				ktime_get_boottime();
-
-	ev.time = ktime_to_timeval(time);
 	ev.type = EV_SYN;
 	ev.code = SYN_DROPPED;
 	ev.value = 0;
@@ -229,7 +214,7 @@ static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid)
 }
 
 static void __pass_event(struct evdev_client *client,
-			 const struct input_event *event)
+			 const struct input_value *event)
 {
 	client->buffer[client->head++] = *event;
 	client->head &= client->bufsize - 1;
@@ -241,7 +226,6 @@ static void __pass_event(struct evdev_client *client,
 		 */
 		client->tail = (client->head - 2) & (client->bufsize - 1);
 
-		client->buffer[client->tail].time = event->time;
 		client->buffer[client->tail].type = EV_SYN;
 		client->buffer[client->tail].code = SYN_DROPPED;
 		client->buffer[client->tail].value = 0;
@@ -256,19 +240,15 @@ static void __pass_event(struct evdev_client *client,
 }
 
 static void evdev_pass_values(struct evdev_client *client,
-			const struct input_value *vals, unsigned int count,
-			ktime_t *ev_time)
+			const struct input_value *vals, unsigned int count)
 {
 	struct evdev *evdev = client->evdev;
 	const struct input_value *v;
-	struct input_event event;
 	bool wakeup = false;
 
 	if (client->revoked)
 		return;
 
-	event.time = ktime_to_timeval(ev_time[client->clk_type]);
-
 	/* Interrupts are disabled, just acquire the lock. */
 	spin_lock(&client->buffer_lock);
 
@@ -284,10 +264,7 @@ static void evdev_pass_values(struct evdev_client *client,
 			wakeup = true;
 		}
 
-		event.type = v->type;
-		event.code = v->code;
-		event.value = v->value;
-		__pass_event(client, &event);
+		__pass_event(client, v);
 	}
 
 	spin_unlock(&client->buffer_lock);
@@ -304,22 +281,16 @@ static void evdev_events(struct input_handle *handle,
 {
 	struct evdev *evdev = handle->private;
 	struct evdev_client *client;
-	ktime_t ev_time[EV_CLK_MAX];
-
-	ev_time[EV_CLK_MONO] = ktime_get();
-	ev_time[EV_CLK_REAL] = ktime_mono_to_real(ev_time[EV_CLK_MONO]);
-	ev_time[EV_CLK_BOOT] = ktime_mono_to_any(ev_time[EV_CLK_MONO],
-						 TK_OFFS_BOOT);
 
 	rcu_read_lock();
 
 	client = rcu_dereference(evdev->grab);
 
 	if (client)
-		evdev_pass_values(client, vals, count, ev_time);
+		evdev_pass_values(client, vals, count);
 	else
 		list_for_each_entry_rcu(client, &evdev->client_list, node)
-			evdev_pass_values(client, vals, count, ev_time);
+			evdev_pass_values(client, vals, count);
 
 	rcu_read_unlock();
 }
@@ -498,7 +469,7 @@ static int evdev_open(struct inode *inode, struct file *file)
 	struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
 	unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
 	unsigned int size = sizeof(struct evdev_client) +
-					bufsize * sizeof(struct input_event);
+					bufsize * sizeof(struct input_value);
 	struct evdev_client *client;
 	int error;
 
@@ -533,10 +504,10 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
 {
 	struct evdev_client *client = file->private_data;
 	struct evdev *evdev = client->evdev;
-	struct input_event event;
+	struct input_value event;
 	int retval = 0;
 
-	if (count != 0 && count < input_event_size())
+	if (count != 0 && count < input_event_size(client->if_type))
 		return -EINVAL;
 
 	retval = mutex_lock_interruptible(&evdev->mutex);
@@ -548,13 +519,19 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
 		goto out;
 	}
 
-	while (retval + input_event_size() <= count) {
+	while (retval + input_event_size(client->if_type) <= count) {
 
-		if (input_event_from_user(buffer + retval, &event)) {
+		if (input_event_from_user(buffer + retval, &event,
+					client->if_type)) {
 			retval = -EFAULT;
 			goto out;
 		}
-		retval += input_event_size();
+
+		/*
+		 * We aren't interested in timestamp from userspace,
+		 * skip it if in composite interface
+		 */
+		retval += input_event_size(client->if_type);
 
 		input_inject_event(&evdev->handle,
 				   event.type, event.code, event.value);
@@ -566,7 +543,7 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
 }
 
 static int evdev_fetch_next_event(struct evdev_client *client,
-				  struct input_event *event)
+				  struct input_value *event)
 {
 	int have_event;
 
@@ -588,11 +565,11 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
 {
 	struct evdev_client *client = file->private_data;
 	struct evdev *evdev = client->evdev;
-	struct input_event event;
+	struct input_value event;
 	size_t read = 0;
 	int error;
 
-	if (count != 0 && count < input_event_size())
+	if (count != 0 && count < input_event_size(client->if_type))
 		return -EINVAL;
 
 	for (;;) {
@@ -610,13 +587,14 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
 		if (count == 0)
 			break;
 
-		while (read + input_event_size() <= count &&
+		while (read + input_event_size(client->if_type) <= count &&
 		       evdev_fetch_next_event(client, &event)) {
 
-			if (input_event_to_user(buffer + read, &event))
+			if (input_event_to_user(buffer + read, &event,
+					client->clk_type, client->if_type))
 				return -EFAULT;
 
-			read += input_event_size();
+			read += input_event_size(client->if_type);
 		}
 
 		if (read)
diff --git a/drivers/input/input-compat.c b/drivers/input/input-compat.c
index 64ca711..09162c6 100644
--- a/drivers/input/input-compat.c
+++ b/drivers/input/input-compat.c
@@ -12,56 +12,118 @@
 #include <asm/uaccess.h>
 #include "input-compat.h"
 
-#ifdef CONFIG_COMPAT
+static ktime_t input_get_time(int clk_type)
+{
+	switch (clk_type) {
+	case EV_CLK_MONO:
+		return ktime_get();
+	case EV_CLK_BOOT:
+		return ktime_get_boottime();
+	case EV_CLK_REAL:
+	default:
+		return ktime_get_real();
+	}
+}
 
 int input_event_from_user(const char __user *buffer,
-			  struct input_event *event)
+			  struct input_value *event, int if_type)
 {
-	if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
-		struct input_event_compat compat_event;
-
-		if (copy_from_user(&compat_event, buffer,
-				   sizeof(struct input_event_compat)))
-			return -EFAULT;
-
-		event->time.tv_sec = compat_event.time.tv_sec;
-		event->time.tv_usec = compat_event.time.tv_usec;
-		event->type = compat_event.type;
-		event->code = compat_event.code;
-		event->value = compat_event.value;
-
-	} else {
-		if (copy_from_user(event, buffer, sizeof(struct input_event)))
+	if (if_type == EV_IF_LEGACY) {
+#ifdef CONFIG_COMPAT
+		if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
+			struct input_event_compat compat_event;
+
+			if (copy_from_user(&compat_event, buffer,
+					   sizeof(struct input_event_compat)))
+				return -EFAULT;
+
+			event->type = compat_event.type;
+			event->code = compat_event.code;
+			event->value = compat_event.value;
+		} else {
+#endif
+			struct input_event ev;
+
+			if (copy_from_user(&ev, buffer,
+						sizeof(struct input_event)))
+				return -EFAULT;
+
+			/* drop timestamp from userspace */
+			event->type = ev.type;
+			event->code = ev.code;
+			event->value = ev.value;
+#ifdef CONFIG_COMPAT
+		}
+#endif
+	} else if (if_type == EV_IF_RAW || if_type == EV_IF_COMPOSITE) {
+		if (copy_from_user(event, buffer, sizeof(struct input_value)))
 			return -EFAULT;
-	}
+	} else
+		return -EINVAL;
 
 	return 0;
 }
 
-int input_event_to_user(char __user *buffer,
-			const struct input_event *event)
+int input_event_to_user(char __user *buffer, const struct input_value *event,
+			int clk_type, int if_type)
 {
-	if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
-		struct input_event_compat compat_event;
-
-		compat_event.time.tv_sec = event->time.tv_sec;
-		compat_event.time.tv_usec = event->time.tv_usec;
-		compat_event.type = event->type;
-		compat_event.code = event->code;
-		compat_event.value = event->value;
+	if (if_type == EV_IF_LEGACY) {
+		struct timeval timestamp = ktime_to_timeval(
+				input_get_time(clk_type));
 
-		if (copy_to_user(buffer, &compat_event,
-				 sizeof(struct input_event_compat)))
+#ifdef CONFIG_COMPAT
+		if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
+			struct input_event_compat compat_event;
+
+			compat_event.time.tv_sec = timestamp.tv_sec;
+			compat_event.time.tv_usec = timestamp.tv_usec;
+			compat_event.type = event->type;
+			compat_event.code = event->code;
+			compat_event.value = event->value;
+
+			if (copy_to_user(buffer, &compat_event,
+					 sizeof(struct input_event_compat)))
+				return -EFAULT;
+		} else {
+#endif
+			struct input_event ev;
+
+			ev.time = timestamp;
+			ev.type = event->type;
+			ev.code = event->code;
+			ev.value = event->value;
+
+			if (copy_to_user(buffer, &ev,
+					sizeof(struct input_event)))
+				return -EFAULT;
+#ifdef CONFIG_COMPAT
+		}
+#endif
+	} else if (if_type == EV_IF_RAW || if_type == EV_IF_COMPOSITE) {
+		if (copy_to_user(buffer, event, sizeof(struct input_value)))
 			return -EFAULT;
 
-	} else {
-		if (copy_to_user(buffer, event, sizeof(struct input_event)))
-			return -EFAULT;
-	}
+		if (if_type != EV_IF_RAW) {
+			/*
+			 * composite interface, send timestamp event
+			 *
+			 * s64 and input_value are the same size, use s64
+			 * directly here.
+			 */
+			s64 time = ktime_to_ns(input_get_time(clk_type));
+
+			if (copy_to_user(buffer + sizeof(struct input_value),
+						&time, sizeof(s64)))
+				return -EFAULT;
+		}
+	} else
+		return -EINVAL;
 
 	return 0;
 }
 
+#ifdef CONFIG_COMPAT
+
 int input_ff_effect_from_user(const char __user *buffer, size_t size,
 			      struct ff_effect *effect)
 {
@@ -99,24 +161,6 @@ int input_ff_effect_from_user(const char __user *buffer, size_t size,
 
 #else
 
-int input_event_from_user(const char __user *buffer,
-			 struct input_event *event)
-{
-	if (copy_from_user(event, buffer, sizeof(struct input_event)))
-		return -EFAULT;
-
-	return 0;
-}
-
-int input_event_to_user(char __user *buffer,
-			const struct input_event *event)
-{
-	if (copy_to_user(buffer, event, sizeof(struct input_event)))
-		return -EFAULT;
-
-	return 0;
-}
-
 int input_ff_effect_from_user(const char __user *buffer, size_t size,
 			      struct ff_effect *effect)
 {
diff --git a/drivers/input/input-compat.h b/drivers/input/input-compat.h
index 148f66f..4ee8095 100644
--- a/drivers/input/input-compat.h
+++ b/drivers/input/input-compat.h
@@ -65,26 +65,48 @@ struct ff_effect_compat {
 	} u;
 };
 
-static inline size_t input_event_size(void)
-{
-	return (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) ?
-		sizeof(struct input_event_compat) : sizeof(struct input_event);
-}
+#endif /* CONFIG_COMPAT */
 
-#else
+enum event_clock_type {
+	EV_CLK_REAL = 0,
+	EV_CLK_MONO,
+	EV_CLK_BOOT,
+	EV_CLK_MAX
+};
+
+enum event_if_type {
+	EV_IF_LEGACY = 0,
+	EV_IF_RAW,
+	EV_IF_COMPOSITE,
+	EV_IF_MAX
+};
 
-static inline size_t input_event_size(void)
+static inline size_t input_event_size(int if_type)
 {
-	return sizeof(struct input_event);
+	switch (if_type) {
+	case EV_IF_LEGACY:
+#ifdef CONFIG_COMPAT
+		if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME)
+			return sizeof(struct input_event_compat);
+#endif
+		return sizeof(struct input_event);
+	case EV_IF_RAW:
+		return sizeof(struct input_value);
+	case EV_IF_COMPOSITE:
+		return sizeof(struct input_composite_event);
+	default:
+		return 0;
+	}
 }
 
-#endif /* CONFIG_COMPAT */
-
 int input_event_from_user(const char __user *buffer,
-			 struct input_event *event);
+			  struct input_value *event, int if_type);
+
+int input_event_to_user(char __user *buffer, const struct input_value *event,
+			int clk_type, int if_type);
 
-int input_event_to_user(char __user *buffer,
-			const struct input_event *event);
+#define input_value_to_user(buffer, event, if_type)	\
+	input_event_to_user(buffer, event, 0, if_type)
 
 int input_ff_effect_from_user(const char __user *buffer, size_t size,
 			      struct ff_effect *effect);
diff --git a/include/linux/input.h b/include/linux/input.h
index 1e96769..9f9d551 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -25,18 +25,6 @@
 #include <linux/mod_devicetable.h>
 
 /**
- * struct input_value - input value representation
- * @type: type of value (EV_KEY, EV_ABS, etc)
- * @code: the value code
- * @value: the value
- */
-struct input_value {
-	__u16 type;
-	__u16 code;
-	__s32 value;
-};
-
-/**
  * struct input_dev - represents an input device
  * @name: name of the device
  * @phys: physical path to the device in the system hierarchy
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index 2758687..79b35ff 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -29,6 +29,23 @@ struct input_event {
 	__s32 value;
 };
 
+/**
+ * struct input_value - input value representation
+ * @type: type of value (EV_KEY, EV_ABS, etc)
+ * @code: the value code
+ * @value: the value
+ */
+struct input_value {
+	__u16 type;
+	__u16 code;
+	__s32 value;
+};
+
+struct input_composite_event {
+	struct input_value v;
+	__s64 time;
+};
+
 /*
  * Protocol version.
  */
