[RFC PATCH 0/4] CDFv3: MIPI DSI bus implementation

STALE4682d

7 messages, 2 authors, 2013-10-10 · open the first message on its own page

[RFC PATCH 0/4] CDFv3: MIPI DSI bus implementation

From: Andrzej Hajda <hidden>
Date: 2013-09-24 14:23:12

Hi Laurent,

This is my MIPI DSI bus implementation. The patchset
contains also two drivers:
- DSI bus master driver for Exynos,
- DSI slave driver for s6e8aa0 panel family.
All code has been tested on real device - Exynos 4210 based 'Trats'.

This is not final version, there are still some things to do.

DSI bus code is based on mipi-dbi-bus, with few major changes.
1. I have replaced three DBI opses:
- write_command,
- write_data,
- read_data
with one op 'transfer', this way it better fits to
MIPI DSI standard, I wonder if this cannot be adapted to DBI also.
The only things which bothers me is number of arguments,
maybe struct should be used instead.

I have added DCS helpers functions which use 'transfer' op:
- mipi_dsi_dcs_read
- mipi_dsi_dcs_write
and two macros which allow to pass variable number of bytes
as arguments, example usage in panel driver code:
- mipi_dsi_dcs_write_seq
- mipi_dsi_dcs_write_static_seq

I have added also following opses:
- set_stream - to enable/disable streaming on DSI master,
- set_power - I have temporarily dropped idea of using runtime_pm
    due to compliacted power on sequence of panel/dsi,
    which would probably require different op anyway.

struct mipi_dsi_device contains videomode and mipi_dsi_interface_params fields.
Those fields are read by opses, I wonder if it would not be better to
pass them directly to opses as arguments.

TODO:
- helpers for non-DT drivers,
- minor power management issues,
- better error handling
- ...

Regards
Andrzej

Andrzej Hajda (4):
  mipi-dsi-bus: add MIPI DSI bus support
  mipi-dsi-exynos: add driver
  panel-s6e8aa0: add driver
  ARM: dts: exynos4210-trats: add panel and dsi nodes

 arch/arm/boot/dts/exynos4210-trats.dts  |   54 ++
 drivers/video/display/Kconfig           |   14 +
 drivers/video/display/Makefile          |    3 +
 drivers/video/display/mipi-dsi-bus.c    |  332 ++++++++
 drivers/video/display/mipi-dsi-exynos.c | 1310 +++++++++++++++++++++++++++++++
 drivers/video/display/panel-s6e8aa0.c   | 1286 ++++++++++++++++++++++++++++++
 include/video/display.h                 |    3 +
 include/video/mipi-dsi-bus.h            |  144 ++++
 include/video/mipi-dsi-exynos.h         |   41 +
 include/video/panel-s6e8aa0.h           |   42 +
 10 files changed, 3229 insertions(+)
 create mode 100644 drivers/video/display/mipi-dsi-bus.c
 create mode 100644 drivers/video/display/mipi-dsi-exynos.c
 create mode 100644 drivers/video/display/panel-s6e8aa0.c
 create mode 100644 include/video/mipi-dsi-bus.h
 create mode 100644 include/video/mipi-dsi-exynos.h
 create mode 100644 include/video/panel-s6e8aa0.h

-- 
1.8.1.2

[RFC PATCH 1/4] mipi-dsi-bus: add MIPI DSI bus support

From: Andrzej Hajda <hidden>
Date: 2013-09-24 14:23:13

MIPI DSI is a high-speed serial interface to transmit
data from/to host to display module.

Signed-off-by: Andrzej Hajda <redacted>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/video/display/Kconfig        |   4 +
 drivers/video/display/Makefile       |   1 +
 drivers/video/display/mipi-dsi-bus.c | 332 +++++++++++++++++++++++++++++++++++
 include/video/display.h              |   3 +
 include/video/mipi-dsi-bus.h         | 144 +++++++++++++++
 5 files changed, 484 insertions(+)
 create mode 100644 drivers/video/display/mipi-dsi-bus.c
 create mode 100644 include/video/mipi-dsi-bus.h
diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 9b482a8..619b05d 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -20,6 +20,10 @@ config DISPLAY_MIPI_DBI
 	tristate
 	default n
 
+config DISPLAY_MIPI_DSI
+	tristate
+	default n
+
 config DISPLAY_PANEL_DPI
 	tristate "DPI (Parallel) Display Panels"
 	---help---
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index d03c64a..b323fd4 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -3,6 +3,7 @@ display-y					:= display-core.o \
 obj-$(CONFIG_DISPLAY_CORE)			+= display.o
 obj-$(CONFIG_DISPLAY_CONNECTOR_VGA)		+= con-vga.o
 obj-$(CONFIG_DISPLAY_MIPI_DBI)			+= mipi-dbi-bus.o
+obj-$(CONFIG_DISPLAY_MIPI_DSI)			+= mipi-dsi-bus.o
 obj-$(CONFIG_DISPLAY_PANEL_DPI)			+= panel-dpi.o
 obj-$(CONFIG_DISPLAY_PANEL_R61505)		+= panel-r61505.o
 obj-$(CONFIG_DISPLAY_PANEL_R61517)		+= panel-r61517.o
diff --git a/drivers/video/display/mipi-dsi-bus.c b/drivers/video/display/mipi-dsi-bus.c
new file mode 100644
index 0000000..a194d92
--- /dev/null
+++ b/drivers/video/display/mipi-dsi-bus.c
@@ -0,0 +1,332 @@
+/*
+ * MIPI DSI Bus
+ *
+ * Copyright (C) 2012, Samsung Electronics, Co., Ltd.
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <video/mipi_display.h>
+#include <video/mipi-dsi-bus.h>
+
+/* -----------------------------------------------------------------------------
+ * Bus operations
+ */
+
+int mipi_dsi_set_power(struct mipi_dsi_device *dev, bool on)
+{
+	const struct mipi_dsi_bus_ops *ops = dev->bus->ops;
+
+	if (!ops->set_power)
+		return 0;
+
+	return ops->set_power(dev->bus, dev, on);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_set_power);
+
+int mipi_dsi_set_stream(struct mipi_dsi_device *dev, bool on)
+{
+	const struct mipi_dsi_bus_ops *ops = dev->bus->ops;
+
+	if (!ops->set_stream)
+		return 0;
+
+	return ops->set_stream(dev->bus, dev, on);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_set_stream);
+
+int mipi_dsi_dcs_write(struct mipi_dsi_device *dev, int channel, const u8 *data,
+		       size_t len)
+{
+	const struct mipi_dsi_bus_ops *ops = dev->bus->ops;
+	u8 type = channel << 6;
+
+	if (!ops->transfer)
+		return -EINVAL;
+
+	switch (len) {
+	case 0:
+		return -EINVAL;
+	case 1:
+		type |= MIPI_DSI_DCS_SHORT_WRITE;
+		break;
+	case 2:
+		type |= MIPI_DSI_DCS_SHORT_WRITE_PARAM;
+		break;
+	default:
+		type |= MIPI_DSI_DCS_LONG_WRITE;
+	}
+
+	return ops->transfer(dev->bus, dev, type, data, len, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_dcs_write);
+
+int mipi_dsi_dcs_read(struct mipi_dsi_device *dev, int channel, u8 cmd,
+		      u8 *data, size_t len)
+{
+	const struct mipi_dsi_bus_ops *ops = dev->bus->ops;
+	u8 type = MIPI_DSI_DCS_READ | (channel << 6);
+
+	if (!ops->transfer)
+		return -EINVAL;
+
+	return ops->transfer(dev->bus, dev, type, &cmd, 1, data, len);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_dcs_read);
+
+/* -----------------------------------------------------------------------------
+ * Bus type
+ */
+
+static const struct mipi_dsi_device_id *
+mipi_dsi_match_id(const struct mipi_dsi_device_id *id,
+		  struct mipi_dsi_device *dev)
+{
+	while (id->name[0]) {
+		if (strcmp(dev->name, id->name) = 0) {
+			dev->id_entry = id;
+			return id;
+		}
+		id++;
+	}
+	return NULL;
+}
+
+static int mipi_dsi_match(struct device *_dev, struct device_driver *_drv)
+{
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+	struct mipi_dsi_driver *drv = to_mipi_dsi_driver(_drv);
+
+	if (of_driver_match_device(_dev, _drv))
+		return 1;
+
+	if (drv->id_table)
+		return mipi_dsi_match_id(drv->id_table, dev) != NULL;
+
+	return (strcmp(dev->name, _drv->name) = 0);
+}
+
+static ssize_t modalias_show(struct device *_dev, struct device_attribute *a,
+			     char *buf)
+{
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+	int len = snprintf(buf, PAGE_SIZE, MIPI_DSI_MODULE_PREFIX "%s\n",
+			   dev->name);
+
+	return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+
+static struct device_attribute mipi_dsi_dev_attrs[] = {
+	__ATTR_RO(modalias),
+	__ATTR_NULL,
+};
+
+static int mipi_dsi_uevent(struct device *_dev, struct kobj_uevent_env *env)
+{
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+
+	add_uevent_var(env, "MODALIAS=%s%s", MIPI_DSI_MODULE_PREFIX,
+		       dev->name);
+	return 0;
+}
+
+static const struct dev_pm_ops mipi_dsi_dev_pm_ops = {
+	.runtime_suspend = pm_generic_runtime_suspend,
+	.runtime_resume = pm_generic_runtime_resume,
+	.suspend = pm_generic_suspend,
+	.resume = pm_generic_resume,
+	.freeze = pm_generic_freeze,
+	.thaw = pm_generic_thaw,
+	.poweroff = pm_generic_poweroff,
+	.restore = pm_generic_restore,
+};
+
+static struct bus_type mipi_dsi_bus_type = {
+	.name		= "mipi-dsi",
+	.dev_attrs	= mipi_dsi_dev_attrs,
+	.match		= mipi_dsi_match,
+	.uevent		= mipi_dsi_uevent,
+	.pm		= &mipi_dsi_dev_pm_ops,
+};
+
+void mipi_dsi_dev_release(struct device *_dev)
+{
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+	kfree(dev);
+}
+
+static struct device_type mipi_dsi_dev_type = {
+	.release	= mipi_dsi_dev_release,
+};
+
+
+/* -----------------------------------------------------------------------------
+ * Device and driver (un)registration
+ */
+
+/**
+ * mipi_dsi_device_register - register a DSI device
+ * @dev: DSI device we're registering
+ */
+int mipi_dsi_device_register(struct mipi_dsi_device *dev,
+			      struct mipi_dsi_bus *bus)
+{
+	device_initialize(&dev->dev);
+
+	dev->bus = bus;
+	dev->dev.bus = &mipi_dsi_bus_type;
+	dev->dev.parent = bus->dev;
+	dev->dev.type = &mipi_dsi_dev_type;
+
+	if (dev->id != -1)
+		dev_set_name(&dev->dev, "%s.%d", dev->name,  dev->id);
+	else
+		dev_set_name(&dev->dev, "%s", dev->name);
+
+	return device_add(&dev->dev);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_device_register);
+
+/**
+ * mipi_dsi_device_unregister - unregister a DSI device
+ * @dev: DSI device we're unregistering
+ */
+void mipi_dsi_device_unregister(struct mipi_dsi_device *dev)
+{
+	device_del(&dev->dev);
+	put_device(&dev->dev);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_device_unregister);
+
+static int mipi_dsi_drv_probe(struct device *_dev)
+{
+	struct mipi_dsi_driver *drv = to_mipi_dsi_driver(_dev->driver);
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+
+	return drv->probe(dev);
+}
+
+static int mipi_dsi_drv_remove(struct device *_dev)
+{
+	struct mipi_dsi_driver *drv = to_mipi_dsi_driver(_dev->driver);
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+
+	return drv->remove(dev);
+}
+
+/**
+ * mipi_dsi_driver_register - register a driver for DSI devices
+ * @drv: DSI driver structure
+ */
+int mipi_dsi_driver_register(struct mipi_dsi_driver *drv)
+{
+	drv->driver.bus = &mipi_dsi_bus_type;
+	if (drv->probe)
+		drv->driver.probe = mipi_dsi_drv_probe;
+	if (drv->remove)
+		drv->driver.remove = mipi_dsi_drv_remove;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_driver_register);
+
+/**
+ * mipi_dsi_driver_unregister - unregister a driver for DSI devices
+ * @drv: DSI driver structure
+ */
+void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_driver_unregister);
+
+struct mipi_dsi_device *of_mipi_dsi_register_device(struct mipi_dsi_bus *bus,
+						    struct device_node *node)
+{
+	struct mipi_dsi_device *d = NULL;
+	int ret;
+
+	d = kzalloc(sizeof(*d), GFP_KERNEL);
+	if (!d)
+		return ERR_PTR(-ENOMEM);
+
+	ret = of_modalias_node(node, d->name, sizeof(d->name));
+	if (ret) {
+		dev_err(bus->dev, "modalias failure on %s\n", node->full_name);
+		goto err_free;
+	}
+
+	d->dev.of_node = of_node_get(node);
+	if (!d->dev.of_node)
+		goto err_free;
+
+	ret = mipi_dsi_device_register(d, bus);
+
+	if (!ret)
+		return d;
+
+	of_node_put(node);
+err_free:
+	kfree(d);
+
+	return ERR_PTR(ret);
+}
+
+int of_mipi_dsi_register_devices(struct mipi_dsi_bus *bus)
+{
+	struct device_node *n;
+
+	for_each_child_of_node(bus->dev->of_node, n)
+		of_mipi_dsi_register_device(bus, n);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_mipi_dsi_register_devices);
+
+static int mipi_dsi_remove_device_fn(struct device *_dev, void *priv)
+{
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+
+	mipi_dsi_device_unregister(dev);
+
+	return 0;
+}
+
+void mipi_dsi_unregister_devices(struct mipi_dsi_bus *bus)
+{
+	device_for_each_child(bus->dev, bus, mipi_dsi_remove_device_fn);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_unregister_devices);
+/* -----------------------------------------------------------------------------
+ * Init/exit
+ */
+
+static int __init mipi_dsi_init(void)
+{
+	return bus_register(&mipi_dsi_bus_type);
+}
+
+static void __exit mipi_dsi_exit(void)
+{
+	bus_unregister(&mipi_dsi_bus_type);
+}
+
+module_init(mipi_dsi_init);
+module_exit(mipi_dsi_exit)
+
+MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
+MODULE_DESCRIPTION("MIPI DSI Bus");
+MODULE_LICENSE("GPL v2");
diff --git a/include/video/display.h b/include/video/display.h
index 3138401..7faca0f 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 #include <media/media-entity.h>
 #include <video/mipi-dbi-bus.h>
+#include <video/mipi-dsi-bus.h>
 
 #define DISPLAY_PIXEL_CODING(option, type, from, to, variant) \
 	(((option) << 17) | ((type) << 13) | ((variant) << 10) | \
@@ -184,6 +185,7 @@ enum display_entity_stream_state {
 enum display_entity_interface_type {
 	DISPLAY_ENTITY_INTERFACE_DPI,
 	DISPLAY_ENTITY_INTERFACE_DBI,
+	DISPLAY_ENTITY_INTERFACE_DSI,
 	DISPLAY_ENTITY_INTERFACE_LVDS,
 	DISPLAY_ENTITY_INTERFACE_VGA,
 };
@@ -192,6 +194,7 @@ struct display_entity_interface_params {
 	enum display_entity_interface_type type;
 	union {
 		struct mipi_dbi_interface_params dbi;
+		struct mipi_dsi_interface_params dsi;
 	} p;
 };
 
diff --git a/include/video/mipi-dsi-bus.h b/include/video/mipi-dsi-bus.h
new file mode 100644
index 0000000..a78792d
--- /dev/null
+++ b/include/video/mipi-dsi-bus.h
@@ -0,0 +1,144 @@
+/*
+ * MIPI DSI Bus
+ *
+ * Copyright (C) 2013, Samsung Electronics, Co., Ltd.
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MIPI_DSI_BUS_H__
+#define __MIPI_DSI_BUS_H__
+
+#include <linux/device.h>
+#include <video/videomode.h>
+
+struct mipi_dsi_bus;
+struct mipi_dsi_device;
+
+struct mipi_dsi_bus_ops {
+	int (*set_power)(struct mipi_dsi_bus *bus, struct mipi_dsi_device *dev,
+			 bool on);
+	int (*set_stream)(struct mipi_dsi_bus *bus, struct mipi_dsi_device *dev,
+			  bool on);
+	int (*transfer)(struct mipi_dsi_bus *bus, struct mipi_dsi_device *dev,
+			u8 type, const u8 *tx_buf, size_t tx_len, u8 *rx_buf,
+			size_t rx_len);
+};
+
+#define DSI_MODE_VIDEO			(1 << 0)
+#define DSI_MODE_VIDEO_BURST		(1 << 1)
+#define DSI_MODE_VIDEO_SYNC_PULSE	(1 << 2)
+#define DSI_MODE_VIDEO_AUTO_VERT	(1 << 3)
+#define DSI_MODE_VIDEO_HSE		(1 << 4)
+#define DSI_MODE_VIDEO_HFP		(1 << 5)
+#define DSI_MODE_VIDEO_HBP		(1 << 6)
+#define DSI_MODE_VIDEO_HSA		(1 << 7)
+#define DSI_MODE_VSYNC_FLUSH		(1 << 8)
+#define DSI_MODE_EOT_PACKET		(1 << 9)
+
+enum mipi_dsi_pixel_format {
+	DSI_FMT_RGB888,
+	DSI_FMT_RGB666,
+	DSI_FMT_RGB666_PACKED,
+	DSI_FMT_RGB565,
+};
+
+struct mipi_dsi_interface_params {
+	enum mipi_dsi_pixel_format format;
+	unsigned long mode;
+	unsigned long hs_clk_freq;
+	unsigned long esc_clk_freq;
+	unsigned char data_lanes;
+	unsigned char cmd_allow;
+};
+
+struct mipi_dsi_bus {
+	struct device *dev;
+	const struct mipi_dsi_bus_ops *ops;
+};
+
+#define MIPI_DSI_MODULE_PREFIX		"mipi-dsi:"
+#define MIPI_DSI_NAME_SIZE		32
+
+struct mipi_dsi_device_id {
+	char name[MIPI_DSI_NAME_SIZE];
+	__kernel_ulong_t driver_data	/* Data private to the driver */
+			__aligned(sizeof(__kernel_ulong_t));
+};
+
+struct mipi_dsi_device {
+	char name[MIPI_DSI_NAME_SIZE];
+	int id;
+	struct device dev;
+
+	const struct mipi_dsi_device_id *id_entry;
+	struct mipi_dsi_bus *bus;
+	struct videomode vm;
+	struct mipi_dsi_interface_params params;
+};
+
+#define to_mipi_dsi_device(d)	container_of(d, struct mipi_dsi_device, dev)
+
+int mipi_dsi_device_register(struct mipi_dsi_device *dev,
+			     struct mipi_dsi_bus *bus);
+void mipi_dsi_device_unregister(struct mipi_dsi_device *dev);
+
+struct mipi_dsi_driver {
+	int(*probe)(struct mipi_dsi_device *);
+	int(*remove)(struct mipi_dsi_device *);
+	struct device_driver driver;
+	const struct mipi_dsi_device_id *id_table;
+};
+
+#define to_mipi_dsi_driver(d)	container_of(d, struct mipi_dsi_driver, driver)
+
+int mipi_dsi_driver_register(struct mipi_dsi_driver *drv);
+void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv);
+
+static inline void *mipi_dsi_get_drvdata(const struct mipi_dsi_device *dev)
+{
+	return dev_get_drvdata(&dev->dev);
+}
+
+static inline void mipi_dsi_set_drvdata(struct mipi_dsi_device *dev,
+					void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+int of_mipi_dsi_register_devices(struct mipi_dsi_bus *bus);
+void mipi_dsi_unregister_devices(struct mipi_dsi_bus *bus);
+
+/* module_mipi_dsi_driver() - Helper macro for drivers that don't do
+ * anything special in module init/exit.  This eliminates a lot of
+ * boilerplate.  Each module may only use this macro once, and
+ * calling it replaces module_init() and module_exit()
+ */
+#define module_mipi_dsi_driver(__mipi_dsi_driver) \
+	module_driver(__mipi_dsi_driver, mipi_dsi_driver_register, \
+			mipi_dsi_driver_unregister)
+
+int mipi_dsi_set_power(struct mipi_dsi_device *dev, bool on);
+int mipi_dsi_set_stream(struct mipi_dsi_device *dev, bool on);
+int mipi_dsi_dcs_write(struct mipi_dsi_device *dev, int channel, const u8 *data,
+		       size_t len);
+int mipi_dsi_dcs_read(struct mipi_dsi_device *dev, int channel, u8 cmd,
+		      u8 *data, size_t len);
+
+#define mipi_dsi_dcs_write_seq(dev, channel, seq...) \
+({\
+	const u8 d[] = { seq };\
+	BUILD_BUG_ON_MSG(ARRAY_SIZE(d) > 64, "DCS sequence too long for stack");\
+	mipi_dsi_dcs_write(dev, channel, d, ARRAY_SIZE(d));\
+})
+
+#define mipi_dsi_dcs_write_static_seq(dev, channel, seq...) \
+({\
+	static const u8 d[] = { seq };\
+	mipi_dsi_dcs_write(dev, channel, d, ARRAY_SIZE(d));\
+})
+
+#endif /* __MIPI_DSI_BUS__ */
-- 
1.8.1.2

[RFC PATCH 2/4] mipi-dsi-exynos: add driver

From: Andrzej Hajda <hidden>
Date: 2013-09-24 14:23:14

This patch adds mipi-dsi-bus master driver for Exynos chipset family.

Signed-off-by: Tomasz Figa <redacted>
Signed-off-by: Donghwa Lee <redacted>
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Andrzej Hajda <redacted>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/video/display/Kconfig           |    4 +
 drivers/video/display/Makefile          |    1 +
 drivers/video/display/mipi-dsi-exynos.c | 1310 +++++++++++++++++++++++++++++++
 include/video/mipi-dsi-exynos.h         |   41 +
 4 files changed, 1356 insertions(+)
 create mode 100644 drivers/video/display/mipi-dsi-exynos.c
 create mode 100644 include/video/mipi-dsi-exynos.h
diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 619b05d..0a1e90b 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -24,6 +24,10 @@ config DISPLAY_MIPI_DSI
 	tristate
 	default n
 
+config DISPLAY_MIPI_DSI_EXYNOS
+	select DISPLAY_MIPI_DSI
+	tristate "Samsung SoC MIPI DSI Master"
+
 config DISPLAY_PANEL_DPI
 	tristate "DPI (Parallel) Display Panels"
 	---help---
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index b323fd4..2fd84f5 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_DISPLAY_CORE)			+= display.o
 obj-$(CONFIG_DISPLAY_CONNECTOR_VGA)		+= con-vga.o
 obj-$(CONFIG_DISPLAY_MIPI_DBI)			+= mipi-dbi-bus.o
 obj-$(CONFIG_DISPLAY_MIPI_DSI)			+= mipi-dsi-bus.o
+obj-$(CONFIG_DISPLAY_MIPI_DSI_EXYNOS)		+= mipi-dsi-exynos.o
 obj-$(CONFIG_DISPLAY_PANEL_DPI)			+= panel-dpi.o
 obj-$(CONFIG_DISPLAY_PANEL_R61505)		+= panel-r61505.o
 obj-$(CONFIG_DISPLAY_PANEL_R61517)		+= panel-r61517.o
diff --git a/drivers/video/display/mipi-dsi-exynos.c b/drivers/video/display/mipi-dsi-exynos.c
new file mode 100644
index 0000000..e094744
--- /dev/null
+++ b/drivers/video/display/mipi-dsi-exynos.c
@@ -0,0 +1,1310 @@
+/*
+ * Samsung SoC MIPI DSI Master driver.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd
+ *
+ * Contacts: Tomasz Figa <t.figa@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/memory.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+#include <video/mipi_display.h>
+#include <video/mipi-dsi-bus.h>
+#include <video/mipi-dsi-exynos.h>
+#include <video/videomode.h>
+
+#define DSIM_STATUS_REG		0x0	/* Status register */
+#define DSIM_SWRST_REG		0x4	/* Software reset register */
+#define DSIM_CLKCTRL_REG	0x8	/* Clock control register */
+#define DSIM_TIMEOUT_REG	0xc	/* Time out register */
+#define DSIM_CONFIG_REG		0x10	/* Configuration register */
+#define DSIM_ESCMODE_REG	0x14	/* Escape mode register */
+
+/* Main display image resolution register */
+#define DSIM_MDRESOL_REG	0x18
+#define DSIM_MVPORCH_REG	0x1c	/* Main display Vporch register */
+#define DSIM_MHPORCH_REG	0x20	/* Main display Hporch register */
+#define DSIM_MSYNC_REG		0x24	/* Main display sync area register */
+
+/* Sub display image resolution register */
+#define DSIM_SDRESOL_REG	0x28
+#define DSIM_INTSRC_REG		0x2c	/* Interrupt source register */
+#define DSIM_INTMSK_REG		0x30	/* Interrupt mask register */
+#define DSIM_PKTHDR_REG		0x34	/* Packet Header FIFO register */
+#define DSIM_PAYLOAD_REG	0x38	/* Payload FIFO register */
+#define DSIM_RXFIFO_REG		0x3c	/* Read FIFO register */
+#define DSIM_FIFOTHLD_REG	0x40	/* FIFO threshold level register */
+#define DSIM_FIFOCTRL_REG	0x44	/* FIFO status and control register */
+
+/* FIFO memory AC characteristic register */
+#define DSIM_PLLCTRL_REG	0x4c	/* PLL control register */
+#define DSIM_PLLTMR_REG		0x50	/* PLL timer register */
+#define DSIM_PHYACCHR_REG	0x54	/* D-PHY AC characteristic register */
+#define DSIM_PHYACCHR1_REG	0x58	/* D-PHY AC characteristic register1 */
+
+/* DSIM_STATUS */
+#define DSIM_STOP_STATE_DAT(x)		(((x) & 0xf) << 0)
+#define DSIM_STOP_STATE_CLK		(1 << 8)
+#define DSIM_TX_READY_HS_CLK		(1 << 10)
+#define DSIM_PLL_STABLE			(1 << 31)
+
+/* DSIM_SWRST */
+#define DSIM_FUNCRST			(1 << 16)
+#define DSIM_SWRST			(1 << 0)
+
+/* DSIM_TIMEOUT */
+#define DSIM_LPDR_TOUT(x)		((x) << 0)
+#define DSIM_BTA_TOUT(x)		((x) << 16)
+
+/* DSIM_CLKCTRL */
+#define DSIM_ESC_PRESCALER(x)		(((x) & 0xffff) << 0)
+#define DSIM_ESC_PRESCALER_MASK		(0xffff << 0)
+#define DSIM_LANE_ESC_CLK_EN_CLK	(1 << 19)
+#define DSIM_LANE_ESC_CLK_EN_DATA(x)	(((x) & 0xf) << 20)
+#define DSIM_LANE_ESC_CLK_EN_DATA_MASK	(0xf << 20)
+#define DSIM_BYTE_CLKEN			(1 << 24)
+#define DSIM_BYTE_CLK_SRC(x)		(((x) & 0x3) << 25)
+#define DSIM_BYTE_CLK_SRC_MASK		(0x3 << 25)
+#define DSIM_PLL_BYPASS			(1 << 27)
+#define DSIM_ESC_CLKEN			(1 << 28)
+#define DSIM_TX_REQUEST_HSCLK		(1 << 31)
+
+/* DSIM_CONFIG */
+#define DSIM_LANE_EN_CLK		(1 << 0)
+#define DSIM_LANE_EN(x)			(((x) & 0xf) << 1)
+#define DSIM_NUM_OF_DATA_LANE(x)	(((x) & 0x3) << 5)
+#define DSIM_SUB_PIX_FORMAT(x)		(((x) & 0x7) << 8)
+#define DSIM_MAIN_PIX_FORMAT_MASK	(0x7 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB888	(0x7 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB666	(0x6 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB666_P	(0x5 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB565	(0x4 << 12)
+#define DSIM_SUB_VC			(((x) & 0x3) << 16)
+#define DSIM_MAIN_VC			(((x) & 0x3) << 18)
+#define DSIM_HSA_MODE			(1 << 20)
+#define DSIM_HBP_MODE			(1 << 21)
+#define DSIM_HFP_MODE			(1 << 22)
+#define DSIM_HSE_MODE			(1 << 23)
+#define DSIM_AUTO_MODE			(1 << 24)
+#define DSIM_VIDEO_MODE			(1 << 25)
+#define DSIM_BURST_MODE			(1 << 26)
+#define DSIM_SYNC_INFORM		(1 << 27)
+#define DSIM_EOT_DISABLE		(1 << 28)
+#define DSIM_MFLUSH_VS			(1 << 29)
+
+/* DSIM_ESCMODE */
+#define DSIM_TX_TRIGGER_RST		(1 << 4)
+#define DSIM_TX_LPDT_LP			(1 << 6)
+#define DSIM_CMD_LPDT_LP		(1 << 7)
+#define DSIM_FORCE_BTA			(1 << 16)
+#define DSIM_FORCE_STOP_STATE		(1 << 20)
+#define DSIM_STOP_STATE_CNT(x)		(((x) & 0x7ff) << 21)
+#define DSIM_STOP_STATE_CNT_MASK	(0x7ff << 21)
+
+/* DSIM_MDRESOL */
+#define DSIM_MAIN_STAND_BY		(1 << 31)
+#define DSIM_MAIN_VRESOL(x)		(((x) & 0x7ff) << 16)
+#define DSIM_MAIN_HRESOL(x)		(((x) & 0X7ff) << 0)
+
+/* DSIM_MVPORCH */
+#define DSIM_CMD_ALLOW(x)		((x) << 28)
+#define DSIM_STABLE_VFP(x)		((x) << 16)
+#define DSIM_MAIN_VBP(x)		((x) << 0)
+#define DSIM_CMD_ALLOW_MASK		(0xf << 28)
+#define DSIM_STABLE_VFP_MASK		(0x7ff << 16)
+#define DSIM_MAIN_VBP_MASK		(0x7ff << 0)
+
+/* DSIM_MHPORCH */
+#define DSIM_MAIN_HFP(x)		((x) << 16)
+#define DSIM_MAIN_HBP(x)		((x) << 0)
+#define DSIM_MAIN_HFP_MASK		((0xffff) << 16)
+#define DSIM_MAIN_HBP_MASK		((0xffff) << 0)
+
+/* DSIM_MSYNC */
+#define DSIM_MAIN_VSA(x)		((x) << 22)
+#define DSIM_MAIN_HSA(x)		((x) << 0)
+#define DSIM_MAIN_VSA_MASK		((0x3ff) << 22)
+#define DSIM_MAIN_HSA_MASK		((0xffff) << 0)
+
+/* DSIM_SDRESOL */
+#define DSIM_SUB_STANDY(x)		((x) << 31)
+#define DSIM_SUB_VRESOL(x)		((x) << 16)
+#define DSIM_SUB_HRESOL(x)		((x) << 0)
+#define DSIM_SUB_STANDY_MASK		((0x1) << 31)
+#define DSIM_SUB_VRESOL_MASK		((0x7ff) << 16)
+#define DSIM_SUB_HRESOL_MASK		((0x7ff) << 0)
+
+/* DSIM_INTSRC */
+#define DSIM_INT_PLL_STABLE		(1 << 31)
+#define DSIM_INT_SW_RST_RELEASE		(1 << 30)
+#define DSIM_INT_SFR_FIFO_EMPTY		(1 << 29)
+#define DSIM_INT_BTA			(1 << 25)
+#define DSIM_INT_FRAME_DONE		(1 << 24)
+#define DSIM_INT_RX_TIMEOUT		(1 << 21)
+#define DSIM_INT_BTA_TIMEOUT		(1 << 20)
+#define DSIM_INT_RX_DONE		(1 << 18)
+#define DSIM_INT_RX_TE			(1 << 17)
+#define DSIM_INT_RX_ACK			(1 << 16)
+#define DSIM_INT_RX_ECC_ERR		(1 << 15)
+#define DSIM_INT_RX_CRC_ERR		(1 << 14)
+
+/* DSIM_FIFOCTRL */
+#define DSIM_FULL_H_SFR			(1 << 23)
+
+/* DSIM_PHYACCHR */
+#define DSIM_AFC_EN			(1 << 14)
+#define DSIM_AFC_CTL(x)			(((x) & 0x7) << 5)
+
+/* DSIM_PLLCTRL */
+#define DSIM_FREQ_BAND(x)		((x) << 24)
+#define DSIM_PLL_EN			(1 << 23)
+#define DSIM_PLL_P(x)			((x) << 13)
+#define DSIM_PLL_M(x)			((x) << 4)
+#define DSIM_PLL_S(x)			((x) << 1)
+
+#define DSI_MAX_BUS_WIDTH		4
+#define DSI_NUM_VIRTUAL_CHANNELS	4
+#define DSI_TX_FIFO_SIZE		2048
+#define DSI_RX_FIFO_SIZE		256
+#define DSI_XFER_TIMEOUT_MS		100
+#define DSI_RX_FIFO_EMPTY		0x30800002
+
+enum exynos_dsi_transfer_type {
+	EXYNOS_DSI_TX,
+	EXYNOS_DSI_RX,
+};
+
+struct exynos_dsi_transfer {
+	struct list_head list;
+	struct completion completed;
+	int result;
+	u8 type;
+	u8 data[2];
+
+	const u8 *tx_payload;
+	u16 tx_len;
+	u16 tx_done;
+
+	u8 *rx_payload;
+	u16 rx_len;
+	u16 rx_done;
+};
+
+struct exynos_dsi {
+	struct mipi_dsi_bus bus;
+	struct mipi_dsi_interface_params params;
+	bool streaming;
+	bool enabled;
+
+	struct platform_device *pdev;
+	struct phy *phy;
+	struct device *dev;
+	struct resource *res;
+	struct clk *pll_clk;
+	struct clk *bus_clk;
+	unsigned int irq;
+	void __iomem *reg_base;
+	struct regulator_bulk_data supplies[2];
+	struct exynos_dsi_platform_data *pd;
+	u32 intsrc;
+	wait_queue_head_t queue;
+
+	spinlock_t transfer_lock;
+	struct list_head transfer_list;
+};
+
+#define bus_to_dsi(_bus) container_of(_bus, struct exynos_dsi, bus)
+
+/*
+ * H/W control
+ */
+
+static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi)
+{
+	int ret;
+
+	ret = wait_event_timeout(dsi->queue,
+				 (dsi->intsrc & DSIM_INT_SW_RST_RELEASE),
+				 msecs_to_jiffies(300));
+	if (ret <= 0)
+		dev_err(dsi->dev, "timeout waiting for reset\n");
+
+	dsi->intsrc = 0;
+}
+
+static void exynos_dsi_reset(struct exynos_dsi *dsi)
+{
+	writel(DSIM_SWRST, dsi->reg_base + DSIM_SWRST_REG);
+}
+
+#ifndef MHZ
+#define MHZ	(1000*1000)
+#endif
+
+static const unsigned long freq_bands[] = {
+	100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
+	270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
+	510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
+	770 * MHZ, 870 * MHZ, 950 * MHZ,
+};
+
+static const int afc_settings[] = {
+	1, 0, 3, 2, 5, 4,
+};
+
+static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi,
+		unsigned long fin, unsigned long fout, u8 *p, u16 *m, u8 *s)
+{
+	unsigned long best_freq = 0;
+	u32 min_delta = 0xffffffff;
+	u8 p_min, p_max;
+	u8 _p, uninitialized_var(best_p);
+	u16 _m, uninitialized_var(best_m);
+	u8 _s, uninitialized_var(best_s);
+
+	p_min = DIV_ROUND_UP(fin, (12 * MHZ));
+	p_max = fin / (6 * MHZ);
+
+	for (_p = p_min; _p <= p_max; ++_p) {
+		for (_s = 0; _s <= 5; ++_s) {
+			u64 tmp;
+			u32 delta;
+			u16 div;
+
+			tmp = (u64)fout * (_p << _s);
+			do_div(tmp, fin);
+			_m = tmp;
+			if (_m < 41 || _m > 125)
+				continue;
+
+			tmp = (u64)_m * fin;
+			do_div(tmp, _p);
+			if (tmp < 500 * MHZ || tmp > 1000 * MHZ)
+				continue;
+
+			tmp = (u64)_m * fin;
+			div = (_p << _s);
+			do_div(tmp, div);
+
+			delta = abs(fout - tmp);
+			if (delta < min_delta) {
+				best_p = _p;
+				best_m = _m;
+				best_s = _s;
+				min_delta = delta;
+				best_freq = tmp;
+			}
+		}
+	}
+
+	if (best_freq) {
+		*p = best_p;
+		*m = best_m;
+		*s = best_s;
+	}
+
+	return best_freq;
+}
+
+static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
+					unsigned long freq)
+{
+	unsigned long fin, fout, fin_pll;
+	int timeout;
+	u8 p;
+	u16 m;
+	u8 s;
+	int band;
+	int afc;
+	u32 reg;
+
+	clk_set_rate(dsi->pll_clk, dsi->pd->pll_clk_rate);
+
+	fin = clk_get_rate(dsi->pll_clk);
+	if (!fin) {
+		dev_err(dsi->dev, "failed to get PLL clock frequency\n");
+		return 0;
+	}
+
+	dev_dbg(dsi->dev, "PLL input frequency: %lu\n", fin);
+
+	fout = exynos_dsi_pll_find_pms(dsi, fin, freq, &p, &m, &s);
+	if (!fout) {
+		dev_err(dsi->dev,
+			"failed to find PLL coefficients for requested frequency\n");
+		return -EFAULT;
+	}
+
+	dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
+
+	for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
+		if (fout < freq_bands[band])
+			break;
+
+	fin_pll = DIV_ROUND_CLOSEST(fin, p);
+	fin_pll /= MHZ;
+	if (fin_pll > 6)
+		fin_pll -= 6;
+	else
+		fin_pll = 0;
+	if (fin_pll >= ARRAY_SIZE(afc_settings))
+		fin_pll = ARRAY_SIZE(afc_settings) - 1;
+
+	afc = afc_settings[fin_pll];
+
+	dev_dbg(dsi->dev, "freq band %d, afc_setting %d\n", band, afc);
+
+	writel(dsi->pd->pll_stable_time, dsi->reg_base + DSIM_PLLTMR_REG);
+
+	reg = DSIM_AFC_CTL(afc) | DSIM_AFC_EN;
+	writel(reg, dsi->reg_base + DSIM_PHYACCHR_REG);
+
+	reg = DSIM_FREQ_BAND(band) | DSIM_PLL_EN
+			| DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
+	writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG);
+
+	timeout = 1000;
+	do {
+		if (timeout-- = 0) {
+			dev_err(dsi->dev, "PLL failed to stabilize\n");
+			return -EFAULT;
+		}
+		reg = readl(dsi->reg_base + DSIM_STATUS_REG);
+	} while ((reg & DSIM_PLL_STABLE) = 0);
+
+	return fout;
+}
+
+static int exynos_dsi_enable_clock(struct exynos_dsi *dsi)
+{
+	struct mipi_dsi_interface_params *params = &dsi->params;
+	unsigned long hs_clk, byte_clk, esc_clk;
+	unsigned long esc_div;
+	u32 reg;
+
+	hs_clk = exynos_dsi_set_pll(dsi, params->hs_clk_freq);
+	if (!hs_clk) {
+		dev_err(dsi->dev, "failed to configure DSI PLL\n");
+		return -EFAULT;
+	}
+
+	byte_clk = hs_clk / 8;
+	esc_div = DIV_ROUND_UP(byte_clk, params->esc_clk_freq);
+	esc_clk = byte_clk / esc_div;
+
+	if (esc_clk > 20 * MHZ) {
+		++esc_div;
+		esc_clk = byte_clk / esc_div;
+	}
+
+	dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n",
+						hs_clk, byte_clk, esc_clk);
+
+	reg = readl(dsi->reg_base + DSIM_CLKCTRL_REG);
+	reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK
+			| DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS
+			| DSIM_BYTE_CLK_SRC_MASK);
+	reg |= DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN
+			| DSIM_ESC_PRESCALER(esc_div)
+			| DSIM_LANE_ESC_CLK_EN_CLK
+			| DSIM_LANE_ESC_CLK_EN_DATA(params->data_lanes)
+			| DSIM_BYTE_CLK_SRC(0)
+			| DSIM_TX_REQUEST_HSCLK;
+	writel(reg, dsi->reg_base + DSIM_CLKCTRL_REG);
+
+	return 0;
+}
+
+static void exynos_dsi_disable_clock(struct exynos_dsi *dsi)
+{
+	u32 reg;
+
+	reg = readl(dsi->reg_base + DSIM_CLKCTRL_REG);
+	reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK |
+			DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN);
+	writel(reg, dsi->reg_base + DSIM_CLKCTRL_REG);
+
+	reg = readl(dsi->reg_base + DSIM_PLLCTRL_REG);
+	reg &= ~DSIM_PLL_EN;
+	writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG);
+}
+
+static int exynos_dsi_init_link(struct exynos_dsi *dsi)
+{
+	struct mipi_dsi_interface_params *params = &dsi->params;
+	int timeout;
+	u32 reg;
+
+	/* Initialize FIFO pointers */
+	reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG);
+	reg &= ~0x1f;
+	writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG);
+
+	usleep_range(10000, 10000);
+
+	reg |= 0x1f;
+	writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG);
+
+	usleep_range(10000, 10000);
+
+	/* DSI configuration */
+	reg = 0;
+
+	if (params->mode & DSI_MODE_VIDEO) {
+		reg |= DSIM_VIDEO_MODE;
+
+		if (!(params->mode & DSI_MODE_VSYNC_FLUSH))
+			reg |= DSIM_MFLUSH_VS;
+		if (!(params->mode & DSI_MODE_EOT_PACKET))
+			reg |= DSIM_EOT_DISABLE;
+		if (params->mode & DSI_MODE_VIDEO_SYNC_PULSE)
+			reg |= DSIM_SYNC_INFORM;
+		if (params->mode & DSI_MODE_VIDEO_BURST)
+			reg |= DSIM_BURST_MODE;
+		if (params->mode & DSI_MODE_VIDEO_AUTO_VERT)
+			reg |= DSIM_AUTO_MODE;
+		if (params->mode & DSI_MODE_VIDEO_HSE)
+			reg |= DSIM_HSE_MODE;
+		if (!(params->mode & DSI_MODE_VIDEO_HFP))
+			reg |= DSIM_HFP_MODE;
+		if (!(params->mode & DSI_MODE_VIDEO_HBP))
+			reg |= DSIM_HBP_MODE;
+		if (!(params->mode & DSI_MODE_VIDEO_HSA))
+			reg |= DSIM_HSA_MODE;
+	}
+
+	switch (params->format) {
+	case DSI_FMT_RGB888:
+		reg |= DSIM_MAIN_PIX_FORMAT_RGB888;
+		break;
+	case DSI_FMT_RGB666:
+		reg |= DSIM_MAIN_PIX_FORMAT_RGB666;
+		break;
+	case DSI_FMT_RGB666_PACKED:
+		reg |= DSIM_MAIN_PIX_FORMAT_RGB666_P;
+		break;
+	case DSI_FMT_RGB565:
+		reg |= DSIM_MAIN_PIX_FORMAT_RGB565;
+		break;
+	default:
+		dev_err(dsi->dev, "invalid pixel format\n");
+		return -EINVAL;
+	}
+
+	switch (params->data_lanes) {
+	case 0x1:
+		reg |= DSIM_NUM_OF_DATA_LANE(0);
+		break;
+	case 0x3:
+		reg |= DSIM_NUM_OF_DATA_LANE(1);
+		break;
+	case 0x7:
+		reg |= DSIM_NUM_OF_DATA_LANE(2);
+		break;
+	case 0xf:
+		reg |= DSIM_NUM_OF_DATA_LANE(3);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
+
+	reg |= DSIM_LANE_EN_CLK;
+	writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
+
+	reg |= DSIM_LANE_EN(params->data_lanes);
+	writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
+
+	/* Check clock and data lane state are stop state */
+	timeout = 100;
+	do {
+		if (timeout-- = 0) {
+			dev_err(dsi->dev, "waiting for bus lanes timed out\n");
+			return -EFAULT;
+		}
+
+		reg = readl(dsi->reg_base + DSIM_STATUS_REG);
+		if ((reg & DSIM_STOP_STATE_DAT(params->data_lanes))
+		    != DSIM_STOP_STATE_DAT(params->data_lanes))
+			continue;
+	} while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK)));
+
+	reg = readl(dsi->reg_base + DSIM_ESCMODE_REG);
+	reg &= ~DSIM_STOP_STATE_CNT_MASK;
+	reg |= DSIM_STOP_STATE_CNT(dsi->pd->stop_holding_cnt);
+	writel(reg, dsi->reg_base + DSIM_ESCMODE_REG);
+
+	reg = DSIM_BTA_TOUT(dsi->pd->bta_timeout)
+					| DSIM_LPDR_TOUT(dsi->pd->rx_timeout);
+	writel(reg, dsi->reg_base + DSIM_TIMEOUT_REG);
+
+	return 0;
+}
+
+static int exynos_dsi_set_display_mode(struct exynos_dsi *dsi,
+				       const struct videomode *mode)
+{
+	struct mipi_dsi_interface_params *params = &dsi->params;
+	u32 reg;
+
+	if (params->mode & DSI_MODE_VIDEO) {
+		reg = DSIM_CMD_ALLOW(params->cmd_allow)
+			| DSIM_STABLE_VFP(mode->vfront_porch)
+			| DSIM_MAIN_VBP(mode->vback_porch);
+		writel(reg, dsi->reg_base + DSIM_MVPORCH_REG);
+
+		reg = DSIM_MAIN_HFP(mode->hfront_porch)
+			| DSIM_MAIN_HBP(mode->hback_porch);
+		writel(reg, dsi->reg_base + DSIM_MHPORCH_REG);
+
+		reg = DSIM_MAIN_VSA(mode->vsync_len)
+			| DSIM_MAIN_HSA(mode->hsync_len);
+		writel(reg, dsi->reg_base + DSIM_MSYNC_REG);
+	}
+
+	reg = DSIM_MAIN_HRESOL(mode->hactive) | DSIM_MAIN_VRESOL(mode->vactive);
+	writel(reg, dsi->reg_base + DSIM_MDRESOL_REG);
+
+	dev_dbg(dsi->dev, "LCD width = %d, height = %d\n",
+		mode->hactive, mode->vactive);
+
+	return 0;
+}
+
+static void exynos_dsi_set_display_enable(struct exynos_dsi *dsi, bool enable)
+{
+	u32 reg;
+
+	reg = readl(dsi->reg_base + DSIM_MDRESOL_REG);
+	if (enable)
+		reg |= DSIM_MAIN_STAND_BY;
+	else
+		reg &= ~DSIM_MAIN_STAND_BY;
+	writel(reg, dsi->reg_base + DSIM_MDRESOL_REG);
+}
+
+/*
+ * FIFO
+ */
+
+static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi)
+{
+	int timeout = 20000;
+
+	do {
+		u32 reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG);
+
+		if (!(reg & DSIM_FULL_H_SFR))
+			return 0;
+
+		if (!cond_resched())
+			usleep_range(950, 1050);
+	} while (--timeout);
+
+	return -ETIMEDOUT;
+}
+
+static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi,
+					struct exynos_dsi_transfer *xfer)
+{
+	const u8 *payload = xfer->tx_payload + xfer->tx_done;
+	u16 length = xfer->tx_len - xfer->tx_done;
+	bool first = !xfer->tx_done;
+	u32 reg;
+
+	dev_dbg(dsi->dev,
+		"< xfer %p, tx_len %u, tx_done %u, rx_len %u, rx_done %u\n",
+		xfer, xfer->tx_len, xfer->tx_done, xfer->rx_len, xfer->rx_done);
+
+	if (length > DSI_TX_FIFO_SIZE)
+		length = DSI_TX_FIFO_SIZE;
+
+	xfer->tx_done += length;
+
+	/* Send payload */
+	while (length >= 4) {
+		reg = (payload[3] << 24) | (payload[2] << 16)
+					| (payload[1] << 8) | payload[0];
+		writel(reg, dsi->reg_base + DSIM_PAYLOAD_REG);
+		payload += 4;
+		length -= 4;
+	}
+
+	reg = 0;
+	switch (length) {
+	case 3:
+		reg |= payload[2] << 16;
+		/* Fall through */
+	case 2:
+		reg |= payload[1] << 8;
+		/* Fall through */
+	case 1:
+		reg |= payload[0];
+		writel(reg, dsi->reg_base + DSIM_PAYLOAD_REG);
+		break;
+	case 0:
+		/* Do nothing */
+		break;
+	}
+
+	/* Send packet header */
+	if (!first)
+		return;
+
+	if (xfer->rx_len) {
+		reg = (xfer->rx_len << 8)
+				| MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE;
+		if (exynos_dsi_wait_for_hdr_fifo(dsi)) {
+			dev_err(dsi->dev,
+					"waiting for header FIFO timed out\n");
+			return;
+		}
+		writel(reg, dsi->reg_base + DSIM_PKTHDR_REG);
+	}
+
+	reg = (xfer->data[1] << 16) | (xfer->data[0] << 8) | xfer->type;
+	if (exynos_dsi_wait_for_hdr_fifo(dsi)) {
+		dev_err(dsi->dev, "waiting for header FIFO timed out\n");
+		return;
+	}
+	writel(reg, dsi->reg_base + DSIM_PKTHDR_REG);
+}
+
+static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi,
+					struct exynos_dsi_transfer *xfer)
+{
+	u8 *payload = xfer->rx_payload + xfer->rx_done;
+	bool first = !xfer->rx_done;
+	u16 length;
+	u32 reg;
+
+	if (first) {
+		reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+
+		switch (reg & 0x3f) {
+		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
+		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
+			if (xfer->rx_len >= 2) {
+				payload[1] = reg >> 16;
+				++xfer->rx_done;
+			}
+			/* Fall through */
+		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
+		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
+			payload[0] = reg >> 8;
+			++xfer->rx_done;
+			xfer->rx_len = xfer->rx_done;
+			xfer->result = 0;
+			goto clear_fifo;
+		}
+
+		length = (reg >> 8) & 0xffff;
+		if (length > xfer->rx_len) {
+			dev_err(dsi->dev,
+				"response too long (expected %u, got %u bytes)\n",
+				xfer->rx_len, length);
+			xfer->rx_len = 0;
+			xfer->rx_done = 0;
+			xfer->result = -EFAULT;
+			goto clear_fifo;
+		}
+		if (length < xfer->rx_len)
+			xfer->rx_len = length;
+	}
+
+	length = xfer->rx_len - xfer->rx_done;
+	xfer->rx_done += length;
+
+	/* Receive payload */
+	while (length >= 4) {
+		reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+		payload[0] = (reg >>  0) & 0xff;
+		payload[1] = (reg >>  8) & 0xff;
+		payload[2] = (reg >> 16) & 0xff;
+		payload[3] = (reg >> 24) & 0xff;
+		payload += 4;
+		length -= 4;
+	}
+
+	if (length) {
+		reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+		switch (length) {
+		case 3:
+			payload[2] = (reg >> 16) & 0xff;
+			/* Fall through */
+		case 2:
+			payload[1] = (reg >> 8) & 0xff;
+			/* Fall through */
+		case 1:
+			payload[0] = reg & 0xff;
+		}
+	}
+
+	if (xfer->rx_done = xfer->rx_len)
+		xfer->result = 0;
+
+clear_fifo:
+	length = DSI_RX_FIFO_SIZE / 4;
+	do {
+		reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+		if (reg = DSI_RX_FIFO_EMPTY)
+			break;
+	} while (--length);
+}
+
+/*
+ * Transfer
+ */
+
+static void exynos_dsi_transfer_start(struct exynos_dsi *dsi)
+{
+	unsigned long flags;
+	struct exynos_dsi_transfer *xfer;
+	bool start = false;
+
+again:
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	if (list_empty(&dsi->transfer_list)) {
+		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+		return;
+	}
+
+	xfer = list_first_entry(&dsi->transfer_list,
+					struct exynos_dsi_transfer, list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+	if (xfer->tx_len && xfer->tx_done = xfer->tx_len)
+		/* waiting for RX */
+		return;
+
+	exynos_dsi_send_to_fifo(dsi, xfer);
+
+	if (xfer->tx_len || xfer->rx_len)
+		return;
+
+	xfer->result = 0;
+	complete(&xfer->completed);
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	list_del_init(&xfer->list);
+	start = !list_empty(&dsi->transfer_list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+	if (start)
+		goto again;
+}
+
+static bool exynos_dsi_transfer_finish(struct exynos_dsi *dsi)
+{
+	struct exynos_dsi_transfer *xfer;
+	unsigned long flags;
+	bool start = true;
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	if (list_empty(&dsi->transfer_list)) {
+		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+		return false;
+	}
+
+	xfer = list_first_entry(&dsi->transfer_list,
+					struct exynos_dsi_transfer, list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+	dev_dbg(dsi->dev,
+		"> xfer %p, tx_len %u, tx_done %u, rx_len %u, rx_done %u\n",
+		xfer, xfer->tx_len, xfer->tx_done, xfer->rx_len, xfer->rx_done);
+
+	if (xfer->tx_done != xfer->tx_len)
+		return true;
+
+	if (xfer->rx_done != xfer->rx_len)
+		exynos_dsi_read_from_fifo(dsi, xfer);
+
+	if (xfer->rx_done != xfer->rx_len)
+		return true;
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	list_del_init(&xfer->list);
+	start = !list_empty(&dsi->transfer_list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+	if (!xfer->rx_len)
+		xfer->result = 0;
+	complete(&xfer->completed);
+
+	return start;
+}
+
+static void exynos_dsi_remove_transfer(struct exynos_dsi *dsi,
+					struct exynos_dsi_transfer *xfer)
+{
+	unsigned long flags;
+	bool start;
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	if (!list_empty(&dsi->transfer_list)
+	    && xfer = list_first_entry(&dsi->transfer_list,
+					struct exynos_dsi_transfer, list)) {
+		list_del_init(&xfer->list);
+		start = !list_empty(&dsi->transfer_list);
+		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+		if (start)
+			exynos_dsi_transfer_start(dsi);
+		return;
+	}
+
+	list_del_init(&xfer->list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+}
+
+static int exynos_dsi_transfer(struct exynos_dsi *dsi,
+					struct exynos_dsi_transfer *xfer)
+{
+	unsigned long flags;
+	bool stopped;
+
+	xfer->tx_done = 0;
+	xfer->rx_done = 0;
+	xfer->result = -ETIMEDOUT;
+	init_completion(&xfer->completed);
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	stopped = list_empty(&dsi->transfer_list);
+	list_add_tail(&xfer->list, &dsi->transfer_list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+	if (stopped)
+		exynos_dsi_transfer_start(dsi);
+
+	wait_for_completion_timeout(&xfer->completed,
+				msecs_to_jiffies(DSI_XFER_TIMEOUT_MS));
+	if (xfer->result = -ETIMEDOUT) {
+		exynos_dsi_remove_transfer(dsi, xfer);
+		dev_err(dsi->dev, "xfer timed out\n");
+		return -ETIMEDOUT;
+	}
+
+	/* Also covers hardware timeout condition */
+	return xfer->result;
+}
+
+/*
+ * Interrupt handler
+ */
+
+static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
+{
+	struct exynos_dsi *dsi = dev_id;
+	u32 status;
+
+	status = readl(dsi->reg_base + DSIM_INTSRC_REG);
+	if (!status) {
+		static unsigned long int j;
+		if (printk_timed_ratelimit(&j, 500))
+			dev_warn(dsi->dev, "spurious interrupt\n");
+		return IRQ_HANDLED;
+	}
+	writel(status, dsi->reg_base + DSIM_INTSRC_REG);
+
+	dev_dbg(dsi->dev, "%s: status = %08x\n", __func__, status);
+
+	if (status & DSIM_INT_SW_RST_RELEASE) {
+		u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY);
+		writel(mask, dsi->reg_base + DSIM_INTMSK_REG);
+		dsi->intsrc = status;
+		wake_up(&dsi->queue);
+		return IRQ_HANDLED;
+	}
+
+	if (exynos_dsi_transfer_finish(dsi))
+		exynos_dsi_transfer_start(dsi);
+
+	return IRQ_HANDLED;
+}
+
+static int exynos_dsi_set_stream(struct mipi_dsi_bus *bus,
+				 struct mipi_dsi_device *dev, bool on)
+{
+	struct exynos_dsi *dsi = bus_to_dsi(bus);
+
+	if (!dsi->enabled)
+		return -EINVAL;
+
+	if (on)
+		exynos_dsi_set_display_mode(dsi, &dev->vm);
+	exynos_dsi_set_display_enable(dsi, on);
+	dsi->streaming = on;
+
+	return 0;
+}
+
+/* enable/disable dsi bus */
+static int exynos_dsi_enable(struct exynos_dsi *dsi)
+{
+	int ret;
+
+	if (dsi->enabled)
+		return 0;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+	if (ret < 0)
+		return ret;
+
+	clk_prepare_enable(dsi->bus_clk);
+	clk_prepare_enable(dsi->pll_clk);
+
+	phy_power_on(dsi->phy);
+
+	exynos_dsi_enable_clock(dsi);
+
+	dsi->intsrc = 0;
+	exynos_dsi_reset(dsi);
+	enable_irq(dsi->irq);
+	exynos_dsi_wait_for_reset(dsi);
+
+	exynos_dsi_init_link(dsi);
+
+	dsi->enabled = true;
+
+	return 0;
+}
+
+static int exynos_dsi_disable(struct exynos_dsi *dsi)
+{
+	int ret;
+
+	if (!dsi->enabled)
+		return 0;
+
+	if (dsi->streaming)
+		return -EBUSY;
+
+	dsi->enabled = false;
+
+	exynos_dsi_disable_clock(dsi);
+
+	disable_irq(dsi->irq);
+
+	phy_power_off(dsi->phy);
+
+	clk_disable_unprepare(dsi->pll_clk);
+	clk_disable_unprepare(dsi->bus_clk);
+
+	ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int exynos_dsi_set_power(struct mipi_dsi_bus *bus,
+				struct mipi_dsi_device *dev, bool on)
+{
+	struct exynos_dsi *dsi = bus_to_dsi(bus);
+
+	if (on) {
+		dsi->params = dev->params;
+		return exynos_dsi_enable(dsi);
+	} else {
+		return exynos_dsi_disable(dsi);
+	}
+}
+
+static bool exynos_dsi_is_short_dsi_type(u8 type)
+{
+	return ((type & 0x0f) <= 8);
+}
+
+static int exynos_dsi_bus_transfer(struct mipi_dsi_bus *bus,
+				   struct mipi_dsi_device *dev, u8 type,
+				   const u8 *tx_buf, size_t tx_len,
+				   u8 *rx_buf, size_t rx_len)
+{
+	struct exynos_dsi *dsi = bus_to_dsi(bus);
+	struct exynos_dsi_transfer xfer;
+
+	if (!tx_len)
+		return -EINVAL;
+
+	xfer.type = type;
+
+	if (exynos_dsi_is_short_dsi_type(type)) {
+		if (tx_len > 2)
+			return -EINVAL;
+		xfer.tx_len = 0;
+		xfer.data[0] = tx_buf[0];
+		xfer.data[1] = (tx_len = 2) ? tx_buf[1] : 0;
+	} else {
+		xfer.tx_len = tx_len;
+		xfer.data[0] = tx_len & 0xff;
+		xfer.data[1] = tx_len >> 8;
+		xfer.tx_payload = tx_buf;
+	}
+
+	xfer.rx_len = rx_len;
+	xfer.rx_payload = rx_buf;
+
+	return exynos_dsi_transfer(dsi, &xfer);
+}
+
+static const struct mipi_dsi_bus_ops exynos_dsi_ops = {
+	.set_power = exynos_dsi_set_power,
+	.set_stream = exynos_dsi_set_stream,
+	.transfer = exynos_dsi_bus_transfer,
+};
+
+#ifdef CONFIG_OF
+/*
+ * Device Tree
+ */
+
+static struct exynos_dsi_platform_data *exynos_dsi_parse_dt(
+						struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct exynos_dsi_platform_data *dsi_pd;
+	struct device *dev = &pdev->dev;
+	const __be32 *prop_data;
+
+	dsi_pd = kzalloc(sizeof(*dsi_pd), GFP_KERNEL);
+	if (!dsi_pd) {
+		dev_err(dev, "failed to allocate dsi platform data\n");
+		return NULL;
+	}
+
+	prop_data = of_get_property(node, "samsung,pll-stable-time", NULL);
+	if (!prop_data) {
+		dev_err(dev, "failed to get pll-stable-time property\n");
+		goto err_free_pd;
+	}
+	dsi_pd->pll_stable_time = be32_to_cpu(*prop_data);
+
+	prop_data = of_get_property(node, "samsung,stop-holding-count", NULL);
+	if (!prop_data) {
+		dev_err(dev, "failed to get stop-holding-count property\n");
+		goto err_free_pd;
+	}
+	dsi_pd->stop_holding_cnt = be32_to_cpu(*prop_data);
+
+	prop_data = of_get_property(node, "samsung,bta-timeout", NULL);
+	if (!prop_data) {
+		dev_err(dev, "failed to get bta-timeout property\n");
+		goto err_free_pd;
+	}
+	dsi_pd->bta_timeout = be32_to_cpu(*prop_data);
+
+	prop_data = of_get_property(node, "samsung,rx-timeout", NULL);
+	if (!prop_data) {
+		dev_err(dev, "failed to get rx-timeout property\n");
+		goto err_free_pd;
+	}
+	dsi_pd->rx_timeout = be32_to_cpu(*prop_data);
+
+	prop_data = of_get_property(node, "samsung,pll-clk-freq", NULL);
+	if (!prop_data) {
+		dev_err(dev, "failed to get pll-clk-freq property\n");
+		goto err_free_pd;
+	}
+	dsi_pd->pll_clk_rate = be32_to_cpu(*prop_data);
+
+	return dsi_pd;
+
+err_free_pd:
+	kfree(dsi_pd);
+
+	return NULL;
+}
+
+static struct of_device_id exynos_dsi_of_match[] = {
+	{ .compatible = "samsung,exynos4210-mipi-dsi" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, exynos_dsi_of_match);
+#else
+static struct exynos_dsi_platform_data *exynos_dsi_parse_dt(
+						struct platform_device *pdev)
+{
+	return NULL;
+}
+#endif
+
+/*
+ * Platform driver
+ */
+
+static int exynos_dsi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct exynos_dsi *dsi;
+	int ret;
+
+	dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
+	if (!dsi) {
+		dev_err(&pdev->dev, "failed to allocate dsi object.\n");
+		return -ENOMEM;
+	}
+
+	init_waitqueue_head(&dsi->queue);
+	spin_lock_init(&dsi->transfer_lock);
+	INIT_LIST_HEAD(&dsi->transfer_list);
+
+	dsi->bus.ops = &exynos_dsi_ops;
+	dsi->bus.dev = &pdev->dev;
+
+	dsi->pdev = pdev;
+	dsi->dev = &pdev->dev;
+	dsi->pd = pdev->dev.platform_data;
+
+	if (dsi->pd = NULL && pdev->dev.of_node)
+		dsi->pd = exynos_dsi_parse_dt(pdev);
+
+	if (dsi->pd = NULL) {
+		dev_err(&pdev->dev, "failed to get platform data for dsi.\n");
+		return -EINVAL;
+	}
+
+	dsi->supplies[0].supply = "vdd11";
+	dsi->supplies[1].supply = "vdd18";
+	ret = devm_regulator_bulk_get(&pdev->dev,
+				ARRAY_SIZE(dsi->supplies), dsi->supplies);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to get regulators: %d\n", ret);
+		return ret;
+	}
+
+	dsi->pll_clk = devm_clk_get(&pdev->dev, "pll_clk");
+	if (IS_ERR(dsi->pll_clk)) {
+		dev_err(&pdev->dev, "failed to get dsi pll input clock\n");
+		return -ENODEV;
+	}
+
+	dsi->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
+	if (IS_ERR(dsi->bus_clk)) {
+		dev_err(&pdev->dev, "failed to get dsi bus clock\n");
+		return -ENODEV;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "failed to get io memory region\n");
+		return -ENODEV;
+	}
+
+	dsi->reg_base = devm_request_and_ioremap(&pdev->dev, res);
+	if (!dsi->reg_base) {
+		dev_err(&pdev->dev, "failed to remap io region\n");
+		return -ENOMEM;
+	}
+
+	dsi->phy = devm_phy_get(&pdev->dev, "dsim");
+	if (IS_ERR(dsi->phy))
+		return PTR_ERR(dsi->phy);
+
+	platform_set_drvdata(pdev, dsi);
+
+	dsi->irq = platform_get_irq(pdev, 0);
+	if (dsi->irq < 0) {
+		dev_err(&pdev->dev, "failed to request dsi irq resource\n");
+		return -EINVAL;
+	}
+
+	irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
+	ret = devm_request_threaded_irq(&pdev->dev, dsi->irq, NULL,
+		exynos_dsi_irq, IRQF_ONESHOT, dev_name(&pdev->dev), dsi);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to request dsi irq\n");
+		return ret;
+	}
+
+	return of_mipi_dsi_register_devices(&dsi->bus);
+}
+
+static int exynos_dsi_remove(struct platform_device *pdev)
+{
+	struct exynos_dsi *dsi = dev_get_drvdata(&pdev->dev);
+
+	mipi_dsi_unregister_devices(&dsi->bus);
+
+	return 0;
+}
+/*
+ * Power management
+ */
+
+#ifdef CONFIG_PM_SLEEP
+static int exynos_dsi_suspend(struct device *dev)
+{
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+
+	return exynos_dsi_disable(dsi);
+}
+
+static int exynos_dsi_resume(struct device *dev)
+{
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+
+	return exynos_dsi_enable(dsi);
+}
+#endif
+
+static const struct dev_pm_ops exynos_dsi_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume)
+};
+
+/*
+ * Module
+ */
+
+static struct platform_driver exynos_dsi_driver = {
+	.probe = exynos_dsi_probe,
+	.remove = exynos_dsi_remove,
+	.driver = {
+		   .name = "exynos-dsi",
+		   .owner = THIS_MODULE,
+		   .pm = &exynos_dsi_pm_ops,
+		   .of_match_table = of_match_ptr(exynos_dsi_of_match),
+	},
+};
+
+module_platform_driver(exynos_dsi_driver);
+
+MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
+MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master");
+MODULE_LICENSE("GPL");
diff --git a/include/video/mipi-dsi-exynos.h b/include/video/mipi-dsi-exynos.h
new file mode 100644
index 0000000..548c473
--- /dev/null
+++ b/include/video/mipi-dsi-exynos.h
@@ -0,0 +1,41 @@
+/* include/video/mipi-dsi-exynos.h
+ *
+ * Platform data header for Samsung SoC MIPI-DSIM.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd
+ *
+ * InKi Dae <inki.dae@samsung.com>
+ * Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef _MIPI_DSI_EXYNOS_H
+#define _MIPI_DSI_EXYNOS_H
+
+#include <linux/device.h>
+
+/*
+ * struct exynos_dsi_platform_data - interface to platform data
+ *	for mipi-dsi driver.
+ *
+ * TODO
+ */
+struct exynos_dsi_platform_data {
+	unsigned int enabled;
+
+	int (*phy_enable)(struct platform_device *pdev, bool on);
+
+	unsigned int pll_stable_time;
+	unsigned long pll_clk_rate;
+	unsigned long esc_clk_rate;
+	unsigned short stop_holding_cnt;
+	unsigned char bta_timeout;
+	unsigned short rx_timeout;
+};
+
+int s5p_dsim_phy_enable(struct platform_device *pdev, bool on);
+
+#endif /* _MIPI_DSI_EXYNOS_H */
-- 
1.8.1.2

[RFC PATCH 3/4] panel-s6e8aa0: add driver

From: Andrzej Hajda <hidden>
Date: 2013-09-24 14:23:15

The patch adds mipi-dsi-bus slave driver for
s6e8aa0 familiy panels.

Signed-off-by: Donghwa Lee <redacted>
Signed-off-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Joongmock Shin <redacted>
Signed-off-by: Eunchul Kim <redacted>
Signed-off-by: Tomasz Figa <redacted>
Signed-off-by: Andrzej Hajda <redacted>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/video/display/Kconfig         |    6 +
 drivers/video/display/Makefile        |    1 +
 drivers/video/display/panel-s6e8aa0.c | 1286 +++++++++++++++++++++++++++++++++
 include/video/panel-s6e8aa0.h         |   42 ++
 4 files changed, 1335 insertions(+)
 create mode 100644 drivers/video/display/panel-s6e8aa0.c
 create mode 100644 include/video/panel-s6e8aa0.h
diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 0a1e90b..9a9b7e9 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -67,4 +67,10 @@ config DISPLAY_VGA_DAC
 	  If you are in doubt, say N. To compile this driver as a module, choose
 	  M here: the module will be called vga-dac.
 
+config DISPLAY_PANEL_S6E8AA0
+	tristate "S6E8AA0 DSI video mode panel"
+	select BACKLIGHT_CLASS_DEVICE
+	select DISPLAY_MIPI_DSI
+	select VIDEOMODE_HELPERS
+
 endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index 2fd84f5..45225e1 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_DISPLAY_PANEL_DPI)			+= panel-dpi.o
 obj-$(CONFIG_DISPLAY_PANEL_R61505)		+= panel-r61505.o
 obj-$(CONFIG_DISPLAY_PANEL_R61517)		+= panel-r61517.o
 obj-$(CONFIG_DISPLAY_VGA_DAC)			+= vga-dac.o
+obj-$(CONFIG_DISPLAY_PANEL_S6E8AA0)		+= panel-s6e8aa0.o
diff --git a/drivers/video/display/panel-s6e8aa0.c b/drivers/video/display/panel-s6e8aa0.c
new file mode 100644
index 0000000..99246da
--- /dev/null
+++ b/drivers/video/display/panel-s6e8aa0.c
@@ -0,0 +1,1286 @@
+/* linux/drivers/video/s6e8aa0.c
+ *
+ * MIPI-DSI based s6e8aa0 AMOLED lcd 5.3 inch panel driver.
+ *
+ * Inki Dae, <inki.dae@samsung.com>
+ * Donghwa Lee, <dh09.lee@samsung.com>
+ * Joongmock Shin <jmock.shin@samsung.com>
+ * Eunchul Kim <chulspro.kim@samsung.com>
+ * Tomasz Figa <t.figa@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <linux/backlight.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/lcd.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+#include <video/mipi-dsi-bus.h>
+#include <video/of_videomode.h>
+#include <video/panel-s6e8aa0.h>
+
+#define LDI_MTP_LENGTH			24
+#define GAMMA_LEVEL_NUM			25
+#define GAMMA_TABLE_LEN			26
+#define S6E8AA0_PANEL_COND_LEN		39
+
+/* 1 */
+#define PANELCTL_SS_MASK		(1 << 5)
+#define PANELCTL_SS_1_800		(0 << 5)
+#define PANELCTL_SS_800_1		(1 << 5)
+#define PANELCTL_GTCON_MASK		(7 << 2)
+#define PANELCTL_GTCON_110		(6 << 2)
+#define PANELCTL_GTCON_111		(7 << 2)
+/* LTPS */
+/* 30 */
+#define PANELCTL_CLK1_CON_MASK		(7 << 3)
+#define PANELCTL_CLK1_000		(0 << 3)
+#define PANELCTL_CLK1_001		(1 << 3)
+#define PANELCTL_CLK2_CON_MASK		(7 << 0)
+#define PANELCTL_CLK2_000		(0 << 0)
+#define PANELCTL_CLK2_001		(1 << 0)
+/* 31 */
+#define PANELCTL_INT1_CON_MASK		(7 << 3)
+#define PANELCTL_INT1_000		(0 << 3)
+#define PANELCTL_INT1_001		(1 << 3)
+#define PANELCTL_INT2_CON_MASK		(7 << 0)
+#define PANELCTL_INT2_000		(0 << 0)
+#define PANELCTL_INT2_001		(1 << 0)
+/* 32 */
+#define PANELCTL_BICTL_CON_MASK		(7 << 3)
+#define PANELCTL_BICTL_000		(0 << 3)
+#define PANELCTL_BICTL_001		(1 << 3)
+#define PANELCTL_BICTLB_CON_MASK	(7 << 0)
+#define PANELCTL_BICTLB_000		(0 << 0)
+#define PANELCTL_BICTLB_001		(1 << 0)
+/* 36 */
+#define PANELCTL_EM_CLK1_CON_MASK	(7 << 3)
+#define PANELCTL_EM_CLK1_110		(6 << 3)
+#define PANELCTL_EM_CLK1_111		(7 << 3)
+#define PANELCTL_EM_CLK1B_CON_MASK	(7 << 0)
+#define PANELCTL_EM_CLK1B_110		(6 << 0)
+#define PANELCTL_EM_CLK1B_111		(7 << 0)
+/* 37 */
+#define PANELCTL_EM_CLK2_CON_MASK	(7 << 3)
+#define PANELCTL_EM_CLK2_110		(6 << 3)
+#define PANELCTL_EM_CLK2_111		(7 << 3)
+#define PANELCTL_EM_CLK2B_CON_MASK	(7 << 0)
+#define PANELCTL_EM_CLK2B_110		(6 << 0)
+#define PANELCTL_EM_CLK2B_111		(7 << 0)
+/* 38 */
+#define PANELCTL_EM_INT1_CON_MASK	(7 << 3)
+#define PANELCTL_EM_INT1_000		(0 << 3)
+#define PANELCTL_EM_INT1_001		(1 << 3)
+#define PANELCTL_EM_INT2_CON_MASK	(7 << 0)
+#define PANELCTL_EM_INT2_000		(0 << 0)
+#define PANELCTL_EM_INT2_001		(1 << 0)
+
+#define AID_DISABLE			(0x4)
+#define AID_1				(0x5)
+#define AID_2				(0x6)
+#define AID_3				(0x7)
+
+typedef u8 s6e8aa0_gamma_table[GAMMA_TABLE_LEN];
+
+struct s6e8aa0 {
+	struct mipi_dsi_device *dev;
+
+	struct s6e8aa0_platform_data *pdata;
+	struct backlight_device *bd;
+	struct lcd_device *ld;
+	struct regulator_bulk_data supplies[2];
+
+	unsigned int id;
+	unsigned int aid;
+	const struct s6e8aa0_variant *variant;
+
+	unsigned int reset_gpio;
+
+	int power;
+	bool streaming;
+	int brightness;
+	struct mutex mutex;
+};
+
+struct s6e8aa0_variant {
+	u8 version;
+
+	int (*panel_cond_set)(struct s6e8aa0 *);
+
+	const u8 *pentile_ctl_table;
+	size_t pentile_ctl_len;
+
+	const u8 *power_ctl_table;
+	size_t power_ctl_len;
+
+	int (*elvss_nvm_set)(struct s6e8aa0 *);
+
+	int (*brightness_set)(struct s6e8aa0 *);
+	const s6e8aa0_gamma_table *gamma_tables;
+};
+
+static void s6e8aa0_apply_level_1_key(struct s6e8aa0 *lcd)
+{
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, 0xf0, 0x5a, 0x5a);
+}
+
+static u8 s6e8aa0_apply_aid_panel_cond(unsigned int aid)
+{
+	u8 ret;
+
+	switch (aid) {
+	case AID_1:
+		ret = 0x60;
+		break;
+	case AID_2:
+		ret = 0x80;
+		break;
+	case AID_3:
+		ret = 0xA0;
+		break;
+	default:
+		ret = 0x04;
+		break;
+	}
+
+	return ret;
+}
+
+static int s6e8aa0_panel_cond_set(struct s6e8aa0 *lcd)
+{
+	return mipi_dsi_dcs_write_static_seq(lcd->dev, 0,
+		0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x94, 0x00, 0x3c,
+		0x78, 0x10, 0x27, 0x08, 0x6e, 0x00, 0x00, 0x00, 0x00,
+		0x04, 0x08, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07,
+		0x23, 0x6e, 0xc0, 0xc1, 0x01, 0x81, 0xc1, 0x00, 0xc3,
+		0xf6, 0xf6, 0xc1
+	);
+}
+
+static int s6e8aa0_panel_cond_set_v142(struct s6e8aa0 *lcd)
+{
+	u8 aid = s6e8aa0_apply_aid_panel_cond(lcd->aid);
+	u8 cfg = 0x3d;
+	u8 clk_con = 0xc8;
+	u8 int_con = 0x08;
+	u8 bictl_con = 0x48;
+	u8 em_clk1_con = 0xff;
+	u8 em_clk2_con = 0xff;
+	u8 em_int_con = 0xc8;
+
+	if (lcd->pdata->flip_vertical) {
+		/* GTCON */
+		cfg &= ~(PANELCTL_GTCON_MASK);
+		cfg |= (PANELCTL_GTCON_110);
+	}
+
+	if (lcd->pdata->flip_horizontal) {
+		/* SS */
+		cfg &= ~(PANELCTL_SS_MASK);
+		cfg |= (PANELCTL_SS_1_800);
+	}
+
+	if (lcd->pdata->flip_horizontal || lcd->pdata->flip_vertical) {
+		/* CLK1,2_CON */
+		clk_con &= ~(PANELCTL_CLK1_CON_MASK |
+			PANELCTL_CLK2_CON_MASK);
+		clk_con |= (PANELCTL_CLK1_000 | PANELCTL_CLK2_001);
+
+		/* INT1,2_CON */
+		int_con &= ~(PANELCTL_INT1_CON_MASK |
+			PANELCTL_INT2_CON_MASK);
+		int_con |= (PANELCTL_INT1_000 | PANELCTL_INT2_001);
+
+		/* BICTL,B_CON */
+		bictl_con &= ~(PANELCTL_BICTL_CON_MASK |
+			PANELCTL_BICTLB_CON_MASK);
+		bictl_con |= (PANELCTL_BICTL_000 |
+			PANELCTL_BICTLB_001);
+
+		/* EM_CLK1,1B_CON */
+		em_clk1_con &= ~(PANELCTL_EM_CLK1_CON_MASK |
+			PANELCTL_EM_CLK1B_CON_MASK);
+		em_clk1_con |= (PANELCTL_EM_CLK1_110 |
+			PANELCTL_EM_CLK1B_110);
+
+		/* EM_CLK2,2B_CON */
+		em_clk2_con &= ~(PANELCTL_EM_CLK2_CON_MASK |
+			PANELCTL_EM_CLK2B_CON_MASK);
+		em_clk2_con |= (PANELCTL_EM_CLK2_110 |
+			PANELCTL_EM_CLK2B_110);
+
+		/* EM_INT1,2_CON */
+		em_int_con &= ~(PANELCTL_EM_INT1_CON_MASK |
+			PANELCTL_EM_INT2_CON_MASK);
+		em_int_con |= (PANELCTL_EM_INT1_000 |
+			PANELCTL_EM_INT2_001);
+	}
+
+	return mipi_dsi_dcs_write_seq(lcd->dev, 0, 0xf8, cfg, 0x35, 0x00, 0x00,
+		0x00, 0x93, 0x00, 0x3c, 0x78, 0x08, 0x27, 0x7d, 0x3f, 0x00,
+		0x00, 0x00, 0x20, aid, 0x08, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x07,
+		0x07, 0x23, 0x23, 0xc0, clk_con, int_con, bictl_con, 0xc1, 0x00,
+		0xc1, em_clk1_con, em_clk2_con, em_int_con);
+}
+
+static void s6e8aa0_display_condition_set(struct s6e8aa0 *lcd)
+{
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, 0xf2, 0x80, 0x03, 0x0d);
+}
+
+static void s6e8aa0_etc_source_control(struct s6e8aa0 *lcd)
+{
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, 0xf6, 0x00, 0x02, 0x00);
+}
+
+static u8 s6e8aa0_pentile_ctl_table[] = {
+	0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0, 0x00
+};
+
+static u8 s6e8aa0_pentile_ctl_table_v32[] = {
+	0xb6, 0x0c, 0x02, 0x03, 0x32, 0xc0, 0x44, 0x44, 0xc0, 0x00
+};
+
+static int s6e8aa0_etc_pentile_control(struct s6e8aa0 *lcd)
+{
+	const struct s6e8aa0_variant *v = lcd->variant;
+
+	return mipi_dsi_dcs_write(lcd->dev, 0, v->pentile_ctl_table,
+				  v->pentile_ctl_len);
+}
+
+static const u8 s6e8aa0_power_ctl_table[] = {
+	0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x1e, 0x33, 0x02
+};
+
+static const u8 s6e8aa0_power_ctl_table_v32[] = {
+	0xf4, 0xcf, 0x0a, 0x15, 0x10, 0x19, 0x33, 0x02
+};
+
+static int s6e8aa0_etc_power_control(struct s6e8aa0 *lcd)
+{
+	const struct s6e8aa0_variant *v = lcd->variant;
+
+	return mipi_dsi_dcs_write(lcd->dev, 0, v->power_ctl_table,
+				  v->power_ctl_len);
+}
+
+static int s6e8aa0_etc_elvss_control(struct s6e8aa0 *lcd)
+{
+	u8 id = !lcd->id ? 0x95 : 0;
+
+	return mipi_dsi_dcs_write_seq(lcd->dev, 0, 0xb1, 0x04, id);
+}
+
+static int s6e8aa0_elvss_nvm_set(struct s6e8aa0 *lcd)
+{
+	return mipi_dsi_dcs_write_static_seq(lcd->dev, 0,
+		0xd9, 0x14, 0x40, 0x0c, 0xcb, 0xce, 0x6e, 0xc4, 0x07,
+		0x40, 0x41, 0xc1, 0x00, 0x60, 0x19);
+};
+
+static int s6e8aa0_elvss_nvm_set_v142(struct s6e8aa0 *lcd)
+{
+	u8 br;
+
+	switch (lcd->brightness) {
+	case 0 ... 6: /* 30cd ~ 100cd */
+		br = 0xdf;
+		break;
+	case 7 ... 11: /* 120cd ~ 150cd */
+		br = 0xdd;
+		break;
+	case 12 ... 15: /* 180cd ~ 210cd */
+	default:
+		br = 0xd9;
+		break;
+	case 16 ... 24: /* 240cd ~ 300cd */
+		br = 0xd0;
+		break;
+	}
+
+	return mipi_dsi_dcs_write_seq(lcd->dev, 0, 0xd9, 0x14, 0x40, 0x0c, 0xcb,
+		0xce, 0x6e, 0xc4, 0x0f, 0x40, 0x41, br, 0x00, 0x60, 0x19);
+}
+
+static void s6e8aa0_apply_level_2_key(struct s6e8aa0 *lcd)
+{
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, 0xfc, 0x5a, 0x5a);
+}
+
+static void s6e8aa0_enable_mtp_register(struct s6e8aa0 *lcd)
+{
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, 0xf1, 0x5a, 0x5a);
+}
+
+static void s6e8aa0_disable_mtp_register(struct s6e8aa0 *lcd)
+{
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, 0xf1, 0xa5, 0xa5);
+}
+
+static void s6e8aa0_read_id(struct s6e8aa0 *lcd, u8 *mtp_id)
+{
+	unsigned int addr = 0xd1;	/* MTP ID */
+
+	mipi_dsi_dcs_read(lcd->dev, 0, addr, mtp_id, 3);
+}
+
+static unsigned int s6e8aa0_read_mtp(struct s6e8aa0 *lcd, u8 *mtp_data)
+{
+	unsigned int ret;
+	unsigned int addr = 0xd3;	/* MTP addr */
+
+	s6e8aa0_enable_mtp_register(lcd);
+
+	ret = mipi_dsi_dcs_read(lcd->dev, 0, addr, mtp_data, LDI_MTP_LENGTH);
+	if (ret)
+		dev_err(&lcd->dev->dev, "%s: dsi read error\n", __func__);
+
+	s6e8aa0_disable_mtp_register(lcd);
+
+	return ret;
+}
+
+static const s6e8aa0_gamma_table s6e8aa0_gamma_tables_v142[GAMMA_LEVEL_NUM] = {
+	{
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0x62, 0x55, 0x55,
+		0xaf, 0xb1, 0xb1, 0xbd, 0xce, 0xb7, 0x9a, 0xb1,
+		0x90, 0xb2, 0xc4, 0xae, 0x00, 0x60, 0x00, 0x40,
+		0x00, 0x70,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0x74, 0x68, 0x69,
+		0xb8, 0xc1, 0xb7, 0xbd, 0xcd, 0xb8, 0x93, 0xab,
+		0x88, 0xb4, 0xc4, 0xb1, 0x00, 0x6b, 0x00, 0x4d,
+		0x00, 0x7d,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0x95, 0x8a, 0x89,
+		0xb4, 0xc6, 0xb2, 0xc5, 0xd2, 0xbf, 0x90, 0xa8,
+		0x85, 0xb5, 0xc4, 0xb3, 0x00, 0x7b, 0x00, 0x5d,
+		0x00, 0x8f,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0x9f, 0x98, 0x92,
+		0xb3, 0xc4, 0xb0, 0xbc, 0xcc, 0xb4, 0x91, 0xa6,
+		0x87, 0xb5, 0xc5, 0xb4, 0x00, 0x87, 0x00, 0x6a,
+		0x00, 0x9e,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0x99, 0x93, 0x8b,
+		0xb2, 0xc2, 0xb0, 0xbd, 0xce, 0xb4, 0x90, 0xa6,
+		0x87, 0xb3, 0xc3, 0xb2, 0x00, 0x8d, 0x00, 0x70,
+		0x00, 0xa4,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa7, 0xa5, 0x99,
+		0xb2, 0xc2, 0xb0, 0xbb, 0xcd, 0xb1, 0x93, 0xa7,
+		0x8a, 0xb2, 0xc1, 0xb0, 0x00, 0x92, 0x00, 0x75,
+		0x00, 0xaa,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa0, 0xa0, 0x93,
+		0xb6, 0xc4, 0xb4, 0xb5, 0xc8, 0xaa, 0x94, 0xa9,
+		0x8c, 0xb2, 0xc0, 0xb0, 0x00, 0x97, 0x00, 0x7a,
+		0x00, 0xaf,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa3, 0xa7, 0x96,
+		0xb3, 0xc2, 0xb0, 0xba, 0xcb, 0xb0, 0x94, 0xa8,
+		0x8c, 0xb0, 0xbf, 0xaf, 0x00, 0x9f, 0x00, 0x83,
+		0x00, 0xb9,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0x9d, 0xa2, 0x90,
+		0xb6, 0xc5, 0xb3, 0xb8, 0xc9, 0xae, 0x94, 0xa8,
+		0x8d, 0xaf, 0xbd, 0xad, 0x00, 0xa4, 0x00, 0x88,
+		0x00, 0xbf,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa6, 0xac, 0x97,
+		0xb4, 0xc4, 0xb1, 0xbb, 0xcb, 0xb2, 0x93, 0xa7,
+		0x8d, 0xae, 0xbc, 0xad, 0x00, 0xa7, 0x00, 0x8c,
+		0x00, 0xc3,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa2, 0xa9, 0x93,
+		0xb6, 0xc5, 0xb2, 0xba, 0xc9, 0xb0, 0x93, 0xa7,
+		0x8d, 0xae, 0xbb, 0xac, 0x00, 0xab, 0x00, 0x90,
+		0x00, 0xc8,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0x9e, 0xa6, 0x8f,
+		0xb7, 0xc6, 0xb3, 0xb8, 0xc8, 0xb0, 0x93, 0xa6,
+		0x8c, 0xae, 0xbb, 0xad, 0x00, 0xae, 0x00, 0x93,
+		0x00, 0xcc,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xab, 0xb4, 0x9c,
+		0xb3, 0xc3, 0xaf, 0xb7, 0xc7, 0xaf, 0x93, 0xa6,
+		0x8c, 0xaf, 0xbc, 0xad, 0x00, 0xb1, 0x00, 0x97,
+		0x00, 0xcf,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa6, 0xb1, 0x98,
+		0xb1, 0xc2, 0xab, 0xba, 0xc9, 0xb2, 0x93, 0xa6,
+		0x8d, 0xae, 0xba, 0xab, 0x00, 0xb5, 0x00, 0x9b,
+		0x00, 0xd4,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa3, 0xae, 0x94,
+		0xb2, 0xc3, 0xac, 0xbb, 0xca, 0xb4, 0x91, 0xa4,
+		0x8a, 0xae, 0xba, 0xac, 0x00, 0xb8, 0x00, 0x9e,
+		0x00, 0xd8,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xab, 0xb7, 0x9c,
+		0xae, 0xc0, 0xa9, 0xba, 0xc9, 0xb3, 0x92, 0xa5,
+		0x8b, 0xad, 0xb9, 0xab, 0x00, 0xbb, 0x00, 0xa1,
+		0x00, 0xdc,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa7, 0xb4, 0x97,
+		0xb0, 0xc1, 0xaa, 0xb9, 0xc8, 0xb2, 0x92, 0xa5,
+		0x8c, 0xae, 0xb9, 0xab, 0x00, 0xbe, 0x00, 0xa4,
+		0x00, 0xdf,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa3, 0xb0, 0x94,
+		0xb0, 0xc2, 0xab, 0xbb, 0xc9, 0xb3, 0x91, 0xa4,
+		0x8b, 0xad, 0xb8, 0xaa, 0x00, 0xc1, 0x00, 0xa8,
+		0x00, 0xe2,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa3, 0xb0, 0x94,
+		0xae, 0xbf, 0xa8, 0xb9, 0xc8, 0xb3, 0x92, 0xa4,
+		0x8b, 0xad, 0xb7, 0xa9, 0x00, 0xc4, 0x00, 0xab,
+		0x00, 0xe6,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa7, 0xb6, 0x98,
+		0xaf, 0xc0, 0xa8, 0xb8, 0xc7, 0xb2, 0x93, 0xa5,
+		0x8d, 0xad, 0xb7, 0xa9, 0x00, 0xc7, 0x00, 0xae,
+		0x00, 0xe9,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa4, 0xb3, 0x95,
+		0xaf, 0xc1, 0xa9, 0xb9, 0xc8, 0xb3, 0x92, 0xa4,
+		0x8b, 0xad, 0xb7, 0xaa, 0x00, 0xc9, 0x00, 0xb0,
+		0x00, 0xec,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa4, 0xb3, 0x95,
+		0xac, 0xbe, 0xa6, 0xbb, 0xc9, 0xb4, 0x90, 0xa3,
+		0x8a, 0xad, 0xb7, 0xa9, 0x00, 0xcc, 0x00, 0xb4,
+		0x00, 0xf0,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa0, 0xb0, 0x91,
+		0xae, 0xc0, 0xa6, 0xba, 0xc8, 0xb4, 0x91, 0xa4,
+		0x8b, 0xad, 0xb7, 0xa9, 0x00, 0xcf, 0x00, 0xb7,
+		0x00, 0xf3,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa7, 0xb8, 0x98,
+		0xab, 0xbd, 0xa4, 0xbb, 0xc9, 0xb5, 0x91, 0xa3,
+		0x8b, 0xac, 0xb6, 0xa8, 0x00, 0xd1, 0x00, 0xb9,
+		0x00, 0xf6,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa4, 0xb5, 0x95,
+		0xa9, 0xbc, 0xa1, 0xbb, 0xc9, 0xb5, 0x91, 0xa3,
+		0x8a, 0xad, 0xb6, 0xa8, 0x00, 0xd6, 0x00, 0xbf,
+		0x00, 0xfc,
+	},
+};
+
+static const s6e8aa0_gamma_table s6e8aa0_gamma_tables_v96[GAMMA_LEVEL_NUM] = {
+	{
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+	       	0xdf, 0x1f, 0xd7, 0xdc, 0xb7, 0xe1, 0xc0, 0xaf,
+	       	0xc4, 0xd2, 0xd0, 0xcf, 0x00, 0x4d, 0x00, 0x40,
+	       	0x00, 0x5f,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+		0xd5, 0x35, 0xcf, 0xdc, 0xc1, 0xe1, 0xbf, 0xb3,
+	       	0xc1, 0xd2, 0xd1, 0xce,	0x00, 0x53, 0x00, 0x46,
+	       	0x00, 0x67,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+		0xd2, 0x64, 0xcf, 0xdb, 0xc6, 0xe1, 0xbd, 0xb3,
+	       	0xbd, 0xd2, 0xd2, 0xce,	0x00, 0x59, 0x00, 0x4b,
+	       	0x00, 0x6e,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+		0xd0, 0x7c, 0xcf, 0xdb, 0xc9, 0xe0, 0xbc, 0xb4,
+	       	0xbb, 0xcf, 0xd1, 0xcc, 0x00, 0x5f, 0x00, 0x50,
+	       	0x00, 0x75,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+		0xd0, 0x8e, 0xd1, 0xdb, 0xcc, 0xdf, 0xbb, 0xb6,
+	       	0xb9, 0xd0, 0xd1, 0xcd,	0x00, 0x63, 0x00, 0x54,
+	       	0x00, 0x7a,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+		0xd1, 0x9e, 0xd5, 0xda, 0xcd, 0xdd, 0xbb, 0xb7,
+	       	0xb9, 0xce, 0xce, 0xc9,	0x00, 0x68, 0x00, 0x59,
+	       	0x00, 0x81,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+		0xd0, 0xa5, 0xd6, 0xda, 0xcf, 0xdd, 0xbb, 0xb7,
+	       	0xb8, 0xcc, 0xcd, 0xc7,	0x00, 0x6c, 0x00, 0x5c,
+	       	0x00, 0x86,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x1f, 0xfe,
+		0xd0, 0xae, 0xd7, 0xd9, 0xd0, 0xdb, 0xb9, 0xb6,
+	       	0xb5, 0xca, 0xcc, 0xc5,	0x00, 0x74, 0x00, 0x63,
+	       	0x00, 0x90,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x1f, 0xf9,
+		0xcf, 0xb0, 0xd6, 0xd9, 0xd1, 0xdb, 0xb9, 0xb6,
+	       	0xb4, 0xca, 0xcb, 0xc5,	0x00, 0x77, 0x00, 0x66,
+	       	0x00, 0x94,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x1f, 0xf7,
+		0xcf, 0xb3, 0xd7, 0xd8, 0xd1, 0xd9, 0xb7, 0xb6,
+	       	0xb3, 0xc9, 0xca, 0xc3,	0x00, 0x7b, 0x00, 0x69,
+	       	0x00, 0x99,
+
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xfd, 0x2f, 0xf7,
+		0xdf, 0xb5, 0xd6, 0xd8, 0xd1, 0xd8, 0xb6, 0xb5,
+	       	0xb2, 0xca, 0xcb, 0xc4,	0x00, 0x7e, 0x00, 0x6c,
+	       	0x00, 0x9d,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xfa, 0x2f, 0xf5,
+		0xce, 0xb6, 0xd5, 0xd7, 0xd2, 0xd8, 0xb6, 0xb4,
+	       	0xb0, 0xc7, 0xc9, 0xc1,	0x00, 0x84, 0x00, 0x71,
+	       	0x00, 0xa5,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xf7, 0x2f, 0xf2,
+		0xce, 0xb9, 0xd5, 0xd8, 0xd2, 0xd8, 0xb4, 0xb4,
+	       	0xaf, 0xc7, 0xc9, 0xc1,	0x00, 0x87, 0x00, 0x73,
+	       	0x00, 0xa8,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xf5, 0x2f, 0xf0,
+		0xdf, 0xba, 0xd5, 0xd7, 0xd2, 0xd7, 0xb4, 0xb4,
+	       	0xaf, 0xc5, 0xc7, 0xbf,	0x00, 0x8a, 0x00, 0x76,
+	       	0x00, 0xac,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xf2, 0x2f, 0xed,
+		0xcE, 0xbb, 0xd4, 0xd6, 0xd2, 0xd6, 0xb5, 0xb4,
+	       	0xaF, 0xc5, 0xc7, 0xbf,	0x00, 0x8c, 0x00, 0x78,
+	       	0x00, 0xaf,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xef, 0x2f, 0xeb,
+		0xcd, 0xbb, 0xd2, 0xd7, 0xd3, 0xd6, 0xb3, 0xb4,
+	       	0xae, 0xc5, 0xc6, 0xbe,	0x00, 0x91, 0x00, 0x7d,
+	       	0x00, 0xb6,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xee, 0x2f, 0xea,
+		0xce, 0xbd, 0xd4, 0xd6, 0xd2, 0xd5, 0xb2, 0xb3,
+	       	0xad, 0xc3, 0xc4, 0xbb,	0x00, 0x94, 0x00, 0x7f,
+	       	0x00, 0xba,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xec, 0x2f, 0xe8,
+		0xce, 0xbe, 0xd3, 0xd6, 0xd3, 0xd5, 0xb2, 0xb2,
+	       	0xac, 0xc3, 0xc5, 0xbc,	0x00, 0x96, 0x00, 0x81,
+	       	0x00, 0xbd,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xeb, 0x2f, 0xe7,
+		0xce, 0xbf, 0xd3, 0xd6, 0xd2, 0xd5, 0xb1, 0xb2,
+	       	0xab, 0xc2, 0xc4, 0xbb,	0x00, 0x99, 0x00, 0x83,
+	       	0x00, 0xc0,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xef, 0x5f, 0xe9,
+		0xca, 0xbf, 0xd3, 0xd5, 0xd2, 0xd4, 0xb2, 0xb2,
+	       	0xab, 0xc1, 0xc4, 0xba,	0x00, 0x9b, 0x00, 0x85,
+	       	0x00, 0xc3,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xea, 0x5f, 0xe8,
+		0xee, 0xbf, 0xd2, 0xd5, 0xd2, 0xd4, 0xb1, 0xb2,
+	       	0xab, 0xc1, 0xc2, 0xb9,	0x00, 0x9D, 0x00, 0x87,
+	       	0x00, 0xc6,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xe9, 0x5f, 0xe7,
+		0xcd, 0xbf, 0xd2, 0xd6, 0xd2, 0xd4, 0xb1, 0xb2,
+	       	0xab, 0xbe, 0xc0, 0xb7,	0x00, 0xa1, 0x00, 0x8a,
+	       	0x00, 0xca,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xe8, 0x61, 0xe6,
+		0xcd, 0xbf, 0xd1, 0xd6, 0xd3, 0xd4, 0xaf, 0xb0,
+	       	0xa9, 0xbe, 0xc1, 0xb7,	0x00, 0xa3, 0x00, 0x8b,
+	       	0x00, 0xce,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xe8, 0x62, 0xe5,
+		0xcc, 0xc0, 0xd0, 0xd6, 0xd2, 0xd4, 0xaf, 0xb1,
+	       	0xa9, 0xbd, 0xc0, 0xb6,	0x00, 0xa5, 0x00, 0x8d,
+	       	0x00, 0xd0,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xe7, 0x7f, 0xe3,
+		0xcc, 0xc1, 0xd0, 0xd5, 0xd3, 0xd3, 0xae, 0xaf,
+	       	0xa8, 0xbe, 0xc0, 0xb7,	0x00, 0xa8, 0x00, 0x90,
+	       	0x00, 0xd3,
+	}
+};
+
+static const s6e8aa0_gamma_table s6e8aa0_gamma_tables_v32[GAMMA_LEVEL_NUM] = {
+	{
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0x72, 0x5e, 0x6b,
+		0xa1, 0xa7, 0x9a, 0xb4, 0xcb, 0xb8, 0x92, 0xac,
+		0x97, 0xb4, 0xc3, 0xb5, 0x00, 0x4e, 0x00, 0x37,
+		0x00, 0x58,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0x85, 0x71, 0x7d,
+		0xa6, 0xb6, 0xa1, 0xb5, 0xca, 0xba, 0x93, 0xac,
+		0x98, 0xb2, 0xc0, 0xaf, 0x00, 0x59, 0x00, 0x43,
+		0x00, 0x64,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xa4, 0x94, 0x9e,
+		0xa0, 0xbb, 0x9c, 0xc3, 0xd2, 0xc6, 0x93, 0xaa,
+		0x95, 0xb7, 0xc2, 0xb4, 0x00, 0x65, 0x00, 0x50,
+		0x00, 0x74,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xa1, 0xa6,
+		0xa0, 0xb9, 0x9b, 0xc3, 0xd1, 0xc8, 0x90, 0xa6,
+		0x90, 0xbb, 0xc3, 0xb7, 0x00, 0x6f, 0x00, 0x5b,
+		0x00, 0x80,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xa6, 0x9d, 0x9f,
+		0x9f, 0xb8, 0x9a, 0xc7, 0xd5, 0xcc, 0x90, 0xa5,
+		0x8f, 0xb8, 0xc1, 0xb6, 0x00, 0x74, 0x00, 0x60,
+		0x00, 0x85,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xb3, 0xae, 0xae,
+		0x9e, 0xb7, 0x9a, 0xc8, 0xd6, 0xce, 0x91, 0xa6,
+		0x90, 0xb6, 0xc0, 0xb3, 0x00, 0x78, 0x00, 0x65,
+		0x00, 0x8a,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xa9, 0xa8,
+		0xa3, 0xb9, 0x9e, 0xc4, 0xd3, 0xcb, 0x94, 0xa6,
+		0x90, 0xb6, 0xbf, 0xb3, 0x00, 0x7c, 0x00, 0x69,
+		0x00, 0x8e,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xaf, 0xaf, 0xa9,
+		0xa5, 0xbc, 0xa2, 0xc7, 0xd5, 0xcd, 0x93, 0xa5,
+		0x8f, 0xb4, 0xbd, 0xb1, 0x00, 0x83, 0x00, 0x70,
+		0x00, 0x96,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xa9, 0xab, 0xa3,
+		0xaa, 0xbf, 0xa7, 0xc5, 0xd3, 0xcb, 0x93, 0xa5,
+		0x8f, 0xb2, 0xbb, 0xb0, 0x00, 0x86, 0x00, 0x74,
+		0x00, 0x9b,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xb1, 0xb5, 0xab,
+		0xab, 0xc0, 0xa9, 0xc7, 0xd4, 0xcc, 0x94, 0xa4,
+		0x8f, 0xb1, 0xbb, 0xaf, 0x00, 0x8a, 0x00, 0x77,
+		0x00, 0x9e,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xb2, 0xa7,
+		0xae, 0xc2, 0xab, 0xc5, 0xd3, 0xca, 0x93, 0xa4,
+		0x8f, 0xb1, 0xba, 0xae, 0x00, 0x8d, 0x00, 0x7b,
+		0x00, 0xa2,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xa9, 0xaf, 0xa3,
+		0xb0, 0xc3, 0xae, 0xc4, 0xd1, 0xc8, 0x93, 0xa4,
+		0x8f, 0xb1, 0xba, 0xaf, 0x00, 0x8f, 0x00, 0x7d,
+		0x00, 0xa5,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xb4, 0xbd, 0xaf,
+		0xae, 0xc1, 0xab, 0xc2, 0xd0, 0xc6, 0x94, 0xa4,
+		0x8f, 0xb1, 0xba, 0xaf, 0x00, 0x92, 0x00, 0x80,
+		0x00, 0xa8,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xb0, 0xb9, 0xac,
+		0xad, 0xc1, 0xab, 0xc4, 0xd1, 0xc7, 0x95, 0xa4,
+		0x90, 0xb0, 0xb9, 0xad, 0x00, 0x95, 0x00, 0x84,
+		0x00, 0xac,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xb6, 0xa7,
+		0xaf, 0xc2, 0xae, 0xc5, 0xd1, 0xc7, 0x93, 0xa3,
+		0x8e, 0xb0, 0xb9, 0xad, 0x00, 0x98, 0x00, 0x86,
+		0x00, 0xaf,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xb4, 0xbf, 0xaf,
+		0xad, 0xc1, 0xab, 0xc3, 0xd0, 0xc6, 0x94, 0xa3,
+		0x8f, 0xaf, 0xb8, 0xac, 0x00, 0x9a, 0x00, 0x89,
+		0x00, 0xb2,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xb0, 0xbc, 0xac,
+		0xaf, 0xc2, 0xad, 0xc2, 0xcf, 0xc4, 0x94, 0xa3,
+		0x90, 0xaf, 0xb8, 0xad, 0x00, 0x9c, 0x00, 0x8b,
+		0x00, 0xb5,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xb9, 0xa7,
+		0xb1, 0xc4, 0xaf, 0xc3, 0xcf, 0xc5, 0x94, 0xa3,
+		0x8f, 0xae, 0xb7, 0xac, 0x00, 0x9f, 0x00, 0x8e,
+		0x00, 0xb8,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xb9, 0xa7,
+		0xaf, 0xc2, 0xad, 0xc1, 0xce, 0xc3, 0x95, 0xa3,
+		0x90, 0xad, 0xb6, 0xab, 0x00, 0xa2, 0x00, 0x91,
+		0x00, 0xbb,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xb1, 0xbe, 0xac,
+		0xb1, 0xc4, 0xaf, 0xc1, 0xcd, 0xc1, 0x95, 0xa4,
+		0x91, 0xad, 0xb6, 0xab, 0x00, 0xa4, 0x00, 0x93,
+		0x00, 0xbd,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xbb, 0xa8,
+		0xb3, 0xc5, 0xb2, 0xc1, 0xcd, 0xc2, 0x95, 0xa3,
+		0x90, 0xad, 0xb6, 0xab, 0x00, 0xa6, 0x00, 0x95,
+		0x00, 0xc0,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xbb, 0xa8,
+		0xb0, 0xc3, 0xaf, 0xc2, 0xce, 0xc2, 0x94, 0xa2,
+		0x90, 0xac, 0xb6, 0xab, 0x00, 0xa8, 0x00, 0x98,
+		0x00, 0xc3,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xa9, 0xb8, 0xa5,
+		0xb3, 0xc5, 0xb2, 0xc1, 0xcc, 0xc0, 0x95, 0xa2,
+		0x90, 0xad, 0xb6, 0xab, 0x00, 0xaa, 0x00, 0x9a,
+		0x00, 0xc5,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xb0, 0xc0, 0xac,
+		0xb0, 0xc3, 0xaf, 0xc1, 0xcd, 0xc1, 0x95, 0xa2,
+		0x90, 0xac, 0xb5, 0xa9, 0x00, 0xac, 0x00, 0x9c,
+		0x00, 0xc8,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xbd, 0xa8,
+		0xaf, 0xc2, 0xaf, 0xc1, 0xcc, 0xc0, 0x95, 0xa2,
+		0x90, 0xac, 0xb5, 0xaa, 0x00, 0xb1, 0x00, 0xa1,
+		0x00, 0xcc,
+	},
+};
+
+static int s6e8aa0_brightness_set(struct s6e8aa0 *lcd)
+{
+	const struct s6e8aa0_variant *variant = lcd->variant;
+	int ret;
+
+	ret = mipi_dsi_dcs_write(lcd->dev, 0,
+				variant->gamma_tables[lcd->brightness],
+				sizeof(variant->gamma_tables[lcd->brightness]));
+	if (ret < 0)
+		return ret;
+
+	/* update gamma table. */
+	ret = mipi_dsi_dcs_write_static_seq(lcd->dev, 0, 0xf7, 0x03);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int s6e8aa0_brightness_set_v142(struct s6e8aa0 *lcd)
+{
+	const struct s6e8aa0_variant *variant = lcd->variant;
+	int ret;
+
+	ret = variant->elvss_nvm_set(lcd);
+	if (ret < 0)
+		return ret;
+
+	return s6e8aa0_brightness_set(lcd);
+}
+
+static int s6e8aa0_panel_init(struct s6e8aa0 *lcd)
+{
+	const struct s6e8aa0_variant *variant = lcd->variant;
+
+	s6e8aa0_apply_level_1_key(lcd);
+	s6e8aa0_apply_level_2_key(lcd);
+	msleep(20);
+
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, MIPI_DCS_EXIT_SLEEP_MODE);
+	msleep(40);
+
+	variant->panel_cond_set(lcd);
+	s6e8aa0_display_condition_set(lcd);
+
+	s6e8aa0_brightness_set(lcd);
+
+	s6e8aa0_etc_source_control(lcd);
+	s6e8aa0_etc_pentile_control(lcd);
+	variant->elvss_nvm_set(lcd);
+	s6e8aa0_etc_power_control(lcd);
+	s6e8aa0_etc_elvss_control(lcd);
+	msleep(lcd->pdata->init_delay);
+
+	dev_dbg(&lcd->dev->dev, "panel init sequence done.\n");
+
+	return 0;
+}
+
+static int s6e8aa0_get_brightness(struct backlight_device *bd)
+{
+	return bd->props.brightness;
+}
+
+static int s6e8aa0_update_status(struct backlight_device *bd)
+{
+	struct s6e8aa0 *lcd = bl_get_data(bd);
+	const struct s6e8aa0_variant *variant = lcd->variant;
+	int ret;
+
+	mutex_lock(&lcd->mutex);
+
+	lcd->brightness = bd->props.brightness;
+
+	if (!lcd->power)
+		goto unlock;
+
+	ret = variant->brightness_set(lcd);
+	if (ret) {
+		dev_err(&lcd->dev->dev, "lcd brightness setting failed.\n");
+		goto unlock;
+	}
+
+unlock:
+	mutex_unlock(&lcd->mutex);
+
+	return 0;
+}
+
+static const struct backlight_ops s6e8aa0_backlight_ops = {
+	.get_brightness = s6e8aa0_get_brightness,
+	.update_status = s6e8aa0_update_status,
+};
+
+static int s6e8aa0_set_stream(struct s6e8aa0 *lcd, bool on);
+
+static int s6e8aa0_set_power(struct lcd_device *ld, int power)
+{
+	struct s6e8aa0 *lcd = lcd_get_data(ld);
+	int ret = 0;
+
+	mutex_lock(&lcd->mutex);
+
+	ret = s6e8aa0_set_stream(lcd, power = FB_BLANK_UNBLANK);
+
+	if (ret)
+		goto unlock;
+
+	lcd->power = power;
+
+unlock:
+	mutex_unlock(&lcd->mutex);
+
+	return ret;
+}
+
+static int s6e8aa0_get_power(struct lcd_device *ld)
+{
+	struct s6e8aa0 *lcd = lcd_get_data(ld);
+
+	return lcd->power;
+}
+
+static struct lcd_ops s6e8aa0_lcd_ops = {
+	.set_power = s6e8aa0_set_power,
+	.get_power = s6e8aa0_get_power,
+};
+
+static const struct s6e8aa0_variant s6e8aa0_variants[] = {
+	{
+		.version = 32,
+		.panel_cond_set = s6e8aa0_panel_cond_set,
+		.pentile_ctl_table = s6e8aa0_pentile_ctl_table_v32,
+		.pentile_ctl_len = ARRAY_SIZE(s6e8aa0_pentile_ctl_table_v32),
+		.power_ctl_table = s6e8aa0_power_ctl_table_v32,
+		.power_ctl_len = ARRAY_SIZE(s6e8aa0_power_ctl_table_v32),
+		.elvss_nvm_set = s6e8aa0_elvss_nvm_set,
+		.brightness_set = s6e8aa0_brightness_set,
+		.gamma_tables = s6e8aa0_gamma_tables_v32,
+	}, {
+		.version = 142,
+		.panel_cond_set = s6e8aa0_panel_cond_set_v142,
+		.pentile_ctl_table = s6e8aa0_pentile_ctl_table,
+		.pentile_ctl_len = ARRAY_SIZE(s6e8aa0_pentile_ctl_table),
+		.power_ctl_table = s6e8aa0_power_ctl_table,
+		.power_ctl_len = ARRAY_SIZE(s6e8aa0_power_ctl_table),
+		.elvss_nvm_set = s6e8aa0_elvss_nvm_set_v142,
+		.brightness_set = s6e8aa0_brightness_set_v142,
+		.gamma_tables = s6e8aa0_gamma_tables_v142,
+	}, {
+		/* FIXME: support M0 panel v96
+		 * need to suitable panel specification
+ 		 */
+		.version = 96,
+		.panel_cond_set = s6e8aa0_panel_cond_set,
+		.pentile_ctl_table = s6e8aa0_pentile_ctl_table_v32,
+		.pentile_ctl_len = ARRAY_SIZE(s6e8aa0_pentile_ctl_table_v32),
+		.power_ctl_table = s6e8aa0_power_ctl_table_v32,
+		.power_ctl_len = ARRAY_SIZE(s6e8aa0_power_ctl_table_v32),
+		.elvss_nvm_set = s6e8aa0_elvss_nvm_set,
+		.brightness_set = s6e8aa0_brightness_set,
+		.gamma_tables = s6e8aa0_gamma_tables_v96,
+	
+	}, {
+		/* FIXME: support U1HD panel v210
+		 * need to suitable panel specification
+ 		 */
+		.version = 210,
+		.panel_cond_set = s6e8aa0_panel_cond_set_v142,
+		.pentile_ctl_table = s6e8aa0_pentile_ctl_table,
+		.pentile_ctl_len = ARRAY_SIZE(s6e8aa0_pentile_ctl_table),
+		.power_ctl_table = s6e8aa0_power_ctl_table,
+		.power_ctl_len = ARRAY_SIZE(s6e8aa0_power_ctl_table),
+		.elvss_nvm_set = s6e8aa0_elvss_nvm_set_v142,
+		.brightness_set = s6e8aa0_brightness_set_v142,
+		.gamma_tables = s6e8aa0_gamma_tables_v142,
+	}
+};
+
+static int s6e8aa0_check_mtp(struct s6e8aa0 *lcd)
+{
+	int ret;
+	u8 mtp_data[LDI_MTP_LENGTH] = {0, };
+	u8 mtp_id[3] = {0, };
+	int i;
+
+	s6e8aa0_read_id(lcd, mtp_id);
+	if (mtp_id[0] = 0x00) {
+		dev_err(&lcd->dev->dev, "read id failed\n");
+		return -EIO;
+	}
+
+	dev_info(&lcd->dev->dev, "Read ID : 0x%2x, 0x%2x, 0x%2x\n",
+					mtp_id[0], mtp_id[1], mtp_id[2]);
+
+	if (mtp_id[2] = 0x33)
+		dev_dbg(&lcd->dev->dev,
+			"ID-3 is 0xff does not support dynamic elvss\n");
+	else {
+		dev_dbg(&lcd->dev->dev,
+			"ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]);
+		dev_dbg(&lcd->dev->dev, "Dynamic ELVSS Information\n");
+	}
+
+	for (i = 0; i < ARRAY_SIZE(s6e8aa0_variants); ++i) {
+		if (mtp_id[1] = s6e8aa0_variants[i].version)
+			break;
+	}
+	if (i >= ARRAY_SIZE(s6e8aa0_variants)) {
+		dev_err(&lcd->dev->dev, "unsupported display version %d\n",
+								mtp_id[1]);
+		return -EINVAL;
+	}
+
+	lcd->variant = &s6e8aa0_variants[i];
+	lcd->id = mtp_id[2];
+	lcd->aid = (mtp_id[2] >> 5);
+
+	ret = s6e8aa0_read_mtp(lcd, mtp_data);
+	if (ret) {
+		dev_err(&lcd->dev->dev, "read mtp failed\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int s6e8aa0_set_sequence(struct s6e8aa0 *lcd)
+{
+	int ret;
+
+	ret = s6e8aa0_check_mtp(lcd);
+	if (ret < 0)
+		return ret;
+
+	s6e8aa0_panel_init(lcd);
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, MIPI_DCS_SET_DISPLAY_ON);
+
+	dev_dbg(&lcd->dev->dev, "%s:done.\n", __func__);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static int s6e8aa0_generic_reset(struct device *dev)
+{
+	struct s6e8aa0 *lcd = dev_get_drvdata(dev);
+
+	gpio_set_value(lcd->reset_gpio, 1);
+	usleep_range(10000, 11000);
+	gpio_set_value(lcd->reset_gpio, 0);
+	usleep_range(10000, 11000);
+	gpio_set_value(lcd->reset_gpio, 1);
+
+	return 0;
+}
+
+static struct s6e8aa0_platform_data *s6e8aa0_parse_dt(struct s6e8aa0 *lcd)
+{
+	struct device_node *node = lcd->dev->dev.of_node;
+	struct s6e8aa0_platform_data *data;
+	const __be32 *prop_data;
+	int ret;
+
+	data = devm_kzalloc(&lcd->dev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		dev_err(&lcd->dev->dev, "failed to allocate platform data.\n");
+		return NULL;
+	}
+
+	ret = of_get_videomode(node, &data->mode, 0);
+	if (ret) {
+		dev_err(&lcd->dev->dev, "failed to read video mode from DT\n");
+		return NULL;
+	}
+
+	lcd->reset_gpio = of_get_named_gpio(node, "reset-gpio", 0);
+	if (lcd->reset_gpio < 0)
+		return NULL;
+
+	prop_data = of_get_property(node, "reset-delay", NULL);
+	if (!prop_data)
+		return NULL;
+	data->reset_delay = be32_to_cpu(*prop_data);
+
+	prop_data = of_get_property(node, "power-on-delay", NULL);
+	if (!prop_data)
+		return NULL;
+	data->power_on_delay = be32_to_cpu(*prop_data);
+
+	prop_data = of_get_property(node, "init-delay", NULL);
+	if (!prop_data)
+		return NULL;
+	data->init_delay = be32_to_cpu(*prop_data);
+
+	if (of_find_property(node, "flip-horizontal", NULL))
+		data->flip_horizontal = true;
+
+	if (of_find_property(node, "flip-vertical", NULL))
+		data->flip_vertical = true;
+
+	data->reset = s6e8aa0_generic_reset;
+
+	return data;
+}
+
+static struct of_device_id s6e8aa0_of_match[] = {
+	{ .compatible = "samsung,s6e8aa0" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, s6e8aa0_of_match);
+#else
+static struct s6e8aa0_platform_data *s6e8aa0_parse_dt(struct s6e8aa0 *lcd)
+{
+	return NULL;
+}
+#endif
+
+static int s6e8aa0_power_on(struct s6e8aa0 *lcd)
+{
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(lcd->supplies), lcd->supplies);
+	if (ret)
+		return ret;
+
+	msleep(lcd->pdata->power_on_delay);
+
+	/* lcd reset */
+	if (lcd->pdata->reset) {
+		ret = lcd->pdata->reset(&lcd->dev->dev);
+		if (ret)
+			goto err;
+	}
+
+	msleep(lcd->pdata->reset_delay);
+	ret = mipi_dsi_set_power(lcd->dev, 1);
+	if (ret)
+		goto err;
+
+	ret = s6e8aa0_set_sequence(lcd);
+	if (!ret)
+		return 0;
+
+	mipi_dsi_set_power(lcd->dev, 0);
+err:
+	regulator_bulk_disable(ARRAY_SIZE(lcd->supplies), lcd->supplies);
+
+	return ret;
+}
+
+static void s6e8aa0_power_off(struct s6e8aa0 *lcd)
+{
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, MIPI_DCS_ENTER_SLEEP_MODE);
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, MIPI_DCS_SET_DISPLAY_OFF);
+
+	mipi_dsi_set_power(lcd->dev, 0);
+
+	regulator_bulk_disable(ARRAY_SIZE(lcd->supplies), lcd->supplies);
+}
+
+static int s6e8aa0_set_stream(struct s6e8aa0 *lcd, bool on)
+{
+	if (on = lcd->streaming)
+		return 0;
+
+	if (on) {
+		s6e8aa0_power_on(lcd);
+		mipi_dsi_set_stream(lcd->dev, true);
+	} else {
+		mipi_dsi_set_stream(lcd->dev, false);
+		s6e8aa0_power_off(lcd);
+	}
+
+	lcd->streaming = on;
+
+	return 0;
+}
+
+static int s6e8aa0_probe(struct mipi_dsi_device *pdev)
+{
+	struct s6e8aa0 *lcd;
+	int ret;
+
+	lcd = kzalloc(sizeof(struct s6e8aa0), GFP_KERNEL);
+	if (!lcd) {
+		dev_err(&pdev->dev, "failed to allocate s6e8aa0 structure.\n");
+		return -ENOMEM;
+	}
+
+	mipi_dsi_set_drvdata(pdev, lcd);
+
+	lcd->dev = pdev;
+	lcd->pdata = (struct s6e8aa0_platform_data *)pdev->dev.platform_data;
+
+	if (!lcd->pdata) {
+		lcd->pdata = s6e8aa0_parse_dt(lcd);
+		if (!lcd->pdata) {
+			dev_err(&pdev->dev, "failed to find platform data\n");
+			return -ENODEV;
+		}
+	}
+
+	lcd->supplies[0].supply = "vdd3";
+	lcd->supplies[1].supply = "vci";
+	ret = regulator_bulk_get(&pdev->dev,
+				ARRAY_SIZE(lcd->supplies), lcd->supplies);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to get regulators: %d\n", ret);
+		goto err_regulator_bulk_get;
+	}
+
+	lcd->ld = lcd_device_register("s6e8aa0", &pdev->dev, lcd,
+			&s6e8aa0_lcd_ops);
+	if (IS_ERR(lcd->ld)) {
+		dev_err(&lcd->dev->dev, "failed to register lcd ops.\n");
+		ret = PTR_ERR(lcd->ld);
+		goto err_lcd_register;
+	}
+
+	lcd->bd = backlight_device_register("s6e8aa0-bl", &pdev->dev, lcd,
+			&s6e8aa0_backlight_ops, NULL);
+	if (IS_ERR(lcd->bd)) {
+		dev_err(&pdev->dev, "failed to register backlight ops.\n");
+		ret = PTR_ERR(lcd->bd);
+		goto err_backlight_register;
+	}
+
+	mutex_init(&lcd->mutex);
+
+	lcd->bd->props.max_brightness = GAMMA_LEVEL_NUM - 1;
+	lcd->bd->props.brightness = GAMMA_LEVEL_NUM - 1;
+	lcd->brightness = GAMMA_LEVEL_NUM - 1;
+
+	dev_dbg(&pdev->dev, "probed s6e8aa0 panel driver.\n");
+	pdev->vm = lcd->pdata->mode;
+
+	pdev->params = (struct mipi_dsi_interface_params) {
+		.format = DSI_FMT_RGB888,
+		.mode = DSI_MODE_VIDEO | DSI_MODE_VIDEO_BURST
+			| DSI_MODE_VIDEO_HFP | DSI_MODE_VIDEO_HBP
+			| DSI_MODE_VIDEO_HSA | DSI_MODE_EOT_PACKET
+			| DSI_MODE_VSYNC_FLUSH,
+		.data_lanes = 0xf,
+		.hs_clk_freq = 500000000,
+		.esc_clk_freq = 20000000,
+	};
+
+	s6e8aa0_set_stream(lcd, true);
+
+	return 0;
+
+err_backlight_register:
+	lcd_device_unregister(lcd->ld);
+err_lcd_register:
+	regulator_bulk_free(ARRAY_SIZE(lcd->supplies), lcd->supplies);
+err_regulator_bulk_get:
+	kfree(lcd);
+
+	return ret;
+}
+
+static int s6e8aa0_remove(struct mipi_dsi_device *pdev)
+{
+	struct s6e8aa0 *lcd = mipi_dsi_get_drvdata(pdev);
+
+	backlight_device_unregister(lcd->bd);
+	lcd_device_unregister(lcd->ld);
+	mipi_dsi_set_drvdata(pdev, NULL);
+	regulator_bulk_free(ARRAY_SIZE(lcd->supplies), lcd->supplies);
+	kfree(lcd);
+
+	return 0;
+}
+
+static int s6e8aa0_suspend(struct device *dev)
+{
+	struct s6e8aa0 *lcd = dev_get_drvdata(dev);
+
+	dev_err(dev, "%s: %d\n", __func__, lcd->power);
+
+	if (lcd->power != FB_BLANK_UNBLANK)
+		return 0;
+
+	mipi_dsi_set_stream(lcd->dev, 0);
+	s6e8aa0_power_off(lcd);
+
+	return 0;
+}
+
+static int s6e8aa0_resume(struct device *dev)
+{
+	struct s6e8aa0 *lcd = dev_get_drvdata(dev);
+	int ret;
+
+	dev_err(dev, "%s: %d\n", __func__, lcd->power);
+
+	if (lcd->power != FB_BLANK_UNBLANK)
+		return 0;
+
+	s6e8aa0_power_on(lcd);
+
+	ret = mipi_dsi_set_stream(lcd->dev, 1);
+	dev_err(dev, "%s: dsi_set_stream=%d\n", __func__, ret);
+
+	return 0;
+}
+
+static struct dev_pm_ops s6e8aa0_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(s6e8aa0_suspend, s6e8aa0_resume)
+};
+
+static const struct mipi_dsi_device_id s6e8aa0_id[] = {
+	{ "s6e8aa0", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(mipi_dsi, s6e8aa0_id);
+
+static struct mipi_dsi_driver s6e8aa0_driver = {
+	.probe = s6e8aa0_probe,
+	.remove = s6e8aa0_remove,
+	.driver = {
+		.name = "panel_s6e8aa0",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(s6e8aa0_of_match),
+		.pm = &s6e8aa0_pm_ops,
+	},
+	.id_table = s6e8aa0_id
+};
+module_mipi_dsi_driver(s6e8aa0_driver);
+
+MODULE_ALIAS("mipi-dsi:s6e8aa0");
+MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
+MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+MODULE_AUTHOR("Joongmock Shin <jmock.shin@samsung.com>");
+MODULE_AUTHOR("Eunchul Kim <chulspro.kim@samsung.com>");
+MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
+MODULE_DESCRIPTION("MIPI-DSI based s6e8aa0 AMOLED LCD Panel Driver");
+MODULE_LICENSE("GPL");
diff --git a/include/video/panel-s6e8aa0.h b/include/video/panel-s6e8aa0.h
new file mode 100644
index 0000000..8a32de8
--- /dev/null
+++ b/include/video/panel-s6e8aa0.h
@@ -0,0 +1,42 @@
+/*
+ * Renesas R61505-based Display Panels
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __PANEL_S6E8AX0_H__
+#define __PANEL_S6E8AX0_H__
+
+#include <video/videomode.h>
+
+struct s6e8aa0_platform_data {
+	unsigned long width;		/* Panel width in mm */
+	unsigned long height;		/* Panel height in mm */
+	struct videomode mode;
+
+	/* reset lcd panel device. */
+	int (*reset)(struct device *dev);
+
+	/* it indicates whether lcd panel was enabled
+	   from bootloader or not. */
+	int lcd_enabled;
+	/* it means delay for stable time when it becomes low to high
+	   or high to low that is dependent on whether reset gpio is
+	   low active or high active. */
+	unsigned int reset_delay;
+	/* stable time needing to become lcd power on. */
+	unsigned int power_on_delay;
+	/* stable time needing to become lcd power off. */
+	unsigned int init_delay;
+	/* panel is reversed */
+	bool flip_vertical;
+	bool flip_horizontal;
+};
+
+#endif /* __PANEL_S6E8AX0_H__ */
-- 
1.8.1.2

[RFC PATCH 4/4] ARM: dts: exynos4210-trats: add panel and dsi nodes

From: Andrzej Hajda <hidden>
Date: 2013-09-24 14:23:16

The patch adds mipi-dsi-exynos bus master node
and s6e8aa0 panel subnode to trats device.

Signed-off-by: Andrzej Hajda <redacted>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

Conflicts:
	arch/arm/boot/dts/exynos4210-trats.dts
---
 arch/arm/boot/dts/exynos4210-trats.dts | 54 ++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)
diff --git a/arch/arm/boot/dts/exynos4210-trats.dts b/arch/arm/boot/dts/exynos4210-trats.dts
index 94eebff..6f1a034 100644
--- a/arch/arm/boot/dts/exynos4210-trats.dts
+++ b/arch/arm/boot/dts/exynos4210-trats.dts
@@ -301,4 +301,58 @@
 			clock-frequency = <24000000>;
 		};
 	};
+
+	dsi_0: dsi@11C80000 {
+		samsung,pll-stable-time = <500>;
+		samsung,stop-holding-count = <0x7ff>;
+		samsung,bta-timeout = <0xff>;
+		samsung,rx-timeout = <0xffff>;
+		samsung,pll-clk-freq = <24000000>;
+		samsung,cmd-allow = <0xf>;
+		vdd11-supply = <&vusb_reg>;
+		vdd18-supply = <&vmipi_reg>;
+		status = "okay";
+
+		fimd0_lcd: panel {
+			compatible = "samsung,s6e8aa0";
+			reset-gpio = <&gpy4 5 0>;
+			power-on-delay= <50>;
+			reset-delay = <100>;
+			init-delay = <100>;
+			vdd3-supply = <&vcclcd_reg>;
+			vci-supply = <&vlcd_reg>;
+			video-source = <&dsi_0>;
+			flip-horizontal;
+			flip-vertical;
+			samsung,panel-width-mm = <58>;
+			samsung,panel-height-mm = <103>;
+
+			display-timings {
+				native-mode = <&timing0>;
+
+				timing0: timing-0 {
+					clock-frequency = <0>;
+					hactive = <720>;
+					vactive = <1280>;
+					hfront-porch = <5>;
+					hback-porch = <5>;
+					hsync-len = <5>;
+					vfront-porch = <13>;
+					vback-porch = <1>;
+					vsync-len = <2>;
+				};
+			};
+		};
+	};
+
+	fimd@11c00000 {
+		samsung,fimd-display = <&fimd0_lcd>;
+		samsung,fimd-vidout-rgb;
+		samsung,fimd-inv-vclk;
+		samsung,fimd-frame-rate = <60>;
+		samsung,default-window = <3>;
+		samsung,fimd-win-bpp = <32>;
+		status = "okay";
+	};
+
 };
-- 
1.8.1.2

RE: [RFC PATCH 1/4] mipi-dsi-bus: add MIPI DSI bus support

From: Bert Kenward <hidden>
Date: 2013-10-07 10:47:49

T24gVHVlc2RheSBTZXB0ZW1iZXIgMjQgMjAxMyBhdCAxNToyMywgQW5kcnplaiBIYWpkYSB3cm90
ZToNCj4gTUlQSSBEU0kgaXMgYSBoaWdoLXNwZWVkIHNlcmlhbCBpbnRlcmZhY2UgdG8gdHJhbnNt
aXQNCj4gZGF0YSBmcm9tL3RvIGhvc3QgdG8gZGlzcGxheSBtb2R1bGUuDQo+IA0KPiBTaWduZWQt
b2ZmLWJ5OiBBbmRyemVqIEhhamRhIDxhLmhhamRhQHNhbXN1bmcuY29tPg0KPiBTaWduZWQtb2Zm
LWJ5OiBLeXVuZ21pbiBQYXJrIDxreXVuZ21pbi5wYXJrQHNhbXN1bmcuY29tPg0KPiAtLS0NCj4g
IGRyaXZlcnMvdmlkZW8vZGlzcGxheS9LY29uZmlnICAgICAgICB8ICAgNCArDQo+ICBkcml2ZXJz
L3ZpZGVvL2Rpc3BsYXkvTWFrZWZpbGUgICAgICAgfCAgIDEgKw0KPiAgZHJpdmVycy92aWRlby9k
aXNwbGF5L21pcGktZHNpLWJ1cy5jIHwgMzMyDQo+ICsrKysrKysrKysrKysrKysrKysrKysrKysr
KysrKysrKysrDQo+ICBpbmNsdWRlL3ZpZGVvL2Rpc3BsYXkuaCAgICAgICAgICAgICAgfCAgIDMg
Kw0KPiAgaW5jbHVkZS92aWRlby9taXBpLWRzaS1idXMuaCAgICAgICAgIHwgMTQ0ICsrKysrKysr
KysrKysrKw0KPiAgNSBmaWxlcyBjaGFuZ2VkLCA0ODQgaW5zZXJ0aW9ucygrKQ0KDQo8c25pcHBl
ZCBhcyBmYXIgYXMgbWlwaS1kc2ktYnVzLmgNCg0KPiBkaWZmIC0tZ2l0IGEvaW5jbHVkZS92aWRl
by9taXBpLWRzaS1idXMuaCBiL2luY2x1ZGUvdmlkZW8vbWlwaS1kc2ktYnVzLmgNCj4gbmV3IGZp
bGUgbW9kZSAxMDA2NDQNCj4gaW5kZXggMDAwMDAwMC4uYTc4NzkyZA0KPiAtLS0gL2Rldi9udWxs
DQo+ICsrKyBiL2luY2x1ZGUvdmlkZW8vbWlwaS1kc2ktYnVzLmgNCj4gQEAgLTAsMCArMSwxNDQg
QEANCj4gKy8qDQo+ICsgKiBNSVBJIERTSSBCdXMNCj4gKyAqDQo+ICsgKiBDb3B5cmlnaHQgKEMp
IDIwMTMsIFNhbXN1bmcgRWxlY3Ryb25pY3MsIENvLiwgTHRkLg0KPiArICogQW5kcnplaiBIYWpk
YSA8YS5oYWpkYUBzYW1zdW5nLmNvbT4NCj4gKyAqDQo+ICsgKiBUaGlzIHByb2dyYW0gaXMgZnJl
ZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeQ0KPiArICog
aXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSB2ZXJz
aW9uIDIgYXMNCj4gKyAqIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9u
Lg0KPiArICovDQo+ICsNCj4gKyNpZm5kZWYgX19NSVBJX0RTSV9CVVNfSF9fDQo+ICsjZGVmaW5l
IF9fTUlQSV9EU0lfQlVTX0hfXw0KPiArDQo+ICsjaW5jbHVkZSA8bGludXgvZGV2aWNlLmg+DQo+
ICsjaW5jbHVkZSA8dmlkZW8vdmlkZW9tb2RlLmg+DQo+ICsNCj4gK3N0cnVjdCBtaXBpX2RzaV9i
dXM7DQo+ICtzdHJ1Y3QgbWlwaV9kc2lfZGV2aWNlOw0KPiArDQo+ICtzdHJ1Y3QgbWlwaV9kc2lf
YnVzX29wcyB7DQo+ICsJaW50ICgqc2V0X3Bvd2VyKShzdHJ1Y3QgbWlwaV9kc2lfYnVzICpidXMs
IHN0cnVjdCBtaXBpX2RzaV9kZXZpY2UNCj4gKmRldiwNCj4gKwkJCSBib29sIG9uKTsNCj4gKwlp
bnQgKCpzZXRfc3RyZWFtKShzdHJ1Y3QgbWlwaV9kc2lfYnVzICpidXMsIHN0cnVjdCBtaXBpX2Rz
aV9kZXZpY2UNCj4gKmRldiwNCj4gKwkJCSAgYm9vbCBvbik7DQo+ICsJaW50ICgqdHJhbnNmZXIp
KHN0cnVjdCBtaXBpX2RzaV9idXMgKmJ1cywgc3RydWN0IG1pcGlfZHNpX2RldmljZQ0KPiAqZGV2
LA0KPiArCQkJdTggdHlwZSwgY29uc3QgdTggKnR4X2J1Ziwgc2l6ZV90IHR4X2xlbiwgdTggKnJ4
X2J1ZiwNCj4gKwkJCXNpemVfdCByeF9sZW4pOw0KPiArfTsNCj4gKw0KPiArI2RlZmluZSBEU0lf
TU9ERV9WSURFTwkJCSgxIDw8IDApDQo+ICsjZGVmaW5lIERTSV9NT0RFX1ZJREVPX0JVUlNUCQko
MSA8PCAxKQ0KPiArI2RlZmluZSBEU0lfTU9ERV9WSURFT19TWU5DX1BVTFNFCSgxIDw8IDIpDQo+
ICsjZGVmaW5lIERTSV9NT0RFX1ZJREVPX0FVVE9fVkVSVAkoMSA8PCAzKQ0KPiArI2RlZmluZSBE
U0lfTU9ERV9WSURFT19IU0UJCSgxIDw8IDQpDQo+ICsjZGVmaW5lIERTSV9NT0RFX1ZJREVPX0hG
UAkJKDEgPDwgNSkNCj4gKyNkZWZpbmUgRFNJX01PREVfVklERU9fSEJQCQkoMSA8PCA2KQ0KPiAr
I2RlZmluZSBEU0lfTU9ERV9WSURFT19IU0EJCSgxIDw8IDcpDQo+ICsjZGVmaW5lIERTSV9NT0RF
X1ZTWU5DX0ZMVVNICQkoMSA8PCA4KQ0KPiArI2RlZmluZSBEU0lfTU9ERV9FT1RfUEFDS0VUCQko
MSA8PCA5KQ0KPiArDQo+ICtlbnVtIG1pcGlfZHNpX3BpeGVsX2Zvcm1hdCB7DQo+ICsJRFNJX0ZN
VF9SR0I4ODgsDQo+ICsJRFNJX0ZNVF9SR0I2NjYsDQo+ICsJRFNJX0ZNVF9SR0I2NjZfUEFDS0VE
LA0KPiArCURTSV9GTVRfUkdCNTY1LA0KPiArfTsNCj4gKw0KPiArc3RydWN0IG1pcGlfZHNpX2lu
dGVyZmFjZV9wYXJhbXMgew0KPiArCWVudW0gbWlwaV9kc2lfcGl4ZWxfZm9ybWF0IGZvcm1hdDsN
Cj4gKwl1bnNpZ25lZCBsb25nIG1vZGU7DQo+ICsJdW5zaWduZWQgbG9uZyBoc19jbGtfZnJlcTsN
Cj4gKwl1bnNpZ25lZCBsb25nIGVzY19jbGtfZnJlcTsNCj4gKwl1bnNpZ25lZCBjaGFyIGRhdGFf
bGFuZXM7DQo+ICsJdW5zaWduZWQgY2hhciBjbWRfYWxsb3c7DQo+ICt9Ow0KPiArDQo+ICtzdHJ1
Y3QgbWlwaV9kc2lfYnVzIHsNCj4gKwlzdHJ1Y3QgZGV2aWNlICpkZXY7DQo+ICsJY29uc3Qgc3Ry
dWN0IG1pcGlfZHNpX2J1c19vcHMgKm9wczsNCj4gK307DQo+ICsNCj4gKyNkZWZpbmUgTUlQSV9E
U0lfTU9EVUxFX1BSRUZJWAkJIm1pcGktZHNpOiINCj4gKyNkZWZpbmUgTUlQSV9EU0lfTkFNRV9T
SVpFCQkzMg0KPiArDQo+ICtzdHJ1Y3QgbWlwaV9kc2lfZGV2aWNlX2lkIHsNCj4gKwljaGFyIG5h
bWVbTUlQSV9EU0lfTkFNRV9TSVpFXTsNCj4gKwlfX2tlcm5lbF91bG9uZ190IGRyaXZlcl9kYXRh
CS8qIERhdGEgcHJpdmF0ZSB0byB0aGUgZHJpdmVyICovDQo+ICsJCQlfX2FsaWduZWQoc2l6ZW9m
KF9fa2VybmVsX3Vsb25nX3QpKTsNCj4gK307DQo+ICsNCj4gK3N0cnVjdCBtaXBpX2RzaV9kZXZp
Y2Ugew0KPiArCWNoYXIgbmFtZVtNSVBJX0RTSV9OQU1FX1NJWkVdOw0KPiArCWludCBpZDsNCj4g
KwlzdHJ1Y3QgZGV2aWNlIGRldjsNCj4gKw0KPiArCWNvbnN0IHN0cnVjdCBtaXBpX2RzaV9kZXZp
Y2VfaWQgKmlkX2VudHJ5Ow0KPiArCXN0cnVjdCBtaXBpX2RzaV9idXMgKmJ1czsNCj4gKwlzdHJ1
Y3QgdmlkZW9tb2RlIHZtOw0KPiArCXN0cnVjdCBtaXBpX2RzaV9pbnRlcmZhY2VfcGFyYW1zIHBh
cmFtczsNCj4gK307DQo+ICsNCj4gKyNkZWZpbmUgdG9fbWlwaV9kc2lfZGV2aWNlKGQpCWNvbnRh
aW5lcl9vZihkLCBzdHJ1Y3QNCj4gbWlwaV9kc2lfZGV2aWNlLCBkZXYpDQo+ICsNCj4gK2ludCBt
aXBpX2RzaV9kZXZpY2VfcmVnaXN0ZXIoc3RydWN0IG1pcGlfZHNpX2RldmljZSAqZGV2LA0KPiAr
CQkJICAgICBzdHJ1Y3QgbWlwaV9kc2lfYnVzICpidXMpOw0KPiArdm9pZCBtaXBpX2RzaV9kZXZp
Y2VfdW5yZWdpc3RlcihzdHJ1Y3QgbWlwaV9kc2lfZGV2aWNlICpkZXYpOw0KPiArDQo+ICtzdHJ1
Y3QgbWlwaV9kc2lfZHJpdmVyIHsNCj4gKwlpbnQoKnByb2JlKShzdHJ1Y3QgbWlwaV9kc2lfZGV2
aWNlICopOw0KPiArCWludCgqcmVtb3ZlKShzdHJ1Y3QgbWlwaV9kc2lfZGV2aWNlICopOw0KPiAr
CXN0cnVjdCBkZXZpY2VfZHJpdmVyIGRyaXZlcjsNCj4gKwljb25zdCBzdHJ1Y3QgbWlwaV9kc2lf
ZGV2aWNlX2lkICppZF90YWJsZTsNCj4gK307DQo+ICsNCj4gKyNkZWZpbmUgdG9fbWlwaV9kc2lf
ZHJpdmVyKGQpCWNvbnRhaW5lcl9vZihkLCBzdHJ1Y3QNCj4gbWlwaV9kc2lfZHJpdmVyLCBkcml2
ZXIpDQo+ICsNCj4gK2ludCBtaXBpX2RzaV9kcml2ZXJfcmVnaXN0ZXIoc3RydWN0IG1pcGlfZHNp
X2RyaXZlciAqZHJ2KTsNCj4gK3ZvaWQgbWlwaV9kc2lfZHJpdmVyX3VucmVnaXN0ZXIoc3RydWN0
IG1pcGlfZHNpX2RyaXZlciAqZHJ2KTsNCj4gKw0KPiArc3RhdGljIGlubGluZSB2b2lkICptaXBp
X2RzaV9nZXRfZHJ2ZGF0YShjb25zdCBzdHJ1Y3QgbWlwaV9kc2lfZGV2aWNlDQo+ICpkZXYpDQo+
ICt7DQo+ICsJcmV0dXJuIGRldl9nZXRfZHJ2ZGF0YSgmZGV2LT5kZXYpOw0KPiArfQ0KPiArDQo+
ICtzdGF0aWMgaW5saW5lIHZvaWQgbWlwaV9kc2lfc2V0X2RydmRhdGEoc3RydWN0IG1pcGlfZHNp
X2RldmljZSAqZGV2LA0KPiArCQkJCQl2b2lkICpkYXRhKQ0KPiArew0KPiArCWRldl9zZXRfZHJ2
ZGF0YSgmZGV2LT5kZXYsIGRhdGEpOw0KPiArfQ0KPiArDQo+ICtpbnQgb2ZfbWlwaV9kc2lfcmVn
aXN0ZXJfZGV2aWNlcyhzdHJ1Y3QgbWlwaV9kc2lfYnVzICpidXMpOw0KPiArdm9pZCBtaXBpX2Rz
aV91bnJlZ2lzdGVyX2RldmljZXMoc3RydWN0IG1pcGlfZHNpX2J1cyAqYnVzKTsNCj4gKw0KPiAr
LyogbW9kdWxlX21pcGlfZHNpX2RyaXZlcigpIC0gSGVscGVyIG1hY3JvIGZvciBkcml2ZXJzIHRo
YXQgZG9uJ3QgZG8NCj4gKyAqIGFueXRoaW5nIHNwZWNpYWwgaW4gbW9kdWxlIGluaXQvZXhpdC4g
IFRoaXMgZWxpbWluYXRlcyBhIGxvdCBvZg0KPiArICogYm9pbGVycGxhdGUuICBFYWNoIG1vZHVs
ZSBtYXkgb25seSB1c2UgdGhpcyBtYWNybyBvbmNlLCBhbmQNCj4gKyAqIGNhbGxpbmcgaXQgcmVw
bGFjZXMgbW9kdWxlX2luaXQoKSBhbmQgbW9kdWxlX2V4aXQoKQ0KPiArICovDQo+ICsjZGVmaW5l
IG1vZHVsZV9taXBpX2RzaV9kcml2ZXIoX19taXBpX2RzaV9kcml2ZXIpIFwNCj4gKwltb2R1bGVf
ZHJpdmVyKF9fbWlwaV9kc2lfZHJpdmVyLCBtaXBpX2RzaV9kcml2ZXJfcmVnaXN0ZXIsIFwNCj4g
KwkJCW1pcGlfZHNpX2RyaXZlcl91bnJlZ2lzdGVyKQ0KPiArDQo+ICtpbnQgbWlwaV9kc2lfc2V0
X3Bvd2VyKHN0cnVjdCBtaXBpX2RzaV9kZXZpY2UgKmRldiwgYm9vbCBvbik7DQo+ICtpbnQgbWlw
aV9kc2lfc2V0X3N0cmVhbShzdHJ1Y3QgbWlwaV9kc2lfZGV2aWNlICpkZXYsIGJvb2wgb24pOw0K
PiAraW50IG1pcGlfZHNpX2Rjc193cml0ZShzdHJ1Y3QgbWlwaV9kc2lfZGV2aWNlICpkZXYsIGlu
dCBjaGFubmVsLCBjb25zdCB1OA0KPiAqZGF0YSwNCj4gKwkJICAgICAgIHNpemVfdCBsZW4pOw0K
PiAraW50IG1pcGlfZHNpX2Rjc19yZWFkKHN0cnVjdCBtaXBpX2RzaV9kZXZpY2UgKmRldiwgaW50
IGNoYW5uZWwsIHU4IGNtZCwNCj4gKwkJICAgICAgdTggKmRhdGEsIHNpemVfdCBsZW4pOw0KPiAr
DQo+ICsjZGVmaW5lIG1pcGlfZHNpX2Rjc193cml0ZV9zZXEoZGV2LCBjaGFubmVsLCBzZXEuLi4p
IFwNCj4gKyh7XA0KPiArCWNvbnN0IHU4IGRbXSA9IHsgc2VxIH07XA0KPiArCUJVSUxEX0JVR19P
Tl9NU0coQVJSQVlfU0laRShkKSA+IDY0LCAiRENTIHNlcXVlbmNlIHRvbyBsb25nIGZvcg0KPiBz
dGFjayIpO1wNCj4gKwltaXBpX2RzaV9kY3Nfd3JpdGUoZGV2LCBjaGFubmVsLCBkLCBBUlJBWV9T
SVpFKGQpKTtcDQo+ICt9KQ0KPiArDQo+ICsjZGVmaW5lIG1pcGlfZHNpX2Rjc193cml0ZV9zdGF0
aWNfc2VxKGRldiwgY2hhbm5lbCwgc2VxLi4uKSBcDQo+ICsoe1wNCj4gKwlzdGF0aWMgY29uc3Qg
dTggZFtdID0geyBzZXEgfTtcDQo+ICsJbWlwaV9kc2lfZGNzX3dyaXRlKGRldiwgY2hhbm5lbCwg
ZCwgQVJSQVlfU0laRShkKSk7XA0KPiArfSkNCj4gKw0KPiArI2VuZGlmIC8qIF9fTUlQSV9EU0lf
QlVTX18gKi8NCg0KSSBtYXkgd2VsbCBoYXZlIG1pc3NlZCBzb21ldGhpbmcsIGJ1dCBJIGNhbid0
IHNlZSBleGFjdGx5IGhvdyBhIGNvbW1hbmQgbW9kZQ0KdXBkYXRlIHdvdWxkIGJlIGRvbmUgd2l0
aCB0aGlzIGludGVyZmFjZS4gV291bGQgdGhpcyByZXF1aXJlIHJlcGVhdGVkIGNhbGxzIHRvDQou
dHJhbnNmZXI/IFN1Y2ggdHJhbnNmZXJzIHdvdWxkIG5lZWQgdG8gYmUgZmxhZ2dlZCBhcyByZXF1
aXJpbmcNCnN5bmNocm9uaXNhdGlvbiB3aXRoIGEgdGVhcmluZyBlZmZlY3QgY29udHJvbCBzaWdu
YWwgLSBlaXRoZXIgdGhlIGluYmFuZA0KbWV0aG9kIG9yIGEgZGVkaWNhdGVkIGxpbmUuIEkgc3Vz
cGVjdCBtYW55IGhhcmR3YXJlIGltcGxlbWVudGF0aW9ucyB3aWxsIGhhdmUNCmEgc3BlY2lmaWMg
bWV0aG9kIGZvciB0cmFuc2ZlcnJpbmcgcGl4ZWwgZGF0YSBpbiBhIERTSSBjb21tYW5kIG1vZGUg
dHJhbnNmZXIuDQoNClRoZSBjb21tYW5kIHNlbmRpbmcgcGVyaW9kIGR1cmluZyB2aWRlbyBtb2Rl
IHNob3VsZCBwcm9iYWJseSBiZSBjb25maWd1cmFibGUNCm9uIGEgcGVyLXRyYW5zZmVyIGJhc2lz
LiBTb21lIGNvbW1hbmRzIGhhdmUgdG8gYmUgc3luY2hyb25pc2VkIHdpdGggdmVydGljYWwNCmJs
YW5raW5nLCBvdGhlcnMgZG8gbm90LiBUaGlzIGNvdWxkIHBlcmhhcHMgYmUgY29tYmluZWQgd2l0
aCBhIHdpZGVyDQpjb25maWd1cmF0aW9uIG9wdGlvbiBmb3IgYSBnaXZlbiBwYW5lbCBvciBpbnRl
cmZhY2UuIFNpbWlsYXJseSwgc2VsZWN0aW9uIG9mDQpsb3cgcG93ZXIgKExQKSBhbmQgaGlnaCBz
cGVlZCAoSFMpIG1vZGUgb24gYSBwZXItdHJhbnNmZXIgYmFzaXMgY2FuIGJlIG5lZWRlZA0KZm9y
IHNvbWUgcGFuZWxzLg0KDQpJcyB0aGVyZSBhIG1lY2hhbmlzbSBmb3IgY29udHJvbGxpbmcgdWx0
cmEtbG93IHBvd2VyIHN0YXRlIChVTFBTKSBlbnRyeT8gQWxzbywNCmlzIHRoZXJlIGEgbWV0aG9k
IGZvciBzZW5kaW5nIGFyYml0cmFyeSB0cmlnZ2VyIG1lc3NhZ2VzIChlZyB0aGUgcmVzZXQNCnRy
aWdnZXIpPw0KDQpUaGFua3MsDQoNCkJlcnQuDQotLSANCkJlcnQgS2Vud2FyZA0KU29mdHdhcmUg
RW5naW5lZXINCkJyb2FkY29tIE1vYmlsZSBQbGF0Zm9ybSBTb2x1dGlvbnMNCkNhbWJyaWRnZSwg
VUsNCg=

Re: [RFC PATCH 1/4] mipi-dsi-bus: add MIPI DSI bus support

From: Andrzej Hajda <hidden>
Date: 2013-10-10 09:57:53

On 10/07/2013 12:47 PM, Bert Kenward wrote:
On Tuesday September 24 2013 at 15:23, Andrzej Hajda wrote:
quoted
MIPI DSI is a high-speed serial interface to transmit
data from/to host to display module.

Signed-off-by: Andrzej Hajda <redacted>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/video/display/Kconfig        |   4 +
 drivers/video/display/Makefile       |   1 +
 drivers/video/display/mipi-dsi-bus.c | 332
+++++++++++++++++++++++++++++++++++
 include/video/display.h              |   3 +
 include/video/mipi-dsi-bus.h         | 144 +++++++++++++++
 5 files changed, 484 insertions(+)
<snipped as far as mipi-dsi-bus.h
quoted
diff --git a/include/video/mipi-dsi-bus.h b/include/video/mipi-dsi-bus.h
new file mode 100644
index 0000000..a78792d
--- /dev/null
+++ b/include/video/mipi-dsi-bus.h
@@ -0,0 +1,144 @@
+/*
+ * MIPI DSI Bus
+ *
+ * Copyright (C) 2013, Samsung Electronics, Co., Ltd.
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MIPI_DSI_BUS_H__
+#define __MIPI_DSI_BUS_H__
+
+#include <linux/device.h>
+#include <video/videomode.h>
+
+struct mipi_dsi_bus;
+struct mipi_dsi_device;
+
+struct mipi_dsi_bus_ops {
+	int (*set_power)(struct mipi_dsi_bus *bus, struct mipi_dsi_device
*dev,
+			 bool on);
+	int (*set_stream)(struct mipi_dsi_bus *bus, struct mipi_dsi_device
*dev,
+			  bool on);
+	int (*transfer)(struct mipi_dsi_bus *bus, struct mipi_dsi_device
*dev,
+			u8 type, const u8 *tx_buf, size_t tx_len, u8 *rx_buf,
+			size_t rx_len);
+};
+
+#define DSI_MODE_VIDEO			(1 << 0)
+#define DSI_MODE_VIDEO_BURST		(1 << 1)
+#define DSI_MODE_VIDEO_SYNC_PULSE	(1 << 2)
+#define DSI_MODE_VIDEO_AUTO_VERT	(1 << 3)
+#define DSI_MODE_VIDEO_HSE		(1 << 4)
+#define DSI_MODE_VIDEO_HFP		(1 << 5)
+#define DSI_MODE_VIDEO_HBP		(1 << 6)
+#define DSI_MODE_VIDEO_HSA		(1 << 7)
+#define DSI_MODE_VSYNC_FLUSH		(1 << 8)
+#define DSI_MODE_EOT_PACKET		(1 << 9)
+
+enum mipi_dsi_pixel_format {
+	DSI_FMT_RGB888,
+	DSI_FMT_RGB666,
+	DSI_FMT_RGB666_PACKED,
+	DSI_FMT_RGB565,
+};
+
+struct mipi_dsi_interface_params {
+	enum mipi_dsi_pixel_format format;
+	unsigned long mode;
+	unsigned long hs_clk_freq;
+	unsigned long esc_clk_freq;
+	unsigned char data_lanes;
+	unsigned char cmd_allow;
+};
+
+struct mipi_dsi_bus {
+	struct device *dev;
+	const struct mipi_dsi_bus_ops *ops;
+};
+
+#define MIPI_DSI_MODULE_PREFIX		"mipi-dsi:"
+#define MIPI_DSI_NAME_SIZE		32
+
+struct mipi_dsi_device_id {
+	char name[MIPI_DSI_NAME_SIZE];
+	__kernel_ulong_t driver_data	/* Data private to the driver */
+			__aligned(sizeof(__kernel_ulong_t));
+};
+
+struct mipi_dsi_device {
+	char name[MIPI_DSI_NAME_SIZE];
+	int id;
+	struct device dev;
+
+	const struct mipi_dsi_device_id *id_entry;
+	struct mipi_dsi_bus *bus;
+	struct videomode vm;
+	struct mipi_dsi_interface_params params;
+};
+
+#define to_mipi_dsi_device(d)	container_of(d, struct
mipi_dsi_device, dev)
+
+int mipi_dsi_device_register(struct mipi_dsi_device *dev,
+			     struct mipi_dsi_bus *bus);
+void mipi_dsi_device_unregister(struct mipi_dsi_device *dev);
+
+struct mipi_dsi_driver {
+	int(*probe)(struct mipi_dsi_device *);
+	int(*remove)(struct mipi_dsi_device *);
+	struct device_driver driver;
+	const struct mipi_dsi_device_id *id_table;
+};
+
+#define to_mipi_dsi_driver(d)	container_of(d, struct
mipi_dsi_driver, driver)
+
+int mipi_dsi_driver_register(struct mipi_dsi_driver *drv);
+void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv);
+
+static inline void *mipi_dsi_get_drvdata(const struct mipi_dsi_device
*dev)
+{
+	return dev_get_drvdata(&dev->dev);
+}
+
+static inline void mipi_dsi_set_drvdata(struct mipi_dsi_device *dev,
+					void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+int of_mipi_dsi_register_devices(struct mipi_dsi_bus *bus);
+void mipi_dsi_unregister_devices(struct mipi_dsi_bus *bus);
+
+/* module_mipi_dsi_driver() - Helper macro for drivers that don't do
+ * anything special in module init/exit.  This eliminates a lot of
+ * boilerplate.  Each module may only use this macro once, and
+ * calling it replaces module_init() and module_exit()
+ */
+#define module_mipi_dsi_driver(__mipi_dsi_driver) \
+	module_driver(__mipi_dsi_driver, mipi_dsi_driver_register, \
+			mipi_dsi_driver_unregister)
+
+int mipi_dsi_set_power(struct mipi_dsi_device *dev, bool on);
+int mipi_dsi_set_stream(struct mipi_dsi_device *dev, bool on);
+int mipi_dsi_dcs_write(struct mipi_dsi_device *dev, int channel, const u8
*data,
+		       size_t len);
+int mipi_dsi_dcs_read(struct mipi_dsi_device *dev, int channel, u8 cmd,
+		      u8 *data, size_t len);
+
+#define mipi_dsi_dcs_write_seq(dev, channel, seq...) \
+({\
+	const u8 d[] = { seq };\
+	BUILD_BUG_ON_MSG(ARRAY_SIZE(d) > 64, "DCS sequence too long for
stack");\
+	mipi_dsi_dcs_write(dev, channel, d, ARRAY_SIZE(d));\
+})
+
+#define mipi_dsi_dcs_write_static_seq(dev, channel, seq...) \
+({\
+	static const u8 d[] = { seq };\
+	mipi_dsi_dcs_write(dev, channel, d, ARRAY_SIZE(d));\
+})
+
+#endif /* __MIPI_DSI_BUS__ */
I may well have missed something,
 but I can't see exactly how a command mode
update would be done with this interface. Would this require repeated calls to
.transfer? Such transfers would need to be flagged as requiring
synchronisation with a tearing effect control signal - either the inband
method or a dedicated line. I suspect many hardware implementations will have
a specific method for transferring pixel data in a DSI command mode transfer.

The command sending period during video mode should probably be configurable
on a per-transfer basis. Some commands have to be synchronised with vertical
blanking, others do not. This could perhaps be combined with a wider
configuration option for a given panel or interface. Similarly, selection of
low power (LP) and high speed (HS) mode on a per-transfer basis can be needed
for some panels.

Is there a mechanism for controlling ultra-low power state (ULPS) entry? Also,
is there a method for sending arbitrary trigger messages (eg the reset
trigger)?
Thanks for the feedback.
The current dsi bus implementation was just made to work with the
hw I have. It should be extended to be more generic, but I hope
now it is just matter of adding good opses and parameters.
Feel free to propose new opses.

Andrzej
Thanks,

Bert.

Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help