All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/4] Common Display Framework-TF
@ 2013-01-30 15:38 ` Tomasz Figa
  0 siblings, 0 replies; 36+ messages in thread
From: Tomasz Figa @ 2013-01-30 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	t.figa, tomasz.figa, Daniel Vetter, Marcus Lorentzon,
	Laurent Pinchart, rob, tomi.valkeinen, Vikas Sajjan, inki.dae,
	dh09.lee, ville.syrjala, s.nawrocki

Hi,

After pretty long time of trying to adapt Exynos-specific DSI display support
to Common Display Framework I'm ready to show some preliminary RFC patches.
This series shows some ideas for CDF that came to my mind during my work,
some changes based on comments received by Tomi's edition of CDF and also
preliminarys version of Exynos DSI (video source part only, still with some
FIXMEs) and Samsung S6E8AX0 DSI panel drivers.

It is heavily based on Tomi's work which can be found here:
http://thread.gmane.org/gmane.comp.video.dri.devel/78227

The code might be a bit hacky in places, as I wanted to get it to a kind
of complete and working state first. However I hope that some of the ideas
might useful for further works.

So, here comes the TF edition of Common Clock Framework.
--------------------------------------------------------

Changes done to Tomi's edition of CDF:

 - Replaced set_operation_mode, set_pixel_format and set_size video source
   operations with get_params display entity operation, as it was originally
   in Laurent's version.

   We have discussed this matter on the mailing list
   and decided that it would be better to have the source ask the sink for
   its parameter structure and do whatever appropriate with it.

 - Defined a preliminary set of DSI bus parameters.

   Following parameters are defined:

   1. Pixel format used for video data transmission.
   2. Mode flags (several bit flags ORed together):
     a) DSI_MODE_VIDEO - entity uses video mode (as opposed to command mode),
        following DSI_MODE_VIDEO_* flags are relevant only if this flag is set.
        b) DSI_MODE_VIDEO_BURST - entity uses burst transfer for video data
        c) DSI_MODE_VIDEO_SYNC_PULSE - entity uses sync pulses (as opposed
           to sync events)
        d) DSI_MODE_VIDEO_AUTO_VERT - entity uses automatic video mode
           detection
        e) DSI_MODE_VIDEO_HSE - entity needs horizontal sync end packets
        f) DSI_MODE_VIDEO_HFP - entity needs horizontal front porch area
        g) DSI_MODE_VIDEO_HBP - entity needs horizontal back porch area
        h) DSI_MODE_VIDEO_HSA - entity needs horizontal sync active area
     i) DSI_MODE_VSYNC_FLUSH - vertical sync pulse flushes video data
     j) DSI_MODE_EOT_PACKET - entity needs EoT packets
   3. Bit (serial) clock frequency in HS mode.
   4. Escape mode clock frequency.
   5. Mask of used data lanes (each bit represents single lane).
   6. Command allow area in video mode - amount of lines after transmitting
      video data when generic commands are accepted.

   Feel free to suggest anything missing or wrong, as the list of
   parameters is based merely on what was used in original Exynos MIPI
   DSIM driver.

 - Redesigned source-entity matching.

   Now each source has name string and integer id and each entity has
   source name and source id. In addition, they can have of_node specified,
   which is used for Device Tree-based matching.

   The matching procedure is as follows:

   1. Matching takes place when display entity registers.
     a) If there is of_node specified for the entity then "video-source"
        phandle of this node is being parsed and list of registered sources
        is traversed in search of a source with of_node received from
        parsing the phandle.
     b) Otherwise the matching key is a pair of source name and id.
   2. If no matching source is found, display_entity_register returns
      -EPROBE_DEFER error which causes the entity driver to defer its
      probe until the source registers.
   3. Otherwise an optional bind operation of video source is called,
      sink field of source and source field of entity are set and the
      matching ends successfully.

 - Some initial concept of source-entity cross-locking.

   Only video source is protected at the moment, as I still have to think
   a bit about locking the entity in a way where removing it by user request
   is still possible.

 - Dropped any panels and chips that I can't test.

   They are irrelevant for this series, so there is no point in including them.

 - Added Exynos DSI video source driver.

   This is a new driver for the DSI controller found in Exynos SoCs. It still
   needs some work, but in current state can be considered an example of DSI
   video source implementation for my version of CDF.

 - Added Samsung S6E8AX0 DSI panel driver.

   This is the old Exynos-specific driver, just migrated to CDF and with
   some hacks to provide control over entity state to userspace using
   lcd device. However it can be used to demonstrate video source ops in use.

Feel free to comment as much as you can.

Tomasz Figa (4):
  video: add display-core
  video: add makefile & kconfig
  video: display: Add exynos-dsi video source driver
  video: display: Add Samsung s6e8ax0 display panel driver

 drivers/video/Kconfig                     |    1 +
 drivers/video/Makefile                    |    1 +
 drivers/video/display/Kconfig             |   16 +
 drivers/video/display/Makefile            |    3 +
 drivers/video/display/display-core.c      |  295 +++++++
 drivers/video/display/panel-s6e8ax0.c     | 1027 ++++++++++++++++++++++
 drivers/video/display/source-exynos_dsi.c | 1313 +++++++++++++++++++++++++++++
 include/video/display.h                   |  230 +++++
 include/video/exynos_dsi.h                |   41 +
 include/video/panel-s6e8ax0.h             |   41 +
 10 files changed, 2968 insertions(+)
 create mode 100644 drivers/video/display/Kconfig
 create mode 100644 drivers/video/display/Makefile
 create mode 100644 drivers/video/display/display-core.c
 create mode 100644 drivers/video/display/panel-s6e8ax0.c
 create mode 100644 drivers/video/display/source-exynos_dsi.c
 create mode 100644 include/video/display.h
 create mode 100644 include/video/exynos_dsi.h
 create mode 100644 include/video/panel-s6e8ax0.h

-- 
1.8.1


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [RFC PATCH 0/4] Common Display Framework-TF
@ 2013-01-30 15:38 ` Tomasz Figa
  0 siblings, 0 replies; 36+ messages in thread
From: Tomasz Figa @ 2013-01-30 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	t.figa, tomasz.figa, Daniel Vetter, Marcus Lorentzon,
	Laurent Pinchart, rob, tomi.valkeinen, Vikas Sajjan, inki.dae,
	dh09.lee, ville.syrjala, s.nawrocki

Hi,

After pretty long time of trying to adapt Exynos-specific DSI display support
to Common Display Framework I'm ready to show some preliminary RFC patches.
This series shows some ideas for CDF that came to my mind during my work,
some changes based on comments received by Tomi's edition of CDF and also
preliminarys version of Exynos DSI (video source part only, still with some
FIXMEs) and Samsung S6E8AX0 DSI panel drivers.

It is heavily based on Tomi's work which can be found here:
http://thread.gmane.org/gmane.comp.video.dri.devel/78227

The code might be a bit hacky in places, as I wanted to get it to a kind
of complete and working state first. However I hope that some of the ideas
might useful for further works.

So, here comes the TF edition of Common Clock Framework.
--------------------------------------------------------

Changes done to Tomi's edition of CDF:

 - Replaced set_operation_mode, set_pixel_format and set_size video source
   operations with get_params display entity operation, as it was originally
   in Laurent's version.

   We have discussed this matter on the mailing list
   and decided that it would be better to have the source ask the sink for
   its parameter structure and do whatever appropriate with it.

 - Defined a preliminary set of DSI bus parameters.

   Following parameters are defined:

   1. Pixel format used for video data transmission.
   2. Mode flags (several bit flags ORed together):
     a) DSI_MODE_VIDEO - entity uses video mode (as opposed to command mode),
        following DSI_MODE_VIDEO_* flags are relevant only if this flag is set.
        b) DSI_MODE_VIDEO_BURST - entity uses burst transfer for video data
        c) DSI_MODE_VIDEO_SYNC_PULSE - entity uses sync pulses (as opposed
           to sync events)
        d) DSI_MODE_VIDEO_AUTO_VERT - entity uses automatic video mode
           detection
        e) DSI_MODE_VIDEO_HSE - entity needs horizontal sync end packets
        f) DSI_MODE_VIDEO_HFP - entity needs horizontal front porch area
        g) DSI_MODE_VIDEO_HBP - entity needs horizontal back porch area
        h) DSI_MODE_VIDEO_HSA - entity needs horizontal sync active area
     i) DSI_MODE_VSYNC_FLUSH - vertical sync pulse flushes video data
     j) DSI_MODE_EOT_PACKET - entity needs EoT packets
   3. Bit (serial) clock frequency in HS mode.
   4. Escape mode clock frequency.
   5. Mask of used data lanes (each bit represents single lane).
   6. Command allow area in video mode - amount of lines after transmitting
      video data when generic commands are accepted.

   Feel free to suggest anything missing or wrong, as the list of
   parameters is based merely on what was used in original Exynos MIPI
   DSIM driver.

 - Redesigned source-entity matching.

   Now each source has name string and integer id and each entity has
   source name and source id. In addition, they can have of_node specified,
   which is used for Device Tree-based matching.

   The matching procedure is as follows:

   1. Matching takes place when display entity registers.
     a) If there is of_node specified for the entity then "video-source"
        phandle of this node is being parsed and list of registered sources
        is traversed in search of a source with of_node received from
        parsing the phandle.
     b) Otherwise the matching key is a pair of source name and id.
   2. If no matching source is found, display_entity_register returns
      -EPROBE_DEFER error which causes the entity driver to defer its
      probe until the source registers.
   3. Otherwise an optional bind operation of video source is called,
      sink field of source and source field of entity are set and the
      matching ends successfully.

 - Some initial concept of source-entity cross-locking.

   Only video source is protected at the moment, as I still have to think
   a bit about locking the entity in a way where removing it by user request
   is still possible.

 - Dropped any panels and chips that I can't test.

   They are irrelevant for this series, so there is no point in including them.

 - Added Exynos DSI video source driver.

   This is a new driver for the DSI controller found in Exynos SoCs. It still
   needs some work, but in current state can be considered an example of DSI
   video source implementation for my version of CDF.

 - Added Samsung S6E8AX0 DSI panel driver.

   This is the old Exynos-specific driver, just migrated to CDF and with
   some hacks to provide control over entity state to userspace using
   lcd device. However it can be used to demonstrate video source ops in use.

Feel free to comment as much as you can.

Tomasz Figa (4):
  video: add display-core
  video: add makefile & kconfig
  video: display: Add exynos-dsi video source driver
  video: display: Add Samsung s6e8ax0 display panel driver

 drivers/video/Kconfig                     |    1 +
 drivers/video/Makefile                    |    1 +
 drivers/video/display/Kconfig             |   16 +
 drivers/video/display/Makefile            |    3 +
 drivers/video/display/display-core.c      |  295 +++++++
 drivers/video/display/panel-s6e8ax0.c     | 1027 ++++++++++++++++++++++
 drivers/video/display/source-exynos_dsi.c | 1313 +++++++++++++++++++++++++++++
 include/video/display.h                   |  230 +++++
 include/video/exynos_dsi.h                |   41 +
 include/video/panel-s6e8ax0.h             |   41 +
 10 files changed, 2968 insertions(+)
 create mode 100644 drivers/video/display/Kconfig
 create mode 100644 drivers/video/display/Makefile
 create mode 100644 drivers/video/display/display-core.c
 create mode 100644 drivers/video/display/panel-s6e8ax0.c
 create mode 100644 drivers/video/display/source-exynos_dsi.c
 create mode 100644 include/video/display.h
 create mode 100644 include/video/exynos_dsi.h
 create mode 100644 include/video/panel-s6e8ax0.h

-- 
1.8.1

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [RFC PATCH 1/4] video: add display-core
  2013-01-30 15:38 ` Tomasz Figa
@ 2013-01-30 15:39   ` Tomasz Figa
  -1 siblings, 0 replies; 36+ messages in thread
From: Tomasz Figa @ 2013-01-30 15:39 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	t.figa, tomasz.figa, Daniel Vetter, Marcus Lorentzon,
	Laurent Pinchart, rob, tomi.valkeinen, Vikas Sajjan, inki.dae,
	dh09.lee, ville.syrjala, s.nawrocki

Signed-off-by: Tomasz Figa <t.figa@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/video/display/display-core.c | 295 +++++++++++++++++++++++++++++++++++
 include/video/display.h              | 230 +++++++++++++++++++++++++++
 2 files changed, 525 insertions(+)
 create mode 100644 drivers/video/display/display-core.c
 create mode 100644 include/video/display.h

diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
new file mode 100644
index 0000000..ed49384
--- /dev/null
+++ b/drivers/video/display/display-core.c
@@ -0,0 +1,295 @@
+/*
+ * Display Core
+ *
+ * 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.
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/videomode.h>
+
+#include <video/display.h>
+
+static struct video_source *video_source_bind(struct display_entity *entity);
+static void video_source_unbind(struct display_entity *entity);
+
+/* -----------------------------------------------------------------------------
+ * Display Entity
+ */
+
+static LIST_HEAD(display_entity_list);
+static DEFINE_MUTEX(display_entity_mutex);
+
+struct display_entity *display_entity_get_first(void)
+{
+	/* FIXME: Don't we need some locking here? */
+
+	if (list_empty(&display_entity_list))
+		return NULL;
+
+	return list_first_entry(&display_entity_list, struct display_entity,
+			list);
+}
+EXPORT_SYMBOL(display_entity_get_first);
+
+int display_entity_set_state(struct display_entity *entity,
+			     enum display_entity_state state)
+{
+	int ret;
+
+	if (entity->state = state)
+		return 0;
+
+	if (!entity->ops || !entity->ops->set_state)
+		return 0;
+
+	ret = entity->ops->set_state(entity, state);
+	if (ret < 0)
+		return ret;
+
+	entity->state = state;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(display_entity_set_state);
+
+int display_entity_get_modes(struct display_entity *entity,
+			     const struct videomode **modes)
+{
+	if (!entity->ops || !entity->ops->get_modes)
+		return 0;
+
+	return entity->ops->get_modes(entity, modes);
+}
+EXPORT_SYMBOL_GPL(display_entity_get_modes);
+
+int display_entity_get_size(struct display_entity *entity,
+			    unsigned int *width, unsigned int *height)
+{
+	if (!entity->ops || !entity->ops->get_size)
+		return -EOPNOTSUPP;
+
+	return entity->ops->get_size(entity, width, height);
+}
+EXPORT_SYMBOL_GPL(display_entity_get_size);
+
+int display_entity_get_params(struct display_entity *entity,
+			      struct display_entity_interface_params *params)
+{
+	if (!entity->ops || !entity->ops->get_params)
+		return -EOPNOTSUPP;
+
+	return entity->ops->get_params(entity, params);
+}
+EXPORT_SYMBOL_GPL(display_entity_get_params);
+
+static void display_entity_release(struct kref *ref)
+{
+	struct display_entity *entity +		container_of(ref, struct display_entity, ref);
+
+	if (entity->release)
+		entity->release(entity);
+}
+
+struct display_entity *display_entity_get(struct display_entity *entity)
+{
+	if (entity = NULL)
+		return NULL;
+
+	kref_get(&entity->ref);
+	return entity;
+}
+EXPORT_SYMBOL_GPL(display_entity_get);
+
+void display_entity_put(struct display_entity *entity)
+{
+	kref_put(&entity->ref, display_entity_release);
+}
+EXPORT_SYMBOL_GPL(display_entity_put);
+
+int __must_check __display_entity_register(struct display_entity *entity,
+					   struct module *owner)
+{
+	struct video_source *src;
+
+	kref_init(&entity->ref);
+	entity->owner = owner;
+	entity->state = DISPLAY_ENTITY_STATE_OFF;
+	entity->source = NULL;
+
+	src = video_source_bind(entity);
+	if (!src)
+		return -EPROBE_DEFER;
+
+	mutex_lock(&display_entity_mutex);
+	list_add(&entity->list, &display_entity_list);
+	mutex_unlock(&display_entity_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(__display_entity_register);
+
+void display_entity_unregister(struct display_entity *entity)
+{
+	video_source_unbind(entity);
+
+	mutex_lock(&display_entity_mutex);
+
+	list_del(&entity->list);
+	mutex_unlock(&display_entity_mutex);
+
+	display_entity_put(entity);
+}
+EXPORT_SYMBOL_GPL(display_entity_unregister);
+
+/* -----------------------------------------------------------------------------
+ * Video Source
+ */
+
+static LIST_HEAD(video_source_list);
+static DEFINE_MUTEX(video_source_mutex);
+
+static void video_source_release(struct kref *ref)
+{
+	struct video_source *src +		container_of(ref, struct video_source, ref);
+
+	if (src->release)
+		src->release(src);
+}
+
+static struct video_source *video_source_get(struct video_source *src)
+{
+	if (src = NULL)
+		return NULL;
+
+	kref_get(&src->ref);
+	if (!try_module_get(src->owner)) {
+		kref_put(&src->ref, video_source_release);
+		return NULL;
+	}
+
+	return src;
+}
+
+static void video_source_put(struct video_source *src)
+{
+	module_put(src->owner);
+	kref_put(&src->ref, video_source_release);
+}
+
+int __must_check __video_source_register(struct video_source *src,
+							struct module *owner)
+{
+	kref_init(&src->ref);
+	src->owner = owner;
+
+	mutex_lock(&video_source_mutex);
+	list_add(&src->list, &video_source_list);
+
+	mutex_unlock(&video_source_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(__video_source_register);
+
+void video_source_unregister(struct video_source *src)
+{
+	mutex_lock(&video_source_mutex);
+
+	list_del(&src->list);
+	mutex_unlock(&video_source_mutex);
+
+	kref_put(&src->ref, video_source_release);
+}
+EXPORT_SYMBOL_GPL(video_source_unregister);
+
+static struct video_source *video_source_bind(struct display_entity *entity)
+{
+	struct video_source *src = NULL;
+	int ret;
+
+	if (entity->source)
+		return entity->source;
+
+	mutex_lock(&video_source_mutex);
+
+	if (entity->of_node) {
+		struct device_node *np;
+
+		np = of_parse_phandle(entity->of_node, "video-source", 0);
+		if (!np)
+			goto unlock;
+
+		list_for_each_entry(src, &video_source_list, list) {
+			if (src->of_node = np)
+				goto found;
+		}
+
+		src = NULL;
+		goto unlock;
+	}
+
+	if (!entity->src_name)
+		goto unlock;
+
+	list_for_each_entry(src, &video_source_list, list) {
+		if (src->id != entity->src_id)
+			continue;
+		if (!strcmp(src->name, entity->src_name))
+			goto found;
+	}
+
+	src = NULL;
+	goto unlock;
+
+found:
+	video_source_get(src);
+
+	if (src->common_ops->bind) {
+		ret = src->common_ops->bind(src, entity);
+		if (ret != 0) {
+			video_source_put(src);
+			src = NULL;
+			goto unlock;
+		}
+	}
+
+	src->sink = entity;
+	entity->source = src;
+
+unlock:
+	mutex_unlock(&video_source_mutex);
+
+	return src;
+}
+
+static void video_source_unbind(struct display_entity *entity)
+{
+	struct video_source *src = entity->source;
+
+	if (!src)
+		return;
+
+	if (src->common_ops && src->common_ops->unbind)
+		src->common_ops->unbind(src, entity);
+
+	src->sink = NULL;
+	entity->source = NULL;
+
+	video_source_put(src);
+}
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Display Core");
+MODULE_LICENSE("GPL");
diff --git a/include/video/display.h b/include/video/display.h
new file mode 100644
index 0000000..7ffea2c
--- /dev/null
+++ b/include/video/display.h
@@ -0,0 +1,230 @@
+/*
+ * Display Core
+ *
+ * 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 __DISPLAY_H__
+#define __DISPLAY_H__
+
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <video/omapdss.h>
+
+struct display_entity;
+struct video_source;
+struct videomode;
+
+/* -----------------------------------------------------------------------------
+ * Display Entity
+ */
+
+/* Hack to get the first registered display entity */
+struct display_entity *display_entity_get_first(void);
+
+enum display_entity_state {
+	DISPLAY_ENTITY_STATE_OFF,
+	DISPLAY_ENTITY_STATE_STANDBY,
+	DISPLAY_ENTITY_STATE_ON,
+};
+
+enum display_entity_interface_type {
+	DISPLAY_ENTITY_INTERFACE_DPI,
+	DISPLAY_ENTITY_INTERFACE_DSI,
+};
+
+#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 display_entity_interface_params {
+	enum display_entity_interface_type type;
+	union {
+		struct mipi_dsi_interface_params dsi;
+	} p;
+};
+
+struct display_entity_control_ops {
+	int (*set_state)(struct display_entity *ent,
+			 enum display_entity_state state);
+	int (*update)(struct display_entity *ent,
+			void (*callback)(int, void *), void *data);
+	int (*get_modes)(struct display_entity *ent,
+			 const struct videomode **modes);
+	int (*get_params)(struct display_entity *ent,
+			  struct display_entity_interface_params *params);
+	int (*get_size)(struct display_entity *ent,
+			unsigned int *width, unsigned int *height);
+};
+
+struct display_entity {
+	struct list_head list;
+	struct device *dev;
+	struct device_node *of_node;
+	struct module *owner;
+	struct kref ref;
+
+	const char *src_name;
+	int src_id;
+	struct video_source *source;
+
+	const struct display_entity_control_ops *ops;
+
+	void(*release)(struct display_entity *ent);
+
+	enum display_entity_state state;
+};
+
+int display_entity_set_state(struct display_entity *entity,
+			     enum display_entity_state state);
+int display_entity_get_params(struct display_entity *entity,
+			      struct display_entity_interface_params *params);
+int display_entity_get_modes(struct display_entity *entity,
+			     const struct videomode **modes);
+int display_entity_get_size(struct display_entity *entity,
+			    unsigned int *width, unsigned int *height);
+
+struct display_entity *display_entity_get(struct display_entity *entity);
+void display_entity_put(struct display_entity *entity);
+
+int __must_check __display_entity_register(struct display_entity *entity,
+					   struct module *owner);
+void display_entity_unregister(struct display_entity *entity);
+
+#define display_entity_register(display_entity) \
+	__display_entity_register(display_entity, THIS_MODULE)
+
+
+/* -----------------------------------------------------------------------------
+ * Video Source
+ */
+
+enum video_source_stream_state {
+	DISPLAY_ENTITY_STREAM_STOPPED,
+	DISPLAY_ENTITY_STREAM_CONTINUOUS,
+};
+
+struct common_video_source_ops {
+	int (*set_stream)(struct video_source *src,
+			 enum video_source_stream_state state);
+	int (*bind)(struct video_source *src, struct display_entity *sink);
+	int (*unbind)(struct video_source *src, struct display_entity *sink);
+};
+
+struct dpi_video_source_ops {
+	int (*set_videomode)(struct video_source *src,
+			const struct videomode *vm);
+	int (*set_data_lines)(struct video_source *src, int lines);
+};
+
+struct dsi_video_source_ops {
+	/* enable/disable dsi bus */
+	int (*enable)(struct video_source *src);
+	int (*disable)(struct video_source *src);
+
+	/* bus configuration */
+	int (*configure_pins)(struct video_source *src,
+			const struct omap_dsi_pin_config *pins);
+	int (*set_clocks)(struct video_source *src,
+			unsigned long ddr_clk,
+			unsigned long lp_clk);
+	/* NOTE: Do we really need configure_pins and set_clocks here? */
+
+	void (*enable_hs)(struct video_source *src, bool enable);
+
+	/* data transfer */
+	int (*dcs_write)(struct video_source *src, int channel,
+			const u8 *data, size_t len);
+	int (*dcs_read)(struct video_source *src, int channel, u8 dcs_cmd,
+			u8 *data, size_t len);
+	/* NOTE: Do we need more write and read types? */
+
+	int (*update)(struct video_source *src, int channel,
+			void (*callback)(int, void *), void *data);
+};
+
+struct dvi_video_source_ops {
+	int (*set_videomode)(struct video_source *src,
+			const struct videomode *vm);
+};
+
+struct video_source {
+	struct list_head list;
+	struct device *dev;
+	struct device_node *of_node;
+	struct module *owner;
+	struct kref ref;
+
+	struct display_entity *sink;
+
+	const char *name;
+	int id;
+
+	const struct common_video_source_ops *common_ops;
+
+	union {
+		const struct dpi_video_source_ops *dpi;
+		const struct dsi_video_source_ops *dsi;
+		const struct dvi_video_source_ops *dvi;
+	} ops;
+
+	void(*release)(struct video_source *src);
+};
+
+static inline int dsi_dcs_write(struct video_source *src, int channel,
+						const u8 *data, size_t len)
+{
+	if (!src->ops.dsi || !src->ops.dsi->dcs_write)
+		return -EINVAL;
+
+	return src->ops.dsi->dcs_write(src, channel, data, len);
+}
+
+static inline int dsi_dcs_read(struct video_source *src, int channel,
+					u8 dcs_cmd, u8 *data, size_t len)
+{
+	if (!src->ops.dsi || !src->ops.dsi->dcs_read)
+		return -EINVAL;
+
+	return src->ops.dsi->dcs_read(src, channel, dcs_cmd, data, len);
+}
+
+
+#define video_source_register(video_source) \
+	__video_source_register(video_source, THIS_MODULE)
+
+int __must_check __video_source_register(struct video_source *entity,
+							struct module *owner);
+void video_source_unregister(struct video_source *entity);
+
+#endif /* __DISPLAY_H__ */
-- 
1.8.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [RFC PATCH 1/4] video: add display-core
@ 2013-01-30 15:39   ` Tomasz Figa
  0 siblings, 0 replies; 36+ messages in thread
From: Tomasz Figa @ 2013-01-30 15:39 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	t.figa, tomasz.figa, Daniel Vetter, Marcus Lorentzon,
	Laurent Pinchart, rob, tomi.valkeinen, Vikas Sajjan, inki.dae,
	dh09.lee, ville.syrjala, s.nawrocki

Signed-off-by: Tomasz Figa <t.figa@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/video/display/display-core.c | 295 +++++++++++++++++++++++++++++++++++
 include/video/display.h              | 230 +++++++++++++++++++++++++++
 2 files changed, 525 insertions(+)
 create mode 100644 drivers/video/display/display-core.c
 create mode 100644 include/video/display.h

diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
new file mode 100644
index 0000000..ed49384
--- /dev/null
+++ b/drivers/video/display/display-core.c
@@ -0,0 +1,295 @@
+/*
+ * Display Core
+ *
+ * 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.
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/videomode.h>
+
+#include <video/display.h>
+
+static struct video_source *video_source_bind(struct display_entity *entity);
+static void video_source_unbind(struct display_entity *entity);
+
+/* -----------------------------------------------------------------------------
+ * Display Entity
+ */
+
+static LIST_HEAD(display_entity_list);
+static DEFINE_MUTEX(display_entity_mutex);
+
+struct display_entity *display_entity_get_first(void)
+{
+	/* FIXME: Don't we need some locking here? */
+
+	if (list_empty(&display_entity_list))
+		return NULL;
+
+	return list_first_entry(&display_entity_list, struct display_entity,
+			list);
+}
+EXPORT_SYMBOL(display_entity_get_first);
+
+int display_entity_set_state(struct display_entity *entity,
+			     enum display_entity_state state)
+{
+	int ret;
+
+	if (entity->state == state)
+		return 0;
+
+	if (!entity->ops || !entity->ops->set_state)
+		return 0;
+
+	ret = entity->ops->set_state(entity, state);
+	if (ret < 0)
+		return ret;
+
+	entity->state = state;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(display_entity_set_state);
+
+int display_entity_get_modes(struct display_entity *entity,
+			     const struct videomode **modes)
+{
+	if (!entity->ops || !entity->ops->get_modes)
+		return 0;
+
+	return entity->ops->get_modes(entity, modes);
+}
+EXPORT_SYMBOL_GPL(display_entity_get_modes);
+
+int display_entity_get_size(struct display_entity *entity,
+			    unsigned int *width, unsigned int *height)
+{
+	if (!entity->ops || !entity->ops->get_size)
+		return -EOPNOTSUPP;
+
+	return entity->ops->get_size(entity, width, height);
+}
+EXPORT_SYMBOL_GPL(display_entity_get_size);
+
+int display_entity_get_params(struct display_entity *entity,
+			      struct display_entity_interface_params *params)
+{
+	if (!entity->ops || !entity->ops->get_params)
+		return -EOPNOTSUPP;
+
+	return entity->ops->get_params(entity, params);
+}
+EXPORT_SYMBOL_GPL(display_entity_get_params);
+
+static void display_entity_release(struct kref *ref)
+{
+	struct display_entity *entity =
+		container_of(ref, struct display_entity, ref);
+
+	if (entity->release)
+		entity->release(entity);
+}
+
+struct display_entity *display_entity_get(struct display_entity *entity)
+{
+	if (entity == NULL)
+		return NULL;
+
+	kref_get(&entity->ref);
+	return entity;
+}
+EXPORT_SYMBOL_GPL(display_entity_get);
+
+void display_entity_put(struct display_entity *entity)
+{
+	kref_put(&entity->ref, display_entity_release);
+}
+EXPORT_SYMBOL_GPL(display_entity_put);
+
+int __must_check __display_entity_register(struct display_entity *entity,
+					   struct module *owner)
+{
+	struct video_source *src;
+
+	kref_init(&entity->ref);
+	entity->owner = owner;
+	entity->state = DISPLAY_ENTITY_STATE_OFF;
+	entity->source = NULL;
+
+	src = video_source_bind(entity);
+	if (!src)
+		return -EPROBE_DEFER;
+
+	mutex_lock(&display_entity_mutex);
+	list_add(&entity->list, &display_entity_list);
+	mutex_unlock(&display_entity_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(__display_entity_register);
+
+void display_entity_unregister(struct display_entity *entity)
+{
+	video_source_unbind(entity);
+
+	mutex_lock(&display_entity_mutex);
+
+	list_del(&entity->list);
+	mutex_unlock(&display_entity_mutex);
+
+	display_entity_put(entity);
+}
+EXPORT_SYMBOL_GPL(display_entity_unregister);
+
+/* -----------------------------------------------------------------------------
+ * Video Source
+ */
+
+static LIST_HEAD(video_source_list);
+static DEFINE_MUTEX(video_source_mutex);
+
+static void video_source_release(struct kref *ref)
+{
+	struct video_source *src =
+		container_of(ref, struct video_source, ref);
+
+	if (src->release)
+		src->release(src);
+}
+
+static struct video_source *video_source_get(struct video_source *src)
+{
+	if (src == NULL)
+		return NULL;
+
+	kref_get(&src->ref);
+	if (!try_module_get(src->owner)) {
+		kref_put(&src->ref, video_source_release);
+		return NULL;
+	}
+
+	return src;
+}
+
+static void video_source_put(struct video_source *src)
+{
+	module_put(src->owner);
+	kref_put(&src->ref, video_source_release);
+}
+
+int __must_check __video_source_register(struct video_source *src,
+							struct module *owner)
+{
+	kref_init(&src->ref);
+	src->owner = owner;
+
+	mutex_lock(&video_source_mutex);
+	list_add(&src->list, &video_source_list);
+
+	mutex_unlock(&video_source_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(__video_source_register);
+
+void video_source_unregister(struct video_source *src)
+{
+	mutex_lock(&video_source_mutex);
+
+	list_del(&src->list);
+	mutex_unlock(&video_source_mutex);
+
+	kref_put(&src->ref, video_source_release);
+}
+EXPORT_SYMBOL_GPL(video_source_unregister);
+
+static struct video_source *video_source_bind(struct display_entity *entity)
+{
+	struct video_source *src = NULL;
+	int ret;
+
+	if (entity->source)
+		return entity->source;
+
+	mutex_lock(&video_source_mutex);
+
+	if (entity->of_node) {
+		struct device_node *np;
+
+		np = of_parse_phandle(entity->of_node, "video-source", 0);
+		if (!np)
+			goto unlock;
+
+		list_for_each_entry(src, &video_source_list, list) {
+			if (src->of_node == np)
+				goto found;
+		}
+
+		src = NULL;
+		goto unlock;
+	}
+
+	if (!entity->src_name)
+		goto unlock;
+
+	list_for_each_entry(src, &video_source_list, list) {
+		if (src->id != entity->src_id)
+			continue;
+		if (!strcmp(src->name, entity->src_name))
+			goto found;
+	}
+
+	src = NULL;
+	goto unlock;
+
+found:
+	video_source_get(src);
+
+	if (src->common_ops->bind) {
+		ret = src->common_ops->bind(src, entity);
+		if (ret != 0) {
+			video_source_put(src);
+			src = NULL;
+			goto unlock;
+		}
+	}
+
+	src->sink = entity;
+	entity->source = src;
+
+unlock:
+	mutex_unlock(&video_source_mutex);
+
+	return src;
+}
+
+static void video_source_unbind(struct display_entity *entity)
+{
+	struct video_source *src = entity->source;
+
+	if (!src)
+		return;
+
+	if (src->common_ops && src->common_ops->unbind)
+		src->common_ops->unbind(src, entity);
+
+	src->sink = NULL;
+	entity->source = NULL;
+
+	video_source_put(src);
+}
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Display Core");
+MODULE_LICENSE("GPL");
diff --git a/include/video/display.h b/include/video/display.h
new file mode 100644
index 0000000..7ffea2c
--- /dev/null
+++ b/include/video/display.h
@@ -0,0 +1,230 @@
+/*
+ * Display Core
+ *
+ * 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 __DISPLAY_H__
+#define __DISPLAY_H__
+
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <video/omapdss.h>
+
+struct display_entity;
+struct video_source;
+struct videomode;
+
+/* -----------------------------------------------------------------------------
+ * Display Entity
+ */
+
+/* Hack to get the first registered display entity */
+struct display_entity *display_entity_get_first(void);
+
+enum display_entity_state {
+	DISPLAY_ENTITY_STATE_OFF,
+	DISPLAY_ENTITY_STATE_STANDBY,
+	DISPLAY_ENTITY_STATE_ON,
+};
+
+enum display_entity_interface_type {
+	DISPLAY_ENTITY_INTERFACE_DPI,
+	DISPLAY_ENTITY_INTERFACE_DSI,
+};
+
+#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 display_entity_interface_params {
+	enum display_entity_interface_type type;
+	union {
+		struct mipi_dsi_interface_params dsi;
+	} p;
+};
+
+struct display_entity_control_ops {
+	int (*set_state)(struct display_entity *ent,
+			 enum display_entity_state state);
+	int (*update)(struct display_entity *ent,
+			void (*callback)(int, void *), void *data);
+	int (*get_modes)(struct display_entity *ent,
+			 const struct videomode **modes);
+	int (*get_params)(struct display_entity *ent,
+			  struct display_entity_interface_params *params);
+	int (*get_size)(struct display_entity *ent,
+			unsigned int *width, unsigned int *height);
+};
+
+struct display_entity {
+	struct list_head list;
+	struct device *dev;
+	struct device_node *of_node;
+	struct module *owner;
+	struct kref ref;
+
+	const char *src_name;
+	int src_id;
+	struct video_source *source;
+
+	const struct display_entity_control_ops *ops;
+
+	void(*release)(struct display_entity *ent);
+
+	enum display_entity_state state;
+};
+
+int display_entity_set_state(struct display_entity *entity,
+			     enum display_entity_state state);
+int display_entity_get_params(struct display_entity *entity,
+			      struct display_entity_interface_params *params);
+int display_entity_get_modes(struct display_entity *entity,
+			     const struct videomode **modes);
+int display_entity_get_size(struct display_entity *entity,
+			    unsigned int *width, unsigned int *height);
+
+struct display_entity *display_entity_get(struct display_entity *entity);
+void display_entity_put(struct display_entity *entity);
+
+int __must_check __display_entity_register(struct display_entity *entity,
+					   struct module *owner);
+void display_entity_unregister(struct display_entity *entity);
+
+#define display_entity_register(display_entity) \
+	__display_entity_register(display_entity, THIS_MODULE)
+
+
+/* -----------------------------------------------------------------------------
+ * Video Source
+ */
+
+enum video_source_stream_state {
+	DISPLAY_ENTITY_STREAM_STOPPED,
+	DISPLAY_ENTITY_STREAM_CONTINUOUS,
+};
+
+struct common_video_source_ops {
+	int (*set_stream)(struct video_source *src,
+			 enum video_source_stream_state state);
+	int (*bind)(struct video_source *src, struct display_entity *sink);
+	int (*unbind)(struct video_source *src, struct display_entity *sink);
+};
+
+struct dpi_video_source_ops {
+	int (*set_videomode)(struct video_source *src,
+			const struct videomode *vm);
+	int (*set_data_lines)(struct video_source *src, int lines);
+};
+
+struct dsi_video_source_ops {
+	/* enable/disable dsi bus */
+	int (*enable)(struct video_source *src);
+	int (*disable)(struct video_source *src);
+
+	/* bus configuration */
+	int (*configure_pins)(struct video_source *src,
+			const struct omap_dsi_pin_config *pins);
+	int (*set_clocks)(struct video_source *src,
+			unsigned long ddr_clk,
+			unsigned long lp_clk);
+	/* NOTE: Do we really need configure_pins and set_clocks here? */
+
+	void (*enable_hs)(struct video_source *src, bool enable);
+
+	/* data transfer */
+	int (*dcs_write)(struct video_source *src, int channel,
+			const u8 *data, size_t len);
+	int (*dcs_read)(struct video_source *src, int channel, u8 dcs_cmd,
+			u8 *data, size_t len);
+	/* NOTE: Do we need more write and read types? */
+
+	int (*update)(struct video_source *src, int channel,
+			void (*callback)(int, void *), void *data);
+};
+
+struct dvi_video_source_ops {
+	int (*set_videomode)(struct video_source *src,
+			const struct videomode *vm);
+};
+
+struct video_source {
+	struct list_head list;
+	struct device *dev;
+	struct device_node *of_node;
+	struct module *owner;
+	struct kref ref;
+
+	struct display_entity *sink;
+
+	const char *name;
+	int id;
+
+	const struct common_video_source_ops *common_ops;
+
+	union {
+		const struct dpi_video_source_ops *dpi;
+		const struct dsi_video_source_ops *dsi;
+		const struct dvi_video_source_ops *dvi;
+	} ops;
+
+	void(*release)(struct video_source *src);
+};
+
+static inline int dsi_dcs_write(struct video_source *src, int channel,
+						const u8 *data, size_t len)
+{
+	if (!src->ops.dsi || !src->ops.dsi->dcs_write)
+		return -EINVAL;
+
+	return src->ops.dsi->dcs_write(src, channel, data, len);
+}
+
+static inline int dsi_dcs_read(struct video_source *src, int channel,
+					u8 dcs_cmd, u8 *data, size_t len)
+{
+	if (!src->ops.dsi || !src->ops.dsi->dcs_read)
+		return -EINVAL;
+
+	return src->ops.dsi->dcs_read(src, channel, dcs_cmd, data, len);
+}
+
+
+#define video_source_register(video_source) \
+	__video_source_register(video_source, THIS_MODULE)
+
+int __must_check __video_source_register(struct video_source *entity,
+							struct module *owner);
+void video_source_unregister(struct video_source *entity);
+
+#endif /* __DISPLAY_H__ */
-- 
1.8.1

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [RFC PATCH 2/4] video: add makefile & kconfig
  2013-01-30 15:38 ` Tomasz Figa
@ 2013-01-30 15:39   ` Tomasz Figa
  -1 siblings, 0 replies; 36+ messages in thread
From: Tomasz Figa @ 2013-01-30 15:39 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	t.figa, tomasz.figa, Daniel Vetter, Marcus Lorentzon,
	Laurent Pinchart, rob, tomi.valkeinen, Vikas Sajjan, inki.dae,
	dh09.lee, ville.syrjala, s.nawrocki

Signed-off-by: Tomasz Figa <t.figa@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/video/Kconfig          |  1 +
 drivers/video/Makefile         |  1 +
 drivers/video/display/Kconfig  | 10 ++++++++++
 drivers/video/display/Makefile |  1 +
 4 files changed, 13 insertions(+)
 create mode 100644 drivers/video/display/Kconfig
 create mode 100644 drivers/video/display/Makefile

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 68f17ee..800e98c 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2444,6 +2444,7 @@ source "drivers/video/omap/Kconfig"
 source "drivers/video/omap2/Kconfig"
 source "drivers/video/exynos/Kconfig"
 source "drivers/video/backlight/Kconfig"
+source "drivers/video/display/Kconfig"
 
 if VT
 	source "drivers/video/console/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index f592f3b..4a091d4 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -14,6 +14,7 @@ fb-objs                           := $(fb-y)
 obj-$(CONFIG_VT)		  += console/
 obj-$(CONFIG_LOGO)		  += logo/
 obj-y				  += backlight/
+obj-y				  += display/
 
 obj-$(CONFIG_EXYNOS_VIDEO)     += exynos/
 
diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
new file mode 100644
index 0000000..477192d
--- /dev/null
+++ b/drivers/video/display/Kconfig
@@ -0,0 +1,10 @@
+menuconfig DISPLAY_CORE
+	tristate "Display Core"
+	---help---
+	  Support common display framework for graphics devices.
+
+if DISPLAY_CORE
+
+
+
+endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
new file mode 100644
index 0000000..bd93496
--- /dev/null
+++ b/drivers/video/display/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DISPLAY_CORE) += display-core.o
-- 
1.8.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [RFC PATCH 2/4] video: add makefile & kconfig
@ 2013-01-30 15:39   ` Tomasz Figa
  0 siblings, 0 replies; 36+ messages in thread
From: Tomasz Figa @ 2013-01-30 15:39 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	t.figa, tomasz.figa, Daniel Vetter, Marcus Lorentzon,
	Laurent Pinchart, rob, tomi.valkeinen, Vikas Sajjan, inki.dae,
	dh09.lee, ville.syrjala, s.nawrocki

Signed-off-by: Tomasz Figa <t.figa@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/video/Kconfig          |  1 +
 drivers/video/Makefile         |  1 +
 drivers/video/display/Kconfig  | 10 ++++++++++
 drivers/video/display/Makefile |  1 +
 4 files changed, 13 insertions(+)
 create mode 100644 drivers/video/display/Kconfig
 create mode 100644 drivers/video/display/Makefile

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 68f17ee..800e98c 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2444,6 +2444,7 @@ source "drivers/video/omap/Kconfig"
 source "drivers/video/omap2/Kconfig"
 source "drivers/video/exynos/Kconfig"
 source "drivers/video/backlight/Kconfig"
+source "drivers/video/display/Kconfig"
 
 if VT
 	source "drivers/video/console/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index f592f3b..4a091d4 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -14,6 +14,7 @@ fb-objs                           := $(fb-y)
 obj-$(CONFIG_VT)		  += console/
 obj-$(CONFIG_LOGO)		  += logo/
 obj-y				  += backlight/
+obj-y				  += display/
 
 obj-$(CONFIG_EXYNOS_VIDEO)     += exynos/
 
diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
new file mode 100644
index 0000000..477192d
--- /dev/null
+++ b/drivers/video/display/Kconfig
@@ -0,0 +1,10 @@
+menuconfig DISPLAY_CORE
+	tristate "Display Core"
+	---help---
+	  Support common display framework for graphics devices.
+
+if DISPLAY_CORE
+
+
+
+endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
new file mode 100644
index 0000000..bd93496
--- /dev/null
+++ b/drivers/video/display/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DISPLAY_CORE) += display-core.o
-- 
1.8.1

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [RFC PATCH 3/4] video: display: Add exynos-dsi video source driver
  2013-01-30 15:38 ` Tomasz Figa
@ 2013-01-30 15:39   ` Tomasz Figa
  -1 siblings, 0 replies; 36+ messages in thread
From: Tomasz Figa @ 2013-01-30 15:39 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	t.figa, tomasz.figa, Daniel Vetter, Marcus Lorentzon,
	Laurent Pinchart, rob, tomi.valkeinen, Vikas Sajjan, inki.dae,
	dh09.lee, ville.syrjala, s.nawrocki

This patch adds new driver for DSI master block available in Samsung
Exynos SoCs. The driver is designed and written specifically for Common
Display Framework.

Signed-off-by: Tomasz Figa <t.figa@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/video/display/Kconfig             |    3 +
 drivers/video/display/Makefile            |    1 +
 drivers/video/display/source-exynos_dsi.c | 1313 +++++++++++++++++++++++++++++
 include/video/exynos_dsi.h                |   41 +
 4 files changed, 1358 insertions(+)
 create mode 100644 drivers/video/display/source-exynos_dsi.c
 create mode 100644 include/video/exynos_dsi.h

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 477192d..b14527a 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -6,5 +6,8 @@ menuconfig DISPLAY_CORE
 if DISPLAY_CORE
 
 
+config DISPLAY_SOURCE_EXYNOS_DSI
+	tristate "Samsung SoC MIPI DSI Master"
+
 
 endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index bd93496..40a283a 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_DISPLAY_CORE) += display-core.o
+obj-$(CONFIG_DISPLAY_SOURCE_EXYNOS_DSI) += source-exynos_dsi.o
diff --git a/drivers/video/display/source-exynos_dsi.c b/drivers/video/display/source-exynos_dsi.c
new file mode 100644
index 0000000..30b15bf
--- /dev/null
+++ b/drivers/video/display/source-exynos_dsi.c
@@ -0,0 +1,1313 @@
+/*
+ * 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/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+#include <linux/videomode.h>
+#include <linux/wait.h>
+
+#include <video/display.h>
+#include <video/exynos_dsi.h>
+#include <video/mipi_display.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_LPDT_LP			(1 << 6)
+#define DSIM_CMD_LPDT_LP		(1 << 7)
+#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 SFR_HEADER_EMPTY		(1 << 22)
+
+/* 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_XFER_TIMEOUT_MS		100
+
+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 video_source out;
+	struct mipi_dsi_interface_params params;
+	bool streaming;
+
+	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;
+
+	spinlock_t transfer_lock;
+	struct list_head transfer_list;
+	bool hs_mode;
+};
+
+#define src_to_dsi(src)		container_of(src, struct exynos_dsi, out)
+
+/*
+ * H/W control
+ */
+
+static void exynos_dsi_reset(struct exynos_dsi *dsi)
+{
+	writel(DSIM_SWRST, dsi->reg_base + DSIM_SWRST_REG);
+
+	udelay(10);
+}
+
+#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, best_p;
+	u16 _m, best_m;
+	u8 _s, 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)
+{
+	unsigned long hs_clk, byte_clk, esc_clk;
+	unsigned long esc_div;
+	u32 reg;
+
+	hs_clk = exynos_dsi_set_pll(dsi, 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, dsi->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(dsi->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)
+{
+	u32 reg;
+	int timeout;
+
+	/* Initialize FIFO pointers */
+	reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG);
+	reg &= ~0x1f;
+	writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG);
+
+	msleep(10);
+
+	reg |= 0x1f;
+	writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG);
+
+	msleep(10);
+
+	/* DSI configuration */
+	reg = 0;
+
+	if (dsi->params.mode & DSI_MODE_VIDEO) {
+		reg |= DSIM_VIDEO_MODE;
+
+		if (!(dsi->params.mode & DSI_MODE_VSYNC_FLUSH))
+			reg |= DSIM_MFLUSH_VS;
+		if (!(dsi->params.mode & DSI_MODE_EOT_PACKET))
+			reg |= DSIM_EOT_DISABLE;
+		if (dsi->params.mode & DSI_MODE_VIDEO_SYNC_PULSE)
+			reg |= DSIM_SYNC_INFORM;
+		if (dsi->params.mode & DSI_MODE_VIDEO_BURST)
+			reg |= DSIM_BURST_MODE;
+		if (dsi->params.mode & DSI_MODE_VIDEO_AUTO_VERT)
+			reg |= DSIM_AUTO_MODE;
+		if (dsi->params.mode & DSI_MODE_VIDEO_HSE)
+			reg |= DSIM_HSE_MODE;
+		if (!(dsi->params.mode & DSI_MODE_VIDEO_HFP))
+			reg |= DSIM_HFP_MODE;
+		if (!(dsi->params.mode & DSI_MODE_VIDEO_HBP))
+			reg |= DSIM_HBP_MODE;
+		if (!(dsi->params.mode & DSI_MODE_VIDEO_HSA))
+			reg |= DSIM_HSA_MODE;
+	}
+
+	switch (dsi->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 (dsi->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(dsi->params.data_lanes);
+	writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
+
+	/* Clock configuration */
+	exynos_dsi_enable_clock(dsi);
+
+	/* 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(dsi->params.data_lanes))
+		    != DSIM_STOP_STATE_DAT(dsi->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;
+	u32 reg;
+	int ret;
+
+	ret = display_entity_get_modes(dsi->out.sink, &mode);
+	if (ret < 0) {
+		dev_err(dsi->dev, "failed to get display video mode\n");
+		return ret;
+	}
+
+	if (dsi->params.mode & DSI_MODE_VIDEO) {
+		reg = DSIM_CMD_ALLOW(dsi->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 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) {
+		if (xfer->rx_len) {
+			reg = (xfer->rx_len << 8)
+				| MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE;
+			writel(reg, dsi->reg_base + DSIM_PKTHDR_REG);
+		}
+		reg = (xfer->data[1] << 16) | (xfer->data[0] << 8) | xfer->type;
+		writel(reg, dsi->reg_base + DSIM_PKTHDR_REG);
+	}
+}
+
+static void exynos_readl_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);
+		length = (reg >> 8) & 0xffff;
+		if (length > xfer->rx_len)
+			dev_dbg(dsi->dev,
+				"response too long (expected %u, got %u bytes)\n",
+				xfer->rx_len, length);
+		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;
+		}
+	}
+
+	do {
+		reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+	} while (reg != 0x30800002);
+}
+
+/*
+ * Transfer
+ */
+
+static void exynos_dsi_transfer_cancel(struct exynos_dsi *dsi,
+					struct exynos_dsi_transfer *xfer)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	if (!list_empty(&xfer->list))
+		list_del_init(&xfer->list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+}
+
+static void exynos_dsi_transfer_start(struct exynos_dsi *dsi)
+{
+	unsigned long flags;
+	struct exynos_dsi_transfer *xfer;
+
+	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);
+}
+
+static void exynos_dsi_transfer_finish(struct exynos_dsi *dsi,
+					enum exynos_dsi_transfer_type type)
+{
+	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);
+		dev_warn(dsi->dev, "unexpected TX/RX interrupt\n");
+		return;
+	}
+
+	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 (type = EXYNOS_DSI_RX)
+		exynos_readl_from_fifo(dsi, xfer);
+
+	if (xfer->tx_done = xfer->tx_len && xfer->rx_done = xfer->rx_len) {
+		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);
+		xfer->result = 0;
+		complete(&xfer->completed);
+	}
+
+	if (type = EXYNOS_DSI_TX && xfer->tx_done != xfer->tx_len)
+		exynos_dsi_send_to_fifo(dsi, xfer);
+
+	if (start)
+		exynos_dsi_transfer_start(dsi);
+}
+
+static int exynos_dsi_transfer(struct exynos_dsi *dsi,
+		struct exynos_dsi_transfer *xfers, unsigned int num_xfers)
+{
+	unsigned long flags;
+	bool stopped;
+	LIST_HEAD(xfer_list);
+
+	for (; num_xfers; --num_xfers, ++xfers) {
+		xfers->tx_done = 0;
+		xfers->rx_done = 0;
+		xfers->result = -ETIMEDOUT;
+		init_completion(&xfers->completed);
+
+		list_add_tail(&xfers->list, &xfer_list);
+	}
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	stopped = list_empty(&dsi->transfer_list);
+	list_splice_tail(&xfer_list, &dsi->transfer_list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+	if (stopped)
+		exynos_dsi_transfer_start(dsi);
+
+	return 0;
+}
+
+/*
+ * 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);
+	writel(status, dsi->reg_base + DSIM_INTSRC_REG);
+
+	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);
+		return IRQ_HANDLED;
+	}
+
+	if (status & DSIM_INT_RX_DONE) {
+		exynos_dsi_transfer_finish(dsi, EXYNOS_DSI_RX);
+		return IRQ_HANDLED;
+	}
+
+	if (status & DSIM_INT_SFR_FIFO_EMPTY) {
+		exynos_dsi_transfer_finish(dsi, EXYNOS_DSI_TX);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+/*
+ * Display source
+ */
+
+static int exynos_dsi_set_stream(struct video_source *src,
+					enum video_source_stream_state state)
+{
+	struct exynos_dsi *dsi = src_to_dsi(src);
+
+	if (pm_runtime_suspended(dsi->dev))
+		return -EINVAL;
+
+	switch (state) {
+	case DISPLAY_ENTITY_STREAM_STOPPED:
+		exynos_dsi_set_display_enable(dsi, false);
+		dsi->streaming = false;
+		break;
+
+	case DISPLAY_ENTITY_STREAM_CONTINUOUS:
+		exynos_dsi_set_display_mode(dsi);
+		exynos_dsi_set_display_enable(dsi, true);
+		dsi->streaming = true;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* enable/disable dsi bus */
+static int exynos_dsi_enable(struct video_source *src)
+{
+	struct display_entity_interface_params params;
+	struct exynos_dsi *dsi = src_to_dsi(src);
+	int ret;
+
+	ret = display_entity_get_params(src->sink, &params);
+	if (ret < 0)
+		return ret;
+	dsi->params = params.p.dsi;
+
+	ret = pm_runtime_get_sync(dsi->dev);
+	if (ret < 0)
+		return ret;
+
+	exynos_dsi_reset(dsi);
+	exynos_dsi_init_link(dsi);
+
+	return 0;
+}
+
+static int exynos_dsi_disable(struct video_source *src)
+{
+	struct exynos_dsi *dsi = src_to_dsi(src);
+
+	if (dsi->streaming)
+		return -EBUSY;
+
+	exynos_dsi_disable_clock(dsi);
+
+	return pm_runtime_put_sync(dsi->dev);
+}
+
+static void exynos_dsi_enable_hs(struct video_source *src, bool enable)
+{
+	struct exynos_dsi *dsi = src_to_dsi(src);
+	unsigned long flags;
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	dsi->hs_mode = enable;
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+}
+
+/* data transfer */
+static int exynos_dsi_dcs_write(struct video_source *src, int channel,
+						const u8 *data, size_t len)
+{
+	struct exynos_dsi *dsi = src_to_dsi(src);
+	struct exynos_dsi_transfer xfer;
+	long ret;
+
+	if (!len)
+		return -EINVAL;
+
+	switch (len) {
+#if 0
+	/* FIXME: Fix handling of short packets */
+	case 1:
+		len = 0;
+		xfer.type = MIPI_DSI_DCS_SHORT_WRITE;
+		xfer.data[0] = data[0];
+		xfer.data[1] = 0;
+		break;
+	case 2:
+		len = 0;
+		xfer.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
+		xfer.data[0] = data[0];
+		xfer.data[1] = data[1];
+		break;
+#endif
+	default:
+		xfer.type = MIPI_DSI_DCS_LONG_WRITE;
+		xfer.data[0] = len & 0xff;
+		xfer.data[1] = len >> 8;
+	}
+	
+	xfer.tx_payload = data;
+	xfer.tx_len = len;
+	xfer.rx_len = 0;
+
+	ret = exynos_dsi_transfer(dsi, &xfer, 1);
+	if (ret < 0)
+		return ret;
+
+	wait_for_completion_timeout(&xfer.completed,
+					msecs_to_jiffies(DSI_XFER_TIMEOUT_MS));
+	if (xfer.result = -ETIMEDOUT) {
+		exynos_dsi_transfer_cancel(dsi, &xfer);
+		dev_err(dsi->dev, "xfer timed out\n");
+		return -ETIMEDOUT;
+	}
+
+	/* Also covers timeout condition */
+	return xfer.result;
+}
+
+static int exynos_dsi_dcs_read(struct video_source *src,
+				int channel, u8 dcs_cmd, u8 *data, size_t len)
+{
+	struct exynos_dsi *dsi = src_to_dsi(src);
+	struct exynos_dsi_transfer xfer;
+	long ret;
+
+	xfer.type = MIPI_DSI_DCS_READ;
+	xfer.tx_len = 0;
+	xfer.rx_payload = data;
+	xfer.rx_len = len;
+	xfer.data[0] = dcs_cmd;
+	xfer.data[1] = 0x00;
+
+	ret = exynos_dsi_transfer(dsi, &xfer, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = wait_for_completion_timeout(&xfer.completed,
+					msecs_to_jiffies(DSI_XFER_TIMEOUT_MS));
+	if (xfer.result = -ETIMEDOUT) {
+		exynos_dsi_transfer_cancel(dsi, &xfer);
+		dev_err(dsi->dev, "xfer timed out\n");
+		return -ETIMEDOUT;
+	}
+
+	/* Also covers timeout condition */
+	return xfer.result;
+}
+
+static const struct common_video_source_ops exynos_dsi_common_ops = {
+	.set_stream = exynos_dsi_set_stream,
+};
+
+static const struct dsi_video_source_ops exynos_dsi_ops = {
+	.enable = exynos_dsi_enable,
+	.disable = exynos_dsi_disable,
+
+	.enable_hs = exynos_dsi_enable_hs,
+
+	.dcs_write = exynos_dsi_dcs_write,
+	.dcs_read = exynos_dsi_dcs_read,
+};
+
+#ifdef CONFIG_OF
+/*
+ * Device Tree
+ */
+
+static int (* const of_phy_enables[])(struct platform_device *, bool) = {
+#ifdef CONFIG_S5P_SETUP_MIPIPHY
+	[0] = s5p_dsim_phy_enable,
+#endif
+};
+
+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;
+	u32 val;
+
+	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,phy-type", NULL);
+	if (!prop_data) {
+		dev_err(dev, "failed to get phy-type property\n");
+		goto err_free_pd;
+	}
+
+	val = be32_to_cpu(*prop_data);
+	if (val >= ARRAY_SIZE(of_phy_enables) || !of_phy_enables[val]) {
+		dev_err(dev, "Invalid phy-type %u\n", val);
+		goto err_free_pd;
+	}
+	dsi_pd->phy_enable = of_phy_enables[val];
+
+	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;
+	}
+
+	spin_lock_init(&dsi->transfer_lock);
+	INIT_LIST_HEAD(&dsi->transfer_list);
+
+	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;
+	}
+
+	platform_set_drvdata(pdev, dsi);
+
+	pm_runtime_set_suspended(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+	pm_runtime_get_sync(&pdev->dev);
+	writel(~0, dsi->reg_base + DSIM_INTMSK_REG);
+	pm_runtime_put_sync(&pdev->dev);
+
+	dsi->irq = platform_get_irq(pdev, 0);
+	if (dsi->irq < 0) {
+		dev_err(&pdev->dev, "failed to request dsi irq resource\n");
+		return -EINVAL;
+	}
+
+	ret = devm_request_irq(&pdev->dev, dsi->irq, exynos_dsi_irq,
+						0, dev_name(&pdev->dev), dsi);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to request dsi irq\n");
+		return ret;
+	}
+
+	dsi->out.name = dev_name(&pdev->dev);
+	dsi->out.id = -1;
+	dsi->out.of_node = pdev->dev.of_node;
+	dsi->out.common_ops = &exynos_dsi_common_ops;
+	dsi->out.ops.dsi = &exynos_dsi_ops;
+
+	ret = video_source_register(&dsi->out);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register video source\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __devexit exynos_dsi_remove(struct platform_device *pdev)
+{
+	struct exynos_dsi *dsi = platform_get_drvdata(pdev);
+
+	video_source_unregister(&dsi->out);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+/*
+ * Power management
+ */
+
+static int exynos_dsi_runtime_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct exynos_dsi *dsi = platform_get_drvdata(pdev);
+
+	dev_info(dev, "%s\n", __func__);
+
+	if (dsi->pd->phy_enable)
+		dsi->pd->phy_enable(pdev, false);
+
+	clk_disable_unprepare(dsi->pll_clk);
+	clk_disable_unprepare(dsi->bus_clk);
+
+	regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+
+	return 0;
+}
+
+static int exynos_dsi_runtime_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct exynos_dsi *dsi = platform_get_drvdata(pdev);
+
+	dev_info(dev, "%s\n", __func__);
+
+	regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+
+	clk_prepare_enable(dsi->bus_clk);
+	clk_prepare_enable(dsi->pll_clk);
+
+	if (dsi->pd->phy_enable)
+		dsi->pd->phy_enable(pdev, true);
+
+	return 0;
+}
+
+static int exynos_dsi_suspend(struct device *dev)
+{
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	return exynos_dsi_runtime_suspend(dev);
+}
+
+static int exynos_dsi_resume(struct device *dev)
+{
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	return exynos_dsi_runtime_resume(dev);
+}
+
+static const struct dev_pm_ops exynos_dsi_pm_ops = {
+	SET_RUNTIME_PM_OPS(exynos_dsi_runtime_suspend,
+					exynos_dsi_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume)
+};
+
+/*
+ * Module
+ */
+
+static struct platform_driver exynos_dsi_driver = {
+	.probe = exynos_dsi_probe,
+	.remove = __devexit_p(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/exynos_dsi.h b/include/video/exynos_dsi.h
new file mode 100644
index 0000000..95e1568
--- /dev/null
+++ b/include/video/exynos_dsi.h
@@ -0,0 +1,41 @@
+/* include/video/exynos_mipi_dsim.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 _EXYNOS_MIPI_DSIM_H
+#define _EXYNOS_MIPI_DSIM_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 /* _EXYNOS_MIPI_DSIM_H */
-- 
1.8.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [RFC PATCH 3/4] video: display: Add exynos-dsi video source driver
@ 2013-01-30 15:39   ` Tomasz Figa
  0 siblings, 0 replies; 36+ messages in thread
From: Tomasz Figa @ 2013-01-30 15:39 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	t.figa, tomasz.figa, Daniel Vetter, Marcus Lorentzon,
	Laurent Pinchart, rob, tomi.valkeinen, Vikas Sajjan, inki.dae,
	dh09.lee, ville.syrjala, s.nawrocki

This patch adds new driver for DSI master block available in Samsung
Exynos SoCs. The driver is designed and written specifically for Common
Display Framework.

Signed-off-by: Tomasz Figa <t.figa@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/video/display/Kconfig             |    3 +
 drivers/video/display/Makefile            |    1 +
 drivers/video/display/source-exynos_dsi.c | 1313 +++++++++++++++++++++++++++++
 include/video/exynos_dsi.h                |   41 +
 4 files changed, 1358 insertions(+)
 create mode 100644 drivers/video/display/source-exynos_dsi.c
 create mode 100644 include/video/exynos_dsi.h

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 477192d..b14527a 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -6,5 +6,8 @@ menuconfig DISPLAY_CORE
 if DISPLAY_CORE
 
 
+config DISPLAY_SOURCE_EXYNOS_DSI
+	tristate "Samsung SoC MIPI DSI Master"
+
 
 endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index bd93496..40a283a 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_DISPLAY_CORE) += display-core.o
+obj-$(CONFIG_DISPLAY_SOURCE_EXYNOS_DSI) += source-exynos_dsi.o
diff --git a/drivers/video/display/source-exynos_dsi.c b/drivers/video/display/source-exynos_dsi.c
new file mode 100644
index 0000000..30b15bf
--- /dev/null
+++ b/drivers/video/display/source-exynos_dsi.c
@@ -0,0 +1,1313 @@
+/*
+ * 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/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+#include <linux/videomode.h>
+#include <linux/wait.h>
+
+#include <video/display.h>
+#include <video/exynos_dsi.h>
+#include <video/mipi_display.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_LPDT_LP			(1 << 6)
+#define DSIM_CMD_LPDT_LP		(1 << 7)
+#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 SFR_HEADER_EMPTY		(1 << 22)
+
+/* 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_XFER_TIMEOUT_MS		100
+
+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 video_source out;
+	struct mipi_dsi_interface_params params;
+	bool streaming;
+
+	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;
+
+	spinlock_t transfer_lock;
+	struct list_head transfer_list;
+	bool hs_mode;
+};
+
+#define src_to_dsi(src)		container_of(src, struct exynos_dsi, out)
+
+/*
+ * H/W control
+ */
+
+static void exynos_dsi_reset(struct exynos_dsi *dsi)
+{
+	writel(DSIM_SWRST, dsi->reg_base + DSIM_SWRST_REG);
+
+	udelay(10);
+}
+
+#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, best_p;
+	u16 _m, best_m;
+	u8 _s, 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)
+{
+	unsigned long hs_clk, byte_clk, esc_clk;
+	unsigned long esc_div;
+	u32 reg;
+
+	hs_clk = exynos_dsi_set_pll(dsi, 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, dsi->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(dsi->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)
+{
+	u32 reg;
+	int timeout;
+
+	/* Initialize FIFO pointers */
+	reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG);
+	reg &= ~0x1f;
+	writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG);
+
+	msleep(10);
+
+	reg |= 0x1f;
+	writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG);
+
+	msleep(10);
+
+	/* DSI configuration */
+	reg = 0;
+
+	if (dsi->params.mode & DSI_MODE_VIDEO) {
+		reg |= DSIM_VIDEO_MODE;
+
+		if (!(dsi->params.mode & DSI_MODE_VSYNC_FLUSH))
+			reg |= DSIM_MFLUSH_VS;
+		if (!(dsi->params.mode & DSI_MODE_EOT_PACKET))
+			reg |= DSIM_EOT_DISABLE;
+		if (dsi->params.mode & DSI_MODE_VIDEO_SYNC_PULSE)
+			reg |= DSIM_SYNC_INFORM;
+		if (dsi->params.mode & DSI_MODE_VIDEO_BURST)
+			reg |= DSIM_BURST_MODE;
+		if (dsi->params.mode & DSI_MODE_VIDEO_AUTO_VERT)
+			reg |= DSIM_AUTO_MODE;
+		if (dsi->params.mode & DSI_MODE_VIDEO_HSE)
+			reg |= DSIM_HSE_MODE;
+		if (!(dsi->params.mode & DSI_MODE_VIDEO_HFP))
+			reg |= DSIM_HFP_MODE;
+		if (!(dsi->params.mode & DSI_MODE_VIDEO_HBP))
+			reg |= DSIM_HBP_MODE;
+		if (!(dsi->params.mode & DSI_MODE_VIDEO_HSA))
+			reg |= DSIM_HSA_MODE;
+	}
+
+	switch (dsi->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 (dsi->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(dsi->params.data_lanes);
+	writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
+
+	/* Clock configuration */
+	exynos_dsi_enable_clock(dsi);
+
+	/* 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(dsi->params.data_lanes))
+		    != DSIM_STOP_STATE_DAT(dsi->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;
+	u32 reg;
+	int ret;
+
+	ret = display_entity_get_modes(dsi->out.sink, &mode);
+	if (ret < 0) {
+		dev_err(dsi->dev, "failed to get display video mode\n");
+		return ret;
+	}
+
+	if (dsi->params.mode & DSI_MODE_VIDEO) {
+		reg = DSIM_CMD_ALLOW(dsi->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 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) {
+		if (xfer->rx_len) {
+			reg = (xfer->rx_len << 8)
+				| MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE;
+			writel(reg, dsi->reg_base + DSIM_PKTHDR_REG);
+		}
+		reg = (xfer->data[1] << 16) | (xfer->data[0] << 8) | xfer->type;
+		writel(reg, dsi->reg_base + DSIM_PKTHDR_REG);
+	}
+}
+
+static void exynos_readl_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);
+		length = (reg >> 8) & 0xffff;
+		if (length > xfer->rx_len)
+			dev_dbg(dsi->dev,
+				"response too long (expected %u, got %u bytes)\n",
+				xfer->rx_len, length);
+		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;
+		}
+	}
+
+	do {
+		reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+	} while (reg != 0x30800002);
+}
+
+/*
+ * Transfer
+ */
+
+static void exynos_dsi_transfer_cancel(struct exynos_dsi *dsi,
+					struct exynos_dsi_transfer *xfer)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	if (!list_empty(&xfer->list))
+		list_del_init(&xfer->list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+}
+
+static void exynos_dsi_transfer_start(struct exynos_dsi *dsi)
+{
+	unsigned long flags;
+	struct exynos_dsi_transfer *xfer;
+
+	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);
+}
+
+static void exynos_dsi_transfer_finish(struct exynos_dsi *dsi,
+					enum exynos_dsi_transfer_type type)
+{
+	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);
+		dev_warn(dsi->dev, "unexpected TX/RX interrupt\n");
+		return;
+	}
+
+	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 (type == EXYNOS_DSI_RX)
+		exynos_readl_from_fifo(dsi, xfer);
+
+	if (xfer->tx_done == xfer->tx_len && xfer->rx_done == xfer->rx_len) {
+		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);
+		xfer->result = 0;
+		complete(&xfer->completed);
+	}
+
+	if (type == EXYNOS_DSI_TX && xfer->tx_done != xfer->tx_len)
+		exynos_dsi_send_to_fifo(dsi, xfer);
+
+	if (start)
+		exynos_dsi_transfer_start(dsi);
+}
+
+static int exynos_dsi_transfer(struct exynos_dsi *dsi,
+		struct exynos_dsi_transfer *xfers, unsigned int num_xfers)
+{
+	unsigned long flags;
+	bool stopped;
+	LIST_HEAD(xfer_list);
+
+	for (; num_xfers; --num_xfers, ++xfers) {
+		xfers->tx_done = 0;
+		xfers->rx_done = 0;
+		xfers->result = -ETIMEDOUT;
+		init_completion(&xfers->completed);
+
+		list_add_tail(&xfers->list, &xfer_list);
+	}
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	stopped = list_empty(&dsi->transfer_list);
+	list_splice_tail(&xfer_list, &dsi->transfer_list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+	if (stopped)
+		exynos_dsi_transfer_start(dsi);
+
+	return 0;
+}
+
+/*
+ * 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);
+	writel(status, dsi->reg_base + DSIM_INTSRC_REG);
+
+	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);
+		return IRQ_HANDLED;
+	}
+
+	if (status & DSIM_INT_RX_DONE) {
+		exynos_dsi_transfer_finish(dsi, EXYNOS_DSI_RX);
+		return IRQ_HANDLED;
+	}
+
+	if (status & DSIM_INT_SFR_FIFO_EMPTY) {
+		exynos_dsi_transfer_finish(dsi, EXYNOS_DSI_TX);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+/*
+ * Display source
+ */
+
+static int exynos_dsi_set_stream(struct video_source *src,
+					enum video_source_stream_state state)
+{
+	struct exynos_dsi *dsi = src_to_dsi(src);
+
+	if (pm_runtime_suspended(dsi->dev))
+		return -EINVAL;
+
+	switch (state) {
+	case DISPLAY_ENTITY_STREAM_STOPPED:
+		exynos_dsi_set_display_enable(dsi, false);
+		dsi->streaming = false;
+		break;
+
+	case DISPLAY_ENTITY_STREAM_CONTINUOUS:
+		exynos_dsi_set_display_mode(dsi);
+		exynos_dsi_set_display_enable(dsi, true);
+		dsi->streaming = true;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* enable/disable dsi bus */
+static int exynos_dsi_enable(struct video_source *src)
+{
+	struct display_entity_interface_params params;
+	struct exynos_dsi *dsi = src_to_dsi(src);
+	int ret;
+
+	ret = display_entity_get_params(src->sink, &params);
+	if (ret < 0)
+		return ret;
+	dsi->params = params.p.dsi;
+
+	ret = pm_runtime_get_sync(dsi->dev);
+	if (ret < 0)
+		return ret;
+
+	exynos_dsi_reset(dsi);
+	exynos_dsi_init_link(dsi);
+
+	return 0;
+}
+
+static int exynos_dsi_disable(struct video_source *src)
+{
+	struct exynos_dsi *dsi = src_to_dsi(src);
+
+	if (dsi->streaming)
+		return -EBUSY;
+
+	exynos_dsi_disable_clock(dsi);
+
+	return pm_runtime_put_sync(dsi->dev);
+}
+
+static void exynos_dsi_enable_hs(struct video_source *src, bool enable)
+{
+	struct exynos_dsi *dsi = src_to_dsi(src);
+	unsigned long flags;
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	dsi->hs_mode = enable;
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+}
+
+/* data transfer */
+static int exynos_dsi_dcs_write(struct video_source *src, int channel,
+						const u8 *data, size_t len)
+{
+	struct exynos_dsi *dsi = src_to_dsi(src);
+	struct exynos_dsi_transfer xfer;
+	long ret;
+
+	if (!len)
+		return -EINVAL;
+
+	switch (len) {
+#if 0
+	/* FIXME: Fix handling of short packets */
+	case 1:
+		len = 0;
+		xfer.type = MIPI_DSI_DCS_SHORT_WRITE;
+		xfer.data[0] = data[0];
+		xfer.data[1] = 0;
+		break;
+	case 2:
+		len = 0;
+		xfer.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
+		xfer.data[0] = data[0];
+		xfer.data[1] = data[1];
+		break;
+#endif
+	default:
+		xfer.type = MIPI_DSI_DCS_LONG_WRITE;
+		xfer.data[0] = len & 0xff;
+		xfer.data[1] = len >> 8;
+	}
+	
+	xfer.tx_payload = data;
+	xfer.tx_len = len;
+	xfer.rx_len = 0;
+
+	ret = exynos_dsi_transfer(dsi, &xfer, 1);
+	if (ret < 0)
+		return ret;
+
+	wait_for_completion_timeout(&xfer.completed,
+					msecs_to_jiffies(DSI_XFER_TIMEOUT_MS));
+	if (xfer.result == -ETIMEDOUT) {
+		exynos_dsi_transfer_cancel(dsi, &xfer);
+		dev_err(dsi->dev, "xfer timed out\n");
+		return -ETIMEDOUT;
+	}
+
+	/* Also covers timeout condition */
+	return xfer.result;
+}
+
+static int exynos_dsi_dcs_read(struct video_source *src,
+				int channel, u8 dcs_cmd, u8 *data, size_t len)
+{
+	struct exynos_dsi *dsi = src_to_dsi(src);
+	struct exynos_dsi_transfer xfer;
+	long ret;
+
+	xfer.type = MIPI_DSI_DCS_READ;
+	xfer.tx_len = 0;
+	xfer.rx_payload = data;
+	xfer.rx_len = len;
+	xfer.data[0] = dcs_cmd;
+	xfer.data[1] = 0x00;
+
+	ret = exynos_dsi_transfer(dsi, &xfer, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = wait_for_completion_timeout(&xfer.completed,
+					msecs_to_jiffies(DSI_XFER_TIMEOUT_MS));
+	if (xfer.result == -ETIMEDOUT) {
+		exynos_dsi_transfer_cancel(dsi, &xfer);
+		dev_err(dsi->dev, "xfer timed out\n");
+		return -ETIMEDOUT;
+	}
+
+	/* Also covers timeout condition */
+	return xfer.result;
+}
+
+static const struct common_video_source_ops exynos_dsi_common_ops = {
+	.set_stream = exynos_dsi_set_stream,
+};
+
+static const struct dsi_video_source_ops exynos_dsi_ops = {
+	.enable = exynos_dsi_enable,
+	.disable = exynos_dsi_disable,
+
+	.enable_hs = exynos_dsi_enable_hs,
+
+	.dcs_write = exynos_dsi_dcs_write,
+	.dcs_read = exynos_dsi_dcs_read,
+};
+
+#ifdef CONFIG_OF
+/*
+ * Device Tree
+ */
+
+static int (* const of_phy_enables[])(struct platform_device *, bool) = {
+#ifdef CONFIG_S5P_SETUP_MIPIPHY
+	[0] = s5p_dsim_phy_enable,
+#endif
+};
+
+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;
+	u32 val;
+
+	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,phy-type", NULL);
+	if (!prop_data) {
+		dev_err(dev, "failed to get phy-type property\n");
+		goto err_free_pd;
+	}
+
+	val = be32_to_cpu(*prop_data);
+	if (val >= ARRAY_SIZE(of_phy_enables) || !of_phy_enables[val]) {
+		dev_err(dev, "Invalid phy-type %u\n", val);
+		goto err_free_pd;
+	}
+	dsi_pd->phy_enable = of_phy_enables[val];
+
+	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;
+	}
+
+	spin_lock_init(&dsi->transfer_lock);
+	INIT_LIST_HEAD(&dsi->transfer_list);
+
+	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;
+	}
+
+	platform_set_drvdata(pdev, dsi);
+
+	pm_runtime_set_suspended(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+	pm_runtime_get_sync(&pdev->dev);
+	writel(~0, dsi->reg_base + DSIM_INTMSK_REG);
+	pm_runtime_put_sync(&pdev->dev);
+
+	dsi->irq = platform_get_irq(pdev, 0);
+	if (dsi->irq < 0) {
+		dev_err(&pdev->dev, "failed to request dsi irq resource\n");
+		return -EINVAL;
+	}
+
+	ret = devm_request_irq(&pdev->dev, dsi->irq, exynos_dsi_irq,
+						0, dev_name(&pdev->dev), dsi);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to request dsi irq\n");
+		return ret;
+	}
+
+	dsi->out.name = dev_name(&pdev->dev);
+	dsi->out.id = -1;
+	dsi->out.of_node = pdev->dev.of_node;
+	dsi->out.common_ops = &exynos_dsi_common_ops;
+	dsi->out.ops.dsi = &exynos_dsi_ops;
+
+	ret = video_source_register(&dsi->out);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register video source\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __devexit exynos_dsi_remove(struct platform_device *pdev)
+{
+	struct exynos_dsi *dsi = platform_get_drvdata(pdev);
+
+	video_source_unregister(&dsi->out);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+/*
+ * Power management
+ */
+
+static int exynos_dsi_runtime_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct exynos_dsi *dsi = platform_get_drvdata(pdev);
+
+	dev_info(dev, "%s\n", __func__);
+
+	if (dsi->pd->phy_enable)
+		dsi->pd->phy_enable(pdev, false);
+
+	clk_disable_unprepare(dsi->pll_clk);
+	clk_disable_unprepare(dsi->bus_clk);
+
+	regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+
+	return 0;
+}
+
+static int exynos_dsi_runtime_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct exynos_dsi *dsi = platform_get_drvdata(pdev);
+
+	dev_info(dev, "%s\n", __func__);
+
+	regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+
+	clk_prepare_enable(dsi->bus_clk);
+	clk_prepare_enable(dsi->pll_clk);
+
+	if (dsi->pd->phy_enable)
+		dsi->pd->phy_enable(pdev, true);
+
+	return 0;
+}
+
+static int exynos_dsi_suspend(struct device *dev)
+{
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	return exynos_dsi_runtime_suspend(dev);
+}
+
+static int exynos_dsi_resume(struct device *dev)
+{
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	return exynos_dsi_runtime_resume(dev);
+}
+
+static const struct dev_pm_ops exynos_dsi_pm_ops = {
+	SET_RUNTIME_PM_OPS(exynos_dsi_runtime_suspend,
+					exynos_dsi_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume)
+};
+
+/*
+ * Module
+ */
+
+static struct platform_driver exynos_dsi_driver = {
+	.probe = exynos_dsi_probe,
+	.remove = __devexit_p(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/exynos_dsi.h b/include/video/exynos_dsi.h
new file mode 100644
index 0000000..95e1568
--- /dev/null
+++ b/include/video/exynos_dsi.h
@@ -0,0 +1,41 @@
+/* include/video/exynos_mipi_dsim.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 _EXYNOS_MIPI_DSIM_H
+#define _EXYNOS_MIPI_DSIM_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 /* _EXYNOS_MIPI_DSIM_H */
-- 
1.8.1

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [RFC PATCH 4/4] video: display: Add Samsung s6e8ax0 display panel driver
  2013-01-30 15:38 ` Tomasz Figa
@ 2013-01-30 15:39   ` Tomasz Figa
  -1 siblings, 0 replies; 36+ messages in thread
From: Tomasz Figa @ 2013-01-30 15:39 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	t.figa, tomasz.figa, Daniel Vetter, Marcus Lorentzon,
	Laurent Pinchart, rob, tomi.valkeinen, Vikas Sajjan, inki.dae,
	dh09.lee, ville.syrjala, s.nawrocki

This patch adds Common Display Framework driver for Samsung s6e8ax0
MIPI DSI display panel.

Signed-off-by: Tomasz Figa <t.figa@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/video/display/Kconfig         |    3 +
 drivers/video/display/Makefile        |    1 +
 drivers/video/display/panel-s6e8ax0.c | 1027 +++++++++++++++++++++++++++++++++
 include/video/panel-s6e8ax0.h         |   41 ++
 4 files changed, 1072 insertions(+)
 create mode 100644 drivers/video/display/panel-s6e8ax0.c
 create mode 100644 include/video/panel-s6e8ax0.h

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index b14527a..f19ec04 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -5,6 +5,9 @@ menuconfig DISPLAY_CORE
 
 if DISPLAY_CORE
 
+config DISPLAY_PANEL_S6E8AX0
+	tristate "S6E8AX0 DSI video mode panel"
+	select OF_VIDEOMODE
 
 config DISPLAY_SOURCE_EXYNOS_DSI
 	tristate "Samsung SoC MIPI DSI Master"
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index 40a283a..0f7fdc2 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_DISPLAY_CORE) += display-core.o
+obj-$(CONFIG_DISPLAY_PANEL_S6E8AX0) += panel-s6e8ax0.o
 obj-$(CONFIG_DISPLAY_SOURCE_EXYNOS_DSI) += source-exynos_dsi.o
diff --git a/drivers/video/display/panel-s6e8ax0.c b/drivers/video/display/panel-s6e8ax0.c
new file mode 100644
index 0000000..4c09fe2
--- /dev/null
+++ b/drivers/video/display/panel-s6e8ax0.c
@@ -0,0 +1,1027 @@
+/* linux/drivers/video/exynos/s6e8ax0.c
+ *
+ * MIPI-DSI based s6e8ax0 AMOLED lcd 4.65 inch panel driver.
+ *
+ * Inki Dae, <inki.dae@samsung.com>
+ * Donghwa Lee, <dh09.lee@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/errno.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/lcd.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_videomode.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/backlight.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/display.h>
+#include <video/mipi_display.h>
+#include <video/panel-s6e8ax0.h>
+
+#define LDI_MTP_LENGTH		24
+#define DSIM_PM_STABLE_TIME	10
+#define MIN_BRIGHTNESS		0
+#define MAX_BRIGHTNESS		24
+#define GAMMA_TABLE_COUNT	26
+
+struct s6e8ax0 {
+	struct display_entity entity;
+	struct device *dev;
+
+	struct s6e8ax0_platform_data *pdata;
+	struct backlight_device	*bd;
+	struct lcd_device *ld;
+	struct regulator_bulk_data supplies[2];
+
+	bool enabled;
+	unsigned int id;
+	unsigned int gamma;
+	unsigned int acl_enable;
+	unsigned int cur_acl;
+	int power;
+
+	unsigned int reset_gpio;
+};
+
+#define to_panel(p)	container_of(p, struct s6e8ax0, entity)
+
+static const unsigned char s6e8ax0_22_gamma_30[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xf5, 0x00, 0xff, 0xad, 0xaf,
+	0xbA, 0xc3, 0xd8, 0xc5, 0x9f, 0xc6, 0x9e, 0xc1, 0xdc, 0xc0,
+	0x00, 0x61, 0x00, 0x5a, 0x00, 0x74,
+};
+
+static const unsigned char s6e8ax0_22_gamma_50[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xe8, 0x1f, 0xf7, 0xad, 0xc0,
+	0xb5, 0xc4, 0xdc, 0xc4, 0x9e, 0xc6, 0x9c, 0xbb, 0xd8, 0xbb,
+	0x00, 0x70, 0x00, 0x68, 0x00, 0x86,
+};
+
+static const unsigned char s6e8ax0_22_gamma_60[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xde, 0x1f, 0xef, 0xad, 0xc4,
+	0xb3, 0xc3, 0xdd, 0xc4, 0x9e, 0xc6, 0x9c, 0xbc, 0xd6, 0xba,
+	0x00, 0x75, 0x00, 0x6e, 0x00, 0x8d,
+};
+
+static const unsigned char s6e8ax0_22_gamma_70[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xd8, 0x1f, 0xe7, 0xaf, 0xc8,
+	0xb4, 0xc4, 0xdd, 0xc3, 0x9d, 0xc6, 0x9c, 0xbb, 0xd6, 0xb9,
+	0x00, 0x7a, 0x00, 0x72, 0x00, 0x93,
+};
+
+static const unsigned char s6e8ax0_22_gamma_80[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xc9, 0x1f, 0xde, 0xae, 0xc9,
+	0xb1, 0xc3, 0xdd, 0xc2, 0x9d, 0xc5, 0x9b, 0xbc, 0xd6, 0xbb,
+	0x00, 0x7f, 0x00, 0x77, 0x00, 0x99,
+};
+
+static const unsigned char s6e8ax0_22_gamma_90[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xc7, 0x1f, 0xd9, 0xb0, 0xcc,
+	0xb2, 0xc3, 0xdc, 0xc1, 0x9c, 0xc6, 0x9c, 0xbc, 0xd4, 0xb9,
+	0x00, 0x83, 0x00, 0x7b, 0x00, 0x9e,
+};
+
+static const unsigned char s6e8ax0_22_gamma_100[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xbd, 0x80, 0xcd, 0xba, 0xce,
+	0xb3, 0xc4, 0xde, 0xc3, 0x9c, 0xc4, 0x9, 0xb8, 0xd3, 0xb6,
+	0x00, 0x88, 0x00, 0x80, 0x00, 0xa5,
+};
+
+static const unsigned char s6e8ax0_22_gamma_120[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb9, 0x95, 0xc8, 0xb1, 0xcf,
+	0xb2, 0xc6, 0xdf, 0xc5, 0x9b, 0xc3, 0x99, 0xb6, 0xd2, 0xb6,
+	0x00, 0x8f, 0x00, 0x86, 0x00, 0xac,
+};
+
+static const unsigned char s6e8ax0_22_gamma_130[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc7, 0xb1, 0xd0,
+	0xb2, 0xc4, 0xdd, 0xc3, 0x9a, 0xc3, 0x98, 0xb6, 0xd0, 0xb4,
+	0x00, 0x92, 0x00, 0x8a, 0x00, 0xb1,
+};
+
+static const unsigned char s6e8ax0_22_gamma_140[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc5, 0xb2, 0xd0,
+	0xb3, 0xc3, 0xde, 0xc3, 0x9b, 0xc2, 0x98, 0xb6, 0xd0, 0xb4,
+	0x00, 0x95, 0x00, 0x8d, 0x00, 0xb5,
+};
+
+static const unsigned char s6e8ax0_22_gamma_150[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xa0, 0xc2, 0xb2, 0xd0,
+	0xb2, 0xc1, 0xdd, 0xc2, 0x9b, 0xc2, 0x98, 0xb4, 0xcf, 0xb1,
+	0x00, 0x99, 0x00, 0x90, 0x00, 0xba,
+};
+
+static const unsigned char s6e8ax0_22_gamma_160[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xa5, 0xbf, 0xb0, 0xd0,
+	0xb1, 0xc3, 0xde, 0xc2, 0x99, 0xc1, 0x97, 0xb4, 0xce, 0xb1,
+	0x00, 0x9c, 0x00, 0x93, 0x00, 0xbe,
+};
+
+static const unsigned char s6e8ax0_22_gamma_170[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb5, 0xbf, 0xb1, 0xd1,
+	0xb1, 0xc3, 0xde, 0xc3, 0x99, 0xc0, 0x96, 0xb4, 0xce, 0xb1,
+	0x00, 0x9f, 0x00, 0x96, 0x00, 0xc2,
+};
+
+static const unsigned char s6e8ax0_22_gamma_180[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb7, 0xbe, 0xb3, 0xd2,
+	0xb3, 0xc3, 0xde, 0xc2, 0x97, 0xbf, 0x95, 0xb4, 0xcd, 0xb1,
+	0x00, 0xa2, 0x00, 0x99, 0x00, 0xc5,
+};
+
+static const unsigned char s6e8ax0_22_gamma_190[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbe, 0xb2, 0xd2,
+	0xb2, 0xc3, 0xdd, 0xc3, 0x98, 0xbf, 0x95, 0xb2, 0xcc, 0xaf,
+	0x00, 0xa5, 0x00, 0x9c, 0x00, 0xc9,
+};
+
+static const unsigned char s6e8ax0_22_gamma_200[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbc, 0xb2, 0xd2,
+	0xb1, 0xc4, 0xdd, 0xc3, 0x97, 0xbe, 0x95, 0xb1, 0xcb, 0xae,
+	0x00, 0xa8, 0x00, 0x9f, 0x00, 0xcd,
+};
+
+static const unsigned char s6e8ax0_22_gamma_210[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc1, 0xbd, 0xb1, 0xd1,
+	0xb1, 0xc2, 0xde, 0xc2, 0x97, 0xbe, 0x94, 0xB0, 0xc9, 0xad,
+	0x00, 0xae, 0x00, 0xa4, 0x00, 0xd4,
+};
+
+static const unsigned char s6e8ax0_22_gamma_220[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc7, 0xbd, 0xb1, 0xd1,
+	0xb1, 0xc2, 0xdd, 0xc2, 0x97, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
+	0x00, 0xad, 0x00, 0xa2, 0x00, 0xd3,
+};
+
+static const unsigned char s6e8ax0_22_gamma_230[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc3, 0xbd, 0xb2, 0xd1,
+	0xb1, 0xc3, 0xdd, 0xc1, 0x96, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
+	0x00, 0xb0, 0x00, 0xa7, 0x00, 0xd7,
+};
+
+static const unsigned char s6e8ax0_22_gamma_240[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xcb, 0xbd, 0xb1, 0xd2,
+	0xb1, 0xc3, 0xdD, 0xc2, 0x95, 0xbd, 0x93, 0xaf, 0xc8, 0xab,
+	0x00, 0xb3, 0x00, 0xa9, 0x00, 0xdb,
+};
+
+static const unsigned char s6e8ax0_22_gamma_250[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xcc, 0xbe, 0xb0, 0xd2,
+	0xb0, 0xc3, 0xdD, 0xc2, 0x94, 0xbc, 0x92, 0xae, 0xc8, 0xab,
+	0x00, 0xb6, 0x00, 0xab, 0x00, 0xde,
+};
+
+static const unsigned char s6e8ax0_22_gamma_260[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xd0, 0xbe, 0xaf, 0xd1,
+	0xaf, 0xc2, 0xdd, 0xc1, 0x96, 0xbc, 0x93, 0xaf, 0xc8, 0xac,
+	0x00, 0xb7, 0x00, 0xad, 0x00, 0xe0,
+};
+
+static const unsigned char s6e8ax0_22_gamma_270[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xcF, 0xbd, 0xb0, 0xd2,
+	0xaf, 0xc2, 0xdc, 0xc1, 0x95, 0xbd, 0x93, 0xae, 0xc6, 0xaa,
+	0x00, 0xba, 0x00, 0xb0, 0x00, 0xe4,
+};
+
+static const unsigned char s6e8ax0_22_gamma_280[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xd0, 0xbd, 0xaf, 0xd0,
+	0xad, 0xc4, 0xdd, 0xc3, 0x95, 0xbd, 0x93, 0xac, 0xc5, 0xa9,
+	0x00, 0xbd, 0x00, 0xb2, 0x00, 0xe7,
+};
+
+static const unsigned char s6e8ax0_22_gamma_300[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb5, 0xd3, 0xbd, 0xb1, 0xd2,
+	0xb0, 0xc0, 0xdc, 0xc0, 0x94, 0xba, 0x91, 0xac, 0xc5, 0xa9,
+	0x00, 0xc2, 0x00, 0xb7, 0x00, 0xed,
+};
+
+static const unsigned char *s6e8ax0_22_gamma_table[] = {
+	s6e8ax0_22_gamma_30,
+	s6e8ax0_22_gamma_50,
+	s6e8ax0_22_gamma_60,
+	s6e8ax0_22_gamma_70,
+	s6e8ax0_22_gamma_80,
+	s6e8ax0_22_gamma_90,
+	s6e8ax0_22_gamma_100,
+	s6e8ax0_22_gamma_120,
+	s6e8ax0_22_gamma_130,
+	s6e8ax0_22_gamma_140,
+	s6e8ax0_22_gamma_150,
+	s6e8ax0_22_gamma_160,
+	s6e8ax0_22_gamma_170,
+	s6e8ax0_22_gamma_180,
+	s6e8ax0_22_gamma_190,
+	s6e8ax0_22_gamma_200,
+	s6e8ax0_22_gamma_210,
+	s6e8ax0_22_gamma_220,
+	s6e8ax0_22_gamma_230,
+	s6e8ax0_22_gamma_240,
+	s6e8ax0_22_gamma_250,
+	s6e8ax0_22_gamma_260,
+	s6e8ax0_22_gamma_270,
+	s6e8ax0_22_gamma_280,
+	s6e8ax0_22_gamma_300,
+};
+
+static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
+		0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
+		0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
+		0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8
+	};
+	static const unsigned char data_to_send_panel_reverse[] = {
+		0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
+		0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
+		0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
+		0xc1, 0x01, 0x41, 0xc1, 0x00, 0xc1, 0xf6, 0xf6, 0xc1
+	};
+
+	if (lcd->pdata->panel_reverse)
+		dsi_dcs_write(lcd->entity.source, 0,
+				data_to_send_panel_reverse,
+				ARRAY_SIZE(data_to_send_panel_reverse));
+	else
+		dsi_dcs_write(lcd->entity.source, 0,
+				data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_display_cond(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xf2, 0x80, 0x03, 0x0d
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+/* Gamma 2.2 Setting (200cd, 7500K, 10MPCD) */
+static void s6e8ax0_gamma_cond(struct s6e8ax0 *lcd)
+{
+	unsigned int gamma = lcd->bd->props.brightness;
+
+	dsi_dcs_write(lcd->entity.source, 0,
+			s6e8ax0_22_gamma_table[gamma],
+			GAMMA_TABLE_COUNT);
+}
+
+static void s6e8ax0_gamma_update(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xf7, 0x03
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0, data_to_send,
+		ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond1(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xd1, 0xfe, 0x80, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x40,
+		0x0d, 0x00, 0x00
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond2(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0,
+		0x00
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond3(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond4(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond5(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+static void s6e8ax0_etc_cond6(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xe3, 0x40
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond7(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_elvss_set(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xb1, 0x04, 0x00
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_elvss_nvm_set(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xd9, 0x5c, 0x20, 0x0c, 0x0f, 0x41, 0x00, 0x10, 0x11,
+		0x12, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcb, 0xed,
+		0x64, 0xaf
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_sleep_in(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0x10
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_sleep_out(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0x11
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_display_on(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0x29
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_display_off(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0x28
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_apply_level2_key(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xf0, 0x5a, 0x5a
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_acl_on(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xc0, 0x01
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_acl_off(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xc0, 0x00
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+/* Full white 50% reducing setting */
+static void s6e8ax0_acl_ctrl_set(struct s6e8ax0 *lcd)
+{
+	/* Full white 50% reducing setting */
+	static const unsigned char cutoff_50[] = {
+		0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
+		0x00, 0x00, 0x04, 0xff,	0x00, 0x00, 0x00, 0x00, 0x00,
+		0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38,
+		0x3f, 0x46
+	};
+	/* Full white 45% reducing setting */
+	static const unsigned char cutoff_45[] = {
+		0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
+		0x00, 0x00, 0x04, 0xff,	0x00, 0x00, 0x00, 0x00, 0x00,
+		0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31,
+		0x37, 0x3d
+	};
+	/* Full white 40% reducing setting */
+	static const unsigned char cutoff_40[] = {
+		0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
+		0x00, 0x00, 0x04, 0xff,	0x00, 0x00, 0x00, 0x00, 0x00,
+		0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b,
+		0x31, 0x36
+	};
+
+	if (lcd->acl_enable) {
+		if (lcd->cur_acl = 0) {
+			if (lcd->gamma = 0 || lcd->gamma = 1) {
+				s6e8ax0_acl_off(lcd);
+				dev_dbg(lcd->dev,
+					"cur_acl=%d\n", lcd->cur_acl);
+			} else
+				s6e8ax0_acl_on(lcd);
+		}
+		switch (lcd->gamma) {
+		case 0: /* 30cd */
+			s6e8ax0_acl_off(lcd);
+			lcd->cur_acl = 0;
+			break;
+		case 1 ... 3: /* 50cd ~ 90cd */
+			dsi_dcs_write(lcd->entity.source, 0,
+				cutoff_40,
+				ARRAY_SIZE(cutoff_40));
+			lcd->cur_acl = 40;
+			break;
+		case 4 ... 7: /* 120cd ~ 210cd */
+			dsi_dcs_write(lcd->entity.source, 0,
+				cutoff_45,
+				ARRAY_SIZE(cutoff_45));
+			lcd->cur_acl = 45;
+			break;
+		case 8 ... 10: /* 220cd ~ 300cd */
+			dsi_dcs_write(lcd->entity.source, 0,
+				cutoff_50,
+				ARRAY_SIZE(cutoff_50));
+			lcd->cur_acl = 50;
+			break;
+		default:
+			break;
+		}
+	} else {
+		s6e8ax0_acl_off(lcd);
+		lcd->cur_acl = 0;
+		dev_dbg(lcd->dev, "cur_acl = %d\n", lcd->cur_acl);
+	}
+}
+
+static void s6e8ax0_read_id(struct s6e8ax0 *lcd, u8 *mtp_id)
+{
+	unsigned int ret;
+	u8 addr = 0xd1;	/* MTP ID */
+
+	ret = dsi_dcs_read(lcd->entity.source, 0, addr, mtp_id, 3);
+}
+
+static int s6e8ax0_panel_init(struct s6e8ax0 *lcd)
+{
+	s6e8ax0_apply_level2_key(lcd);
+	s6e8ax0_sleep_out(lcd);
+	msleep(1);
+	s6e8ax0_panel_cond(lcd);
+	s6e8ax0_display_cond(lcd);
+	s6e8ax0_gamma_cond(lcd);
+	s6e8ax0_gamma_update(lcd);
+
+	s6e8ax0_etc_cond1(lcd);
+	s6e8ax0_etc_cond2(lcd);
+	s6e8ax0_etc_cond3(lcd);
+	s6e8ax0_etc_cond4(lcd);
+	s6e8ax0_etc_cond5(lcd);
+	s6e8ax0_etc_cond6(lcd);
+	s6e8ax0_etc_cond7(lcd);
+
+	s6e8ax0_elvss_nvm_set(lcd);
+	s6e8ax0_elvss_set(lcd);
+
+	s6e8ax0_acl_ctrl_set(lcd);
+	s6e8ax0_acl_on(lcd);
+
+	/* if ID3 value is not 33h, branch private elvss mode */
+	msleep(lcd->pdata->power_on_delay);
+
+	return 0;
+}
+
+static int s6e8ax0_update_gamma_ctrl(struct s6e8ax0 *lcd, int brightness)
+{
+	dsi_dcs_write(lcd->entity.source, 0,
+			s6e8ax0_22_gamma_table[brightness],
+			ARRAY_SIZE(s6e8ax0_22_gamma_table));
+
+	/* update gamma table. */
+	s6e8ax0_gamma_update(lcd);
+	lcd->gamma = brightness;
+
+	return 0;
+}
+
+static int s6e8ax0_gamma_ctrl(struct s6e8ax0 *lcd, int gamma)
+{
+	s6e8ax0_update_gamma_ctrl(lcd, gamma);
+
+	return 0;
+}
+
+static int s6e8ax0_get_brightness(struct backlight_device *bd)
+{
+	return bd->props.brightness;
+}
+
+static int s6e8ax0_set_brightness(struct backlight_device *bd)
+{
+	int ret = 0, brightness = bd->props.brightness;
+	struct s6e8ax0 *lcd = bl_get_data(bd);
+
+	if (brightness < MIN_BRIGHTNESS ||
+		brightness > bd->props.max_brightness) {
+		dev_err(lcd->dev,
+					"lcd brightness should be %d to %d.\n",
+					MIN_BRIGHTNESS, MAX_BRIGHTNESS);
+		return -EINVAL;
+	}
+
+	ret = s6e8ax0_gamma_ctrl(lcd, brightness);
+	if (ret) {
+		dev_err(lcd->dev,
+					"lcd brightness setting failed.\n");
+		return -EIO;
+	}
+
+	return ret;
+}
+
+static const struct backlight_ops s6e8ax0_backlight_ops = {
+	.get_brightness = s6e8ax0_get_brightness,
+	.update_status = s6e8ax0_set_brightness,
+};
+
+static int s6e8ax0_set_power(struct lcd_device *ld, int power)
+{
+	struct s6e8ax0 *lcd = lcd_get_data(ld);
+	enum display_entity_state state;
+	int ret;
+
+	if (power = FB_BLANK_POWERDOWN)
+		state = DISPLAY_ENTITY_STATE_OFF;
+	else if (power != FB_BLANK_UNBLANK)
+		state = DISPLAY_ENTITY_STATE_STANDBY;
+	else
+		state = DISPLAY_ENTITY_STATE_ON;
+
+	ret = display_entity_set_state(&lcd->entity, state);
+	if (ret)
+		return ret;
+
+	lcd->power = power;
+	return 0;
+}
+
+static int s6e8ax0_get_power(struct lcd_device *ld)
+{
+	struct s6e8ax0 *lcd = lcd_get_data(ld);
+
+	return lcd->power;
+}
+
+static struct lcd_ops s6e8ax0_lcd_ops = {
+	.set_power = s6e8ax0_set_power,
+	.get_power = s6e8ax0_get_power,
+};
+
+static void s6e8ax0_set_sequence(struct s6e8ax0 *lcd)
+{
+	u8 mtp_id[3] = {0, };
+
+	msleep(100);
+
+	s6e8ax0_read_id(lcd, mtp_id);
+	if (mtp_id[0] = 0x00)
+		dev_err(lcd->dev, "read id failed\n");
+
+	dev_info(lcd->dev, "Read ID : %x, %x, %x\n",
+			mtp_id[0], mtp_id[1], mtp_id[2]);
+
+	if (mtp_id[2] = 0x33)
+		dev_info(lcd->dev,
+			"ID-3 is 0xff does not support dynamic elvss\n");
+	else
+		dev_info(lcd->dev,
+			"ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]);
+
+	s6e8ax0_panel_init(lcd);
+	s6e8ax0_display_on(lcd);
+}
+
+#ifdef CONFIG_OF
+static int s6e8ax0_generic_reset(struct device *dev)
+{
+	struct s6e8ax0 *lcd = dev_get_drvdata(dev);
+
+	mdelay(10);
+	gpio_set_value(lcd->reset_gpio, 0);
+	mdelay(10);
+	gpio_set_value(lcd->reset_gpio, 1);
+
+	return 0;
+}
+
+static struct s6e8ax0_platform_data *s6e8ax0_parse_dt(struct s6e8ax0 *lcd)
+{
+	struct device_node *node = lcd->dev->of_node;
+	struct s6e8ax0_platform_data *data;
+	const __be32 *prop_data;
+	int ret;
+
+	data = devm_kzalloc(lcd->dev, sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		dev_err(lcd->dev, "failed to allocate platform data.\n");
+		return NULL;
+	}
+
+	ret = of_get_videomode(node, &data->mode, 0);
+	if (ret) {
+		dev_err(lcd->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-off-delay", NULL);
+	if (!prop_data)
+		return NULL;
+	data->power_off_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);
+
+	data->reset = s6e8ax0_generic_reset;
+
+	return data;
+}
+
+static struct of_device_id s6e8ax0_of_match[] = {
+	{ .compatible = "samsung,s6e8ax0" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, s6e8ax0_of_match);
+#else
+static struct s6e8ax0_platform_data *s6e8ax0_parse_dt(struct s6e8ax0 *lcd)
+{
+	return NULL;
+}
+#endif
+
+static const struct display_entity_interface_params s6e8ax0_params = {
+	.type = DISPLAY_ENTITY_INTERFACE_DSI,
+	.p.dsi = {
+		.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 = 10000000,
+	},
+};
+
+static int s6e8ax0_set_state(struct display_entity *entity,
+			    enum display_entity_state state)
+{
+	struct s6e8ax0 *panel = to_panel(entity);
+
+	switch (state) {
+	case DISPLAY_ENTITY_STATE_OFF:
+		if (entity->state = DISPLAY_ENTITY_STATE_ON) {
+			s6e8ax0_sleep_in(panel);
+			panel->entity.source->common_ops->set_stream(entity->source,
+						DISPLAY_ENTITY_STREAM_STOPPED);
+
+			msleep(panel->pdata->power_off_delay);
+			s6e8ax0_display_off(panel);
+
+			panel->entity.source->ops.dsi->disable(entity->source);
+
+			regulator_bulk_disable(ARRAY_SIZE(panel->supplies),
+								panel->supplies);
+		} else if (entity->state = DISPLAY_ENTITY_STATE_STANDBY) {
+			msleep(panel->pdata->power_off_delay);
+			s6e8ax0_display_off(panel);
+
+			panel->entity.source->ops.dsi->disable(entity->source);
+
+			regulator_bulk_disable(ARRAY_SIZE(panel->supplies),
+								panel->supplies);
+		}
+		break;
+
+	case DISPLAY_ENTITY_STATE_STANDBY:
+		if (entity->state = DISPLAY_ENTITY_STATE_ON) {
+			s6e8ax0_sleep_in(panel);
+			panel->entity.source->common_ops->set_stream(entity->source,
+						DISPLAY_ENTITY_STREAM_STOPPED);
+		} else if (entity->state = DISPLAY_ENTITY_STATE_OFF) {
+			msleep(panel->pdata->power_on_delay);
+
+			regulator_bulk_enable(ARRAY_SIZE(panel->supplies),
+								panel->supplies);
+
+			msleep(panel->pdata->reset_delay);
+
+			/* lcd reset */
+			if (panel->pdata->reset)
+				panel->pdata->reset(panel->dev);
+			msleep(5);
+
+			panel->entity.source->ops.dsi->enable(entity->source);
+
+			s6e8ax0_set_sequence(panel);
+			s6e8ax0_sleep_in(panel);
+		}
+		break;
+
+	case DISPLAY_ENTITY_STATE_ON:
+		if (entity->state = DISPLAY_ENTITY_STATE_OFF) {
+			msleep(panel->pdata->power_on_delay);
+
+			regulator_bulk_enable(ARRAY_SIZE(panel->supplies),
+								panel->supplies);
+
+			msleep(panel->pdata->reset_delay);
+
+			/* lcd reset */
+			if (panel->pdata->reset)
+				panel->pdata->reset(panel->dev);
+			msleep(5);
+
+			panel->entity.source->ops.dsi->enable(entity->source);
+
+			s6e8ax0_set_sequence(panel);
+
+			panel->entity.source->common_ops->set_stream(entity->source,
+					DISPLAY_ENTITY_STREAM_CONTINUOUS);
+		} else {
+			panel->entity.source->common_ops->set_stream(entity->source,
+					DISPLAY_ENTITY_STREAM_CONTINUOUS);
+			s6e8ax0_sleep_out(panel);
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int s6e8ax0_get_modes(struct display_entity *entity,
+			    const struct videomode **modes)
+{
+	struct s6e8ax0 *panel = to_panel(entity);
+
+	*modes = &panel->pdata->mode;
+	return 1;
+}
+
+static int s6e8ax0_get_size(struct display_entity *entity,
+			   unsigned int *width, unsigned int *height)
+{
+	struct s6e8ax0 *panel = to_panel(entity);
+
+	*width = panel->pdata->width;
+	*height = panel->pdata->height;
+	return 0;
+}
+
+static int s6e8ax0_get_params(struct display_entity *entity,
+				struct display_entity_interface_params *params)
+{
+	*params = s6e8ax0_params;
+	return 0;
+}
+
+static const struct display_entity_control_ops s6e8ax0_control_ops = {
+	.set_state = s6e8ax0_set_state,
+	.get_modes = s6e8ax0_get_modes,
+	.get_size = s6e8ax0_get_size,
+	.get_params = s6e8ax0_get_params,
+};
+
+static void s6e8ax0_release(struct display_entity *entity)
+{
+	struct s6e8ax0 *panel = to_panel(entity);
+
+	backlight_device_unregister(panel->bd);
+	lcd_device_unregister(panel->ld);
+	regulator_bulk_free(ARRAY_SIZE(panel->supplies), panel->supplies);
+	kfree(panel);
+}
+
+static int s6e8ax0_probe(struct platform_device *pdev)
+{
+	struct s6e8ax0 *lcd;
+	int ret;
+
+	lcd = kzalloc(sizeof(struct s6e8ax0), GFP_KERNEL);
+	if (!lcd) {
+		dev_err(&pdev->dev, "failed to allocate s6e8ax0 structure.\n");
+		return -ENOMEM;
+	}
+
+	lcd->dev = &pdev->dev;
+	lcd->pdata = (struct s6e8ax0_platform_data *)pdev->dev.platform_data;
+
+	if (!lcd->pdata) {
+		lcd->pdata = s6e8ax0_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("s6e8ax0", &pdev->dev, lcd,
+			&s6e8ax0_lcd_ops);
+	if (IS_ERR(lcd->ld)) {
+		dev_err(&pdev->dev, "failed to register lcd ops.\n");
+		ret = PTR_ERR(lcd->ld);
+		goto err_lcd_register;
+	}
+
+	lcd->bd = backlight_device_register("s6e8ax0-bl", &pdev->dev, lcd,
+			&s6e8ax0_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;
+	}
+
+	lcd->acl_enable = 1;
+	lcd->cur_acl = 0;
+	lcd->power = FB_BLANK_UNBLANK;
+
+	lcd->bd->props.max_brightness = MAX_BRIGHTNESS;
+	lcd->bd->props.brightness = MAX_BRIGHTNESS;
+
+	lcd->entity.of_node = pdev->dev.of_node;
+	lcd->entity.dev = &pdev->dev;
+	lcd->entity.release = s6e8ax0_release;
+	lcd->entity.ops = &s6e8ax0_control_ops;
+
+	platform_set_drvdata(pdev, lcd);
+
+	ret = display_entity_register(&lcd->entity);
+	if (ret < 0)
+		goto err_display_register;
+
+	display_entity_set_state(&lcd->entity, DISPLAY_ENTITY_STATE_ON);
+
+	dev_dbg(&pdev->dev, "probed s6e8ax0 panel driver.\n");
+
+	return 0;
+
+err_display_register:
+	backlight_device_unregister(lcd->bd);
+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 s6e8ax0_remove(struct platform_device *dev)
+{
+	struct s6e8ax0 *lcd = platform_get_drvdata(dev);
+
+	platform_set_drvdata(dev, NULL);
+	display_entity_unregister(&lcd->entity);
+
+	return 0;
+}
+
+static int s6e8ax0_suspend(struct device *dev)
+{
+	struct s6e8ax0 *lcd = dev_get_drvdata(dev);
+
+	if (lcd->power = FB_BLANK_UNBLANK)
+		display_entity_set_state(&lcd->entity,
+						DISPLAY_ENTITY_STATE_OFF);
+
+	return 0;
+}
+
+static int s6e8ax0_resume(struct device *dev)
+{
+	struct s6e8ax0 *lcd = dev_get_drvdata(dev);
+
+	if (lcd->power = FB_BLANK_UNBLANK)
+		display_entity_set_state(&lcd->entity,
+						DISPLAY_ENTITY_STATE_ON);
+
+	return 0;
+}
+
+static struct dev_pm_ops s6e8ax0_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(s6e8ax0_suspend, s6e8ax0_resume)
+};
+
+static struct platform_driver s6e8ax0_driver = {
+	.probe = s6e8ax0_probe,
+	.remove = s6e8ax0_remove,
+	.driver = {
+		.name = "panel_s6e8ax0",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(s6e8ax0_of_match),
+		.pm = &s6e8ax0_pm_ops,
+	},
+};
+module_platform_driver(s6e8ax0_driver);
+
+MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
+MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
+MODULE_DESCRIPTION("MIPI-DSI based s6e8ax0 AMOLED LCD Panel Driver");
+MODULE_LICENSE("GPL");
diff --git a/include/video/panel-s6e8ax0.h b/include/video/panel-s6e8ax0.h
new file mode 100644
index 0000000..e522bfb
--- /dev/null
+++ b/include/video/panel-s6e8ax0.h
@@ -0,0 +1,41 @@
+/*
+ * 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 <linux/videomode.h>
+
+struct s6e8ax0_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 power_off_delay;
+	/* panel is reversed */
+	bool panel_reverse;
+};
+
+#endif /* __PANEL_S6E8AX0_H__ */
-- 
1.8.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [RFC PATCH 4/4] video: display: Add Samsung s6e8ax0 display panel driver
@ 2013-01-30 15:39   ` Tomasz Figa
  0 siblings, 0 replies; 36+ messages in thread
From: Tomasz Figa @ 2013-01-30 15:39 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	t.figa, tomasz.figa, Daniel Vetter, Marcus Lorentzon,
	Laurent Pinchart, rob, tomi.valkeinen, Vikas Sajjan, inki.dae,
	dh09.lee, ville.syrjala, s.nawrocki

This patch adds Common Display Framework driver for Samsung s6e8ax0
MIPI DSI display panel.

Signed-off-by: Tomasz Figa <t.figa@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/video/display/Kconfig         |    3 +
 drivers/video/display/Makefile        |    1 +
 drivers/video/display/panel-s6e8ax0.c | 1027 +++++++++++++++++++++++++++++++++
 include/video/panel-s6e8ax0.h         |   41 ++
 4 files changed, 1072 insertions(+)
 create mode 100644 drivers/video/display/panel-s6e8ax0.c
 create mode 100644 include/video/panel-s6e8ax0.h

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index b14527a..f19ec04 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -5,6 +5,9 @@ menuconfig DISPLAY_CORE
 
 if DISPLAY_CORE
 
+config DISPLAY_PANEL_S6E8AX0
+	tristate "S6E8AX0 DSI video mode panel"
+	select OF_VIDEOMODE
 
 config DISPLAY_SOURCE_EXYNOS_DSI
 	tristate "Samsung SoC MIPI DSI Master"
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index 40a283a..0f7fdc2 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_DISPLAY_CORE) += display-core.o
+obj-$(CONFIG_DISPLAY_PANEL_S6E8AX0) += panel-s6e8ax0.o
 obj-$(CONFIG_DISPLAY_SOURCE_EXYNOS_DSI) += source-exynos_dsi.o
diff --git a/drivers/video/display/panel-s6e8ax0.c b/drivers/video/display/panel-s6e8ax0.c
new file mode 100644
index 0000000..4c09fe2
--- /dev/null
+++ b/drivers/video/display/panel-s6e8ax0.c
@@ -0,0 +1,1027 @@
+/* linux/drivers/video/exynos/s6e8ax0.c
+ *
+ * MIPI-DSI based s6e8ax0 AMOLED lcd 4.65 inch panel driver.
+ *
+ * Inki Dae, <inki.dae@samsung.com>
+ * Donghwa Lee, <dh09.lee@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/errno.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/lcd.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_videomode.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/backlight.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/display.h>
+#include <video/mipi_display.h>
+#include <video/panel-s6e8ax0.h>
+
+#define LDI_MTP_LENGTH		24
+#define DSIM_PM_STABLE_TIME	10
+#define MIN_BRIGHTNESS		0
+#define MAX_BRIGHTNESS		24
+#define GAMMA_TABLE_COUNT	26
+
+struct s6e8ax0 {
+	struct display_entity entity;
+	struct device *dev;
+
+	struct s6e8ax0_platform_data *pdata;
+	struct backlight_device	*bd;
+	struct lcd_device *ld;
+	struct regulator_bulk_data supplies[2];
+
+	bool enabled;
+	unsigned int id;
+	unsigned int gamma;
+	unsigned int acl_enable;
+	unsigned int cur_acl;
+	int power;
+
+	unsigned int reset_gpio;
+};
+
+#define to_panel(p)	container_of(p, struct s6e8ax0, entity)
+
+static const unsigned char s6e8ax0_22_gamma_30[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xf5, 0x00, 0xff, 0xad, 0xaf,
+	0xbA, 0xc3, 0xd8, 0xc5, 0x9f, 0xc6, 0x9e, 0xc1, 0xdc, 0xc0,
+	0x00, 0x61, 0x00, 0x5a, 0x00, 0x74,
+};
+
+static const unsigned char s6e8ax0_22_gamma_50[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xe8, 0x1f, 0xf7, 0xad, 0xc0,
+	0xb5, 0xc4, 0xdc, 0xc4, 0x9e, 0xc6, 0x9c, 0xbb, 0xd8, 0xbb,
+	0x00, 0x70, 0x00, 0x68, 0x00, 0x86,
+};
+
+static const unsigned char s6e8ax0_22_gamma_60[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xde, 0x1f, 0xef, 0xad, 0xc4,
+	0xb3, 0xc3, 0xdd, 0xc4, 0x9e, 0xc6, 0x9c, 0xbc, 0xd6, 0xba,
+	0x00, 0x75, 0x00, 0x6e, 0x00, 0x8d,
+};
+
+static const unsigned char s6e8ax0_22_gamma_70[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xd8, 0x1f, 0xe7, 0xaf, 0xc8,
+	0xb4, 0xc4, 0xdd, 0xc3, 0x9d, 0xc6, 0x9c, 0xbb, 0xd6, 0xb9,
+	0x00, 0x7a, 0x00, 0x72, 0x00, 0x93,
+};
+
+static const unsigned char s6e8ax0_22_gamma_80[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xc9, 0x1f, 0xde, 0xae, 0xc9,
+	0xb1, 0xc3, 0xdd, 0xc2, 0x9d, 0xc5, 0x9b, 0xbc, 0xd6, 0xbb,
+	0x00, 0x7f, 0x00, 0x77, 0x00, 0x99,
+};
+
+static const unsigned char s6e8ax0_22_gamma_90[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xc7, 0x1f, 0xd9, 0xb0, 0xcc,
+	0xb2, 0xc3, 0xdc, 0xc1, 0x9c, 0xc6, 0x9c, 0xbc, 0xd4, 0xb9,
+	0x00, 0x83, 0x00, 0x7b, 0x00, 0x9e,
+};
+
+static const unsigned char s6e8ax0_22_gamma_100[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xbd, 0x80, 0xcd, 0xba, 0xce,
+	0xb3, 0xc4, 0xde, 0xc3, 0x9c, 0xc4, 0x9, 0xb8, 0xd3, 0xb6,
+	0x00, 0x88, 0x00, 0x80, 0x00, 0xa5,
+};
+
+static const unsigned char s6e8ax0_22_gamma_120[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb9, 0x95, 0xc8, 0xb1, 0xcf,
+	0xb2, 0xc6, 0xdf, 0xc5, 0x9b, 0xc3, 0x99, 0xb6, 0xd2, 0xb6,
+	0x00, 0x8f, 0x00, 0x86, 0x00, 0xac,
+};
+
+static const unsigned char s6e8ax0_22_gamma_130[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc7, 0xb1, 0xd0,
+	0xb2, 0xc4, 0xdd, 0xc3, 0x9a, 0xc3, 0x98, 0xb6, 0xd0, 0xb4,
+	0x00, 0x92, 0x00, 0x8a, 0x00, 0xb1,
+};
+
+static const unsigned char s6e8ax0_22_gamma_140[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc5, 0xb2, 0xd0,
+	0xb3, 0xc3, 0xde, 0xc3, 0x9b, 0xc2, 0x98, 0xb6, 0xd0, 0xb4,
+	0x00, 0x95, 0x00, 0x8d, 0x00, 0xb5,
+};
+
+static const unsigned char s6e8ax0_22_gamma_150[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xa0, 0xc2, 0xb2, 0xd0,
+	0xb2, 0xc1, 0xdd, 0xc2, 0x9b, 0xc2, 0x98, 0xb4, 0xcf, 0xb1,
+	0x00, 0x99, 0x00, 0x90, 0x00, 0xba,
+};
+
+static const unsigned char s6e8ax0_22_gamma_160[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xa5, 0xbf, 0xb0, 0xd0,
+	0xb1, 0xc3, 0xde, 0xc2, 0x99, 0xc1, 0x97, 0xb4, 0xce, 0xb1,
+	0x00, 0x9c, 0x00, 0x93, 0x00, 0xbe,
+};
+
+static const unsigned char s6e8ax0_22_gamma_170[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb5, 0xbf, 0xb1, 0xd1,
+	0xb1, 0xc3, 0xde, 0xc3, 0x99, 0xc0, 0x96, 0xb4, 0xce, 0xb1,
+	0x00, 0x9f, 0x00, 0x96, 0x00, 0xc2,
+};
+
+static const unsigned char s6e8ax0_22_gamma_180[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb7, 0xbe, 0xb3, 0xd2,
+	0xb3, 0xc3, 0xde, 0xc2, 0x97, 0xbf, 0x95, 0xb4, 0xcd, 0xb1,
+	0x00, 0xa2, 0x00, 0x99, 0x00, 0xc5,
+};
+
+static const unsigned char s6e8ax0_22_gamma_190[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbe, 0xb2, 0xd2,
+	0xb2, 0xc3, 0xdd, 0xc3, 0x98, 0xbf, 0x95, 0xb2, 0xcc, 0xaf,
+	0x00, 0xa5, 0x00, 0x9c, 0x00, 0xc9,
+};
+
+static const unsigned char s6e8ax0_22_gamma_200[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbc, 0xb2, 0xd2,
+	0xb1, 0xc4, 0xdd, 0xc3, 0x97, 0xbe, 0x95, 0xb1, 0xcb, 0xae,
+	0x00, 0xa8, 0x00, 0x9f, 0x00, 0xcd,
+};
+
+static const unsigned char s6e8ax0_22_gamma_210[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc1, 0xbd, 0xb1, 0xd1,
+	0xb1, 0xc2, 0xde, 0xc2, 0x97, 0xbe, 0x94, 0xB0, 0xc9, 0xad,
+	0x00, 0xae, 0x00, 0xa4, 0x00, 0xd4,
+};
+
+static const unsigned char s6e8ax0_22_gamma_220[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc7, 0xbd, 0xb1, 0xd1,
+	0xb1, 0xc2, 0xdd, 0xc2, 0x97, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
+	0x00, 0xad, 0x00, 0xa2, 0x00, 0xd3,
+};
+
+static const unsigned char s6e8ax0_22_gamma_230[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc3, 0xbd, 0xb2, 0xd1,
+	0xb1, 0xc3, 0xdd, 0xc1, 0x96, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
+	0x00, 0xb0, 0x00, 0xa7, 0x00, 0xd7,
+};
+
+static const unsigned char s6e8ax0_22_gamma_240[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xcb, 0xbd, 0xb1, 0xd2,
+	0xb1, 0xc3, 0xdD, 0xc2, 0x95, 0xbd, 0x93, 0xaf, 0xc8, 0xab,
+	0x00, 0xb3, 0x00, 0xa9, 0x00, 0xdb,
+};
+
+static const unsigned char s6e8ax0_22_gamma_250[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xcc, 0xbe, 0xb0, 0xd2,
+	0xb0, 0xc3, 0xdD, 0xc2, 0x94, 0xbc, 0x92, 0xae, 0xc8, 0xab,
+	0x00, 0xb6, 0x00, 0xab, 0x00, 0xde,
+};
+
+static const unsigned char s6e8ax0_22_gamma_260[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xd0, 0xbe, 0xaf, 0xd1,
+	0xaf, 0xc2, 0xdd, 0xc1, 0x96, 0xbc, 0x93, 0xaf, 0xc8, 0xac,
+	0x00, 0xb7, 0x00, 0xad, 0x00, 0xe0,
+};
+
+static const unsigned char s6e8ax0_22_gamma_270[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xcF, 0xbd, 0xb0, 0xd2,
+	0xaf, 0xc2, 0xdc, 0xc1, 0x95, 0xbd, 0x93, 0xae, 0xc6, 0xaa,
+	0x00, 0xba, 0x00, 0xb0, 0x00, 0xe4,
+};
+
+static const unsigned char s6e8ax0_22_gamma_280[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xd0, 0xbd, 0xaf, 0xd0,
+	0xad, 0xc4, 0xdd, 0xc3, 0x95, 0xbd, 0x93, 0xac, 0xc5, 0xa9,
+	0x00, 0xbd, 0x00, 0xb2, 0x00, 0xe7,
+};
+
+static const unsigned char s6e8ax0_22_gamma_300[] = {
+	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb5, 0xd3, 0xbd, 0xb1, 0xd2,
+	0xb0, 0xc0, 0xdc, 0xc0, 0x94, 0xba, 0x91, 0xac, 0xc5, 0xa9,
+	0x00, 0xc2, 0x00, 0xb7, 0x00, 0xed,
+};
+
+static const unsigned char *s6e8ax0_22_gamma_table[] = {
+	s6e8ax0_22_gamma_30,
+	s6e8ax0_22_gamma_50,
+	s6e8ax0_22_gamma_60,
+	s6e8ax0_22_gamma_70,
+	s6e8ax0_22_gamma_80,
+	s6e8ax0_22_gamma_90,
+	s6e8ax0_22_gamma_100,
+	s6e8ax0_22_gamma_120,
+	s6e8ax0_22_gamma_130,
+	s6e8ax0_22_gamma_140,
+	s6e8ax0_22_gamma_150,
+	s6e8ax0_22_gamma_160,
+	s6e8ax0_22_gamma_170,
+	s6e8ax0_22_gamma_180,
+	s6e8ax0_22_gamma_190,
+	s6e8ax0_22_gamma_200,
+	s6e8ax0_22_gamma_210,
+	s6e8ax0_22_gamma_220,
+	s6e8ax0_22_gamma_230,
+	s6e8ax0_22_gamma_240,
+	s6e8ax0_22_gamma_250,
+	s6e8ax0_22_gamma_260,
+	s6e8ax0_22_gamma_270,
+	s6e8ax0_22_gamma_280,
+	s6e8ax0_22_gamma_300,
+};
+
+static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
+		0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
+		0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
+		0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8
+	};
+	static const unsigned char data_to_send_panel_reverse[] = {
+		0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
+		0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
+		0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
+		0xc1, 0x01, 0x41, 0xc1, 0x00, 0xc1, 0xf6, 0xf6, 0xc1
+	};
+
+	if (lcd->pdata->panel_reverse)
+		dsi_dcs_write(lcd->entity.source, 0,
+				data_to_send_panel_reverse,
+				ARRAY_SIZE(data_to_send_panel_reverse));
+	else
+		dsi_dcs_write(lcd->entity.source, 0,
+				data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_display_cond(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xf2, 0x80, 0x03, 0x0d
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+/* Gamma 2.2 Setting (200cd, 7500K, 10MPCD) */
+static void s6e8ax0_gamma_cond(struct s6e8ax0 *lcd)
+{
+	unsigned int gamma = lcd->bd->props.brightness;
+
+	dsi_dcs_write(lcd->entity.source, 0,
+			s6e8ax0_22_gamma_table[gamma],
+			GAMMA_TABLE_COUNT);
+}
+
+static void s6e8ax0_gamma_update(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xf7, 0x03
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0, data_to_send,
+		ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond1(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xd1, 0xfe, 0x80, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x40,
+		0x0d, 0x00, 0x00
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond2(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0,
+		0x00
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond3(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond4(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond5(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+static void s6e8ax0_etc_cond6(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xe3, 0x40
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond7(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_elvss_set(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xb1, 0x04, 0x00
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_elvss_nvm_set(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xd9, 0x5c, 0x20, 0x0c, 0x0f, 0x41, 0x00, 0x10, 0x11,
+		0x12, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcb, 0xed,
+		0x64, 0xaf
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_sleep_in(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0x10
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_sleep_out(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0x11
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_display_on(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0x29
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_display_off(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0x28
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_apply_level2_key(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xf0, 0x5a, 0x5a
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_acl_on(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xc0, 0x01
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_acl_off(struct s6e8ax0 *lcd)
+{
+	static const unsigned char data_to_send[] = {
+		0xc0, 0x00
+	};
+
+	dsi_dcs_write(lcd->entity.source, 0,
+		data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+/* Full white 50% reducing setting */
+static void s6e8ax0_acl_ctrl_set(struct s6e8ax0 *lcd)
+{
+	/* Full white 50% reducing setting */
+	static const unsigned char cutoff_50[] = {
+		0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
+		0x00, 0x00, 0x04, 0xff,	0x00, 0x00, 0x00, 0x00, 0x00,
+		0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38,
+		0x3f, 0x46
+	};
+	/* Full white 45% reducing setting */
+	static const unsigned char cutoff_45[] = {
+		0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
+		0x00, 0x00, 0x04, 0xff,	0x00, 0x00, 0x00, 0x00, 0x00,
+		0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31,
+		0x37, 0x3d
+	};
+	/* Full white 40% reducing setting */
+	static const unsigned char cutoff_40[] = {
+		0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
+		0x00, 0x00, 0x04, 0xff,	0x00, 0x00, 0x00, 0x00, 0x00,
+		0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b,
+		0x31, 0x36
+	};
+
+	if (lcd->acl_enable) {
+		if (lcd->cur_acl == 0) {
+			if (lcd->gamma == 0 || lcd->gamma == 1) {
+				s6e8ax0_acl_off(lcd);
+				dev_dbg(lcd->dev,
+					"cur_acl=%d\n", lcd->cur_acl);
+			} else
+				s6e8ax0_acl_on(lcd);
+		}
+		switch (lcd->gamma) {
+		case 0: /* 30cd */
+			s6e8ax0_acl_off(lcd);
+			lcd->cur_acl = 0;
+			break;
+		case 1 ... 3: /* 50cd ~ 90cd */
+			dsi_dcs_write(lcd->entity.source, 0,
+				cutoff_40,
+				ARRAY_SIZE(cutoff_40));
+			lcd->cur_acl = 40;
+			break;
+		case 4 ... 7: /* 120cd ~ 210cd */
+			dsi_dcs_write(lcd->entity.source, 0,
+				cutoff_45,
+				ARRAY_SIZE(cutoff_45));
+			lcd->cur_acl = 45;
+			break;
+		case 8 ... 10: /* 220cd ~ 300cd */
+			dsi_dcs_write(lcd->entity.source, 0,
+				cutoff_50,
+				ARRAY_SIZE(cutoff_50));
+			lcd->cur_acl = 50;
+			break;
+		default:
+			break;
+		}
+	} else {
+		s6e8ax0_acl_off(lcd);
+		lcd->cur_acl = 0;
+		dev_dbg(lcd->dev, "cur_acl = %d\n", lcd->cur_acl);
+	}
+}
+
+static void s6e8ax0_read_id(struct s6e8ax0 *lcd, u8 *mtp_id)
+{
+	unsigned int ret;
+	u8 addr = 0xd1;	/* MTP ID */
+
+	ret = dsi_dcs_read(lcd->entity.source, 0, addr, mtp_id, 3);
+}
+
+static int s6e8ax0_panel_init(struct s6e8ax0 *lcd)
+{
+	s6e8ax0_apply_level2_key(lcd);
+	s6e8ax0_sleep_out(lcd);
+	msleep(1);
+	s6e8ax0_panel_cond(lcd);
+	s6e8ax0_display_cond(lcd);
+	s6e8ax0_gamma_cond(lcd);
+	s6e8ax0_gamma_update(lcd);
+
+	s6e8ax0_etc_cond1(lcd);
+	s6e8ax0_etc_cond2(lcd);
+	s6e8ax0_etc_cond3(lcd);
+	s6e8ax0_etc_cond4(lcd);
+	s6e8ax0_etc_cond5(lcd);
+	s6e8ax0_etc_cond6(lcd);
+	s6e8ax0_etc_cond7(lcd);
+
+	s6e8ax0_elvss_nvm_set(lcd);
+	s6e8ax0_elvss_set(lcd);
+
+	s6e8ax0_acl_ctrl_set(lcd);
+	s6e8ax0_acl_on(lcd);
+
+	/* if ID3 value is not 33h, branch private elvss mode */
+	msleep(lcd->pdata->power_on_delay);
+
+	return 0;
+}
+
+static int s6e8ax0_update_gamma_ctrl(struct s6e8ax0 *lcd, int brightness)
+{
+	dsi_dcs_write(lcd->entity.source, 0,
+			s6e8ax0_22_gamma_table[brightness],
+			ARRAY_SIZE(s6e8ax0_22_gamma_table));
+
+	/* update gamma table. */
+	s6e8ax0_gamma_update(lcd);
+	lcd->gamma = brightness;
+
+	return 0;
+}
+
+static int s6e8ax0_gamma_ctrl(struct s6e8ax0 *lcd, int gamma)
+{
+	s6e8ax0_update_gamma_ctrl(lcd, gamma);
+
+	return 0;
+}
+
+static int s6e8ax0_get_brightness(struct backlight_device *bd)
+{
+	return bd->props.brightness;
+}
+
+static int s6e8ax0_set_brightness(struct backlight_device *bd)
+{
+	int ret = 0, brightness = bd->props.brightness;
+	struct s6e8ax0 *lcd = bl_get_data(bd);
+
+	if (brightness < MIN_BRIGHTNESS ||
+		brightness > bd->props.max_brightness) {
+		dev_err(lcd->dev,
+					"lcd brightness should be %d to %d.\n",
+					MIN_BRIGHTNESS, MAX_BRIGHTNESS);
+		return -EINVAL;
+	}
+
+	ret = s6e8ax0_gamma_ctrl(lcd, brightness);
+	if (ret) {
+		dev_err(lcd->dev,
+					"lcd brightness setting failed.\n");
+		return -EIO;
+	}
+
+	return ret;
+}
+
+static const struct backlight_ops s6e8ax0_backlight_ops = {
+	.get_brightness = s6e8ax0_get_brightness,
+	.update_status = s6e8ax0_set_brightness,
+};
+
+static int s6e8ax0_set_power(struct lcd_device *ld, int power)
+{
+	struct s6e8ax0 *lcd = lcd_get_data(ld);
+	enum display_entity_state state;
+	int ret;
+
+	if (power == FB_BLANK_POWERDOWN)
+		state = DISPLAY_ENTITY_STATE_OFF;
+	else if (power != FB_BLANK_UNBLANK)
+		state = DISPLAY_ENTITY_STATE_STANDBY;
+	else
+		state = DISPLAY_ENTITY_STATE_ON;
+
+	ret = display_entity_set_state(&lcd->entity, state);
+	if (ret)
+		return ret;
+
+	lcd->power = power;
+	return 0;
+}
+
+static int s6e8ax0_get_power(struct lcd_device *ld)
+{
+	struct s6e8ax0 *lcd = lcd_get_data(ld);
+
+	return lcd->power;
+}
+
+static struct lcd_ops s6e8ax0_lcd_ops = {
+	.set_power = s6e8ax0_set_power,
+	.get_power = s6e8ax0_get_power,
+};
+
+static void s6e8ax0_set_sequence(struct s6e8ax0 *lcd)
+{
+	u8 mtp_id[3] = {0, };
+
+	msleep(100);
+
+	s6e8ax0_read_id(lcd, mtp_id);
+	if (mtp_id[0] == 0x00)
+		dev_err(lcd->dev, "read id failed\n");
+
+	dev_info(lcd->dev, "Read ID : %x, %x, %x\n",
+			mtp_id[0], mtp_id[1], mtp_id[2]);
+
+	if (mtp_id[2] == 0x33)
+		dev_info(lcd->dev,
+			"ID-3 is 0xff does not support dynamic elvss\n");
+	else
+		dev_info(lcd->dev,
+			"ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]);
+
+	s6e8ax0_panel_init(lcd);
+	s6e8ax0_display_on(lcd);
+}
+
+#ifdef CONFIG_OF
+static int s6e8ax0_generic_reset(struct device *dev)
+{
+	struct s6e8ax0 *lcd = dev_get_drvdata(dev);
+
+	mdelay(10);
+	gpio_set_value(lcd->reset_gpio, 0);
+	mdelay(10);
+	gpio_set_value(lcd->reset_gpio, 1);
+
+	return 0;
+}
+
+static struct s6e8ax0_platform_data *s6e8ax0_parse_dt(struct s6e8ax0 *lcd)
+{
+	struct device_node *node = lcd->dev->of_node;
+	struct s6e8ax0_platform_data *data;
+	const __be32 *prop_data;
+	int ret;
+
+	data = devm_kzalloc(lcd->dev, sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		dev_err(lcd->dev, "failed to allocate platform data.\n");
+		return NULL;
+	}
+
+	ret = of_get_videomode(node, &data->mode, 0);
+	if (ret) {
+		dev_err(lcd->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-off-delay", NULL);
+	if (!prop_data)
+		return NULL;
+	data->power_off_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);
+
+	data->reset = s6e8ax0_generic_reset;
+
+	return data;
+}
+
+static struct of_device_id s6e8ax0_of_match[] = {
+	{ .compatible = "samsung,s6e8ax0" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, s6e8ax0_of_match);
+#else
+static struct s6e8ax0_platform_data *s6e8ax0_parse_dt(struct s6e8ax0 *lcd)
+{
+	return NULL;
+}
+#endif
+
+static const struct display_entity_interface_params s6e8ax0_params = {
+	.type = DISPLAY_ENTITY_INTERFACE_DSI,
+	.p.dsi = {
+		.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 = 10000000,
+	},
+};
+
+static int s6e8ax0_set_state(struct display_entity *entity,
+			    enum display_entity_state state)
+{
+	struct s6e8ax0 *panel = to_panel(entity);
+
+	switch (state) {
+	case DISPLAY_ENTITY_STATE_OFF:
+		if (entity->state == DISPLAY_ENTITY_STATE_ON) {
+			s6e8ax0_sleep_in(panel);
+			panel->entity.source->common_ops->set_stream(entity->source,
+						DISPLAY_ENTITY_STREAM_STOPPED);
+
+			msleep(panel->pdata->power_off_delay);
+			s6e8ax0_display_off(panel);
+
+			panel->entity.source->ops.dsi->disable(entity->source);
+
+			regulator_bulk_disable(ARRAY_SIZE(panel->supplies),
+								panel->supplies);
+		} else if (entity->state == DISPLAY_ENTITY_STATE_STANDBY) {
+			msleep(panel->pdata->power_off_delay);
+			s6e8ax0_display_off(panel);
+
+			panel->entity.source->ops.dsi->disable(entity->source);
+
+			regulator_bulk_disable(ARRAY_SIZE(panel->supplies),
+								panel->supplies);
+		}
+		break;
+
+	case DISPLAY_ENTITY_STATE_STANDBY:
+		if (entity->state == DISPLAY_ENTITY_STATE_ON) {
+			s6e8ax0_sleep_in(panel);
+			panel->entity.source->common_ops->set_stream(entity->source,
+						DISPLAY_ENTITY_STREAM_STOPPED);
+		} else if (entity->state == DISPLAY_ENTITY_STATE_OFF) {
+			msleep(panel->pdata->power_on_delay);
+
+			regulator_bulk_enable(ARRAY_SIZE(panel->supplies),
+								panel->supplies);
+
+			msleep(panel->pdata->reset_delay);
+
+			/* lcd reset */
+			if (panel->pdata->reset)
+				panel->pdata->reset(panel->dev);
+			msleep(5);
+
+			panel->entity.source->ops.dsi->enable(entity->source);
+
+			s6e8ax0_set_sequence(panel);
+			s6e8ax0_sleep_in(panel);
+		}
+		break;
+
+	case DISPLAY_ENTITY_STATE_ON:
+		if (entity->state == DISPLAY_ENTITY_STATE_OFF) {
+			msleep(panel->pdata->power_on_delay);
+
+			regulator_bulk_enable(ARRAY_SIZE(panel->supplies),
+								panel->supplies);
+
+			msleep(panel->pdata->reset_delay);
+
+			/* lcd reset */
+			if (panel->pdata->reset)
+				panel->pdata->reset(panel->dev);
+			msleep(5);
+
+			panel->entity.source->ops.dsi->enable(entity->source);
+
+			s6e8ax0_set_sequence(panel);
+
+			panel->entity.source->common_ops->set_stream(entity->source,
+					DISPLAY_ENTITY_STREAM_CONTINUOUS);
+		} else {
+			panel->entity.source->common_ops->set_stream(entity->source,
+					DISPLAY_ENTITY_STREAM_CONTINUOUS);
+			s6e8ax0_sleep_out(panel);
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int s6e8ax0_get_modes(struct display_entity *entity,
+			    const struct videomode **modes)
+{
+	struct s6e8ax0 *panel = to_panel(entity);
+
+	*modes = &panel->pdata->mode;
+	return 1;
+}
+
+static int s6e8ax0_get_size(struct display_entity *entity,
+			   unsigned int *width, unsigned int *height)
+{
+	struct s6e8ax0 *panel = to_panel(entity);
+
+	*width = panel->pdata->width;
+	*height = panel->pdata->height;
+	return 0;
+}
+
+static int s6e8ax0_get_params(struct display_entity *entity,
+				struct display_entity_interface_params *params)
+{
+	*params = s6e8ax0_params;
+	return 0;
+}
+
+static const struct display_entity_control_ops s6e8ax0_control_ops = {
+	.set_state = s6e8ax0_set_state,
+	.get_modes = s6e8ax0_get_modes,
+	.get_size = s6e8ax0_get_size,
+	.get_params = s6e8ax0_get_params,
+};
+
+static void s6e8ax0_release(struct display_entity *entity)
+{
+	struct s6e8ax0 *panel = to_panel(entity);
+
+	backlight_device_unregister(panel->bd);
+	lcd_device_unregister(panel->ld);
+	regulator_bulk_free(ARRAY_SIZE(panel->supplies), panel->supplies);
+	kfree(panel);
+}
+
+static int s6e8ax0_probe(struct platform_device *pdev)
+{
+	struct s6e8ax0 *lcd;
+	int ret;
+
+	lcd = kzalloc(sizeof(struct s6e8ax0), GFP_KERNEL);
+	if (!lcd) {
+		dev_err(&pdev->dev, "failed to allocate s6e8ax0 structure.\n");
+		return -ENOMEM;
+	}
+
+	lcd->dev = &pdev->dev;
+	lcd->pdata = (struct s6e8ax0_platform_data *)pdev->dev.platform_data;
+
+	if (!lcd->pdata) {
+		lcd->pdata = s6e8ax0_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("s6e8ax0", &pdev->dev, lcd,
+			&s6e8ax0_lcd_ops);
+	if (IS_ERR(lcd->ld)) {
+		dev_err(&pdev->dev, "failed to register lcd ops.\n");
+		ret = PTR_ERR(lcd->ld);
+		goto err_lcd_register;
+	}
+
+	lcd->bd = backlight_device_register("s6e8ax0-bl", &pdev->dev, lcd,
+			&s6e8ax0_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;
+	}
+
+	lcd->acl_enable = 1;
+	lcd->cur_acl = 0;
+	lcd->power = FB_BLANK_UNBLANK;
+
+	lcd->bd->props.max_brightness = MAX_BRIGHTNESS;
+	lcd->bd->props.brightness = MAX_BRIGHTNESS;
+
+	lcd->entity.of_node = pdev->dev.of_node;
+	lcd->entity.dev = &pdev->dev;
+	lcd->entity.release = s6e8ax0_release;
+	lcd->entity.ops = &s6e8ax0_control_ops;
+
+	platform_set_drvdata(pdev, lcd);
+
+	ret = display_entity_register(&lcd->entity);
+	if (ret < 0)
+		goto err_display_register;
+
+	display_entity_set_state(&lcd->entity, DISPLAY_ENTITY_STATE_ON);
+
+	dev_dbg(&pdev->dev, "probed s6e8ax0 panel driver.\n");
+
+	return 0;
+
+err_display_register:
+	backlight_device_unregister(lcd->bd);
+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 s6e8ax0_remove(struct platform_device *dev)
+{
+	struct s6e8ax0 *lcd = platform_get_drvdata(dev);
+
+	platform_set_drvdata(dev, NULL);
+	display_entity_unregister(&lcd->entity);
+
+	return 0;
+}
+
+static int s6e8ax0_suspend(struct device *dev)
+{
+	struct s6e8ax0 *lcd = dev_get_drvdata(dev);
+
+	if (lcd->power == FB_BLANK_UNBLANK)
+		display_entity_set_state(&lcd->entity,
+						DISPLAY_ENTITY_STATE_OFF);
+
+	return 0;
+}
+
+static int s6e8ax0_resume(struct device *dev)
+{
+	struct s6e8ax0 *lcd = dev_get_drvdata(dev);
+
+	if (lcd->power == FB_BLANK_UNBLANK)
+		display_entity_set_state(&lcd->entity,
+						DISPLAY_ENTITY_STATE_ON);
+
+	return 0;
+}
+
+static struct dev_pm_ops s6e8ax0_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(s6e8ax0_suspend, s6e8ax0_resume)
+};
+
+static struct platform_driver s6e8ax0_driver = {
+	.probe = s6e8ax0_probe,
+	.remove = s6e8ax0_remove,
+	.driver = {
+		.name = "panel_s6e8ax0",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(s6e8ax0_of_match),
+		.pm = &s6e8ax0_pm_ops,
+	},
+};
+module_platform_driver(s6e8ax0_driver);
+
+MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
+MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
+MODULE_DESCRIPTION("MIPI-DSI based s6e8ax0 AMOLED LCD Panel Driver");
+MODULE_LICENSE("GPL");
diff --git a/include/video/panel-s6e8ax0.h b/include/video/panel-s6e8ax0.h
new file mode 100644
index 0000000..e522bfb
--- /dev/null
+++ b/include/video/panel-s6e8ax0.h
@@ -0,0 +1,41 @@
+/*
+ * 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 <linux/videomode.h>
+
+struct s6e8ax0_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 power_off_delay;
+	/* panel is reversed */
+	bool panel_reverse;
+};
+
+#endif /* __PANEL_S6E8AX0_H__ */
-- 
1.8.1

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
  2013-01-30 15:38 ` Tomasz Figa
@ 2013-02-02 10:39   ` Laurent Pinchart
  -1 siblings, 0 replies; 36+ messages in thread
From: Laurent Pinchart @ 2013-02-02 10:39 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: dri-devel, linux-fbdev, linux-samsung-soc, kyungmin.park,
	m.szyprowski, tomasz.figa, Daniel Vetter, Marcus Lorentzon, rob,
	tomi.valkeinen, Vikas Sajjan, inki.dae, dh09.lee, ville.syrjala,
	s.nawrocki

Hi Tomasz,

Thank you for your RFC.

On Wednesday 30 January 2013 16:38:59 Tomasz Figa wrote:
> Hi,
> 
> After pretty long time of trying to adapt Exynos-specific DSI display
> support to Common Display Framework I'm ready to show some preliminary RFC
> patches. This series shows some ideas for CDF that came to my mind during
> my work, some changes based on comments received by Tomi's edition of CDF
> and also preliminarys version of Exynos DSI (video source part only, still
> with some FIXMEs) and Samsung S6E8AX0 DSI panel drivers.
> 
> It is heavily based on Tomi's work which can be found here:
> http://thread.gmane.org/gmane.comp.video.dri.devel/78227
> 
> The code might be a bit hacky in places, as I wanted to get it to a kind
> of complete and working state first. However I hope that some of the ideas
> might useful for further works.
> 
> So, here comes the TF edition of Common Clock Framework.
> --------------------------------------------------------
> 
> Changes done to Tomi's edition of CDF:
> 
>  - Replaced set_operation_mode, set_pixel_format and set_size video source
>    operations with get_params display entity operation, as it was originally
>    in Laurent's version.
> 
>    We have discussed this matter on the mailing list and decided that it
>    would be better to have the source ask the sink for its parameter
>    structure and do whatever appropriate with it.

Could you briefly outline the rationale behind this ?

I'm wondering whether get_params could limit us if a device needs to modify 
parameters at runtime. For instance a panel might need to change clock 
frequencies or number of used data lane depending on various runtime 
conditions. I don't have a real use case here, so it might just be a bit of 
over-engineering.

>  - Defined a preliminary set of DSI bus parameters.
> 
>    Following parameters are defined:
> 
>    1. Pixel format used for video data transmission.
>    2. Mode flags (several bit flags ORed together):
>      a) DSI_MODE_VIDEO - entity uses video mode (as opposed to command
>         mode), following DSI_MODE_VIDEO_* flags are relevant only if this
>         flag is set.
>         b) DSI_MODE_VIDEO_BURST - entity uses burst transfer for video data
>         c) DSI_MODE_VIDEO_SYNC_PULSE - entity uses sync pulses (as opposed
>            to sync events)
>         d) DSI_MODE_VIDEO_AUTO_VERT - entity uses automatic video mode
>            detection
>         e) DSI_MODE_VIDEO_HSE - entity needs horizontal sync end packets
>         f) DSI_MODE_VIDEO_HFP - entity needs horizontal front porch area
>         g) DSI_MODE_VIDEO_HBP - entity needs horizontal back porch area
>         h) DSI_MODE_VIDEO_HSA - entity needs horizontal sync active area
>      i) DSI_MODE_VSYNC_FLUSH - vertical sync pulse flushes video data
>      j) DSI_MODE_EOT_PACKET - entity needs EoT packets
>    3. Bit (serial) clock frequency in HS mode.
>    4. Escape mode clock frequency.
>    5. Mask of used data lanes (each bit represents single lane).

From my experience with MIPI CSI (Camera Serial Interface, similar to DSI) 
some transceivers can reroute data lanes internally. Is that true for DSI as 
well ? In that case we would need a list of data lanes, not just a mask.

>    6. Command allow area in video mode - amount of lines after transmitting
>       video data when generic commands are accepted.
> 
>    Feel free to suggest anything missing or wrong, as the list of
>    parameters is based merely on what was used in original Exynos MIPI
>    DSIM driver.
> 
>  - Redesigned source-entity matching.
> 
>    Now each source has name string and integer id and each entity has
>    source name and source id. In addition, they can have of_node specified,
>    which is used for Device Tree-based matching.
> 
>    The matching procedure is as follows:
> 
>    1. Matching takes place when display entity registers.
>      a) If there is of_node specified for the entity then "video-source"
>         phandle of this node is being parsed and list of registered sources
>         is traversed in search of a source with of_node received from
>         parsing the phandle.
>      b) Otherwise the matching key is a pair of source name and id.
>    2. If no matching source is found, display_entity_register returns
>       -EPROBE_DEFER error which causes the entity driver to defer its
>       probe until the source registers.
>    3. Otherwise an optional bind operation of video source is called,
>       sink field of source and source field of entity are set and the
>       matching ends successfully.
> 
>  - Some initial concept of source-entity cross-locking.
> 
>    Only video source is protected at the moment, as I still have to think
>    a bit about locking the entity in a way where removing it by user request
>    is still possible.

One of the main locking issues here are cross-references. The display entity 
holds a reference to the video source (for video operations), and the display 
controller driver holds a reference to the display entity (for control 
operations), resulting in a cross-references lock situation. One possible 
solution would be to first unbind the display entity device from its driver to 
break the cycle.

>  - Dropped any panels and chips that I can't test.
> 
>    They are irrelevant for this series, so there is no point in including
>    them.
> 
>  - Added Exynos DSI video source driver.
> 
>    This is a new driver for the DSI controller found in Exynos SoCs. It
>    still needs some work, but in current state can be considered an example
>    of DSI video source implementation for my version of CDF.
> 
>  - Added Samsung S6E8AX0 DSI panel driver.
> 
>    This is the old Exynos-specific driver, just migrated to CDF and with
>    some hacks to provide control over entity state to userspace using
>    lcd device. However it can be used to demonstrate video source ops in
>    use.
> 
> Feel free to comment as much as you can.
> 
> Tomasz Figa (4):
>   video: add display-core
>   video: add makefile & kconfig
>   video: display: Add exynos-dsi video source driver
>   video: display: Add Samsung s6e8ax0 display panel driver
> 
>  drivers/video/Kconfig                     |    1 +
>  drivers/video/Makefile                    |    1 +
>  drivers/video/display/Kconfig             |   16 +
>  drivers/video/display/Makefile            |    3 +
>  drivers/video/display/display-core.c      |  295 +++++++
>  drivers/video/display/panel-s6e8ax0.c     | 1027 ++++++++++++++++++++++
>  drivers/video/display/source-exynos_dsi.c | 1313 ++++++++++++++++++++++++++
>  include/video/display.h                   |  230 +++++
>  include/video/exynos_dsi.h                |   41 +
>  include/video/panel-s6e8ax0.h             |   41 +
>  10 files changed, 2968 insertions(+)
>  create mode 100644 drivers/video/display/Kconfig
>  create mode 100644 drivers/video/display/Makefile
>  create mode 100644 drivers/video/display/display-core.c
>  create mode 100644 drivers/video/display/panel-s6e8ax0.c
>  create mode 100644 drivers/video/display/source-exynos_dsi.c
>  create mode 100644 include/video/display.h
>  create mode 100644 include/video/exynos_dsi.h
>  create mode 100644 include/video/panel-s6e8ax0.h
-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
@ 2013-02-02 10:39   ` Laurent Pinchart
  0 siblings, 0 replies; 36+ messages in thread
From: Laurent Pinchart @ 2013-02-02 10:39 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: dri-devel, linux-fbdev, linux-samsung-soc, kyungmin.park,
	m.szyprowski, tomasz.figa, Daniel Vetter, Marcus Lorentzon, rob,
	tomi.valkeinen, Vikas Sajjan, inki.dae, dh09.lee, ville.syrjala,
	s.nawrocki

Hi Tomasz,

Thank you for your RFC.

On Wednesday 30 January 2013 16:38:59 Tomasz Figa wrote:
> Hi,
> 
> After pretty long time of trying to adapt Exynos-specific DSI display
> support to Common Display Framework I'm ready to show some preliminary RFC
> patches. This series shows some ideas for CDF that came to my mind during
> my work, some changes based on comments received by Tomi's edition of CDF
> and also preliminarys version of Exynos DSI (video source part only, still
> with some FIXMEs) and Samsung S6E8AX0 DSI panel drivers.
> 
> It is heavily based on Tomi's work which can be found here:
> http://thread.gmane.org/gmane.comp.video.dri.devel/78227
> 
> The code might be a bit hacky in places, as I wanted to get it to a kind
> of complete and working state first. However I hope that some of the ideas
> might useful for further works.
> 
> So, here comes the TF edition of Common Clock Framework.
> --------------------------------------------------------
> 
> Changes done to Tomi's edition of CDF:
> 
>  - Replaced set_operation_mode, set_pixel_format and set_size video source
>    operations with get_params display entity operation, as it was originally
>    in Laurent's version.
> 
>    We have discussed this matter on the mailing list and decided that it
>    would be better to have the source ask the sink for its parameter
>    structure and do whatever appropriate with it.

Could you briefly outline the rationale behind this ?

I'm wondering whether get_params could limit us if a device needs to modify 
parameters at runtime. For instance a panel might need to change clock 
frequencies or number of used data lane depending on various runtime 
conditions. I don't have a real use case here, so it might just be a bit of 
over-engineering.

>  - Defined a preliminary set of DSI bus parameters.
> 
>    Following parameters are defined:
> 
>    1. Pixel format used for video data transmission.
>    2. Mode flags (several bit flags ORed together):
>      a) DSI_MODE_VIDEO - entity uses video mode (as opposed to command
>         mode), following DSI_MODE_VIDEO_* flags are relevant only if this
>         flag is set.
>         b) DSI_MODE_VIDEO_BURST - entity uses burst transfer for video data
>         c) DSI_MODE_VIDEO_SYNC_PULSE - entity uses sync pulses (as opposed
>            to sync events)
>         d) DSI_MODE_VIDEO_AUTO_VERT - entity uses automatic video mode
>            detection
>         e) DSI_MODE_VIDEO_HSE - entity needs horizontal sync end packets
>         f) DSI_MODE_VIDEO_HFP - entity needs horizontal front porch area
>         g) DSI_MODE_VIDEO_HBP - entity needs horizontal back porch area
>         h) DSI_MODE_VIDEO_HSA - entity needs horizontal sync active area
>      i) DSI_MODE_VSYNC_FLUSH - vertical sync pulse flushes video data
>      j) DSI_MODE_EOT_PACKET - entity needs EoT packets
>    3. Bit (serial) clock frequency in HS mode.
>    4. Escape mode clock frequency.
>    5. Mask of used data lanes (each bit represents single lane).

>From my experience with MIPI CSI (Camera Serial Interface, similar to DSI) 
some transceivers can reroute data lanes internally. Is that true for DSI as 
well ? In that case we would need a list of data lanes, not just a mask.

>    6. Command allow area in video mode - amount of lines after transmitting
>       video data when generic commands are accepted.
> 
>    Feel free to suggest anything missing or wrong, as the list of
>    parameters is based merely on what was used in original Exynos MIPI
>    DSIM driver.
> 
>  - Redesigned source-entity matching.
> 
>    Now each source has name string and integer id and each entity has
>    source name and source id. In addition, they can have of_node specified,
>    which is used for Device Tree-based matching.
> 
>    The matching procedure is as follows:
> 
>    1. Matching takes place when display entity registers.
>      a) If there is of_node specified for the entity then "video-source"
>         phandle of this node is being parsed and list of registered sources
>         is traversed in search of a source with of_node received from
>         parsing the phandle.
>      b) Otherwise the matching key is a pair of source name and id.
>    2. If no matching source is found, display_entity_register returns
>       -EPROBE_DEFER error which causes the entity driver to defer its
>       probe until the source registers.
>    3. Otherwise an optional bind operation of video source is called,
>       sink field of source and source field of entity are set and the
>       matching ends successfully.
> 
>  - Some initial concept of source-entity cross-locking.
> 
>    Only video source is protected at the moment, as I still have to think
>    a bit about locking the entity in a way where removing it by user request
>    is still possible.

One of the main locking issues here are cross-references. The display entity 
holds a reference to the video source (for video operations), and the display 
controller driver holds a reference to the display entity (for control 
operations), resulting in a cross-references lock situation. One possible 
solution would be to first unbind the display entity device from its driver to 
break the cycle.

>  - Dropped any panels and chips that I can't test.
> 
>    They are irrelevant for this series, so there is no point in including
>    them.
> 
>  - Added Exynos DSI video source driver.
> 
>    This is a new driver for the DSI controller found in Exynos SoCs. It
>    still needs some work, but in current state can be considered an example
>    of DSI video source implementation for my version of CDF.
> 
>  - Added Samsung S6E8AX0 DSI panel driver.
> 
>    This is the old Exynos-specific driver, just migrated to CDF and with
>    some hacks to provide control over entity state to userspace using
>    lcd device. However it can be used to demonstrate video source ops in
>    use.
> 
> Feel free to comment as much as you can.
> 
> Tomasz Figa (4):
>   video: add display-core
>   video: add makefile & kconfig
>   video: display: Add exynos-dsi video source driver
>   video: display: Add Samsung s6e8ax0 display panel driver
> 
>  drivers/video/Kconfig                     |    1 +
>  drivers/video/Makefile                    |    1 +
>  drivers/video/display/Kconfig             |   16 +
>  drivers/video/display/Makefile            |    3 +
>  drivers/video/display/display-core.c      |  295 +++++++
>  drivers/video/display/panel-s6e8ax0.c     | 1027 ++++++++++++++++++++++
>  drivers/video/display/source-exynos_dsi.c | 1313 ++++++++++++++++++++++++++
>  include/video/display.h                   |  230 +++++
>  include/video/exynos_dsi.h                |   41 +
>  include/video/panel-s6e8ax0.h             |   41 +
>  10 files changed, 2968 insertions(+)
>  create mode 100644 drivers/video/display/Kconfig
>  create mode 100644 drivers/video/display/Makefile
>  create mode 100644 drivers/video/display/display-core.c
>  create mode 100644 drivers/video/display/panel-s6e8ax0.c
>  create mode 100644 drivers/video/display/source-exynos_dsi.c
>  create mode 100644 include/video/display.h
>  create mode 100644 include/video/exynos_dsi.h
>  create mode 100644 include/video/panel-s6e8ax0.h
-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
  2013-02-02 10:39   ` Laurent Pinchart
@ 2013-02-03 19:17     ` Tomasz Figa
  -1 siblings, 0 replies; 36+ messages in thread
From: Tomasz Figa @ 2013-02-03 19:17 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Tomasz Figa, dri-devel, linux-fbdev, linux-samsung-soc,
	kyungmin.park, m.szyprowski, Daniel Vetter, Marcus Lorentzon,
	rob, tomi.valkeinen, Vikas Sajjan, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki

Hi Laurent,

On Saturday 02 of February 2013 11:39:59 Laurent Pinchart wrote:
> Hi Tomasz,
> 
> Thank you for your RFC.
> 
> On Wednesday 30 January 2013 16:38:59 Tomasz Figa wrote:
> > Hi,
> > 
> > After pretty long time of trying to adapt Exynos-specific DSI display
> > support to Common Display Framework I'm ready to show some preliminary
> > RFC patches. This series shows some ideas for CDF that came to my
> > mind during my work, some changes based on comments received by
> > Tomi's edition of CDF and also preliminarys version of Exynos DSI
> > (video source part only, still with some FIXMEs) and Samsung S6E8AX0
> > DSI panel drivers.
> > 
> > It is heavily based on Tomi's work which can be found here:
> > http://thread.gmane.org/gmane.comp.video.dri.devel/78227
> > 
> > The code might be a bit hacky in places, as I wanted to get it to a
> > kind of complete and working state first. However I hope that some of
> > the ideas might useful for further works.
> > 
> > So, here comes the TF edition of Common Clock Framework.
> > --------------------------------------------------------
> > 
> > Changes done to Tomi's edition of CDF:
> >  - Replaced set_operation_mode, set_pixel_format and set_size video
> >  source>  
> >    operations with get_params display entity operation, as it was
> >    originally in Laurent's version.
> >    
> >    We have discussed this matter on the mailing list and decided that
> >    it
> >    would be better to have the source ask the sink for its parameter
> >    structure and do whatever appropriate with it.
> 
> Could you briefly outline the rationale behind this ?

Display interfaces may be described by an arbitrary number of parameters. 
Some will require just video timings, others like DSI will require a 
significant number of additional bus-specific ones.

Separating all the parameters (all of which are usually set at the same 
time) into a lot of ops setting single parameter will just add unnecessary 
complexity.

> I'm wondering whether get_params could limit us if a device needs to
> modify parameters at runtime. For instance a panel might need to change
> clock frequencies or number of used data lane depending on various
> runtime conditions. I don't have a real use case here, so it might just
> be a bit of over-engineering.

Hmm, isn't it in the opposite direction (the user requests the display 
interface to change, for example, video mode, which in turn reconfigures 
the link and requests the panel to update its settings)?

However it might be reasonable to split the parameters into constant and 
variable ones. In case of DSI bus, there is a lot of parameters that are 
considered just at link initialization time (the whole dsi params struct I 
defined). Video mode, however, is a variable parameter that can be changed 
on some displays.

> 
> >  - Defined a preliminary set of DSI bus parameters.
> >  
> >    Following parameters are defined:
> >    
> >    1. Pixel format used for video data transmission.
> >    
> >    2. Mode flags (several bit flags ORed together):
> >      a) DSI_MODE_VIDEO - entity uses video mode (as opposed to command
> >      
> >         mode), following DSI_MODE_VIDEO_* flags are relevant only if
> >         this
> >         flag is set.
> >         b) DSI_MODE_VIDEO_BURST - entity uses burst transfer for video
> >         data
> >         c) DSI_MODE_VIDEO_SYNC_PULSE - entity uses sync pulses (as
> >         opposed
> >         
> >            to sync events)
> >         
> >         d) DSI_MODE_VIDEO_AUTO_VERT - entity uses automatic video mode
> >         
> >            detection
> >         
> >         e) DSI_MODE_VIDEO_HSE - entity needs horizontal sync end
> >         packets
> >         f) DSI_MODE_VIDEO_HFP - entity needs horizontal front porch
> >         area
> >         g) DSI_MODE_VIDEO_HBP - entity needs horizontal back porch
> >         area
> >         h) DSI_MODE_VIDEO_HSA - entity needs horizontal sync active
> >         area
> >      
> >      i) DSI_MODE_VSYNC_FLUSH - vertical sync pulse flushes video data
> >      j) DSI_MODE_EOT_PACKET - entity needs EoT packets
> >    
> >    3. Bit (serial) clock frequency in HS mode.
> >    4. Escape mode clock frequency.
> >    5. Mask of used data lanes (each bit represents single lane).
> 
> From my experience with MIPI CSI (Camera Serial Interface, similar to
> DSI) some transceivers can reroute data lanes internally. Is that true
> for DSI as well ? In that case we would need a list of data lanes, not
> just a mask.

Hmm, I don't remember at the moment, will have to look to the 
specification. Exynos DSI master doesn't support such feature.

> >    6. Command allow area in video mode - amount of lines after
> >    transmitting>    
> >       video data when generic commands are accepted.
> >    
> >    Feel free to suggest anything missing or wrong, as the list of
> >    parameters is based merely on what was used in original Exynos MIPI
> >    DSIM driver.
> >  
> >  - Redesigned source-entity matching.
> >  
> >    Now each source has name string and integer id and each entity has
> >    source name and source id. In addition, they can have of_node
> >    specified, which is used for Device Tree-based matching.
> >    
> >    The matching procedure is as follows:
> >    
> >    1. Matching takes place when display entity registers.
> >    
> >      a) If there is of_node specified for the entity then
> >      "video-source"
> >      
> >         phandle of this node is being parsed and list of registered
> >         sources
> >         is traversed in search of a source with of_node received from
> >         parsing the phandle.
> >      
> >      b) Otherwise the matching key is a pair of source name and id.
> >    
> >    2. If no matching source is found, display_entity_register returns
> >    
> >       -EPROBE_DEFER error which causes the entity driver to defer its
> >       probe until the source registers.
> >    
> >    3. Otherwise an optional bind operation of video source is called,
> >    
> >       sink field of source and source field of entity are set and the
> >       matching ends successfully.
> >  
> >  - Some initial concept of source-entity cross-locking.
> >  
> >    Only video source is protected at the moment, as I still have to
> >    think
> >    a bit about locking the entity in a way where removing it by user
> >    request is still possible.
> 
> One of the main locking issues here are cross-references. The display
> entity holds a reference to the video source (for video operations),
> and the display controller driver holds a reference to the display
> entity (for control operations), resulting in a cross-references lock
> situation. One possible solution would be to first unbind the display
> entity device from its driver to break the cycle.

Yes, this is a problem that requires a bit more thought.

I've been discussing this issue with Sylwester and we came to a conclusion 
that for sure a display entity can normally get a reference to its video 
source (e.g. module_get, to prevent unloading the module), as there should 
be no possibility of video source going away, leaving display entity 
present and so there is no need for unregistering a bound video source. 
This is what I implemented in my code.

Now it shall be possible to unregister a bound display entity, for example 
to have the ability of unplugging a hotpluggable display (HDMI, Display 
Port), but this will need some non-trivial synchronization. I'm still 
thinking how to do it properly.

Best regards,
Tomasz
 
> >  - Dropped any panels and chips that I can't test.
> >  
> >    They are irrelevant for this series, so there is no point in
> >    including
> >    them.
> >  
> >  - Added Exynos DSI video source driver.
> >  
> >    This is a new driver for the DSI controller found in Exynos SoCs.
> >    It
> >    still needs some work, but in current state can be considered an
> >    example of DSI video source implementation for my version of CDF.
> >  
> >  - Added Samsung S6E8AX0 DSI panel driver.
> >  
> >    This is the old Exynos-specific driver, just migrated to CDF and
> >    with
> >    some hacks to provide control over entity state to userspace using
> >    lcd device. However it can be used to demonstrate video source ops
> >    in
> >    use.
> > 
> > Feel free to comment as much as you can.
> > 
> > Tomasz Figa (4):
> >   video: add display-core
> >   video: add makefile & kconfig
> >   video: display: Add exynos-dsi video source driver
> >   video: display: Add Samsung s6e8ax0 display panel driver
> >  
> >  drivers/video/Kconfig                     |    1 +
> >  drivers/video/Makefile                    |    1 +
> >  drivers/video/display/Kconfig             |   16 +
> >  drivers/video/display/Makefile            |    3 +
> >  drivers/video/display/display-core.c      |  295 +++++++
> >  drivers/video/display/panel-s6e8ax0.c     | 1027
> >  ++++++++++++++++++++++
> >  drivers/video/display/source-exynos_dsi.c | 1313
> >  ++++++++++++++++++++++++++ include/video/display.h                  
> >  |  230 +++++
> >  include/video/exynos_dsi.h                |   41 +
> >  include/video/panel-s6e8ax0.h             |   41 +
> >  10 files changed, 2968 insertions(+)
> >  create mode 100644 drivers/video/display/Kconfig
> >  create mode 100644 drivers/video/display/Makefile
> >  create mode 100644 drivers/video/display/display-core.c
> >  create mode 100644 drivers/video/display/panel-s6e8ax0.c
> >  create mode 100644 drivers/video/display/source-exynos_dsi.c
> >  create mode 100644 include/video/display.h
> >  create mode 100644 include/video/exynos_dsi.h
> >  create mode 100644 include/video/panel-s6e8ax0.h

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
@ 2013-02-03 19:17     ` Tomasz Figa
  0 siblings, 0 replies; 36+ messages in thread
From: Tomasz Figa @ 2013-02-03 19:17 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Tomasz Figa, dri-devel, linux-fbdev, linux-samsung-soc,
	kyungmin.park, m.szyprowski, Daniel Vetter, Marcus Lorentzon,
	rob, tomi.valkeinen, Vikas Sajjan, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki

Hi Laurent,

On Saturday 02 of February 2013 11:39:59 Laurent Pinchart wrote:
> Hi Tomasz,
> 
> Thank you for your RFC.
> 
> On Wednesday 30 January 2013 16:38:59 Tomasz Figa wrote:
> > Hi,
> > 
> > After pretty long time of trying to adapt Exynos-specific DSI display
> > support to Common Display Framework I'm ready to show some preliminary
> > RFC patches. This series shows some ideas for CDF that came to my
> > mind during my work, some changes based on comments received by
> > Tomi's edition of CDF and also preliminarys version of Exynos DSI
> > (video source part only, still with some FIXMEs) and Samsung S6E8AX0
> > DSI panel drivers.
> > 
> > It is heavily based on Tomi's work which can be found here:
> > http://thread.gmane.org/gmane.comp.video.dri.devel/78227
> > 
> > The code might be a bit hacky in places, as I wanted to get it to a
> > kind of complete and working state first. However I hope that some of
> > the ideas might useful for further works.
> > 
> > So, here comes the TF edition of Common Clock Framework.
> > --------------------------------------------------------
> > 
> > Changes done to Tomi's edition of CDF:
> >  - Replaced set_operation_mode, set_pixel_format and set_size video
> >  source>  
> >    operations with get_params display entity operation, as it was
> >    originally in Laurent's version.
> >    
> >    We have discussed this matter on the mailing list and decided that
> >    it
> >    would be better to have the source ask the sink for its parameter
> >    structure and do whatever appropriate with it.
> 
> Could you briefly outline the rationale behind this ?

Display interfaces may be described by an arbitrary number of parameters. 
Some will require just video timings, others like DSI will require a 
significant number of additional bus-specific ones.

Separating all the parameters (all of which are usually set at the same 
time) into a lot of ops setting single parameter will just add unnecessary 
complexity.

> I'm wondering whether get_params could limit us if a device needs to
> modify parameters at runtime. For instance a panel might need to change
> clock frequencies or number of used data lane depending on various
> runtime conditions. I don't have a real use case here, so it might just
> be a bit of over-engineering.

Hmm, isn't it in the opposite direction (the user requests the display 
interface to change, for example, video mode, which in turn reconfigures 
the link and requests the panel to update its settings)?

However it might be reasonable to split the parameters into constant and 
variable ones. In case of DSI bus, there is a lot of parameters that are 
considered just at link initialization time (the whole dsi params struct I 
defined). Video mode, however, is a variable parameter that can be changed 
on some displays.

> 
> >  - Defined a preliminary set of DSI bus parameters.
> >  
> >    Following parameters are defined:
> >    
> >    1. Pixel format used for video data transmission.
> >    
> >    2. Mode flags (several bit flags ORed together):
> >      a) DSI_MODE_VIDEO - entity uses video mode (as opposed to command
> >      
> >         mode), following DSI_MODE_VIDEO_* flags are relevant only if
> >         this
> >         flag is set.
> >         b) DSI_MODE_VIDEO_BURST - entity uses burst transfer for video
> >         data
> >         c) DSI_MODE_VIDEO_SYNC_PULSE - entity uses sync pulses (as
> >         opposed
> >         
> >            to sync events)
> >         
> >         d) DSI_MODE_VIDEO_AUTO_VERT - entity uses automatic video mode
> >         
> >            detection
> >         
> >         e) DSI_MODE_VIDEO_HSE - entity needs horizontal sync end
> >         packets
> >         f) DSI_MODE_VIDEO_HFP - entity needs horizontal front porch
> >         area
> >         g) DSI_MODE_VIDEO_HBP - entity needs horizontal back porch
> >         area
> >         h) DSI_MODE_VIDEO_HSA - entity needs horizontal sync active
> >         area
> >      
> >      i) DSI_MODE_VSYNC_FLUSH - vertical sync pulse flushes video data
> >      j) DSI_MODE_EOT_PACKET - entity needs EoT packets
> >    
> >    3. Bit (serial) clock frequency in HS mode.
> >    4. Escape mode clock frequency.
> >    5. Mask of used data lanes (each bit represents single lane).
> 
> From my experience with MIPI CSI (Camera Serial Interface, similar to
> DSI) some transceivers can reroute data lanes internally. Is that true
> for DSI as well ? In that case we would need a list of data lanes, not
> just a mask.

Hmm, I don't remember at the moment, will have to look to the 
specification. Exynos DSI master doesn't support such feature.

> >    6. Command allow area in video mode - amount of lines after
> >    transmitting>    
> >       video data when generic commands are accepted.
> >    
> >    Feel free to suggest anything missing or wrong, as the list of
> >    parameters is based merely on what was used in original Exynos MIPI
> >    DSIM driver.
> >  
> >  - Redesigned source-entity matching.
> >  
> >    Now each source has name string and integer id and each entity has
> >    source name and source id. In addition, they can have of_node
> >    specified, which is used for Device Tree-based matching.
> >    
> >    The matching procedure is as follows:
> >    
> >    1. Matching takes place when display entity registers.
> >    
> >      a) If there is of_node specified for the entity then
> >      "video-source"
> >      
> >         phandle of this node is being parsed and list of registered
> >         sources
> >         is traversed in search of a source with of_node received from
> >         parsing the phandle.
> >      
> >      b) Otherwise the matching key is a pair of source name and id.
> >    
> >    2. If no matching source is found, display_entity_register returns
> >    
> >       -EPROBE_DEFER error which causes the entity driver to defer its
> >       probe until the source registers.
> >    
> >    3. Otherwise an optional bind operation of video source is called,
> >    
> >       sink field of source and source field of entity are set and the
> >       matching ends successfully.
> >  
> >  - Some initial concept of source-entity cross-locking.
> >  
> >    Only video source is protected at the moment, as I still have to
> >    think
> >    a bit about locking the entity in a way where removing it by user
> >    request is still possible.
> 
> One of the main locking issues here are cross-references. The display
> entity holds a reference to the video source (for video operations),
> and the display controller driver holds a reference to the display
> entity (for control operations), resulting in a cross-references lock
> situation. One possible solution would be to first unbind the display
> entity device from its driver to break the cycle.

Yes, this is a problem that requires a bit more thought.

I've been discussing this issue with Sylwester and we came to a conclusion 
that for sure a display entity can normally get a reference to its video 
source (e.g. module_get, to prevent unloading the module), as there should 
be no possibility of video source going away, leaving display entity 
present and so there is no need for unregistering a bound video source. 
This is what I implemented in my code.

Now it shall be possible to unregister a bound display entity, for example 
to have the ability of unplugging a hotpluggable display (HDMI, Display 
Port), but this will need some non-trivial synchronization. I'm still 
thinking how to do it properly.

Best regards,
Tomasz
 
> >  - Dropped any panels and chips that I can't test.
> >  
> >    They are irrelevant for this series, so there is no point in
> >    including
> >    them.
> >  
> >  - Added Exynos DSI video source driver.
> >  
> >    This is a new driver for the DSI controller found in Exynos SoCs.
> >    It
> >    still needs some work, but in current state can be considered an
> >    example of DSI video source implementation for my version of CDF.
> >  
> >  - Added Samsung S6E8AX0 DSI panel driver.
> >  
> >    This is the old Exynos-specific driver, just migrated to CDF and
> >    with
> >    some hacks to provide control over entity state to userspace using
> >    lcd device. However it can be used to demonstrate video source ops
> >    in
> >    use.
> > 
> > Feel free to comment as much as you can.
> > 
> > Tomasz Figa (4):
> >   video: add display-core
> >   video: add makefile & kconfig
> >   video: display: Add exynos-dsi video source driver
> >   video: display: Add Samsung s6e8ax0 display panel driver
> >  
> >  drivers/video/Kconfig                     |    1 +
> >  drivers/video/Makefile                    |    1 +
> >  drivers/video/display/Kconfig             |   16 +
> >  drivers/video/display/Makefile            |    3 +
> >  drivers/video/display/display-core.c      |  295 +++++++
> >  drivers/video/display/panel-s6e8ax0.c     | 1027
> >  ++++++++++++++++++++++
> >  drivers/video/display/source-exynos_dsi.c | 1313
> >  ++++++++++++++++++++++++++ include/video/display.h                  
> >  |  230 +++++
> >  include/video/exynos_dsi.h                |   41 +
> >  include/video/panel-s6e8ax0.h             |   41 +
> >  10 files changed, 2968 insertions(+)
> >  create mode 100644 drivers/video/display/Kconfig
> >  create mode 100644 drivers/video/display/Makefile
> >  create mode 100644 drivers/video/display/display-core.c
> >  create mode 100644 drivers/video/display/panel-s6e8ax0.c
> >  create mode 100644 drivers/video/display/source-exynos_dsi.c
> >  create mode 100644 include/video/display.h
> >  create mode 100644 include/video/exynos_dsi.h
> >  create mode 100644 include/video/panel-s6e8ax0.h

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/4] video: display: Add Samsung s6e8ax0 display panel driver
  2013-01-30 15:39   ` Tomasz Figa
@ 2013-02-07  9:46     ` Vikas Sajjan
  -1 siblings, 0 replies; 36+ messages in thread
From: Vikas Sajjan @ 2013-02-07  9:34 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: dri-devel, linux-fbdev, linux-samsung-soc, kyungmin.park,
	m.szyprowski, tomasz.figa, Daniel Vetter, Marcus Lorentzon,
	Laurent Pinchart, rob, tomi.valkeinen, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki, aditya.ps, sunil joshi

Hi Figa,

On Wed, Jan 30, 2013 at 9:09 PM, Tomasz Figa <t.figa@samsung.com> wrote:
> This patch adds Common Display Framework driver for Samsung s6e8ax0
> MIPI DSI display panel.
>
> Signed-off-by: Tomasz Figa <t.figa@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/video/display/Kconfig         |    3 +
>  drivers/video/display/Makefile        |    1 +
>  drivers/video/display/panel-s6e8ax0.c | 1027 +++++++++++++++++++++++++++++++++
>  include/video/panel-s6e8ax0.h         |   41 ++
>  4 files changed, 1072 insertions(+)
>  create mode 100644 drivers/video/display/panel-s6e8ax0.c
>  create mode 100644 include/video/panel-s6e8ax0.h
>
> diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
> index b14527a..f19ec04 100644
> --- a/drivers/video/display/Kconfig
> +++ b/drivers/video/display/Kconfig
> @@ -5,6 +5,9 @@ menuconfig DISPLAY_CORE
>
>  if DISPLAY_CORE
>
> +config DISPLAY_PANEL_S6E8AX0
> +       tristate "S6E8AX0 DSI video mode panel"
> +       select OF_VIDEOMODE
>
>  config DISPLAY_SOURCE_EXYNOS_DSI
>         tristate "Samsung SoC MIPI DSI Master"
> diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
> index 40a283a..0f7fdc2 100644
> --- a/drivers/video/display/Makefile
> +++ b/drivers/video/display/Makefile
> @@ -1,2 +1,3 @@
>  obj-$(CONFIG_DISPLAY_CORE) += display-core.o
> +obj-$(CONFIG_DISPLAY_PANEL_S6E8AX0) += panel-s6e8ax0.o
>  obj-$(CONFIG_DISPLAY_SOURCE_EXYNOS_DSI) += source-exynos_dsi.o
> diff --git a/drivers/video/display/panel-s6e8ax0.c b/drivers/video/display/panel-s6e8ax0.c
> new file mode 100644
> index 0000000..4c09fe2
> --- /dev/null
> +++ b/drivers/video/display/panel-s6e8ax0.c
> @@ -0,0 +1,1027 @@
> +/* linux/drivers/video/exynos/s6e8ax0.c
> + *
> + * MIPI-DSI based s6e8ax0 AMOLED lcd 4.65 inch panel driver.
> + *
> + * Inki Dae, <inki.dae@samsung.com>
> + * Donghwa Lee, <dh09.lee@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/errno.h>
> +#include <linux/mutex.h>
> +#include <linux/wait.h>
> +#include <linux/ctype.h>
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +
> +#include <linux/fb.h>
> +#include <linux/gpio.h>
> +#include <linux/lcd.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_videomode.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/backlight.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <video/display.h>
> +#include <video/mipi_display.h>
> +#include <video/panel-s6e8ax0.h>
> +
> +#define LDI_MTP_LENGTH         24
> +#define DSIM_PM_STABLE_TIME    10
> +#define MIN_BRIGHTNESS         0
> +#define MAX_BRIGHTNESS         24
> +#define GAMMA_TABLE_COUNT      26
> +
> +struct s6e8ax0 {
> +       struct display_entity entity;
> +       struct device *dev;
> +
> +       struct s6e8ax0_platform_data *pdata;
> +       struct backlight_device *bd;
> +       struct lcd_device *ld;
> +       struct regulator_bulk_data supplies[2];
> +
> +       bool enabled;
> +       unsigned int id;
> +       unsigned int gamma;
> +       unsigned int acl_enable;
> +       unsigned int cur_acl;
> +       int power;
> +
> +       unsigned int reset_gpio;
> +};
> +
> +#define to_panel(p)    container_of(p, struct s6e8ax0, entity)
> +
> +static const unsigned char s6e8ax0_22_gamma_30[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xf5, 0x00, 0xff, 0xad, 0xaf,
> +       0xbA, 0xc3, 0xd8, 0xc5, 0x9f, 0xc6, 0x9e, 0xc1, 0xdc, 0xc0,
> +       0x00, 0x61, 0x00, 0x5a, 0x00, 0x74,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_50[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xe8, 0x1f, 0xf7, 0xad, 0xc0,
> +       0xb5, 0xc4, 0xdc, 0xc4, 0x9e, 0xc6, 0x9c, 0xbb, 0xd8, 0xbb,
> +       0x00, 0x70, 0x00, 0x68, 0x00, 0x86,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_60[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xde, 0x1f, 0xef, 0xad, 0xc4,
> +       0xb3, 0xc3, 0xdd, 0xc4, 0x9e, 0xc6, 0x9c, 0xbc, 0xd6, 0xba,
> +       0x00, 0x75, 0x00, 0x6e, 0x00, 0x8d,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_70[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xd8, 0x1f, 0xe7, 0xaf, 0xc8,
> +       0xb4, 0xc4, 0xdd, 0xc3, 0x9d, 0xc6, 0x9c, 0xbb, 0xd6, 0xb9,
> +       0x00, 0x7a, 0x00, 0x72, 0x00, 0x93,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_80[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xc9, 0x1f, 0xde, 0xae, 0xc9,
> +       0xb1, 0xc3, 0xdd, 0xc2, 0x9d, 0xc5, 0x9b, 0xbc, 0xd6, 0xbb,
> +       0x00, 0x7f, 0x00, 0x77, 0x00, 0x99,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_90[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xc7, 0x1f, 0xd9, 0xb0, 0xcc,
> +       0xb2, 0xc3, 0xdc, 0xc1, 0x9c, 0xc6, 0x9c, 0xbc, 0xd4, 0xb9,
> +       0x00, 0x83, 0x00, 0x7b, 0x00, 0x9e,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_100[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xbd, 0x80, 0xcd, 0xba, 0xce,
> +       0xb3, 0xc4, 0xde, 0xc3, 0x9c, 0xc4, 0x9, 0xb8, 0xd3, 0xb6,
> +       0x00, 0x88, 0x00, 0x80, 0x00, 0xa5,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_120[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb9, 0x95, 0xc8, 0xb1, 0xcf,
> +       0xb2, 0xc6, 0xdf, 0xc5, 0x9b, 0xc3, 0x99, 0xb6, 0xd2, 0xb6,
> +       0x00, 0x8f, 0x00, 0x86, 0x00, 0xac,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_130[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc7, 0xb1, 0xd0,
> +       0xb2, 0xc4, 0xdd, 0xc3, 0x9a, 0xc3, 0x98, 0xb6, 0xd0, 0xb4,
> +       0x00, 0x92, 0x00, 0x8a, 0x00, 0xb1,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_140[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc5, 0xb2, 0xd0,
> +       0xb3, 0xc3, 0xde, 0xc3, 0x9b, 0xc2, 0x98, 0xb6, 0xd0, 0xb4,
> +       0x00, 0x95, 0x00, 0x8d, 0x00, 0xb5,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_150[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xa0, 0xc2, 0xb2, 0xd0,
> +       0xb2, 0xc1, 0xdd, 0xc2, 0x9b, 0xc2, 0x98, 0xb4, 0xcf, 0xb1,
> +       0x00, 0x99, 0x00, 0x90, 0x00, 0xba,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_160[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xa5, 0xbf, 0xb0, 0xd0,
> +       0xb1, 0xc3, 0xde, 0xc2, 0x99, 0xc1, 0x97, 0xb4, 0xce, 0xb1,
> +       0x00, 0x9c, 0x00, 0x93, 0x00, 0xbe,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_170[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb5, 0xbf, 0xb1, 0xd1,
> +       0xb1, 0xc3, 0xde, 0xc3, 0x99, 0xc0, 0x96, 0xb4, 0xce, 0xb1,
> +       0x00, 0x9f, 0x00, 0x96, 0x00, 0xc2,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_180[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb7, 0xbe, 0xb3, 0xd2,
> +       0xb3, 0xc3, 0xde, 0xc2, 0x97, 0xbf, 0x95, 0xb4, 0xcd, 0xb1,
> +       0x00, 0xa2, 0x00, 0x99, 0x00, 0xc5,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_190[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbe, 0xb2, 0xd2,
> +       0xb2, 0xc3, 0xdd, 0xc3, 0x98, 0xbf, 0x95, 0xb2, 0xcc, 0xaf,
> +       0x00, 0xa5, 0x00, 0x9c, 0x00, 0xc9,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_200[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbc, 0xb2, 0xd2,
> +       0xb1, 0xc4, 0xdd, 0xc3, 0x97, 0xbe, 0x95, 0xb1, 0xcb, 0xae,
> +       0x00, 0xa8, 0x00, 0x9f, 0x00, 0xcd,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_210[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc1, 0xbd, 0xb1, 0xd1,
> +       0xb1, 0xc2, 0xde, 0xc2, 0x97, 0xbe, 0x94, 0xB0, 0xc9, 0xad,
> +       0x00, 0xae, 0x00, 0xa4, 0x00, 0xd4,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_220[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc7, 0xbd, 0xb1, 0xd1,
> +       0xb1, 0xc2, 0xdd, 0xc2, 0x97, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
> +       0x00, 0xad, 0x00, 0xa2, 0x00, 0xd3,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_230[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc3, 0xbd, 0xb2, 0xd1,
> +       0xb1, 0xc3, 0xdd, 0xc1, 0x96, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
> +       0x00, 0xb0, 0x00, 0xa7, 0x00, 0xd7,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_240[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xcb, 0xbd, 0xb1, 0xd2,
> +       0xb1, 0xc3, 0xdD, 0xc2, 0x95, 0xbd, 0x93, 0xaf, 0xc8, 0xab,
> +       0x00, 0xb3, 0x00, 0xa9, 0x00, 0xdb,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_250[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xcc, 0xbe, 0xb0, 0xd2,
> +       0xb0, 0xc3, 0xdD, 0xc2, 0x94, 0xbc, 0x92, 0xae, 0xc8, 0xab,
> +       0x00, 0xb6, 0x00, 0xab, 0x00, 0xde,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_260[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xd0, 0xbe, 0xaf, 0xd1,
> +       0xaf, 0xc2, 0xdd, 0xc1, 0x96, 0xbc, 0x93, 0xaf, 0xc8, 0xac,
> +       0x00, 0xb7, 0x00, 0xad, 0x00, 0xe0,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_270[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xcF, 0xbd, 0xb0, 0xd2,
> +       0xaf, 0xc2, 0xdc, 0xc1, 0x95, 0xbd, 0x93, 0xae, 0xc6, 0xaa,
> +       0x00, 0xba, 0x00, 0xb0, 0x00, 0xe4,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_280[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xd0, 0xbd, 0xaf, 0xd0,
> +       0xad, 0xc4, 0xdd, 0xc3, 0x95, 0xbd, 0x93, 0xac, 0xc5, 0xa9,
> +       0x00, 0xbd, 0x00, 0xb2, 0x00, 0xe7,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_300[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb5, 0xd3, 0xbd, 0xb1, 0xd2,
> +       0xb0, 0xc0, 0xdc, 0xc0, 0x94, 0xba, 0x91, 0xac, 0xc5, 0xa9,
> +       0x00, 0xc2, 0x00, 0xb7, 0x00, 0xed,
> +};
> +
> +static const unsigned char *s6e8ax0_22_gamma_table[] = {
> +       s6e8ax0_22_gamma_30,
> +       s6e8ax0_22_gamma_50,
> +       s6e8ax0_22_gamma_60,
> +       s6e8ax0_22_gamma_70,
> +       s6e8ax0_22_gamma_80,
> +       s6e8ax0_22_gamma_90,
> +       s6e8ax0_22_gamma_100,
> +       s6e8ax0_22_gamma_120,
> +       s6e8ax0_22_gamma_130,
> +       s6e8ax0_22_gamma_140,
> +       s6e8ax0_22_gamma_150,
> +       s6e8ax0_22_gamma_160,
> +       s6e8ax0_22_gamma_170,
> +       s6e8ax0_22_gamma_180,
> +       s6e8ax0_22_gamma_190,
> +       s6e8ax0_22_gamma_200,
> +       s6e8ax0_22_gamma_210,
> +       s6e8ax0_22_gamma_220,
> +       s6e8ax0_22_gamma_230,
> +       s6e8ax0_22_gamma_240,
> +       s6e8ax0_22_gamma_250,
> +       s6e8ax0_22_gamma_260,
> +       s6e8ax0_22_gamma_270,
> +       s6e8ax0_22_gamma_280,
> +       s6e8ax0_22_gamma_300,
> +};
> +
> +static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
> +               0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
> +               0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
> +               0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8
> +       };
> +       static const unsigned char data_to_send_panel_reverse[] = {
> +               0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
> +               0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
> +               0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
> +               0xc1, 0x01, 0x41, 0xc1, 0x00, 0xc1, 0xf6, 0xf6, 0xc1
> +       };
> +
> +       if (lcd->pdata->panel_reverse)
> +               dsi_dcs_write(lcd->entity.source, 0,
> +                               data_to_send_panel_reverse,
> +                               ARRAY_SIZE(data_to_send_panel_reverse));
> +       else
> +               dsi_dcs_write(lcd->entity.source, 0,
> +                               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_display_cond(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xf2, 0x80, 0x03, 0x0d
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +/* Gamma 2.2 Setting (200cd, 7500K, 10MPCD) */
> +static void s6e8ax0_gamma_cond(struct s6e8ax0 *lcd)
> +{
> +       unsigned int gamma = lcd->bd->props.brightness;
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +                       s6e8ax0_22_gamma_table[gamma],
> +                       GAMMA_TABLE_COUNT);
> +}
> +
> +static void s6e8ax0_gamma_update(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xf7, 0x03
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0, data_to_send,
> +               ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond1(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xd1, 0xfe, 0x80, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x40,
> +               0x0d, 0x00, 0x00
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond2(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0,
> +               0x00
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond3(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond4(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond5(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +static void s6e8ax0_etc_cond6(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xe3, 0x40
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond7(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_elvss_set(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xb1, 0x04, 0x00
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_elvss_nvm_set(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xd9, 0x5c, 0x20, 0x0c, 0x0f, 0x41, 0x00, 0x10, 0x11,
> +               0x12, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcb, 0xed,
> +               0x64, 0xaf
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_sleep_in(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0x10
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_sleep_out(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0x11
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_display_on(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0x29
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_display_off(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0x28
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_apply_level2_key(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xf0, 0x5a, 0x5a
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_acl_on(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xc0, 0x01
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_acl_off(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xc0, 0x00
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +/* Full white 50% reducing setting */
> +static void s6e8ax0_acl_ctrl_set(struct s6e8ax0 *lcd)
> +{
> +       /* Full white 50% reducing setting */
> +       static const unsigned char cutoff_50[] = {
> +               0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
> +               0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
> +               0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38,
> +               0x3f, 0x46
> +       };
> +       /* Full white 45% reducing setting */
> +       static const unsigned char cutoff_45[] = {
> +               0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
> +               0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
> +               0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31,
> +               0x37, 0x3d
> +       };
> +       /* Full white 40% reducing setting */
> +       static const unsigned char cutoff_40[] = {
> +               0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
> +               0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
> +               0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b,
> +               0x31, 0x36
> +       };
> +
> +       if (lcd->acl_enable) {
> +               if (lcd->cur_acl == 0) {
> +                       if (lcd->gamma == 0 || lcd->gamma == 1) {
> +                               s6e8ax0_acl_off(lcd);
> +                               dev_dbg(lcd->dev,
> +                                       "cur_acl=%d\n", lcd->cur_acl);
> +                       } else
> +                               s6e8ax0_acl_on(lcd);
> +               }
> +               switch (lcd->gamma) {
> +               case 0: /* 30cd */
> +                       s6e8ax0_acl_off(lcd);
> +                       lcd->cur_acl = 0;
> +                       break;
> +               case 1 ... 3: /* 50cd ~ 90cd */
> +                       dsi_dcs_write(lcd->entity.source, 0,
> +                               cutoff_40,
> +                               ARRAY_SIZE(cutoff_40));
> +                       lcd->cur_acl = 40;
> +                       break;
> +               case 4 ... 7: /* 120cd ~ 210cd */
> +                       dsi_dcs_write(lcd->entity.source, 0,
> +                               cutoff_45,
> +                               ARRAY_SIZE(cutoff_45));
> +                       lcd->cur_acl = 45;
> +                       break;
> +               case 8 ... 10: /* 220cd ~ 300cd */
> +                       dsi_dcs_write(lcd->entity.source, 0,
> +                               cutoff_50,
> +                               ARRAY_SIZE(cutoff_50));
> +                       lcd->cur_acl = 50;
> +                       break;
> +               default:
> +                       break;
> +               }
> +       } else {
> +               s6e8ax0_acl_off(lcd);
> +               lcd->cur_acl = 0;
> +               dev_dbg(lcd->dev, "cur_acl = %d\n", lcd->cur_acl);
> +       }
> +}
> +
> +static void s6e8ax0_read_id(struct s6e8ax0 *lcd, u8 *mtp_id)
> +{
> +       unsigned int ret;
> +       u8 addr = 0xd1; /* MTP ID */
> +
> +       ret = dsi_dcs_read(lcd->entity.source, 0, addr, mtp_id, 3);
> +}
> +
> +static int s6e8ax0_panel_init(struct s6e8ax0 *lcd)
> +{
> +       s6e8ax0_apply_level2_key(lcd);
> +       s6e8ax0_sleep_out(lcd);
> +       msleep(1);
> +       s6e8ax0_panel_cond(lcd);
> +       s6e8ax0_display_cond(lcd);
> +       s6e8ax0_gamma_cond(lcd);
> +       s6e8ax0_gamma_update(lcd);
> +
> +       s6e8ax0_etc_cond1(lcd);
> +       s6e8ax0_etc_cond2(lcd);
> +       s6e8ax0_etc_cond3(lcd);
> +       s6e8ax0_etc_cond4(lcd);
> +       s6e8ax0_etc_cond5(lcd);
> +       s6e8ax0_etc_cond6(lcd);
> +       s6e8ax0_etc_cond7(lcd);
> +
> +       s6e8ax0_elvss_nvm_set(lcd);
> +       s6e8ax0_elvss_set(lcd);
> +
> +       s6e8ax0_acl_ctrl_set(lcd);
> +       s6e8ax0_acl_on(lcd);
> +
> +       /* if ID3 value is not 33h, branch private elvss mode */
> +       msleep(lcd->pdata->power_on_delay);
> +
> +       return 0;
> +}
> +
> +static int s6e8ax0_update_gamma_ctrl(struct s6e8ax0 *lcd, int brightness)
> +{
> +       dsi_dcs_write(lcd->entity.source, 0,
> +                       s6e8ax0_22_gamma_table[brightness],
> +                       ARRAY_SIZE(s6e8ax0_22_gamma_table));
> +
> +       /* update gamma table. */
> +       s6e8ax0_gamma_update(lcd);
> +       lcd->gamma = brightness;
> +
> +       return 0;
> +}
> +
> +static int s6e8ax0_gamma_ctrl(struct s6e8ax0 *lcd, int gamma)
> +{
> +       s6e8ax0_update_gamma_ctrl(lcd, gamma);
> +
> +       return 0;
> +}
> +
> +static int s6e8ax0_get_brightness(struct backlight_device *bd)
> +{
> +       return bd->props.brightness;
> +}
> +
> +static int s6e8ax0_set_brightness(struct backlight_device *bd)
> +{
> +       int ret = 0, brightness = bd->props.brightness;
> +       struct s6e8ax0 *lcd = bl_get_data(bd);
> +
> +       if (brightness < MIN_BRIGHTNESS ||
> +               brightness > bd->props.max_brightness) {
> +               dev_err(lcd->dev,
> +                                       "lcd brightness should be %d to %d.\n",
> +                                       MIN_BRIGHTNESS, MAX_BRIGHTNESS);
> +               return -EINVAL;
> +       }
> +
> +       ret = s6e8ax0_gamma_ctrl(lcd, brightness);
> +       if (ret) {
> +               dev_err(lcd->dev,
> +                                       "lcd brightness setting failed.\n");
> +               return -EIO;
> +       }
> +
> +       return ret;
> +}
> +
> +static const struct backlight_ops s6e8ax0_backlight_ops = {
> +       .get_brightness = s6e8ax0_get_brightness,
> +       .update_status = s6e8ax0_set_brightness,
> +};
> +
> +static int s6e8ax0_set_power(struct lcd_device *ld, int power)
> +{
> +       struct s6e8ax0 *lcd = lcd_get_data(ld);
> +       enum display_entity_state state;
> +       int ret;
> +
> +       if (power == FB_BLANK_POWERDOWN)
> +               state = DISPLAY_ENTITY_STATE_OFF;
> +       else if (power != FB_BLANK_UNBLANK)
> +               state = DISPLAY_ENTITY_STATE_STANDBY;
> +       else
> +               state = DISPLAY_ENTITY_STATE_ON;
> +
> +       ret = display_entity_set_state(&lcd->entity, state);
> +       if (ret)
> +               return ret;
> +
> +       lcd->power = power;
> +       return 0;
> +}
> +
> +static int s6e8ax0_get_power(struct lcd_device *ld)
> +{
> +       struct s6e8ax0 *lcd = lcd_get_data(ld);
> +
> +       return lcd->power;
> +}
> +
> +static struct lcd_ops s6e8ax0_lcd_ops = {
> +       .set_power = s6e8ax0_set_power,
> +       .get_power = s6e8ax0_get_power,
> +};
> +
> +static void s6e8ax0_set_sequence(struct s6e8ax0 *lcd)
> +{
> +       u8 mtp_id[3] = {0, };
> +
> +       msleep(100);
> +
> +       s6e8ax0_read_id(lcd, mtp_id);
> +       if (mtp_id[0] == 0x00)
> +               dev_err(lcd->dev, "read id failed\n");
> +
> +       dev_info(lcd->dev, "Read ID : %x, %x, %x\n",
> +                       mtp_id[0], mtp_id[1], mtp_id[2]);
> +
> +       if (mtp_id[2] == 0x33)
> +               dev_info(lcd->dev,
> +                       "ID-3 is 0xff does not support dynamic elvss\n");
> +       else
> +               dev_info(lcd->dev,
> +                       "ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]);
> +
> +       s6e8ax0_panel_init(lcd);
> +       s6e8ax0_display_on(lcd);
> +}
> +
> +#ifdef CONFIG_OF
> +static int s6e8ax0_generic_reset(struct device *dev)
> +{
> +       struct s6e8ax0 *lcd = dev_get_drvdata(dev);
> +
> +       mdelay(10);
> +       gpio_set_value(lcd->reset_gpio, 0);
> +       mdelay(10);
> +       gpio_set_value(lcd->reset_gpio, 1);
> +
> +       return 0;
> +}
> +
> +static struct s6e8ax0_platform_data *s6e8ax0_parse_dt(struct s6e8ax0 *lcd)
> +{
> +       struct device_node *node = lcd->dev->of_node;
> +       struct s6e8ax0_platform_data *data;
> +       const __be32 *prop_data;
> +       int ret;
> +
> +       data = devm_kzalloc(lcd->dev, sizeof(*data), GFP_KERNEL);
> +       if (!data) {
> +               dev_err(lcd->dev, "failed to allocate platform data.\n");
> +               return NULL;
> +       }
> +
> +       ret = of_get_videomode(node, &data->mode, 0);
> +       if (ret) {
> +               dev_err(lcd->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-off-delay", NULL);
> +       if (!prop_data)
> +               return NULL;
> +       data->power_off_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);
> +
> +       data->reset = s6e8ax0_generic_reset;
> +
> +       return data;
> +}
> +
> +static struct of_device_id s6e8ax0_of_match[] = {
> +       { .compatible = "samsung,s6e8ax0" },
> +       { }
> +};
> +
> +MODULE_DEVICE_TABLE(of, s6e8ax0_of_match);
> +#else
> +static struct s6e8ax0_platform_data *s6e8ax0_parse_dt(struct s6e8ax0 *lcd)
> +{
> +       return NULL;
> +}
> +#endif
> +
> +static const struct display_entity_interface_params s6e8ax0_params = {
> +       .type = DISPLAY_ENTITY_INTERFACE_DSI,
> +       .p.dsi = {
> +               .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 = 10000000,
> +       },
> +};
> +
> +static int s6e8ax0_set_state(struct display_entity *entity,
> +                           enum display_entity_state state)
> +{
> +       struct s6e8ax0 *panel = to_panel(entity);
> +
> +       switch (state) {
> +       case DISPLAY_ENTITY_STATE_OFF:
> +               if (entity->state == DISPLAY_ENTITY_STATE_ON) {
> +                       s6e8ax0_sleep_in(panel);
> +                       panel->entity.source->common_ops->set_stream(entity->source,
> +                                               DISPLAY_ENTITY_STREAM_STOPPED);
> +
> +                       msleep(panel->pdata->power_off_delay);
> +                       s6e8ax0_display_off(panel);
> +
> +                       panel->entity.source->ops.dsi->disable(entity->source);
> +
> +                       regulator_bulk_disable(ARRAY_SIZE(panel->supplies),
> +                                                               panel->supplies);
> +               } else if (entity->state == DISPLAY_ENTITY_STATE_STANDBY) {
> +                       msleep(panel->pdata->power_off_delay);
> +                       s6e8ax0_display_off(panel);
> +
> +                       panel->entity.source->ops.dsi->disable(entity->source);
> +
> +                       regulator_bulk_disable(ARRAY_SIZE(panel->supplies),
> +                                                               panel->supplies);
> +               }
> +               break;
> +
> +       case DISPLAY_ENTITY_STATE_STANDBY:
> +               if (entity->state == DISPLAY_ENTITY_STATE_ON) {
> +                       s6e8ax0_sleep_in(panel);
> +                       panel->entity.source->common_ops->set_stream(entity->source,
> +                                               DISPLAY_ENTITY_STREAM_STOPPED);
> +               } else if (entity->state == DISPLAY_ENTITY_STATE_OFF) {
> +                       msleep(panel->pdata->power_on_delay);
> +
> +                       regulator_bulk_enable(ARRAY_SIZE(panel->supplies),
> +                                                               panel->supplies);
> +
> +                       msleep(panel->pdata->reset_delay);
> +
> +                       /* lcd reset */
> +                       if (panel->pdata->reset)
> +                               panel->pdata->reset(panel->dev);
> +                       msleep(5);
> +
> +                       panel->entity.source->ops.dsi->enable(entity->source);
> +
> +                       s6e8ax0_set_sequence(panel);
> +                       s6e8ax0_sleep_in(panel);
> +               }
> +               break;
> +
> +       case DISPLAY_ENTITY_STATE_ON:
> +               if (entity->state == DISPLAY_ENTITY_STATE_OFF) {
> +                       msleep(panel->pdata->power_on_delay);
> +
> +                       regulator_bulk_enable(ARRAY_SIZE(panel->supplies),
> +                                                               panel->supplies);
> +
> +                       msleep(panel->pdata->reset_delay);
> +
> +                       /* lcd reset */
> +                       if (panel->pdata->reset)
> +                               panel->pdata->reset(panel->dev);
> +                       msleep(5);
> +
> +                       panel->entity.source->ops.dsi->enable(entity->source);
> +
> +                       s6e8ax0_set_sequence(panel);
> +
> +                       panel->entity.source->common_ops->set_stream(entity->source,
> +                                       DISPLAY_ENTITY_STREAM_CONTINUOUS);
> +               } else {
> +                       panel->entity.source->common_ops->set_stream(entity->source,
> +                                       DISPLAY_ENTITY_STREAM_CONTINUOUS);
> +                       s6e8ax0_sleep_out(panel);
> +               }
> +               break;
> +
> +       default:
> +               break;
> +       }
> +
> +       return 0;
> +}
> +
> +static int s6e8ax0_get_modes(struct display_entity *entity,
> +                           const struct videomode **modes)
> +{
> +       struct s6e8ax0 *panel = to_panel(entity);
> +
> +       *modes = &panel->pdata->mode;
> +       return 1;
> +}
> +
> +static int s6e8ax0_get_size(struct display_entity *entity,
> +                          unsigned int *width, unsigned int *height)
> +{
> +       struct s6e8ax0 *panel = to_panel(entity);
> +
> +       *width = panel->pdata->width;
> +       *height = panel->pdata->height;
> +       return 0;
> +}
> +
> +static int s6e8ax0_get_params(struct display_entity *entity,
> +                               struct display_entity_interface_params *params)
> +{
> +       *params = s6e8ax0_params;
> +       return 0;
> +}
> +
> +static const struct display_entity_control_ops s6e8ax0_control_ops = {
> +       .set_state = s6e8ax0_set_state,
> +       .get_modes = s6e8ax0_get_modes,
> +       .get_size = s6e8ax0_get_size,
> +       .get_params = s6e8ax0_get_params,
> +};
> +
> +static void s6e8ax0_release(struct display_entity *entity)
> +{
> +       struct s6e8ax0 *panel = to_panel(entity);
> +
> +       backlight_device_unregister(panel->bd);
> +       lcd_device_unregister(panel->ld);
> +       regulator_bulk_free(ARRAY_SIZE(panel->supplies), panel->supplies);
> +       kfree(panel);
> +}
> +
> +static int s6e8ax0_probe(struct platform_device *pdev)
> +{
> +       struct s6e8ax0 *lcd;
> +       int ret;
> +
> +       lcd = kzalloc(sizeof(struct s6e8ax0), GFP_KERNEL);
> +       if (!lcd) {
> +               dev_err(&pdev->dev, "failed to allocate s6e8ax0 structure.\n");
> +               return -ENOMEM;
> +       }
> +
> +       lcd->dev = &pdev->dev;
> +       lcd->pdata = (struct s6e8ax0_platform_data *)pdev->dev.platform_data;
> +
> +       if (!lcd->pdata) {
> +               lcd->pdata = s6e8ax0_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("s6e8ax0", &pdev->dev, lcd,
> +                       &s6e8ax0_lcd_ops);
> +       if (IS_ERR(lcd->ld)) {
> +               dev_err(&pdev->dev, "failed to register lcd ops.\n");
> +               ret = PTR_ERR(lcd->ld);
> +               goto err_lcd_register;
> +       }
> +
> +       lcd->bd = backlight_device_register("s6e8ax0-bl", &pdev->dev, lcd,
> +                       &s6e8ax0_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;
> +       }
> +

I think we should try to remove the dependency with LCD framework and
Backlight framework, and incorporate those functionality as par of
CDF.
you can refer to my similar patch "Make s6e8ax0 panel driver compliant
with CDF" ( http://comments.gmane.org/gmane.linux.drivers.video-input-infrastructure/59187
) which i had posted couple of weeks back, where I made an attempt to
remove "lcd_ops" dependency.

> +       lcd->acl_enable = 1;
> +       lcd->cur_acl = 0;
> +       lcd->power = FB_BLANK_UNBLANK;
> +
> +       lcd->bd->props.max_brightness = MAX_BRIGHTNESS;
> +       lcd->bd->props.brightness = MAX_BRIGHTNESS;
> +
> +       lcd->entity.of_node = pdev->dev.of_node;
> +       lcd->entity.dev = &pdev->dev;
> +       lcd->entity.release = s6e8ax0_release;
> +       lcd->entity.ops = &s6e8ax0_control_ops;
> +
> +       platform_set_drvdata(pdev, lcd);
> +
> +       ret = display_entity_register(&lcd->entity);
> +       if (ret < 0)
> +               goto err_display_register;
> +
> +       display_entity_set_state(&lcd->entity, DISPLAY_ENTITY_STATE_ON);
> +
> +       dev_dbg(&pdev->dev, "probed s6e8ax0 panel driver.\n");
> +
> +       return 0;
> +
> +err_display_register:
> +       backlight_device_unregister(lcd->bd);
> +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 s6e8ax0_remove(struct platform_device *dev)
> +{
> +       struct s6e8ax0 *lcd = platform_get_drvdata(dev);
> +
> +       platform_set_drvdata(dev, NULL);
> +       display_entity_unregister(&lcd->entity);
> +
> +       return 0;
> +}
> +
> +static int s6e8ax0_suspend(struct device *dev)
> +{
> +       struct s6e8ax0 *lcd = dev_get_drvdata(dev);
> +
> +       if (lcd->power == FB_BLANK_UNBLANK)
> +               display_entity_set_state(&lcd->entity,
> +                                               DISPLAY_ENTITY_STATE_OFF);
> +
> +       return 0;
> +}
> +
> +static int s6e8ax0_resume(struct device *dev)
> +{
> +       struct s6e8ax0 *lcd = dev_get_drvdata(dev);
> +
> +       if (lcd->power == FB_BLANK_UNBLANK)
> +               display_entity_set_state(&lcd->entity,
> +                                               DISPLAY_ENTITY_STATE_ON);
> +
> +       return 0;
> +}
> +
> +static struct dev_pm_ops s6e8ax0_pm_ops = {
> +       SET_SYSTEM_SLEEP_PM_OPS(s6e8ax0_suspend, s6e8ax0_resume)
> +};
> +
> +static struct platform_driver s6e8ax0_driver = {
> +       .probe = s6e8ax0_probe,
> +       .remove = s6e8ax0_remove,
> +       .driver = {
> +               .name = "panel_s6e8ax0",
> +               .owner = THIS_MODULE,
> +               .of_match_table = of_match_ptr(s6e8ax0_of_match),
> +               .pm = &s6e8ax0_pm_ops,
> +       },
> +};
> +module_platform_driver(s6e8ax0_driver);
> +
> +MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
> +MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
> +MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
> +MODULE_DESCRIPTION("MIPI-DSI based s6e8ax0 AMOLED LCD Panel Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/video/panel-s6e8ax0.h b/include/video/panel-s6e8ax0.h
> new file mode 100644
> index 0000000..e522bfb
> --- /dev/null
> +++ b/include/video/panel-s6e8ax0.h
> @@ -0,0 +1,41 @@
> +/*
> + * 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 <linux/videomode.h>
> +
> +struct s6e8ax0_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 power_off_delay;
> +       /* panel is reversed */
> +       bool panel_reverse;
> +};
> +
> +#endif /* __PANEL_S6E8AX0_H__ */
> --
> 1.8.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/4] video: display: Add Samsung s6e8ax0 display panel driver
@ 2013-02-07  9:46     ` Vikas Sajjan
  0 siblings, 0 replies; 36+ messages in thread
From: Vikas Sajjan @ 2013-02-07  9:46 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: dri-devel, linux-fbdev, linux-samsung-soc, kyungmin.park,
	m.szyprowski, tomasz.figa, Daniel Vetter, Marcus Lorentzon,
	Laurent Pinchart, rob, tomi.valkeinen, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki, aditya.ps, sunil joshi

Hi Figa,

On Wed, Jan 30, 2013 at 9:09 PM, Tomasz Figa <t.figa@samsung.com> wrote:
> This patch adds Common Display Framework driver for Samsung s6e8ax0
> MIPI DSI display panel.
>
> Signed-off-by: Tomasz Figa <t.figa@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/video/display/Kconfig         |    3 +
>  drivers/video/display/Makefile        |    1 +
>  drivers/video/display/panel-s6e8ax0.c | 1027 +++++++++++++++++++++++++++++++++
>  include/video/panel-s6e8ax0.h         |   41 ++
>  4 files changed, 1072 insertions(+)
>  create mode 100644 drivers/video/display/panel-s6e8ax0.c
>  create mode 100644 include/video/panel-s6e8ax0.h
>
> diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
> index b14527a..f19ec04 100644
> --- a/drivers/video/display/Kconfig
> +++ b/drivers/video/display/Kconfig
> @@ -5,6 +5,9 @@ menuconfig DISPLAY_CORE
>
>  if DISPLAY_CORE
>
> +config DISPLAY_PANEL_S6E8AX0
> +       tristate "S6E8AX0 DSI video mode panel"
> +       select OF_VIDEOMODE
>
>  config DISPLAY_SOURCE_EXYNOS_DSI
>         tristate "Samsung SoC MIPI DSI Master"
> diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
> index 40a283a..0f7fdc2 100644
> --- a/drivers/video/display/Makefile
> +++ b/drivers/video/display/Makefile
> @@ -1,2 +1,3 @@
>  obj-$(CONFIG_DISPLAY_CORE) += display-core.o
> +obj-$(CONFIG_DISPLAY_PANEL_S6E8AX0) += panel-s6e8ax0.o
>  obj-$(CONFIG_DISPLAY_SOURCE_EXYNOS_DSI) += source-exynos_dsi.o
> diff --git a/drivers/video/display/panel-s6e8ax0.c b/drivers/video/display/panel-s6e8ax0.c
> new file mode 100644
> index 0000000..4c09fe2
> --- /dev/null
> +++ b/drivers/video/display/panel-s6e8ax0.c
> @@ -0,0 +1,1027 @@
> +/* linux/drivers/video/exynos/s6e8ax0.c
> + *
> + * MIPI-DSI based s6e8ax0 AMOLED lcd 4.65 inch panel driver.
> + *
> + * Inki Dae, <inki.dae@samsung.com>
> + * Donghwa Lee, <dh09.lee@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/errno.h>
> +#include <linux/mutex.h>
> +#include <linux/wait.h>
> +#include <linux/ctype.h>
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +
> +#include <linux/fb.h>
> +#include <linux/gpio.h>
> +#include <linux/lcd.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_videomode.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/backlight.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <video/display.h>
> +#include <video/mipi_display.h>
> +#include <video/panel-s6e8ax0.h>
> +
> +#define LDI_MTP_LENGTH         24
> +#define DSIM_PM_STABLE_TIME    10
> +#define MIN_BRIGHTNESS         0
> +#define MAX_BRIGHTNESS         24
> +#define GAMMA_TABLE_COUNT      26
> +
> +struct s6e8ax0 {
> +       struct display_entity entity;
> +       struct device *dev;
> +
> +       struct s6e8ax0_platform_data *pdata;
> +       struct backlight_device *bd;
> +       struct lcd_device *ld;
> +       struct regulator_bulk_data supplies[2];
> +
> +       bool enabled;
> +       unsigned int id;
> +       unsigned int gamma;
> +       unsigned int acl_enable;
> +       unsigned int cur_acl;
> +       int power;
> +
> +       unsigned int reset_gpio;
> +};
> +
> +#define to_panel(p)    container_of(p, struct s6e8ax0, entity)
> +
> +static const unsigned char s6e8ax0_22_gamma_30[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xf5, 0x00, 0xff, 0xad, 0xaf,
> +       0xbA, 0xc3, 0xd8, 0xc5, 0x9f, 0xc6, 0x9e, 0xc1, 0xdc, 0xc0,
> +       0x00, 0x61, 0x00, 0x5a, 0x00, 0x74,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_50[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xe8, 0x1f, 0xf7, 0xad, 0xc0,
> +       0xb5, 0xc4, 0xdc, 0xc4, 0x9e, 0xc6, 0x9c, 0xbb, 0xd8, 0xbb,
> +       0x00, 0x70, 0x00, 0x68, 0x00, 0x86,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_60[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xde, 0x1f, 0xef, 0xad, 0xc4,
> +       0xb3, 0xc3, 0xdd, 0xc4, 0x9e, 0xc6, 0x9c, 0xbc, 0xd6, 0xba,
> +       0x00, 0x75, 0x00, 0x6e, 0x00, 0x8d,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_70[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xd8, 0x1f, 0xe7, 0xaf, 0xc8,
> +       0xb4, 0xc4, 0xdd, 0xc3, 0x9d, 0xc6, 0x9c, 0xbb, 0xd6, 0xb9,
> +       0x00, 0x7a, 0x00, 0x72, 0x00, 0x93,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_80[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xc9, 0x1f, 0xde, 0xae, 0xc9,
> +       0xb1, 0xc3, 0xdd, 0xc2, 0x9d, 0xc5, 0x9b, 0xbc, 0xd6, 0xbb,
> +       0x00, 0x7f, 0x00, 0x77, 0x00, 0x99,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_90[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xc7, 0x1f, 0xd9, 0xb0, 0xcc,
> +       0xb2, 0xc3, 0xdc, 0xc1, 0x9c, 0xc6, 0x9c, 0xbc, 0xd4, 0xb9,
> +       0x00, 0x83, 0x00, 0x7b, 0x00, 0x9e,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_100[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xbd, 0x80, 0xcd, 0xba, 0xce,
> +       0xb3, 0xc4, 0xde, 0xc3, 0x9c, 0xc4, 0x9, 0xb8, 0xd3, 0xb6,
> +       0x00, 0x88, 0x00, 0x80, 0x00, 0xa5,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_120[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb9, 0x95, 0xc8, 0xb1, 0xcf,
> +       0xb2, 0xc6, 0xdf, 0xc5, 0x9b, 0xc3, 0x99, 0xb6, 0xd2, 0xb6,
> +       0x00, 0x8f, 0x00, 0x86, 0x00, 0xac,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_130[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc7, 0xb1, 0xd0,
> +       0xb2, 0xc4, 0xdd, 0xc3, 0x9a, 0xc3, 0x98, 0xb6, 0xd0, 0xb4,
> +       0x00, 0x92, 0x00, 0x8a, 0x00, 0xb1,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_140[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc5, 0xb2, 0xd0,
> +       0xb3, 0xc3, 0xde, 0xc3, 0x9b, 0xc2, 0x98, 0xb6, 0xd0, 0xb4,
> +       0x00, 0x95, 0x00, 0x8d, 0x00, 0xb5,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_150[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xa0, 0xc2, 0xb2, 0xd0,
> +       0xb2, 0xc1, 0xdd, 0xc2, 0x9b, 0xc2, 0x98, 0xb4, 0xcf, 0xb1,
> +       0x00, 0x99, 0x00, 0x90, 0x00, 0xba,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_160[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xa5, 0xbf, 0xb0, 0xd0,
> +       0xb1, 0xc3, 0xde, 0xc2, 0x99, 0xc1, 0x97, 0xb4, 0xce, 0xb1,
> +       0x00, 0x9c, 0x00, 0x93, 0x00, 0xbe,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_170[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb5, 0xbf, 0xb1, 0xd1,
> +       0xb1, 0xc3, 0xde, 0xc3, 0x99, 0xc0, 0x96, 0xb4, 0xce, 0xb1,
> +       0x00, 0x9f, 0x00, 0x96, 0x00, 0xc2,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_180[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb7, 0xbe, 0xb3, 0xd2,
> +       0xb3, 0xc3, 0xde, 0xc2, 0x97, 0xbf, 0x95, 0xb4, 0xcd, 0xb1,
> +       0x00, 0xa2, 0x00, 0x99, 0x00, 0xc5,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_190[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbe, 0xb2, 0xd2,
> +       0xb2, 0xc3, 0xdd, 0xc3, 0x98, 0xbf, 0x95, 0xb2, 0xcc, 0xaf,
> +       0x00, 0xa5, 0x00, 0x9c, 0x00, 0xc9,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_200[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbc, 0xb2, 0xd2,
> +       0xb1, 0xc4, 0xdd, 0xc3, 0x97, 0xbe, 0x95, 0xb1, 0xcb, 0xae,
> +       0x00, 0xa8, 0x00, 0x9f, 0x00, 0xcd,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_210[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc1, 0xbd, 0xb1, 0xd1,
> +       0xb1, 0xc2, 0xde, 0xc2, 0x97, 0xbe, 0x94, 0xB0, 0xc9, 0xad,
> +       0x00, 0xae, 0x00, 0xa4, 0x00, 0xd4,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_220[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc7, 0xbd, 0xb1, 0xd1,
> +       0xb1, 0xc2, 0xdd, 0xc2, 0x97, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
> +       0x00, 0xad, 0x00, 0xa2, 0x00, 0xd3,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_230[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc3, 0xbd, 0xb2, 0xd1,
> +       0xb1, 0xc3, 0xdd, 0xc1, 0x96, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
> +       0x00, 0xb0, 0x00, 0xa7, 0x00, 0xd7,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_240[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xcb, 0xbd, 0xb1, 0xd2,
> +       0xb1, 0xc3, 0xdD, 0xc2, 0x95, 0xbd, 0x93, 0xaf, 0xc8, 0xab,
> +       0x00, 0xb3, 0x00, 0xa9, 0x00, 0xdb,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_250[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xcc, 0xbe, 0xb0, 0xd2,
> +       0xb0, 0xc3, 0xdD, 0xc2, 0x94, 0xbc, 0x92, 0xae, 0xc8, 0xab,
> +       0x00, 0xb6, 0x00, 0xab, 0x00, 0xde,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_260[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xd0, 0xbe, 0xaf, 0xd1,
> +       0xaf, 0xc2, 0xdd, 0xc1, 0x96, 0xbc, 0x93, 0xaf, 0xc8, 0xac,
> +       0x00, 0xb7, 0x00, 0xad, 0x00, 0xe0,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_270[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xcF, 0xbd, 0xb0, 0xd2,
> +       0xaf, 0xc2, 0xdc, 0xc1, 0x95, 0xbd, 0x93, 0xae, 0xc6, 0xaa,
> +       0x00, 0xba, 0x00, 0xb0, 0x00, 0xe4,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_280[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xd0, 0xbd, 0xaf, 0xd0,
> +       0xad, 0xc4, 0xdd, 0xc3, 0x95, 0xbd, 0x93, 0xac, 0xc5, 0xa9,
> +       0x00, 0xbd, 0x00, 0xb2, 0x00, 0xe7,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_300[] = {
> +       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb5, 0xd3, 0xbd, 0xb1, 0xd2,
> +       0xb0, 0xc0, 0xdc, 0xc0, 0x94, 0xba, 0x91, 0xac, 0xc5, 0xa9,
> +       0x00, 0xc2, 0x00, 0xb7, 0x00, 0xed,
> +};
> +
> +static const unsigned char *s6e8ax0_22_gamma_table[] = {
> +       s6e8ax0_22_gamma_30,
> +       s6e8ax0_22_gamma_50,
> +       s6e8ax0_22_gamma_60,
> +       s6e8ax0_22_gamma_70,
> +       s6e8ax0_22_gamma_80,
> +       s6e8ax0_22_gamma_90,
> +       s6e8ax0_22_gamma_100,
> +       s6e8ax0_22_gamma_120,
> +       s6e8ax0_22_gamma_130,
> +       s6e8ax0_22_gamma_140,
> +       s6e8ax0_22_gamma_150,
> +       s6e8ax0_22_gamma_160,
> +       s6e8ax0_22_gamma_170,
> +       s6e8ax0_22_gamma_180,
> +       s6e8ax0_22_gamma_190,
> +       s6e8ax0_22_gamma_200,
> +       s6e8ax0_22_gamma_210,
> +       s6e8ax0_22_gamma_220,
> +       s6e8ax0_22_gamma_230,
> +       s6e8ax0_22_gamma_240,
> +       s6e8ax0_22_gamma_250,
> +       s6e8ax0_22_gamma_260,
> +       s6e8ax0_22_gamma_270,
> +       s6e8ax0_22_gamma_280,
> +       s6e8ax0_22_gamma_300,
> +};
> +
> +static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
> +               0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
> +               0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
> +               0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8
> +       };
> +       static const unsigned char data_to_send_panel_reverse[] = {
> +               0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
> +               0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
> +               0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
> +               0xc1, 0x01, 0x41, 0xc1, 0x00, 0xc1, 0xf6, 0xf6, 0xc1
> +       };
> +
> +       if (lcd->pdata->panel_reverse)
> +               dsi_dcs_write(lcd->entity.source, 0,
> +                               data_to_send_panel_reverse,
> +                               ARRAY_SIZE(data_to_send_panel_reverse));
> +       else
> +               dsi_dcs_write(lcd->entity.source, 0,
> +                               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_display_cond(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xf2, 0x80, 0x03, 0x0d
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +/* Gamma 2.2 Setting (200cd, 7500K, 10MPCD) */
> +static void s6e8ax0_gamma_cond(struct s6e8ax0 *lcd)
> +{
> +       unsigned int gamma = lcd->bd->props.brightness;
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +                       s6e8ax0_22_gamma_table[gamma],
> +                       GAMMA_TABLE_COUNT);
> +}
> +
> +static void s6e8ax0_gamma_update(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xf7, 0x03
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0, data_to_send,
> +               ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond1(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xd1, 0xfe, 0x80, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x40,
> +               0x0d, 0x00, 0x00
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond2(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0,
> +               0x00
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond3(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond4(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond5(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +static void s6e8ax0_etc_cond6(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xe3, 0x40
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond7(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_elvss_set(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xb1, 0x04, 0x00
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_elvss_nvm_set(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xd9, 0x5c, 0x20, 0x0c, 0x0f, 0x41, 0x00, 0x10, 0x11,
> +               0x12, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcb, 0xed,
> +               0x64, 0xaf
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_sleep_in(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0x10
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_sleep_out(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0x11
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_display_on(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0x29
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_display_off(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0x28
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_apply_level2_key(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xf0, 0x5a, 0x5a
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_acl_on(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xc0, 0x01
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_acl_off(struct s6e8ax0 *lcd)
> +{
> +       static const unsigned char data_to_send[] = {
> +               0xc0, 0x00
> +       };
> +
> +       dsi_dcs_write(lcd->entity.source, 0,
> +               data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +/* Full white 50% reducing setting */
> +static void s6e8ax0_acl_ctrl_set(struct s6e8ax0 *lcd)
> +{
> +       /* Full white 50% reducing setting */
> +       static const unsigned char cutoff_50[] = {
> +               0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
> +               0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
> +               0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38,
> +               0x3f, 0x46
> +       };
> +       /* Full white 45% reducing setting */
> +       static const unsigned char cutoff_45[] = {
> +               0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
> +               0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
> +               0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31,
> +               0x37, 0x3d
> +       };
> +       /* Full white 40% reducing setting */
> +       static const unsigned char cutoff_40[] = {
> +               0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
> +               0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
> +               0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b,
> +               0x31, 0x36
> +       };
> +
> +       if (lcd->acl_enable) {
> +               if (lcd->cur_acl = 0) {
> +                       if (lcd->gamma = 0 || lcd->gamma = 1) {
> +                               s6e8ax0_acl_off(lcd);
> +                               dev_dbg(lcd->dev,
> +                                       "cur_acl=%d\n", lcd->cur_acl);
> +                       } else
> +                               s6e8ax0_acl_on(lcd);
> +               }
> +               switch (lcd->gamma) {
> +               case 0: /* 30cd */
> +                       s6e8ax0_acl_off(lcd);
> +                       lcd->cur_acl = 0;
> +                       break;
> +               case 1 ... 3: /* 50cd ~ 90cd */
> +                       dsi_dcs_write(lcd->entity.source, 0,
> +                               cutoff_40,
> +                               ARRAY_SIZE(cutoff_40));
> +                       lcd->cur_acl = 40;
> +                       break;
> +               case 4 ... 7: /* 120cd ~ 210cd */
> +                       dsi_dcs_write(lcd->entity.source, 0,
> +                               cutoff_45,
> +                               ARRAY_SIZE(cutoff_45));
> +                       lcd->cur_acl = 45;
> +                       break;
> +               case 8 ... 10: /* 220cd ~ 300cd */
> +                       dsi_dcs_write(lcd->entity.source, 0,
> +                               cutoff_50,
> +                               ARRAY_SIZE(cutoff_50));
> +                       lcd->cur_acl = 50;
> +                       break;
> +               default:
> +                       break;
> +               }
> +       } else {
> +               s6e8ax0_acl_off(lcd);
> +               lcd->cur_acl = 0;
> +               dev_dbg(lcd->dev, "cur_acl = %d\n", lcd->cur_acl);
> +       }
> +}
> +
> +static void s6e8ax0_read_id(struct s6e8ax0 *lcd, u8 *mtp_id)
> +{
> +       unsigned int ret;
> +       u8 addr = 0xd1; /* MTP ID */
> +
> +       ret = dsi_dcs_read(lcd->entity.source, 0, addr, mtp_id, 3);
> +}
> +
> +static int s6e8ax0_panel_init(struct s6e8ax0 *lcd)
> +{
> +       s6e8ax0_apply_level2_key(lcd);
> +       s6e8ax0_sleep_out(lcd);
> +       msleep(1);
> +       s6e8ax0_panel_cond(lcd);
> +       s6e8ax0_display_cond(lcd);
> +       s6e8ax0_gamma_cond(lcd);
> +       s6e8ax0_gamma_update(lcd);
> +
> +       s6e8ax0_etc_cond1(lcd);
> +       s6e8ax0_etc_cond2(lcd);
> +       s6e8ax0_etc_cond3(lcd);
> +       s6e8ax0_etc_cond4(lcd);
> +       s6e8ax0_etc_cond5(lcd);
> +       s6e8ax0_etc_cond6(lcd);
> +       s6e8ax0_etc_cond7(lcd);
> +
> +       s6e8ax0_elvss_nvm_set(lcd);
> +       s6e8ax0_elvss_set(lcd);
> +
> +       s6e8ax0_acl_ctrl_set(lcd);
> +       s6e8ax0_acl_on(lcd);
> +
> +       /* if ID3 value is not 33h, branch private elvss mode */
> +       msleep(lcd->pdata->power_on_delay);
> +
> +       return 0;
> +}
> +
> +static int s6e8ax0_update_gamma_ctrl(struct s6e8ax0 *lcd, int brightness)
> +{
> +       dsi_dcs_write(lcd->entity.source, 0,
> +                       s6e8ax0_22_gamma_table[brightness],
> +                       ARRAY_SIZE(s6e8ax0_22_gamma_table));
> +
> +       /* update gamma table. */
> +       s6e8ax0_gamma_update(lcd);
> +       lcd->gamma = brightness;
> +
> +       return 0;
> +}
> +
> +static int s6e8ax0_gamma_ctrl(struct s6e8ax0 *lcd, int gamma)
> +{
> +       s6e8ax0_update_gamma_ctrl(lcd, gamma);
> +
> +       return 0;
> +}
> +
> +static int s6e8ax0_get_brightness(struct backlight_device *bd)
> +{
> +       return bd->props.brightness;
> +}
> +
> +static int s6e8ax0_set_brightness(struct backlight_device *bd)
> +{
> +       int ret = 0, brightness = bd->props.brightness;
> +       struct s6e8ax0 *lcd = bl_get_data(bd);
> +
> +       if (brightness < MIN_BRIGHTNESS ||
> +               brightness > bd->props.max_brightness) {
> +               dev_err(lcd->dev,
> +                                       "lcd brightness should be %d to %d.\n",
> +                                       MIN_BRIGHTNESS, MAX_BRIGHTNESS);
> +               return -EINVAL;
> +       }
> +
> +       ret = s6e8ax0_gamma_ctrl(lcd, brightness);
> +       if (ret) {
> +               dev_err(lcd->dev,
> +                                       "lcd brightness setting failed.\n");
> +               return -EIO;
> +       }
> +
> +       return ret;
> +}
> +
> +static const struct backlight_ops s6e8ax0_backlight_ops = {
> +       .get_brightness = s6e8ax0_get_brightness,
> +       .update_status = s6e8ax0_set_brightness,
> +};
> +
> +static int s6e8ax0_set_power(struct lcd_device *ld, int power)
> +{
> +       struct s6e8ax0 *lcd = lcd_get_data(ld);
> +       enum display_entity_state state;
> +       int ret;
> +
> +       if (power = FB_BLANK_POWERDOWN)
> +               state = DISPLAY_ENTITY_STATE_OFF;
> +       else if (power != FB_BLANK_UNBLANK)
> +               state = DISPLAY_ENTITY_STATE_STANDBY;
> +       else
> +               state = DISPLAY_ENTITY_STATE_ON;
> +
> +       ret = display_entity_set_state(&lcd->entity, state);
> +       if (ret)
> +               return ret;
> +
> +       lcd->power = power;
> +       return 0;
> +}
> +
> +static int s6e8ax0_get_power(struct lcd_device *ld)
> +{
> +       struct s6e8ax0 *lcd = lcd_get_data(ld);
> +
> +       return lcd->power;
> +}
> +
> +static struct lcd_ops s6e8ax0_lcd_ops = {
> +       .set_power = s6e8ax0_set_power,
> +       .get_power = s6e8ax0_get_power,
> +};
> +
> +static void s6e8ax0_set_sequence(struct s6e8ax0 *lcd)
> +{
> +       u8 mtp_id[3] = {0, };
> +
> +       msleep(100);
> +
> +       s6e8ax0_read_id(lcd, mtp_id);
> +       if (mtp_id[0] = 0x00)
> +               dev_err(lcd->dev, "read id failed\n");
> +
> +       dev_info(lcd->dev, "Read ID : %x, %x, %x\n",
> +                       mtp_id[0], mtp_id[1], mtp_id[2]);
> +
> +       if (mtp_id[2] = 0x33)
> +               dev_info(lcd->dev,
> +                       "ID-3 is 0xff does not support dynamic elvss\n");
> +       else
> +               dev_info(lcd->dev,
> +                       "ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]);
> +
> +       s6e8ax0_panel_init(lcd);
> +       s6e8ax0_display_on(lcd);
> +}
> +
> +#ifdef CONFIG_OF
> +static int s6e8ax0_generic_reset(struct device *dev)
> +{
> +       struct s6e8ax0 *lcd = dev_get_drvdata(dev);
> +
> +       mdelay(10);
> +       gpio_set_value(lcd->reset_gpio, 0);
> +       mdelay(10);
> +       gpio_set_value(lcd->reset_gpio, 1);
> +
> +       return 0;
> +}
> +
> +static struct s6e8ax0_platform_data *s6e8ax0_parse_dt(struct s6e8ax0 *lcd)
> +{
> +       struct device_node *node = lcd->dev->of_node;
> +       struct s6e8ax0_platform_data *data;
> +       const __be32 *prop_data;
> +       int ret;
> +
> +       data = devm_kzalloc(lcd->dev, sizeof(*data), GFP_KERNEL);
> +       if (!data) {
> +               dev_err(lcd->dev, "failed to allocate platform data.\n");
> +               return NULL;
> +       }
> +
> +       ret = of_get_videomode(node, &data->mode, 0);
> +       if (ret) {
> +               dev_err(lcd->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-off-delay", NULL);
> +       if (!prop_data)
> +               return NULL;
> +       data->power_off_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);
> +
> +       data->reset = s6e8ax0_generic_reset;
> +
> +       return data;
> +}
> +
> +static struct of_device_id s6e8ax0_of_match[] = {
> +       { .compatible = "samsung,s6e8ax0" },
> +       { }
> +};
> +
> +MODULE_DEVICE_TABLE(of, s6e8ax0_of_match);
> +#else
> +static struct s6e8ax0_platform_data *s6e8ax0_parse_dt(struct s6e8ax0 *lcd)
> +{
> +       return NULL;
> +}
> +#endif
> +
> +static const struct display_entity_interface_params s6e8ax0_params = {
> +       .type = DISPLAY_ENTITY_INTERFACE_DSI,
> +       .p.dsi = {
> +               .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 = 10000000,
> +       },
> +};
> +
> +static int s6e8ax0_set_state(struct display_entity *entity,
> +                           enum display_entity_state state)
> +{
> +       struct s6e8ax0 *panel = to_panel(entity);
> +
> +       switch (state) {
> +       case DISPLAY_ENTITY_STATE_OFF:
> +               if (entity->state = DISPLAY_ENTITY_STATE_ON) {
> +                       s6e8ax0_sleep_in(panel);
> +                       panel->entity.source->common_ops->set_stream(entity->source,
> +                                               DISPLAY_ENTITY_STREAM_STOPPED);
> +
> +                       msleep(panel->pdata->power_off_delay);
> +                       s6e8ax0_display_off(panel);
> +
> +                       panel->entity.source->ops.dsi->disable(entity->source);
> +
> +                       regulator_bulk_disable(ARRAY_SIZE(panel->supplies),
> +                                                               panel->supplies);
> +               } else if (entity->state = DISPLAY_ENTITY_STATE_STANDBY) {
> +                       msleep(panel->pdata->power_off_delay);
> +                       s6e8ax0_display_off(panel);
> +
> +                       panel->entity.source->ops.dsi->disable(entity->source);
> +
> +                       regulator_bulk_disable(ARRAY_SIZE(panel->supplies),
> +                                                               panel->supplies);
> +               }
> +               break;
> +
> +       case DISPLAY_ENTITY_STATE_STANDBY:
> +               if (entity->state = DISPLAY_ENTITY_STATE_ON) {
> +                       s6e8ax0_sleep_in(panel);
> +                       panel->entity.source->common_ops->set_stream(entity->source,
> +                                               DISPLAY_ENTITY_STREAM_STOPPED);
> +               } else if (entity->state = DISPLAY_ENTITY_STATE_OFF) {
> +                       msleep(panel->pdata->power_on_delay);
> +
> +                       regulator_bulk_enable(ARRAY_SIZE(panel->supplies),
> +                                                               panel->supplies);
> +
> +                       msleep(panel->pdata->reset_delay);
> +
> +                       /* lcd reset */
> +                       if (panel->pdata->reset)
> +                               panel->pdata->reset(panel->dev);
> +                       msleep(5);
> +
> +                       panel->entity.source->ops.dsi->enable(entity->source);
> +
> +                       s6e8ax0_set_sequence(panel);
> +                       s6e8ax0_sleep_in(panel);
> +               }
> +               break;
> +
> +       case DISPLAY_ENTITY_STATE_ON:
> +               if (entity->state = DISPLAY_ENTITY_STATE_OFF) {
> +                       msleep(panel->pdata->power_on_delay);
> +
> +                       regulator_bulk_enable(ARRAY_SIZE(panel->supplies),
> +                                                               panel->supplies);
> +
> +                       msleep(panel->pdata->reset_delay);
> +
> +                       /* lcd reset */
> +                       if (panel->pdata->reset)
> +                               panel->pdata->reset(panel->dev);
> +                       msleep(5);
> +
> +                       panel->entity.source->ops.dsi->enable(entity->source);
> +
> +                       s6e8ax0_set_sequence(panel);
> +
> +                       panel->entity.source->common_ops->set_stream(entity->source,
> +                                       DISPLAY_ENTITY_STREAM_CONTINUOUS);
> +               } else {
> +                       panel->entity.source->common_ops->set_stream(entity->source,
> +                                       DISPLAY_ENTITY_STREAM_CONTINUOUS);
> +                       s6e8ax0_sleep_out(panel);
> +               }
> +               break;
> +
> +       default:
> +               break;
> +       }
> +
> +       return 0;
> +}
> +
> +static int s6e8ax0_get_modes(struct display_entity *entity,
> +                           const struct videomode **modes)
> +{
> +       struct s6e8ax0 *panel = to_panel(entity);
> +
> +       *modes = &panel->pdata->mode;
> +       return 1;
> +}
> +
> +static int s6e8ax0_get_size(struct display_entity *entity,
> +                          unsigned int *width, unsigned int *height)
> +{
> +       struct s6e8ax0 *panel = to_panel(entity);
> +
> +       *width = panel->pdata->width;
> +       *height = panel->pdata->height;
> +       return 0;
> +}
> +
> +static int s6e8ax0_get_params(struct display_entity *entity,
> +                               struct display_entity_interface_params *params)
> +{
> +       *params = s6e8ax0_params;
> +       return 0;
> +}
> +
> +static const struct display_entity_control_ops s6e8ax0_control_ops = {
> +       .set_state = s6e8ax0_set_state,
> +       .get_modes = s6e8ax0_get_modes,
> +       .get_size = s6e8ax0_get_size,
> +       .get_params = s6e8ax0_get_params,
> +};
> +
> +static void s6e8ax0_release(struct display_entity *entity)
> +{
> +       struct s6e8ax0 *panel = to_panel(entity);
> +
> +       backlight_device_unregister(panel->bd);
> +       lcd_device_unregister(panel->ld);
> +       regulator_bulk_free(ARRAY_SIZE(panel->supplies), panel->supplies);
> +       kfree(panel);
> +}
> +
> +static int s6e8ax0_probe(struct platform_device *pdev)
> +{
> +       struct s6e8ax0 *lcd;
> +       int ret;
> +
> +       lcd = kzalloc(sizeof(struct s6e8ax0), GFP_KERNEL);
> +       if (!lcd) {
> +               dev_err(&pdev->dev, "failed to allocate s6e8ax0 structure.\n");
> +               return -ENOMEM;
> +       }
> +
> +       lcd->dev = &pdev->dev;
> +       lcd->pdata = (struct s6e8ax0_platform_data *)pdev->dev.platform_data;
> +
> +       if (!lcd->pdata) {
> +               lcd->pdata = s6e8ax0_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("s6e8ax0", &pdev->dev, lcd,
> +                       &s6e8ax0_lcd_ops);
> +       if (IS_ERR(lcd->ld)) {
> +               dev_err(&pdev->dev, "failed to register lcd ops.\n");
> +               ret = PTR_ERR(lcd->ld);
> +               goto err_lcd_register;
> +       }
> +
> +       lcd->bd = backlight_device_register("s6e8ax0-bl", &pdev->dev, lcd,
> +                       &s6e8ax0_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;
> +       }
> +

I think we should try to remove the dependency with LCD framework and
Backlight framework, and incorporate those functionality as par of
CDF.
you can refer to my similar patch "Make s6e8ax0 panel driver compliant
with CDF" ( http://comments.gmane.org/gmane.linux.drivers.video-input-infrastructure/59187
) which i had posted couple of weeks back, where I made an attempt to
remove "lcd_ops" dependency.

> +       lcd->acl_enable = 1;
> +       lcd->cur_acl = 0;
> +       lcd->power = FB_BLANK_UNBLANK;
> +
> +       lcd->bd->props.max_brightness = MAX_BRIGHTNESS;
> +       lcd->bd->props.brightness = MAX_BRIGHTNESS;
> +
> +       lcd->entity.of_node = pdev->dev.of_node;
> +       lcd->entity.dev = &pdev->dev;
> +       lcd->entity.release = s6e8ax0_release;
> +       lcd->entity.ops = &s6e8ax0_control_ops;
> +
> +       platform_set_drvdata(pdev, lcd);
> +
> +       ret = display_entity_register(&lcd->entity);
> +       if (ret < 0)
> +               goto err_display_register;
> +
> +       display_entity_set_state(&lcd->entity, DISPLAY_ENTITY_STATE_ON);
> +
> +       dev_dbg(&pdev->dev, "probed s6e8ax0 panel driver.\n");
> +
> +       return 0;
> +
> +err_display_register:
> +       backlight_device_unregister(lcd->bd);
> +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 s6e8ax0_remove(struct platform_device *dev)
> +{
> +       struct s6e8ax0 *lcd = platform_get_drvdata(dev);
> +
> +       platform_set_drvdata(dev, NULL);
> +       display_entity_unregister(&lcd->entity);
> +
> +       return 0;
> +}
> +
> +static int s6e8ax0_suspend(struct device *dev)
> +{
> +       struct s6e8ax0 *lcd = dev_get_drvdata(dev);
> +
> +       if (lcd->power = FB_BLANK_UNBLANK)
> +               display_entity_set_state(&lcd->entity,
> +                                               DISPLAY_ENTITY_STATE_OFF);
> +
> +       return 0;
> +}
> +
> +static int s6e8ax0_resume(struct device *dev)
> +{
> +       struct s6e8ax0 *lcd = dev_get_drvdata(dev);
> +
> +       if (lcd->power = FB_BLANK_UNBLANK)
> +               display_entity_set_state(&lcd->entity,
> +                                               DISPLAY_ENTITY_STATE_ON);
> +
> +       return 0;
> +}
> +
> +static struct dev_pm_ops s6e8ax0_pm_ops = {
> +       SET_SYSTEM_SLEEP_PM_OPS(s6e8ax0_suspend, s6e8ax0_resume)
> +};
> +
> +static struct platform_driver s6e8ax0_driver = {
> +       .probe = s6e8ax0_probe,
> +       .remove = s6e8ax0_remove,
> +       .driver = {
> +               .name = "panel_s6e8ax0",
> +               .owner = THIS_MODULE,
> +               .of_match_table = of_match_ptr(s6e8ax0_of_match),
> +               .pm = &s6e8ax0_pm_ops,
> +       },
> +};
> +module_platform_driver(s6e8ax0_driver);
> +
> +MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
> +MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
> +MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
> +MODULE_DESCRIPTION("MIPI-DSI based s6e8ax0 AMOLED LCD Panel Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/video/panel-s6e8ax0.h b/include/video/panel-s6e8ax0.h
> new file mode 100644
> index 0000000..e522bfb
> --- /dev/null
> +++ b/include/video/panel-s6e8ax0.h
> @@ -0,0 +1,41 @@
> +/*
> + * 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 <linux/videomode.h>
> +
> +struct s6e8ax0_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 power_off_delay;
> +       /* panel is reversed */
> +       bool panel_reverse;
> +};
> +
> +#endif /* __PANEL_S6E8AX0_H__ */
> --
> 1.8.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/4] video: display: Add Samsung s6e8ax0 display panel driver
  2013-02-07  9:46     ` Vikas Sajjan
@ 2013-02-07 10:18       ` Tomasz Figa
  -1 siblings, 0 replies; 36+ messages in thread
From: Tomasz Figa @ 2013-02-07 10:18 UTC (permalink / raw)
  To: Vikas Sajjan
  Cc: dri-devel, linux-fbdev, linux-samsung-soc, kyungmin.park,
	m.szyprowski, tomasz.figa, Daniel Vetter, Marcus Lorentzon,
	Laurent Pinchart, rob, tomi.valkeinen, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki, aditya.ps, sunil joshi

On Thursday 07 of February 2013 15:04:30 Vikas Sajjan wrote:
> Hi Figa,
> 
> On Wed, Jan 30, 2013 at 9:09 PM, Tomasz Figa <t.figa@samsung.com> wrote:
> > This patch adds Common Display Framework driver for Samsung s6e8ax0
> > MIPI DSI display panel.
> > 
> > Signed-off-by: Tomasz Figa <t.figa@samsung.com>
> > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> > ---
> > 
> >  drivers/video/display/Kconfig         |    3 +
> >  drivers/video/display/Makefile        |    1 +
> >  drivers/video/display/panel-s6e8ax0.c | 1027
> >  +++++++++++++++++++++++++++++++++ include/video/panel-s6e8ax0.h     
> >     |   41 ++
> >  4 files changed, 1072 insertions(+)
> >  create mode 100644 drivers/video/display/panel-s6e8ax0.c
> >  create mode 100644 include/video/panel-s6e8ax0.h
> > 
[snip]
> > +       lcd->ld = lcd_device_register("s6e8ax0", &pdev->dev, lcd,
> > +                       &s6e8ax0_lcd_ops);
> > +       if (IS_ERR(lcd->ld)) {
> > +               dev_err(&pdev->dev, "failed to register lcd ops.\n");
> > +               ret = PTR_ERR(lcd->ld);
> > +               goto err_lcd_register;
> > +       }
> > +
> > +       lcd->bd = backlight_device_register("s6e8ax0-bl", &pdev->dev,
> > lcd, +                       &s6e8ax0_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;
> > +       }
> > +
> 
> I think we should try to remove the dependency with LCD framework and
> Backlight framework, and incorporate those functionality as par of
> CDF.
> you can refer to my similar patch "Make s6e8ax0 panel driver compliant
> with CDF" (
> http://comments.gmane.org/gmane.linux.drivers.video-input-infrastructur
> e/59187 ) which i had posted couple of weeks back, where I made an
> attempt to remove "lcd_ops" dependency.

Yes, I have written in the cover letter that those interfaces is just a 
hack to be able to control the display from userspace in current state of 
CDF.

I agree that CDF will have to be extended with backlight/brightness 
control. However currently CDF does not expose any interface to userspace.

Laurent, what's your opinion on this?

P.S. Tomasz is my first name.

Best regards,
-- 
Tomasz Figa
Samsung Poland R&D Center
SW Solution Development, Linux Platform


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/4] video: display: Add Samsung s6e8ax0 display panel driver
@ 2013-02-07 10:18       ` Tomasz Figa
  0 siblings, 0 replies; 36+ messages in thread
From: Tomasz Figa @ 2013-02-07 10:18 UTC (permalink / raw)
  To: Vikas Sajjan
  Cc: dri-devel, linux-fbdev, linux-samsung-soc, kyungmin.park,
	m.szyprowski, tomasz.figa, Daniel Vetter, Marcus Lorentzon,
	Laurent Pinchart, rob, tomi.valkeinen, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki, aditya.ps, sunil joshi

On Thursday 07 of February 2013 15:04:30 Vikas Sajjan wrote:
> Hi Figa,
> 
> On Wed, Jan 30, 2013 at 9:09 PM, Tomasz Figa <t.figa@samsung.com> wrote:
> > This patch adds Common Display Framework driver for Samsung s6e8ax0
> > MIPI DSI display panel.
> > 
> > Signed-off-by: Tomasz Figa <t.figa@samsung.com>
> > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> > ---
> > 
> >  drivers/video/display/Kconfig         |    3 +
> >  drivers/video/display/Makefile        |    1 +
> >  drivers/video/display/panel-s6e8ax0.c | 1027
> >  +++++++++++++++++++++++++++++++++ include/video/panel-s6e8ax0.h     
> >     |   41 ++
> >  4 files changed, 1072 insertions(+)
> >  create mode 100644 drivers/video/display/panel-s6e8ax0.c
> >  create mode 100644 include/video/panel-s6e8ax0.h
> > 
[snip]
> > +       lcd->ld = lcd_device_register("s6e8ax0", &pdev->dev, lcd,
> > +                       &s6e8ax0_lcd_ops);
> > +       if (IS_ERR(lcd->ld)) {
> > +               dev_err(&pdev->dev, "failed to register lcd ops.\n");
> > +               ret = PTR_ERR(lcd->ld);
> > +               goto err_lcd_register;
> > +       }
> > +
> > +       lcd->bd = backlight_device_register("s6e8ax0-bl", &pdev->dev,
> > lcd, +                       &s6e8ax0_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;
> > +       }
> > +
> 
> I think we should try to remove the dependency with LCD framework and
> Backlight framework, and incorporate those functionality as par of
> CDF.
> you can refer to my similar patch "Make s6e8ax0 panel driver compliant
> with CDF" (
> http://comments.gmane.org/gmane.linux.drivers.video-input-infrastructur
> e/59187 ) which i had posted couple of weeks back, where I made an
> attempt to remove "lcd_ops" dependency.

Yes, I have written in the cover letter that those interfaces is just a 
hack to be able to control the display from userspace in current state of 
CDF.

I agree that CDF will have to be extended with backlight/brightness 
control. However currently CDF does not expose any interface to userspace.

Laurent, what's your opinion on this?

P.S. Tomasz is my first name.

Best regards,
-- 
Tomasz Figa
Samsung Poland R&D Center
SW Solution Development, Linux Platform

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
  2013-02-03 19:17     ` Tomasz Figa
@ 2013-02-08 11:40       ` Tomi Valkeinen
  -1 siblings, 0 replies; 36+ messages in thread
From: Tomi Valkeinen @ 2013-02-08 11:40 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: Laurent Pinchart, Tomasz Figa, dri-devel, linux-fbdev,
	linux-samsung-soc, kyungmin.park, m.szyprowski, Daniel Vetter,
	Marcus Lorentzon, rob, Vikas Sajjan, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki

[-- Attachment #1: Type: text/plain, Size: 4639 bytes --]

Hi,

On 2013-02-03 21:17, Tomasz Figa wrote:
> Hi Laurent,
> 
> On Saturday 02 of February 2013 11:39:59 Laurent Pinchart wrote:
>> Hi Tomasz,
>>
>> Thank you for your RFC.
>>
>> On Wednesday 30 January 2013 16:38:59 Tomasz Figa wrote:

>>> Changes done to Tomi's edition of CDF:
>>>  - Replaced set_operation_mode, set_pixel_format and set_size video
>>>  source>  
>>>    operations with get_params display entity operation, as it was
>>>    originally in Laurent's version.
>>>    
>>>    We have discussed this matter on the mailing list and decided that
>>>    it
>>>    would be better to have the source ask the sink for its parameter
>>>    structure and do whatever appropriate with it.
>>
>> Could you briefly outline the rationale behind this ?
> 
> Display interfaces may be described by an arbitrary number of parameters. 
> Some will require just video timings, others like DSI will require a 
> significant number of additional bus-specific ones.
> 
> Separating all the parameters (all of which are usually set at the same 
> time) into a lot of ops setting single parameter will just add unnecessary 
> complexity.

I have nothing against combining the parameters that way. I think the
important thing here is just that we have to allow changing of the
parameters, whenever the display device needs that. So the bus
parameters cannot be a one time constant setting.

>> I'm wondering whether get_params could limit us if a device needs to
>> modify parameters at runtime. For instance a panel might need to change
>> clock frequencies or number of used data lane depending on various
>> runtime conditions. I don't have a real use case here, so it might just
>> be a bit of over-engineering.
> 
> Hmm, isn't it in the opposite direction (the user requests the display 
> interface to change, for example, video mode, which in turn reconfigures 
> the link and requests the panel to update its settings)?

Well, now, that's the question.

Let's consider a simplified case with DSI output from the SoC, and a DSI
panel. If the panel is a rather simple one, you could well call a method
in the API in the DSI output driver, which would do necessary
configuration and inform the panel driver to do any configuration it
needs to do, based on the parameters.

However, in my experience display devices, DSI devices in particular,
are often quite "interesting". If the control of the operation in
question is in the DSI output side, we are limited about what kind of
DSI panels we can use, as the DSI output driver has to know all the
tricks needed to make the panels work.

I'm having hard time trying to explain this, so let's try an example.
Consider the initial powering up of the bus. If the DSI output driver is
in control, something like the following probably happens:

- output driver asks for the parameters from the panel driver
- output driver programs the DSI output according to these parameters
- output driver informs the panel that the bus is now up and running

Then consider a totally invented case, but which perhaps describes the
problem with the above approach: The DSI panel requires the DSI bus
first to be started in HS mode, but with a certain predefined bus speed,
and only one lane used. This predefined bus setup is used to send
configuration commands to the panel hardware, and only after that can
you reconfigure the bus with proper speed and lanes.

That kind of thing is a bit difficult to manage with the DSI output
driver is in control. However, if the DSI panel driver is in control,
it's simple:

- panel driver calls config methods in the DSI output driver, setting
the predefined speed and one lane
- panel driver sends configuration commands to the panel
- panel driver calls config methods in the DSI output driver, setting
the final bus config

>>>    5. Mask of used data lanes (each bit represents single lane).
>>
>> From my experience with MIPI CSI (Camera Serial Interface, similar to
>> DSI) some transceivers can reroute data lanes internally. Is that true
>> for DSI as well ? In that case we would need a list of data lanes, not
>> just a mask.
> 
> Hmm, I don't remember at the moment, will have to look to the 
> specification. Exynos DSI master doesn't support such feature.

In OMAP you can configure the DSI pins quite freely. We have the
following struct:

struct omap_dsi_pin_config {
	int num_pins;
	/*
	 * pin numbers in the following order:
	 * clk+, clk-
	 * data1+, data1-
	 * data2+, data2-
	 * ...
	 */
	int pins[OMAP_DSS_MAX_DSI_PINS];
};

 Tomi



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 899 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
@ 2013-02-08 11:40       ` Tomi Valkeinen
  0 siblings, 0 replies; 36+ messages in thread
From: Tomi Valkeinen @ 2013-02-08 11:40 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: Laurent Pinchart, Tomasz Figa, dri-devel, linux-fbdev,
	linux-samsung-soc, kyungmin.park, m.szyprowski, Daniel Vetter,
	Marcus Lorentzon, rob, Vikas Sajjan, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki

[-- Attachment #1: Type: text/plain, Size: 4639 bytes --]

Hi,

On 2013-02-03 21:17, Tomasz Figa wrote:
> Hi Laurent,
> 
> On Saturday 02 of February 2013 11:39:59 Laurent Pinchart wrote:
>> Hi Tomasz,
>>
>> Thank you for your RFC.
>>
>> On Wednesday 30 January 2013 16:38:59 Tomasz Figa wrote:

>>> Changes done to Tomi's edition of CDF:
>>>  - Replaced set_operation_mode, set_pixel_format and set_size video
>>>  source>  
>>>    operations with get_params display entity operation, as it was
>>>    originally in Laurent's version.
>>>    
>>>    We have discussed this matter on the mailing list and decided that
>>>    it
>>>    would be better to have the source ask the sink for its parameter
>>>    structure and do whatever appropriate with it.
>>
>> Could you briefly outline the rationale behind this ?
> 
> Display interfaces may be described by an arbitrary number of parameters. 
> Some will require just video timings, others like DSI will require a 
> significant number of additional bus-specific ones.
> 
> Separating all the parameters (all of which are usually set at the same 
> time) into a lot of ops setting single parameter will just add unnecessary 
> complexity.

I have nothing against combining the parameters that way. I think the
important thing here is just that we have to allow changing of the
parameters, whenever the display device needs that. So the bus
parameters cannot be a one time constant setting.

>> I'm wondering whether get_params could limit us if a device needs to
>> modify parameters at runtime. For instance a panel might need to change
>> clock frequencies or number of used data lane depending on various
>> runtime conditions. I don't have a real use case here, so it might just
>> be a bit of over-engineering.
> 
> Hmm, isn't it in the opposite direction (the user requests the display 
> interface to change, for example, video mode, which in turn reconfigures 
> the link and requests the panel to update its settings)?

Well, now, that's the question.

Let's consider a simplified case with DSI output from the SoC, and a DSI
panel. If the panel is a rather simple one, you could well call a method
in the API in the DSI output driver, which would do necessary
configuration and inform the panel driver to do any configuration it
needs to do, based on the parameters.

However, in my experience display devices, DSI devices in particular,
are often quite "interesting". If the control of the operation in
question is in the DSI output side, we are limited about what kind of
DSI panels we can use, as the DSI output driver has to know all the
tricks needed to make the panels work.

I'm having hard time trying to explain this, so let's try an example.
Consider the initial powering up of the bus. If the DSI output driver is
in control, something like the following probably happens:

- output driver asks for the parameters from the panel driver
- output driver programs the DSI output according to these parameters
- output driver informs the panel that the bus is now up and running

Then consider a totally invented case, but which perhaps describes the
problem with the above approach: The DSI panel requires the DSI bus
first to be started in HS mode, but with a certain predefined bus speed,
and only one lane used. This predefined bus setup is used to send
configuration commands to the panel hardware, and only after that can
you reconfigure the bus with proper speed and lanes.

That kind of thing is a bit difficult to manage with the DSI output
driver is in control. However, if the DSI panel driver is in control,
it's simple:

- panel driver calls config methods in the DSI output driver, setting
the predefined speed and one lane
- panel driver sends configuration commands to the panel
- panel driver calls config methods in the DSI output driver, setting
the final bus config

>>>    5. Mask of used data lanes (each bit represents single lane).
>>
>> From my experience with MIPI CSI (Camera Serial Interface, similar to
>> DSI) some transceivers can reroute data lanes internally. Is that true
>> for DSI as well ? In that case we would need a list of data lanes, not
>> just a mask.
> 
> Hmm, I don't remember at the moment, will have to look to the 
> specification. Exynos DSI master doesn't support such feature.

In OMAP you can configure the DSI pins quite freely. We have the
following struct:

struct omap_dsi_pin_config {
	int num_pins;
	/*
	 * pin numbers in the following order:
	 * clk+, clk-
	 * data1+, data1-
	 * data2+, data2-
	 * ...
	 */
	int pins[OMAP_DSS_MAX_DSI_PINS];
};

 Tomi



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 899 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
  2013-02-08 11:40       ` Tomi Valkeinen
@ 2013-02-08 12:40         ` Marcus Lorentzon
  -1 siblings, 0 replies; 36+ messages in thread
From: Marcus Lorentzon @ 2013-02-08 12:40 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa, dri-devel,
	linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	Daniel Vetter, rob, Vikas Sajjan, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki

On 02/08/2013 12:40 PM, Tomi Valkeinen wrote:
> Hi,
>
> On 2013-02-03 21:17, Tomasz Figa wrote:
>> Hi Laurent,
>>
>> On Saturday 02 of February 2013 11:39:59 Laurent Pinchart wrote:
>>> Hi Tomasz,
>>>
>>> Thank you for your RFC.
>>>
>>> On Wednesday 30 January 2013 16:38:59 Tomasz Figa wrote:
>>>> Changes done to Tomi's edition of CDF:
>>>>   - Replaced set_operation_mode, set_pixel_format and set_size video
>>>>   source>
>>>>     operations with get_params display entity operation, as it was
>>>>     originally in Laurent's version.
>>>>     
>>>>     We have discussed this matter on the mailing list and decided that
>>>>     it
>>>>     would be better to have the source ask the sink for its parameter
>>>>     structure and do whatever appropriate with it.
>>> Could you briefly outline the rationale behind this ?
>> Display interfaces may be described by an arbitrary number of parameters.
>> Some will require just video timings, others like DSI will require a
>> significant number of additional bus-specific ones.
>>
>> Separating all the parameters (all of which are usually set at the same
>> time) into a lot of ops setting single parameter will just add unnecessary
>> complexity.
> I have nothing against combining the parameters that way. I think the
> important thing here is just that we have to allow changing of the
> parameters, whenever the display device needs that. So the bus
> parameters cannot be a one time constant setting.

I agree, but I think it should be 
setup->enable->video_on->video_off->disable->setup->...
I don't think there is any interface parameters that should be changed 
while link is enabled. And if there are, they should be identified and 
split out into a separate operation.
>>> I'm wondering whether get_params could limit us if a device needs to
>>> modify parameters at runtime. For instance a panel might need to change
>>> clock frequencies or number of used data lane depending on various
>>> runtime conditions. I don't have a real use case here, so it might just
>>> be a bit of over-engineering.
>> Hmm, isn't it in the opposite direction (the user requests the display
>> interface to change, for example, video mode, which in turn reconfigures
>> the link and requests the panel to update its settings)?
> Well, now, that's the question.
>
> Let's consider a simplified case with DSI output from the SoC, and a DSI
> panel. If the panel is a rather simple one, you could well call a method
> in the API in the DSI output driver, which would do necessary
> configuration and inform the panel driver to do any configuration it
> needs to do, based on the parameters.
>
> However, in my experience display devices, DSI devices in particular,
> are often quite "interesting". If the control of the operation in
> question is in the DSI output side, we are limited about what kind of
> DSI panels we can use, as the DSI output driver has to know all the
> tricks needed to make the panels work.
>
> I'm having hard time trying to explain this, so let's try an example.
> Consider the initial powering up of the bus. If the DSI output driver is
> in control, something like the following probably happens:
>
> - output driver asks for the parameters from the panel driver
> - output driver programs the DSI output according to these parameters
> - output driver informs the panel that the bus is now up and running
>
> Then consider a totally invented case, but which perhaps describes the
> problem with the above approach: The DSI panel requires the DSI bus
> first to be started in HS mode, but with a certain predefined bus speed,
> and only one lane used. This predefined bus setup is used to send
> configuration commands to the panel hardware, and only after that can
> you reconfigure the bus with proper speed and lanes.
>
> That kind of thing is a bit difficult to manage with the DSI output
> driver is in control. However, if the DSI panel driver is in control,
> it's simple:
>
> - panel driver calls config methods in the DSI output driver, setting
> the predefined speed and one lane
> - panel driver sends configuration commands to the panel
> - panel driver calls config methods in the DSI output driver, setting
> the final bus config

We have similar use cases and so I agree (and have implemented) the 
latter approach where panel driver is in control. I think it is 
important to separate the panel calling dispc (which it should not) from 
panel calling bus/videosource ops. Compare to I2C, no one would expect 
panel to call dispc to do I2C ops, instead panel call bus ops directly 
in response to the CDF ops request panel is running.
>>>>     5. Mask of used data lanes (each bit represents single lane).
>>>  From my experience with MIPI CSI (Camera Serial Interface, similar to
>>> DSI) some transceivers can reroute data lanes internally. Is that true
>>> for DSI as well ? In that case we would need a list of data lanes, not
>>> just a mask.
>> Hmm, I don't remember at the moment, will have to look to the
>> specification. Exynos DSI master doesn't support such feature.
> In OMAP you can configure the DSI pins quite freely. We have the
> following struct:
>
> struct omap_dsi_pin_config {
> 	int num_pins;
> 	/*
> 	 * pin numbers in the following order:
> 	 * clk+, clk-
> 	 * data1+, data1-
> 	 * data2+, data2-
> 	 * ...
> 	 */
> 	int pins[OMAP_DSS_MAX_DSI_PINS];
> };
>
Do you reroute after boot? Or is this just "board/product setup". We 
have some pinmuxing as well and DPhy sharing, but we have never used it 
after init/boot. If not runtime, I think this could be put in DT config 
for product instead of under dynamic control from panel.

/BR
/Marcus


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
@ 2013-02-08 12:40         ` Marcus Lorentzon
  0 siblings, 0 replies; 36+ messages in thread
From: Marcus Lorentzon @ 2013-02-08 12:40 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa, dri-devel,
	linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	Daniel Vetter, rob, Vikas Sajjan, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki

On 02/08/2013 12:40 PM, Tomi Valkeinen wrote:
> Hi,
>
> On 2013-02-03 21:17, Tomasz Figa wrote:
>> Hi Laurent,
>>
>> On Saturday 02 of February 2013 11:39:59 Laurent Pinchart wrote:
>>> Hi Tomasz,
>>>
>>> Thank you for your RFC.
>>>
>>> On Wednesday 30 January 2013 16:38:59 Tomasz Figa wrote:
>>>> Changes done to Tomi's edition of CDF:
>>>>   - Replaced set_operation_mode, set_pixel_format and set_size video
>>>>   source>
>>>>     operations with get_params display entity operation, as it was
>>>>     originally in Laurent's version.
>>>>     
>>>>     We have discussed this matter on the mailing list and decided that
>>>>     it
>>>>     would be better to have the source ask the sink for its parameter
>>>>     structure and do whatever appropriate with it.
>>> Could you briefly outline the rationale behind this ?
>> Display interfaces may be described by an arbitrary number of parameters.
>> Some will require just video timings, others like DSI will require a
>> significant number of additional bus-specific ones.
>>
>> Separating all the parameters (all of which are usually set at the same
>> time) into a lot of ops setting single parameter will just add unnecessary
>> complexity.
> I have nothing against combining the parameters that way. I think the
> important thing here is just that we have to allow changing of the
> parameters, whenever the display device needs that. So the bus
> parameters cannot be a one time constant setting.

I agree, but I think it should be 
setup->enable->video_on->video_off->disable->setup->...
I don't think there is any interface parameters that should be changed 
while link is enabled. And if there are, they should be identified and 
split out into a separate operation.
>>> I'm wondering whether get_params could limit us if a device needs to
>>> modify parameters at runtime. For instance a panel might need to change
>>> clock frequencies or number of used data lane depending on various
>>> runtime conditions. I don't have a real use case here, so it might just
>>> be a bit of over-engineering.
>> Hmm, isn't it in the opposite direction (the user requests the display
>> interface to change, for example, video mode, which in turn reconfigures
>> the link and requests the panel to update its settings)?
> Well, now, that's the question.
>
> Let's consider a simplified case with DSI output from the SoC, and a DSI
> panel. If the panel is a rather simple one, you could well call a method
> in the API in the DSI output driver, which would do necessary
> configuration and inform the panel driver to do any configuration it
> needs to do, based on the parameters.
>
> However, in my experience display devices, DSI devices in particular,
> are often quite "interesting". If the control of the operation in
> question is in the DSI output side, we are limited about what kind of
> DSI panels we can use, as the DSI output driver has to know all the
> tricks needed to make the panels work.
>
> I'm having hard time trying to explain this, so let's try an example.
> Consider the initial powering up of the bus. If the DSI output driver is
> in control, something like the following probably happens:
>
> - output driver asks for the parameters from the panel driver
> - output driver programs the DSI output according to these parameters
> - output driver informs the panel that the bus is now up and running
>
> Then consider a totally invented case, but which perhaps describes the
> problem with the above approach: The DSI panel requires the DSI bus
> first to be started in HS mode, but with a certain predefined bus speed,
> and only one lane used. This predefined bus setup is used to send
> configuration commands to the panel hardware, and only after that can
> you reconfigure the bus with proper speed and lanes.
>
> That kind of thing is a bit difficult to manage with the DSI output
> driver is in control. However, if the DSI panel driver is in control,
> it's simple:
>
> - panel driver calls config methods in the DSI output driver, setting
> the predefined speed and one lane
> - panel driver sends configuration commands to the panel
> - panel driver calls config methods in the DSI output driver, setting
> the final bus config

We have similar use cases and so I agree (and have implemented) the 
latter approach where panel driver is in control. I think it is 
important to separate the panel calling dispc (which it should not) from 
panel calling bus/videosource ops. Compare to I2C, no one would expect 
panel to call dispc to do I2C ops, instead panel call bus ops directly 
in response to the CDF ops request panel is running.
>>>>     5. Mask of used data lanes (each bit represents single lane).
>>>  From my experience with MIPI CSI (Camera Serial Interface, similar to
>>> DSI) some transceivers can reroute data lanes internally. Is that true
>>> for DSI as well ? In that case we would need a list of data lanes, not
>>> just a mask.
>> Hmm, I don't remember at the moment, will have to look to the
>> specification. Exynos DSI master doesn't support such feature.
> In OMAP you can configure the DSI pins quite freely. We have the
> following struct:
>
> struct omap_dsi_pin_config {
> 	int num_pins;
> 	/*
> 	 * pin numbers in the following order:
> 	 * clk+, clk-
> 	 * data1+, data1-
> 	 * data2+, data2-
> 	 * ...
> 	 */
> 	int pins[OMAP_DSS_MAX_DSI_PINS];
> };
>
Do you reroute after boot? Or is this just "board/product setup". We 
have some pinmuxing as well and DPhy sharing, but we have never used it 
after init/boot. If not runtime, I think this could be put in DT config 
for product instead of under dynamic control from panel.

/BR
/Marcus

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
  2013-02-08 12:40         ` Marcus Lorentzon
@ 2013-02-08 13:04           ` Tomi Valkeinen
  -1 siblings, 0 replies; 36+ messages in thread
From: Tomi Valkeinen @ 2013-02-08 13:04 UTC (permalink / raw)
  To: Marcus Lorentzon
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa, dri-devel,
	linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	Daniel Vetter, rob, Vikas Sajjan, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki

[-- Attachment #1: Type: text/plain, Size: 2647 bytes --]

On 2013-02-08 14:40, Marcus Lorentzon wrote:

> I agree, but I think it should be
> setup->enable->video_on->video_off->disable->setup->...
> I don't think there is any interface parameters that should be changed
> while link is enabled. And if there are, they should be identified and
> split out into a separate operation.

Hmm. At least on OMAP and DSI video mode, it is possible to change at
least timings on the fly. But yes, generally the link has to be disabled
first before changing any parameters.

>> In OMAP you can configure the DSI pins quite freely. We have the
>> following struct:
>>
>> struct omap_dsi_pin_config {
>>     int num_pins;
>>     /*
>>      * pin numbers in the following order:
>>      * clk+, clk-
>>      * data1+, data1-
>>      * data2+, data2-
>>      * ...
>>      */
>>     int pins[OMAP_DSS_MAX_DSI_PINS];
>> };
>>
> Do you reroute after boot? Or is this just "board/product setup". We
> have some pinmuxing as well and DPhy sharing, but we have never used it
> after init/boot. If not runtime, I think this could be put in DT config
> for product instead of under dynamic control from panel.

The pin config with the struct above is done later, when the panel
driver configures the DSI bus. So in OMAP we have two distinct things
that need to be configured:

- The actual pin muxing, i.e. selecting the functions for pin N on the
OMAP package. The functions for a single pin could be for example GPIO
70, DSI DX0, UART1_CTS, etc. This is normally done once during board init.

- DSI pin configuration in the display subsystem. This is used to map
the pins to DSI functions. I.e. DSI DX0 pin is mapped to DATA1+. This is
done by the DSS driver, when the panel driver gives it the parameters.

So the first muxing basically assigns the pin to DSI in general, and
then DSI will internally route the pin to a an actual DSI function.

Whether the muxing needs to changed during runtime... I'm not sure. On
OMAP the DSI pin config also tells how many lanes are used. So if a DSI
panel would first want to use only one lane, and later change it to n
lanes, we'd need this kind of function.

I think it conceptually fits better if the pin config data is passed to
the panel via DT data, and the panel then gives the config to the DSI
master. It's just a part of the DSI bus parameters, like, say, clock
speed or whether to use HS or LP. That way the DT node for the panel
contains the information about the panel. (versus having pin config data
in the DSI master, in which case the DSI master's node contains data
about a specific DSI panel).

 Tomi



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 899 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
@ 2013-02-08 13:04           ` Tomi Valkeinen
  0 siblings, 0 replies; 36+ messages in thread
From: Tomi Valkeinen @ 2013-02-08 13:04 UTC (permalink / raw)
  To: Marcus Lorentzon
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa, dri-devel,
	linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	Daniel Vetter, rob, Vikas Sajjan, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki

[-- Attachment #1: Type: text/plain, Size: 2647 bytes --]

On 2013-02-08 14:40, Marcus Lorentzon wrote:

> I agree, but I think it should be
> setup->enable->video_on->video_off->disable->setup->...
> I don't think there is any interface parameters that should be changed
> while link is enabled. And if there are, they should be identified and
> split out into a separate operation.

Hmm. At least on OMAP and DSI video mode, it is possible to change at
least timings on the fly. But yes, generally the link has to be disabled
first before changing any parameters.

>> In OMAP you can configure the DSI pins quite freely. We have the
>> following struct:
>>
>> struct omap_dsi_pin_config {
>>     int num_pins;
>>     /*
>>      * pin numbers in the following order:
>>      * clk+, clk-
>>      * data1+, data1-
>>      * data2+, data2-
>>      * ...
>>      */
>>     int pins[OMAP_DSS_MAX_DSI_PINS];
>> };
>>
> Do you reroute after boot? Or is this just "board/product setup". We
> have some pinmuxing as well and DPhy sharing, but we have never used it
> after init/boot. If not runtime, I think this could be put in DT config
> for product instead of under dynamic control from panel.

The pin config with the struct above is done later, when the panel
driver configures the DSI bus. So in OMAP we have two distinct things
that need to be configured:

- The actual pin muxing, i.e. selecting the functions for pin N on the
OMAP package. The functions for a single pin could be for example GPIO
70, DSI DX0, UART1_CTS, etc. This is normally done once during board init.

- DSI pin configuration in the display subsystem. This is used to map
the pins to DSI functions. I.e. DSI DX0 pin is mapped to DATA1+. This is
done by the DSS driver, when the panel driver gives it the parameters.

So the first muxing basically assigns the pin to DSI in general, and
then DSI will internally route the pin to a an actual DSI function.

Whether the muxing needs to changed during runtime... I'm not sure. On
OMAP the DSI pin config also tells how many lanes are used. So if a DSI
panel would first want to use only one lane, and later change it to n
lanes, we'd need this kind of function.

I think it conceptually fits better if the pin config data is passed to
the panel via DT data, and the panel then gives the config to the DSI
master. It's just a part of the DSI bus parameters, like, say, clock
speed or whether to use HS or LP. That way the DT node for the panel
contains the information about the panel. (versus having pin config data
in the DSI master, in which case the DSI master's node contains data
about a specific DSI panel).

 Tomi



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 899 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
  2013-02-08 13:04           ` Tomi Valkeinen
@ 2013-02-08 13:28             ` Marcus Lorentzon
  -1 siblings, 0 replies; 36+ messages in thread
From: Marcus Lorentzon @ 2013-02-08 13:28 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa, dri-devel,
	linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	Daniel Vetter, rob, Vikas Sajjan, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki

On 02/08/2013 02:04 PM, Tomi Valkeinen wrote:
> On 2013-02-08 14:40, Marcus Lorentzon wrote:
>
>> I agree, but I think it should be
>> setup->enable->video_on->video_off->disable->setup->...
>> I don't think there is any interface parameters that should be changed
>> while link is enabled. And if there are, they should be identified and
>> split out into a separate operation.
> Hmm. At least on OMAP and DSI video mode, it is possible to change at
> least timings on the fly. But yes, generally the link has to be disabled
> first before changing any parameters.

When we do that we stop->setup->start during blanking. So our "DSS" is 
optimized to be able to do that without getting blocked. All DSI video 
mode panels (and DPI) products we have done so far have not had any 
issue with that (as long as DSI HS clock is set to continuous). I think 
this approach is less platform dependant, as long as there is no SoC 
that take more than a blanking period to reconfigure.
>>> In OMAP you can configure the DSI pins quite freely. We have the
>>> following struct:
>>>
>>> struct omap_dsi_pin_config {
>>>      int num_pins;
>>>      /*
>>>       * pin numbers in the following order:
>>>       * clk+, clk-
>>>       * data1+, data1-
>>>       * data2+, data2-
>>>       * ...
>>>       */
>>>      int pins[OMAP_DSS_MAX_DSI_PINS];
>>> };
>>>
>> Do you reroute after boot? Or is this just "board/product setup". We
>> have some pinmuxing as well and DPhy sharing, but we have never used it
>> after init/boot. If not runtime, I think this could be put in DT config
>> for product instead of under dynamic control from panel.
> The pin config with the struct above is done later, when the panel
> driver configures the DSI bus. So in OMAP we have two distinct things
> that need to be configured:
>
> - The actual pin muxing, i.e. selecting the functions for pin N on the
> OMAP package. The functions for a single pin could be for example GPIO
> 70, DSI DX0, UART1_CTS, etc. This is normally done once during board init.
>
> - DSI pin configuration in the display subsystem. This is used to map
> the pins to DSI functions. I.e. DSI DX0 pin is mapped to DATA1+. This is
> done by the DSS driver, when the panel driver gives it the parameters.
>
> So the first muxing basically assigns the pin to DSI in general, and
> then DSI will internally route the pin to a an actual DSI function.
>
> Whether the muxing needs to changed during runtime... I'm not sure. On
> OMAP the DSI pin config also tells how many lanes are used. So if a DSI
> panel would first want to use only one lane, and later change it to n
> lanes, we'd need this kind of function.
>
> I think it conceptually fits better if the pin config data is passed to
> the panel via DT data, and the panel then gives the config to the DSI
> master. It's just a part of the DSI bus parameters, like, say, clock
> speed or whether to use HS or LP. That way the DT node for the panel
> contains the information about the panel. (versus having pin config data
> in the DSI master, in which case the DSI master's node contains data
> about a specific DSI panel).
>
I think it still is OMAP specifics and doesn't belong in the panel 
drivers any longer. If you revisit this requirement in the CDF context 
where DSI ifc parameters should describe how to interface with a panel 
outside the SoC, there can't really be any dependencies on SoC internal 
routing. As you say, this is inside pinmux, so how can that affect the 
SoC external interface? I would suggest moving this to dispc-dsilink DT 
settings that are activated on dsilink->enable/disable. At least that is 
how I plan to solve similar STE SoC specific DSI config settings that 
are not really CDF panel generic, like some DPhy trim settings. They do 
depend on the panel and clock speed, but they are more product specific 
than panel driver specific. Then if there are these type of settings 
that every SoC have, then we could look at standardize those. But for 
starters I would try to keep it in product/board-DT per DSI link. So we 
should try to differentiate between DSI host and slave bus params and 
keep slave params in panel driver.

/BR
/Marcus


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
@ 2013-02-08 13:28             ` Marcus Lorentzon
  0 siblings, 0 replies; 36+ messages in thread
From: Marcus Lorentzon @ 2013-02-08 13:28 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa, dri-devel,
	linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	Daniel Vetter, rob, Vikas Sajjan, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki

On 02/08/2013 02:04 PM, Tomi Valkeinen wrote:
> On 2013-02-08 14:40, Marcus Lorentzon wrote:
>
>> I agree, but I think it should be
>> setup->enable->video_on->video_off->disable->setup->...
>> I don't think there is any interface parameters that should be changed
>> while link is enabled. And if there are, they should be identified and
>> split out into a separate operation.
> Hmm. At least on OMAP and DSI video mode, it is possible to change at
> least timings on the fly. But yes, generally the link has to be disabled
> first before changing any parameters.

When we do that we stop->setup->start during blanking. So our "DSS" is 
optimized to be able to do that without getting blocked. All DSI video 
mode panels (and DPI) products we have done so far have not had any 
issue with that (as long as DSI HS clock is set to continuous). I think 
this approach is less platform dependant, as long as there is no SoC 
that take more than a blanking period to reconfigure.
>>> In OMAP you can configure the DSI pins quite freely. We have the
>>> following struct:
>>>
>>> struct omap_dsi_pin_config {
>>>      int num_pins;
>>>      /*
>>>       * pin numbers in the following order:
>>>       * clk+, clk-
>>>       * data1+, data1-
>>>       * data2+, data2-
>>>       * ...
>>>       */
>>>      int pins[OMAP_DSS_MAX_DSI_PINS];
>>> };
>>>
>> Do you reroute after boot? Or is this just "board/product setup". We
>> have some pinmuxing as well and DPhy sharing, but we have never used it
>> after init/boot. If not runtime, I think this could be put in DT config
>> for product instead of under dynamic control from panel.
> The pin config with the struct above is done later, when the panel
> driver configures the DSI bus. So in OMAP we have two distinct things
> that need to be configured:
>
> - The actual pin muxing, i.e. selecting the functions for pin N on the
> OMAP package. The functions for a single pin could be for example GPIO
> 70, DSI DX0, UART1_CTS, etc. This is normally done once during board init.
>
> - DSI pin configuration in the display subsystem. This is used to map
> the pins to DSI functions. I.e. DSI DX0 pin is mapped to DATA1+. This is
> done by the DSS driver, when the panel driver gives it the parameters.
>
> So the first muxing basically assigns the pin to DSI in general, and
> then DSI will internally route the pin to a an actual DSI function.
>
> Whether the muxing needs to changed during runtime... I'm not sure. On
> OMAP the DSI pin config also tells how many lanes are used. So if a DSI
> panel would first want to use only one lane, and later change it to n
> lanes, we'd need this kind of function.
>
> I think it conceptually fits better if the pin config data is passed to
> the panel via DT data, and the panel then gives the config to the DSI
> master. It's just a part of the DSI bus parameters, like, say, clock
> speed or whether to use HS or LP. That way the DT node for the panel
> contains the information about the panel. (versus having pin config data
> in the DSI master, in which case the DSI master's node contains data
> about a specific DSI panel).
>
I think it still is OMAP specifics and doesn't belong in the panel 
drivers any longer. If you revisit this requirement in the CDF context 
where DSI ifc parameters should describe how to interface with a panel 
outside the SoC, there can't really be any dependencies on SoC internal 
routing. As you say, this is inside pinmux, so how can that affect the 
SoC external interface? I would suggest moving this to dispc-dsilink DT 
settings that are activated on dsilink->enable/disable. At least that is 
how I plan to solve similar STE SoC specific DSI config settings that 
are not really CDF panel generic, like some DPhy trim settings. They do 
depend on the panel and clock speed, but they are more product specific 
than panel driver specific. Then if there are these type of settings 
that every SoC have, then we could look at standardize those. But for 
starters I would try to keep it in product/board-DT per DSI link. So we 
should try to differentiate between DSI host and slave bus params and 
keep slave params in panel driver.

/BR
/Marcus

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
  2013-02-08 13:28             ` Marcus Lorentzon
@ 2013-02-08 14:02               ` Tomi Valkeinen
  -1 siblings, 0 replies; 36+ messages in thread
From: Tomi Valkeinen @ 2013-02-08 14:02 UTC (permalink / raw)
  To: Marcus Lorentzon
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa, dri-devel,
	linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	Daniel Vetter, rob, Vikas Sajjan, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki

[-- Attachment #1: Type: text/plain, Size: 3372 bytes --]

On 2013-02-08 15:28, Marcus Lorentzon wrote:

> When we do that we stop->setup->start during blanking. So our "DSS" is
> optimized to be able to do that without getting blocked. All DSI video
> mode panels (and DPI) products we have done so far have not had any
> issue with that (as long as DSI HS clock is set to continuous). I think
> this approach is less platform dependant, as long as there is no SoC
> that take more than a blanking period to reconfigure.

So do you stop, setup and start the link with CPU, and this has to be
happen during blanking? Isn't that prone to errors? Or did you mean that
the hardware handles that automatically?

In OMAP DSS there are so called shadow registers, that can be programmed
at any time. The you set a bit (GO bit), which tells the hardware to
take the new settings into use at the next vblank.

From DSI driver's perspective the link is never stopped when
reconfiguring the video timings. However, many other settings have to be
configured when the link is disabled.

>>>> In OMAP you can configure the DSI pins quite freely. We have the
>>>> following struct:
>>>>
>>>> struct omap_dsi_pin_config {
>>>>      int num_pins;
>>>>      /*
>>>>       * pin numbers in the following order:
>>>>       * clk+, clk-
>>>>       * data1+, data1-
>>>>       * data2+, data2-
>>>>       * ...
>>>>       */
>>>>      int pins[OMAP_DSS_MAX_DSI_PINS];
>>>> };
>>>>

> I think it still is OMAP specifics and doesn't belong in the panel
> drivers any longer. If you revisit this requirement in the CDF context
> where DSI ifc parameters should describe how to interface with a panel
> outside the SoC, there can't really be any dependencies on SoC internal
> routing. As you say, this is inside pinmux, so how can that affect the
> SoC external interface? I would suggest moving this to dispc-dsilink DT
> settings that are activated on dsilink->enable/disable. At least that is
> how I plan to solve similar STE SoC specific DSI config settings that
> are not really CDF panel generic, like some DPhy trim settings. They do
> depend on the panel and clock speed, but they are more product specific
> than panel driver specific. Then if there are these type of settings
> that every SoC have, then we could look at standardize those. But for
> starters I would try to keep it in product/board-DT per DSI link. So we
> should try to differentiate between DSI host and slave bus params and
> keep slave params in panel driver.

Ok, I think I was being a bit vague here. I explained the OMAP DSI
routing not because I meant that this API is specific to that, but
because it explains why this kind of routing info is needed, and a
bitmask is not enough.

If you look at the omap_dsi_pin_config struct, there's nothing OMAP
specific there (except the names =). All it tells is that this device
uses N DSI pins, and the device's clk+ function should be connected to
pin X on the DSI master, clk- should be connected to pin Y, etc. X and Y
are integers, and what they mean is specific to the DSI master.

When the DSI master is OMAP's DSI, the OMAP DSI driver does the pin
configuration as I explained. When the DSI master is something else,
say, a DSI bridge, it does whatever it needs to do (which could be
nothing) to assign a particular DSI function to a pin.

 Tomi



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 899 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
@ 2013-02-08 14:02               ` Tomi Valkeinen
  0 siblings, 0 replies; 36+ messages in thread
From: Tomi Valkeinen @ 2013-02-08 14:02 UTC (permalink / raw)
  To: Marcus Lorentzon
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa, dri-devel,
	linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	Daniel Vetter, rob, Vikas Sajjan, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki

[-- Attachment #1: Type: text/plain, Size: 3372 bytes --]

On 2013-02-08 15:28, Marcus Lorentzon wrote:

> When we do that we stop->setup->start during blanking. So our "DSS" is
> optimized to be able to do that without getting blocked. All DSI video
> mode panels (and DPI) products we have done so far have not had any
> issue with that (as long as DSI HS clock is set to continuous). I think
> this approach is less platform dependant, as long as there is no SoC
> that take more than a blanking period to reconfigure.

So do you stop, setup and start the link with CPU, and this has to be
happen during blanking? Isn't that prone to errors? Or did you mean that
the hardware handles that automatically?

In OMAP DSS there are so called shadow registers, that can be programmed
at any time. The you set a bit (GO bit), which tells the hardware to
take the new settings into use at the next vblank.

From DSI driver's perspective the link is never stopped when
reconfiguring the video timings. However, many other settings have to be
configured when the link is disabled.

>>>> In OMAP you can configure the DSI pins quite freely. We have the
>>>> following struct:
>>>>
>>>> struct omap_dsi_pin_config {
>>>>      int num_pins;
>>>>      /*
>>>>       * pin numbers in the following order:
>>>>       * clk+, clk-
>>>>       * data1+, data1-
>>>>       * data2+, data2-
>>>>       * ...
>>>>       */
>>>>      int pins[OMAP_DSS_MAX_DSI_PINS];
>>>> };
>>>>

> I think it still is OMAP specifics and doesn't belong in the panel
> drivers any longer. If you revisit this requirement in the CDF context
> where DSI ifc parameters should describe how to interface with a panel
> outside the SoC, there can't really be any dependencies on SoC internal
> routing. As you say, this is inside pinmux, so how can that affect the
> SoC external interface? I would suggest moving this to dispc-dsilink DT
> settings that are activated on dsilink->enable/disable. At least that is
> how I plan to solve similar STE SoC specific DSI config settings that
> are not really CDF panel generic, like some DPhy trim settings. They do
> depend on the panel and clock speed, but they are more product specific
> than panel driver specific. Then if there are these type of settings
> that every SoC have, then we could look at standardize those. But for
> starters I would try to keep it in product/board-DT per DSI link. So we
> should try to differentiate between DSI host and slave bus params and
> keep slave params in panel driver.

Ok, I think I was being a bit vague here. I explained the OMAP DSI
routing not because I meant that this API is specific to that, but
because it explains why this kind of routing info is needed, and a
bitmask is not enough.

If you look at the omap_dsi_pin_config struct, there's nothing OMAP
specific there (except the names =). All it tells is that this device
uses N DSI pins, and the device's clk+ function should be connected to
pin X on the DSI master, clk- should be connected to pin Y, etc. X and Y
are integers, and what they mean is specific to the DSI master.

When the DSI master is OMAP's DSI, the OMAP DSI driver does the pin
configuration as I explained. When the DSI master is something else,
say, a DSI bridge, it does whatever it needs to do (which could be
nothing) to assign a particular DSI function to a pin.

 Tomi



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 899 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
  2013-02-08 14:02               ` Tomi Valkeinen
@ 2013-02-08 14:54                 ` Marcus Lorentzon
  -1 siblings, 0 replies; 36+ messages in thread
From: Marcus Lorentzon @ 2013-02-08 14:54 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa, dri-devel,
	linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	Daniel Vetter, rob, Vikas Sajjan, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki

On 02/08/2013 03:02 PM, Tomi Valkeinen wrote:
> On 2013-02-08 15:28, Marcus Lorentzon wrote:
>
>> When we do that we stop->setup->start during blanking. So our "DSS" is
>> optimized to be able to do that without getting blocked. All DSI video
>> mode panels (and DPI) products we have done so far have not had any
>> issue with that (as long as DSI HS clock is set to continuous). I think
>> this approach is less platform dependant, as long as there is no SoC
>> that take more than a blanking period to reconfigure.
> So do you stop, setup and start the link with CPU, and this has to be
> happen during blanking? Isn't that prone to errors? Or did you mean that
> the hardware handles that automatically?
>
> In OMAP DSS there are so called shadow registers, that can be programmed
> at any time. The you set a bit (GO bit), which tells the hardware to
> take the new settings into use at the next vblank.
>
>  From DSI driver's perspective the link is never stopped when
> reconfiguring the video timings. However, many other settings have to be
> configured when the link is disabled.

Yeah, you lucky guys with the GO bit ;). No, we actually do CPU 
stop,setup,start. But since it is video mode, master is driving the sync 
so it is not a hard deadline. It is enough to restart before pixels 
start to degrade. On an LCD that is not so much time, but on an OLED it 
could be 10 secs :). Anyway, we have had several mass products with this 
soft solution and it has worked well.
>>>>> In OMAP you can configure the DSI pins quite freely. We have the
>>>>> following struct:
>>>>>
>>>>> struct omap_dsi_pin_config {
>>>>>       int num_pins;
>>>>>       /*
>>>>>        * pin numbers in the following order:
>>>>>        * clk+, clk-
>>>>>        * data1+, data1-
>>>>>        * data2+, data2-
>>>>>        * ...
>>>>>        */
>>>>>       int pins[OMAP_DSS_MAX_DSI_PINS];
>>>>> };
>>>>>
>> I think it still is OMAP specifics and doesn't belong in the panel
>> drivers any longer. If you revisit this requirement in the CDF context
>> where DSI ifc parameters should describe how to interface with a panel
>> outside the SoC, there can't really be any dependencies on SoC internal
>> routing. As you say, this is inside pinmux, so how can that affect the
>> SoC external interface? I would suggest moving this to dispc-dsilink DT
>> settings that are activated on dsilink->enable/disable. At least that is
>> how I plan to solve similar STE SoC specific DSI config settings that
>> are not really CDF panel generic, like some DPhy trim settings. They do
>> depend on the panel and clock speed, but they are more product specific
>> than panel driver specific. Then if there are these type of settings
>> that every SoC have, then we could look at standardize those. But for
>> starters I would try to keep it in product/board-DT per DSI link. So we
>> should try to differentiate between DSI host and slave bus params and
>> keep slave params in panel driver.
> Ok, I think I was being a bit vague here. I explained the OMAP DSI
> routing not because I meant that this API is specific to that, but
> because it explains why this kind of routing info is needed, and a
> bitmask is not enough.
>
> If you look at the omap_dsi_pin_config struct, there's nothing OMAP
> specific there (except the names =). All it tells is that this device
> uses N DSI pins, and the device's clk+ function should be connected to
> pin X on the DSI master, clk- should be connected to pin Y, etc. X and Y
> are integers, and what they mean is specific to the DSI master.
>
> When the DSI master is OMAP's DSI, the OMAP DSI driver does the pin
> configuration as I explained. When the DSI master is something else,
> say, a DSI bridge, it does whatever it needs to do (which could be
> nothing) to assign a particular DSI function to a pin.
>
I understand, but removing the omap prefix doesn't mean it has to go in 
the panel slave port/bus settings. I still can't see why this should be 
configuration on the panel driver and not the DSI master driver. Number 
of pins might be useful since you might start with one lane and then 
activate the rest. But partial muxing (pre pinmux) doesn't seem to be 
something the panel should control or know anything about. Sounds like 
normal platform/DT data per product/board.

/BR
/Marcus


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
@ 2013-02-08 14:54                 ` Marcus Lorentzon
  0 siblings, 0 replies; 36+ messages in thread
From: Marcus Lorentzon @ 2013-02-08 14:54 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa, dri-devel,
	linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	Daniel Vetter, rob, Vikas Sajjan, inki.dae, dh09.lee,
	ville.syrjala, s.nawrocki

On 02/08/2013 03:02 PM, Tomi Valkeinen wrote:
> On 2013-02-08 15:28, Marcus Lorentzon wrote:
>
>> When we do that we stop->setup->start during blanking. So our "DSS" is
>> optimized to be able to do that without getting blocked. All DSI video
>> mode panels (and DPI) products we have done so far have not had any
>> issue with that (as long as DSI HS clock is set to continuous). I think
>> this approach is less platform dependant, as long as there is no SoC
>> that take more than a blanking period to reconfigure.
> So do you stop, setup and start the link with CPU, and this has to be
> happen during blanking? Isn't that prone to errors? Or did you mean that
> the hardware handles that automatically?
>
> In OMAP DSS there are so called shadow registers, that can be programmed
> at any time. The you set a bit (GO bit), which tells the hardware to
> take the new settings into use at the next vblank.
>
>  From DSI driver's perspective the link is never stopped when
> reconfiguring the video timings. However, many other settings have to be
> configured when the link is disabled.

Yeah, you lucky guys with the GO bit ;). No, we actually do CPU 
stop,setup,start. But since it is video mode, master is driving the sync 
so it is not a hard deadline. It is enough to restart before pixels 
start to degrade. On an LCD that is not so much time, but on an OLED it 
could be 10 secs :). Anyway, we have had several mass products with this 
soft solution and it has worked well.
>>>>> In OMAP you can configure the DSI pins quite freely. We have the
>>>>> following struct:
>>>>>
>>>>> struct omap_dsi_pin_config {
>>>>>       int num_pins;
>>>>>       /*
>>>>>        * pin numbers in the following order:
>>>>>        * clk+, clk-
>>>>>        * data1+, data1-
>>>>>        * data2+, data2-
>>>>>        * ...
>>>>>        */
>>>>>       int pins[OMAP_DSS_MAX_DSI_PINS];
>>>>> };
>>>>>
>> I think it still is OMAP specifics and doesn't belong in the panel
>> drivers any longer. If you revisit this requirement in the CDF context
>> where DSI ifc parameters should describe how to interface with a panel
>> outside the SoC, there can't really be any dependencies on SoC internal
>> routing. As you say, this is inside pinmux, so how can that affect the
>> SoC external interface? I would suggest moving this to dispc-dsilink DT
>> settings that are activated on dsilink->enable/disable. At least that is
>> how I plan to solve similar STE SoC specific DSI config settings that
>> are not really CDF panel generic, like some DPhy trim settings. They do
>> depend on the panel and clock speed, but they are more product specific
>> than panel driver specific. Then if there are these type of settings
>> that every SoC have, then we could look at standardize those. But for
>> starters I would try to keep it in product/board-DT per DSI link. So we
>> should try to differentiate between DSI host and slave bus params and
>> keep slave params in panel driver.
> Ok, I think I was being a bit vague here. I explained the OMAP DSI
> routing not because I meant that this API is specific to that, but
> because it explains why this kind of routing info is needed, and a
> bitmask is not enough.
>
> If you look at the omap_dsi_pin_config struct, there's nothing OMAP
> specific there (except the names =). All it tells is that this device
> uses N DSI pins, and the device's clk+ function should be connected to
> pin X on the DSI master, clk- should be connected to pin Y, etc. X and Y
> are integers, and what they mean is specific to the DSI master.
>
> When the DSI master is OMAP's DSI, the OMAP DSI driver does the pin
> configuration as I explained. When the DSI master is something else,
> say, a DSI bridge, it does whatever it needs to do (which could be
> nothing) to assign a particular DSI function to a pin.
>
I understand, but removing the omap prefix doesn't mean it has to go in 
the panel slave port/bus settings. I still can't see why this should be 
configuration on the panel driver and not the DSI master driver. Number 
of pins might be useful since you might start with one lane and then 
activate the rest. But partial muxing (pre pinmux) doesn't seem to be 
something the panel should control or know anything about. Sounds like 
normal platform/DT data per product/board.

/BR
/Marcus

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
  2013-02-08 14:54                 ` Marcus Lorentzon
@ 2013-02-11  8:21                   ` Tomi Valkeinen
  -1 siblings, 0 replies; 36+ messages in thread
From: Tomi Valkeinen @ 2013-02-11  8:21 UTC (permalink / raw)
  To: Marcus Lorentzon
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa, dri-devel,
	linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	Daniel Vetter, Vikas Sajjan, inki.dae, dh09.lee, ville.syrjala,
	s.nawrocki

[-- Attachment #1: Type: text/plain, Size: 4218 bytes --]

On 2013-02-08 16:54, Marcus Lorentzon wrote:
> On 02/08/2013 03:02 PM, Tomi Valkeinen wrote:
>> On 2013-02-08 15:28, Marcus Lorentzon wrote:
>>
>>> When we do that we stop->setup->start during blanking. So our "DSS" is
>>> optimized to be able to do that without getting blocked. All DSI video
>>> mode panels (and DPI) products we have done so far have not had any
>>> issue with that (as long as DSI HS clock is set to continuous). I think
>>> this approach is less platform dependant, as long as there is no SoC
>>> that take more than a blanking period to reconfigure.
>> So do you stop, setup and start the link with CPU, and this has to be
>> happen during blanking? Isn't that prone to errors? Or did you mean that
>> the hardware handles that automatically?
>>
>> In OMAP DSS there are so called shadow registers, that can be programmed
>> at any time. The you set a bit (GO bit), which tells the hardware to
>> take the new settings into use at the next vblank.
>>
>>  From DSI driver's perspective the link is never stopped when
>> reconfiguring the video timings. However, many other settings have to be
>> configured when the link is disabled.
> 
> Yeah, you lucky guys with the GO bit ;). No, we actually do CPU
> stop,setup,start. But since it is video mode, master is driving the sync
> so it is not a hard deadline. It is enough to restart before pixels
> start to degrade. On an LCD that is not so much time, but on an OLED it
> could be 10 secs :). Anyway, we have had several mass products with this
> soft solution and it has worked well.

Ah, ok. But in that case what you said in an earlier mail is not quite
correct: "I think this approach is less platform dependant, as long as
there is no SoC that take more than a blanking period to reconfigure.".
So in your approach the reconfiguration doesn't have to be done inside
the blanking period, but before the panel's picture starts to fade?

I don't know... It's early Monday morning, and not enough coffee yet,
but I get a bit uneasy feeling if I think that your method of
reconfiguring would be the only think allowed by the CDF API.

Some SoCs do support reconfiguring on the fly, without disabling the
link. It would not be nice if we didn't allow this to happen. And
actually, we're not only talking about SoCs here, but also any display
devices, like external buffer chips etc. They may also offer means to
change configs on the fly.

Well, I don't have any hard point about this at the moment, but I think
we should think different approaches how the configuration can be done.

> I understand, but removing the omap prefix doesn't mean it has to go in
> the panel slave port/bus settings. I still can't see why this should be
> configuration on the panel driver and not the DSI master driver. Number
> of pins might be useful since you might start with one lane and then
> activate the rest. But partial muxing (pre pinmux) doesn't seem to be
> something the panel should control or know anything about. Sounds like
> normal platform/DT data per product/board.

I think one case where this kind of pin configuration is needed, and
which also dictates that all panel related configuration has to be in
the panel's data, not in the DSI master's data, is hotplug.

If you have a board that has two panels connected to the same video
output, probably via some kind of mux, at least for the sensitive pins
like DSI, only one of the panels can be enabled at a time. The panels
can have different wiring, and thus the panel driver would need to
configure everything related to the bus when it's starting up.

The same also happens if you have a true hotplug, i.e. you can remove
the panel totally and plug in a new one. Again the wiring can be
different, and needs to be set up.

And, as I said, this means that all relevant data about the video bus
has to be in the panel's data, so that each panel can have its own set
of, say, pin configuration.

Hotplug is not a use case we should aim to support for the CDF v1, but I
think we should strive not to prevent hotplug either. So if we can
design the API so that hotplug is possible, I say let's do that.

 Tomi



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 899 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
@ 2013-02-11  8:21                   ` Tomi Valkeinen
  0 siblings, 0 replies; 36+ messages in thread
From: Tomi Valkeinen @ 2013-02-11  8:21 UTC (permalink / raw)
  To: Marcus Lorentzon
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa, dri-devel,
	linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	Daniel Vetter, Vikas Sajjan, inki.dae, dh09.lee, ville.syrjala,
	s.nawrocki

[-- Attachment #1: Type: text/plain, Size: 4218 bytes --]

On 2013-02-08 16:54, Marcus Lorentzon wrote:
> On 02/08/2013 03:02 PM, Tomi Valkeinen wrote:
>> On 2013-02-08 15:28, Marcus Lorentzon wrote:
>>
>>> When we do that we stop->setup->start during blanking. So our "DSS" is
>>> optimized to be able to do that without getting blocked. All DSI video
>>> mode panels (and DPI) products we have done so far have not had any
>>> issue with that (as long as DSI HS clock is set to continuous). I think
>>> this approach is less platform dependant, as long as there is no SoC
>>> that take more than a blanking period to reconfigure.
>> So do you stop, setup and start the link with CPU, and this has to be
>> happen during blanking? Isn't that prone to errors? Or did you mean that
>> the hardware handles that automatically?
>>
>> In OMAP DSS there are so called shadow registers, that can be programmed
>> at any time. The you set a bit (GO bit), which tells the hardware to
>> take the new settings into use at the next vblank.
>>
>>  From DSI driver's perspective the link is never stopped when
>> reconfiguring the video timings. However, many other settings have to be
>> configured when the link is disabled.
> 
> Yeah, you lucky guys with the GO bit ;). No, we actually do CPU
> stop,setup,start. But since it is video mode, master is driving the sync
> so it is not a hard deadline. It is enough to restart before pixels
> start to degrade. On an LCD that is not so much time, but on an OLED it
> could be 10 secs :). Anyway, we have had several mass products with this
> soft solution and it has worked well.

Ah, ok. But in that case what you said in an earlier mail is not quite
correct: "I think this approach is less platform dependant, as long as
there is no SoC that take more than a blanking period to reconfigure.".
So in your approach the reconfiguration doesn't have to be done inside
the blanking period, but before the panel's picture starts to fade?

I don't know... It's early Monday morning, and not enough coffee yet,
but I get a bit uneasy feeling if I think that your method of
reconfiguring would be the only think allowed by the CDF API.

Some SoCs do support reconfiguring on the fly, without disabling the
link. It would not be nice if we didn't allow this to happen. And
actually, we're not only talking about SoCs here, but also any display
devices, like external buffer chips etc. They may also offer means to
change configs on the fly.

Well, I don't have any hard point about this at the moment, but I think
we should think different approaches how the configuration can be done.

> I understand, but removing the omap prefix doesn't mean it has to go in
> the panel slave port/bus settings. I still can't see why this should be
> configuration on the panel driver and not the DSI master driver. Number
> of pins might be useful since you might start with one lane and then
> activate the rest. But partial muxing (pre pinmux) doesn't seem to be
> something the panel should control or know anything about. Sounds like
> normal platform/DT data per product/board.

I think one case where this kind of pin configuration is needed, and
which also dictates that all panel related configuration has to be in
the panel's data, not in the DSI master's data, is hotplug.

If you have a board that has two panels connected to the same video
output, probably via some kind of mux, at least for the sensitive pins
like DSI, only one of the panels can be enabled at a time. The panels
can have different wiring, and thus the panel driver would need to
configure everything related to the bus when it's starting up.

The same also happens if you have a true hotplug, i.e. you can remove
the panel totally and plug in a new one. Again the wiring can be
different, and needs to be set up.

And, as I said, this means that all relevant data about the video bus
has to be in the panel's data, so that each panel can have its own set
of, say, pin configuration.

Hotplug is not a use case we should aim to support for the CDF v1, but I
think we should strive not to prevent hotplug either. So if we can
design the API so that hotplug is possible, I say let's do that.

 Tomi



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 899 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
  2013-02-11  8:21                   ` Tomi Valkeinen
@ 2013-02-11  9:31                     ` Marcus Lorentzon
  -1 siblings, 0 replies; 36+ messages in thread
From: Marcus Lorentzon @ 2013-02-11  9:31 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa, dri-devel,
	linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	Daniel Vetter, Vikas Sajjan, inki.dae, dh09.lee, ville.syrjala,
	s.nawrocki

On 02/11/2013 09:21 AM, Tomi Valkeinen wrote:
> On 2013-02-08 16:54, Marcus Lorentzon wrote:
>> On 02/08/2013 03:02 PM, Tomi Valkeinen wrote:
>>> On 2013-02-08 15:28, Marcus Lorentzon wrote:
>>>
>>>> When we do that we stop->setup->start during blanking. So our "DSS" is
>>>> optimized to be able to do that without getting blocked. All DSI video
>>>> mode panels (and DPI) products we have done so far have not had any
>>>> issue with that (as long as DSI HS clock is set to continuous). I think
>>>> this approach is less platform dependant, as long as there is no SoC
>>>> that take more than a blanking period to reconfigure.
>>> So do you stop, setup and start the link with CPU, and this has to be
>>> happen during blanking? Isn't that prone to errors? Or did you mean that
>>> the hardware handles that automatically?
>>>
>>> In OMAP DSS there are so called shadow registers, that can be programmed
>>> at any time. The you set a bit (GO bit), which tells the hardware to
>>> take the new settings into use at the next vblank.
>>>
>>>   From DSI driver's perspective the link is never stopped when
>>> reconfiguring the video timings. However, many other settings have to be
>>> configured when the link is disabled.
>> Yeah, you lucky guys with the GO bit ;). No, we actually do CPU
>> stop,setup,start. But since it is video mode, master is driving the sync
>> so it is not a hard deadline. It is enough to restart before pixels
>> start to degrade. On an LCD that is not so much time, but on an OLED it
>> could be 10 secs :). Anyway, we have had several mass products with this
>> soft solution and it has worked well.
> Ah, ok. But in that case what you said in an earlier mail is not quite
> correct: "I think this approach is less platform dependant, as long as
> there is no SoC that take more than a blanking period to reconfigure.".
> So in your approach the reconfiguration doesn't have to be done inside
> the blanking period, but before the panel's picture starts to fade?
>
> I don't know... It's early Monday morning, and not enough coffee yet,
> but I get a bit uneasy feeling if I think that your method of
> reconfiguring would be the only think allowed by the CDF API.
>
> Some SoCs do support reconfiguring on the fly, without disabling the
> link. It would not be nice if we didn't allow this to happen. And
> actually, we're not only talking about SoCs here, but also any display
> devices, like external buffer chips etc. They may also offer means to
> change configs on the fly.
>
> Well, I don't have any hard point about this at the moment, but I think
> we should think different approaches how the configuration can be done.

Ok, so what about a compromise which I think would work for "both" HWs 
... we allow the "configure" operation during video on, then each HW 
driver could decide if this means you have to stop or not to do the 
changes required. But then it is also important that we keep all config 
in one struct and not split it up. Otherwise HW like ours that has so be 
stopped could need to stop once for each setting/operation called.
So in short, allow configure(bus_params) during video on, keep all 
bus_params in the struct. It is in kernel struct so it can easily be 
changed/refactored.
>
>> I understand, but removing the omap prefix doesn't mean it has to go in
>> the panel slave port/bus settings. I still can't see why this should be
>> configuration on the panel driver and not the DSI master driver. Number
>> of pins might be useful since you might start with one lane and then
>> activate the rest. But partial muxing (pre pinmux) doesn't seem to be
>> something the panel should control or know anything about. Sounds like
>> normal platform/DT data per product/board.
> I think one case where this kind of pin configuration is needed, and
> which also dictates that all panel related configuration has to be in
> the panel's data, not in the DSI master's data, is hotplug.
>
> If you have a board that has two panels connected to the same video
> output, probably via some kind of mux, at least for the sensitive pins
> like DSI, only one of the panels can be enabled at a time. The panels
> can have different wiring, and thus the panel driver would need to
> configure everything related to the bus when it's starting up.
>
> The same also happens if you have a true hotplug, i.e. you can remove
> the panel totally and plug in a new one. Again the wiring can be
> different, and needs to be set up.
>
> And, as I said, this means that all relevant data about the video bus
> has to be in the panel's data, so that each panel can have its own set
> of, say, pin configuration.
>
> Hotplug is not a use case we should aim to support for the CDF v1, but I
> think we should strive not to prevent hotplug either. So if we can
> design the API so that hotplug is possible, I say let's do that.
>
Again, this probing and bus muxing is platform/bus specific and not 
panel specific. Any panel of that type will only ever work on Omap (or 
someone else implementing the same muxing features) as I see it. So why 
not just put that config on the bus master, dispc? I still can't see how 
this is panel config. You are only pushing CDF API and meta data to 
describe something that is only needed by one bus master. I have never 
seen any DSI slave that can change their pin config. And since there is 
no generic hot plug detect of DSI panels, at least not before DSI bus is 
available, I have to assume this probing it very platform specific. We 
have some products that provide 1-2 gpios to specify what panel is 
available, some use I2C sensor probing to then assume the panel plugged.
At least in the first step I don't think this type of hot plug should be 
in the API. Then once the base panel driver is in we could discuss 
different solutions for you hot plug scenario.

/BR
/Marcus

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
@ 2013-02-11  9:31                     ` Marcus Lorentzon
  0 siblings, 0 replies; 36+ messages in thread
From: Marcus Lorentzon @ 2013-02-11  9:31 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa, dri-devel,
	linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	Daniel Vetter, Vikas Sajjan, inki.dae, dh09.lee, ville.syrjala,
	s.nawrocki

On 02/11/2013 09:21 AM, Tomi Valkeinen wrote:
> On 2013-02-08 16:54, Marcus Lorentzon wrote:
>> On 02/08/2013 03:02 PM, Tomi Valkeinen wrote:
>>> On 2013-02-08 15:28, Marcus Lorentzon wrote:
>>>
>>>> When we do that we stop->setup->start during blanking. So our "DSS" is
>>>> optimized to be able to do that without getting blocked. All DSI video
>>>> mode panels (and DPI) products we have done so far have not had any
>>>> issue with that (as long as DSI HS clock is set to continuous). I think
>>>> this approach is less platform dependant, as long as there is no SoC
>>>> that take more than a blanking period to reconfigure.
>>> So do you stop, setup and start the link with CPU, and this has to be
>>> happen during blanking? Isn't that prone to errors? Or did you mean that
>>> the hardware handles that automatically?
>>>
>>> In OMAP DSS there are so called shadow registers, that can be programmed
>>> at any time. The you set a bit (GO bit), which tells the hardware to
>>> take the new settings into use at the next vblank.
>>>
>>>   From DSI driver's perspective the link is never stopped when
>>> reconfiguring the video timings. However, many other settings have to be
>>> configured when the link is disabled.
>> Yeah, you lucky guys with the GO bit ;). No, we actually do CPU
>> stop,setup,start. But since it is video mode, master is driving the sync
>> so it is not a hard deadline. It is enough to restart before pixels
>> start to degrade. On an LCD that is not so much time, but on an OLED it
>> could be 10 secs :). Anyway, we have had several mass products with this
>> soft solution and it has worked well.
> Ah, ok. But in that case what you said in an earlier mail is not quite
> correct: "I think this approach is less platform dependant, as long as
> there is no SoC that take more than a blanking period to reconfigure.".
> So in your approach the reconfiguration doesn't have to be done inside
> the blanking period, but before the panel's picture starts to fade?
>
> I don't know... It's early Monday morning, and not enough coffee yet,
> but I get a bit uneasy feeling if I think that your method of
> reconfiguring would be the only think allowed by the CDF API.
>
> Some SoCs do support reconfiguring on the fly, without disabling the
> link. It would not be nice if we didn't allow this to happen. And
> actually, we're not only talking about SoCs here, but also any display
> devices, like external buffer chips etc. They may also offer means to
> change configs on the fly.
>
> Well, I don't have any hard point about this at the moment, but I think
> we should think different approaches how the configuration can be done.

Ok, so what about a compromise which I think would work for "both" HWs 
... we allow the "configure" operation during video on, then each HW 
driver could decide if this means you have to stop or not to do the 
changes required. But then it is also important that we keep all config 
in one struct and not split it up. Otherwise HW like ours that has so be 
stopped could need to stop once for each setting/operation called.
So in short, allow configure(bus_params) during video on, keep all 
bus_params in the struct. It is in kernel struct so it can easily be 
changed/refactored.
>
>> I understand, but removing the omap prefix doesn't mean it has to go in
>> the panel slave port/bus settings. I still can't see why this should be
>> configuration on the panel driver and not the DSI master driver. Number
>> of pins might be useful since you might start with one lane and then
>> activate the rest. But partial muxing (pre pinmux) doesn't seem to be
>> something the panel should control or know anything about. Sounds like
>> normal platform/DT data per product/board.
> I think one case where this kind of pin configuration is needed, and
> which also dictates that all panel related configuration has to be in
> the panel's data, not in the DSI master's data, is hotplug.
>
> If you have a board that has two panels connected to the same video
> output, probably via some kind of mux, at least for the sensitive pins
> like DSI, only one of the panels can be enabled at a time. The panels
> can have different wiring, and thus the panel driver would need to
> configure everything related to the bus when it's starting up.
>
> The same also happens if you have a true hotplug, i.e. you can remove
> the panel totally and plug in a new one. Again the wiring can be
> different, and needs to be set up.
>
> And, as I said, this means that all relevant data about the video bus
> has to be in the panel's data, so that each panel can have its own set
> of, say, pin configuration.
>
> Hotplug is not a use case we should aim to support for the CDF v1, but I
> think we should strive not to prevent hotplug either. So if we can
> design the API so that hotplug is possible, I say let's do that.
>
Again, this probing and bus muxing is platform/bus specific and not 
panel specific. Any panel of that type will only ever work on Omap (or 
someone else implementing the same muxing features) as I see it. So why 
not just put that config on the bus master, dispc? I still can't see how 
this is panel config. You are only pushing CDF API and meta data to 
describe something that is only needed by one bus master. I have never 
seen any DSI slave that can change their pin config. And since there is 
no generic hot plug detect of DSI panels, at least not before DSI bus is 
available, I have to assume this probing it very platform specific. We 
have some products that provide 1-2 gpios to specify what panel is 
available, some use I2C sensor probing to then assume the panel plugged.
At least in the first step I don't think this type of hot plug should be 
in the API. Then once the base panel driver is in we could discuss 
different solutions for you hot plug scenario.

/BR
/Marcus

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
  2013-02-11  9:31                     ` Marcus Lorentzon
@ 2013-02-11 10:14                       ` Tomi Valkeinen
  -1 siblings, 0 replies; 36+ messages in thread
From: Tomi Valkeinen @ 2013-02-11 10:14 UTC (permalink / raw)
  To: Marcus Lorentzon
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa, dri-devel,
	linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	Daniel Vetter, Vikas Sajjan, inki.dae, dh09.lee, ville.syrjala,
	s.nawrocki

[-- Attachment #1: Type: text/plain, Size: 4844 bytes --]

On 2013-02-11 11:31, Marcus Lorentzon wrote:

> Ok, so what about a compromise which I think would work for "both" HWs
> ... we allow the "configure" operation during video on, then each HW
> driver could decide if this means you have to stop or not to do the
> changes required. But then it is also important that we keep all config
> in one struct and not split it up. Otherwise HW like ours that has so be
> stopped could need to stop once for each setting/operation called.
> So in short, allow configure(bus_params) during video on, keep all
> bus_params in the struct. It is in kernel struct so it can easily be
> changed/refactored.

Right, we need some way to group the configuration parameters. Either
one big struct, or something like start_config() and end_config(). I
think we could also think what parameters make no sense to be configured
on the fly, and possibly have those separately. Although it may be a bit
difficult to think of all the (weird) use cases.

> Again, this probing and bus muxing is platform/bus specific and not
> panel specific. Any panel of that type will only ever work on Omap (or

The parameters used for configuration itself is platform specific, and
that's why it needs to be defined in the DT data. But the API itself is
not platform specific. The parameters are information about how the
panel is connected, just like, say, number of data lines is for DPI.
Which is also something I think should be configured by the panel.

> someone else implementing the same muxing features) as I see it. So why

No, it works for everyone. Well, at least I still don't see anything
omap or platform specific in the API. Of course, if the platform/device
doesn't support modifying the pin functions, then the function does nothing.

> not just put that config on the bus master, dispc? I still can't see how
> this is panel config. You are only pushing CDF API and meta data to
> describe something that is only needed by one bus master. I have never

The information about how the panel is connected (the wiring) has to be
somewhere in the DT data. We could have the info in the DSI master or in
the DSI slave. Or, in some platforms where the DSS is not involved in
the muxing/config, the info could be totally separate, in the boards
pinmuxing data.

I think all those work ok for normal cases without hotplug. But if you
have hotplug, then you need separate pinmuxing/config data for each panel.

You could possibly have a list of panels in your DSI master node,
containing the muxing data, but that sounds rather hacky, and also very
hardcoded, i.e. you need to know each panel that is ever going to be
connected.

If, on the other hand, you have the info in the panel node, it "just
works". When a new panel is connected, the relevant panel DT data comes
from somewhere (it's not relevant where), and it tells the DSI master
how it's connected.

Something like this is probably needed for the BeagleBone capes, if you
have happened to follow the discussion. Although it could be argued that
the capes may perhaps be not runtime hotswappable, and thus the
configuration can be done only once during boot after the cape has been
probed. But I'd rather not design the API so that we prevent hot swapping.

> seen any DSI slave that can change their pin config. And since there is

Well, if omap is the only SoC/device out there that supports configuring
the pin functions, and everybody is against the API, I'm not going to
press it.

But then again, I think similar configuration support may be needed even
for the normal pinmuxing, even in the case where you can't reconfigure
the DSI pin functions. You still need to mux the pins (perhaps, say,
between DSI and a GPIO), depending on how many lanes the panel uses.

In fact, speaking about all pins in general, I'm not very fond of having
a static pinmuxing in the board DT data, handled by the board setup
code. I think generally the pins should be muxed to safe-mode by the
board setup code, and then configured to their proper function by the
driver when it is initializing.

> no generic hot plug detect of DSI panels, at least not before DSI bus is
> available, I have to assume this probing it very platform specific. We
> have some products that provide 1-2 gpios to specify what panel is
> available, some use I2C sensor probing to then assume the panel plugged.

Yes, the hotplug mechanism is platform/board specific. But that's not
relevant here.

> At least in the first step I don't think this type of hot plug should be
> in the API. Then once the base panel driver is in we could discuss
> different solutions for you hot plug scenario.

Yes, as I said, I also think we shouldn't aim for hotplug in the v1. But
we also shouldn't prevent it.

 Tomi



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 899 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 0/4] Common Display Framework-TF
@ 2013-02-11 10:14                       ` Tomi Valkeinen
  0 siblings, 0 replies; 36+ messages in thread
From: Tomi Valkeinen @ 2013-02-11 10:14 UTC (permalink / raw)
  To: Marcus Lorentzon
  Cc: Tomasz Figa, Laurent Pinchart, Tomasz Figa, dri-devel,
	linux-fbdev, linux-samsung-soc, kyungmin.park, m.szyprowski,
	Daniel Vetter, Vikas Sajjan, inki.dae, dh09.lee, ville.syrjala,
	s.nawrocki

[-- Attachment #1: Type: text/plain, Size: 4844 bytes --]

On 2013-02-11 11:31, Marcus Lorentzon wrote:

> Ok, so what about a compromise which I think would work for "both" HWs
> ... we allow the "configure" operation during video on, then each HW
> driver could decide if this means you have to stop or not to do the
> changes required. But then it is also important that we keep all config
> in one struct and not split it up. Otherwise HW like ours that has so be
> stopped could need to stop once for each setting/operation called.
> So in short, allow configure(bus_params) during video on, keep all
> bus_params in the struct. It is in kernel struct so it can easily be
> changed/refactored.

Right, we need some way to group the configuration parameters. Either
one big struct, or something like start_config() and end_config(). I
think we could also think what parameters make no sense to be configured
on the fly, and possibly have those separately. Although it may be a bit
difficult to think of all the (weird) use cases.

> Again, this probing and bus muxing is platform/bus specific and not
> panel specific. Any panel of that type will only ever work on Omap (or

The parameters used for configuration itself is platform specific, and
that's why it needs to be defined in the DT data. But the API itself is
not platform specific. The parameters are information about how the
panel is connected, just like, say, number of data lines is for DPI.
Which is also something I think should be configured by the panel.

> someone else implementing the same muxing features) as I see it. So why

No, it works for everyone. Well, at least I still don't see anything
omap or platform specific in the API. Of course, if the platform/device
doesn't support modifying the pin functions, then the function does nothing.

> not just put that config on the bus master, dispc? I still can't see how
> this is panel config. You are only pushing CDF API and meta data to
> describe something that is only needed by one bus master. I have never

The information about how the panel is connected (the wiring) has to be
somewhere in the DT data. We could have the info in the DSI master or in
the DSI slave. Or, in some platforms where the DSS is not involved in
the muxing/config, the info could be totally separate, in the boards
pinmuxing data.

I think all those work ok for normal cases without hotplug. But if you
have hotplug, then you need separate pinmuxing/config data for each panel.

You could possibly have a list of panels in your DSI master node,
containing the muxing data, but that sounds rather hacky, and also very
hardcoded, i.e. you need to know each panel that is ever going to be
connected.

If, on the other hand, you have the info in the panel node, it "just
works". When a new panel is connected, the relevant panel DT data comes
from somewhere (it's not relevant where), and it tells the DSI master
how it's connected.

Something like this is probably needed for the BeagleBone capes, if you
have happened to follow the discussion. Although it could be argued that
the capes may perhaps be not runtime hotswappable, and thus the
configuration can be done only once during boot after the cape has been
probed. But I'd rather not design the API so that we prevent hot swapping.

> seen any DSI slave that can change their pin config. And since there is

Well, if omap is the only SoC/device out there that supports configuring
the pin functions, and everybody is against the API, I'm not going to
press it.

But then again, I think similar configuration support may be needed even
for the normal pinmuxing, even in the case where you can't reconfigure
the DSI pin functions. You still need to mux the pins (perhaps, say,
between DSI and a GPIO), depending on how many lanes the panel uses.

In fact, speaking about all pins in general, I'm not very fond of having
a static pinmuxing in the board DT data, handled by the board setup
code. I think generally the pins should be muxed to safe-mode by the
board setup code, and then configured to their proper function by the
driver when it is initializing.

> no generic hot plug detect of DSI panels, at least not before DSI bus is
> available, I have to assume this probing it very platform specific. We
> have some products that provide 1-2 gpios to specify what panel is
> available, some use I2C sensor probing to then assume the panel plugged.

Yes, the hotplug mechanism is platform/board specific. But that's not
relevant here.

> At least in the first step I don't think this type of hot plug should be
> in the API. Then once the base panel driver is in we could discuss
> different solutions for you hot plug scenario.

Yes, as I said, I also think we shouldn't aim for hotplug in the v1. But
we also shouldn't prevent it.

 Tomi



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 899 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

end of thread, other threads:[~2013-02-11 10:14 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-01-30 15:38 [RFC PATCH 0/4] Common Display Framework-TF Tomasz Figa
2013-01-30 15:38 ` Tomasz Figa
2013-01-30 15:39 ` [RFC PATCH 1/4] video: add display-core Tomasz Figa
2013-01-30 15:39   ` Tomasz Figa
2013-01-30 15:39 ` [RFC PATCH 2/4] video: add makefile & kconfig Tomasz Figa
2013-01-30 15:39   ` Tomasz Figa
2013-01-30 15:39 ` [RFC PATCH 3/4] video: display: Add exynos-dsi video source driver Tomasz Figa
2013-01-30 15:39   ` Tomasz Figa
2013-01-30 15:39 ` [RFC PATCH 4/4] video: display: Add Samsung s6e8ax0 display panel driver Tomasz Figa
2013-01-30 15:39   ` Tomasz Figa
2013-02-07  9:34   ` Vikas Sajjan
2013-02-07  9:46     ` Vikas Sajjan
2013-02-07 10:18     ` Tomasz Figa
2013-02-07 10:18       ` Tomasz Figa
2013-02-02 10:39 ` [RFC PATCH 0/4] Common Display Framework-TF Laurent Pinchart
2013-02-02 10:39   ` Laurent Pinchart
2013-02-03 19:17   ` Tomasz Figa
2013-02-03 19:17     ` Tomasz Figa
2013-02-08 11:40     ` Tomi Valkeinen
2013-02-08 11:40       ` Tomi Valkeinen
2013-02-08 12:40       ` Marcus Lorentzon
2013-02-08 12:40         ` Marcus Lorentzon
2013-02-08 13:04         ` Tomi Valkeinen
2013-02-08 13:04           ` Tomi Valkeinen
2013-02-08 13:28           ` Marcus Lorentzon
2013-02-08 13:28             ` Marcus Lorentzon
2013-02-08 14:02             ` Tomi Valkeinen
2013-02-08 14:02               ` Tomi Valkeinen
2013-02-08 14:54               ` Marcus Lorentzon
2013-02-08 14:54                 ` Marcus Lorentzon
2013-02-11  8:21                 ` Tomi Valkeinen
2013-02-11  8:21                   ` Tomi Valkeinen
2013-02-11  9:31                   ` Marcus Lorentzon
2013-02-11  9:31                     ` Marcus Lorentzon
2013-02-11 10:14                     ` Tomi Valkeinen
2013-02-11 10:14                       ` Tomi Valkeinen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.