All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V2 0/9] drm: exynos: few patches to enhance bridge chip support
@ 2014-04-21 22:39 Ajay Kumar
  2014-04-21 22:39 ` [PATCH V2 1/9] drm/exynos: dp: support hotplug detection via GPIO Ajay Kumar
                   ` (8 more replies)
  0 siblings, 9 replies; 58+ messages in thread
From: Ajay Kumar @ 2014-04-21 22:39 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: inki.dae, seanpaul, ajaynumb, joshi, prashanth.g, Ajay Kumar

This series is based on exynos-drm-next-todo branch of Inki Dae's tree at:
git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git

This set of drm patches are needed to support bridge chips and
eDP/LVDS panels with exynos_dp.

Bridge chip driver for parade DP to LVDS converter is also added.

For testing, I have used exynos5250-snow, and exynos5420-peach-pit boards
along with a few local arch side patches.

Andrew Bresticker (1):
  [PATCH 1/9] drm/exynos: dp: support hotplug detection via GPIO

Ajay Kumar (7):
  [PATCH 2/9] drm/panel: add pre_enable and post_disable routines
  [PATCH 3/9] drm/panel: Add driver for exynos_dp based panels
  [PATCH 4/9] drm/exynos: add exynos_dp_panel driver registration to drm driver
  [PATCH 5/9] drm/exynos: dp: modify driver to support drm_panel
  [PATCH 7/9] drm/bridge: ptn3460: add drm_panel controls
  [PATCH 8/9] drm/bridge: Add PS8622 bridge driver
  [PATCH 9/9] drm/exynos: Add ps8622 lvds bridge discovery to DP driver

Rahul Sharma (1):
  [PATCH 6/9] drm/bridge: ptn3460: enable polling based detection

Major changes since V1:
	Add post_disable callback and add bridge chip driver for parade DP
	to LVDS converter

 .../devicetree/bindings/panel/exynos-dp-panel.txt  |   45 ++
 .../devicetree/bindings/video/exynos_dp.txt        |    4 +
 drivers/gpu/drm/bridge/Kconfig                     |    8 +
 drivers/gpu/drm/bridge/Makefile                    |    1 +
 drivers/gpu/drm/bridge/ps8622.c                    |  566 ++++++++++++++++++++
 drivers/gpu/drm/bridge/ptn3460.c                   |   21 +-
 drivers/gpu/drm/exynos/Kconfig                     |    1 +
 drivers/gpu/drm/exynos/exynos_dp_core.c            |   75 ++-
 drivers/gpu/drm/exynos/exynos_dp_core.h            |    2 +
 drivers/gpu/drm/exynos/exynos_dp_reg.c             |   44 +-
 drivers/gpu/drm/exynos/exynos_drm_drv.c            |   15 +
 drivers/gpu/drm/exynos/exynos_drm_drv.h            |    1 +
 drivers/gpu/drm/panel/Kconfig                      |    9 +
 drivers/gpu/drm/panel/Makefile                     |    1 +
 drivers/gpu/drm/panel/panel-exynos-dp.c            |  251 +++++++++
 include/drm/bridge/ps8622.h                        |   42 ++
 include/drm/bridge/ptn3460.h                       |    6 +-
 include/drm/drm_panel.h                            |   18 +
 18 files changed, 1088 insertions(+), 22 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/panel/exynos-dp-panel.txt
 create mode 100644 drivers/gpu/drm/bridge/ps8622.c
 create mode 100644 drivers/gpu/drm/panel/panel-exynos-dp.c
 create mode 100644 include/drm/bridge/ps8622.h

-- 
1.7.9.5

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

* [PATCH V2 1/9] drm/exynos: dp: support hotplug detection via GPIO
  2014-04-21 22:39 [PATCH V2 0/9] drm: exynos: few patches to enhance bridge chip support Ajay Kumar
@ 2014-04-21 22:39 ` Ajay Kumar
  2014-04-22  7:13   ` Jingoo Han
  2014-04-21 22:39 ` [PATCH V2 2/9] drm/panel: add pre_enable and post_disable routines Ajay Kumar
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 58+ messages in thread
From: Ajay Kumar @ 2014-04-21 22:39 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: inki.dae, seanpaul, ajaynumb, joshi, prashanth.g,
	Andrew Bresticker, Rahul Sharma, Ajay Kumar

From: Andrew Bresticker <abrestic@chromium.org>

Certain bridge chips use a GPIO to indicate the cable status instead
of the I_DP_HPD pin.  This adds an optional device-tree property,
"samsung,hpd-gpio", to the exynos-dp controller which indicates that
the specified GPIO should be used for hotplug detection.
The GPIO is then set up as an edge-triggered interrupt where the
rising edge indicates hotplug-in and the falling edge indicates hotplug-out.

Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
---
Changes since V1:
	Address reiew comments from Jingoo Han

 .../devicetree/bindings/video/exynos_dp.txt        |    4 ++
 drivers/gpu/drm/exynos/exynos_dp_core.c            |   32 ++++++++++++--
 drivers/gpu/drm/exynos/exynos_dp_core.h            |    1 +
 drivers/gpu/drm/exynos/exynos_dp_reg.c             |   44 ++++++++++++++------
 4 files changed, 66 insertions(+), 15 deletions(-)

diff --git a/Documentation/devicetree/bindings/video/exynos_dp.txt b/Documentation/devicetree/bindings/video/exynos_dp.txt
index 57ccdde..53dbccf 100644
--- a/Documentation/devicetree/bindings/video/exynos_dp.txt
+++ b/Documentation/devicetree/bindings/video/exynos_dp.txt
@@ -62,6 +62,10 @@ Optional properties for dp-controller:
 	-hsync-active-high:
 		HSYNC polarity configuration.
 			High if defined, Low if not defined
+	-samsung,hpd-gpio:
+		Hotplug detect GPIO.
+			Indicates which GPIO should be used for hotplug
+			detection
 
 Example:
 
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
index 1cc3981..18fd9c5 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.c
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.c
@@ -18,6 +18,8 @@
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
 #include <linux/component.h>
 #include <linux/phy/phy.h>
 #include <video/of_display_timing.h>
@@ -1226,6 +1228,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
 	struct drm_device *drm_dev = data;
 	struct resource *res;
 	struct exynos_dp_device *dp;
+	unsigned int irq_flags;
 
 	int ret = 0;
 
@@ -1265,7 +1268,30 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
 	if (IS_ERR(dp->reg_base))
 		return PTR_ERR(dp->reg_base);
 
-	dp->irq = platform_get_irq(pdev, 0);
+	dp->hpd_gpio = of_get_named_gpio(dev->of_node, "samsung,hpd-gpio", 0);
+
+	if (gpio_is_valid(dp->hpd_gpio)) {
+		/*
+		 * Set up the hotplug GPIO from the device tree as an interrupt.
+		 * Simply specifying a different interrupt in the device tree
+		 * doesn't work since we handle hotplug rather differently when
+		 * using a GPIO.  We also need the actual GPIO specifier so
+		 * that we can get the current state of the GPIO.
+		 */
+		ret = devm_gpio_request_one(&pdev->dev, dp->hpd_gpio, GPIOF_IN,
+					    "hpd_gpio");
+		if (ret) {
+			dev_err(&pdev->dev, "failed to get hpd gpio\n");
+			return ret;
+		}
+		dp->irq = gpio_to_irq(dp->hpd_gpio);
+		irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+	} else {
+		dp->hpd_gpio = -ENODEV;
+		dp->irq = platform_get_irq(pdev, 0);
+		irq_flags = 0;
+	}
+
 	if (dp->irq == -ENXIO) {
 		dev_err(&pdev->dev, "failed to get irq\n");
 		return -ENODEV;
@@ -1277,8 +1303,8 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
 
 	exynos_dp_init_dp(dp);
 
-	ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler, 0,
-				"exynos-dp", dp);
+	ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler,
+			irq_flags, "exynos-dp", dp);
 	if (ret) {
 		dev_err(&pdev->dev, "failed to request irq\n");
 		return ret;
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.h b/drivers/gpu/drm/exynos/exynos_dp_core.h
index d6a900d..56fa43e 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.h
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.h
@@ -159,6 +159,7 @@ struct exynos_dp_device {
 	struct work_struct	hotplug_work;
 	struct phy		*phy;
 	int			dpms_mode;
+	int			hpd_gpio;
 
 	struct exynos_drm_panel_info panel;
 };
diff --git a/drivers/gpu/drm/exynos/exynos_dp_reg.c b/drivers/gpu/drm/exynos/exynos_dp_reg.c
index b70da50..79291a2 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_reg.c
+++ b/drivers/gpu/drm/exynos/exynos_dp_reg.c
@@ -13,6 +13,7 @@
 #include <linux/device.h>
 #include <linux/io.h>
 #include <linux/delay.h>
+#include <linux/gpio.h>
 
 #include "exynos_dp_core.h"
 #include "exynos_dp_reg.h"
@@ -326,6 +327,9 @@ void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp)
 {
 	u32 reg;
 
+	if (gpio_is_valid(dp->hpd_gpio))
+		return;
+
 	reg = HOTPLUG_CHG | HPD_LOST | PLUG;
 	writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
 
@@ -337,6 +341,9 @@ void exynos_dp_init_hpd(struct exynos_dp_device *dp)
 {
 	u32 reg;
 
+	if (gpio_is_valid(dp->hpd_gpio))
+		return;
+
 	exynos_dp_clear_hotplug_interrupts(dp);
 
 	reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
@@ -348,19 +355,27 @@ enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp)
 {
 	u32 reg;
 
-	/* Parse hotplug interrupt status register */
-	reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
+	if (gpio_is_valid(dp->hpd_gpio)) {
+		reg = gpio_get_value(dp->hpd_gpio);
+		if (reg)
+			return DP_IRQ_TYPE_HP_CABLE_IN;
+		else
+			return DP_IRQ_TYPE_HP_CABLE_OUT;
+	} else {
+		/* Parse hotplug interrupt status register */
+		reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
 
-	if (reg & PLUG)
-		return DP_IRQ_TYPE_HP_CABLE_IN;
+		if (reg & PLUG)
+			return DP_IRQ_TYPE_HP_CABLE_IN;
 
-	if (reg & HPD_LOST)
-		return DP_IRQ_TYPE_HP_CABLE_OUT;
+		if (reg & HPD_LOST)
+			return DP_IRQ_TYPE_HP_CABLE_OUT;
 
-	if (reg & HOTPLUG_CHG)
-		return DP_IRQ_TYPE_HP_CHANGE;
+		if (reg & HOTPLUG_CHG)
+			return DP_IRQ_TYPE_HP_CHANGE;
 
-	return DP_IRQ_TYPE_UNKNOWN;
+		return DP_IRQ_TYPE_UNKNOWN;
+	}
 }
 
 void exynos_dp_reset_aux(struct exynos_dp_device *dp)
@@ -402,9 +417,14 @@ int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp)
 {
 	u32 reg;
 
-	reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-	if (reg & HPD_STATUS)
-		return 0;
+	if (gpio_is_valid(dp->hpd_gpio)) {
+		if (gpio_get_value(dp->hpd_gpio))
+			return 0;
+	} else {
+		reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
+		if (reg & HPD_STATUS)
+			return 0;
+	}
 
 	return -EINVAL;
 }
-- 
1.7.9.5

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

* [PATCH V2 2/9] drm/panel: add pre_enable and post_disable routines
  2014-04-21 22:39 [PATCH V2 0/9] drm: exynos: few patches to enhance bridge chip support Ajay Kumar
  2014-04-21 22:39 ` [PATCH V2 1/9] drm/exynos: dp: support hotplug detection via GPIO Ajay Kumar
@ 2014-04-21 22:39 ` Ajay Kumar
  2014-04-22  8:19   ` Thierry Reding
  2014-04-21 22:39 ` [PATCH V2 3/9] drm/panel: Add driver for exynos_dp based panels Ajay Kumar
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 58+ messages in thread
From: Ajay Kumar @ 2014-04-21 22:39 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: inki.dae, seanpaul, ajaynumb, joshi, prashanth.g, Ajay Kumar

Most of the panels need an init sequence as mentioned below:
	-- poweron LCD unit/LCD_EN
	-- start video data
	-- poweron LED unit/BL_EN
And, a de-init sequence as mentioned below:
	-- poweroff LED unit/BL_EN
	-- stop video data
	-- poweroff LCD unit/LCD_EN
With existing callbacks for drm panel, we cannot accomodate such panels,
since only two callbacks, i.e "panel_enable" and panel_disable are supported.

This patch adds:
	-- "pre_enable" callback which can be called before
	the actual video data is on, and then call the "enable"
	callback after the video data is available.

	-- "post_disable" callback which can be called after
	the video data is off, and use "disable" callback
	to do something before switching off the video data.

Now, we can easily map the above scenario as shown below:
	poweron LCD unit/LCD_EN = "pre_enable" callback
	poweron LED unit/BL_EN = "enable" callback
	poweroff LED unit/BL_EN = "disable" callback
	poweroff LCD unit/LCD_EN = "post_disable" callback

Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
---
Changes since V1:
	Added post_disable callback

 include/drm/drm_panel.h |   18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h
index c2ab77a..bf191df 100644
--- a/include/drm/drm_panel.h
+++ b/include/drm/drm_panel.h
@@ -31,7 +31,9 @@ struct drm_device;
 struct drm_panel;
 
 struct drm_panel_funcs {
+	int (*post_disable)(struct drm_panel *panel);
 	int (*disable)(struct drm_panel *panel);
+	int (*pre_enable)(struct drm_panel *panel);
 	int (*enable)(struct drm_panel *panel);
 	int (*get_modes)(struct drm_panel *panel);
 };
@@ -46,6 +48,14 @@ struct drm_panel {
 	struct list_head list;
 };
 
+static inline int drm_panel_post_disable(struct drm_panel *panel)
+{
+	if (panel && panel->funcs && panel->funcs->post_disable)
+		return panel->funcs->post_disable(panel);
+
+	return panel ? -ENOSYS : -EINVAL;
+}
+
 static inline int drm_panel_disable(struct drm_panel *panel)
 {
 	if (panel && panel->funcs && panel->funcs->disable)
@@ -54,6 +64,14 @@ static inline int drm_panel_disable(struct drm_panel *panel)
 	return panel ? -ENOSYS : -EINVAL;
 }
 
+static inline int drm_panel_pre_enable(struct drm_panel *panel)
+{
+	if (panel && panel->funcs && panel->funcs->pre_enable)
+		return panel->funcs->pre_enable(panel);
+
+	return panel ? -ENOSYS : -EINVAL;
+}
+
 static inline int drm_panel_enable(struct drm_panel *panel)
 {
 	if (panel && panel->funcs && panel->funcs->enable)
-- 
1.7.9.5

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

* [PATCH V2 3/9] drm/panel: Add driver for exynos_dp based panels
  2014-04-21 22:39 [PATCH V2 0/9] drm: exynos: few patches to enhance bridge chip support Ajay Kumar
  2014-04-21 22:39 ` [PATCH V2 1/9] drm/exynos: dp: support hotplug detection via GPIO Ajay Kumar
  2014-04-21 22:39 ` [PATCH V2 2/9] drm/panel: add pre_enable and post_disable routines Ajay Kumar
@ 2014-04-21 22:39 ` Ajay Kumar
  2014-04-22  7:22   ` Jingoo Han
  2014-04-22  8:26   ` Thierry Reding
  2014-04-21 22:39 ` [PATCH V2 4/9] drm/exynos: add exynos_dp_panel driver registration to drm driver Ajay Kumar
                   ` (5 subsequent siblings)
  8 siblings, 2 replies; 58+ messages in thread
From: Ajay Kumar @ 2014-04-21 22:39 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: inki.dae, seanpaul, ajaynumb, joshi, prashanth.g, Ajay Kumar

This patch adds a simple driver to handle all the LCD and LED
powerup/down routines needed to support eDP/eDP-LVDS panels
supported on exynos boards.

The LCD and LED units are usually powered up via regulators,
and almost on all boards, we will have a BL_EN pin to enable/
disable the backlight. Sometimes, we can have LCD_EN switches
as well. The routines in this driver can be used to control
panel power sequence on such boards.

Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
---
Changes since V1:
	Added routine for post_disable, removed few unwanted headers.
	Refactored a lot of code.

 .../devicetree/bindings/panel/exynos-dp-panel.txt  |   45 ++++
 drivers/gpu/drm/panel/Kconfig                      |    9 +
 drivers/gpu/drm/panel/Makefile                     |    1 +
 drivers/gpu/drm/panel/panel-exynos-dp.c            |  251 ++++++++++++++++++++
 4 files changed, 306 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/panel/exynos-dp-panel.txt
 create mode 100644 drivers/gpu/drm/panel/panel-exynos-dp.c

diff --git a/Documentation/devicetree/bindings/panel/exynos-dp-panel.txt b/Documentation/devicetree/bindings/panel/exynos-dp-panel.txt
new file mode 100644
index 0000000..3fbca54
--- /dev/null
+++ b/Documentation/devicetree/bindings/panel/exynos-dp-panel.txt
@@ -0,0 +1,45 @@
+exynos_DP_panel/DP_to_LVDS_panel
+
+Required properties:
+  - compatible: "samsung,exynos-dp-panel"
+
+Optional properties:
+	-samsung,lcd-en-gpio:
+		eDP panel LCD poweron GPIO.
+			Indicates which GPIO needs to be powered up as output
+			to powerup/enable the switch to the LCD panel.
+	-samsung,led-en-gpio:
+		eDP panel LED enable GPIO.
+			Indicates which GPIO needs to be powered up as output
+			to enable the backlight.
+	-samsung,panel-pre-enable-delay:
+		delay value in ms required for panel_pre_enable process
+			Delay in ms needed for the eDP panel LCD unit to
+			powerup, and delay needed between panel_VCC and
+			video_enable.
+	-samsung,panel-enable-delay:
+		delay value in ms required for panel_enable process
+			Delay in ms needed for the eDP panel backlight/LED unit
+			to powerup, and delay needed between video_enable and
+			BL_EN.
+	samsung,panel-disable-delay:
+		delay value in ms required for panel_disable process
+			Delay in ms needed for the eDP panel backlight/LED unit
+			powerdown, and delay needed between BL_DISABLE and
+			video_disable.
+	samsung,panel-post-disable-delay:
+		delay value in ms required for panel_post_disable process
+			Delay in ms needed for the eDP panel LCD unit to
+			to powerdown, and delay between video_disable and
+			panel_VCC going down.
+
+Example:
+
+	dp-panel {
+		compatible = "samsung,exynos-dp-panel";
+		samsung,led-en-gpio = <&gpx3 0 1>;
+		samsung,panel-pre-enable-delay = <40>;
+		samsung,panel-enable-delay = <20>;
+		samsung,panel-disable-delay = <20>;
+		samsung,panel-post-disable-delay = <30>;
+	};
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 4ec874d..ea9d5ac 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -30,4 +30,13 @@ config DRM_PANEL_S6E8AA0
 	select DRM_MIPI_DSI
 	select VIDEOMODE_HELPERS
 
+config DRM_PANEL_EXYNOS_DP
+	tristate "support for DP panels"
+	depends on OF && DRM_PANEL && DRM_EXYNOS_DP
+	help
+	  DRM panel driver for DP panels and LVDS connected via DP bridges
+	  that need at most a regulator for LCD unit, a regulator for LED unit
+	  and/or enable GPIOs for LCD or LED units. Delay values can also be
+	  specified to support powerup and powerdown process.
+
 endmenu
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 8b92921..30311a4 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
 obj-$(CONFIG_DRM_PANEL_LD9040) += panel-ld9040.o
 obj-$(CONFIG_DRM_PANEL_S6E8AA0) += panel-s6e8aa0.o
+obj-$(CONFIG_DRM_PANEL_EXYNOS_DP) += panel-exynos-dp.o
diff --git a/drivers/gpu/drm/panel/panel-exynos-dp.c b/drivers/gpu/drm/panel/panel-exynos-dp.c
new file mode 100644
index 0000000..5568d6a
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-exynos-dp.c
@@ -0,0 +1,251 @@
+/*
+ * Exynos DP panel driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd
+ *
+ * Ajay Kumar <ajaykumar.rs@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/gpio.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_panel.h>
+
+enum panel_state {
+	PRE_ENABLE,
+	ENABLE,
+	DISABLE,
+	POST_DISABLE,
+};
+
+struct panel_exynos_dp {
+	struct drm_panel	base;
+	struct regulator	*backlight_fet;
+	struct regulator	*lcd_fet;
+	int			led_en_gpio;
+	int			lcd_en_gpio;
+	int			panel_pre_enable_delay;
+	int			panel_enable_delay;
+	int			panel_disable_delay;
+	int			panel_post_disable_delay;
+	enum panel_state	panel_state;
+};
+
+static inline struct panel_exynos_dp *to_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct panel_exynos_dp, base);
+}
+
+static int panel_exynos_dp_post_disable(struct drm_panel *panel)
+{
+	struct panel_exynos_dp *dp_panel = to_panel(panel);
+	struct device *dev = dp_panel->base.dev;
+
+	if (dp_panel->panel_state != DISABLE) {
+		dev_err(dev, "invoking panel_exynos_dp_post_disable" \
+					"from an improper state\n");
+		return -EINVAL;
+	}
+
+	if (gpio_is_valid(dp_panel->lcd_en_gpio))
+		gpio_set_value(dp_panel->lcd_en_gpio, 0);
+
+	if (!IS_ERR_OR_NULL(dp_panel->lcd_fet))
+		regulator_disable(dp_panel->lcd_fet);
+
+	msleep(dp_panel->panel_post_disable_delay);
+
+	dp_panel->panel_state = POST_DISABLE;
+
+	return 0;
+}
+
+static int panel_exynos_dp_disable(struct drm_panel *panel)
+{
+	struct panel_exynos_dp *dp_panel = to_panel(panel);
+	struct device *dev = dp_panel->base.dev;
+
+	if (dp_panel->panel_state != ENABLE) {
+		dev_err(dev, "invoking panel_exynos_dp_disable" \
+					"from an improper state\n");
+		return -EINVAL;
+	}
+
+	if (gpio_is_valid(dp_panel->led_en_gpio))
+		gpio_set_value(dp_panel->led_en_gpio, 0);
+
+	if (!IS_ERR_OR_NULL(dp_panel->backlight_fet))
+		regulator_disable(dp_panel->backlight_fet);
+
+	msleep(dp_panel->panel_disable_delay);
+
+	dp_panel->panel_state = DISABLE;
+
+	return 0;
+}
+
+static int panel_exynos_dp_pre_enable(struct drm_panel *panel)
+{
+	struct panel_exynos_dp *dp_panel = to_panel(panel);
+	struct device *dev = dp_panel->base.dev;
+
+	if (dp_panel->panel_state != POST_DISABLE) {
+		dev_err(dev, "invoking panel_exynos_dp_pre_enable" \
+					"from an improper state\n");
+		return -EINVAL;
+	}
+
+	if (!IS_ERR_OR_NULL(dp_panel->lcd_fet))
+		if (regulator_enable(dp_panel->lcd_fet))
+			DRM_ERROR("Failed to enable LCD fet\n");
+
+	if (gpio_is_valid(dp_panel->lcd_en_gpio))
+		gpio_set_value(dp_panel->lcd_en_gpio, 1);
+
+	msleep(dp_panel->panel_pre_enable_delay);
+
+	dp_panel->panel_state = PRE_ENABLE;
+
+	return 0;
+}
+
+static int panel_exynos_dp_enable(struct drm_panel *panel)
+{
+	struct panel_exynos_dp *dp_panel = to_panel(panel);
+	struct device *dev = dp_panel->base.dev;
+
+	if (dp_panel->panel_state != PRE_ENABLE) {
+		dev_err(dev, "invoking panel_exynos_dp_enable" \
+					"from an improper state\n");
+		return -EINVAL;
+	}
+
+	if (!IS_ERR_OR_NULL(dp_panel->backlight_fet))
+		if (regulator_enable(dp_panel->backlight_fet))
+				DRM_ERROR("Failed to enable LED fet\n");
+
+	msleep(dp_panel->panel_enable_delay);
+
+	if (gpio_is_valid(dp_panel->led_en_gpio))
+		gpio_set_value(dp_panel->led_en_gpio, 1);
+
+	dp_panel->panel_state = ENABLE;
+
+	return 0;
+}
+
+static const struct drm_panel_funcs panel_exynos_dp_funcs = {
+	.post_disable = panel_exynos_dp_post_disable,
+	.disable = panel_exynos_dp_disable,
+	.pre_enable = panel_exynos_dp_pre_enable,
+	.enable = panel_exynos_dp_enable,
+};
+
+static int panel_exynos_dp_probe(struct platform_device *pdev)
+{
+	struct panel_exynos_dp *dp_panel;
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	dp_panel = devm_kzalloc(dev, sizeof(*dp_panel), GFP_KERNEL);
+	if (!dp_panel)
+		return -ENOMEM;
+
+	dp_panel->panel_state = POST_DISABLE;
+
+	dp_panel->lcd_en_gpio = of_get_named_gpio(dev->of_node,
+						"samsung,lcd-en-gpio", 0);
+	dp_panel->led_en_gpio = of_get_named_gpio(dev->of_node,
+						"samsung,led-en-gpio", 0);
+
+	of_property_read_u32(dev->of_node, "samsung,panel-pre-enable-delay",
+					&dp_panel->panel_pre_enable_delay);
+	of_property_read_u32(dev->of_node, "samsung,panel-enable-delay",
+					&dp_panel->panel_enable_delay);
+	of_property_read_u32(dev->of_node, "samsung,panel-disable-delay",
+					&dp_panel->panel_disable_delay);
+	of_property_read_u32(dev->of_node, "samsung,panel-post-disable-delay",
+					&dp_panel->panel_post_disable_delay);
+
+	dp_panel->lcd_fet = devm_regulator_get(dev, "lcd_vdd");
+	if (IS_ERR(dp_panel->lcd_fet))
+		return PTR_ERR(dp_panel->lcd_fet);
+
+	dp_panel->backlight_fet = devm_regulator_get(dev, "vcd_led");
+	if (IS_ERR(dp_panel->backlight_fet))
+		return PTR_ERR(dp_panel->backlight_fet);
+
+	if (gpio_is_valid(dp_panel->lcd_en_gpio)) {
+		ret = devm_gpio_request_one(dev, dp_panel->lcd_en_gpio,
+					GPIOF_OUT_INIT_LOW, "lcd_en_gpio");
+		if (ret) {
+			DRM_ERROR("failed to get lcd-en gpio [%d]\n", ret);
+			return ret;
+		}
+	} else {
+		dp_panel->lcd_en_gpio = -ENODEV;
+	}
+
+	if (gpio_is_valid(dp_panel->led_en_gpio)) {
+		ret = devm_gpio_request_one(dev, dp_panel->led_en_gpio,
+					GPIOF_OUT_INIT_LOW, "led_en_gpio");
+		if (ret) {
+			DRM_ERROR("failed to get led-en gpio [%d]\n", ret);
+			return ret;
+		}
+	} else {
+		dp_panel->led_en_gpio = -ENODEV;
+	}
+
+	dp_panel->panel_state = POST_DISABLE;
+	drm_panel_init(&dp_panel->base);
+	dp_panel->base.dev = dev;
+	dp_panel->base.funcs = &panel_exynos_dp_funcs;
+
+	ret = drm_panel_add(&dp_panel->base);
+	if (ret < 0)
+		return ret;
+
+	dev_set_drvdata(dev, dp_panel);
+
+	return 0;
+}
+
+static int panel_exynos_dp_remove(struct platform_device *pdev)
+{
+	struct panel_exynos_dp *dp_panel = dev_get_drvdata(&pdev->dev);
+
+	dp_panel->panel_state = ENABLE;
+	panel_exynos_dp_disable(&dp_panel->base);
+	panel_exynos_dp_post_disable(&dp_panel->base);
+
+	drm_panel_detach(&dp_panel->base);
+	drm_panel_remove(&dp_panel->base);
+
+	return 0;
+}
+
+static const struct of_device_id exynos_dp_panel_dt_match[] = {
+	{ .compatible = "samsung,exynos-dp-panel" },
+	{},
+};
+
+struct platform_driver exynos_dp_panel_driver = {
+	.driver = {
+		.name = "exynos-dp-panel",
+		.owner = THIS_MODULE,
+		.of_match_table = exynos_dp_panel_dt_match,
+	},
+	.probe = panel_exynos_dp_probe,
+	.remove = panel_exynos_dp_remove,
+};
-- 
1.7.9.5

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

* [PATCH V2 4/9] drm/exynos: add exynos_dp_panel driver registration to drm driver
  2014-04-21 22:39 [PATCH V2 0/9] drm: exynos: few patches to enhance bridge chip support Ajay Kumar
                   ` (2 preceding siblings ...)
  2014-04-21 22:39 ` [PATCH V2 3/9] drm/panel: Add driver for exynos_dp based panels Ajay Kumar
@ 2014-04-21 22:39 ` Ajay Kumar
  2014-04-22  8:33   ` Thierry Reding
  2014-04-21 22:39 ` [PATCH V2 5/9] drm/exynos: dp: modify driver to support drm_panel Ajay Kumar
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 58+ messages in thread
From: Ajay Kumar @ 2014-04-21 22:39 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: inki.dae, seanpaul, ajaynumb, joshi, prashanth.g, Ajay Kumar

Register exynos_dp_panel before the list of exynos crtcs and
connectors are probed.

This is needed because exynos_dp_panel should be registered to
the drm_panel list via panel-exynos-dp probe, i.e much before
exynos_dp_bind calls of_drm_find_panel().

Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
---
Changes since V1:
	Added platform_driver_unregister(&exynos_dp_panel_driver) to
	exynos_drm_platform_remove as per Jingoo Han's correction

 drivers/gpu/drm/exynos/exynos_drm_drv.c |   15 +++++++++++++++
 drivers/gpu/drm/exynos/exynos_drm_drv.h |    1 +
 2 files changed, 16 insertions(+)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 1d653f8..2db7f67 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -530,12 +530,23 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
 		goto err_unregister_ipp_drv;
 #endif
 
+#ifdef CONFIG_DRM_PANEL_EXYNOS_DP
+	ret = platform_driver_register(&exynos_dp_panel_driver);
+	if (ret < 0)
+		goto err_unregister_dp_panel;
+#endif
+
 	ret = component_master_add(&pdev->dev, &exynos_drm_ops);
 	if (ret < 0)
 		DRM_DEBUG_KMS("re-tried by last sub driver probed later.\n");
 
 	return 0;
 
+#ifdef CONFIG_DRM_PANEL_EXYNOS_DP
+	platform_driver_unregister(&exynos_dp_panel_driver);
+err_unregister_dp_panel:
+#endif
+
 #ifdef CONFIG_DRM_EXYNOS_IPP
 err_unregister_ipp_drv:
 	platform_driver_unregister(&ipp_driver);
@@ -587,6 +598,10 @@ err_unregister_fimd_drv:
 
 static int exynos_drm_platform_remove(struct platform_device *pdev)
 {
+#ifdef CONFIG_DRM_PANEL_EXYNOS_DP
+	platform_driver_unregister(&exynos_dp_panel_driver);
+#endif
+
 #ifdef CONFIG_DRM_EXYNOS_IPP
 	exynos_platform_device_ipp_unregister();
 	platform_driver_unregister(&ipp_driver);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index fc15fe6..b33050d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -368,4 +368,5 @@ extern struct platform_driver fimc_driver;
 extern struct platform_driver rotator_driver;
 extern struct platform_driver gsc_driver;
 extern struct platform_driver ipp_driver;
+extern struct platform_driver exynos_dp_panel_driver;
 #endif
-- 
1.7.9.5

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

* [PATCH V2 5/9] drm/exynos: dp: modify driver to support drm_panel
  2014-04-21 22:39 [PATCH V2 0/9] drm: exynos: few patches to enhance bridge chip support Ajay Kumar
                   ` (3 preceding siblings ...)
  2014-04-21 22:39 ` [PATCH V2 4/9] drm/exynos: add exynos_dp_panel driver registration to drm driver Ajay Kumar
@ 2014-04-21 22:39 ` Ajay Kumar
  2014-04-22  8:37   ` Thierry Reding
  2014-04-21 22:39 ` [PATCH V2 6/9] drm/bridge: ptn3460: enable polling based detection Ajay Kumar
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 58+ messages in thread
From: Ajay Kumar @ 2014-04-21 22:39 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: inki.dae, seanpaul, ajaynumb, joshi, prashanth.g, Ajay Kumar

This patch attaches the dp connector to exynos_dp_panel, and adds
calls to drm_panel functions to control panel power sequence.

Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
---
Changes since V1:
	Addressed a comment from Jingoo Han. Also added post_disable
	control to exynos_dp driver.

 drivers/gpu/drm/exynos/Kconfig          |    1 +
 drivers/gpu/drm/exynos/exynos_dp_core.c |   18 ++++++++++++++++++
 drivers/gpu/drm/exynos/exynos_dp_core.h |    1 +
 3 files changed, 20 insertions(+)

diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 5bf5bca..56af433 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -52,6 +52,7 @@ config DRM_EXYNOS_DP
 	bool "EXYNOS DRM DP driver support"
 	depends on DRM_EXYNOS && ARCH_EXYNOS
 	default DRM_EXYNOS
+	select DRM_PANEL
 	help
 	  This enables support for DP device.
 
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
index 18fd9c5..dbc5ccc 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.c
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.c
@@ -28,6 +28,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
 #include <drm/bridge/ptn3460.h>
 
 #include "exynos_drm_drv.h"
@@ -1028,6 +1029,9 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display,
 	drm_sysfs_connector_add(connector);
 	drm_mode_connector_attach_encoder(connector, encoder);
 
+	if (dp->drm_panel)
+		drm_panel_attach(dp->drm_panel, &dp->connector);
+
 	return 0;
 }
 
@@ -1062,10 +1066,12 @@ static void exynos_dp_poweron(struct exynos_dp_device *dp)
 	if (dp->dpms_mode == DRM_MODE_DPMS_ON)
 		return;
 
+	drm_panel_pre_enable(dp->drm_panel);
 	clk_prepare_enable(dp->clock);
 	exynos_dp_phy_init(dp);
 	exynos_dp_init_dp(dp);
 	enable_irq(dp->irq);
+	drm_panel_enable(dp->drm_panel);
 }
 
 static void exynos_dp_poweroff(struct exynos_dp_device *dp)
@@ -1073,10 +1079,12 @@ static void exynos_dp_poweroff(struct exynos_dp_device *dp)
 	if (dp->dpms_mode != DRM_MODE_DPMS_ON)
 		return;
 
+	drm_panel_disable(dp->drm_panel);
 	disable_irq(dp->irq);
 	flush_work(&dp->hotplug_work);
 	exynos_dp_phy_exit(dp);
 	clk_disable_unprepare(dp->clock);
+	drm_panel_post_disable(dp->drm_panel);
 }
 
 static void exynos_dp_dpms(struct exynos_drm_display *display, int mode)
@@ -1225,6 +1233,7 @@ static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
 static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
+	struct device_node *panel_node;
 	struct drm_device *drm_dev = data;
 	struct resource *res;
 	struct exynos_dp_device *dp;
@@ -1299,6 +1308,15 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
 
 	INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug);
 
+	panel_node = of_find_compatible_node(NULL, NULL,
+						"samsung,exynos-dp-panel");
+	if (panel_node) {
+		dp->drm_panel = of_drm_find_panel(panel_node);
+		of_node_put(panel_node);
+		if (!dp->drm_panel)
+			return -ENODEV;
+	}
+
 	exynos_dp_phy_init(dp);
 
 	exynos_dp_init_dp(dp);
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.h b/drivers/gpu/drm/exynos/exynos_dp_core.h
index 56fa43e..9dc7991 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.h
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.h
@@ -148,6 +148,7 @@ struct exynos_dp_device {
 	struct drm_device	*drm_dev;
 	struct drm_connector	connector;
 	struct drm_encoder	*encoder;
+	struct drm_panel	*drm_panel;
 	struct clk		*clock;
 	unsigned int		irq;
 	void __iomem		*reg_base;
-- 
1.7.9.5

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

* [PATCH V2 6/9] drm/bridge: ptn3460: enable polling based detection
  2014-04-21 22:39 [PATCH V2 0/9] drm: exynos: few patches to enhance bridge chip support Ajay Kumar
                   ` (4 preceding siblings ...)
  2014-04-21 22:39 ` [PATCH V2 5/9] drm/exynos: dp: modify driver to support drm_panel Ajay Kumar
@ 2014-04-21 22:39 ` Ajay Kumar
  2014-04-22  8:43   ` Thierry Reding
  2014-04-21 22:39 ` [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls Ajay Kumar
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 58+ messages in thread
From: Ajay Kumar @ 2014-04-21 22:39 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: inki.dae, seanpaul, ajaynumb, joshi, prashanth.g, Rahul Sharma,
	Ajay Kumar

From: Rahul Sharma <Rahul.Sharma@samsung.com>

Add DRM_CONNECTOR_POLL_HPD to the set of connector flags while
registering drm_connector for ptn3460.

Signed-off-by: Rahul Sharma <Rahul.Sharma@samsung.com>
Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
---
Changes since V1:
	No Change since V1

 drivers/gpu/drm/bridge/ptn3460.c |    1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
index b171901..f1d2afc 100644
--- a/drivers/gpu/drm/bridge/ptn3460.c
+++ b/drivers/gpu/drm/bridge/ptn3460.c
@@ -326,6 +326,7 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
 
 	bridge->driver_private = ptn_bridge;
 	encoder->bridge = bridge;
+	ptn_bridge->connector.polled = DRM_CONNECTOR_POLL_HPD;
 
 	ret = drm_connector_init(dev, &ptn_bridge->connector,
 			&ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
-- 
1.7.9.5

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

* [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls
  2014-04-21 22:39 [PATCH V2 0/9] drm: exynos: few patches to enhance bridge chip support Ajay Kumar
                   ` (5 preceding siblings ...)
  2014-04-21 22:39 ` [PATCH V2 6/9] drm/bridge: ptn3460: enable polling based detection Ajay Kumar
@ 2014-04-21 22:39 ` Ajay Kumar
  2014-04-22  8:55   ` Thierry Reding
  2014-04-22 11:34   ` Rob Clark
  2014-04-21 22:39 ` [PATCH V2 8/9] drm/bridge: Add PS8622 bridge driver Ajay Kumar
  2014-04-21 22:39 ` [PATCH V2 9/9] drm/exynos: Add ps8622 lvds bridge discovery to DP driver Ajay Kumar
  8 siblings, 2 replies; 58+ messages in thread
From: Ajay Kumar @ 2014-04-21 22:39 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: inki.dae, seanpaul, ajaynumb, joshi, prashanth.g, Ajay Kumar

attach ptn3460 connector to drm_panel and support drm_panel routines,
if a valid drm_panel object is passed to ptn3460_init.

Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
---
Changes since V1:
	Address few coding style comments from Jingoo Han

 drivers/gpu/drm/bridge/Kconfig          |    1 +
 drivers/gpu/drm/bridge/ptn3460.c        |   20 +++++++++++++++++++-
 drivers/gpu/drm/exynos/exynos_dp_core.c |   16 ++++++++++++----
 include/drm/bridge/ptn3460.h            |    6 ++++--
 4 files changed, 36 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 884923f..3bc6845 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -2,4 +2,5 @@ config DRM_PTN3460
 	tristate "PTN3460 DP/LVDS bridge"
 	depends on DRM
 	select DRM_KMS_HELPER
+	select DRM_PANEL
 	---help---
diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
index f1d2afc..3920202 100644
--- a/drivers/gpu/drm/bridge/ptn3460.c
+++ b/drivers/gpu/drm/bridge/ptn3460.c
@@ -19,6 +19,7 @@
 #include <linux/i2c.h>
 #include <linux/gpio.h>
 #include <linux/delay.h>
+#include <drm/drm_panel.h>
 
 #include "drmP.h"
 #include "drm_edid.h"
@@ -38,6 +39,7 @@ struct ptn3460_bridge {
 	struct i2c_client *client;
 	struct drm_encoder *encoder;
 	struct drm_bridge *bridge;
+	struct drm_panel *panel;
 	struct edid *edid;
 	int gpio_pd_n;
 	int gpio_rst_n;
@@ -126,6 +128,8 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge)
 		gpio_set_value(ptn_bridge->gpio_rst_n, 1);
 	}
 
+	drm_panel_pre_enable(ptn_bridge->panel);
+
 	/*
 	 * There's a bug in the PTN chip where it falsely asserts hotplug before
 	 * it is fully functional. We're forced to wait for the maximum start up
@@ -142,6 +146,10 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge)
 
 static void ptn3460_enable(struct drm_bridge *bridge)
 {
+	struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
+
+	if (ptn_bridge->enabled)
+		drm_panel_enable(ptn_bridge->panel);
 }
 
 static void ptn3460_disable(struct drm_bridge *bridge)
@@ -153,6 +161,9 @@ static void ptn3460_disable(struct drm_bridge *bridge)
 
 	ptn_bridge->enabled = false;
 
+	drm_panel_disable(ptn_bridge->panel);
+	drm_panel_post_disable(ptn_bridge->panel);
+
 	if (gpio_is_valid(ptn_bridge->gpio_rst_n))
 		gpio_set_value(ptn_bridge->gpio_rst_n, 1);
 
@@ -198,6 +209,7 @@ int ptn3460_get_modes(struct drm_connector *connector)
 
 	power_off = !ptn_bridge->enabled;
 	ptn3460_pre_enable(ptn_bridge->bridge);
+	ptn3460_enable(ptn_bridge->bridge);
 
 	edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
 	if (!edid) {
@@ -265,7 +277,8 @@ struct drm_connector_funcs ptn3460_connector_funcs = {
 };
 
 int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
-		struct i2c_client *client, struct device_node *node)
+		struct i2c_client *client, struct device_node *node,
+		struct drm_panel *panel)
 {
 	int ret;
 	struct drm_bridge *bridge;
@@ -324,6 +337,11 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
 		goto err;
 	}
 
+	if (panel) {
+		ptn_bridge->panel = panel;
+		drm_panel_attach(ptn_bridge->panel, &ptn_bridge->connector);
+	}
+
 	bridge->driver_private = ptn_bridge;
 	encoder->bridge = bridge;
 	ptn_bridge->connector.polled = DRM_CONNECTOR_POLL_HPD;
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
index dbc5ccc..4853f31 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.c
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.c
@@ -989,13 +989,14 @@ static bool find_bridge(const char *compat, struct bridge_init *bridge)
 
 /* returns the number of bridges attached */
 static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
-		struct drm_encoder *encoder)
+		struct drm_encoder *encoder, struct drm_panel *panel)
 {
 	struct bridge_init bridge;
 	int ret;
 
 	if (find_bridge("nxp,ptn3460", &bridge)) {
-		ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
+		ret = ptn3460_init(dev, encoder, bridge.client, bridge.node,
+				panel);
 		if (!ret)
 			return 1;
 	}
@@ -1012,9 +1013,16 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display,
 	dp->encoder = encoder;
 
 	/* Pre-empt DP connector creation if there's a bridge */
-	ret = exynos_drm_attach_lcd_bridge(dp->drm_dev, encoder);
-	if (ret)
+	ret = exynos_drm_attach_lcd_bridge(dp->drm_dev, encoder, dp->drm_panel);
+	if (ret) {
+		/*
+		 * Also set "dp->drm_panel = NULL" so that we don't end up
+		 * controlling panel power both in exynos_dp and bridge
+		 * DPMS routines.
+		 */
+		dp->drm_panel = NULL;
 		return 0;
+	}
 
 	connector->polled = DRM_CONNECTOR_POLL_HPD;
 
diff --git a/include/drm/bridge/ptn3460.h b/include/drm/bridge/ptn3460.h
index ff62344..570cebb 100644
--- a/include/drm/bridge/ptn3460.h
+++ b/include/drm/bridge/ptn3460.h
@@ -18,16 +18,18 @@ struct drm_device;
 struct drm_encoder;
 struct i2c_client;
 struct device_node;
+struct drm_panel;
 
 #if defined(CONFIG_DRM_PTN3460) || defined(CONFIG_DRM_PTN3460_MODULE)
 
 int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
-		struct i2c_client *client, struct device_node *node);
+		struct i2c_client *client, struct device_node *node,
+		struct drm_panel *panel);
 #else
 
 static inline int ptn3460_init(struct drm_device *dev,
 		struct drm_encoder *encoder, struct i2c_client *client,
-		struct device_node *node)
+		struct device_node *node, struct drm_panel *panel)
 {
 	return 0;
 }
-- 
1.7.9.5

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

* [PATCH V2 8/9] drm/bridge: Add PS8622 bridge driver
  2014-04-21 22:39 [PATCH V2 0/9] drm: exynos: few patches to enhance bridge chip support Ajay Kumar
                   ` (6 preceding siblings ...)
  2014-04-21 22:39 ` [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls Ajay Kumar
@ 2014-04-21 22:39 ` Ajay Kumar
  2014-04-22  8:43   ` Jingoo Han
  2014-04-22 13:31   ` Sean Paul
  2014-04-21 22:39 ` [PATCH V2 9/9] drm/exynos: Add ps8622 lvds bridge discovery to DP driver Ajay Kumar
  8 siblings, 2 replies; 58+ messages in thread
From: Ajay Kumar @ 2014-04-21 22:39 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: inki.dae, seanpaul, ajaynumb, joshi, prashanth.g, Ajay Kumar,
	Andrew Bresticker, Sean Paul, Rahul Sharma

This patch adds a drm_bridge driver for the PS8622 DisplayPort to LVDS
bridge chip.

Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
Signed-off-by: Sean Paul <seanpaul@chromium.org>
Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
---
Changes since V1:
	Pushing V1 for this as V2 because this patch holds good in this series.

 drivers/gpu/drm/bridge/Kconfig  |    7 +
 drivers/gpu/drm/bridge/Makefile |    1 +
 drivers/gpu/drm/bridge/ps8622.c |  566 +++++++++++++++++++++++++++++++++++++++
 include/drm/bridge/ps8622.h     |   42 +++
 4 files changed, 616 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/ps8622.c
 create mode 100644 include/drm/bridge/ps8622.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 3bc6845..aba21fc 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -4,3 +4,10 @@ config DRM_PTN3460
 	select DRM_KMS_HELPER
 	select DRM_PANEL
 	---help---
+
+config DRM_PS8622
+	tristate "Parade eDP/LVDS bridge"
+	depends on DRM
+	select DRM_KMS_HELPER
+	select DRM_PANEL
+	---help---
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index b4733e1..d1b5daa 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -1,3 +1,4 @@
 ccflags-y := -Iinclude/drm
 
 obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
+obj-$(CONFIG_DRM_PS8622) += ps8622.o
diff --git a/drivers/gpu/drm/bridge/ps8622.c b/drivers/gpu/drm/bridge/ps8622.c
new file mode 100644
index 0000000..1e6b3ca
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ps8622.c
@@ -0,0 +1,566 @@
+/*
+ * Parade PS8622 eDP/LVDS bridge driver
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/pm.h>
+#include <linux/regulator/consumer.h>
+#include <drm/drm_panel.h>
+
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+struct ps8622_bridge {
+	struct drm_connector connector;
+	struct drm_bridge *bridge;
+	struct drm_encoder *encoder;
+	struct drm_panel *panel;
+	struct i2c_client *client;
+	struct regulator *v12;
+	struct backlight_device *bl;
+	struct mutex enable_mutex;
+
+	int gpio_slp_n;
+	int gpio_rst_n;
+
+	u8 max_lane_count;
+	u8 lane_count;
+
+	bool enabled;
+
+	struct drm_display_mode mode;
+};
+
+struct ps8622_device_data {
+	u8 max_lane_count;
+};
+
+static const struct ps8622_device_data ps8622_data = {
+	.max_lane_count = 1,
+};
+
+static const struct ps8622_device_data ps8625_data = {
+	.max_lane_count = 2,
+};
+
+/* Brightness scale on the Parade chip */
+#define PS8622_MAX_BRIGHTNESS 0xff
+
+/* Timings taken from the version 1.7 datasheet for the PS8622/PS8625 */
+#define PS8622_POWER_RISE_T1_MIN_US 10
+#define PS8622_POWER_RISE_T1_MAX_US 10000
+#define PS8622_RST_HIGH_T2_MIN_US 3000
+#define PS8622_RST_HIGH_T2_MAX_US 30000
+#define PS8622_PWMO_END_T12_MS 200
+#define PS8622_POWER_FALL_T16_MAX_US 10000
+#define PS8622_POWER_OFF_T17_MS 500
+
+#if ((PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US) > \
+	(PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US))
+#error "T2.min + T1.max must be less than T2.max + T1.min"
+#endif
+
+static int ps8622_set(struct i2c_client *client, u8 page, u8 reg, u8 val)
+{
+	int ret;
+	struct i2c_adapter *adap = client->adapter;
+	struct i2c_msg msg;
+	u8 data[] = {reg, val};
+
+	msg.addr = client->addr + page;
+	msg.flags = 0;
+	msg.len = sizeof(data);
+	msg.buf = data;
+
+	ret = i2c_transfer(adap, &msg, 1);
+	if (ret != 1)
+		pr_warn("PS8622 I2C write (0x%02x,0x%02x,0x%02x) failed: %d\n",
+			client->addr + page, reg, val, ret);
+	return !(ret == 1);
+}
+
+static int ps8622_send_config(struct ps8622_bridge *ps_bridge)
+{
+	struct i2c_client *cl = ps_bridge->client;
+	int err = 0;
+
+	/* wait 20ms after power ON */
+	usleep_range(20000, 30000);
+
+	err |= ps8622_set(cl, 0x02, 0xa1, 0x01); /* HPD low */
+	/* SW setting */
+	err |= ps8622_set(cl, 0x04, 0x14, 0x01); /* [1:0] SW output 1.2V voltage
+						  * is lower to 96% */
+	/* RCO SS setting */
+	err |= ps8622_set(cl, 0x04, 0xe3, 0x20); /* [5:4] = b01 0.5%, b10 1%,
+						  * b11 1.5% */
+	err |= ps8622_set(cl, 0x04, 0xe2, 0x80); /* [7] RCO SS enable */
+	/* RPHY Setting */
+	err |= ps8622_set(cl, 0x04, 0x8a, 0x0c); /* [3:2] CDR tune wait cycle
+						  * before measure for fine tune
+						  * b00: 1us b01: 0.5us b10:2us
+						  * b11: 4us */
+	err |= ps8622_set(cl, 0x04, 0x89, 0x08); /* [3] RFD always on */
+	err |= ps8622_set(cl, 0x04, 0x71, 0x2d); /* CTN lock in/out:
+						  * 20000ppm/80000ppm.
+						  * Lock out 2 times. */
+	/* 2.7G CDR settings */
+	err |= ps8622_set(cl, 0x04, 0x7d, 0x07); /* NOF=40LSB for HBR CDR
+						  * setting */
+	err |= ps8622_set(cl, 0x04, 0x7b, 0x00); /* [1:0] Fmin=+4bands */
+	err |= ps8622_set(cl, 0x04, 0x7a, 0xfd); /* [7:5] DCO_FTRNG=+-40% */
+	/* 1.62G CDR settings */
+	err |= ps8622_set(cl, 0x04, 0xc0, 0x12); /* [5:2]NOF=64LSB [1:0]DCO
+						  * scale is 2/5 */
+	err |= ps8622_set(cl, 0x04, 0xc1, 0x92); /* Gitune=-37% */
+	err |= ps8622_set(cl, 0x04, 0xc2, 0x1c); /* Fbstep=100% */
+	err |= ps8622_set(cl, 0x04, 0x32, 0x80); /* [7] LOS signal disable */
+	/* RPIO Setting */
+	err |= ps8622_set(cl, 0x04, 0x00, 0xb0); /* [7:4] LVDS driver bias
+						  * current : 75% (250mV swing)
+						  * */
+	err |= ps8622_set(cl, 0x04, 0x15, 0x40); /* [7:6] Right-bar GPIO output
+						  * strength is 8mA */
+	/* EQ Training State Machine Setting */
+	err |= ps8622_set(cl, 0x04, 0x54, 0x10); /* RCO calibration start */
+	/* Logic, needs more than 10 I2C command */
+	err |= ps8622_set(cl, 0x01, 0x02, 0x80 | ps_bridge->max_lane_count);
+						 /* [4:0] MAX_LANE_COUNT set to
+						  * max supported lanes */
+	err |= ps8622_set(cl, 0x01, 0x21, 0x80 | ps_bridge->lane_count);
+						 /* [4:0] LANE_COUNT_SET set to
+						  * chosen lane count */
+	err |= ps8622_set(cl, 0x00, 0x52, 0x20);
+	err |= ps8622_set(cl, 0x00, 0xf1, 0x03); /* HPD CP toggle enable */
+	err |= ps8622_set(cl, 0x00, 0x62, 0x41);
+	err |= ps8622_set(cl, 0x00, 0xf6, 0x01); /* Counter number, add 1ms
+						  * counter delay */
+	err |= ps8622_set(cl, 0x00, 0x77, 0x06); /* [6]PWM function control by
+						  * DPCD0040f[7], default is PWM
+						  * block always works. */
+	err |= ps8622_set(cl, 0x00, 0x4c, 0x04); /* 04h Adjust VTotal tolerance
+						  * to fix the 30Hz no display
+						  * issue */
+	err |= ps8622_set(cl, 0x01, 0xc0, 0x00); /* DPCD00400='h00, Parade OUI =
+						  * 'h001cf8 */
+	err |= ps8622_set(cl, 0x01, 0xc1, 0x1c); /* DPCD00401='h1c */
+	err |= ps8622_set(cl, 0x01, 0xc2, 0xf8); /* DPCD00402='hf8 */
+	err |= ps8622_set(cl, 0x01, 0xc3, 0x44); /* DPCD403~408 = ASCII code
+						  * D2SLV5='h4432534c5635 */
+	err |= ps8622_set(cl, 0x01, 0xc4, 0x32); /* DPCD404 */
+	err |= ps8622_set(cl, 0x01, 0xc5, 0x53); /* DPCD405 */
+	err |= ps8622_set(cl, 0x01, 0xc6, 0x4c); /* DPCD406 */
+	err |= ps8622_set(cl, 0x01, 0xc7, 0x56); /* DPCD407 */
+	err |= ps8622_set(cl, 0x01, 0xc8, 0x35); /* DPCD408 */
+	err |= ps8622_set(cl, 0x01, 0xca, 0x01); /* DPCD40A, Initial Code major
+						  * revision '01' */
+	err |= ps8622_set(cl, 0x01, 0xcb, 0x05); /* DPCD40B, Initial Code minor
+						  * revision '05' */
+	if (ps_bridge->bl) {
+		err |= ps8622_set(cl, 0x01, 0xa5, 0xa0);
+						/* DPCD720, internal PWM */
+		err |= ps8622_set(cl, 0x01, 0xa7,
+				ps_bridge->bl->props.brightness);
+						 /* FFh for 100% brightness,
+						  *  0h for 0% brightness */
+	} else {
+		err |= ps8622_set(cl, 0x01, 0xa5, 0x80);
+						/* DPCD720, external PWM */
+	}
+	err |= ps8622_set(cl, 0x01, 0xcc, 0x13); /* Set LVDS output as 6bit-VESA
+						  * mapping, single LVDS channel
+						  * */
+	err |= ps8622_set(cl, 0x02, 0xb1, 0x20); /* Enable SSC set by register
+						  * */
+	err |= ps8622_set(cl, 0x04, 0x10, 0x16); /* Set SSC enabled and +/-1%
+						  * central spreading */
+	/* Logic end */
+	err |= ps8622_set(cl, 0x04, 0x59, 0x60); /* MPU Clock source: LC => RCO
+						  * */
+	err |= ps8622_set(cl, 0x04, 0x54, 0x14); /* LC -> RCO */
+	err |= ps8622_set(cl, 0x02, 0xa1, 0x91); /* HPD high */
+
+	return err ? -EIO : 0;
+}
+
+static int ps8622_backlight_update(struct backlight_device *bl)
+{
+	struct ps8622_bridge *ps_bridge = dev_get_drvdata(&bl->dev);
+	int ret, brightness = bl->props.brightness;
+
+	if (bl->props.power != FB_BLANK_UNBLANK ||
+	    bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
+		brightness = 0;
+
+	mutex_lock(&ps_bridge->enable_mutex);
+
+	if (!ps_bridge->enabled) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = ps8622_set(ps_bridge->client, 0x01, 0xa7, brightness);
+
+out:
+	mutex_unlock(&ps_bridge->enable_mutex);
+	return ret;
+}
+
+static int ps8622_backlight_get(struct backlight_device *bl)
+{
+	return bl->props.brightness;
+}
+
+static const struct backlight_ops ps8622_backlight_ops = {
+	.update_status	= ps8622_backlight_update,
+	.get_brightness	= ps8622_backlight_get,
+};
+
+static void ps8622_pre_enable(struct drm_bridge *bridge)
+{
+	struct ps8622_bridge *ps_bridge = bridge->driver_private;
+	int ret;
+
+	mutex_lock(&ps_bridge->enable_mutex);
+	if (ps_bridge->enabled)
+		goto out;
+
+	if (gpio_is_valid(ps_bridge->gpio_rst_n))
+		gpio_set_value(ps_bridge->gpio_rst_n, 0);
+
+	if (ps_bridge->v12) {
+		ret = regulator_enable(ps_bridge->v12);
+		if (ret)
+			DRM_ERROR("fails to enable ps_bridge->v12");
+	}
+
+	drm_panel_pre_enable(ps_bridge->panel);
+
+	if (gpio_is_valid(ps_bridge->gpio_slp_n))
+		gpio_set_value(ps_bridge->gpio_slp_n, 1);
+
+	/*
+	 * T1 is the range of time that it takes for the power to rise after we
+	 * enable the lcd fet. T2 is the range of time in which the data sheet
+	 * specifies we should deassert the reset pin.
+	 *
+	 * If it takes T1.max for the power to rise, we need to wait atleast
+	 * T2.min before deasserting the reset pin. If it takes T1.min for the
+	 * power to rise, we need to wait at most T2.max before deasserting the
+	 * reset pin.
+	 */
+	usleep_range(PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US,
+		     PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US);
+
+	if (gpio_is_valid(ps_bridge->gpio_rst_n))
+		gpio_set_value(ps_bridge->gpio_rst_n, 1);
+
+	ret = ps8622_send_config(ps_bridge);
+	if (ret)
+		DRM_ERROR("Failed to send config to bridge (%d)\n", ret);
+
+	ps_bridge->enabled = true;
+
+	mutex_unlock(&ps_bridge->enable_mutex);
+	return;
+
+out:
+
+	mutex_unlock(&ps_bridge->enable_mutex);
+}
+
+static void ps8622_enable(struct drm_bridge *bridge)
+{
+	struct ps8622_bridge *ps_bridge = bridge->driver_private;
+
+	mutex_lock(&ps_bridge->enable_mutex);
+	if (ps_bridge->enabled)
+		drm_panel_enable(ps_bridge->panel);
+	mutex_unlock(&ps_bridge->enable_mutex);
+}
+
+static void ps8622_disable(struct drm_bridge *bridge)
+{
+	struct ps8622_bridge *ps_bridge = bridge->driver_private;
+
+	mutex_lock(&ps_bridge->enable_mutex);
+
+	if (!ps_bridge->enabled)
+		goto out;
+
+	ps_bridge->enabled = false;
+
+	drm_panel_disable(ps_bridge->panel);
+	msleep(PS8622_PWMO_END_T12_MS);
+
+	/*
+	 * This doesn't matter if the regulators are turned off, but something
+	 * else might keep them on. In that case, we want to assert the slp gpio
+	 * to lower power.
+	 */
+	if (gpio_is_valid(ps_bridge->gpio_slp_n))
+		gpio_set_value(ps_bridge->gpio_slp_n, 0);
+
+	drm_panel_post_disable(ps_bridge->panel);
+	if (ps_bridge->v12)
+		regulator_disable(ps_bridge->v12);
+
+	/*
+	 * Sleep for at least the amount of time that it takes the power rail to
+	 * fall to prevent asserting the rst gpio from doing anything.
+	 */
+	usleep_range(PS8622_POWER_FALL_T16_MAX_US,
+		     2 * PS8622_POWER_FALL_T16_MAX_US);
+	if (gpio_is_valid(ps_bridge->gpio_rst_n))
+		gpio_set_value(ps_bridge->gpio_rst_n, 0);
+
+	msleep(PS8622_POWER_OFF_T17_MS);
+
+out:
+	mutex_unlock(&ps_bridge->enable_mutex);
+}
+
+static void ps8622_post_disable(struct drm_bridge *bridge)
+{
+}
+
+static void ps8622_destroy(struct drm_bridge *bridge)
+{
+	struct ps8622_bridge *ps_bridge = bridge->driver_private;
+
+	drm_bridge_cleanup(bridge);
+	if (ps_bridge->bl)
+		backlight_device_unregister(ps_bridge->bl);
+
+	put_device(&ps_bridge->client->dev);
+}
+
+static const struct drm_bridge_funcs ps8622_bridge_funcs = {
+	.pre_enable = ps8622_pre_enable,
+	.enable = ps8622_enable,
+	.disable = ps8622_disable,
+	.post_disable = ps8622_post_disable,
+	.destroy = ps8622_destroy,
+};
+
+static int ps8622_get_modes(struct drm_connector *connector)
+{
+	struct ps8622_bridge *ps_bridge;
+	struct drm_display_mode *new_mode;
+
+	ps_bridge = container_of(connector, struct ps8622_bridge, connector);
+
+	new_mode = drm_mode_duplicate(connector->dev, &ps_bridge->mode);
+	if (!new_mode) {
+		DRM_ERROR("Failed to duplicate ps_bridge mode!\n");
+		return 0;
+	}
+	drm_mode_probed_add(connector, new_mode);
+	return 1;
+}
+
+static int ps8622_mode_valid(struct drm_connector *connector,
+		struct drm_display_mode *mode)
+{
+	struct ps8622_bridge *ps_bridge;
+
+	ps_bridge = container_of(connector, struct ps8622_bridge, connector);
+
+	return drm_mode_equal(mode, &ps_bridge->mode) ? MODE_OK : MODE_BAD;
+}
+
+static struct drm_encoder *ps8622_best_encoder(struct drm_connector *connector)
+{
+	struct ps8622_bridge *ps_bridge;
+
+	ps_bridge = container_of(connector, struct ps8622_bridge, connector);
+
+	return ps_bridge->encoder;
+}
+
+static const struct drm_connector_helper_funcs ps8622_connector_helper_funcs = {
+	.get_modes = ps8622_get_modes,
+	.mode_valid = ps8622_mode_valid,
+	.best_encoder = ps8622_best_encoder,
+};
+
+static enum drm_connector_status ps8622_detect(struct drm_connector *connector,
+		bool force)
+{
+	return connector_status_connected;
+}
+
+static void ps8622_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs ps8622_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = ps8622_detect,
+	.destroy = ps8622_connector_destroy,
+};
+
+static const struct of_device_id ps8622_devices[] = {
+	{
+		.compatible = "parade,ps8622",
+		.data	= &ps8622_data,
+	}, {
+		.compatible = "parade,ps8625",
+		.data	= &ps8625_data,
+	}, {
+		/* end node */
+	}
+};
+
+int ps8622_init(struct drm_device *dev, struct drm_encoder *encoder,
+		struct i2c_client *client, struct device_node *node,
+		struct drm_panel *panel)
+{
+	int ret;
+	const struct of_device_id *match;
+	const struct ps8622_device_data *device_data;
+	struct drm_bridge *bridge;
+	struct ps8622_bridge *ps_bridge;
+
+	node = of_find_matching_node_and_match(NULL, ps8622_devices, &match);
+	if (!node)
+		return -ENODEV;
+
+	bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL);
+	if (!bridge) {
+		DRM_ERROR("Failed to allocate drm bridge\n");
+		return -ENOMEM;
+	}
+
+	ps_bridge = devm_kzalloc(dev->dev, sizeof(*ps_bridge), GFP_KERNEL);
+	if (!ps_bridge) {
+		DRM_ERROR("could not allocate ps bridge\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&ps_bridge->enable_mutex);
+
+	device_data = match->data;
+	ps_bridge->client = client;
+	ps_bridge->encoder = encoder;
+	ps_bridge->bridge = bridge;
+	ps_bridge->v12 = devm_regulator_get(&client->dev, "vdd_bridge");
+	if (IS_ERR(ps_bridge->v12)) {
+		DRM_INFO("no 1.2v regulator found for PS8622\n");
+		ps_bridge->v12 = NULL;
+	}
+
+	ps_bridge->gpio_slp_n = of_get_named_gpio(node, "sleep-gpio", 0);
+	if (gpio_is_valid(ps_bridge->gpio_slp_n)) {
+		ret = devm_gpio_request_one(&client->dev, ps_bridge->gpio_slp_n,
+					    GPIOF_OUT_INIT_HIGH,
+					    "PS8622_SLP_N");
+		if (ret)
+			goto err_client;
+	}
+
+	ps_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0);
+	if (gpio_is_valid(ps_bridge->gpio_rst_n)) {
+		/*
+		 * Assert the reset pin high to avoid the bridge being
+		 * initialized prematurely
+		 */
+		ret = devm_gpio_request_one(&client->dev, ps_bridge->gpio_rst_n,
+					    GPIOF_OUT_INIT_HIGH,
+					    "PS8622_RST_N");
+		if (ret)
+			goto err_client;
+	}
+
+	ps_bridge->max_lane_count = device_data->max_lane_count;
+
+	if (of_property_read_u8(node, "lane-count", &ps_bridge->lane_count))
+		ps_bridge->lane_count = ps_bridge->max_lane_count;
+	else if (ps_bridge->lane_count > ps_bridge->max_lane_count) {
+		DRM_ERROR("lane-count property is too high for DP bridge\n");
+		ps_bridge->lane_count = ps_bridge->max_lane_count;
+	}
+
+	if (!of_find_property(node, "use-external-pwm", NULL)) {
+		ps_bridge->bl = backlight_device_register("ps8622-backlight",
+				dev->dev, ps_bridge, &ps8622_backlight_ops,
+				NULL);
+		if (IS_ERR(ps_bridge->bl)) {
+			DRM_ERROR("failed to register backlight\n");
+			ret = PTR_ERR(ps_bridge->bl);
+			ps_bridge->bl = NULL;
+			goto err_client;
+		}
+		ps_bridge->bl->props.max_brightness = PS8622_MAX_BRIGHTNESS;
+		ps_bridge->bl->props.brightness = PS8622_MAX_BRIGHTNESS;
+	}
+
+	ret = of_get_drm_display_mode(node, &ps_bridge->mode, 0);
+	if (ret) {
+		DRM_ERROR("Failed to get display mode, ret=%d\n", ret);
+		goto err_backlight;
+	}
+
+	ret = drm_bridge_init(dev, ps_bridge->bridge, &ps8622_bridge_funcs);
+	if (ret) {
+		DRM_ERROR("Failed to initialize bridge with drm\n");
+		goto err_backlight;
+	}
+
+	if (panel) {
+		ps_bridge->panel = panel;
+		drm_panel_attach(ps_bridge->panel, &ps_bridge->connector);
+	}
+
+	ps_bridge->bridge->driver_private = ps_bridge;
+	encoder->bridge = ps_bridge->bridge;
+	ps_bridge->enabled = false;
+	ps_bridge->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+	ret = drm_connector_init(dev, &ps_bridge->connector,
+			&ps8622_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
+	if (ret) {
+		DRM_ERROR("Failed to initialize connector with drm\n");
+		goto err_bridge;
+	}
+	drm_connector_helper_add(&ps_bridge->connector,
+			&ps8622_connector_helper_funcs);
+	drm_sysfs_connector_add(&ps_bridge->connector);
+	drm_mode_connector_attach_encoder(&ps_bridge->connector, encoder);
+
+	return 0;
+
+err_bridge:
+	drm_bridge_cleanup(ps_bridge->bridge);
+err_backlight:
+	if (ps_bridge->bl)
+		backlight_device_unregister(ps_bridge->bl);
+
+err_client:
+	put_device(&client->dev);
+	DRM_ERROR("device probe failed : %d\n", ret);
+	return ret;
+}
+EXPORT_SYMBOL(ps8622_init);
diff --git a/include/drm/bridge/ps8622.h b/include/drm/bridge/ps8622.h
new file mode 100644
index 0000000..4fc1959
--- /dev/null
+++ b/include/drm/bridge/ps8622.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DRM_BRIDGE_PS8622_H_
+#define _DRM_BRIDGE_PS8622_H_
+
+struct drm_device;
+struct drm_encoder;
+struct i2c_client;
+struct device_node;
+struct drm_panel;
+
+#ifdef CONFIG_DRM_PS8622
+
+extern int ps8622_init(struct drm_device *dev, struct drm_encoder *encoder,
+		struct i2c_client *client, struct device_node *node,
+		struct drm_panel *panel);
+
+#else
+
+static inline int ps8622_init(struct drm_device *dev,
+				struct drm_encoder *encoder,
+				struct i2c_client *client,
+				struct device_node *node,
+				struct drm_panel *panel)
+{
+	return -ENODEV;
+}
+
+#endif
+
+#endif
-- 
1.7.9.5

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

* [PATCH V2 9/9] drm/exynos: Add ps8622 lvds bridge discovery to DP driver
  2014-04-21 22:39 [PATCH V2 0/9] drm: exynos: few patches to enhance bridge chip support Ajay Kumar
                   ` (7 preceding siblings ...)
  2014-04-21 22:39 ` [PATCH V2 8/9] drm/bridge: Add PS8622 bridge driver Ajay Kumar
@ 2014-04-21 22:39 ` Ajay Kumar
  2014-04-22  9:27   ` Thierry Reding
  8 siblings, 1 reply; 58+ messages in thread
From: Ajay Kumar @ 2014-04-21 22:39 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: inki.dae, seanpaul, ajaynumb, joshi, prashanth.g, Ajay Kumar,
	Rahul Sharma

This patch adds ps8622 lvds bridge discovery code to the dp driver.

Signed-off-by: Rahul Sharma <Rahul.Sharma@samsung.com>
Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
---
Changes since V1:
	Pushing V1 for this as V2 because this patch holds good in this series.

 drivers/gpu/drm/exynos/exynos_dp_core.c |    9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
index 4853f31..0006412 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.c
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.c
@@ -30,6 +30,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_panel.h>
 #include <drm/bridge/ptn3460.h>
+#include <drm/bridge/ps8622.h>
 
 #include "exynos_drm_drv.h"
 #include "exynos_dp_core.h"
@@ -999,7 +1000,15 @@ static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
 				panel);
 		if (!ret)
 			return 1;
+	} else if (find_bridge("parade,ps8625", &bridge)) {
+
+		ret = ps8622_init(dev, encoder, bridge.client, bridge.node,
+				panel);
+
+		if (!ret)
+			return 1;
 	}
+
 	return 0;
 }
 
-- 
1.7.9.5

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

* Re: [PATCH V2 1/9] drm/exynos: dp: support hotplug detection via GPIO
  2014-04-21 22:39 ` [PATCH V2 1/9] drm/exynos: dp: support hotplug detection via GPIO Ajay Kumar
@ 2014-04-22  7:13   ` Jingoo Han
  2014-05-09  0:57     ` Jingoo Han
  0 siblings, 1 reply; 58+ messages in thread
From: Jingoo Han @ 2014-04-22  7:13 UTC (permalink / raw)
  To: 'Ajay Kumar'
  Cc: linux-samsung-soc, seanpaul, 'Andrew Bresticker',
	joshi, dri-devel, ajaynumb, prashanth.g, 'Rahul Sharma'

On Tuesday, April 22, 2014 7:39 AM, Ajay Kumar wrote:
> 
> From: Andrew Bresticker <abrestic@chromium.org>
> 
> Certain bridge chips use a GPIO to indicate the cable status instead
> of the I_DP_HPD pin.  This adds an optional device-tree property,
> "samsung,hpd-gpio", to the exynos-dp controller which indicates that
> the specified GPIO should be used for hotplug detection.
> The GPIO is then set up as an edge-triggered interrupt where the
> rising edge indicates hotplug-in and the falling edge indicates hotplug-out.
> 
> Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>

Acked-by: Jingoo Han <jg1.han@samsung.com>

Best regards,
Jingoo Han

> ---
> Changes since V1:
> 	Address reiew comments from Jingoo Han
> 
>  .../devicetree/bindings/video/exynos_dp.txt        |    4 ++
>  drivers/gpu/drm/exynos/exynos_dp_core.c            |   32 ++++++++++++--
>  drivers/gpu/drm/exynos/exynos_dp_core.h            |    1 +
>  drivers/gpu/drm/exynos/exynos_dp_reg.c             |   44 ++++++++++++++------
>  4 files changed, 66 insertions(+), 15 deletions(-)

[.....]

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

* Re: [PATCH V2 3/9] drm/panel: Add driver for exynos_dp based panels
  2014-04-21 22:39 ` [PATCH V2 3/9] drm/panel: Add driver for exynos_dp based panels Ajay Kumar
@ 2014-04-22  7:22   ` Jingoo Han
  2014-04-22 14:37     ` Ajay kumar
  2014-04-22  8:26   ` Thierry Reding
  1 sibling, 1 reply; 58+ messages in thread
From: Jingoo Han @ 2014-04-22  7:22 UTC (permalink / raw)
  To: 'Ajay Kumar'
  Cc: linux-samsung-soc, seanpaul, joshi, dri-devel, ajaynumb, prashanth.g

On Tuesday, April 22, 2014 7:39 AM, Ajay Kumar wrote:
> 
> This patch adds a simple driver to handle all the LCD and LED
> powerup/down routines needed to support eDP/eDP-LVDS panels
> supported on exynos boards.
> 
> The LCD and LED units are usually powered up via regulators,
> and almost on all boards, we will have a BL_EN pin to enable/
> disable the backlight. Sometimes, we can have LCD_EN switches
> as well. The routines in this driver can be used to control
> panel power sequence on such boards.
> 
> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
> ---
> Changes since V1:
> 	Added routine for post_disable, removed few unwanted headers.
> 	Refactored a lot of code.
> 
>  .../devicetree/bindings/panel/exynos-dp-panel.txt  |   45 ++++
>  drivers/gpu/drm/panel/Kconfig                      |    9 +
>  drivers/gpu/drm/panel/Makefile                     |    1 +
>  drivers/gpu/drm/panel/panel-exynos-dp.c            |  251 ++++++++++++++++++++
>  4 files changed, 306 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/panel/exynos-dp-panel.txt
>  create mode 100644 drivers/gpu/drm/panel/panel-exynos-dp.c
> 
> diff --git a/Documentation/devicetree/bindings/panel/exynos-dp-panel.txt
> b/Documentation/devicetree/bindings/panel/exynos-dp-panel.txt
> new file mode 100644
> index 0000000..3fbca54
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/panel/exynos-dp-panel.txt
> @@ -0,0 +1,45 @@
> +exynos_DP_panel/DP_to_LVDS_panel

Please fix it as below.

+Exynos DP panel/DP to LVDS panel

[....]

> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index 4ec874d..ea9d5ac 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -30,4 +30,13 @@ config DRM_PANEL_S6E8AA0
>  	select DRM_MIPI_DSI
>  	select VIDEOMODE_HELPERS
> 
> +config DRM_PANEL_EXYNOS_DP
> +	tristate "support for DP panels"

It looks very general. Please fix it as below.

+	tristate "support for Exynos DP panels"

Best regards,
Jingoo Han

> +	depends on OF && DRM_PANEL && DRM_EXYNOS_DP
> +	help
> +	  DRM panel driver for DP panels and LVDS connected via DP bridges
> +	  that need at most a regulator for LCD unit, a regulator for LED unit
> +	  and/or enable GPIOs for LCD or LED units. Delay values can also be
> +	  specified to support powerup and powerdown process.
> +

[.....]

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

* Re: [PATCH V2 2/9] drm/panel: add pre_enable and post_disable routines
  2014-04-21 22:39 ` [PATCH V2 2/9] drm/panel: add pre_enable and post_disable routines Ajay Kumar
@ 2014-04-22  8:19   ` Thierry Reding
  2014-04-22 14:36     ` Ajay kumar
  0 siblings, 1 reply; 58+ messages in thread
From: Thierry Reding @ 2014-04-22  8:19 UTC (permalink / raw)
  To: Ajay Kumar
  Cc: linux-samsung-soc, seanpaul, joshi, dri-devel, ajaynumb, prashanth.g


[-- Attachment #1.1: Type: text/plain, Size: 1385 bytes --]

On Tue, Apr 22, 2014 at 04:09:11AM +0530, Ajay Kumar wrote:
> Most of the panels need an init sequence as mentioned below:
> 	-- poweron LCD unit/LCD_EN
> 	-- start video data
> 	-- poweron LED unit/BL_EN
> And, a de-init sequence as mentioned below:
> 	-- poweroff LED unit/BL_EN
> 	-- stop video data
> 	-- poweroff LCD unit/LCD_EN
> With existing callbacks for drm panel, we cannot accomodate such panels,
> since only two callbacks, i.e "panel_enable" and panel_disable are supported.
> 
> This patch adds:
> 	-- "pre_enable" callback which can be called before
> 	the actual video data is on, and then call the "enable"
> 	callback after the video data is available.
> 
> 	-- "post_disable" callback which can be called after
> 	the video data is off, and use "disable" callback
> 	to do something before switching off the video data.
> 
> Now, we can easily map the above scenario as shown below:
> 	poweron LCD unit/LCD_EN = "pre_enable" callback
> 	poweron LED unit/BL_EN = "enable" callback
> 	poweroff LED unit/BL_EN = "disable" callback
> 	poweroff LCD unit/LCD_EN = "post_disable" callback

I don't like this. What happens when the next panel comes around that
has a yet slightly different requirement? Will we introduce a new
pre_pre_enable() and post_post_disable() function then?

There's got to be a better way to solve this.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH V2 3/9] drm/panel: Add driver for exynos_dp based panels
  2014-04-21 22:39 ` [PATCH V2 3/9] drm/panel: Add driver for exynos_dp based panels Ajay Kumar
  2014-04-22  7:22   ` Jingoo Han
@ 2014-04-22  8:26   ` Thierry Reding
  2014-04-22 14:53     ` Ajay kumar
  1 sibling, 1 reply; 58+ messages in thread
From: Thierry Reding @ 2014-04-22  8:26 UTC (permalink / raw)
  To: Ajay Kumar
  Cc: dri-devel, linux-samsung-soc, seanpaul, joshi, ajaynumb, prashanth.g

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

On Tue, Apr 22, 2014 at 04:09:12AM +0530, Ajay Kumar wrote:
> This patch adds a simple driver to handle all the LCD and LED
> powerup/down routines needed to support eDP/eDP-LVDS panels
> supported on exynos boards.
> 
> The LCD and LED units are usually powered up via regulators,
> and almost on all boards, we will have a BL_EN pin to enable/
> disable the backlight. Sometimes, we can have LCD_EN switches
> as well. The routines in this driver can be used to control
> panel power sequence on such boards.
> 
> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
> ---
> Changes since V1:
> 	Added routine for post_disable, removed few unwanted headers.
> 	Refactored a lot of code.
> 
>  .../devicetree/bindings/panel/exynos-dp-panel.txt  |   45 ++++
>  drivers/gpu/drm/panel/Kconfig                      |    9 +
>  drivers/gpu/drm/panel/Makefile                     |    1 +
>  drivers/gpu/drm/panel/panel-exynos-dp.c            |  251 ++++++++++++++++++++
>  4 files changed, 306 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/panel/exynos-dp-panel.txt
>  create mode 100644 drivers/gpu/drm/panel/panel-exynos-dp.c

What this patch does is in no way Exynos specific. It is also not eDP
specific.

This conflates panel drivers with board drivers in a strange way. Panel
drivers should be for *panels*, not for a given SoC or specific outputs
on that SoC.

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

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

* Re: [PATCH V2 4/9] drm/exynos: add exynos_dp_panel driver registration to drm driver
  2014-04-21 22:39 ` [PATCH V2 4/9] drm/exynos: add exynos_dp_panel driver registration to drm driver Ajay Kumar
@ 2014-04-22  8:33   ` Thierry Reding
  2014-04-22 15:03     ` Ajay kumar
  0 siblings, 1 reply; 58+ messages in thread
From: Thierry Reding @ 2014-04-22  8:33 UTC (permalink / raw)
  To: Ajay Kumar
  Cc: linux-samsung-soc, seanpaul, joshi, dri-devel, ajaynumb, prashanth.g


[-- Attachment #1.1: Type: text/plain, Size: 2081 bytes --]

On Tue, Apr 22, 2014 at 04:09:13AM +0530, Ajay Kumar wrote:
> Register exynos_dp_panel before the list of exynos crtcs and
> connectors are probed.
> 
> This is needed because exynos_dp_panel should be registered to
> the drm_panel list via panel-exynos-dp probe, i.e much before
> exynos_dp_bind calls of_drm_find_panel().
> 
> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
> ---
> Changes since V1:
> 	Added platform_driver_unregister(&exynos_dp_panel_driver) to
> 	exynos_drm_platform_remove as per Jingoo Han's correction
> 
>  drivers/gpu/drm/exynos/exynos_drm_drv.c |   15 +++++++++++++++
>  drivers/gpu/drm/exynos/exynos_drm_drv.h |    1 +
>  2 files changed, 16 insertions(+)
> 
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> index 1d653f8..2db7f67 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> @@ -530,12 +530,23 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
>  		goto err_unregister_ipp_drv;
>  #endif
>  
> +#ifdef CONFIG_DRM_PANEL_EXYNOS_DP
> +	ret = platform_driver_register(&exynos_dp_panel_driver);
> +	if (ret < 0)
> +		goto err_unregister_dp_panel;
> +#endif

No, this is not how you're supposed to use DRM panel drivers. The idea
is that you write a standalone driver for a given panel.

What you do here has a number of problems. For one it's a driver that's
tightly coupled to Exynos SoCs. But if I have a different SoC that uses
the same panel I want to be able to use the same driver, and not have to
rewrite the driver for my SoC.

Another problem is that you're assuming here that the driver is built in
and it will break if you try to build either Exynos DRM or the panel
driver as a module. This is perhaps nothing you care about right now,
but eventually people will want to ship a single kernel that can run on
a number of SoCs. But if we keep adding things like this, that kernel
will keep growing in size until it no longer fits in any kind of memory.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH V2 5/9] drm/exynos: dp: modify driver to support drm_panel
  2014-04-21 22:39 ` [PATCH V2 5/9] drm/exynos: dp: modify driver to support drm_panel Ajay Kumar
@ 2014-04-22  8:37   ` Thierry Reding
  0 siblings, 0 replies; 58+ messages in thread
From: Thierry Reding @ 2014-04-22  8:37 UTC (permalink / raw)
  To: Ajay Kumar
  Cc: linux-samsung-soc, seanpaul, joshi, dri-devel, ajaynumb, prashanth.g


[-- Attachment #1.1: Type: text/plain, Size: 1235 bytes --]

On Tue, Apr 22, 2014 at 04:09:14AM +0530, Ajay Kumar wrote:
[...]
> diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
[...]
> @@ -1299,6 +1308,15 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
>  
>  	INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug);
>  
> +	panel_node = of_find_compatible_node(NULL, NULL,
> +						"samsung,exynos-dp-panel");

No, please don't do this. It will break as soon as you have two panels
of the same type in one system.

Also the compatible value of a panel should describe the specific panel
in use (e.g. "samsung,s6e8aa0"), so you shouldn't rely on "magic" like
this. Use a phandle to find the panel's struct device_node.

[...]
> diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.h b/drivers/gpu/drm/exynos/exynos_dp_core.h
> index 56fa43e..9dc7991 100644
> --- a/drivers/gpu/drm/exynos/exynos_dp_core.h
> +++ b/drivers/gpu/drm/exynos/exynos_dp_core.h
> @@ -148,6 +148,7 @@ struct exynos_dp_device {
>  	struct drm_device	*drm_dev;
>  	struct drm_connector	connector;
>  	struct drm_encoder	*encoder;
> +	struct drm_panel	*drm_panel;

I don't think you need the drm_ prefix here.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH V2 6/9] drm/bridge: ptn3460: enable polling based detection
  2014-04-21 22:39 ` [PATCH V2 6/9] drm/bridge: ptn3460: enable polling based detection Ajay Kumar
@ 2014-04-22  8:43   ` Thierry Reding
  0 siblings, 0 replies; 58+ messages in thread
From: Thierry Reding @ 2014-04-22  8:43 UTC (permalink / raw)
  To: Ajay Kumar
  Cc: linux-samsung-soc, seanpaul, joshi, dri-devel, ajaynumb,
	prashanth.g, Rahul Sharma


[-- Attachment #1.1: Type: text/plain, Size: 575 bytes --]

On Tue, Apr 22, 2014 at 04:09:15AM +0530, Ajay Kumar wrote:
> From: Rahul Sharma <Rahul.Sharma@samsung.com>
> 
> Add DRM_CONNECTOR_POLL_HPD to the set of connector flags while
> registering drm_connector for ptn3460.
> 
> Signed-off-by: Rahul Sharma <Rahul.Sharma@samsung.com>
> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>

This needs to explain *why* you think this change is necessary. As far
as I can see, the PTN3460 bridge doesn't support hotplug detection at
all, in which case setting DRM_CONNECTOR_POLL_HPD would be completely
wrong.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH V2 8/9] drm/bridge: Add PS8622 bridge driver
  2014-04-21 22:39 ` [PATCH V2 8/9] drm/bridge: Add PS8622 bridge driver Ajay Kumar
@ 2014-04-22  8:43   ` Jingoo Han
  2014-04-22 13:57     ` Ajay kumar
  2014-04-22 13:31   ` Sean Paul
  1 sibling, 1 reply; 58+ messages in thread
From: Jingoo Han @ 2014-04-22  8:43 UTC (permalink / raw)
  To: 'Ajay Kumar'
  Cc: linux-samsung-soc, seanpaul, 'Andrew Bresticker',
	joshi, dri-devel, ajaynumb, prashanth.g, 'Rahul Sharma'

On Tuesday, April 22, 2014 7:39 AM, Ajay Kumar wrote:
> 
> This patch adds a drm_bridge driver for the PS8622 DisplayPort to LVDS
> bridge chip.
> 
> Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
> Signed-off-by: Sean Paul <seanpaul@chromium.org>
> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
> ---
> Changes since V1:
> 	Pushing V1 for this as V2 because this patch holds good in this series.
> 
>  drivers/gpu/drm/bridge/Kconfig  |    7 +
>  drivers/gpu/drm/bridge/Makefile |    1 +
>  drivers/gpu/drm/bridge/ps8622.c |  566 +++++++++++++++++++++++++++++++++++++++
>  include/drm/bridge/ps8622.h     |   42 +++
>  4 files changed, 616 insertions(+)
>  create mode 100644 drivers/gpu/drm/bridge/ps8622.c
>  create mode 100644 include/drm/bridge/ps8622.h
> 
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index 3bc6845..aba21fc 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -4,3 +4,10 @@ config DRM_PTN3460
>  	select DRM_KMS_HELPER
>  	select DRM_PANEL
>  	---help---
> +
> +config DRM_PS8622
> +	tristate "Parade eDP/LVDS bridge"
> +	depends on DRM
> +	select DRM_KMS_HELPER
> +	select DRM_PANEL

Please add the following select.

+       select BACKLIGHT_LCD_SUPPORT
+       select BACKLIGHT_CLASS_DEVICE

Without this, the following build issues happen.

drivers/gpu/drm/bridge/ps8622.c:353: undefined reference to `backlight_device_unregister'
drivers/built-in.o: In function `ps8622_init':
drivers/gpu/drm/bridge/ps8622.c:559: undefined reference to `backlight_device_unregister'
drivers/gpu/drm/bridge/ps8622.c:507: undefined reference to `backlight_device_register'

> +	---help---
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index b4733e1..d1b5daa 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -1,3 +1,4 @@
>  ccflags-y := -Iinclude/drm
> 
>  obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
> +obj-$(CONFIG_DRM_PS8622) += ps8622.o
> diff --git a/drivers/gpu/drm/bridge/ps8622.c b/drivers/gpu/drm/bridge/ps8622.c
> new file mode 100644
> index 0000000..1e6b3ca
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/ps8622.c

[.....]

> +static int ps8622_send_config(struct ps8622_bridge *ps_bridge)
> +{
> +	struct i2c_client *cl = ps_bridge->client;
> +	int err = 0;
> +
> +	/* wait 20ms after power ON */
> +	usleep_range(20000, 30000);
> +
> +	err |= ps8622_set(cl, 0x02, 0xa1, 0x01); /* HPD low */
> +	/* SW setting */
> +	err |= ps8622_set(cl, 0x04, 0x14, 0x01); /* [1:0] SW output 1.2V voltage
> +						  * is lower to 96% */

The comment style looks awkward.
Please choose one of two options. And change all comments in this file.

1. 

+	/* SW setting */
+	err |= ps8622_set(cl, 0x04, 0x14, 0x01); /* [1:0] SW output 1.2V voltage is lower to 96% */

2. 

+	/* SW setting */
+	/* [1:0] SW output 1.2V voltage is lower to 96% */
+	err |= ps8622_set(cl, 0x04, 0x14, 0x01);


> +	/* RCO SS setting */
> +	err |= ps8622_set(cl, 0x04, 0xe3, 0x20); /* [5:4] = b01 0.5%, b10 1%,
> +						  * b11 1.5% */
> +	err |= ps8622_set(cl, 0x04, 0xe2, 0x80); /* [7] RCO SS enable */
> +	/* RPHY Setting */
> +	err |= ps8622_set(cl, 0x04, 0x8a, 0x0c); /* [3:2] CDR tune wait cycle
> +						  * before measure for fine tune
> +						  * b00: 1us b01: 0.5us b10:2us
> +						  * b11: 4us */
> +	err |= ps8622_set(cl, 0x04, 0x89, 0x08); /* [3] RFD always on */
> +	err |= ps8622_set(cl, 0x04, 0x71, 0x2d); /* CTN lock in/out:
> +						  * 20000ppm/80000ppm.
> +						  * Lock out 2 times. */
> +	/* 2.7G CDR settings */
> +	err |= ps8622_set(cl, 0x04, 0x7d, 0x07); /* NOF=40LSB for HBR CDR
> +						  * setting */
> +	err |= ps8622_set(cl, 0x04, 0x7b, 0x00); /* [1:0] Fmin=+4bands */
> +	err |= ps8622_set(cl, 0x04, 0x7a, 0xfd); /* [7:5] DCO_FTRNG=+-40% */
> +	/* 1.62G CDR settings */
> +	err |= ps8622_set(cl, 0x04, 0xc0, 0x12); /* [5:2]NOF=64LSB [1:0]DCO
> +						  * scale is 2/5 */
> +	err |= ps8622_set(cl, 0x04, 0xc1, 0x92); /* Gitune=-37% */
> +	err |= ps8622_set(cl, 0x04, 0xc2, 0x1c); /* Fbstep=100% */
> +	err |= ps8622_set(cl, 0x04, 0x32, 0x80); /* [7] LOS signal disable */
> +	/* RPIO Setting */
> +	err |= ps8622_set(cl, 0x04, 0x00, 0xb0); /* [7:4] LVDS driver bias
> +						  * current : 75% (250mV swing)
> +						  * */
> +	err |= ps8622_set(cl, 0x04, 0x15, 0x40); /* [7:6] Right-bar GPIO output
> +						  * strength is 8mA */
> +	/* EQ Training State Machine Setting */
> +	err |= ps8622_set(cl, 0x04, 0x54, 0x10); /* RCO calibration start */
> +	/* Logic, needs more than 10 I2C command */
> +	err |= ps8622_set(cl, 0x01, 0x02, 0x80 | ps_bridge->max_lane_count);
> +						 /* [4:0] MAX_LANE_COUNT set to
> +						  * max supported lanes */
> +	err |= ps8622_set(cl, 0x01, 0x21, 0x80 | ps_bridge->lane_count);
> +						 /* [4:0] LANE_COUNT_SET set to
> +						  * chosen lane count */
> +	err |= ps8622_set(cl, 0x00, 0x52, 0x20);
> +	err |= ps8622_set(cl, 0x00, 0xf1, 0x03); /* HPD CP toggle enable */
> +	err |= ps8622_set(cl, 0x00, 0x62, 0x41);
> +	err |= ps8622_set(cl, 0x00, 0xf6, 0x01); /* Counter number, add 1ms
> +						  * counter delay */
> +	err |= ps8622_set(cl, 0x00, 0x77, 0x06); /* [6]PWM function control by
> +						  * DPCD0040f[7], default is PWM
> +						  * block always works. */
> +	err |= ps8622_set(cl, 0x00, 0x4c, 0x04); /* 04h Adjust VTotal tolerance
> +						  * to fix the 30Hz no display
> +						  * issue */
> +	err |= ps8622_set(cl, 0x01, 0xc0, 0x00); /* DPCD00400='h00, Parade OUI =
> +						  * 'h001cf8 */
> +	err |= ps8622_set(cl, 0x01, 0xc1, 0x1c); /* DPCD00401='h1c */
> +	err |= ps8622_set(cl, 0x01, 0xc2, 0xf8); /* DPCD00402='hf8 */
> +	err |= ps8622_set(cl, 0x01, 0xc3, 0x44); /* DPCD403~408 = ASCII code
> +						  * D2SLV5='h4432534c5635 */
> +	err |= ps8622_set(cl, 0x01, 0xc4, 0x32); /* DPCD404 */
> +	err |= ps8622_set(cl, 0x01, 0xc5, 0x53); /* DPCD405 */
> +	err |= ps8622_set(cl, 0x01, 0xc6, 0x4c); /* DPCD406 */
> +	err |= ps8622_set(cl, 0x01, 0xc7, 0x56); /* DPCD407 */
> +	err |= ps8622_set(cl, 0x01, 0xc8, 0x35); /* DPCD408 */
> +	err |= ps8622_set(cl, 0x01, 0xca, 0x01); /* DPCD40A, Initial Code major
> +						  * revision '01' */
> +	err |= ps8622_set(cl, 0x01, 0xcb, 0x05); /* DPCD40B, Initial Code minor
> +						  * revision '05' */
> +	if (ps_bridge->bl) {
> +		err |= ps8622_set(cl, 0x01, 0xa5, 0xa0);
> +						/* DPCD720, internal PWM */
> +		err |= ps8622_set(cl, 0x01, 0xa7,
> +				ps_bridge->bl->props.brightness);
> +						 /* FFh for 100% brightness,
> +						  *  0h for 0% brightness */
> +	} else {
> +		err |= ps8622_set(cl, 0x01, 0xa5, 0x80);
> +						/* DPCD720, external PWM */
> +	}
> +	err |= ps8622_set(cl, 0x01, 0xcc, 0x13); /* Set LVDS output as 6bit-VESA
> +						  * mapping, single LVDS channel
> +						  * */
> +	err |= ps8622_set(cl, 0x02, 0xb1, 0x20); /* Enable SSC set by register
> +						  * */
> +	err |= ps8622_set(cl, 0x04, 0x10, 0x16); /* Set SSC enabled and +/-1%
> +						  * central spreading */
> +	/* Logic end */
> +	err |= ps8622_set(cl, 0x04, 0x59, 0x60); /* MPU Clock source: LC => RCO
> +						  * */
> +	err |= ps8622_set(cl, 0x04, 0x54, 0x14); /* LC -> RCO */
> +	err |= ps8622_set(cl, 0x02, 0xa1, 0x91); /* HPD high */
> +
> +	return err ? -EIO : 0;
> +}
> +

[.....]

> +
> +static void ps8622_pre_enable(struct drm_bridge *bridge)
> +{
> +	struct ps8622_bridge *ps_bridge = bridge->driver_private;
> +	int ret;
> +
> +	mutex_lock(&ps_bridge->enable_mutex);
> +	if (ps_bridge->enabled)
> +		goto out;
> +
> +	if (gpio_is_valid(ps_bridge->gpio_rst_n))
> +		gpio_set_value(ps_bridge->gpio_rst_n, 0);
> +
> +	if (ps_bridge->v12) {
> +		ret = regulator_enable(ps_bridge->v12);
> +		if (ret)
> +			DRM_ERROR("fails to enable ps_bridge->v12");
> +	}
> +
> +	drm_panel_pre_enable(ps_bridge->panel);
> +
> +	if (gpio_is_valid(ps_bridge->gpio_slp_n))
> +		gpio_set_value(ps_bridge->gpio_slp_n, 1);
> +
> +	/*
> +	 * T1 is the range of time that it takes for the power to rise after we
> +	 * enable the lcd fet. T2 is the range of time in which the data sheet
> +	 * specifies we should deassert the reset pin.
> +	 *
> +	 * If it takes T1.max for the power to rise, we need to wait atleast
> +	 * T2.min before deasserting the reset pin. If it takes T1.min for the
> +	 * power to rise, we need to wait at most T2.max before deasserting the
> +	 * reset pin.
> +	 */
> +	usleep_range(PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US,
> +		     PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US);
> +
> +	if (gpio_is_valid(ps_bridge->gpio_rst_n))
> +		gpio_set_value(ps_bridge->gpio_rst_n, 1);
> +
> +	ret = ps8622_send_config(ps_bridge);
> +	if (ret)
> +		DRM_ERROR("Failed to send config to bridge (%d)\n", ret);
> +
> +	ps_bridge->enabled = true;
> +
> +	mutex_unlock(&ps_bridge->enable_mutex);
> +	return;
> +
> +out:
> +
> +	mutex_unlock(&ps_bridge->enable_mutex);
> +}

mutex_unlock() is duplicated. Also, 'return' is unnecessary.
Please fix it as below.

+	ps_bridge->enabled = true;
+
+out:
+	mutex_unlock(&ps_bridge->enable_mutex);
+}

> +
> +static void ps8622_enable(struct drm_bridge *bridge)
> +{
> +	struct ps8622_bridge *ps_bridge = bridge->driver_private;
> +
> +	mutex_lock(&ps_bridge->enable_mutex);
> +	if (ps_bridge->enabled)
> +		drm_panel_enable(ps_bridge->panel);
> +	mutex_unlock(&ps_bridge->enable_mutex);
> +}
> +
> +static void ps8622_disable(struct drm_bridge *bridge)
> +{
> +	struct ps8622_bridge *ps_bridge = bridge->driver_private;
> +
> +	mutex_lock(&ps_bridge->enable_mutex);
> +
> +	if (!ps_bridge->enabled)
> +		goto out;
> +
> +	ps_bridge->enabled = false;
> +
> +	drm_panel_disable(ps_bridge->panel);
> +	msleep(PS8622_PWMO_END_T12_MS);
> +
> +	/*
> +	 * This doesn't matter if the regulators are turned off, but something
> +	 * else might keep them on. In that case, we want to assert the slp gpio
> +	 * to lower power.
> +	 */
> +	if (gpio_is_valid(ps_bridge->gpio_slp_n))
> +		gpio_set_value(ps_bridge->gpio_slp_n, 0);
> +
> +	drm_panel_post_disable(ps_bridge->panel);
> +	if (ps_bridge->v12)
> +		regulator_disable(ps_bridge->v12);
> +
> +	/*
> +	 * Sleep for at least the amount of time that it takes the power rail to
> +	 * fall to prevent asserting the rst gpio from doing anything.
> +	 */
> +	usleep_range(PS8622_POWER_FALL_T16_MAX_US,
> +		     2 * PS8622_POWER_FALL_T16_MAX_US);
> +	if (gpio_is_valid(ps_bridge->gpio_rst_n))
> +		gpio_set_value(ps_bridge->gpio_rst_n, 0);
> +
> +	msleep(PS8622_POWER_OFF_T17_MS);
> +
> +out:
> +	mutex_unlock(&ps_bridge->enable_mutex);
> +}
> +
> +static void ps8622_post_disable(struct drm_bridge *bridge)
> +{
> +}

How about just removing this empty function?

[.....]

Best regards,
Jingoo Han

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

* Re: [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls
  2014-04-21 22:39 ` [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls Ajay Kumar
@ 2014-04-22  8:55   ` Thierry Reding
  2014-04-22 11:34   ` Rob Clark
  1 sibling, 0 replies; 58+ messages in thread
From: Thierry Reding @ 2014-04-22  8:55 UTC (permalink / raw)
  To: Ajay Kumar
  Cc: linux-samsung-soc, seanpaul, joshi, dri-devel, ajaynumb, prashanth.g


[-- Attachment #1.1: Type: text/plain, Size: 985 bytes --]

On Tue, Apr 22, 2014 at 04:09:16AM +0530, Ajay Kumar wrote:
[...]
> diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
[...]
> @@ -1012,9 +1013,16 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display,
>  	dp->encoder = encoder;
>  
>  	/* Pre-empt DP connector creation if there's a bridge */
> -	ret = exynos_drm_attach_lcd_bridge(dp->drm_dev, encoder);
> -	if (ret)
> +	ret = exynos_drm_attach_lcd_bridge(dp->drm_dev, encoder, dp->drm_panel);
> +	if (ret) {
> +		/*
> +		 * Also set "dp->drm_panel = NULL" so that we don't end up
> +		 * controlling panel power both in exynos_dp and bridge
> +		 * DPMS routines.
> +		 */
> +		dp->drm_panel = NULL;
>  		return 0;
> +	}

This kind of hack should set off an alarm that you're doing something
wrong. I'm not sure integration of bridges and panels has been thought
about or discussed a lot, but this doesn't look right to me at all.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH V2 9/9] drm/exynos: Add ps8622 lvds bridge discovery to DP driver
  2014-04-21 22:39 ` [PATCH V2 9/9] drm/exynos: Add ps8622 lvds bridge discovery to DP driver Ajay Kumar
@ 2014-04-22  9:27   ` Thierry Reding
  2014-04-22 17:20     ` Ajay kumar
  0 siblings, 1 reply; 58+ messages in thread
From: Thierry Reding @ 2014-04-22  9:27 UTC (permalink / raw)
  To: Ajay Kumar
  Cc: dri-devel, linux-samsung-soc, seanpaul, joshi, ajaynumb,
	prashanth.g, Rahul Sharma

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

On Tue, Apr 22, 2014 at 04:09:18AM +0530, Ajay Kumar wrote:
> This patch adds ps8622 lvds bridge discovery code to the dp driver.
> 
> Signed-off-by: Rahul Sharma <Rahul.Sharma@samsung.com>
> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
> ---
> Changes since V1:
> 	Pushing V1 for this as V2 because this patch holds good in this series.
> 
>  drivers/gpu/drm/exynos/exynos_dp_core.c |    9 +++++++++
>  1 file changed, 9 insertions(+)
> 
> diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
> index 4853f31..0006412 100644
> --- a/drivers/gpu/drm/exynos/exynos_dp_core.c
> +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c
> @@ -30,6 +30,7 @@
>  #include <drm/drm_crtc_helper.h>
>  #include <drm/drm_panel.h>
>  #include <drm/bridge/ptn3460.h>
> +#include <drm/bridge/ps8622.h>
>  
>  #include "exynos_drm_drv.h"
>  #include "exynos_dp_core.h"
> @@ -999,7 +1000,15 @@ static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
>  				panel);
>  		if (!ret)
>  			return 1;
> +	} else if (find_bridge("parade,ps8625", &bridge)) {

So this is where the inspiration for the of_find_compatible_node() in
the earlier patch came from.

> +		ret = ps8622_init(dev, encoder, bridge.client, bridge.node,
> +				panel);

Long-term I don't think this is going to scale very well. In my opinion
it would be much more useful to have the bridge driver initialize what
it can and then have the DRM driver call a generic initialization
function to bind it to the DRM device or encoder. Otherwise we have to
keep knowledge about the type of bridge in each driver that uses it,
whereas the goal (I think) would be to create a proper abstraction so
that the DRM driver doesn't have to know that kind of detail.

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

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

* Re: [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls
  2014-04-21 22:39 ` [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls Ajay Kumar
  2014-04-22  8:55   ` Thierry Reding
@ 2014-04-22 11:34   ` Rob Clark
  2014-04-22 13:53     ` Daniel Vetter
  2014-04-23 18:56     ` Ajay kumar
  1 sibling, 2 replies; 58+ messages in thread
From: Rob Clark @ 2014-04-22 11:34 UTC (permalink / raw)
  To: Ajay Kumar
  Cc: dri-devel, moderated list:ARM/S5P EXYNOS AR...,
	seanpaul, sunil joshi, ajaynumb, Prashanth G

So what about, rather than adding drm_panel support to each bridge
individually, we introduce a drm_panel_bridge (with a form of
chaining).. ie:

  struct drm_panel_bridge {
    struct drm_bridge base;
    struct drm_panel *panel;
    struct drm_bridge *bridge; /* optional */
  };

  static void drm_panel_bridge_enable(struct drm_bridge *bridge)
  {
    struct drm_panel_bridge *pb = to_panel_bridge(bridge);
    if (pb->bridge)
      pb->bridge->funcs->enable(pb->bridge);
    pb->panel->funcs->enable(pb->panel);
  }

... and so on.

If you don't need a real bridge, just create one of these w/
pb->bridge==NULL, otherwise create it as a wrapper for your real
bridge.

BR,
-R

On Mon, Apr 21, 2014 at 6:39 PM, Ajay Kumar <ajaykumar.rs@samsung.com> wrote:
> attach ptn3460 connector to drm_panel and support drm_panel routines,
> if a valid drm_panel object is passed to ptn3460_init.
>
> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
> ---
> Changes since V1:
>         Address few coding style comments from Jingoo Han
>
>  drivers/gpu/drm/bridge/Kconfig          |    1 +
>  drivers/gpu/drm/bridge/ptn3460.c        |   20 +++++++++++++++++++-
>  drivers/gpu/drm/exynos/exynos_dp_core.c |   16 ++++++++++++----
>  include/drm/bridge/ptn3460.h            |    6 ++++--
>  4 files changed, 36 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index 884923f..3bc6845 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -2,4 +2,5 @@ config DRM_PTN3460
>         tristate "PTN3460 DP/LVDS bridge"
>         depends on DRM
>         select DRM_KMS_HELPER
> +       select DRM_PANEL
>         ---help---
> diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
> index f1d2afc..3920202 100644
> --- a/drivers/gpu/drm/bridge/ptn3460.c
> +++ b/drivers/gpu/drm/bridge/ptn3460.c
> @@ -19,6 +19,7 @@
>  #include <linux/i2c.h>
>  #include <linux/gpio.h>
>  #include <linux/delay.h>
> +#include <drm/drm_panel.h>
>
>  #include "drmP.h"
>  #include "drm_edid.h"
> @@ -38,6 +39,7 @@ struct ptn3460_bridge {
>         struct i2c_client *client;
>         struct drm_encoder *encoder;
>         struct drm_bridge *bridge;
> +       struct drm_panel *panel;
>         struct edid *edid;
>         int gpio_pd_n;
>         int gpio_rst_n;
> @@ -126,6 +128,8 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge)
>                 gpio_set_value(ptn_bridge->gpio_rst_n, 1);
>         }
>
> +       drm_panel_pre_enable(ptn_bridge->panel);
> +
>         /*
>          * There's a bug in the PTN chip where it falsely asserts hotplug before
>          * it is fully functional. We're forced to wait for the maximum start up
> @@ -142,6 +146,10 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge)
>
>  static void ptn3460_enable(struct drm_bridge *bridge)
>  {
> +       struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
> +
> +       if (ptn_bridge->enabled)
> +               drm_panel_enable(ptn_bridge->panel);
>  }
>
>  static void ptn3460_disable(struct drm_bridge *bridge)
> @@ -153,6 +161,9 @@ static void ptn3460_disable(struct drm_bridge *bridge)
>
>         ptn_bridge->enabled = false;
>
> +       drm_panel_disable(ptn_bridge->panel);
> +       drm_panel_post_disable(ptn_bridge->panel);
> +
>         if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>                 gpio_set_value(ptn_bridge->gpio_rst_n, 1);
>
> @@ -198,6 +209,7 @@ int ptn3460_get_modes(struct drm_connector *connector)
>
>         power_off = !ptn_bridge->enabled;
>         ptn3460_pre_enable(ptn_bridge->bridge);
> +       ptn3460_enable(ptn_bridge->bridge);
>
>         edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
>         if (!edid) {
> @@ -265,7 +277,8 @@ struct drm_connector_funcs ptn3460_connector_funcs = {
>  };
>
>  int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
> -               struct i2c_client *client, struct device_node *node)
> +               struct i2c_client *client, struct device_node *node,
> +               struct drm_panel *panel)
>  {
>         int ret;
>         struct drm_bridge *bridge;
> @@ -324,6 +337,11 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>                 goto err;
>         }
>
> +       if (panel) {
> +               ptn_bridge->panel = panel;
> +               drm_panel_attach(ptn_bridge->panel, &ptn_bridge->connector);
> +       }
> +
>         bridge->driver_private = ptn_bridge;
>         encoder->bridge = bridge;
>         ptn_bridge->connector.polled = DRM_CONNECTOR_POLL_HPD;
> diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
> index dbc5ccc..4853f31 100644
> --- a/drivers/gpu/drm/exynos/exynos_dp_core.c
> +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c
> @@ -989,13 +989,14 @@ static bool find_bridge(const char *compat, struct bridge_init *bridge)
>
>  /* returns the number of bridges attached */
>  static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
> -               struct drm_encoder *encoder)
> +               struct drm_encoder *encoder, struct drm_panel *panel)
>  {
>         struct bridge_init bridge;
>         int ret;
>
>         if (find_bridge("nxp,ptn3460", &bridge)) {
> -               ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
> +               ret = ptn3460_init(dev, encoder, bridge.client, bridge.node,
> +                               panel);
>                 if (!ret)
>                         return 1;
>         }
> @@ -1012,9 +1013,16 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display,
>         dp->encoder = encoder;
>
>         /* Pre-empt DP connector creation if there's a bridge */
> -       ret = exynos_drm_attach_lcd_bridge(dp->drm_dev, encoder);
> -       if (ret)
> +       ret = exynos_drm_attach_lcd_bridge(dp->drm_dev, encoder, dp->drm_panel);
> +       if (ret) {
> +               /*
> +                * Also set "dp->drm_panel = NULL" so that we don't end up
> +                * controlling panel power both in exynos_dp and bridge
> +                * DPMS routines.
> +                */
> +               dp->drm_panel = NULL;
>                 return 0;
> +       }
>
>         connector->polled = DRM_CONNECTOR_POLL_HPD;
>
> diff --git a/include/drm/bridge/ptn3460.h b/include/drm/bridge/ptn3460.h
> index ff62344..570cebb 100644
> --- a/include/drm/bridge/ptn3460.h
> +++ b/include/drm/bridge/ptn3460.h
> @@ -18,16 +18,18 @@ struct drm_device;
>  struct drm_encoder;
>  struct i2c_client;
>  struct device_node;
> +struct drm_panel;
>
>  #if defined(CONFIG_DRM_PTN3460) || defined(CONFIG_DRM_PTN3460_MODULE)
>
>  int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
> -               struct i2c_client *client, struct device_node *node);
> +               struct i2c_client *client, struct device_node *node,
> +               struct drm_panel *panel);
>  #else
>
>  static inline int ptn3460_init(struct drm_device *dev,
>                 struct drm_encoder *encoder, struct i2c_client *client,
> -               struct device_node *node)
> +               struct device_node *node, struct drm_panel *panel)
>  {
>         return 0;
>  }
> --
> 1.7.9.5
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH V2 8/9] drm/bridge: Add PS8622 bridge driver
  2014-04-21 22:39 ` [PATCH V2 8/9] drm/bridge: Add PS8622 bridge driver Ajay Kumar
  2014-04-22  8:43   ` Jingoo Han
@ 2014-04-22 13:31   ` Sean Paul
  2014-04-22 14:03     ` Ajay kumar
  1 sibling, 1 reply; 58+ messages in thread
From: Sean Paul @ 2014-04-22 13:31 UTC (permalink / raw)
  To: Ajay Kumar
  Cc: dri-devel, linux-samsung-soc, InKi Dae, ajaynumb, sunil joshi,
	Prashanth G, Andrew Bresticker, Rahul Sharma, Olof Johansson,
	vpalatin

On Mon, Apr 21, 2014 at 6:39 PM, Ajay Kumar <ajaykumar.rs@samsung.com> wrote:
> This patch adds a drm_bridge driver for the PS8622 DisplayPort to LVDS
> bridge chip.
>
> Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
> Signed-off-by: Sean Paul <seanpaul@chromium.org>
> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>

Hi Ajay,
I don't think that you should be claiming authorship of this driver, I
don't see any commits from you in the chromium tree ([1] & [2]). The
driver was originally written by vpalatin@chromium.org (who's
completely missing from SoB), it should probably be attributed to him.

Sean


[1]- https://git.chromium.org/gitweb/?p=chromiumos/third_party/kernel-next.git;a=history;f=drivers/auxdisplay/ps8622.c;hb=fac579ad884d064d2c3cda988117e7dd66fc30c4
[2]- https://git.chromium.org/gitweb/?p=chromiumos/third_party/kernel-next.git;a=history;f=drivers/gpu/drm/bridge/ps8622.c;hb=refs/heads/chromeos-3.8



> ---
> Changes since V1:
>         Pushing V1 for this as V2 because this patch holds good in this series.
>
>  drivers/gpu/drm/bridge/Kconfig  |    7 +
>  drivers/gpu/drm/bridge/Makefile |    1 +
>  drivers/gpu/drm/bridge/ps8622.c |  566 +++++++++++++++++++++++++++++++++++++++
>  include/drm/bridge/ps8622.h     |   42 +++
>  4 files changed, 616 insertions(+)
>  create mode 100644 drivers/gpu/drm/bridge/ps8622.c
>  create mode 100644 include/drm/bridge/ps8622.h
>
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index 3bc6845..aba21fc 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -4,3 +4,10 @@ config DRM_PTN3460
>         select DRM_KMS_HELPER
>         select DRM_PANEL
>         ---help---
> +
> +config DRM_PS8622
> +       tristate "Parade eDP/LVDS bridge"
> +       depends on DRM
> +       select DRM_KMS_HELPER
> +       select DRM_PANEL
> +       ---help---
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index b4733e1..d1b5daa 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -1,3 +1,4 @@
>  ccflags-y := -Iinclude/drm
>
>  obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
> +obj-$(CONFIG_DRM_PS8622) += ps8622.o
> diff --git a/drivers/gpu/drm/bridge/ps8622.c b/drivers/gpu/drm/bridge/ps8622.c
> new file mode 100644
> index 0000000..1e6b3ca
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/ps8622.c
> @@ -0,0 +1,566 @@
> +/*
> + * Parade PS8622 eDP/LVDS bridge driver
> + *
> + * Copyright (C) 2014 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/backlight.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/fb.h>
> +#include <linux/gpio.h>
> +#include <linux/i2c.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/pm.h>
> +#include <linux/regulator/consumer.h>
> +#include <drm/drm_panel.h>
> +
> +#include "drmP.h"
> +#include "drm_crtc.h"
> +#include "drm_crtc_helper.h"
> +
> +struct ps8622_bridge {
> +       struct drm_connector connector;
> +       struct drm_bridge *bridge;
> +       struct drm_encoder *encoder;
> +       struct drm_panel *panel;
> +       struct i2c_client *client;
> +       struct regulator *v12;
> +       struct backlight_device *bl;
> +       struct mutex enable_mutex;
> +
> +       int gpio_slp_n;
> +       int gpio_rst_n;
> +
> +       u8 max_lane_count;
> +       u8 lane_count;
> +
> +       bool enabled;
> +
> +       struct drm_display_mode mode;
> +};
> +
> +struct ps8622_device_data {
> +       u8 max_lane_count;
> +};
> +
> +static const struct ps8622_device_data ps8622_data = {
> +       .max_lane_count = 1,
> +};
> +
> +static const struct ps8622_device_data ps8625_data = {
> +       .max_lane_count = 2,
> +};
> +
> +/* Brightness scale on the Parade chip */
> +#define PS8622_MAX_BRIGHTNESS 0xff
> +
> +/* Timings taken from the version 1.7 datasheet for the PS8622/PS8625 */
> +#define PS8622_POWER_RISE_T1_MIN_US 10
> +#define PS8622_POWER_RISE_T1_MAX_US 10000
> +#define PS8622_RST_HIGH_T2_MIN_US 3000
> +#define PS8622_RST_HIGH_T2_MAX_US 30000
> +#define PS8622_PWMO_END_T12_MS 200
> +#define PS8622_POWER_FALL_T16_MAX_US 10000
> +#define PS8622_POWER_OFF_T17_MS 500
> +
> +#if ((PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US) > \
> +       (PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US))
> +#error "T2.min + T1.max must be less than T2.max + T1.min"
> +#endif
> +
> +static int ps8622_set(struct i2c_client *client, u8 page, u8 reg, u8 val)
> +{
> +       int ret;
> +       struct i2c_adapter *adap = client->adapter;
> +       struct i2c_msg msg;
> +       u8 data[] = {reg, val};
> +
> +       msg.addr = client->addr + page;
> +       msg.flags = 0;
> +       msg.len = sizeof(data);
> +       msg.buf = data;
> +
> +       ret = i2c_transfer(adap, &msg, 1);
> +       if (ret != 1)
> +               pr_warn("PS8622 I2C write (0x%02x,0x%02x,0x%02x) failed: %d\n",
> +                       client->addr + page, reg, val, ret);
> +       return !(ret == 1);
> +}
> +
> +static int ps8622_send_config(struct ps8622_bridge *ps_bridge)
> +{
> +       struct i2c_client *cl = ps_bridge->client;
> +       int err = 0;
> +
> +       /* wait 20ms after power ON */
> +       usleep_range(20000, 30000);
> +
> +       err |= ps8622_set(cl, 0x02, 0xa1, 0x01); /* HPD low */
> +       /* SW setting */
> +       err |= ps8622_set(cl, 0x04, 0x14, 0x01); /* [1:0] SW output 1.2V voltage
> +                                                 * is lower to 96% */
> +       /* RCO SS setting */
> +       err |= ps8622_set(cl, 0x04, 0xe3, 0x20); /* [5:4] = b01 0.5%, b10 1%,
> +                                                 * b11 1.5% */
> +       err |= ps8622_set(cl, 0x04, 0xe2, 0x80); /* [7] RCO SS enable */
> +       /* RPHY Setting */
> +       err |= ps8622_set(cl, 0x04, 0x8a, 0x0c); /* [3:2] CDR tune wait cycle
> +                                                 * before measure for fine tune
> +                                                 * b00: 1us b01: 0.5us b10:2us
> +                                                 * b11: 4us */
> +       err |= ps8622_set(cl, 0x04, 0x89, 0x08); /* [3] RFD always on */
> +       err |= ps8622_set(cl, 0x04, 0x71, 0x2d); /* CTN lock in/out:
> +                                                 * 20000ppm/80000ppm.
> +                                                 * Lock out 2 times. */
> +       /* 2.7G CDR settings */
> +       err |= ps8622_set(cl, 0x04, 0x7d, 0x07); /* NOF=40LSB for HBR CDR
> +                                                 * setting */
> +       err |= ps8622_set(cl, 0x04, 0x7b, 0x00); /* [1:0] Fmin=+4bands */
> +       err |= ps8622_set(cl, 0x04, 0x7a, 0xfd); /* [7:5] DCO_FTRNG=+-40% */
> +       /* 1.62G CDR settings */
> +       err |= ps8622_set(cl, 0x04, 0xc0, 0x12); /* [5:2]NOF=64LSB [1:0]DCO
> +                                                 * scale is 2/5 */
> +       err |= ps8622_set(cl, 0x04, 0xc1, 0x92); /* Gitune=-37% */
> +       err |= ps8622_set(cl, 0x04, 0xc2, 0x1c); /* Fbstep=100% */
> +       err |= ps8622_set(cl, 0x04, 0x32, 0x80); /* [7] LOS signal disable */
> +       /* RPIO Setting */
> +       err |= ps8622_set(cl, 0x04, 0x00, 0xb0); /* [7:4] LVDS driver bias
> +                                                 * current : 75% (250mV swing)
> +                                                 * */
> +       err |= ps8622_set(cl, 0x04, 0x15, 0x40); /* [7:6] Right-bar GPIO output
> +                                                 * strength is 8mA */
> +       /* EQ Training State Machine Setting */
> +       err |= ps8622_set(cl, 0x04, 0x54, 0x10); /* RCO calibration start */
> +       /* Logic, needs more than 10 I2C command */
> +       err |= ps8622_set(cl, 0x01, 0x02, 0x80 | ps_bridge->max_lane_count);
> +                                                /* [4:0] MAX_LANE_COUNT set to
> +                                                 * max supported lanes */
> +       err |= ps8622_set(cl, 0x01, 0x21, 0x80 | ps_bridge->lane_count);
> +                                                /* [4:0] LANE_COUNT_SET set to
> +                                                 * chosen lane count */
> +       err |= ps8622_set(cl, 0x00, 0x52, 0x20);
> +       err |= ps8622_set(cl, 0x00, 0xf1, 0x03); /* HPD CP toggle enable */
> +       err |= ps8622_set(cl, 0x00, 0x62, 0x41);
> +       err |= ps8622_set(cl, 0x00, 0xf6, 0x01); /* Counter number, add 1ms
> +                                                 * counter delay */
> +       err |= ps8622_set(cl, 0x00, 0x77, 0x06); /* [6]PWM function control by
> +                                                 * DPCD0040f[7], default is PWM
> +                                                 * block always works. */
> +       err |= ps8622_set(cl, 0x00, 0x4c, 0x04); /* 04h Adjust VTotal tolerance
> +                                                 * to fix the 30Hz no display
> +                                                 * issue */
> +       err |= ps8622_set(cl, 0x01, 0xc0, 0x00); /* DPCD00400='h00, Parade OUI =
> +                                                 * 'h001cf8 */
> +       err |= ps8622_set(cl, 0x01, 0xc1, 0x1c); /* DPCD00401='h1c */
> +       err |= ps8622_set(cl, 0x01, 0xc2, 0xf8); /* DPCD00402='hf8 */
> +       err |= ps8622_set(cl, 0x01, 0xc3, 0x44); /* DPCD403~408 = ASCII code
> +                                                 * D2SLV5='h4432534c5635 */
> +       err |= ps8622_set(cl, 0x01, 0xc4, 0x32); /* DPCD404 */
> +       err |= ps8622_set(cl, 0x01, 0xc5, 0x53); /* DPCD405 */
> +       err |= ps8622_set(cl, 0x01, 0xc6, 0x4c); /* DPCD406 */
> +       err |= ps8622_set(cl, 0x01, 0xc7, 0x56); /* DPCD407 */
> +       err |= ps8622_set(cl, 0x01, 0xc8, 0x35); /* DPCD408 */
> +       err |= ps8622_set(cl, 0x01, 0xca, 0x01); /* DPCD40A, Initial Code major
> +                                                 * revision '01' */
> +       err |= ps8622_set(cl, 0x01, 0xcb, 0x05); /* DPCD40B, Initial Code minor
> +                                                 * revision '05' */
> +       if (ps_bridge->bl) {
> +               err |= ps8622_set(cl, 0x01, 0xa5, 0xa0);
> +                                               /* DPCD720, internal PWM */
> +               err |= ps8622_set(cl, 0x01, 0xa7,
> +                               ps_bridge->bl->props.brightness);
> +                                                /* FFh for 100% brightness,
> +                                                 *  0h for 0% brightness */
> +       } else {
> +               err |= ps8622_set(cl, 0x01, 0xa5, 0x80);
> +                                               /* DPCD720, external PWM */
> +       }
> +       err |= ps8622_set(cl, 0x01, 0xcc, 0x13); /* Set LVDS output as 6bit-VESA
> +                                                 * mapping, single LVDS channel
> +                                                 * */
> +       err |= ps8622_set(cl, 0x02, 0xb1, 0x20); /* Enable SSC set by register
> +                                                 * */
> +       err |= ps8622_set(cl, 0x04, 0x10, 0x16); /* Set SSC enabled and +/-1%
> +                                                 * central spreading */
> +       /* Logic end */
> +       err |= ps8622_set(cl, 0x04, 0x59, 0x60); /* MPU Clock source: LC => RCO
> +                                                 * */
> +       err |= ps8622_set(cl, 0x04, 0x54, 0x14); /* LC -> RCO */
> +       err |= ps8622_set(cl, 0x02, 0xa1, 0x91); /* HPD high */
> +
> +       return err ? -EIO : 0;
> +}
> +
> +static int ps8622_backlight_update(struct backlight_device *bl)
> +{
> +       struct ps8622_bridge *ps_bridge = dev_get_drvdata(&bl->dev);
> +       int ret, brightness = bl->props.brightness;
> +
> +       if (bl->props.power != FB_BLANK_UNBLANK ||
> +           bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
> +               brightness = 0;
> +
> +       mutex_lock(&ps_bridge->enable_mutex);
> +
> +       if (!ps_bridge->enabled) {
> +               ret = -EINVAL;
> +               goto out;
> +       }
> +
> +       ret = ps8622_set(ps_bridge->client, 0x01, 0xa7, brightness);
> +
> +out:
> +       mutex_unlock(&ps_bridge->enable_mutex);
> +       return ret;
> +}
> +
> +static int ps8622_backlight_get(struct backlight_device *bl)
> +{
> +       return bl->props.brightness;
> +}
> +
> +static const struct backlight_ops ps8622_backlight_ops = {
> +       .update_status  = ps8622_backlight_update,
> +       .get_brightness = ps8622_backlight_get,
> +};
> +
> +static void ps8622_pre_enable(struct drm_bridge *bridge)
> +{
> +       struct ps8622_bridge *ps_bridge = bridge->driver_private;
> +       int ret;
> +
> +       mutex_lock(&ps_bridge->enable_mutex);
> +       if (ps_bridge->enabled)
> +               goto out;
> +
> +       if (gpio_is_valid(ps_bridge->gpio_rst_n))
> +               gpio_set_value(ps_bridge->gpio_rst_n, 0);
> +
> +       if (ps_bridge->v12) {
> +               ret = regulator_enable(ps_bridge->v12);
> +               if (ret)
> +                       DRM_ERROR("fails to enable ps_bridge->v12");
> +       }
> +
> +       drm_panel_pre_enable(ps_bridge->panel);
> +
> +       if (gpio_is_valid(ps_bridge->gpio_slp_n))
> +               gpio_set_value(ps_bridge->gpio_slp_n, 1);
> +
> +       /*
> +        * T1 is the range of time that it takes for the power to rise after we
> +        * enable the lcd fet. T2 is the range of time in which the data sheet
> +        * specifies we should deassert the reset pin.
> +        *
> +        * If it takes T1.max for the power to rise, we need to wait atleast
> +        * T2.min before deasserting the reset pin. If it takes T1.min for the
> +        * power to rise, we need to wait at most T2.max before deasserting the
> +        * reset pin.
> +        */
> +       usleep_range(PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US,
> +                    PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US);
> +
> +       if (gpio_is_valid(ps_bridge->gpio_rst_n))
> +               gpio_set_value(ps_bridge->gpio_rst_n, 1);
> +
> +       ret = ps8622_send_config(ps_bridge);
> +       if (ret)
> +               DRM_ERROR("Failed to send config to bridge (%d)\n", ret);
> +
> +       ps_bridge->enabled = true;
> +
> +       mutex_unlock(&ps_bridge->enable_mutex);
> +       return;
> +
> +out:
> +
> +       mutex_unlock(&ps_bridge->enable_mutex);
> +}
> +
> +static void ps8622_enable(struct drm_bridge *bridge)
> +{
> +       struct ps8622_bridge *ps_bridge = bridge->driver_private;
> +
> +       mutex_lock(&ps_bridge->enable_mutex);
> +       if (ps_bridge->enabled)
> +               drm_panel_enable(ps_bridge->panel);
> +       mutex_unlock(&ps_bridge->enable_mutex);
> +}
> +
> +static void ps8622_disable(struct drm_bridge *bridge)
> +{
> +       struct ps8622_bridge *ps_bridge = bridge->driver_private;
> +
> +       mutex_lock(&ps_bridge->enable_mutex);
> +
> +       if (!ps_bridge->enabled)
> +               goto out;
> +
> +       ps_bridge->enabled = false;
> +
> +       drm_panel_disable(ps_bridge->panel);
> +       msleep(PS8622_PWMO_END_T12_MS);
> +
> +       /*
> +        * This doesn't matter if the regulators are turned off, but something
> +        * else might keep them on. In that case, we want to assert the slp gpio
> +        * to lower power.
> +        */
> +       if (gpio_is_valid(ps_bridge->gpio_slp_n))
> +               gpio_set_value(ps_bridge->gpio_slp_n, 0);
> +
> +       drm_panel_post_disable(ps_bridge->panel);
> +       if (ps_bridge->v12)
> +               regulator_disable(ps_bridge->v12);
> +
> +       /*
> +        * Sleep for at least the amount of time that it takes the power rail to
> +        * fall to prevent asserting the rst gpio from doing anything.
> +        */
> +       usleep_range(PS8622_POWER_FALL_T16_MAX_US,
> +                    2 * PS8622_POWER_FALL_T16_MAX_US);
> +       if (gpio_is_valid(ps_bridge->gpio_rst_n))
> +               gpio_set_value(ps_bridge->gpio_rst_n, 0);
> +
> +       msleep(PS8622_POWER_OFF_T17_MS);
> +
> +out:
> +       mutex_unlock(&ps_bridge->enable_mutex);
> +}
> +
> +static void ps8622_post_disable(struct drm_bridge *bridge)
> +{
> +}
> +
> +static void ps8622_destroy(struct drm_bridge *bridge)
> +{
> +       struct ps8622_bridge *ps_bridge = bridge->driver_private;
> +
> +       drm_bridge_cleanup(bridge);
> +       if (ps_bridge->bl)
> +               backlight_device_unregister(ps_bridge->bl);
> +
> +       put_device(&ps_bridge->client->dev);
> +}
> +
> +static const struct drm_bridge_funcs ps8622_bridge_funcs = {
> +       .pre_enable = ps8622_pre_enable,
> +       .enable = ps8622_enable,
> +       .disable = ps8622_disable,
> +       .post_disable = ps8622_post_disable,
> +       .destroy = ps8622_destroy,
> +};
> +
> +static int ps8622_get_modes(struct drm_connector *connector)
> +{
> +       struct ps8622_bridge *ps_bridge;
> +       struct drm_display_mode *new_mode;
> +
> +       ps_bridge = container_of(connector, struct ps8622_bridge, connector);
> +
> +       new_mode = drm_mode_duplicate(connector->dev, &ps_bridge->mode);
> +       if (!new_mode) {
> +               DRM_ERROR("Failed to duplicate ps_bridge mode!\n");
> +               return 0;
> +       }
> +       drm_mode_probed_add(connector, new_mode);
> +       return 1;
> +}
> +
> +static int ps8622_mode_valid(struct drm_connector *connector,
> +               struct drm_display_mode *mode)
> +{
> +       struct ps8622_bridge *ps_bridge;
> +
> +       ps_bridge = container_of(connector, struct ps8622_bridge, connector);
> +
> +       return drm_mode_equal(mode, &ps_bridge->mode) ? MODE_OK : MODE_BAD;
> +}
> +
> +static struct drm_encoder *ps8622_best_encoder(struct drm_connector *connector)
> +{
> +       struct ps8622_bridge *ps_bridge;
> +
> +       ps_bridge = container_of(connector, struct ps8622_bridge, connector);
> +
> +       return ps_bridge->encoder;
> +}
> +
> +static const struct drm_connector_helper_funcs ps8622_connector_helper_funcs = {
> +       .get_modes = ps8622_get_modes,
> +       .mode_valid = ps8622_mode_valid,
> +       .best_encoder = ps8622_best_encoder,
> +};
> +
> +static enum drm_connector_status ps8622_detect(struct drm_connector *connector,
> +               bool force)
> +{
> +       return connector_status_connected;
> +}
> +
> +static void ps8622_connector_destroy(struct drm_connector *connector)
> +{
> +       drm_connector_cleanup(connector);
> +}
> +
> +static const struct drm_connector_funcs ps8622_connector_funcs = {
> +       .dpms = drm_helper_connector_dpms,
> +       .fill_modes = drm_helper_probe_single_connector_modes,
> +       .detect = ps8622_detect,
> +       .destroy = ps8622_connector_destroy,
> +};
> +
> +static const struct of_device_id ps8622_devices[] = {
> +       {
> +               .compatible = "parade,ps8622",
> +               .data   = &ps8622_data,
> +       }, {
> +               .compatible = "parade,ps8625",
> +               .data   = &ps8625_data,
> +       }, {
> +               /* end node */
> +       }
> +};
> +
> +int ps8622_init(struct drm_device *dev, struct drm_encoder *encoder,
> +               struct i2c_client *client, struct device_node *node,
> +               struct drm_panel *panel)
> +{
> +       int ret;
> +       const struct of_device_id *match;
> +       const struct ps8622_device_data *device_data;
> +       struct drm_bridge *bridge;
> +       struct ps8622_bridge *ps_bridge;
> +
> +       node = of_find_matching_node_and_match(NULL, ps8622_devices, &match);
> +       if (!node)
> +               return -ENODEV;
> +
> +       bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL);
> +       if (!bridge) {
> +               DRM_ERROR("Failed to allocate drm bridge\n");
> +               return -ENOMEM;
> +       }
> +
> +       ps_bridge = devm_kzalloc(dev->dev, sizeof(*ps_bridge), GFP_KERNEL);
> +       if (!ps_bridge) {
> +               DRM_ERROR("could not allocate ps bridge\n");
> +               return -ENOMEM;
> +       }
> +
> +       mutex_init(&ps_bridge->enable_mutex);
> +
> +       device_data = match->data;
> +       ps_bridge->client = client;
> +       ps_bridge->encoder = encoder;
> +       ps_bridge->bridge = bridge;
> +       ps_bridge->v12 = devm_regulator_get(&client->dev, "vdd_bridge");
> +       if (IS_ERR(ps_bridge->v12)) {
> +               DRM_INFO("no 1.2v regulator found for PS8622\n");
> +               ps_bridge->v12 = NULL;
> +       }
> +
> +       ps_bridge->gpio_slp_n = of_get_named_gpio(node, "sleep-gpio", 0);
> +       if (gpio_is_valid(ps_bridge->gpio_slp_n)) {
> +               ret = devm_gpio_request_one(&client->dev, ps_bridge->gpio_slp_n,
> +                                           GPIOF_OUT_INIT_HIGH,
> +                                           "PS8622_SLP_N");
> +               if (ret)
> +                       goto err_client;
> +       }
> +
> +       ps_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0);
> +       if (gpio_is_valid(ps_bridge->gpio_rst_n)) {
> +               /*
> +                * Assert the reset pin high to avoid the bridge being
> +                * initialized prematurely
> +                */
> +               ret = devm_gpio_request_one(&client->dev, ps_bridge->gpio_rst_n,
> +                                           GPIOF_OUT_INIT_HIGH,
> +                                           "PS8622_RST_N");
> +               if (ret)
> +                       goto err_client;
> +       }
> +
> +       ps_bridge->max_lane_count = device_data->max_lane_count;
> +
> +       if (of_property_read_u8(node, "lane-count", &ps_bridge->lane_count))
> +               ps_bridge->lane_count = ps_bridge->max_lane_count;
> +       else if (ps_bridge->lane_count > ps_bridge->max_lane_count) {
> +               DRM_ERROR("lane-count property is too high for DP bridge\n");
> +               ps_bridge->lane_count = ps_bridge->max_lane_count;
> +       }
> +
> +       if (!of_find_property(node, "use-external-pwm", NULL)) {
> +               ps_bridge->bl = backlight_device_register("ps8622-backlight",
> +                               dev->dev, ps_bridge, &ps8622_backlight_ops,
> +                               NULL);
> +               if (IS_ERR(ps_bridge->bl)) {
> +                       DRM_ERROR("failed to register backlight\n");
> +                       ret = PTR_ERR(ps_bridge->bl);
> +                       ps_bridge->bl = NULL;
> +                       goto err_client;
> +               }
> +               ps_bridge->bl->props.max_brightness = PS8622_MAX_BRIGHTNESS;
> +               ps_bridge->bl->props.brightness = PS8622_MAX_BRIGHTNESS;
> +       }
> +
> +       ret = of_get_drm_display_mode(node, &ps_bridge->mode, 0);
> +       if (ret) {
> +               DRM_ERROR("Failed to get display mode, ret=%d\n", ret);
> +               goto err_backlight;
> +       }
> +
> +       ret = drm_bridge_init(dev, ps_bridge->bridge, &ps8622_bridge_funcs);
> +       if (ret) {
> +               DRM_ERROR("Failed to initialize bridge with drm\n");
> +               goto err_backlight;
> +       }
> +
> +       if (panel) {
> +               ps_bridge->panel = panel;
> +               drm_panel_attach(ps_bridge->panel, &ps_bridge->connector);
> +       }
> +
> +       ps_bridge->bridge->driver_private = ps_bridge;
> +       encoder->bridge = ps_bridge->bridge;
> +       ps_bridge->enabled = false;
> +       ps_bridge->connector.polled = DRM_CONNECTOR_POLL_HPD;
> +
> +       ret = drm_connector_init(dev, &ps_bridge->connector,
> +                       &ps8622_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
> +       if (ret) {
> +               DRM_ERROR("Failed to initialize connector with drm\n");
> +               goto err_bridge;
> +       }
> +       drm_connector_helper_add(&ps_bridge->connector,
> +                       &ps8622_connector_helper_funcs);
> +       drm_sysfs_connector_add(&ps_bridge->connector);
> +       drm_mode_connector_attach_encoder(&ps_bridge->connector, encoder);
> +
> +       return 0;
> +
> +err_bridge:
> +       drm_bridge_cleanup(ps_bridge->bridge);
> +err_backlight:
> +       if (ps_bridge->bl)
> +               backlight_device_unregister(ps_bridge->bl);
> +
> +err_client:
> +       put_device(&client->dev);
> +       DRM_ERROR("device probe failed : %d\n", ret);
> +       return ret;
> +}
> +EXPORT_SYMBOL(ps8622_init);
> diff --git a/include/drm/bridge/ps8622.h b/include/drm/bridge/ps8622.h
> new file mode 100644
> index 0000000..4fc1959
> --- /dev/null
> +++ b/include/drm/bridge/ps8622.h
> @@ -0,0 +1,42 @@
> +/*
> + * Copyright (C) 2014 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _DRM_BRIDGE_PS8622_H_
> +#define _DRM_BRIDGE_PS8622_H_
> +
> +struct drm_device;
> +struct drm_encoder;
> +struct i2c_client;
> +struct device_node;
> +struct drm_panel;
> +
> +#ifdef CONFIG_DRM_PS8622
> +
> +extern int ps8622_init(struct drm_device *dev, struct drm_encoder *encoder,
> +               struct i2c_client *client, struct device_node *node,
> +               struct drm_panel *panel);
> +
> +#else
> +
> +static inline int ps8622_init(struct drm_device *dev,
> +                               struct drm_encoder *encoder,
> +                               struct i2c_client *client,
> +                               struct device_node *node,
> +                               struct drm_panel *panel)
> +{
> +       return -ENODEV;
> +}
> +
> +#endif
> +
> +#endif
> --
> 1.7.9.5
>

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

* Re: [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls
  2014-04-22 11:34   ` Rob Clark
@ 2014-04-22 13:53     ` Daniel Vetter
  2014-04-23 18:56     ` Ajay kumar
  1 sibling, 0 replies; 58+ messages in thread
From: Daniel Vetter @ 2014-04-22 13:53 UTC (permalink / raw)
  To: Rob Clark
  Cc: moderated list:ARM/S5P EXYNOS AR...,
	seanpaul, sunil joshi, dri-devel, ajaynumb, Prashanth G,
	Ajay Kumar

On Tue, Apr 22, 2014 at 07:34:03AM -0400, Rob Clark wrote:
> So what about, rather than adding drm_panel support to each bridge
> individually, we introduce a drm_panel_bridge (with a form of
> chaining).. ie:
> 
>   struct drm_panel_bridge {
>     struct drm_bridge base;
>     struct drm_panel *panel;
>     struct drm_bridge *bridge; /* optional */
>   };
> 
>   static void drm_panel_bridge_enable(struct drm_bridge *bridge)
>   {
>     struct drm_panel_bridge *pb = to_panel_bridge(bridge);
>     if (pb->bridge)
>       pb->bridge->funcs->enable(pb->bridge);
>     pb->panel->funcs->enable(pb->panel);
>   }
> 
> ... and so on.
> 
> If you don't need a real bridge, just create one of these w/
> pb->bridge==NULL, otherwise create it as a wrapper for your real
> bridge.

Yeah I think that's how I'd have implemented some generic abstraction for
drivers using the crtc helpers. This allows you to keep bridge drivers,
panel drivers and anything else you might have in your driver to feed the
pixel stream to those 2 guys separate. And it also allows you to set it
all up in different ways, e.g. using device tree metadata, or acpi or some
other table hardcoded in a video rom somewhere.

Imo we also should have something similar to chain up drm_bridge devices.
tbh I'm not terribly happy about how the current integration with the crtc
modeset helpers is done ...
-Daniel

> 
> BR,
> -R
> 
> On Mon, Apr 21, 2014 at 6:39 PM, Ajay Kumar <ajaykumar.rs@samsung.com> wrote:
> > attach ptn3460 connector to drm_panel and support drm_panel routines,
> > if a valid drm_panel object is passed to ptn3460_init.
> >
> > Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
> > ---
> > Changes since V1:
> >         Address few coding style comments from Jingoo Han
> >
> >  drivers/gpu/drm/bridge/Kconfig          |    1 +
> >  drivers/gpu/drm/bridge/ptn3460.c        |   20 +++++++++++++++++++-
> >  drivers/gpu/drm/exynos/exynos_dp_core.c |   16 ++++++++++++----
> >  include/drm/bridge/ptn3460.h            |    6 ++++--
> >  4 files changed, 36 insertions(+), 7 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> > index 884923f..3bc6845 100644
> > --- a/drivers/gpu/drm/bridge/Kconfig
> > +++ b/drivers/gpu/drm/bridge/Kconfig
> > @@ -2,4 +2,5 @@ config DRM_PTN3460
> >         tristate "PTN3460 DP/LVDS bridge"
> >         depends on DRM
> >         select DRM_KMS_HELPER
> > +       select DRM_PANEL
> >         ---help---
> > diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
> > index f1d2afc..3920202 100644
> > --- a/drivers/gpu/drm/bridge/ptn3460.c
> > +++ b/drivers/gpu/drm/bridge/ptn3460.c
> > @@ -19,6 +19,7 @@
> >  #include <linux/i2c.h>
> >  #include <linux/gpio.h>
> >  #include <linux/delay.h>
> > +#include <drm/drm_panel.h>
> >
> >  #include "drmP.h"
> >  #include "drm_edid.h"
> > @@ -38,6 +39,7 @@ struct ptn3460_bridge {
> >         struct i2c_client *client;
> >         struct drm_encoder *encoder;
> >         struct drm_bridge *bridge;
> > +       struct drm_panel *panel;
> >         struct edid *edid;
> >         int gpio_pd_n;
> >         int gpio_rst_n;
> > @@ -126,6 +128,8 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge)
> >                 gpio_set_value(ptn_bridge->gpio_rst_n, 1);
> >         }
> >
> > +       drm_panel_pre_enable(ptn_bridge->panel);
> > +
> >         /*
> >          * There's a bug in the PTN chip where it falsely asserts hotplug before
> >          * it is fully functional. We're forced to wait for the maximum start up
> > @@ -142,6 +146,10 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge)
> >
> >  static void ptn3460_enable(struct drm_bridge *bridge)
> >  {
> > +       struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
> > +
> > +       if (ptn_bridge->enabled)
> > +               drm_panel_enable(ptn_bridge->panel);
> >  }
> >
> >  static void ptn3460_disable(struct drm_bridge *bridge)
> > @@ -153,6 +161,9 @@ static void ptn3460_disable(struct drm_bridge *bridge)
> >
> >         ptn_bridge->enabled = false;
> >
> > +       drm_panel_disable(ptn_bridge->panel);
> > +       drm_panel_post_disable(ptn_bridge->panel);
> > +
> >         if (gpio_is_valid(ptn_bridge->gpio_rst_n))
> >                 gpio_set_value(ptn_bridge->gpio_rst_n, 1);
> >
> > @@ -198,6 +209,7 @@ int ptn3460_get_modes(struct drm_connector *connector)
> >
> >         power_off = !ptn_bridge->enabled;
> >         ptn3460_pre_enable(ptn_bridge->bridge);
> > +       ptn3460_enable(ptn_bridge->bridge);
> >
> >         edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
> >         if (!edid) {
> > @@ -265,7 +277,8 @@ struct drm_connector_funcs ptn3460_connector_funcs = {
> >  };
> >
> >  int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
> > -               struct i2c_client *client, struct device_node *node)
> > +               struct i2c_client *client, struct device_node *node,
> > +               struct drm_panel *panel)
> >  {
> >         int ret;
> >         struct drm_bridge *bridge;
> > @@ -324,6 +337,11 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
> >                 goto err;
> >         }
> >
> > +       if (panel) {
> > +               ptn_bridge->panel = panel;
> > +               drm_panel_attach(ptn_bridge->panel, &ptn_bridge->connector);
> > +       }
> > +
> >         bridge->driver_private = ptn_bridge;
> >         encoder->bridge = bridge;
> >         ptn_bridge->connector.polled = DRM_CONNECTOR_POLL_HPD;
> > diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
> > index dbc5ccc..4853f31 100644
> > --- a/drivers/gpu/drm/exynos/exynos_dp_core.c
> > +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c
> > @@ -989,13 +989,14 @@ static bool find_bridge(const char *compat, struct bridge_init *bridge)
> >
> >  /* returns the number of bridges attached */
> >  static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
> > -               struct drm_encoder *encoder)
> > +               struct drm_encoder *encoder, struct drm_panel *panel)
> >  {
> >         struct bridge_init bridge;
> >         int ret;
> >
> >         if (find_bridge("nxp,ptn3460", &bridge)) {
> > -               ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
> > +               ret = ptn3460_init(dev, encoder, bridge.client, bridge.node,
> > +                               panel);
> >                 if (!ret)
> >                         return 1;
> >         }
> > @@ -1012,9 +1013,16 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display,
> >         dp->encoder = encoder;
> >
> >         /* Pre-empt DP connector creation if there's a bridge */
> > -       ret = exynos_drm_attach_lcd_bridge(dp->drm_dev, encoder);
> > -       if (ret)
> > +       ret = exynos_drm_attach_lcd_bridge(dp->drm_dev, encoder, dp->drm_panel);
> > +       if (ret) {
> > +               /*
> > +                * Also set "dp->drm_panel = NULL" so that we don't end up
> > +                * controlling panel power both in exynos_dp and bridge
> > +                * DPMS routines.
> > +                */
> > +               dp->drm_panel = NULL;
> >                 return 0;
> > +       }
> >
> >         connector->polled = DRM_CONNECTOR_POLL_HPD;
> >
> > diff --git a/include/drm/bridge/ptn3460.h b/include/drm/bridge/ptn3460.h
> > index ff62344..570cebb 100644
> > --- a/include/drm/bridge/ptn3460.h
> > +++ b/include/drm/bridge/ptn3460.h
> > @@ -18,16 +18,18 @@ struct drm_device;
> >  struct drm_encoder;
> >  struct i2c_client;
> >  struct device_node;
> > +struct drm_panel;
> >
> >  #if defined(CONFIG_DRM_PTN3460) || defined(CONFIG_DRM_PTN3460_MODULE)
> >
> >  int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
> > -               struct i2c_client *client, struct device_node *node);
> > +               struct i2c_client *client, struct device_node *node,
> > +               struct drm_panel *panel);
> >  #else
> >
> >  static inline int ptn3460_init(struct drm_device *dev,
> >                 struct drm_encoder *encoder, struct i2c_client *client,
> > -               struct device_node *node)
> > +               struct device_node *node, struct drm_panel *panel)
> >  {
> >         return 0;
> >  }
> > --
> > 1.7.9.5
> >
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > http://lists.freedesktop.org/mailman/listinfo/dri-devel
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH V2 8/9] drm/bridge: Add PS8622 bridge driver
  2014-04-22  8:43   ` Jingoo Han
@ 2014-04-22 13:57     ` Ajay kumar
  0 siblings, 0 replies; 58+ messages in thread
From: Ajay kumar @ 2014-04-22 13:57 UTC (permalink / raw)
  To: Jingoo Han
  Cc: linux-samsung-soc, Sean Paul, Andrew Bresticker, sunil joshi,
	dri-devel, Prashanth G, Ajay Kumar, Rahul Sharma


[-- Attachment #1.1: Type: text/plain, Size: 13875 bytes --]

Hi Jingoo,


On Tue, Apr 22, 2014 at 2:13 PM, Jingoo Han <jg1.han@samsung.com> wrote:

> On Tuesday, April 22, 2014 7:39 AM, Ajay Kumar wrote:
> >
> > This patch adds a drm_bridge driver for the PS8622 DisplayPort to LVDS
> > bridge chip.
> >
> > Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
> > Signed-off-by: Sean Paul <seanpaul@chromium.org>
> > Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
> > Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
> > ---
> > Changes since V1:
> >       Pushing V1 for this as V2 because this patch holds good in this
> series.
> >
> >  drivers/gpu/drm/bridge/Kconfig  |    7 +
> >  drivers/gpu/drm/bridge/Makefile |    1 +
> >  drivers/gpu/drm/bridge/ps8622.c |  566
> +++++++++++++++++++++++++++++++++++++++
> >  include/drm/bridge/ps8622.h     |   42 +++
> >  4 files changed, 616 insertions(+)
> >  create mode 100644 drivers/gpu/drm/bridge/ps8622.c
> >  create mode 100644 include/drm/bridge/ps8622.h
> >
> > diff --git a/drivers/gpu/drm/bridge/Kconfig
> b/drivers/gpu/drm/bridge/Kconfig
> > index 3bc6845..aba21fc 100644
> > --- a/drivers/gpu/drm/bridge/Kconfig
> > +++ b/drivers/gpu/drm/bridge/Kconfig
> > @@ -4,3 +4,10 @@ config DRM_PTN3460
> >       select DRM_KMS_HELPER
> >       select DRM_PANEL
> >       ---help---
> > +
> > +config DRM_PS8622
> > +     tristate "Parade eDP/LVDS bridge"
> > +     depends on DRM
> > +     select DRM_KMS_HELPER
> > +     select DRM_PANEL
>
> Please add the following select.
>
> +       select BACKLIGHT_LCD_SUPPORT
> +       select BACKLIGHT_CLASS_DEVICE
>
> Without this, the following build issues happen.
>
> drivers/gpu/drm/bridge/ps8622.c:353: undefined reference to
> `backlight_device_unregister'
> drivers/built-in.o: In function `ps8622_init':
> drivers/gpu/drm/bridge/ps8622.c:559: undefined reference to
> `backlight_device_unregister'
> drivers/gpu/drm/bridge/ps8622.c:507: undefined reference to
> `backlight_device_register'
>
> Right. I will add it.


> > +     ---help---
> > diff --git a/drivers/gpu/drm/bridge/Makefile
> b/drivers/gpu/drm/bridge/Makefile
> > index b4733e1..d1b5daa 100644
> > --- a/drivers/gpu/drm/bridge/Makefile
> > +++ b/drivers/gpu/drm/bridge/Makefile
> > @@ -1,3 +1,4 @@
> >  ccflags-y := -Iinclude/drm
> >
> >  obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
> > +obj-$(CONFIG_DRM_PS8622) += ps8622.o
> > diff --git a/drivers/gpu/drm/bridge/ps8622.c
> b/drivers/gpu/drm/bridge/ps8622.c
> > new file mode 100644
> > index 0000000..1e6b3ca
> > --- /dev/null
> > +++ b/drivers/gpu/drm/bridge/ps8622.c
>
> [.....]
>
> > +static int ps8622_send_config(struct ps8622_bridge *ps_bridge)
> > +{
> > +     struct i2c_client *cl = ps_bridge->client;
> > +     int err = 0;
> > +
> > +     /* wait 20ms after power ON */
> > +     usleep_range(20000, 30000);
> > +
> > +     err |= ps8622_set(cl, 0x02, 0xa1, 0x01); /* HPD low */
> > +     /* SW setting */
> > +     err |= ps8622_set(cl, 0x04, 0x14, 0x01); /* [1:0] SW output 1.2V
> voltage
> > +                                               * is lower to 96% */
>
> The comment style looks awkward.
> Please choose one of two options. And change all comments in this file.
>
> 1.
>
> +       /* SW setting */
> +       err |= ps8622_set(cl, 0x04, 0x14, 0x01); /* [1:0] SW output 1.2V
> voltage is lower to 96% */
>
> 2.
>
> +       /* SW setting */
> +       /* [1:0] SW output 1.2V voltage is lower to 96% */
> +       err |= ps8622_set(cl, 0x04, 0x14, 0x01);
>
> Will use this.


> > +     /* RCO SS setting */
> > +     err |= ps8622_set(cl, 0x04, 0xe3, 0x20); /* [5:4] = b01 0.5%, b10
> 1%,
> > +                                               * b11 1.5% */
> > +     err |= ps8622_set(cl, 0x04, 0xe2, 0x80); /* [7] RCO SS enable */
> > +     /* RPHY Setting */
> > +     err |= ps8622_set(cl, 0x04, 0x8a, 0x0c); /* [3:2] CDR tune wait
> cycle
> > +                                               * before measure for
> fine tune
> > +                                               * b00: 1us b01: 0.5us
> b10:2us
> > +                                               * b11: 4us */
> > +     err |= ps8622_set(cl, 0x04, 0x89, 0x08); /* [3] RFD always on */
> > +     err |= ps8622_set(cl, 0x04, 0x71, 0x2d); /* CTN lock in/out:
> > +                                               * 20000ppm/80000ppm.
> > +                                               * Lock out 2 times. */
> > +     /* 2.7G CDR settings */
> > +     err |= ps8622_set(cl, 0x04, 0x7d, 0x07); /* NOF=40LSB for HBR CDR
> > +                                               * setting */
> > +     err |= ps8622_set(cl, 0x04, 0x7b, 0x00); /* [1:0] Fmin=+4bands */
> > +     err |= ps8622_set(cl, 0x04, 0x7a, 0xfd); /* [7:5] DCO_FTRNG=+-40%
> */
> > +     /* 1.62G CDR settings */
> > +     err |= ps8622_set(cl, 0x04, 0xc0, 0x12); /* [5:2]NOF=64LSB [1:0]DCO
> > +                                               * scale is 2/5 */
> > +     err |= ps8622_set(cl, 0x04, 0xc1, 0x92); /* Gitune=-37% */
> > +     err |= ps8622_set(cl, 0x04, 0xc2, 0x1c); /* Fbstep=100% */
> > +     err |= ps8622_set(cl, 0x04, 0x32, 0x80); /* [7] LOS signal disable
> */
> > +     /* RPIO Setting */
> > +     err |= ps8622_set(cl, 0x04, 0x00, 0xb0); /* [7:4] LVDS driver bias
> > +                                               * current : 75% (250mV
> swing)
> > +                                               * */
> > +     err |= ps8622_set(cl, 0x04, 0x15, 0x40); /* [7:6] Right-bar GPIO
> output
> > +                                               * strength is 8mA */
> > +     /* EQ Training State Machine Setting */
> > +     err |= ps8622_set(cl, 0x04, 0x54, 0x10); /* RCO calibration start
> */
> > +     /* Logic, needs more than 10 I2C command */
> > +     err |= ps8622_set(cl, 0x01, 0x02, 0x80 |
> ps_bridge->max_lane_count);
> > +                                              /* [4:0] MAX_LANE_COUNT
> set to
> > +                                               * max supported lanes */
> > +     err |= ps8622_set(cl, 0x01, 0x21, 0x80 | ps_bridge->lane_count);
> > +                                              /* [4:0] LANE_COUNT_SET
> set to
> > +                                               * chosen lane count */
> > +     err |= ps8622_set(cl, 0x00, 0x52, 0x20);
> > +     err |= ps8622_set(cl, 0x00, 0xf1, 0x03); /* HPD CP toggle enable */
> > +     err |= ps8622_set(cl, 0x00, 0x62, 0x41);
> > +     err |= ps8622_set(cl, 0x00, 0xf6, 0x01); /* Counter number, add 1ms
> > +                                               * counter delay */
> > +     err |= ps8622_set(cl, 0x00, 0x77, 0x06); /* [6]PWM function
> control by
> > +                                               * DPCD0040f[7], default
> is PWM
> > +                                               * block always works. */
> > +     err |= ps8622_set(cl, 0x00, 0x4c, 0x04); /* 04h Adjust VTotal
> tolerance
> > +                                               * to fix the 30Hz no
> display
> > +                                               * issue */
> > +     err |= ps8622_set(cl, 0x01, 0xc0, 0x00); /* DPCD00400='h00, Parade
> OUI =
> > +                                               * 'h001cf8 */
> > +     err |= ps8622_set(cl, 0x01, 0xc1, 0x1c); /* DPCD00401='h1c */
> > +     err |= ps8622_set(cl, 0x01, 0xc2, 0xf8); /* DPCD00402='hf8 */
> > +     err |= ps8622_set(cl, 0x01, 0xc3, 0x44); /* DPCD403~408 = ASCII
> code
> > +                                               * D2SLV5='h4432534c5635
> */
> > +     err |= ps8622_set(cl, 0x01, 0xc4, 0x32); /* DPCD404 */
> > +     err |= ps8622_set(cl, 0x01, 0xc5, 0x53); /* DPCD405 */
> > +     err |= ps8622_set(cl, 0x01, 0xc6, 0x4c); /* DPCD406 */
> > +     err |= ps8622_set(cl, 0x01, 0xc7, 0x56); /* DPCD407 */
> > +     err |= ps8622_set(cl, 0x01, 0xc8, 0x35); /* DPCD408 */
> > +     err |= ps8622_set(cl, 0x01, 0xca, 0x01); /* DPCD40A, Initial Code
> major
> > +                                               * revision '01' */
> > +     err |= ps8622_set(cl, 0x01, 0xcb, 0x05); /* DPCD40B, Initial Code
> minor
> > +                                               * revision '05' */
> > +     if (ps_bridge->bl) {
> > +             err |= ps8622_set(cl, 0x01, 0xa5, 0xa0);
> > +                                             /* DPCD720, internal PWM */
> > +             err |= ps8622_set(cl, 0x01, 0xa7,
> > +                             ps_bridge->bl->props.brightness);
> > +                                              /* FFh for 100%
> brightness,
> > +                                               *  0h for 0% brightness
> */
> > +     } else {
> > +             err |= ps8622_set(cl, 0x01, 0xa5, 0x80);
> > +                                             /* DPCD720, external PWM */
> > +     }
> > +     err |= ps8622_set(cl, 0x01, 0xcc, 0x13); /* Set LVDS output as
> 6bit-VESA
> > +                                               * mapping, single LVDS
> channel
> > +                                               * */
> > +     err |= ps8622_set(cl, 0x02, 0xb1, 0x20); /* Enable SSC set by
> register
> > +                                               * */
> > +     err |= ps8622_set(cl, 0x04, 0x10, 0x16); /* Set SSC enabled and
> +/-1%
> > +                                               * central spreading */
> > +     /* Logic end */
> > +     err |= ps8622_set(cl, 0x04, 0x59, 0x60); /* MPU Clock source: LC
> => RCO
> > +                                               * */
> > +     err |= ps8622_set(cl, 0x04, 0x54, 0x14); /* LC -> RCO */
> > +     err |= ps8622_set(cl, 0x02, 0xa1, 0x91); /* HPD high */
> > +
> > +     return err ? -EIO : 0;
> > +}
> > +
>
> [.....]
>
> > +
> > +static void ps8622_pre_enable(struct drm_bridge *bridge)
> > +{
> > +     struct ps8622_bridge *ps_bridge = bridge->driver_private;
> > +     int ret;
> > +
> > +     mutex_lock(&ps_bridge->enable_mutex);
> > +     if (ps_bridge->enabled)
> > +             goto out;
> > +
> > +     if (gpio_is_valid(ps_bridge->gpio_rst_n))
> > +             gpio_set_value(ps_bridge->gpio_rst_n, 0);
> > +
> > +     if (ps_bridge->v12) {
> > +             ret = regulator_enable(ps_bridge->v12);
> > +             if (ret)
> > +                     DRM_ERROR("fails to enable ps_bridge->v12");
> > +     }
> > +
> > +     drm_panel_pre_enable(ps_bridge->panel);
> > +
> > +     if (gpio_is_valid(ps_bridge->gpio_slp_n))
> > +             gpio_set_value(ps_bridge->gpio_slp_n, 1);
> > +
> > +     /*
> > +      * T1 is the range of time that it takes for the power to rise
> after we
> > +      * enable the lcd fet. T2 is the range of time in which the data
> sheet
> > +      * specifies we should deassert the reset pin.
> > +      *
> > +      * If it takes T1.max for the power to rise, we need to wait
> atleast
> > +      * T2.min before deasserting the reset pin. If it takes T1.min for
> the
> > +      * power to rise, we need to wait at most T2.max before
> deasserting the
> > +      * reset pin.
> > +      */
> > +     usleep_range(PS8622_RST_HIGH_T2_MIN_US +
> PS8622_POWER_RISE_T1_MAX_US,
> > +                  PS8622_RST_HIGH_T2_MAX_US +
> PS8622_POWER_RISE_T1_MIN_US);
> > +
> > +     if (gpio_is_valid(ps_bridge->gpio_rst_n))
> > +             gpio_set_value(ps_bridge->gpio_rst_n, 1);
> > +
> > +     ret = ps8622_send_config(ps_bridge);
> > +     if (ret)
> > +             DRM_ERROR("Failed to send config to bridge (%d)\n", ret);
> > +
> > +     ps_bridge->enabled = true;
> > +
> > +     mutex_unlock(&ps_bridge->enable_mutex);
> > +     return;
> > +
> > +out:
> > +
> > +     mutex_unlock(&ps_bridge->enable_mutex);
> > +}
>
> mutex_unlock() is duplicated. Also, 'return' is unnecessary.
> Please fix it as below.
>
> +       ps_bridge->enabled = true;
> +
> +out:
> +       mutex_unlock(&ps_bridge->enable_mutex);
> +}
>
Right. Will change it.


>
> > +
> > +static void ps8622_enable(struct drm_bridge *bridge)
> > +{
> > +     struct ps8622_bridge *ps_bridge = bridge->driver_private;
> > +
> > +     mutex_lock(&ps_bridge->enable_mutex);
> > +     if (ps_bridge->enabled)
> > +             drm_panel_enable(ps_bridge->panel);
> > +     mutex_unlock(&ps_bridge->enable_mutex);
> > +}
> > +
> > +static void ps8622_disable(struct drm_bridge *bridge)
> > +{
> > +     struct ps8622_bridge *ps_bridge = bridge->driver_private;
> > +
> > +     mutex_lock(&ps_bridge->enable_mutex);
> > +
> > +     if (!ps_bridge->enabled)
> > +             goto out;
> > +
> > +     ps_bridge->enabled = false;
> > +
> > +     drm_panel_disable(ps_bridge->panel);
> > +     msleep(PS8622_PWMO_END_T12_MS);
> > +
> > +     /*
> > +      * This doesn't matter if the regulators are turned off, but
> something
> > +      * else might keep them on. In that case, we want to assert the
> slp gpio
> > +      * to lower power.
> > +      */
> > +     if (gpio_is_valid(ps_bridge->gpio_slp_n))
> > +             gpio_set_value(ps_bridge->gpio_slp_n, 0);
> > +
> > +     drm_panel_post_disable(ps_bridge->panel);
> > +     if (ps_bridge->v12)
> > +             regulator_disable(ps_bridge->v12);
> > +
> > +     /*
> > +      * Sleep for at least the amount of time that it takes the power
> rail to
> > +      * fall to prevent asserting the rst gpio from doing anything.
> > +      */
> > +     usleep_range(PS8622_POWER_FALL_T16_MAX_US,
> > +                  2 * PS8622_POWER_FALL_T16_MAX_US);
> > +     if (gpio_is_valid(ps_bridge->gpio_rst_n))
> > +             gpio_set_value(ps_bridge->gpio_rst_n, 0);
> > +
> > +     msleep(PS8622_POWER_OFF_T17_MS);
> > +
> > +out:
> > +     mutex_unlock(&ps_bridge->enable_mutex);
> > +}
> > +
> > +static void ps8622_post_disable(struct drm_bridge *bridge)
> > +{
> > +}
>
> How about just removing this empty function?

That is not possible. Because the bridge callbacks are called in this
fashion:
       bridge->funcs->post_disable(bridge);
So, if that particular callback turns NULL, it will crash!


>
[.....]
>
> Best regards,
> Jingoo Han
>
>
Regards,
Ajay Kumar

[-- Attachment #1.2: Type: text/html, Size: 18603 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH V2 8/9] drm/bridge: Add PS8622 bridge driver
  2014-04-22 13:31   ` Sean Paul
@ 2014-04-22 14:03     ` Ajay kumar
  0 siblings, 0 replies; 58+ messages in thread
From: Ajay kumar @ 2014-04-22 14:03 UTC (permalink / raw)
  To: Sean Paul, vpalatin
  Cc: Ajay Kumar, dri-devel, linux-samsung-soc, InKi Dae, sunil joshi,
	Prashanth G, Andrew Bresticker, Rahul Sharma, Olof Johansson

Hi Sean,


On Tue, Apr 22, 2014 at 7:01 PM, Sean Paul <seanpaul@google.com> wrote:
>
> On Mon, Apr 21, 2014 at 6:39 PM, Ajay Kumar <ajaykumar.rs@samsung.com> wrote:
> > This patch adds a drm_bridge driver for the PS8622 DisplayPort to LVDS
> > bridge chip.
> >
> > Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
> > Signed-off-by: Sean Paul <seanpaul@chromium.org>
> > Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
> > Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
>
> Hi Ajay,
> I don't think that you should be claiming authorship of this driver, I
> don't see any commits from you in the chromium tree ([1] & [2]). The
> driver was originally written by vpalatin@chromium.org (who's
> completely missing from SoB), it should probably be attributed to him.
>
> Sean
>
Oops. I am really sorry and I didn't really want to claim the authorship
for this patch. I missed it while doing "git format-patch".
And,  thanks for letting me know that Vincent was the actual author.
I will add his authorship.

Regards,
Ajay Kumar

>
>
> [1]- https://git.chromium.org/gitweb/?p=chromiumos/third_party/kernel-next.git;a=history;f=drivers/auxdisplay/ps8622.c;hb=fac579ad884d064d2c3cda988117e7dd66fc30c4
> [2]- https://git.chromium.org/gitweb/?p=chromiumos/third_party/kernel-next.git;a=history;f=drivers/gpu/drm/bridge/ps8622.c;hb=refs/heads/chromeos-3.8
>
>
>
> > ---
> > Changes since V1:
> >         Pushing V1 for this as V2 because this patch holds good in this series.
> >
> >  drivers/gpu/drm/bridge/Kconfig  |    7 +
> >  drivers/gpu/drm/bridge/Makefile |    1 +
> >  drivers/gpu/drm/bridge/ps8622.c |  566 +++++++++++++++++++++++++++++++++++++++
> >  include/drm/bridge/ps8622.h     |   42 +++
> >  4 files changed, 616 insertions(+)
> >  create mode 100644 drivers/gpu/drm/bridge/ps8622.c
> >  create mode 100644 include/drm/bridge/ps8622.h
> >
> > diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> > index 3bc6845..aba21fc 100644
> > --- a/drivers/gpu/drm/bridge/Kconfig
> > +++ b/drivers/gpu/drm/bridge/Kconfig
> > @@ -4,3 +4,10 @@ config DRM_PTN3460
> >         select DRM_KMS_HELPER
> >         select DRM_PANEL
> >         ---help---
> > +
> > +config DRM_PS8622
> > +       tristate "Parade eDP/LVDS bridge"
> > +       depends on DRM
> > +       select DRM_KMS_HELPER
> > +       select DRM_PANEL
> > +       ---help---
> > diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> > index b4733e1..d1b5daa 100644
> > --- a/drivers/gpu/drm/bridge/Makefile
> > +++ b/drivers/gpu/drm/bridge/Makefile
> > @@ -1,3 +1,4 @@
> >  ccflags-y := -Iinclude/drm
> >
> >  obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
> > +obj-$(CONFIG_DRM_PS8622) += ps8622.o
> > diff --git a/drivers/gpu/drm/bridge/ps8622.c b/drivers/gpu/drm/bridge/ps8622.c
> > new file mode 100644
> > index 0000000..1e6b3ca
> > --- /dev/null
> > +++ b/drivers/gpu/drm/bridge/ps8622.c
> > @@ -0,0 +1,566 @@
> > +/*
> > + * Parade PS8622 eDP/LVDS bridge driver
> > + *
> > + * Copyright (C) 2014 Google, Inc.
> > + *
> > + * This software is licensed under the terms of the GNU General Public
> > + * License version 2, as published by the Free Software Foundation, and
> > + * may be copied, distributed, and modified under those terms.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/backlight.h>
> > +#include <linux/delay.h>
> > +#include <linux/err.h>
> > +#include <linux/fb.h>
> > +#include <linux/gpio.h>
> > +#include <linux/i2c.h>
> > +#include <linux/of.h>
> > +#include <linux/of_gpio.h>
> > +#include <linux/pm.h>
> > +#include <linux/regulator/consumer.h>
> > +#include <drm/drm_panel.h>
> > +
> > +#include "drmP.h"
> > +#include "drm_crtc.h"
> > +#include "drm_crtc_helper.h"
> > +
> > +struct ps8622_bridge {
> > +       struct drm_connector connector;
> > +       struct drm_bridge *bridge;
> > +       struct drm_encoder *encoder;
> > +       struct drm_panel *panel;
> > +       struct i2c_client *client;
> > +       struct regulator *v12;
> > +       struct backlight_device *bl;
> > +       struct mutex enable_mutex;
> > +
> > +       int gpio_slp_n;
> > +       int gpio_rst_n;
> > +
> > +       u8 max_lane_count;
> > +       u8 lane_count;
> > +
> > +       bool enabled;
> > +
> > +       struct drm_display_mode mode;
> > +};
> > +
> > +struct ps8622_device_data {
> > +       u8 max_lane_count;
> > +};
> > +
> > +static const struct ps8622_device_data ps8622_data = {
> > +       .max_lane_count = 1,
> > +};
> > +
> > +static const struct ps8622_device_data ps8625_data = {
> > +       .max_lane_count = 2,
> > +};
> > +
> > +/* Brightness scale on the Parade chip */
> > +#define PS8622_MAX_BRIGHTNESS 0xff
> > +
> > +/* Timings taken from the version 1.7 datasheet for the PS8622/PS8625 */
> > +#define PS8622_POWER_RISE_T1_MIN_US 10
> > +#define PS8622_POWER_RISE_T1_MAX_US 10000
> > +#define PS8622_RST_HIGH_T2_MIN_US 3000
> > +#define PS8622_RST_HIGH_T2_MAX_US 30000
> > +#define PS8622_PWMO_END_T12_MS 200
> > +#define PS8622_POWER_FALL_T16_MAX_US 10000
> > +#define PS8622_POWER_OFF_T17_MS 500
> > +
> > +#if ((PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US) > \
> > +       (PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US))
> > +#error "T2.min + T1.max must be less than T2.max + T1.min"
> > +#endif
> > +
> > +static int ps8622_set(struct i2c_client *client, u8 page, u8 reg, u8 val)
> > +{
> > +       int ret;
> > +       struct i2c_adapter *adap = client->adapter;
> > +       struct i2c_msg msg;
> > +       u8 data[] = {reg, val};
> > +
> > +       msg.addr = client->addr + page;
> > +       msg.flags = 0;
> > +       msg.len = sizeof(data);
> > +       msg.buf = data;
> > +
> > +       ret = i2c_transfer(adap, &msg, 1);
> > +       if (ret != 1)
> > +               pr_warn("PS8622 I2C write (0x%02x,0x%02x,0x%02x) failed: %d\n",
> > +                       client->addr + page, reg, val, ret);
> > +       return !(ret == 1);
> > +}
> > +
> > +static int ps8622_send_config(struct ps8622_bridge *ps_bridge)
> > +{
> > +       struct i2c_client *cl = ps_bridge->client;
> > +       int err = 0;
> > +
> > +       /* wait 20ms after power ON */
> > +       usleep_range(20000, 30000);
> > +
> > +       err |= ps8622_set(cl, 0x02, 0xa1, 0x01); /* HPD low */
> > +       /* SW setting */
> > +       err |= ps8622_set(cl, 0x04, 0x14, 0x01); /* [1:0] SW output 1.2V voltage
> > +                                                 * is lower to 96% */
> > +       /* RCO SS setting */
> > +       err |= ps8622_set(cl, 0x04, 0xe3, 0x20); /* [5:4] = b01 0.5%, b10 1%,
> > +                                                 * b11 1.5% */
> > +       err |= ps8622_set(cl, 0x04, 0xe2, 0x80); /* [7] RCO SS enable */
> > +       /* RPHY Setting */
> > +       err |= ps8622_set(cl, 0x04, 0x8a, 0x0c); /* [3:2] CDR tune wait cycle
> > +                                                 * before measure for fine tune
> > +                                                 * b00: 1us b01: 0.5us b10:2us
> > +                                                 * b11: 4us */
> > +       err |= ps8622_set(cl, 0x04, 0x89, 0x08); /* [3] RFD always on */
> > +       err |= ps8622_set(cl, 0x04, 0x71, 0x2d); /* CTN lock in/out:
> > +                                                 * 20000ppm/80000ppm.
> > +                                                 * Lock out 2 times. */
> > +       /* 2.7G CDR settings */
> > +       err |= ps8622_set(cl, 0x04, 0x7d, 0x07); /* NOF=40LSB for HBR CDR
> > +                                                 * setting */
> > +       err |= ps8622_set(cl, 0x04, 0x7b, 0x00); /* [1:0] Fmin=+4bands */
> > +       err |= ps8622_set(cl, 0x04, 0x7a, 0xfd); /* [7:5] DCO_FTRNG=+-40% */
> > +       /* 1.62G CDR settings */
> > +       err |= ps8622_set(cl, 0x04, 0xc0, 0x12); /* [5:2]NOF=64LSB [1:0]DCO
> > +                                                 * scale is 2/5 */
> > +       err |= ps8622_set(cl, 0x04, 0xc1, 0x92); /* Gitune=-37% */
> > +       err |= ps8622_set(cl, 0x04, 0xc2, 0x1c); /* Fbstep=100% */
> > +       err |= ps8622_set(cl, 0x04, 0x32, 0x80); /* [7] LOS signal disable */
> > +       /* RPIO Setting */
> > +       err |= ps8622_set(cl, 0x04, 0x00, 0xb0); /* [7:4] LVDS driver bias
> > +                                                 * current : 75% (250mV swing)
> > +                                                 * */
> > +       err |= ps8622_set(cl, 0x04, 0x15, 0x40); /* [7:6] Right-bar GPIO output
> > +                                                 * strength is 8mA */
> > +       /* EQ Training State Machine Setting */
> > +       err |= ps8622_set(cl, 0x04, 0x54, 0x10); /* RCO calibration start */
> > +       /* Logic, needs more than 10 I2C command */
> > +       err |= ps8622_set(cl, 0x01, 0x02, 0x80 | ps_bridge->max_lane_count);
> > +                                                /* [4:0] MAX_LANE_COUNT set to
> > +                                                 * max supported lanes */
> > +       err |= ps8622_set(cl, 0x01, 0x21, 0x80 | ps_bridge->lane_count);
> > +                                                /* [4:0] LANE_COUNT_SET set to
> > +                                                 * chosen lane count */
> > +       err |= ps8622_set(cl, 0x00, 0x52, 0x20);
> > +       err |= ps8622_set(cl, 0x00, 0xf1, 0x03); /* HPD CP toggle enable */
> > +       err |= ps8622_set(cl, 0x00, 0x62, 0x41);
> > +       err |= ps8622_set(cl, 0x00, 0xf6, 0x01); /* Counter number, add 1ms
> > +                                                 * counter delay */
> > +       err |= ps8622_set(cl, 0x00, 0x77, 0x06); /* [6]PWM function control by
> > +                                                 * DPCD0040f[7], default is PWM
> > +                                                 * block always works. */
> > +       err |= ps8622_set(cl, 0x00, 0x4c, 0x04); /* 04h Adjust VTotal tolerance
> > +                                                 * to fix the 30Hz no display
> > +                                                 * issue */
> > +       err |= ps8622_set(cl, 0x01, 0xc0, 0x00); /* DPCD00400='h00, Parade OUI =
> > +                                                 * 'h001cf8 */
> > +       err |= ps8622_set(cl, 0x01, 0xc1, 0x1c); /* DPCD00401='h1c */
> > +       err |= ps8622_set(cl, 0x01, 0xc2, 0xf8); /* DPCD00402='hf8 */
> > +       err |= ps8622_set(cl, 0x01, 0xc3, 0x44); /* DPCD403~408 = ASCII code
> > +                                                 * D2SLV5='h4432534c5635 */
> > +       err |= ps8622_set(cl, 0x01, 0xc4, 0x32); /* DPCD404 */
> > +       err |= ps8622_set(cl, 0x01, 0xc5, 0x53); /* DPCD405 */
> > +       err |= ps8622_set(cl, 0x01, 0xc6, 0x4c); /* DPCD406 */
> > +       err |= ps8622_set(cl, 0x01, 0xc7, 0x56); /* DPCD407 */
> > +       err |= ps8622_set(cl, 0x01, 0xc8, 0x35); /* DPCD408 */
> > +       err |= ps8622_set(cl, 0x01, 0xca, 0x01); /* DPCD40A, Initial Code major
> > +                                                 * revision '01' */
> > +       err |= ps8622_set(cl, 0x01, 0xcb, 0x05); /* DPCD40B, Initial Code minor
> > +                                                 * revision '05' */
> > +       if (ps_bridge->bl) {
> > +               err |= ps8622_set(cl, 0x01, 0xa5, 0xa0);
> > +                                               /* DPCD720, internal PWM */
> > +               err |= ps8622_set(cl, 0x01, 0xa7,
> > +                               ps_bridge->bl->props.brightness);
> > +                                                /* FFh for 100% brightness,
> > +                                                 *  0h for 0% brightness */
> > +       } else {
> > +               err |= ps8622_set(cl, 0x01, 0xa5, 0x80);
> > +                                               /* DPCD720, external PWM */
> > +       }
> > +       err |= ps8622_set(cl, 0x01, 0xcc, 0x13); /* Set LVDS output as 6bit-VESA
> > +                                                 * mapping, single LVDS channel
> > +                                                 * */
> > +       err |= ps8622_set(cl, 0x02, 0xb1, 0x20); /* Enable SSC set by register
> > +                                                 * */
> > +       err |= ps8622_set(cl, 0x04, 0x10, 0x16); /* Set SSC enabled and +/-1%
> > +                                                 * central spreading */
> > +       /* Logic end */
> > +       err |= ps8622_set(cl, 0x04, 0x59, 0x60); /* MPU Clock source: LC => RCO
> > +                                                 * */
> > +       err |= ps8622_set(cl, 0x04, 0x54, 0x14); /* LC -> RCO */
> > +       err |= ps8622_set(cl, 0x02, 0xa1, 0x91); /* HPD high */
> > +
> > +       return err ? -EIO : 0;
> > +}
> > +
> > +static int ps8622_backlight_update(struct backlight_device *bl)
> > +{
> > +       struct ps8622_bridge *ps_bridge = dev_get_drvdata(&bl->dev);
> > +       int ret, brightness = bl->props.brightness;
> > +
> > +       if (bl->props.power != FB_BLANK_UNBLANK ||
> > +           bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
> > +               brightness = 0;
> > +
> > +       mutex_lock(&ps_bridge->enable_mutex);
> > +
> > +       if (!ps_bridge->enabled) {
> > +               ret = -EINVAL;
> > +               goto out;
> > +       }
> > +
> > +       ret = ps8622_set(ps_bridge->client, 0x01, 0xa7, brightness);
> > +
> > +out:
> > +       mutex_unlock(&ps_bridge->enable_mutex);
> > +       return ret;
> > +}
> > +
> > +static int ps8622_backlight_get(struct backlight_device *bl)
> > +{
> > +       return bl->props.brightness;
> > +}
> > +
> > +static const struct backlight_ops ps8622_backlight_ops = {
> > +       .update_status  = ps8622_backlight_update,
> > +       .get_brightness = ps8622_backlight_get,
> > +};
> > +
> > +static void ps8622_pre_enable(struct drm_bridge *bridge)
> > +{
> > +       struct ps8622_bridge *ps_bridge = bridge->driver_private;
> > +       int ret;
> > +
> > +       mutex_lock(&ps_bridge->enable_mutex);
> > +       if (ps_bridge->enabled)
> > +               goto out;
> > +
> > +       if (gpio_is_valid(ps_bridge->gpio_rst_n))
> > +               gpio_set_value(ps_bridge->gpio_rst_n, 0);
> > +
> > +       if (ps_bridge->v12) {
> > +               ret = regulator_enable(ps_bridge->v12);
> > +               if (ret)
> > +                       DRM_ERROR("fails to enable ps_bridge->v12");
> > +       }
> > +
> > +       drm_panel_pre_enable(ps_bridge->panel);
> > +
> > +       if (gpio_is_valid(ps_bridge->gpio_slp_n))
> > +               gpio_set_value(ps_bridge->gpio_slp_n, 1);
> > +
> > +       /*
> > +        * T1 is the range of time that it takes for the power to rise after we
> > +        * enable the lcd fet. T2 is the range of time in which the data sheet
> > +        * specifies we should deassert the reset pin.
> > +        *
> > +        * If it takes T1.max for the power to rise, we need to wait atleast
> > +        * T2.min before deasserting the reset pin. If it takes T1.min for the
> > +        * power to rise, we need to wait at most T2.max before deasserting the
> > +        * reset pin.
> > +        */
> > +       usleep_range(PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US,
> > +                    PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US);
> > +
> > +       if (gpio_is_valid(ps_bridge->gpio_rst_n))
> > +               gpio_set_value(ps_bridge->gpio_rst_n, 1);
> > +
> > +       ret = ps8622_send_config(ps_bridge);
> > +       if (ret)
> > +               DRM_ERROR("Failed to send config to bridge (%d)\n", ret);
> > +
> > +       ps_bridge->enabled = true;
> > +
> > +       mutex_unlock(&ps_bridge->enable_mutex);
> > +       return;
> > +
> > +out:
> > +
> > +       mutex_unlock(&ps_bridge->enable_mutex);
> > +}
> > +
> > +static void ps8622_enable(struct drm_bridge *bridge)
> > +{
> > +       struct ps8622_bridge *ps_bridge = bridge->driver_private;
> > +
> > +       mutex_lock(&ps_bridge->enable_mutex);
> > +       if (ps_bridge->enabled)
> > +               drm_panel_enable(ps_bridge->panel);
> > +       mutex_unlock(&ps_bridge->enable_mutex);
> > +}
> > +
> > +static void ps8622_disable(struct drm_bridge *bridge)
> > +{
> > +       struct ps8622_bridge *ps_bridge = bridge->driver_private;
> > +
> > +       mutex_lock(&ps_bridge->enable_mutex);
> > +
> > +       if (!ps_bridge->enabled)
> > +               goto out;
> > +
> > +       ps_bridge->enabled = false;
> > +
> > +       drm_panel_disable(ps_bridge->panel);
> > +       msleep(PS8622_PWMO_END_T12_MS);
> > +
> > +       /*
> > +        * This doesn't matter if the regulators are turned off, but something
> > +        * else might keep them on. In that case, we want to assert the slp gpio
> > +        * to lower power.
> > +        */
> > +       if (gpio_is_valid(ps_bridge->gpio_slp_n))
> > +               gpio_set_value(ps_bridge->gpio_slp_n, 0);
> > +
> > +       drm_panel_post_disable(ps_bridge->panel);
> > +       if (ps_bridge->v12)
> > +               regulator_disable(ps_bridge->v12);
> > +
> > +       /*
> > +        * Sleep for at least the amount of time that it takes the power rail to
> > +        * fall to prevent asserting the rst gpio from doing anything.
> > +        */
> > +       usleep_range(PS8622_POWER_FALL_T16_MAX_US,
> > +                    2 * PS8622_POWER_FALL_T16_MAX_US);
> > +       if (gpio_is_valid(ps_bridge->gpio_rst_n))
> > +               gpio_set_value(ps_bridge->gpio_rst_n, 0);
> > +
> > +       msleep(PS8622_POWER_OFF_T17_MS);
> > +
> > +out:
> > +       mutex_unlock(&ps_bridge->enable_mutex);
> > +}
> > +
> > +static void ps8622_post_disable(struct drm_bridge *bridge)
> > +{
> > +}
> > +
> > +static void ps8622_destroy(struct drm_bridge *bridge)
> > +{
> > +       struct ps8622_bridge *ps_bridge = bridge->driver_private;
> > +
> > +       drm_bridge_cleanup(bridge);
> > +       if (ps_bridge->bl)
> > +               backlight_device_unregister(ps_bridge->bl);
> > +
> > +       put_device(&ps_bridge->client->dev);
> > +}
> > +
> > +static const struct drm_bridge_funcs ps8622_bridge_funcs = {
> > +       .pre_enable = ps8622_pre_enable,
> > +       .enable = ps8622_enable,
> > +       .disable = ps8622_disable,
> > +       .post_disable = ps8622_post_disable,
> > +       .destroy = ps8622_destroy,
> > +};
> > +
> > +static int ps8622_get_modes(struct drm_connector *connector)
> > +{
> > +       struct ps8622_bridge *ps_bridge;
> > +       struct drm_display_mode *new_mode;
> > +
> > +       ps_bridge = container_of(connector, struct ps8622_bridge, connector);
> > +
> > +       new_mode = drm_mode_duplicate(connector->dev, &ps_bridge->mode);
> > +       if (!new_mode) {
> > +               DRM_ERROR("Failed to duplicate ps_bridge mode!\n");
> > +               return 0;
> > +       }
> > +       drm_mode_probed_add(connector, new_mode);
> > +       return 1;
> > +}
> > +
> > +static int ps8622_mode_valid(struct drm_connector *connector,
> > +               struct drm_display_mode *mode)
> > +{
> > +       struct ps8622_bridge *ps_bridge;
> > +
> > +       ps_bridge = container_of(connector, struct ps8622_bridge, connector);
> > +
> > +       return drm_mode_equal(mode, &ps_bridge->mode) ? MODE_OK : MODE_BAD;
> > +}
> > +
> > +static struct drm_encoder *ps8622_best_encoder(struct drm_connector *connector)
> > +{
> > +       struct ps8622_bridge *ps_bridge;
> > +
> > +       ps_bridge = container_of(connector, struct ps8622_bridge, connector);
> > +
> > +       return ps_bridge->encoder;
> > +}
> > +
> > +static const struct drm_connector_helper_funcs ps8622_connector_helper_funcs = {
> > +       .get_modes = ps8622_get_modes,
> > +       .mode_valid = ps8622_mode_valid,
> > +       .best_encoder = ps8622_best_encoder,
> > +};
> > +
> > +static enum drm_connector_status ps8622_detect(struct drm_connector *connector,
> > +               bool force)
> > +{
> > +       return connector_status_connected;
> > +}
> > +
> > +static void ps8622_connector_destroy(struct drm_connector *connector)
> > +{
> > +       drm_connector_cleanup(connector);
> > +}
> > +
> > +static const struct drm_connector_funcs ps8622_connector_funcs = {
> > +       .dpms = drm_helper_connector_dpms,
> > +       .fill_modes = drm_helper_probe_single_connector_modes,
> > +       .detect = ps8622_detect,
> > +       .destroy = ps8622_connector_destroy,
> > +};
> > +
> > +static const struct of_device_id ps8622_devices[] = {
> > +       {
> > +               .compatible = "parade,ps8622",
> > +               .data   = &ps8622_data,
> > +       }, {
> > +               .compatible = "parade,ps8625",
> > +               .data   = &ps8625_data,
> > +       }, {
> > +               /* end node */
> > +       }
> > +};
> > +
> > +int ps8622_init(struct drm_device *dev, struct drm_encoder *encoder,
> > +               struct i2c_client *client, struct device_node *node,
> > +               struct drm_panel *panel)
> > +{
> > +       int ret;
> > +       const struct of_device_id *match;
> > +       const struct ps8622_device_data *device_data;
> > +       struct drm_bridge *bridge;
> > +       struct ps8622_bridge *ps_bridge;
> > +
> > +       node = of_find_matching_node_and_match(NULL, ps8622_devices, &match);
> > +       if (!node)
> > +               return -ENODEV;
> > +
> > +       bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL);
> > +       if (!bridge) {
> > +               DRM_ERROR("Failed to allocate drm bridge\n");
> > +               return -ENOMEM;
> > +       }
> > +
> > +       ps_bridge = devm_kzalloc(dev->dev, sizeof(*ps_bridge), GFP_KERNEL);
> > +       if (!ps_bridge) {
> > +               DRM_ERROR("could not allocate ps bridge\n");
> > +               return -ENOMEM;
> > +       }
> > +
> > +       mutex_init(&ps_bridge->enable_mutex);
> > +
> > +       device_data = match->data;
> > +       ps_bridge->client = client;
> > +       ps_bridge->encoder = encoder;
> > +       ps_bridge->bridge = bridge;
> > +       ps_bridge->v12 = devm_regulator_get(&client->dev, "vdd_bridge");
> > +       if (IS_ERR(ps_bridge->v12)) {
> > +               DRM_INFO("no 1.2v regulator found for PS8622\n");
> > +               ps_bridge->v12 = NULL;
> > +       }
> > +
> > +       ps_bridge->gpio_slp_n = of_get_named_gpio(node, "sleep-gpio", 0);
> > +       if (gpio_is_valid(ps_bridge->gpio_slp_n)) {
> > +               ret = devm_gpio_request_one(&client->dev, ps_bridge->gpio_slp_n,
> > +                                           GPIOF_OUT_INIT_HIGH,
> > +                                           "PS8622_SLP_N");
> > +               if (ret)
> > +                       goto err_client;
> > +       }
> > +
> > +       ps_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0);
> > +       if (gpio_is_valid(ps_bridge->gpio_rst_n)) {
> > +               /*
> > +                * Assert the reset pin high to avoid the bridge being
> > +                * initialized prematurely
> > +                */
> > +               ret = devm_gpio_request_one(&client->dev, ps_bridge->gpio_rst_n,
> > +                                           GPIOF_OUT_INIT_HIGH,
> > +                                           "PS8622_RST_N");
> > +               if (ret)
> > +                       goto err_client;
> > +       }
> > +
> > +       ps_bridge->max_lane_count = device_data->max_lane_count;
> > +
> > +       if (of_property_read_u8(node, "lane-count", &ps_bridge->lane_count))
> > +               ps_bridge->lane_count = ps_bridge->max_lane_count;
> > +       else if (ps_bridge->lane_count > ps_bridge->max_lane_count) {
> > +               DRM_ERROR("lane-count property is too high for DP bridge\n");
> > +               ps_bridge->lane_count = ps_bridge->max_lane_count;
> > +       }
> > +
> > +       if (!of_find_property(node, "use-external-pwm", NULL)) {
> > +               ps_bridge->bl = backlight_device_register("ps8622-backlight",
> > +                               dev->dev, ps_bridge, &ps8622_backlight_ops,
> > +                               NULL);
> > +               if (IS_ERR(ps_bridge->bl)) {
> > +                       DRM_ERROR("failed to register backlight\n");
> > +                       ret = PTR_ERR(ps_bridge->bl);
> > +                       ps_bridge->bl = NULL;
> > +                       goto err_client;
> > +               }
> > +               ps_bridge->bl->props.max_brightness = PS8622_MAX_BRIGHTNESS;
> > +               ps_bridge->bl->props.brightness = PS8622_MAX_BRIGHTNESS;
> > +       }
> > +
> > +       ret = of_get_drm_display_mode(node, &ps_bridge->mode, 0);
> > +       if (ret) {
> > +               DRM_ERROR("Failed to get display mode, ret=%d\n", ret);
> > +               goto err_backlight;
> > +       }
> > +
> > +       ret = drm_bridge_init(dev, ps_bridge->bridge, &ps8622_bridge_funcs);
> > +       if (ret) {
> > +               DRM_ERROR("Failed to initialize bridge with drm\n");
> > +               goto err_backlight;
> > +       }
> > +
> > +       if (panel) {
> > +               ps_bridge->panel = panel;
> > +               drm_panel_attach(ps_bridge->panel, &ps_bridge->connector);
> > +       }
> > +
> > +       ps_bridge->bridge->driver_private = ps_bridge;
> > +       encoder->bridge = ps_bridge->bridge;
> > +       ps_bridge->enabled = false;
> > +       ps_bridge->connector.polled = DRM_CONNECTOR_POLL_HPD;
> > +
> > +       ret = drm_connector_init(dev, &ps_bridge->connector,
> > +                       &ps8622_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
> > +       if (ret) {
> > +               DRM_ERROR("Failed to initialize connector with drm\n");
> > +               goto err_bridge;
> > +       }
> > +       drm_connector_helper_add(&ps_bridge->connector,
> > +                       &ps8622_connector_helper_funcs);
> > +       drm_sysfs_connector_add(&ps_bridge->connector);
> > +       drm_mode_connector_attach_encoder(&ps_bridge->connector, encoder);
> > +
> > +       return 0;
> > +
> > +err_bridge:
> > +       drm_bridge_cleanup(ps_bridge->bridge);
> > +err_backlight:
> > +       if (ps_bridge->bl)
> > +               backlight_device_unregister(ps_bridge->bl);
> > +
> > +err_client:
> > +       put_device(&client->dev);
> > +       DRM_ERROR("device probe failed : %d\n", ret);
> > +       return ret;
> > +}
> > +EXPORT_SYMBOL(ps8622_init);
> > diff --git a/include/drm/bridge/ps8622.h b/include/drm/bridge/ps8622.h
> > new file mode 100644
> > index 0000000..4fc1959
> > --- /dev/null
> > +++ b/include/drm/bridge/ps8622.h
> > @@ -0,0 +1,42 @@
> > +/*
> > + * Copyright (C) 2014 Google, Inc.
> > + *
> > + * This software is licensed under the terms of the GNU General Public
> > + * License version 2, as published by the Free Software Foundation, and
> > + * may be copied, distributed, and modified under those terms.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#ifndef _DRM_BRIDGE_PS8622_H_
> > +#define _DRM_BRIDGE_PS8622_H_
> > +
> > +struct drm_device;
> > +struct drm_encoder;
> > +struct i2c_client;
> > +struct device_node;
> > +struct drm_panel;
> > +
> > +#ifdef CONFIG_DRM_PS8622
> > +
> > +extern int ps8622_init(struct drm_device *dev, struct drm_encoder *encoder,
> > +               struct i2c_client *client, struct device_node *node,
> > +               struct drm_panel *panel);
> > +
> > +#else
> > +
> > +static inline int ps8622_init(struct drm_device *dev,
> > +                               struct drm_encoder *encoder,
> > +                               struct i2c_client *client,
> > +                               struct device_node *node,
> > +                               struct drm_panel *panel)
> > +{
> > +       return -ENODEV;
> > +}
> > +
> > +#endif
> > +
> > +#endif
> > --
> > 1.7.9.5
> >

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

* Re: [PATCH V2 2/9] drm/panel: add pre_enable and post_disable routines
  2014-04-22  8:19   ` Thierry Reding
@ 2014-04-22 14:36     ` Ajay kumar
  2014-04-23  7:29       ` Daniel Vetter
  0 siblings, 1 reply; 58+ messages in thread
From: Ajay kumar @ 2014-04-22 14:36 UTC (permalink / raw)
  To: Thierry Reding
  Cc: linux-samsung-soc, Sean Paul, sunil joshi, dri-devel,
	Prashanth G, Ajay Kumar


[-- Attachment #1.1: Type: text/plain, Size: 2618 bytes --]

Hi Thierry,


On Tue, Apr 22, 2014 at 1:49 PM, Thierry Reding <thierry.reding@gmail.com>
wrote:
> On Tue, Apr 22, 2014 at 04:09:11AM +0530, Ajay Kumar wrote:
>> Most of the panels need an init sequence as mentioned below:
>>       -- poweron LCD unit/LCD_EN
>>       -- start video data
>>       -- poweron LED unit/BL_EN
>> And, a de-init sequence as mentioned below:
>>       -- poweroff LED unit/BL_EN
>>       -- stop video data
>>       -- poweroff LCD unit/LCD_EN
>> With existing callbacks for drm panel, we cannot accomodate such panels,
>> since only two callbacks, i.e "panel_enable" and panel_disable are
supported.
>>
>> This patch adds:
>>       -- "pre_enable" callback which can be called before
>>       the actual video data is on, and then call the "enable"
>>       callback after the video data is available.
>>
>>       -- "post_disable" callback which can be called after
>>       the video data is off, and use "disable" callback
>>       to do something before switching off the video data.
>>
>> Now, we can easily map the above scenario as shown below:
>>       poweron LCD unit/LCD_EN = "pre_enable" callback
>>       poweron LED unit/BL_EN = "enable" callback
>>       poweroff LED unit/BL_EN = "disable" callback
>>       poweroff LCD unit/LCD_EN = "post_disable" callback
>
> I don't like this. What happens when the next panel comes around that
> has a yet slightly different requirement? Will we introduce a new
> pre_pre_enable() and post_post_disable() function then?
>
As I have already explained, these 2 callbacks are sufficient enough to
take care
the power up/down sequence for LVDS and eDP panels. And, definitely having
just 2 callbacks "enable" and "disable" is not at all sufficient.

I initially thought of using panel_simple_enable from panel-simple driver.
But it doesn't go well with case wherein there are 2 regulators(one for LCD
and one for LED)
a BL_EN signal etc. And, often(Believe me, I have referred to both eDP
panel datasheets
and LVDS panel datasheets) proper powerup sequence for such panels is as
mentioned below:

powerup:
-- power up the supply (LCD_VCC).
-- start video data.
-- enable backlight.

powerdown
-- disable backlight.
-- stop video data.
-- power off the supply (LCD VCC)

For the above cases, if I have to somehow fit in all the required settings,
it breaks the sequence and I end up getting glitches during bootup/DPMS.

Also, when the drm_bridge can support pre_enable and post_disable, why not
drm_panel provide that both should work in tandem?

> There's got to be a better way to solve this.
>
> Thierry


Thanks and Regards,
Ajay Kumar

[-- Attachment #1.2: Type: text/html, Size: 3505 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH V2 3/9] drm/panel: Add driver for exynos_dp based panels
  2014-04-22  7:22   ` Jingoo Han
@ 2014-04-22 14:37     ` Ajay kumar
  0 siblings, 0 replies; 58+ messages in thread
From: Ajay kumar @ 2014-04-22 14:37 UTC (permalink / raw)
  To: Jingoo Han
  Cc: Ajay Kumar, dri-devel, linux-samsung-soc, Dae, Sean Paul,
	sunil joshi, Prashanth G

Hi Jingoo,


On Tue, Apr 22, 2014 at 12:52 PM, Jingoo Han <jg1.han@samsung.com> wrote:
>
> On Tuesday, April 22, 2014 7:39 AM, Ajay Kumar wrote:
> >
> > This patch adds a simple driver to handle all the LCD and LED
> > powerup/down routines needed to support eDP/eDP-LVDS panels
> > supported on exynos boards.
> >
> > The LCD and LED units are usually powered up via regulators,
> > and almost on all boards, we will have a BL_EN pin to enable/
> > disable the backlight. Sometimes, we can have LCD_EN switches
> > as well. The routines in this driver can be used to control
> > panel power sequence on such boards.
> >
> > Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
> > ---
> > Changes since V1:
> >       Added routine for post_disable, removed few unwanted headers.
> >       Refactored a lot of code.
> >
> >  .../devicetree/bindings/panel/exynos-dp-panel.txt  |   45 ++++
> >  drivers/gpu/drm/panel/Kconfig                      |    9 +
> >  drivers/gpu/drm/panel/Makefile                     |    1 +
> >  drivers/gpu/drm/panel/panel-exynos-dp.c            |  251 ++++++++++++++++++++
> >  4 files changed, 306 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/panel/exynos-dp-panel.txt
> >  create mode 100644 drivers/gpu/drm/panel/panel-exynos-dp.c
> >
> > diff --git a/Documentation/devicetree/bindings/panel/exynos-dp-panel.txt
> > b/Documentation/devicetree/bindings/panel/exynos-dp-panel.txt
> > new file mode 100644
> > index 0000000..3fbca54
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/panel/exynos-dp-panel.txt
> > @@ -0,0 +1,45 @@
> > +exynos_DP_panel/DP_to_LVDS_panel
>
> Please fix it as below.
>
> +Exynos DP panel/DP to LVDS panel
>
Ok.

> [....]
>
> > diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> > index 4ec874d..ea9d5ac 100644
> > --- a/drivers/gpu/drm/panel/Kconfig
> > +++ b/drivers/gpu/drm/panel/Kconfig
> > @@ -30,4 +30,13 @@ config DRM_PANEL_S6E8AA0
> >       select DRM_MIPI_DSI
> >       select VIDEOMODE_HELPERS
> >
> > +config DRM_PANEL_EXYNOS_DP
> > +     tristate "support for DP panels"
>
> It looks very general. Please fix it as below.
>
> +       tristate "support for Exynos DP panels"
>
Ok.

> Best regards,
> Jingoo Han
>
> > +     depends on OF && DRM_PANEL && DRM_EXYNOS_DP
> > +     help
> > +       DRM panel driver for DP panels and LVDS connected via DP bridges
> > +       that need at most a regulator for LCD unit, a regulator for LED unit
> > +       and/or enable GPIOs for LCD or LED units. Delay values can also be
> > +       specified to support powerup and powerdown process.
> > +
>
> [.....]
>
>

Thanks and Regards,
Ajay Kumar

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

* Re: [PATCH V2 3/9] drm/panel: Add driver for exynos_dp based panels
  2014-04-22  8:26   ` Thierry Reding
@ 2014-04-22 14:53     ` Ajay kumar
  2014-04-22 15:16       ` Thierry Reding
  0 siblings, 1 reply; 58+ messages in thread
From: Ajay kumar @ 2014-04-22 14:53 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Ajay Kumar, dri-devel, linux-samsung-soc, Sean Paul, sunil joshi,
	Prashanth G

Hi Thierry,


On Tue, Apr 22, 2014 at 1:56 PM, Thierry Reding
<thierry.reding@gmail.com> wrote:
> On Tue, Apr 22, 2014 at 04:09:12AM +0530, Ajay Kumar wrote:
>> This patch adds a simple driver to handle all the LCD and LED
>> powerup/down routines needed to support eDP/eDP-LVDS panels
>> supported on exynos boards.
>>
>> The LCD and LED units are usually powered up via regulators,
>> and almost on all boards, we will have a BL_EN pin to enable/
>> disable the backlight. Sometimes, we can have LCD_EN switches
>> as well. The routines in this driver can be used to control
>> panel power sequence on such boards.
>>
>> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
>> ---
>> Changes since V1:
>>       Added routine for post_disable, removed few unwanted headers.
>>       Refactored a lot of code.
>>
>>  .../devicetree/bindings/panel/exynos-dp-panel.txt  |   45 ++++
>>  drivers/gpu/drm/panel/Kconfig                      |    9 +
>>  drivers/gpu/drm/panel/Makefile                     |    1 +
>>  drivers/gpu/drm/panel/panel-exynos-dp.c            |  251 ++++++++++++++++++++
>>  4 files changed, 306 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/panel/exynos-dp-panel.txt
>>  create mode 100644 drivers/gpu/drm/panel/panel-exynos-dp.c
>
> What this patch does is in no way Exynos specific. It is also not eDP
> specific.
>
Right. This is not at all writing into any "exynos" specific register,
but can't this be just a placeholder for all the panel controls
which can serve boards which use exynos_dp?

> This conflates panel drivers with board drivers in a strange way. Panel
> drivers should be for *panels*, not for a given SoC or specific outputs
> on that SoC.
>
Right. But for that matter, even the "panel-simple" driver is doing the same,
which is just a placeholder for "generic" panel controls which serves
multiple boards.
I din't choose to reuse that because the boards which I have expect more
control over the panel sequence as mentioned before.
If you don't mind, I can fit in all these settings into panel-simple
driver itself ;)
But again, I should be able to register the panel driver much before
exynos_dp gets probed, That's the actual catch!


Thanks and regards,
Ajay Kumar

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

* Re: [PATCH V2 4/9] drm/exynos: add exynos_dp_panel driver registration to drm driver
  2014-04-22  8:33   ` Thierry Reding
@ 2014-04-22 15:03     ` Ajay kumar
  2014-04-22 15:26       ` Thierry Reding
  0 siblings, 1 reply; 58+ messages in thread
From: Ajay kumar @ 2014-04-22 15:03 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Ajay Kumar, dri-devel, linux-samsung-soc, Sean Paul, sunil joshi,
	Prashanth G

Hi Thierry,

On Tue, Apr 22, 2014 at 2:03 PM, Thierry Reding
<thierry.reding@gmail.com> wrote:
> On Tue, Apr 22, 2014 at 04:09:13AM +0530, Ajay Kumar wrote:
>> Register exynos_dp_panel before the list of exynos crtcs and
>> connectors are probed.
>>
>> This is needed because exynos_dp_panel should be registered to
>> the drm_panel list via panel-exynos-dp probe, i.e much before
>> exynos_dp_bind calls of_drm_find_panel().
>>
>> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
>> ---
>> Changes since V1:
>>       Added platform_driver_unregister(&exynos_dp_panel_driver) to
>>       exynos_drm_platform_remove as per Jingoo Han's correction
>>
>>  drivers/gpu/drm/exynos/exynos_drm_drv.c |   15 +++++++++++++++
>>  drivers/gpu/drm/exynos/exynos_drm_drv.h |    1 +
>>  2 files changed, 16 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>> index 1d653f8..2db7f67 100644
>> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>> @@ -530,12 +530,23 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
>>               goto err_unregister_ipp_drv;
>>  #endif
>>
>> +#ifdef CONFIG_DRM_PANEL_EXYNOS_DP
>> +     ret = platform_driver_register(&exynos_dp_panel_driver);
>> +     if (ret < 0)
>> +             goto err_unregister_dp_panel;
>> +#endif
>
> No, this is not how you're supposed to use DRM panel drivers. The idea
> is that you write a standalone driver for a given panel.
>
> What you do here has a number of problems. For one it's a driver that's
> tightly coupled to Exynos SoCs. But if I have a different SoC that uses
> the same panel I want to be able to use the same driver, and not have to
> rewrite the driver for my SoC.
>
> Another problem is that you're assuming here that the driver is built in
> and it will break if you try to build either Exynos DRM or the panel
> driver as a module. This is perhaps nothing you care about right now,
> but eventually people will want to ship a single kernel that can run on
> a number of SoCs. But if we keep adding things like this, that kernel
> will keep growing in size until it no longer fits in any kind of memory.
>
> Thierry

I completely agree with you in this!

Yes, this is not acceptable, but I want to know an "acceptable"
workaround for the situation below:
I register the driver using module_init().
And, exynos_drm gets probed much before the panel driver probe happens.
So, the panel driver hasn't probed yet, but exynos_dp via exynos_drm
tries to call
"of_drm_find_panel" which always returns NULL.

Thanks and regards,
Ajay Kumar

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

* Re: [PATCH V2 3/9] drm/panel: Add driver for exynos_dp based panels
  2014-04-22 14:53     ` Ajay kumar
@ 2014-04-22 15:16       ` Thierry Reding
  0 siblings, 0 replies; 58+ messages in thread
From: Thierry Reding @ 2014-04-22 15:16 UTC (permalink / raw)
  To: Ajay kumar
  Cc: linux-samsung-soc, Sean Paul, sunil joshi, dri-devel,
	Prashanth G, Ajay Kumar


[-- Attachment #1.1: Type: text/plain, Size: 2963 bytes --]

On Tue, Apr 22, 2014 at 08:23:03PM +0530, Ajay kumar wrote:
> Hi Thierry,
> 
> 
> On Tue, Apr 22, 2014 at 1:56 PM, Thierry Reding
> <thierry.reding@gmail.com> wrote:
> > On Tue, Apr 22, 2014 at 04:09:12AM +0530, Ajay Kumar wrote:
> >> This patch adds a simple driver to handle all the LCD and LED
> >> powerup/down routines needed to support eDP/eDP-LVDS panels
> >> supported on exynos boards.
> >>
> >> The LCD and LED units are usually powered up via regulators,
> >> and almost on all boards, we will have a BL_EN pin to enable/
> >> disable the backlight. Sometimes, we can have LCD_EN switches
> >> as well. The routines in this driver can be used to control
> >> panel power sequence on such boards.
> >>
> >> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
> >> ---
> >> Changes since V1:
> >>       Added routine for post_disable, removed few unwanted headers.
> >>       Refactored a lot of code.
> >>
> >>  .../devicetree/bindings/panel/exynos-dp-panel.txt  |   45 ++++
> >>  drivers/gpu/drm/panel/Kconfig                      |    9 +
> >>  drivers/gpu/drm/panel/Makefile                     |    1 +
> >>  drivers/gpu/drm/panel/panel-exynos-dp.c            |  251 ++++++++++++++++++++
> >>  4 files changed, 306 insertions(+)
> >>  create mode 100644 Documentation/devicetree/bindings/panel/exynos-dp-panel.txt
> >>  create mode 100644 drivers/gpu/drm/panel/panel-exynos-dp.c
> >
> > What this patch does is in no way Exynos specific. It is also not eDP
> > specific.
> >
> Right. This is not at all writing into any "exynos" specific register,
> but can't this be just a placeholder for all the panel controls
> which can serve boards which use exynos_dp?

No, it shouldn't. Like I said, if I have a panel that happens to be used
on an Exynos board but I use it on a different board, then I don't want
to have to know that the panel might be supported by Exynos so that I
know which driver to use.

So ideally there should be one driver per panel. panel-simple was
introduced because of the five panels that I had access to at the time,
five panels could be supported using the same code.

> > This conflates panel drivers with board drivers in a strange way. Panel
> > drivers should be for *panels*, not for a given SoC or specific outputs
> > on that SoC.
> >
> Right. But for that matter, even the "panel-simple" driver is doing the same,
> which is just a placeholder for "generic" panel controls which serves
> multiple boards.

panel-simple is meant for panels that require only the parameters that
are specified for those. Anything that needs more is by definition no
longer "simple".

And the difference here is that panel-simple is a true wildcard, but it
isn't specific to one SoC. And the name doesn't imply that either. Also
each panel is still identified by the specific compatible value, which
makes it easier to find out which driver supports the panel.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH V2 4/9] drm/exynos: add exynos_dp_panel driver registration to drm driver
  2014-04-22 15:03     ` Ajay kumar
@ 2014-04-22 15:26       ` Thierry Reding
  2014-08-20  4:02         ` Stéphane Marchesin
  0 siblings, 1 reply; 58+ messages in thread
From: Thierry Reding @ 2014-04-22 15:26 UTC (permalink / raw)
  To: Ajay kumar
  Cc: linux-samsung-soc, Sean Paul, sunil joshi, dri-devel,
	Prashanth G, Ajay Kumar


[-- Attachment #1.1: Type: text/plain, Size: 3452 bytes --]

On Tue, Apr 22, 2014 at 08:33:23PM +0530, Ajay kumar wrote:
> Hi Thierry,
> 
> On Tue, Apr 22, 2014 at 2:03 PM, Thierry Reding
> <thierry.reding@gmail.com> wrote:
> > On Tue, Apr 22, 2014 at 04:09:13AM +0530, Ajay Kumar wrote:
> >> Register exynos_dp_panel before the list of exynos crtcs and
> >> connectors are probed.
> >>
> >> This is needed because exynos_dp_panel should be registered to
> >> the drm_panel list via panel-exynos-dp probe, i.e much before
> >> exynos_dp_bind calls of_drm_find_panel().
> >>
> >> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
> >> ---
> >> Changes since V1:
> >>       Added platform_driver_unregister(&exynos_dp_panel_driver) to
> >>       exynos_drm_platform_remove as per Jingoo Han's correction
> >>
> >>  drivers/gpu/drm/exynos/exynos_drm_drv.c |   15 +++++++++++++++
> >>  drivers/gpu/drm/exynos/exynos_drm_drv.h |    1 +
> >>  2 files changed, 16 insertions(+)
> >>
> >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >> index 1d653f8..2db7f67 100644
> >> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >> @@ -530,12 +530,23 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
> >>               goto err_unregister_ipp_drv;
> >>  #endif
> >>
> >> +#ifdef CONFIG_DRM_PANEL_EXYNOS_DP
> >> +     ret = platform_driver_register(&exynos_dp_panel_driver);
> >> +     if (ret < 0)
> >> +             goto err_unregister_dp_panel;
> >> +#endif
> >
> > No, this is not how you're supposed to use DRM panel drivers. The idea
> > is that you write a standalone driver for a given panel.
> >
> > What you do here has a number of problems. For one it's a driver that's
> > tightly coupled to Exynos SoCs. But if I have a different SoC that uses
> > the same panel I want to be able to use the same driver, and not have to
> > rewrite the driver for my SoC.
> >
> > Another problem is that you're assuming here that the driver is built in
> > and it will break if you try to build either Exynos DRM or the panel
> > driver as a module. This is perhaps nothing you care about right now,
> > but eventually people will want to ship a single kernel that can run on
> > a number of SoCs. But if we keep adding things like this, that kernel
> > will keep growing in size until it no longer fits in any kind of memory.
> >
> > Thierry
> 
> I completely agree with you in this!
> 
> Yes, this is not acceptable, but I want to know an "acceptable"
> workaround for the situation below:
> I register the driver using module_init().
> And, exynos_drm gets probed much before the panel driver probe happens.
> So, the panel driver hasn't probed yet, but exynos_dp via exynos_drm
> tries to call
> "of_drm_find_panel" which always returns NULL.

That's a situation that your driver needs to be able to deal with. The
driver registration order doesn't matter one bit. It may happen to work
most of the time, but as soon as one of the resources that your panel
driver needs isn't there when the panel is probed, then it won't be
registered and of_drm_find_panel() will still return NULL.

Usually the right thing to do in that case would be to return (and
propagate) -EPROBE_DEFER so that your driver's probe is deferred and
retried when other drivers have been probed. That way it should
eventually get a non-NULL panel.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH V2 9/9] drm/exynos: Add ps8622 lvds bridge discovery to DP driver
  2014-04-22  9:27   ` Thierry Reding
@ 2014-04-22 17:20     ` Ajay kumar
  0 siblings, 0 replies; 58+ messages in thread
From: Ajay kumar @ 2014-04-22 17:20 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Ajay Kumar, dri-devel, linux-samsung-soc, Sean Paul, sunil joshi,
	Prashanth G, Rahul Sharma

Hi Thierry,


On Tue, Apr 22, 2014 at 2:57 PM, Thierry Reding
<thierry.reding@gmail.com> wrote:
> On Tue, Apr 22, 2014 at 04:09:18AM +0530, Ajay Kumar wrote:
>> This patch adds ps8622 lvds bridge discovery code to the dp driver.
>>
>> Signed-off-by: Rahul Sharma <Rahul.Sharma@samsung.com>
>> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
>> ---
>> Changes since V1:
>>       Pushing V1 for this as V2 because this patch holds good in this series.
>>
>>  drivers/gpu/drm/exynos/exynos_dp_core.c |    9 +++++++++
>>  1 file changed, 9 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
>> index 4853f31..0006412 100644
>> --- a/drivers/gpu/drm/exynos/exynos_dp_core.c
>> +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c
>> @@ -30,6 +30,7 @@
>>  #include <drm/drm_crtc_helper.h>
>>  #include <drm/drm_panel.h>
>>  #include <drm/bridge/ptn3460.h>
>> +#include <drm/bridge/ps8622.h>
>>
>>  #include "exynos_drm_drv.h"
>>  #include "exynos_dp_core.h"
>> @@ -999,7 +1000,15 @@ static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
>>                               panel);
>>               if (!ret)
>>                       return 1;
>> +     } else if (find_bridge("parade,ps8625", &bridge)) {
>
> So this is where the inspiration for the of_find_compatible_node() in
> the earlier patch came from.
>
I shall use phandle for that as suggested by you for previous patches.

>> +             ret = ps8622_init(dev, encoder, bridge.client, bridge.node,
>> +                             panel);
>
> Long-term I don't think this is going to scale very well. In my opinion
> it would be much more useful to have the bridge driver initialize what
> it can and then have the DRM driver call a generic initialization
> function to bind it to the DRM device or encoder. Otherwise we have to
> keep knowledge about the type of bridge in each driver that uses it,
> whereas the goal (I think) would be to create a proper abstraction so
> that the DRM driver doesn't have to know that kind of detail.
Well, having a generic initialization function makes more sense when
we have multiple platforms supporting the 2 available bridge chips.
Then we can let the bridge chip initialize first, and then call a drm_bridge
search and bind function to search the bridge_list to find the bridge which has
already got registered.
But, this again poses a challenge that the bridge chip driver should
be probed before
the corresponding drm_encoder is trying to bind the bridge with
drm_device/encoder.

So, I think the current way of explicitly calling bridgeXXX_init is
fine, at least
till multiple platforms start keeping track of all possible bridges
they support,
inside their respective drivers.

Thanks and regards,
Ajay Kumar

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

* Re: [PATCH V2 2/9] drm/panel: add pre_enable and post_disable routines
  2014-04-22 14:36     ` Ajay kumar
@ 2014-04-23  7:29       ` Daniel Vetter
  2014-04-23  7:42         ` Thierry Reding
  2014-04-23 19:14         ` Ajay kumar
  0 siblings, 2 replies; 58+ messages in thread
From: Daniel Vetter @ 2014-04-23  7:29 UTC (permalink / raw)
  To: Ajay kumar
  Cc: Thierry Reding, linux-samsung-soc, Sean Paul, sunil joshi,
	dri-devel, Prashanth G, Ajay Kumar

On Tue, Apr 22, 2014 at 08:06:19PM +0530, Ajay kumar wrote:
> Hi Thierry,
> 
> 
> On Tue, Apr 22, 2014 at 1:49 PM, Thierry Reding <thierry.reding@gmail.com>
> wrote:
> > On Tue, Apr 22, 2014 at 04:09:11AM +0530, Ajay Kumar wrote:
> >> Most of the panels need an init sequence as mentioned below:
> >>       -- poweron LCD unit/LCD_EN
> >>       -- start video data
> >>       -- poweron LED unit/BL_EN
> >> And, a de-init sequence as mentioned below:
> >>       -- poweroff LED unit/BL_EN
> >>       -- stop video data
> >>       -- poweroff LCD unit/LCD_EN
> >> With existing callbacks for drm panel, we cannot accomodate such panels,
> >> since only two callbacks, i.e "panel_enable" and panel_disable are
> supported.
> >>
> >> This patch adds:
> >>       -- "pre_enable" callback which can be called before
> >>       the actual video data is on, and then call the "enable"
> >>       callback after the video data is available.
> >>
> >>       -- "post_disable" callback which can be called after
> >>       the video data is off, and use "disable" callback
> >>       to do something before switching off the video data.
> >>
> >> Now, we can easily map the above scenario as shown below:
> >>       poweron LCD unit/LCD_EN = "pre_enable" callback
> >>       poweron LED unit/BL_EN = "enable" callback
> >>       poweroff LED unit/BL_EN = "disable" callback
> >>       poweroff LCD unit/LCD_EN = "post_disable" callback
> >
> > I don't like this. What happens when the next panel comes around that
> > has a yet slightly different requirement? Will we introduce a new
> > pre_pre_enable() and post_post_disable() function then?
> >
> As I have already explained, these 2 callbacks are sufficient enough to
> take care
> the power up/down sequence for LVDS and eDP panels. And, definitely having
> just 2 callbacks "enable" and "disable" is not at all sufficient.
> 
> I initially thought of using panel_simple_enable from panel-simple driver.
> But it doesn't go well with case wherein there are 2 regulators(one for LCD
> and one for LED)
> a BL_EN signal etc. And, often(Believe me, I have referred to both eDP
> panel datasheets
> and LVDS panel datasheets) proper powerup sequence for such panels is as
> mentioned below:
> 
> powerup:
> -- power up the supply (LCD_VCC).
> -- start video data.
> -- enable backlight.
> 
> powerdown
> -- disable backlight.
> -- stop video data.
> -- power off the supply (LCD VCC)
> 
> For the above cases, if I have to somehow fit in all the required settings,
> it breaks the sequence and I end up getting glitches during bootup/DPMS.
> 
> Also, when the drm_bridge can support pre_enable and post_disable, why not
> drm_panel provide that both should work in tandem?

Imo this makes sense, especially if we go through with the idea talked
about yesterday of creating a drm_bridge to wrap-up a drm_panel with
sufficient isolation between all components.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH V2 2/9] drm/panel: add pre_enable and post_disable routines
  2014-04-23  7:29       ` Daniel Vetter
@ 2014-04-23  7:42         ` Thierry Reding
  2014-04-23 19:26           ` Ajay kumar
  2014-04-23 19:14         ` Ajay kumar
  1 sibling, 1 reply; 58+ messages in thread
From: Thierry Reding @ 2014-04-23  7:42 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: linux-samsung-soc, Sean Paul, sunil joshi, dri-devel, Ajay kumar,
	Prashanth G, Ajay Kumar


[-- Attachment #1.1: Type: text/plain, Size: 3850 bytes --]

On Wed, Apr 23, 2014 at 09:29:15AM +0200, Daniel Vetter wrote:
> On Tue, Apr 22, 2014 at 08:06:19PM +0530, Ajay kumar wrote:
> > Hi Thierry,
> > 
> > 
> > On Tue, Apr 22, 2014 at 1:49 PM, Thierry Reding <thierry.reding@gmail.com>
> > wrote:
> > > On Tue, Apr 22, 2014 at 04:09:11AM +0530, Ajay Kumar wrote:
> > >> Most of the panels need an init sequence as mentioned below:
> > >>       -- poweron LCD unit/LCD_EN
> > >>       -- start video data
> > >>       -- poweron LED unit/BL_EN
> > >> And, a de-init sequence as mentioned below:
> > >>       -- poweroff LED unit/BL_EN
> > >>       -- stop video data
> > >>       -- poweroff LCD unit/LCD_EN
> > >> With existing callbacks for drm panel, we cannot accomodate such panels,
> > >> since only two callbacks, i.e "panel_enable" and panel_disable are
> > supported.
> > >>
> > >> This patch adds:
> > >>       -- "pre_enable" callback which can be called before
> > >>       the actual video data is on, and then call the "enable"
> > >>       callback after the video data is available.
> > >>
> > >>       -- "post_disable" callback which can be called after
> > >>       the video data is off, and use "disable" callback
> > >>       to do something before switching off the video data.
> > >>
> > >> Now, we can easily map the above scenario as shown below:
> > >>       poweron LCD unit/LCD_EN = "pre_enable" callback
> > >>       poweron LED unit/BL_EN = "enable" callback
> > >>       poweroff LED unit/BL_EN = "disable" callback
> > >>       poweroff LCD unit/LCD_EN = "post_disable" callback
> > >
> > > I don't like this. What happens when the next panel comes around that
> > > has a yet slightly different requirement? Will we introduce a new
> > > pre_pre_enable() and post_post_disable() function then?
> > >
> > As I have already explained, these 2 callbacks are sufficient enough to
> > take care
> > the power up/down sequence for LVDS and eDP panels. And, definitely having
> > just 2 callbacks "enable" and "disable" is not at all sufficient.
> > 
> > I initially thought of using panel_simple_enable from panel-simple driver.
> > But it doesn't go well with case wherein there are 2 regulators(one for LCD
> > and one for LED)
> > a BL_EN signal etc. And, often(Believe me, I have referred to both eDP
> > panel datasheets
> > and LVDS panel datasheets) proper powerup sequence for such panels is as
> > mentioned below:
> > 
> > powerup:
> > -- power up the supply (LCD_VCC).
> > -- start video data.
> > -- enable backlight.
> > 
> > powerdown
> > -- disable backlight.
> > -- stop video data.
> > -- power off the supply (LCD VCC)
> > 
> > For the above cases, if I have to somehow fit in all the required settings,
> > it breaks the sequence and I end up getting glitches during bootup/DPMS.
> > 
> > Also, when the drm_bridge can support pre_enable and post_disable, why not
> > drm_panel provide that both should work in tandem?
> 
> Imo this makes sense, especially if we go through with the idea talked
> about yesterday of creating a drm_bridge to wrap-up a drm_panel with
> sufficient isolation between all components.

I'm not at all comfortable with these. The names are totally confusing.
Next somebody will need to do something after the panel has been enabled
and we'll have to introduce .post_enable() and .pre_disable() functions.
And worse, what if somebody needs something to be done between
.pre_enable() and .enable()? .post_pre_enable()? Where does it end?

According to the above description the only reason why we need this is
to avoid visible glitches when the panel is already on, but the video
stream hasn't started yet. If that's the only reason why we need this,
then perhaps adding a way for a panel to expose the associated backlight
would be better?

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls
  2014-04-22 11:34   ` Rob Clark
  2014-04-22 13:53     ` Daniel Vetter
@ 2014-04-23 18:56     ` Ajay kumar
  2014-04-23 19:02       ` Ajay kumar
  1 sibling, 1 reply; 58+ messages in thread
From: Ajay kumar @ 2014-04-23 18:56 UTC (permalink / raw)
  To: Rob Clark
  Cc: Ajay Kumar, dri-devel, moderated list:ARM/S5P EXYNOS AR...,
	Sean Paul, sunil joshi, Prashanth G

Rob,

On Tue, Apr 22, 2014 at 5:04 PM, Rob Clark <robdclark@gmail.com> wrote:
> So what about, rather than adding drm_panel support to each bridge
> individually, we introduce a drm_panel_bridge (with a form of
> chaining).. ie:
>
>   struct drm_panel_bridge {
>     struct drm_bridge base;
>     struct drm_panel *panel;
>     struct drm_bridge *bridge; /* optional */
>   };
>
>   static void drm_panel_bridge_enable(struct drm_bridge *bridge)
>   {
>     struct drm_panel_bridge *pb = to_panel_bridge(bridge);
>     if (pb->bridge)
>       pb->bridge->funcs->enable(pb->bridge);
>     pb->panel->funcs->enable(pb->panel);
>   }
>
We cannot call them like this from crtc helpers in the order you mentioned,
since each individual bridge chip expects the panel controls at
different places.
I mean,
sometimes panel controls needs to be done before bridge controls,
> ... and so on.
>
> If you don't need a real bridge, just create one of these w/
> pb->bridge==NULL, otherwise create it as a wrapper for your real
> bridge.
>
> BR,
> -R
>
> On Mon, Apr 21, 2014 at 6:39 PM, Ajay Kumar <ajaykumar.rs@samsung.com> wrote:
>> attach ptn3460 connector to drm_panel and support drm_panel routines,
>> if a valid drm_panel object is passed to ptn3460_init.
>>
>> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
>> ---
>> Changes since V1:
>>         Address few coding style comments from Jingoo Han
>>
>>  drivers/gpu/drm/bridge/Kconfig          |    1 +
>>  drivers/gpu/drm/bridge/ptn3460.c        |   20 +++++++++++++++++++-
>>  drivers/gpu/drm/exynos/exynos_dp_core.c |   16 ++++++++++++----
>>  include/drm/bridge/ptn3460.h            |    6 ++++--
>>  4 files changed, 36 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
>> index 884923f..3bc6845 100644
>> --- a/drivers/gpu/drm/bridge/Kconfig
>> +++ b/drivers/gpu/drm/bridge/Kconfig
>> @@ -2,4 +2,5 @@ config DRM_PTN3460
>>         tristate "PTN3460 DP/LVDS bridge"
>>         depends on DRM
>>         select DRM_KMS_HELPER
>> +       select DRM_PANEL
>>         ---help---
>> diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
>> index f1d2afc..3920202 100644
>> --- a/drivers/gpu/drm/bridge/ptn3460.c
>> +++ b/drivers/gpu/drm/bridge/ptn3460.c
>> @@ -19,6 +19,7 @@
>>  #include <linux/i2c.h>
>>  #include <linux/gpio.h>
>>  #include <linux/delay.h>
>> +#include <drm/drm_panel.h>
>>
>>  #include "drmP.h"
>>  #include "drm_edid.h"
>> @@ -38,6 +39,7 @@ struct ptn3460_bridge {
>>         struct i2c_client *client;
>>         struct drm_encoder *encoder;
>>         struct drm_bridge *bridge;
>> +       struct drm_panel *panel;
>>         struct edid *edid;
>>         int gpio_pd_n;
>>         int gpio_rst_n;
>> @@ -126,6 +128,8 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge)
>>                 gpio_set_value(ptn_bridge->gpio_rst_n, 1);
>>         }
>>
>> +       drm_panel_pre_enable(ptn_bridge->panel);
>> +
>>         /*
>>          * There's a bug in the PTN chip where it falsely asserts hotplug before
>>          * it is fully functional. We're forced to wait for the maximum start up
>> @@ -142,6 +146,10 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge)
>>
>>  static void ptn3460_enable(struct drm_bridge *bridge)
>>  {
>> +       struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>> +
>> +       if (ptn_bridge->enabled)
>> +               drm_panel_enable(ptn_bridge->panel);
>>  }
>>
>>  static void ptn3460_disable(struct drm_bridge *bridge)
>> @@ -153,6 +161,9 @@ static void ptn3460_disable(struct drm_bridge *bridge)
>>
>>         ptn_bridge->enabled = false;
>>
>> +       drm_panel_disable(ptn_bridge->panel);
>> +       drm_panel_post_disable(ptn_bridge->panel);
>> +
>>         if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>>                 gpio_set_value(ptn_bridge->gpio_rst_n, 1);
>>
>> @@ -198,6 +209,7 @@ int ptn3460_get_modes(struct drm_connector *connector)
>>
>>         power_off = !ptn_bridge->enabled;
>>         ptn3460_pre_enable(ptn_bridge->bridge);
>> +       ptn3460_enable(ptn_bridge->bridge);
>>
>>         edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
>>         if (!edid) {
>> @@ -265,7 +277,8 @@ struct drm_connector_funcs ptn3460_connector_funcs = {
>>  };
>>
>>  int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>> -               struct i2c_client *client, struct device_node *node)
>> +               struct i2c_client *client, struct device_node *node,
>> +               struct drm_panel *panel)
>>  {
>>         int ret;
>>         struct drm_bridge *bridge;
>> @@ -324,6 +337,11 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>>                 goto err;
>>         }
>>
>> +       if (panel) {
>> +               ptn_bridge->panel = panel;
>> +               drm_panel_attach(ptn_bridge->panel, &ptn_bridge->connector);
>> +       }
>> +
>>         bridge->driver_private = ptn_bridge;
>>         encoder->bridge = bridge;
>>         ptn_bridge->connector.polled = DRM_CONNECTOR_POLL_HPD;
>> diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
>> index dbc5ccc..4853f31 100644
>> --- a/drivers/gpu/drm/exynos/exynos_dp_core.c
>> +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c
>> @@ -989,13 +989,14 @@ static bool find_bridge(const char *compat, struct bridge_init *bridge)
>>
>>  /* returns the number of bridges attached */
>>  static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
>> -               struct drm_encoder *encoder)
>> +               struct drm_encoder *encoder, struct drm_panel *panel)
>>  {
>>         struct bridge_init bridge;
>>         int ret;
>>
>>         if (find_bridge("nxp,ptn3460", &bridge)) {
>> -               ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
>> +               ret = ptn3460_init(dev, encoder, bridge.client, bridge.node,
>> +                               panel);
>>                 if (!ret)
>>                         return 1;
>>         }
>> @@ -1012,9 +1013,16 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display,
>>         dp->encoder = encoder;
>>
>>         /* Pre-empt DP connector creation if there's a bridge */
>> -       ret = exynos_drm_attach_lcd_bridge(dp->drm_dev, encoder);
>> -       if (ret)
>> +       ret = exynos_drm_attach_lcd_bridge(dp->drm_dev, encoder, dp->drm_panel);
>> +       if (ret) {
>> +               /*
>> +                * Also set "dp->drm_panel = NULL" so that we don't end up
>> +                * controlling panel power both in exynos_dp and bridge
>> +                * DPMS routines.
>> +                */
>> +               dp->drm_panel = NULL;
>>                 return 0;
>> +       }
>>
>>         connector->polled = DRM_CONNECTOR_POLL_HPD;
>>
>> diff --git a/include/drm/bridge/ptn3460.h b/include/drm/bridge/ptn3460.h
>> index ff62344..570cebb 100644
>> --- a/include/drm/bridge/ptn3460.h
>> +++ b/include/drm/bridge/ptn3460.h
>> @@ -18,16 +18,18 @@ struct drm_device;
>>  struct drm_encoder;
>>  struct i2c_client;
>>  struct device_node;
>> +struct drm_panel;
>>
>>  #if defined(CONFIG_DRM_PTN3460) || defined(CONFIG_DRM_PTN3460_MODULE)
>>
>>  int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>> -               struct i2c_client *client, struct device_node *node);
>> +               struct i2c_client *client, struct device_node *node,
>> +               struct drm_panel *panel);
>>  #else
>>
>>  static inline int ptn3460_init(struct drm_device *dev,
>>                 struct drm_encoder *encoder, struct i2c_client *client,
>> -               struct device_node *node)
>> +               struct device_node *node, struct drm_panel *panel)
>>  {
>>         return 0;
>>  }
>> --
>> 1.7.9.5
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls
  2014-04-23 18:56     ` Ajay kumar
@ 2014-04-23 19:02       ` Ajay kumar
  2014-04-24 16:11         ` Rob Clark
  0 siblings, 1 reply; 58+ messages in thread
From: Ajay kumar @ 2014-04-23 19:02 UTC (permalink / raw)
  To: Rob Clark
  Cc: Ajay Kumar, dri-devel, moderated list:ARM/S5P EXYNOS AR...,
	Sean Paul, sunil joshi, Prashanth G

Sorry for the previous reply,

Here goes the full explaination:

> Rob,
>
> On Tue, Apr 22, 2014 at 5:04 PM, Rob Clark <robdclark@gmail.com> wrote:
>> So what about, rather than adding drm_panel support to each bridge
>> individually, we introduce a drm_panel_bridge (with a form of
>> chaining).. ie:
>>
>>   struct drm_panel_bridge {
>>     struct drm_bridge base;
>>     struct drm_panel *panel;
>>     struct drm_bridge *bridge; /* optional */
>>   };
>>
>>   static void drm_panel_bridge_enable(struct drm_bridge *bridge)
>>   {
>>     struct drm_panel_bridge *pb = to_panel_bridge(bridge);
>>     if (pb->bridge)
>>       pb->bridge->funcs->enable(pb->bridge);
>>     pb->panel->funcs->enable(pb->panel);
>>   }
>>
We cannot call them like this from crtc helpers in the order you mentioned,
since each individual bridge chip expects the panel controls at
different places.
I mean,
-- sometimes panel controls needs to be done before bridge
controls(ptn3460: before RST_N and PD_N)
-- sometimes in between the bridge controls (ps8622: one panel control
before SLP_N and one after SLP_N)
-- sometimes panel controls needs to be done after bridge controls.

So, putting these drm_panel controls inside individual bridge drivers will work,
but keeping them in crtc_helpers might break stuff.

Thanks and regards,
Ajay Kumar

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

* Re: [PATCH V2 2/9] drm/panel: add pre_enable and post_disable routines
  2014-04-23  7:29       ` Daniel Vetter
  2014-04-23  7:42         ` Thierry Reding
@ 2014-04-23 19:14         ` Ajay kumar
  1 sibling, 0 replies; 58+ messages in thread
From: Ajay kumar @ 2014-04-23 19:14 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: Thierry Reding, linux-samsung-soc, Sean Paul, sunil joshi,
	dri-devel, Prashanth G, Ajay Kumar

Daniel,

On Wed, Apr 23, 2014 at 12:59 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Tue, Apr 22, 2014 at 08:06:19PM +0530, Ajay kumar wrote:
>> Hi Thierry,
>>
>>
>> On Tue, Apr 22, 2014 at 1:49 PM, Thierry Reding <thierry.reding@gmail.com>
>> wrote:
>> > On Tue, Apr 22, 2014 at 04:09:11AM +0530, Ajay Kumar wrote:
>> >> Most of the panels need an init sequence as mentioned below:
>> >>       -- poweron LCD unit/LCD_EN
>> >>       -- start video data
>> >>       -- poweron LED unit/BL_EN
>> >> And, a de-init sequence as mentioned below:
>> >>       -- poweroff LED unit/BL_EN
>> >>       -- stop video data
>> >>       -- poweroff LCD unit/LCD_EN
>> >> With existing callbacks for drm panel, we cannot accomodate such panels,
>> >> since only two callbacks, i.e "panel_enable" and panel_disable are
>> supported.
>> >>
>> >> This patch adds:
>> >>       -- "pre_enable" callback which can be called before
>> >>       the actual video data is on, and then call the "enable"
>> >>       callback after the video data is available.
>> >>
>> >>       -- "post_disable" callback which can be called after
>> >>       the video data is off, and use "disable" callback
>> >>       to do something before switching off the video data.
>> >>
>> >> Now, we can easily map the above scenario as shown below:
>> >>       poweron LCD unit/LCD_EN = "pre_enable" callback
>> >>       poweron LED unit/BL_EN = "enable" callback
>> >>       poweroff LED unit/BL_EN = "disable" callback
>> >>       poweroff LCD unit/LCD_EN = "post_disable" callback
>> >
>> > I don't like this. What happens when the next panel comes around that
>> > has a yet slightly different requirement? Will we introduce a new
>> > pre_pre_enable() and post_post_disable() function then?
>> >
>> As I have already explained, these 2 callbacks are sufficient enough to
>> take care
>> the power up/down sequence for LVDS and eDP panels. And, definitely having
>> just 2 callbacks "enable" and "disable" is not at all sufficient.
>>
>> I initially thought of using panel_simple_enable from panel-simple driver.
>> But it doesn't go well with case wherein there are 2 regulators(one for LCD
>> and one for LED)
>> a BL_EN signal etc. And, often(Believe me, I have referred to both eDP
>> panel datasheets
>> and LVDS panel datasheets) proper powerup sequence for such panels is as
>> mentioned below:
>>
>> powerup:
>> -- power up the supply (LCD_VCC).
>> -- start video data.
>> -- enable backlight.
>>
>> powerdown
>> -- disable backlight.
>> -- stop video data.
>> -- power off the supply (LCD VCC)
>>
>> For the above cases, if I have to somehow fit in all the required settings,
>> it breaks the sequence and I end up getting glitches during bootup/DPMS.
>>
>> Also, when the drm_bridge can support pre_enable and post_disable, why not
>> drm_panel provide that both should work in tandem?
>
> Imo this makes sense, especially if we go through with the idea talked
> about yesterday of creating a drm_bridge to wrap-up a drm_panel with
> sufficient isolation between all components.
> -Daniel

Actually, I tried implementing this for ptn3460. But, it breaks the working.
As explained in the other patch(reply for Rob), we cannot truly ISOLATE
the panel controls from bridge controls and keep them as seperate.
They should be kept together, in the individual bridge chip drivers,
so that the bridge chip driver can decide which panel control to call
at what point.

So, I think combining bridge chip and panel controls was really a bad idea.
I will implement the basic panel controls required by the bridge
as optional bridge properties via DT. By that way, at least the driver
remains robust.


Thanks and regards,
Ajay Kumar

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

* Re: [PATCH V2 2/9] drm/panel: add pre_enable and post_disable routines
  2014-04-23  7:42         ` Thierry Reding
@ 2014-04-23 19:26           ` Ajay kumar
  2014-04-24 18:31             ` Thierry Reding
  0 siblings, 1 reply; 58+ messages in thread
From: Ajay kumar @ 2014-04-23 19:26 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Daniel Vetter, linux-samsung-soc, Sean Paul, sunil joshi,
	dri-devel, Prashanth G, Ajay Kumar

Thierry,

On Wed, Apr 23, 2014 at 1:12 PM, Thierry Reding
<thierry.reding@gmail.com> wrote:
> On Wed, Apr 23, 2014 at 09:29:15AM +0200, Daniel Vetter wrote:
>> On Tue, Apr 22, 2014 at 08:06:19PM +0530, Ajay kumar wrote:
>> > Hi Thierry,
>> >
>> >
>> > On Tue, Apr 22, 2014 at 1:49 PM, Thierry Reding <thierry.reding@gmail.com>
>> > wrote:
>> > > On Tue, Apr 22, 2014 at 04:09:11AM +0530, Ajay Kumar wrote:
>> > >> Most of the panels need an init sequence as mentioned below:
>> > >>       -- poweron LCD unit/LCD_EN
>> > >>       -- start video data
>> > >>       -- poweron LED unit/BL_EN
>> > >> And, a de-init sequence as mentioned below:
>> > >>       -- poweroff LED unit/BL_EN
>> > >>       -- stop video data
>> > >>       -- poweroff LCD unit/LCD_EN
>> > >> With existing callbacks for drm panel, we cannot accomodate such panels,
>> > >> since only two callbacks, i.e "panel_enable" and panel_disable are
>> > supported.
>> > >>
>> > >> This patch adds:
>> > >>       -- "pre_enable" callback which can be called before
>> > >>       the actual video data is on, and then call the "enable"
>> > >>       callback after the video data is available.
>> > >>
>> > >>       -- "post_disable" callback which can be called after
>> > >>       the video data is off, and use "disable" callback
>> > >>       to do something before switching off the video data.
>> > >>
>> > >> Now, we can easily map the above scenario as shown below:
>> > >>       poweron LCD unit/LCD_EN = "pre_enable" callback
>> > >>       poweron LED unit/BL_EN = "enable" callback
>> > >>       poweroff LED unit/BL_EN = "disable" callback
>> > >>       poweroff LCD unit/LCD_EN = "post_disable" callback
>> > >
>> > > I don't like this. What happens when the next panel comes around that
>> > > has a yet slightly different requirement? Will we introduce a new
>> > > pre_pre_enable() and post_post_disable() function then?
>> > >
>> > As I have already explained, these 2 callbacks are sufficient enough to
>> > take care
>> > the power up/down sequence for LVDS and eDP panels. And, definitely having
>> > just 2 callbacks "enable" and "disable" is not at all sufficient.
>> >
>> > I initially thought of using panel_simple_enable from panel-simple driver.
>> > But it doesn't go well with case wherein there are 2 regulators(one for LCD
>> > and one for LED)
>> > a BL_EN signal etc. And, often(Believe me, I have referred to both eDP
>> > panel datasheets
>> > and LVDS panel datasheets) proper powerup sequence for such panels is as
>> > mentioned below:
>> >
>> > powerup:
>> > -- power up the supply (LCD_VCC).
>> > -- start video data.
>> > -- enable backlight.
>> >
>> > powerdown
>> > -- disable backlight.
>> > -- stop video data.
>> > -- power off the supply (LCD VCC)
>> >
>> > For the above cases, if I have to somehow fit in all the required settings,
>> > it breaks the sequence and I end up getting glitches during bootup/DPMS.
>> >
>> > Also, when the drm_bridge can support pre_enable and post_disable, why not
>> > drm_panel provide that both should work in tandem?
>>
>> Imo this makes sense, especially if we go through with the idea talked
>> about yesterday of creating a drm_bridge to wrap-up a drm_panel with
>> sufficient isolation between all components.
>
> I'm not at all comfortable with these. The names are totally confusing.
> Next somebody will need to do something after the panel has been enabled
> and we'll have to introduce .post_enable() and .pre_disable() functions.
> And worse, what if somebody needs something to be done between
> .pre_enable() and .enable()? .post_pre_enable()? Where does it end?
>
> According to the above description the only reason why we need this is
> to avoid visible glitches when the panel is already on, but the video
> stream hasn't started yet. If that's the only reason why we need this,
> then perhaps adding a way for a panel to expose the associated backlight
> would be better?
Actually, we need not expose the entire backlight device.
AFAIK, the glitch is caused when you enable BL_EN before
there is valid video data. So, one way to mask off the glitch is to
follow this sequence:
-- power up the panel.
-- start video data, (start PWM here or)
-- (start PWM here), enable backlight

The problem is that the above scenario cannot be mapped to panel-simple driver.
IMO, panel_simple should provide enable/disable controls both for LCD
and backlight.
something like panel_simple_lcd_enable/panel_simple_led_enable, and
panel_simple_lcd_disable/panel_simple_led_disable.

Then we can call panel_simple_lcd_enable before video stream is present,
And call panel_simple_led_enable after the video stream is present.
In that way we can accomodate majority of LVDS and eDP panels inside
panel_simple driver without having to worry about the glitch :)

Thanks and regards,
Ajay Kumar

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

* Re: [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls
  2014-04-23 19:02       ` Ajay kumar
@ 2014-04-24 16:11         ` Rob Clark
  2014-04-24 16:55           ` Ajay kumar
  0 siblings, 1 reply; 58+ messages in thread
From: Rob Clark @ 2014-04-24 16:11 UTC (permalink / raw)
  To: Ajay kumar
  Cc: moderated list:ARM/S5P EXYNOS AR...,
	Sean Paul, sunil joshi, dri-devel, Prashanth G, Ajay Kumar

On Wed, Apr 23, 2014 at 3:02 PM, Ajay kumar <ajaynumb@gmail.com> wrote:
> Sorry for the previous reply,
>
> Here goes the full explaination:
>
>> Rob,
>>
>> On Tue, Apr 22, 2014 at 5:04 PM, Rob Clark <robdclark@gmail.com> wrote:
>>> So what about, rather than adding drm_panel support to each bridge
>>> individually, we introduce a drm_panel_bridge (with a form of
>>> chaining).. ie:
>>>
>>>   struct drm_panel_bridge {
>>>     struct drm_bridge base;
>>>     struct drm_panel *panel;
>>>     struct drm_bridge *bridge; /* optional */
>>>   };
>>>
>>>   static void drm_panel_bridge_enable(struct drm_bridge *bridge)
>>>   {
>>>     struct drm_panel_bridge *pb = to_panel_bridge(bridge);
>>>     if (pb->bridge)
>>>       pb->bridge->funcs->enable(pb->bridge);
>>>     pb->panel->funcs->enable(pb->panel);
>>>   }
>>>
> We cannot call them like this from crtc helpers in the order you mentioned,
> since each individual bridge chip expects the panel controls at
> different places.
> I mean,
> -- sometimes panel controls needs to be done before bridge
> controls(ptn3460: before RST_N and PD_N)

well, this is why bridge has pre-enable/enable/disable/post-disable
hooks, so you can choose before or after..

> -- sometimes in between the bridge controls (ps8622: one panel control
> before SLP_N and one after SLP_N)
> -- sometimes panel controls needs to be done after bridge controls.

I am not convinced that a generic panel/bridge adapter is not
possible.  Maybe we need more fine grained fxn ptr callbacks, although
seems like pre+post should give you enough.  It would certainly be
easier than having to add panel support in each individual bridge
driver (which seems like it will certainly result that only certain
combinations of panel+bridge actually work as intended)

> So, putting these drm_panel controls inside individual bridge drivers will work,
> but keeping them in crtc_helpers might break stuff.

I'm not talking about crtc changing helpers.

BR,
-R

> Thanks and regards,
> Ajay Kumar

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

* Re: [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls
  2014-04-24 16:11         ` Rob Clark
@ 2014-04-24 16:55           ` Ajay kumar
  2014-04-24 17:25             ` Rob Clark
  0 siblings, 1 reply; 58+ messages in thread
From: Ajay kumar @ 2014-04-24 16:55 UTC (permalink / raw)
  To: Rob Clark
  Cc: Ajay Kumar, dri-devel, moderated list:ARM/S5P EXYNOS AR...,
	Sean Paul, sunil joshi, Prashanth G

Rob,

On Thu, Apr 24, 2014 at 9:41 PM, Rob Clark <robdclark@gmail.com> wrote:
> On Wed, Apr 23, 2014 at 3:02 PM, Ajay kumar <ajaynumb@gmail.com> wrote:
>> Sorry for the previous reply,
>>
>> Here goes the full explaination:
>>
>>> Rob,
>>>
>>> On Tue, Apr 22, 2014 at 5:04 PM, Rob Clark <robdclark@gmail.com> wrote:
>>>> So what about, rather than adding drm_panel support to each bridge
>>>> individually, we introduce a drm_panel_bridge (with a form of
>>>> chaining).. ie:
>>>>
>>>>   struct drm_panel_bridge {
>>>>     struct drm_bridge base;
>>>>     struct drm_panel *panel;
>>>>     struct drm_bridge *bridge; /* optional */
>>>>   };
>>>>
>>>>   static void drm_panel_bridge_enable(struct drm_bridge *bridge)
>>>>   {
>>>>     struct drm_panel_bridge *pb = to_panel_bridge(bridge);
>>>>     if (pb->bridge)
>>>>       pb->bridge->funcs->enable(pb->bridge);
>>>>     pb->panel->funcs->enable(pb->panel);
>>>>   }
>>>>
>> We cannot call them like this from crtc helpers in the order you mentioned,
>> since each individual bridge chip expects the panel controls at
>> different places.
>> I mean,
>> -- sometimes panel controls needs to be done before bridge
>> controls(ptn3460: before RST_N and PD_N)
>
> well, this is why bridge has pre-enable/enable/disable/post-disable
> hooks, so you can choose before or after..
These calls are for a bridge to sync with the encoder calls.
We might end up defining pre-enable/enable/disable/post-disable for a
panel to sync
with the bridge calls! I have explained below.

>> -- sometimes in between the bridge controls (ps8622: one panel control
>> before SLP_N and one after SLP_N)
>> -- sometimes panel controls needs to be done after bridge controls.
>
> I am not convinced that a generic panel/bridge adapter is not
> possible.  Maybe we need more fine grained fxn ptr callbacks, although
> seems like pre+post should give you enough.  It would certainly be
> easier than having to add panel support in each individual bridge
> driver (which seems like it will certainly result that only certain
> combinations of panel+bridge actually work as intended)
Ok. Consider this case:
Currently, we have the sequence as "bridge->pre_enable,
encoder_enable, bridge->enable"
And, the bridge should obviously be enabled in bridge->pre_enable.
Now, where do you choose to call panel->pre_enable?
-- as the first step of bridge->pre_enable, before we pull up/down bridge GPIOs.
-- at the last step of bridge->pre_enable, after we pull up/down the
bridge GPIOs.

Ideally, PTN3460 expects it to be the second case, and PS8625 expects
it to be the first case.
So, we will end up adding pre_pre_enable, post_pre_enable to
accomodate such scenarios.

So, we leave the choice for the individual bridge chip drivers to
implement panel
power up/down controls in the place where they wish to.


Thanks and regards,
 Ajay Kumar

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

* Re: [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls
  2014-04-24 16:55           ` Ajay kumar
@ 2014-04-24 17:25             ` Rob Clark
  2014-04-24 17:38               ` Ajay kumar
  0 siblings, 1 reply; 58+ messages in thread
From: Rob Clark @ 2014-04-24 17:25 UTC (permalink / raw)
  To: Ajay kumar
  Cc: moderated list:ARM/S5P EXYNOS AR...,
	Sean Paul, sunil joshi, dri-devel, Prashanth G, Ajay Kumar

On Thu, Apr 24, 2014 at 12:55 PM, Ajay kumar <ajaynumb@gmail.com> wrote:
> Rob,
>
> On Thu, Apr 24, 2014 at 9:41 PM, Rob Clark <robdclark@gmail.com> wrote:
>> On Wed, Apr 23, 2014 at 3:02 PM, Ajay kumar <ajaynumb@gmail.com> wrote:
>>> Sorry for the previous reply,
>>>
>>> Here goes the full explaination:
>>>
>>>> Rob,
>>>>
>>>> On Tue, Apr 22, 2014 at 5:04 PM, Rob Clark <robdclark@gmail.com> wrote:
>>>>> So what about, rather than adding drm_panel support to each bridge
>>>>> individually, we introduce a drm_panel_bridge (with a form of
>>>>> chaining).. ie:
>>>>>
>>>>>   struct drm_panel_bridge {
>>>>>     struct drm_bridge base;
>>>>>     struct drm_panel *panel;
>>>>>     struct drm_bridge *bridge; /* optional */
>>>>>   };
>>>>>
>>>>>   static void drm_panel_bridge_enable(struct drm_bridge *bridge)
>>>>>   {
>>>>>     struct drm_panel_bridge *pb = to_panel_bridge(bridge);
>>>>>     if (pb->bridge)
>>>>>       pb->bridge->funcs->enable(pb->bridge);
>>>>>     pb->panel->funcs->enable(pb->panel);
>>>>>   }
>>>>>
>>> We cannot call them like this from crtc helpers in the order you mentioned,
>>> since each individual bridge chip expects the panel controls at
>>> different places.
>>> I mean,
>>> -- sometimes panel controls needs to be done before bridge
>>> controls(ptn3460: before RST_N and PD_N)
>>
>> well, this is why bridge has pre-enable/enable/disable/post-disable
>> hooks, so you can choose before or after..
> These calls are for a bridge to sync with the encoder calls.
> We might end up defining pre-enable/enable/disable/post-disable for a
> panel to sync
> with the bridge calls! I have explained below.
>
>>> -- sometimes in between the bridge controls (ps8622: one panel control
>>> before SLP_N and one after SLP_N)
>>> -- sometimes panel controls needs to be done after bridge controls.
>>
>> I am not convinced that a generic panel/bridge adapter is not
>> possible.  Maybe we need more fine grained fxn ptr callbacks, although
>> seems like pre+post should give you enough.  It would certainly be
>> easier than having to add panel support in each individual bridge
>> driver (which seems like it will certainly result that only certain
>> combinations of panel+bridge actually work as intended)
> Ok. Consider this case:
> Currently, we have the sequence as "bridge->pre_enable,
> encoder_enable, bridge->enable"
> And, the bridge should obviously be enabled in bridge->pre_enable.
> Now, where do you choose to call panel->pre_enable?
> -- as the first step of bridge->pre_enable, before we pull up/down bridge GPIOs.
> -- at the last step of bridge->pre_enable, after we pull up/down the
> bridge GPIOs.
>
> Ideally, PTN3460 expects it to be the second case, and PS8625 expects
> it to be the first case.
> So, we will end up adding pre_pre_enable, post_pre_enable to
> accomodate such scenarios.

ok, well I think we can deal with this with a slight modification of
my original proposal.  Split drm_panel_bridge into
drm_composite_bridge and drm_panel_bridge:

  struct drm_composite_bridge {
    struct drm_bridge base;
    struct drm_bridge *b1, *b2;
  }

  static void drm_composite_bridge_enable(struct drm_bridge *bridge)
  {
    struct drm_composite_bridge *cbridge = to_composite_bridge(bridge);
    cbridge->b1->funcs->enable(cbridge->b1);
    cbridge->b2->funcs->enable(cbridge->b2);
  }

  .. and so on ..

  struct drm_panel_bridge {
    struct drm_bridge base;
    struct drm_panel *panel;
  }

  static void drm_panel_bridge_enable(struct drm_bridge *bridge)
  {
    struct drm_panel_bridge *pbridge = to_panel_bridge(bridge);
    pbridge->panel->funcs->enable(pbridge->panel);
  }

  .. and so on..


then in initialization, order can be managed like:

  if (panel_first)
    bridge = drm_composite_bridge_init(panel_bridge, actual_bridge)
  else
    bridge = drm_composite_bridge_init(actual_bridge, panel_bridge);

  possibly parametrize drm_panel_bridge if we need to map
panel->enable() to bridge->pre_enable() vs bridge->enable(), etc..

BR,
-R

> So, we leave the choice for the individual bridge chip drivers to
> implement panel
> power up/down controls in the place where they wish to.
>
>
> Thanks and regards,
>  Ajay Kumar

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

* Re: [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls
  2014-04-24 17:25             ` Rob Clark
@ 2014-04-24 17:38               ` Ajay kumar
  2014-04-25  6:10                 ` Ajay kumar
  0 siblings, 1 reply; 58+ messages in thread
From: Ajay kumar @ 2014-04-24 17:38 UTC (permalink / raw)
  To: Rob Clark
  Cc: Ajay Kumar, dri-devel, moderated list:ARM/S5P EXYNOS AR...,
	Sean Paul, sunil joshi, Prashanth G

On Thu, Apr 24, 2014 at 10:55 PM, Rob Clark <robdclark@gmail.com> wrote:
> On Thu, Apr 24, 2014 at 12:55 PM, Ajay kumar <ajaynumb@gmail.com> wrote:
>> Rob,
>>
>> On Thu, Apr 24, 2014 at 9:41 PM, Rob Clark <robdclark@gmail.com> wrote:
>>> On Wed, Apr 23, 2014 at 3:02 PM, Ajay kumar <ajaynumb@gmail.com> wrote:
>>>> Sorry for the previous reply,
>>>>
>>>> Here goes the full explaination:
>>>>
>>>>> Rob,
>>>>>
>>>>> On Tue, Apr 22, 2014 at 5:04 PM, Rob Clark <robdclark@gmail.com> wrote:
>>>>>> So what about, rather than adding drm_panel support to each bridge
>>>>>> individually, we introduce a drm_panel_bridge (with a form of
>>>>>> chaining).. ie:
>>>>>>
>>>>>>   struct drm_panel_bridge {
>>>>>>     struct drm_bridge base;
>>>>>>     struct drm_panel *panel;
>>>>>>     struct drm_bridge *bridge; /* optional */
>>>>>>   };
>>>>>>
>>>>>>   static void drm_panel_bridge_enable(struct drm_bridge *bridge)
>>>>>>   {
>>>>>>     struct drm_panel_bridge *pb = to_panel_bridge(bridge);
>>>>>>     if (pb->bridge)
>>>>>>       pb->bridge->funcs->enable(pb->bridge);
>>>>>>     pb->panel->funcs->enable(pb->panel);
>>>>>>   }
>>>>>>
>>>> We cannot call them like this from crtc helpers in the order you mentioned,
>>>> since each individual bridge chip expects the panel controls at
>>>> different places.
>>>> I mean,
>>>> -- sometimes panel controls needs to be done before bridge
>>>> controls(ptn3460: before RST_N and PD_N)
>>>
>>> well, this is why bridge has pre-enable/enable/disable/post-disable
>>> hooks, so you can choose before or after..
>> These calls are for a bridge to sync with the encoder calls.
>> We might end up defining pre-enable/enable/disable/post-disable for a
>> panel to sync
>> with the bridge calls! I have explained below.
>>
>>>> -- sometimes in between the bridge controls (ps8622: one panel control
>>>> before SLP_N and one after SLP_N)
>>>> -- sometimes panel controls needs to be done after bridge controls.
>>>
>>> I am not convinced that a generic panel/bridge adapter is not
>>> possible.  Maybe we need more fine grained fxn ptr callbacks, although
>>> seems like pre+post should give you enough.  It would certainly be
>>> easier than having to add panel support in each individual bridge
>>> driver (which seems like it will certainly result that only certain
>>> combinations of panel+bridge actually work as intended)
>> Ok. Consider this case:
>> Currently, we have the sequence as "bridge->pre_enable,
>> encoder_enable, bridge->enable"
>> And, the bridge should obviously be enabled in bridge->pre_enable.
>> Now, where do you choose to call panel->pre_enable?
>> -- as the first step of bridge->pre_enable, before we pull up/down bridge GPIOs.
>> -- at the last step of bridge->pre_enable, after we pull up/down the
>> bridge GPIOs.
>>
>> Ideally, PTN3460 expects it to be the second case, and PS8625 expects
>> it to be the first case.
>> So, we will end up adding pre_pre_enable, post_pre_enable to
>> accomodate such scenarios.
>
> ok, well I think we can deal with this with a slight modification of
> my original proposal.  Split drm_panel_bridge into
> drm_composite_bridge and drm_panel_bridge:
>
>   struct drm_composite_bridge {
>     struct drm_bridge base;
>     struct drm_bridge *b1, *b2;
>   }
>
>   static void drm_composite_bridge_enable(struct drm_bridge *bridge)
>   {
>     struct drm_composite_bridge *cbridge = to_composite_bridge(bridge);
>     cbridge->b1->funcs->enable(cbridge->b1);
>     cbridge->b2->funcs->enable(cbridge->b2);
>   }
>
>   .. and so on ..
>
>   struct drm_panel_bridge {
>     struct drm_bridge base;
>     struct drm_panel *panel;
>   }
>
>   static void drm_panel_bridge_enable(struct drm_bridge *bridge)
>   {
>     struct drm_panel_bridge *pbridge = to_panel_bridge(bridge);
>     pbridge->panel->funcs->enable(pbridge->panel);
>   }
>
>   .. and so on..
>
>
> then in initialization, order can be managed like:
>
>   if (panel_first)
>     bridge = drm_composite_bridge_init(panel_bridge, actual_bridge)
>   else
>     bridge = drm_composite_bridge_init(actual_bridge, panel_bridge);
>
>   possibly parametrize drm_panel_bridge if we need to map
> panel->enable() to bridge->pre_enable() vs bridge->enable(), etc..

Well, this really does seems complex to me.
Don't you think just having a drm_panel inside drm_bridge structure is
sufficient?!
And, make a call for pre_panel_enable and post_panel_enable around
bridge->pre_enable..and so on.?

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

* Re: [PATCH V2 2/9] drm/panel: add pre_enable and post_disable routines
  2014-04-23 19:26           ` Ajay kumar
@ 2014-04-24 18:31             ` Thierry Reding
  2014-04-25  6:04               ` Ajay kumar
  0 siblings, 1 reply; 58+ messages in thread
From: Thierry Reding @ 2014-04-24 18:31 UTC (permalink / raw)
  To: Ajay kumar
  Cc: linux-samsung-soc, Sean Paul, sunil joshi, dri-devel,
	Prashanth G, Ajay Kumar


[-- Attachment #1.1: Type: text/plain, Size: 2404 bytes --]

On Thu, Apr 24, 2014 at 12:56:02AM +0530, Ajay kumar wrote:
> Thierry,
> 
> On Wed, Apr 23, 2014 at 1:12 PM, Thierry Reding
> <thierry.reding@gmail.com> wrote:
> > On Wed, Apr 23, 2014 at 09:29:15AM +0200, Daniel Vetter wrote:
[...]
> >> Imo this makes sense, especially if we go through with the idea talked
> >> about yesterday of creating a drm_bridge to wrap-up a drm_panel with
> >> sufficient isolation between all components.
> >
> > I'm not at all comfortable with these. The names are totally confusing.
> > Next somebody will need to do something after the panel has been enabled
> > and we'll have to introduce .post_enable() and .pre_disable() functions.
> > And worse, what if somebody needs something to be done between
> > .pre_enable() and .enable()? .post_pre_enable()? Where does it end?
> >
> > According to the above description the only reason why we need this is
> > to avoid visible glitches when the panel is already on, but the video
> > stream hasn't started yet. If that's the only reason why we need this,
> > then perhaps adding a way for a panel to expose the associated backlight
> > would be better?
> Actually, we need not expose the entire backlight device.
> AFAIK, the glitch is caused when you enable BL_EN before
> there is valid video data. So, one way to mask off the glitch is to
> follow this sequence:
> -- power up the panel.
> -- start video data, (start PWM here or)
> -- (start PWM here), enable backlight

That's very difficult to get right, isn't it? Even if you have fine-
grained control over what to enable you still need a way to determine
_when_ it's safe to enable the backlight. Typically I guess that would
be the duration of one frame (or perhaps 2, depending on when the panel
syncs to the video signal).

Perhaps it could even by sync'ed to the VBLANK?

> The problem is that the above scenario cannot be mapped to panel-simple driver.
> IMO, panel_simple should provide enable/disable controls both for LCD
> and backlight.
> something like panel_simple_lcd_enable/panel_simple_led_enable, and
> panel_simple_lcd_disable/panel_simple_led_disable.

That's not what the simple panel driver can do. If we want this it needs
to be solved in a generic way for all panels since they all need to use
the drm_panel_*() functions to abstract away the details from drivers
that use the panels.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH V2 2/9] drm/panel: add pre_enable and post_disable routines
  2014-04-24 18:31             ` Thierry Reding
@ 2014-04-25  6:04               ` Ajay kumar
  2014-04-25 18:16                 ` Ajay kumar
  0 siblings, 1 reply; 58+ messages in thread
From: Ajay kumar @ 2014-04-25  6:04 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Daniel Vetter, linux-samsung-soc, Sean Paul, sunil joshi,
	dri-devel, Prashanth G, Ajay Kumar

On Friday, April 25, 2014, Thierry Reding <thierry.reding@gmail.com> wrote:
>
> On Thu, Apr 24, 2014 at 12:56:02AM +0530, Ajay kumar wrote:
> > Thierry,
> >
> > On Wed, Apr 23, 2014 at 1:12 PM, Thierry Reding
> > <thierry.reding@gmail.com> wrote:
> > > On Wed, Apr 23, 2014 at 09:29:15AM +0200, Daniel Vetter wrote:
> [...]
> > >> Imo this makes sense, especially if we go through with the idea talked
> > >> about yesterday of creating a drm_bridge to wrap-up a drm_panel with
> > >> sufficient isolation between all components.
> > >
> > > I'm not at all comfortable with these. The names are totally confusing.
> > > Next somebody will need to do something after the panel has been enabled
> > > and we'll have to introduce .post_enable() and .pre_disable() functions.
> > > And worse, what if somebody needs something to be done between
> > > .pre_enable() and .enable()? .post_pre_enable()? Where does it end?
> > >
> > > According to the above description the only reason why we need this is
> > > to avoid visible glitches when the panel is already on, but the video
> > > stream hasn't started yet. If that's the only reason why we need this,
> > > then perhaps adding a way for a panel to expose the associated backlight
> > > would be better?
> > Actually, we need not expose the entire backlight device.
> > AFAIK, the glitch is caused when you enable BL_EN before
> > there is valid video data. So, one way to mask off the glitch is to
> > follow this sequence:
> > -- power up the panel.
> > -- start video data, (start PWM here or)
> > -- (start PWM here), enable backlight
>
> That's very difficult to get right, isn't it? Even if you have fine-
> grained control over what to enable you still need a way to determine
> _when_ it's safe to enable the backlight. Typically I guess that would
> be the duration of one frame (or perhaps 2, depending on when the panel
> syncs to the video signal).
We need not "determine", its already present in LVDS datasheet.
The LVDS datasheet says at least 200ms delay is needed from "Valid
data" to "BL on".

> Perhaps it could even by sync'ed to the VBLANK?
No. vblanks are related to crtc. And the bridge/panel driver should be
independent of vblank.

> > The problem is that the above scenario cannot be mapped to panel-simple driver.
> > IMO, panel_simple should provide enable/disable controls both for LCD
> > and backlight.
> > something like panel_simple_lcd_enable/panel_simple_led_enable, and
> > panel_simple_lcd_disable/panel_simple_led_disable.
>
> That's not what the simple panel driver can do. If we want this it needs
> to be solved in a generic way for all panels since they all need to use
> the drm_panel_*() functions to abstract away the details from drivers
> that use the panels.
Right. So only I have added pre_enable and post_disable callbacks.
Using that name won't harm existing panel drivers and still addresses
our requirement.


Regards,
Ajay

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

* Re: [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls
  2014-04-24 17:38               ` Ajay kumar
@ 2014-04-25  6:10                 ` Ajay kumar
  2014-04-25  6:17                   ` Ajay kumar
  0 siblings, 1 reply; 58+ messages in thread
From: Ajay kumar @ 2014-04-25  6:10 UTC (permalink / raw)
  To: Rob Clark
  Cc: Ajay Kumar, dri-devel, moderated list:ARM/S5P EXYNOS AR...,
	Sean Paul, sunil joshi, Prashanth G

On Thu, Apr 24, 2014 at 11:08 PM, Ajay kumar <ajaynumb@gmail.com> wrote:
> On Thu, Apr 24, 2014 at 10:55 PM, Rob Clark <robdclark@gmail.com> wrote:
>> On Thu, Apr 24, 2014 at 12:55 PM, Ajay kumar <ajaynumb@gmail.com> wrote:
>>> Rob,
>>>
>>> On Thu, Apr 24, 2014 at 9:41 PM, Rob Clark <robdclark@gmail.com> wrote:
>>>> On Wed, Apr 23, 2014 at 3:02 PM, Ajay kumar <ajaynumb@gmail.com> wrote:
>>>>> Sorry for the previous reply,
>>>>>
>>>>> Here goes the full explaination:
>>>>>
>>>>>> Rob,
>>>>>>
>>>>>> On Tue, Apr 22, 2014 at 5:04 PM, Rob Clark <robdclark@gmail.com> wrote:
>>>>>>> So what about, rather than adding drm_panel support to each bridge
>>>>>>> individually, we introduce a drm_panel_bridge (with a form of
>>>>>>> chaining).. ie:
>>>>>>>
>>>>>>>   struct drm_panel_bridge {
>>>>>>>     struct drm_bridge base;
>>>>>>>     struct drm_panel *panel;
>>>>>>>     struct drm_bridge *bridge; /* optional */
>>>>>>>   };
>>>>>>>
>>>>>>>   static void drm_panel_bridge_enable(struct drm_bridge *bridge)
>>>>>>>   {
>>>>>>>     struct drm_panel_bridge *pb = to_panel_bridge(bridge);
>>>>>>>     if (pb->bridge)
>>>>>>>       pb->bridge->funcs->enable(pb->bridge);
>>>>>>>     pb->panel->funcs->enable(pb->panel);
>>>>>>>   }
>>>>>>>
>>>>> We cannot call them like this from crtc helpers in the order you mentioned,
>>>>> since each individual bridge chip expects the panel controls at
>>>>> different places.
>>>>> I mean,
>>>>> -- sometimes panel controls needs to be done before bridge
>>>>> controls(ptn3460: before RST_N and PD_N)
>>>>
>>>> well, this is why bridge has pre-enable/enable/disable/post-disable
>>>> hooks, so you can choose before or after..
>>> These calls are for a bridge to sync with the encoder calls.
>>> We might end up defining pre-enable/enable/disable/post-disable for a
>>> panel to sync
>>> with the bridge calls! I have explained below.
>>>
>>>>> -- sometimes in between the bridge controls (ps8622: one panel control
>>>>> before SLP_N and one after SLP_N)
>>>>> -- sometimes panel controls needs to be done after bridge controls.
>>>>
>>>> I am not convinced that a generic panel/bridge adapter is not
>>>> possible.  Maybe we need more fine grained fxn ptr callbacks, although
>>>> seems like pre+post should give you enough.  It would certainly be
>>>> easier than having to add panel support in each individual bridge
>>>> driver (which seems like it will certainly result that only certain
>>>> combinations of panel+bridge actually work as intended)
>>> Ok. Consider this case:
>>> Currently, we have the sequence as "bridge->pre_enable,
>>> encoder_enable, bridge->enable"
>>> And, the bridge should obviously be enabled in bridge->pre_enable.
>>> Now, where do you choose to call panel->pre_enable?
>>> -- as the first step of bridge->pre_enable, before we pull up/down bridge GPIOs.
>>> -- at the last step of bridge->pre_enable, after we pull up/down the
>>> bridge GPIOs.
>>>
>>> Ideally, PTN3460 expects it to be the second case, and PS8625 expects
>>> it to be the first case.
>>> So, we will end up adding pre_pre_enable, post_pre_enable to
>>> accomodate such scenarios.
>>
>> ok, well I think we can deal with this with a slight modification of
>> my original proposal.  Split drm_panel_bridge into
>> drm_composite_bridge and drm_panel_bridge:
>>
>>   struct drm_composite_bridge {
>>     struct drm_bridge base;
>>     struct drm_bridge *b1, *b2;
>>   }
>>
>>   static void drm_composite_bridge_enable(struct drm_bridge *bridge)
>>   {
>>     struct drm_composite_bridge *cbridge = to_composite_bridge(bridge);
>>     cbridge->b1->funcs->enable(cbridge->b1);
>>     cbridge->b2->funcs->enable(cbridge->b2);
>>   }
>>
>>   .. and so on ..
>>
>>   struct drm_panel_bridge {
>>     struct drm_bridge base;
>>     struct drm_panel *panel;
>>   }
>>
>>   static void drm_panel_bridge_enable(struct drm_bridge *bridge)
>>   {
>>     struct drm_panel_bridge *pbridge = to_panel_bridge(bridge);
>>     pbridge->panel->funcs->enable(pbridge->panel);
>>   }
>>
>>   .. and so on..
>>
>>
>> then in initialization, order can be managed like:
>>
>>   if (panel_first)
>>     bridge = drm_composite_bridge_init(panel_bridge, actual_bridge)
>>   else
>>     bridge = drm_composite_bridge_init(actual_bridge, panel_bridge);
>>
>>   possibly parametrize drm_panel_bridge if we need to map
>> panel->enable() to bridge->pre_enable() vs bridge->enable(), etc..
>
> Well, this really does seems complex to me.
> Don't you think just having a drm_panel inside drm_bridge structure is
> sufficient?!
> And, make a call for pre_panel_enable and post_panel_enable around
> bridge->pre_enable..and so on.?
Adding more comments:
The beauty of drm_panel is in the flexibility which it provides.
We can call panel_enable/disable at the right point. Even the

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

* Re: [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls
  2014-04-25  6:10                 ` Ajay kumar
@ 2014-04-25  6:17                   ` Ajay kumar
  2014-04-27 12:55                     ` Daniel Vetter
  0 siblings, 1 reply; 58+ messages in thread
From: Ajay kumar @ 2014-04-25  6:17 UTC (permalink / raw)
  To: Rob Clark
  Cc: Ajay Kumar, dri-devel, moderated list:ARM/S5P EXYNOS AR...,
	Sean Paul, sunil joshi, Prashanth G

Sorry for the previous reply. Here goes the full explaination.

On Fri, Apr 25, 2014 at 11:40 AM, Ajay kumar <ajaynumb@gmail.com> wrote:
> On Thu, Apr 24, 2014 at 11:08 PM, Ajay kumar <ajaynumb@gmail.com> wrote:
>> On Thu, Apr 24, 2014 at 10:55 PM, Rob Clark <robdclark@gmail.com> wrote:
>>> On Thu, Apr 24, 2014 at 12:55 PM, Ajay kumar <ajaynumb@gmail.com> wrote:
>>>> Rob,
>>>>
>>>> On Thu, Apr 24, 2014 at 9:41 PM, Rob Clark <robdclark@gmail.com> wrote:
>>>>> On Wed, Apr 23, 2014 at 3:02 PM, Ajay kumar <ajaynumb@gmail.com> wrote:
>>>>>> Sorry for the previous reply,
>>>>>>
>>>>>> Here goes the full explaination:
>>>>>>
>>>>>>> Rob,
>>>>>>>
>>>>>>> On Tue, Apr 22, 2014 at 5:04 PM, Rob Clark <robdclark@gmail.com> wrote:
>>>>>>>> So what about, rather than adding drm_panel support to each bridge
>>>>>>>> individually, we introduce a drm_panel_bridge (with a form of
>>>>>>>> chaining).. ie:
>>>>>>>>
>>>>>>>>   struct drm_panel_bridge {
>>>>>>>>     struct drm_bridge base;
>>>>>>>>     struct drm_panel *panel;
>>>>>>>>     struct drm_bridge *bridge; /* optional */
>>>>>>>>   };
>>>>>>>>
>>>>>>>>   static void drm_panel_bridge_enable(struct drm_bridge *bridge)
>>>>>>>>   {
>>>>>>>>     struct drm_panel_bridge *pb = to_panel_bridge(bridge);
>>>>>>>>     if (pb->bridge)
>>>>>>>>       pb->bridge->funcs->enable(pb->bridge);
>>>>>>>>     pb->panel->funcs->enable(pb->panel);
>>>>>>>>   }
>>>>>>>>
>>>>>> We cannot call them like this from crtc helpers in the order you mentioned,
>>>>>> since each individual bridge chip expects the panel controls at
>>>>>> different places.
>>>>>> I mean,
>>>>>> -- sometimes panel controls needs to be done before bridge
>>>>>> controls(ptn3460: before RST_N and PD_N)
>>>>>
>>>>> well, this is why bridge has pre-enable/enable/disable/post-disable
>>>>> hooks, so you can choose before or after..
>>>> These calls are for a bridge to sync with the encoder calls.
>>>> We might end up defining pre-enable/enable/disable/post-disable for a
>>>> panel to sync
>>>> with the bridge calls! I have explained below.
>>>>
>>>>>> -- sometimes in between the bridge controls (ps8622: one panel control
>>>>>> before SLP_N and one after SLP_N)
>>>>>> -- sometimes panel controls needs to be done after bridge controls.
>>>>>
>>>>> I am not convinced that a generic panel/bridge adapter is not
>>>>> possible.  Maybe we need more fine grained fxn ptr callbacks, although
>>>>> seems like pre+post should give you enough.  It would certainly be
>>>>> easier than having to add panel support in each individual bridge
>>>>> driver (which seems like it will certainly result that only certain
>>>>> combinations of panel+bridge actually work as intended)
>>>> Ok. Consider this case:
>>>> Currently, we have the sequence as "bridge->pre_enable,
>>>> encoder_enable, bridge->enable"
>>>> And, the bridge should obviously be enabled in bridge->pre_enable.
>>>> Now, where do you choose to call panel->pre_enable?
>>>> -- as the first step of bridge->pre_enable, before we pull up/down bridge GPIOs.
>>>> -- at the last step of bridge->pre_enable, after we pull up/down the
>>>> bridge GPIOs.
>>>>
>>>> Ideally, PTN3460 expects it to be the second case, and PS8625 expects
>>>> it to be the first case.
>>>> So, we will end up adding pre_pre_enable, post_pre_enable to
>>>> accomodate such scenarios.
>>>
>>> ok, well I think we can deal with this with a slight modification of
>>> my original proposal.  Split drm_panel_bridge into
>>> drm_composite_bridge and drm_panel_bridge:
>>>
>>>   struct drm_composite_bridge {
>>>     struct drm_bridge base;
>>>     struct drm_bridge *b1, *b2;
>>>   }
>>>
>>>   static void drm_composite_bridge_enable(struct drm_bridge *bridge)
>>>   {
>>>     struct drm_composite_bridge *cbridge = to_composite_bridge(bridge);
>>>     cbridge->b1->funcs->enable(cbridge->b1);
>>>     cbridge->b2->funcs->enable(cbridge->b2);
>>>   }
>>>
>>>   .. and so on ..
>>>
>>>   struct drm_panel_bridge {
>>>     struct drm_bridge base;
>>>     struct drm_panel *panel;
>>>   }
>>>
>>>   static void drm_panel_bridge_enable(struct drm_bridge *bridge)
>>>   {
>>>     struct drm_panel_bridge *pbridge = to_panel_bridge(bridge);
>>>     pbridge->panel->funcs->enable(pbridge->panel);
>>>   }
>>>
>>>   .. and so on..
>>>
>>>
>>> then in initialization, order can be managed like:
>>>
>>>   if (panel_first)
>>>     bridge = drm_composite_bridge_init(panel_bridge, actual_bridge)
>>>   else
>>>     bridge = drm_composite_bridge_init(actual_bridge, panel_bridge);
>>>
>>>   possibly parametrize drm_panel_bridge if we need to map
>>> panel->enable() to bridge->pre_enable() vs bridge->enable(), etc..
>>
>> Well, this really does seems complex to me.
>> Don't you think just having a drm_panel inside drm_bridge structure is
>> sufficient?!
>> And, make a call for pre_panel_enable and post_panel_enable around
>> bridge->pre_enable..and so on.?
Adding more comments:
The beauty of drm_panel is in the flexibility which it provides.
We can call panel_enable/disable at the right point. Even the bridge chips
expect the same. So, I am not ok with combining the bridge and panel and
calling the fxn pointers from crtc_helpers.
So, either we leave it the way it is in this patch (call drm_panel
functions at right points) or
we don't use drm_panel to control the panel sequence and instead use few DT
properties and regulators, inside the bridge chip driver.

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

* Re: [PATCH V2 2/9] drm/panel: add pre_enable and post_disable routines
  2014-04-25  6:04               ` Ajay kumar
@ 2014-04-25 18:16                 ` Ajay kumar
  2014-04-29 12:15                   ` Ajay kumar
  0 siblings, 1 reply; 58+ messages in thread
From: Ajay kumar @ 2014-04-25 18:16 UTC (permalink / raw)
  To: Thierry Reding, Daniel Vetter, Rob Clark
  Cc: linux-samsung-soc, Sean Paul, sunil joshi, dri-devel,
	Prashanth G, Ajay Kumar

On Fri, Apr 25, 2014 at 11:34 AM, Ajay kumar <ajaynumb@gmail.com> wrote:
> On Friday, April 25, 2014, Thierry Reding <thierry.reding@gmail.com> wrote:
>>
>> On Thu, Apr 24, 2014 at 12:56:02AM +0530, Ajay kumar wrote:
>> > Thierry,
>> >
>> > On Wed, Apr 23, 2014 at 1:12 PM, Thierry Reding
>> > <thierry.reding@gmail.com> wrote:
>> > > On Wed, Apr 23, 2014 at 09:29:15AM +0200, Daniel Vetter wrote:
>> [...]
>> > >> Imo this makes sense, especially if we go through with the idea talked
>> > >> about yesterday of creating a drm_bridge to wrap-up a drm_panel with
>> > >> sufficient isolation between all components.
>> > >
>> > > I'm not at all comfortable with these. The names are totally confusing.
>> > > Next somebody will need to do something after the panel has been enabled
>> > > and we'll have to introduce .post_enable() and .pre_disable() functions.
>> > > And worse, what if somebody needs something to be done between
>> > > .pre_enable() and .enable()? .post_pre_enable()? Where does it end?
>> > >
>> > > According to the above description the only reason why we need this is
>> > > to avoid visible glitches when the panel is already on, but the video
>> > > stream hasn't started yet. If that's the only reason why we need this,
>> > > then perhaps adding a way for a panel to expose the associated backlight
>> > > would be better?
>> > Actually, we need not expose the entire backlight device.
>> > AFAIK, the glitch is caused when you enable BL_EN before
>> > there is valid video data. So, one way to mask off the glitch is to
>> > follow this sequence:
>> > -- power up the panel.
>> > -- start video data, (start PWM here or)
>> > -- (start PWM here), enable backlight
>>
>> That's very difficult to get right, isn't it? Even if you have fine-
>> grained control over what to enable you still need a way to determine
>> _when_ it's safe to enable the backlight. Typically I guess that would
>> be the duration of one frame (or perhaps 2, depending on when the panel
>> syncs to the video signal).
> We need not "determine", its already present in LVDS datasheet.
> The LVDS datasheet says at least 200ms delay is needed from "Valid
> data" to "BL on".
>
>> Perhaps it could even by sync'ed to the VBLANK?
> No. vblanks are related to crtc. And the bridge/panel driver should be
> independent of vblank.
>
>> > The problem is that the above scenario cannot be mapped to panel-simple driver.
>> > IMO, panel_simple should provide enable/disable controls both for LCD
>> > and backlight.
>> > something like panel_simple_lcd_enable/panel_simple_led_enable, and
>> > panel_simple_lcd_disable/panel_simple_led_disable.
>>
>> That's not what the simple panel driver can do. If we want this it needs
>> to be solved in a generic way for all panels since they all need to use
>> the drm_panel_*() functions to abstract away the details from drivers
>> that use the panels.
> Right. So only I have added pre_enable and post_disable callbacks.
> Using that name won't harm existing panel drivers and still addresses
> our requirement.
>
>
> Regards,
> Ajay


Thierry :
Are you really ok with the new callback names? like pre_enable and post_disable?
Adding those new callbacks really won't harm the existing panels anyhow.

Daniel, Rob :
I think I have given sufficient amount of information as to why the concept of
drm_panel_bridge fails and why we cannot have callbacks for drm_panel
inside crtc helpers
or any other generic place.

Please let me know your final opinion so that I can start reworking on
this series.

Thanks and regards,
Ajay Kumar

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

* Re: [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls
  2014-04-25  6:17                   ` Ajay kumar
@ 2014-04-27 12:55                     ` Daniel Vetter
  2014-04-28 13:08                       ` Ajay kumar
  0 siblings, 1 reply; 58+ messages in thread
From: Daniel Vetter @ 2014-04-27 12:55 UTC (permalink / raw)
  To: Ajay kumar
  Cc: moderated list:ARM/S5P EXYNOS AR...,
	Sean Paul, sunil joshi, dri-devel, Prashanth G, Ajay Kumar

On Fri, Apr 25, 2014 at 8:17 AM, Ajay kumar <ajaynumb@gmail.com> wrote:
> We can call panel_enable/disable at the right point. Even the bridge chips
> expect the same. So, I am not ok with combining the bridge and panel and
> calling the fxn pointers from crtc_helpers.
> So, either we leave it the way it is in this patch (call drm_panel
> functions at right points) or
> we don't use drm_panel to control the panel sequence and instead use few DT
> properties and regulators, inside the bridge chip driver.

That wasn't really suggested, but instead the idea was to provide a
default drm_bridge which wraps the drm_panel so that you could more
easily chain them up.

Also I'm not really happy that the bridge callbacks have been added to
the drm helpers (I'd prefer if driver callbacks would bear that
responsibility). But you can always create your own drm_bridge
integration. In any case your concern that drivers need to control
when certain callbacks are called is shared by everyone here afaics.
And I also don't see any issues with Rob's proposal in this regard.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls
  2014-04-27 12:55                     ` Daniel Vetter
@ 2014-04-28 13:08                       ` Ajay kumar
  2014-04-29 13:14                         ` Rob Clark
  0 siblings, 1 reply; 58+ messages in thread
From: Ajay kumar @ 2014-04-28 13:08 UTC (permalink / raw)
  To: Daniel Vetter, Rob Clark
  Cc: moderated list:ARM/S5P EXYNOS AR...,
	Sean Paul, sunil joshi, dri-devel, Prashanth G, Ajay Kumar

Daniel,

On Sun, Apr 27, 2014 at 6:25 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Fri, Apr 25, 2014 at 8:17 AM, Ajay kumar <ajaynumb@gmail.com> wrote:
>> We can call panel_enable/disable at the right point. Even the bridge chips
>> expect the same. So, I am not ok with combining the bridge and panel and
>> calling the fxn pointers from crtc_helpers.
>> So, either we leave it the way it is in this patch (call drm_panel
>> functions at right points) or
>> we don't use drm_panel to control the panel sequence and instead use few DT
>> properties and regulators, inside the bridge chip driver.
>
> That wasn't really suggested, but instead the idea was to provide a
> default drm_bridge which wraps the drm_panel so that you could more
> easily chain them up.
What do you mean by "default" drm_bridge?
I just want to clear few things. Let me explain what I have understood:
What I understand is that Rob wants me to create a common
structure which wraps up drm_panel and drm_bridge into a single structure:
  struct drm_panel_bridge {
    struct drm_bridge base;
    struct drm_panel *panel;
    struct drm_bridge *bridge; /* optional */  ==> Really not sure why
this is needed!
  };

  static void drm_panel_bridge_enable(struct drm_bridge *bridge)
  {
    struct drm_panel_bridge *pb = to_panel_bridge(bridge);
    if (pb->bridge)
      pb->bridge->funcs->enable(pb->bridge);
    pb->panel->funcs->enable(pb->panel);
  }

And now, the above function becomes a common function
(also with an ordering issue btw panel and bridge).
Where do we keep it? And, where do we call it from?

> Also I'm not really happy that the bridge callbacks have been added to
> the drm helpers (I'd prefer if driver callbacks would bear that
> responsibility). But you can always create your own drm_bridge
> integration.
Do you mean, the bridge calls should be moved out of common drm_helper
functions and called from platform specific drivers instead?

> In any case your concern that drivers need to control
> when certain callbacks are called is shared by everyone here afaics.
> And I also don't see any issues with Rob's proposal in this regard.
> -Daniel
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch

Please let me know if my understanding is correct and correct me if
there is a misconception.

Regards,
Ajay Kumar

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

* Re: [PATCH V2 2/9] drm/panel: add pre_enable and post_disable routines
  2014-04-25 18:16                 ` Ajay kumar
@ 2014-04-29 12:15                   ` Ajay kumar
  0 siblings, 0 replies; 58+ messages in thread
From: Ajay kumar @ 2014-04-29 12:15 UTC (permalink / raw)
  To: Thierry Reding, Daniel Vetter, Rob Clark
  Cc: linux-samsung-soc, Sean Paul, sunil joshi, dri-devel,
	Prashanth G, Ajay Kumar

ping!

On Fri, Apr 25, 2014 at 11:46 PM, Ajay kumar <ajaynumb@gmail.com> wrote:
> On Fri, Apr 25, 2014 at 11:34 AM, Ajay kumar <ajaynumb@gmail.com> wrote:
>> On Friday, April 25, 2014, Thierry Reding <thierry.reding@gmail.com> wrote:
>>>
>>> On Thu, Apr 24, 2014 at 12:56:02AM +0530, Ajay kumar wrote:
>>> > Thierry,
>>> >
>>> > On Wed, Apr 23, 2014 at 1:12 PM, Thierry Reding
>>> > <thierry.reding@gmail.com> wrote:
>>> > > On Wed, Apr 23, 2014 at 09:29:15AM +0200, Daniel Vetter wrote:
>>> [...]
>>> > >> Imo this makes sense, especially if we go through with the idea talked
>>> > >> about yesterday of creating a drm_bridge to wrap-up a drm_panel with
>>> > >> sufficient isolation between all components.
>>> > >
>>> > > I'm not at all comfortable with these. The names are totally confusing.
>>> > > Next somebody will need to do something after the panel has been enabled
>>> > > and we'll have to introduce .post_enable() and .pre_disable() functions.
>>> > > And worse, what if somebody needs something to be done between
>>> > > .pre_enable() and .enable()? .post_pre_enable()? Where does it end?
>>> > >
>>> > > According to the above description the only reason why we need this is
>>> > > to avoid visible glitches when the panel is already on, but the video
>>> > > stream hasn't started yet. If that's the only reason why we need this,
>>> > > then perhaps adding a way for a panel to expose the associated backlight
>>> > > would be better?
>>> > Actually, we need not expose the entire backlight device.
>>> > AFAIK, the glitch is caused when you enable BL_EN before
>>> > there is valid video data. So, one way to mask off the glitch is to
>>> > follow this sequence:
>>> > -- power up the panel.
>>> > -- start video data, (start PWM here or)
>>> > -- (start PWM here), enable backlight
>>>
>>> That's very difficult to get right, isn't it? Even if you have fine-
>>> grained control over what to enable you still need a way to determine
>>> _when_ it's safe to enable the backlight. Typically I guess that would
>>> be the duration of one frame (or perhaps 2, depending on when the panel
>>> syncs to the video signal).
>> We need not "determine", its already present in LVDS datasheet.
>> The LVDS datasheet says at least 200ms delay is needed from "Valid
>> data" to "BL on".
>>
>>> Perhaps it could even by sync'ed to the VBLANK?
>> No. vblanks are related to crtc. And the bridge/panel driver should be
>> independent of vblank.
>>
>>> > The problem is that the above scenario cannot be mapped to panel-simple driver.
>>> > IMO, panel_simple should provide enable/disable controls both for LCD
>>> > and backlight.
>>> > something like panel_simple_lcd_enable/panel_simple_led_enable, and
>>> > panel_simple_lcd_disable/panel_simple_led_disable.
>>>
>>> That's not what the simple panel driver can do. If we want this it needs
>>> to be solved in a generic way for all panels since they all need to use
>>> the drm_panel_*() functions to abstract away the details from drivers
>>> that use the panels.
>> Right. So only I have added pre_enable and post_disable callbacks.
>> Using that name won't harm existing panel drivers and still addresses
>> our requirement.
>>
>>
>> Regards,
>> Ajay
>
>
> Thierry :
> Are you really ok with the new callback names? like pre_enable and post_disable?
> Adding those new callbacks really won't harm the existing panels anyhow.
>
> Daniel, Rob :
> I think I have given sufficient amount of information as to why the concept of
> drm_panel_bridge fails and why we cannot have callbacks for drm_panel
> inside crtc helpers
> or any other generic place.
>
> Please let me know your final opinion so that I can start reworking on
> this series.
>
> Thanks and regards,
> Ajay Kumar

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

* Re: [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls
  2014-04-28 13:08                       ` Ajay kumar
@ 2014-04-29 13:14                         ` Rob Clark
  0 siblings, 0 replies; 58+ messages in thread
From: Rob Clark @ 2014-04-29 13:14 UTC (permalink / raw)
  To: Ajay kumar
  Cc: moderated list:ARM/S5P EXYNOS AR...,
	Sean Paul, sunil joshi, dri-devel, Prashanth G, Ajay Kumar

On Mon, Apr 28, 2014 at 9:08 AM, Ajay kumar <ajaynumb@gmail.com> wrote:
> Daniel,
>
> On Sun, Apr 27, 2014 at 6:25 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
>> On Fri, Apr 25, 2014 at 8:17 AM, Ajay kumar <ajaynumb@gmail.com> wrote:
>>> We can call panel_enable/disable at the right point. Even the bridge chips
>>> expect the same. So, I am not ok with combining the bridge and panel and
>>> calling the fxn pointers from crtc_helpers.
>>> So, either we leave it the way it is in this patch (call drm_panel
>>> functions at right points) or
>>> we don't use drm_panel to control the panel sequence and instead use few DT
>>> properties and regulators, inside the bridge chip driver.
>>
>> That wasn't really suggested, but instead the idea was to provide a
>> default drm_bridge which wraps the drm_panel so that you could more
>> easily chain them up.
> What do you mean by "default" drm_bridge?
> I just want to clear few things. Let me explain what I have understood:
> What I understand is that Rob wants me to create a common
> structure which wraps up drm_panel and drm_bridge into a single structure:
>   struct drm_panel_bridge {
>     struct drm_bridge base;
>     struct drm_panel *panel;
>     struct drm_bridge *bridge; /* optional */  ==> Really not sure why
> this is needed!
>   };
>
>   static void drm_panel_bridge_enable(struct drm_bridge *bridge)
>   {
>     struct drm_panel_bridge *pb = to_panel_bridge(bridge);
>     if (pb->bridge)
>       pb->bridge->funcs->enable(pb->bridge);
>     pb->panel->funcs->enable(pb->panel);
>   }
>
> And now, the above function becomes a common function
> (also with an ordering issue btw panel and bridge).
> Where do we keep it? And, where do we call it from?

my proposal involves no change to crtc helpers.  The later variation
(with composite_bridge so you could choose the order) would have a
sequence along the lines of:

crtc helpers:
+ bridge_funcs->enable(): -> composite_bridge_enable()
   + cbridge->b1->funcs->enable(): -> ptn3460_bridge_enable()
   + cbridge->b2->funcs->enable(): -> panel_bridge_enable();
       + pbridge->panel->funcs->enable(): -> foo_panel_enable()

or if the order needs to be swapped you can use ptn3460_bridge as b2
and panel bridge as b1.

BR,
-R

>> Also I'm not really happy that the bridge callbacks have been added to
>> the drm helpers (I'd prefer if driver callbacks would bear that
>> responsibility). But you can always create your own drm_bridge
>> integration.
> Do you mean, the bridge calls should be moved out of common drm_helper
> functions and called from platform specific drivers instead?
>
>> In any case your concern that drivers need to control
>> when certain callbacks are called is shared by everyone here afaics.
>> And I also don't see any issues with Rob's proposal in this regard.
>> -Daniel
>> --
>> Daniel Vetter
>> Software Engineer, Intel Corporation
>> +41 (0) 79 365 57 48 - http://blog.ffwll.ch
>
> Please let me know if my understanding is correct and correct me if
> there is a misconception.
>
> Regards,
> Ajay Kumar

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

* Re: [PATCH V2 1/9] drm/exynos: dp: support hotplug detection via GPIO
  2014-04-22  7:13   ` Jingoo Han
@ 2014-05-09  0:57     ` Jingoo Han
  0 siblings, 0 replies; 58+ messages in thread
From: Jingoo Han @ 2014-05-09  0:57 UTC (permalink / raw)
  To: 'Inki Dae', 'Ajay Kumar'
  Cc: linux-samsung-soc, seanpaul, 'Andrew Bresticker',
	joshi, dri-devel, ajaynumb, prashanth.g, 'Rahul Sharma'

On Tuesday, April 22, 2014 4:14 PM, Jingoo Han wrote:
> On Tuesday, April 22, 2014 7:39 AM, Ajay Kumar wrote:
> >
> > From: Andrew Bresticker <abrestic@chromium.org>
> >
> > Certain bridge chips use a GPIO to indicate the cable status instead
> > of the I_DP_HPD pin.  This adds an optional device-tree property,
> > "samsung,hpd-gpio", to the exynos-dp controller which indicates that
> > the specified GPIO should be used for hotplug detection.
> > The GPIO is then set up as an edge-triggered interrupt where the
> > rising edge indicates hotplug-in and the falling edge indicates hotplug-out.
> >
> > Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
> > Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
> > Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
> 
> Acked-by: Jingoo Han <jg1.han@samsung.com>

Hi Inki Dae,

Actually, this patch is NOT related to other patches for
bridge chip support. So, would you apply this patch into
your exynos drm git with my Acked-by?

Best regards,
Jingoo Han


> 
> > ---
> > Changes since V1:
> > 	Address reiew comments from Jingoo Han
> >
> >  .../devicetree/bindings/video/exynos_dp.txt        |    4 ++
> >  drivers/gpu/drm/exynos/exynos_dp_core.c            |   32 ++++++++++++--
> >  drivers/gpu/drm/exynos/exynos_dp_core.h            |    1 +
> >  drivers/gpu/drm/exynos/exynos_dp_reg.c             |   44 ++++++++++++++------
> >  4 files changed, 66 insertions(+), 15 deletions(-)
> 
> [.....]

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

* Re: [PATCH V2 4/9] drm/exynos: add exynos_dp_panel driver registration to drm driver
  2014-04-22 15:26       ` Thierry Reding
@ 2014-08-20  4:02         ` Stéphane Marchesin
  2014-08-21  7:36           ` Thierry Reding
  0 siblings, 1 reply; 58+ messages in thread
From: Stéphane Marchesin @ 2014-08-20  4:02 UTC (permalink / raw)
  To: Thierry Reding
  Cc: linux-samsung-soc, Sean Paul, sunil joshi, dri-devel, Ajay kumar,
	Prashanth G, Ajay Kumar

On Tue, Apr 22, 2014 at 8:26 AM, Thierry Reding
<thierry.reding@gmail.com> wrote:
> On Tue, Apr 22, 2014 at 08:33:23PM +0530, Ajay kumar wrote:
>> Hi Thierry,
>>
>> On Tue, Apr 22, 2014 at 2:03 PM, Thierry Reding
>> <thierry.reding@gmail.com> wrote:
>> > On Tue, Apr 22, 2014 at 04:09:13AM +0530, Ajay Kumar wrote:
>> >> Register exynos_dp_panel before the list of exynos crtcs and
>> >> connectors are probed.
>> >>
>> >> This is needed because exynos_dp_panel should be registered to
>> >> the drm_panel list via panel-exynos-dp probe, i.e much before
>> >> exynos_dp_bind calls of_drm_find_panel().
>> >>
>> >> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
>> >> ---
>> >> Changes since V1:
>> >>       Added platform_driver_unregister(&exynos_dp_panel_driver) to
>> >>       exynos_drm_platform_remove as per Jingoo Han's correction
>> >>
>> >>  drivers/gpu/drm/exynos/exynos_drm_drv.c |   15 +++++++++++++++
>> >>  drivers/gpu/drm/exynos/exynos_drm_drv.h |    1 +
>> >>  2 files changed, 16 insertions(+)
>> >>
>> >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>> >> index 1d653f8..2db7f67 100644
>> >> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
>> >> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>> >> @@ -530,12 +530,23 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
>> >>               goto err_unregister_ipp_drv;
>> >>  #endif
>> >>
>> >> +#ifdef CONFIG_DRM_PANEL_EXYNOS_DP
>> >> +     ret = platform_driver_register(&exynos_dp_panel_driver);
>> >> +     if (ret < 0)
>> >> +             goto err_unregister_dp_panel;
>> >> +#endif
>> >
>> > No, this is not how you're supposed to use DRM panel drivers. The idea
>> > is that you write a standalone driver for a given panel.
>> >
>> > What you do here has a number of problems. For one it's a driver that's
>> > tightly coupled to Exynos SoCs. But if I have a different SoC that uses
>> > the same panel I want to be able to use the same driver, and not have to
>> > rewrite the driver for my SoC.
>> >
>> > Another problem is that you're assuming here that the driver is built in
>> > and it will break if you try to build either Exynos DRM or the panel
>> > driver as a module. This is perhaps nothing you care about right now,
>> > but eventually people will want to ship a single kernel that can run on
>> > a number of SoCs. But if we keep adding things like this, that kernel
>> > will keep growing in size until it no longer fits in any kind of memory.
>> >
>> > Thierry
>>
>> I completely agree with you in this!
>>
>> Yes, this is not acceptable, but I want to know an "acceptable"
>> workaround for the situation below:
>> I register the driver using module_init().
>> And, exynos_drm gets probed much before the panel driver probe happens.
>> So, the panel driver hasn't probed yet, but exynos_dp via exynos_drm
>> tries to call
>> "of_drm_find_panel" which always returns NULL.
>
> That's a situation that your driver needs to be able to deal with. The
> driver registration order doesn't matter one bit. It may happen to work
> most of the time, but as soon as one of the resources that your panel
> driver needs isn't there when the panel is probed, then it won't be
> registered and of_drm_find_panel() will still return NULL.
>
> Usually the right thing to do in that case would be to return (and
> propagate) -EPROBE_DEFER so that your driver's probe is deferred and
> retried when other drivers have been probed. That way it should
> eventually get a non-NULL panel.

So I just gave this (drm_panel + probe deferring) a shot on exynos,
and correctly reacting to -EPROBE_DEFER postpones DP initialization by
approximately 1.5 second. Is there a good way to handle that? As it
stands, this isn't usable.

Stéphane
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH V2 4/9] drm/exynos: add exynos_dp_panel driver registration to drm driver
  2014-08-20  4:02         ` Stéphane Marchesin
@ 2014-08-21  7:36           ` Thierry Reding
  2014-08-21  8:25             ` Stéphane Marchesin
  0 siblings, 1 reply; 58+ messages in thread
From: Thierry Reding @ 2014-08-21  7:36 UTC (permalink / raw)
  To: Stéphane Marchesin
  Cc: Ajay kumar, linux-samsung-soc, Sean Paul, sunil joshi, dri-devel,
	Prashanth G, Ajay Kumar

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

On Tue, Aug 19, 2014 at 09:02:39PM -0700, Stéphane Marchesin wrote:
> On Tue, Apr 22, 2014 at 8:26 AM, Thierry Reding
> <thierry.reding@gmail.com> wrote:
> > On Tue, Apr 22, 2014 at 08:33:23PM +0530, Ajay kumar wrote:
> >> Hi Thierry,
> >>
> >> On Tue, Apr 22, 2014 at 2:03 PM, Thierry Reding
> >> <thierry.reding@gmail.com> wrote:
> >> > On Tue, Apr 22, 2014 at 04:09:13AM +0530, Ajay Kumar wrote:
> >> >> Register exynos_dp_panel before the list of exynos crtcs and
> >> >> connectors are probed.
> >> >>
> >> >> This is needed because exynos_dp_panel should be registered to
> >> >> the drm_panel list via panel-exynos-dp probe, i.e much before
> >> >> exynos_dp_bind calls of_drm_find_panel().
> >> >>
> >> >> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
> >> >> ---
> >> >> Changes since V1:
> >> >>       Added platform_driver_unregister(&exynos_dp_panel_driver) to
> >> >>       exynos_drm_platform_remove as per Jingoo Han's correction
> >> >>
> >> >>  drivers/gpu/drm/exynos/exynos_drm_drv.c |   15 +++++++++++++++
> >> >>  drivers/gpu/drm/exynos/exynos_drm_drv.h |    1 +
> >> >>  2 files changed, 16 insertions(+)
> >> >>
> >> >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >> >> index 1d653f8..2db7f67 100644
> >> >> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >> >> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >> >> @@ -530,12 +530,23 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
> >> >>               goto err_unregister_ipp_drv;
> >> >>  #endif
> >> >>
> >> >> +#ifdef CONFIG_DRM_PANEL_EXYNOS_DP
> >> >> +     ret = platform_driver_register(&exynos_dp_panel_driver);
> >> >> +     if (ret < 0)
> >> >> +             goto err_unregister_dp_panel;
> >> >> +#endif
> >> >
> >> > No, this is not how you're supposed to use DRM panel drivers. The idea
> >> > is that you write a standalone driver for a given panel.
> >> >
> >> > What you do here has a number of problems. For one it's a driver that's
> >> > tightly coupled to Exynos SoCs. But if I have a different SoC that uses
> >> > the same panel I want to be able to use the same driver, and not have to
> >> > rewrite the driver for my SoC.
> >> >
> >> > Another problem is that you're assuming here that the driver is built in
> >> > and it will break if you try to build either Exynos DRM or the panel
> >> > driver as a module. This is perhaps nothing you care about right now,
> >> > but eventually people will want to ship a single kernel that can run on
> >> > a number of SoCs. But if we keep adding things like this, that kernel
> >> > will keep growing in size until it no longer fits in any kind of memory.
> >> >
> >> > Thierry
> >>
> >> I completely agree with you in this!
> >>
> >> Yes, this is not acceptable, but I want to know an "acceptable"
> >> workaround for the situation below:
> >> I register the driver using module_init().
> >> And, exynos_drm gets probed much before the panel driver probe happens.
> >> So, the panel driver hasn't probed yet, but exynos_dp via exynos_drm
> >> tries to call
> >> "of_drm_find_panel" which always returns NULL.
> >
> > That's a situation that your driver needs to be able to deal with. The
> > driver registration order doesn't matter one bit. It may happen to work
> > most of the time, but as soon as one of the resources that your panel
> > driver needs isn't there when the panel is probed, then it won't be
> > registered and of_drm_find_panel() will still return NULL.
> >
> > Usually the right thing to do in that case would be to return (and
> > propagate) -EPROBE_DEFER so that your driver's probe is deferred and
> > retried when other drivers have been probed. That way it should
> > eventually get a non-NULL panel.
> 
> So I just gave this (drm_panel + probe deferring) a shot on exynos,
> and correctly reacting to -EPROBE_DEFER postpones DP initialization by
> approximately 1.5 second. Is there a good way to handle that? As it
> stands, this isn't usable.

How much is 1.5 seconds compared to the overall boot time of the device?
What exactly is causing this 1.5 second delay?

This really is a fundamental issue with deferred probing and the issue
has come up several times in the past. A couple of possible solutions
have been proposed, with the latest being here[0] I think. That ended in
a bit of a debacle, unfortunately, but on of the outcomes was that a lot
of the ordering problems could be fixed by using phandle references to
track dependencies. I'm not aware of anyone working on that right now,
presumably because everyone is busy getting features merged rather than
optimizing boot speed.

Thierry

[0]: https://lkml.org/lkml/2014/5/12/452

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH V2 4/9] drm/exynos: add exynos_dp_panel driver registration to drm driver
  2014-08-21  7:36           ` Thierry Reding
@ 2014-08-21  8:25             ` Stéphane Marchesin
  2014-08-21  9:27               ` Ajay kumar
  2014-08-21  9:28               ` Thierry Reding
  0 siblings, 2 replies; 58+ messages in thread
From: Stéphane Marchesin @ 2014-08-21  8:25 UTC (permalink / raw)
  To: Thierry Reding
  Cc: linux-samsung-soc, Sean Paul, sunil joshi, dri-devel, Ajay kumar,
	Prashanth G, Ajay Kumar

On Thu, Aug 21, 2014 at 12:36 AM, Thierry Reding
<thierry.reding@gmail.com> wrote:
> On Tue, Aug 19, 2014 at 09:02:39PM -0700, Stéphane Marchesin wrote:
>> On Tue, Apr 22, 2014 at 8:26 AM, Thierry Reding
>> <thierry.reding@gmail.com> wrote:
>> > On Tue, Apr 22, 2014 at 08:33:23PM +0530, Ajay kumar wrote:
>> >> Hi Thierry,
>> >>
>> >> On Tue, Apr 22, 2014 at 2:03 PM, Thierry Reding
>> >> <thierry.reding@gmail.com> wrote:
>> >> > On Tue, Apr 22, 2014 at 04:09:13AM +0530, Ajay Kumar wrote:
>> >> >> Register exynos_dp_panel before the list of exynos crtcs and
>> >> >> connectors are probed.
>> >> >>
>> >> >> This is needed because exynos_dp_panel should be registered to
>> >> >> the drm_panel list via panel-exynos-dp probe, i.e much before
>> >> >> exynos_dp_bind calls of_drm_find_panel().
>> >> >>
>> >> >> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
>> >> >> ---
>> >> >> Changes since V1:
>> >> >>       Added platform_driver_unregister(&exynos_dp_panel_driver) to
>> >> >>       exynos_drm_platform_remove as per Jingoo Han's correction
>> >> >>
>> >> >>  drivers/gpu/drm/exynos/exynos_drm_drv.c |   15 +++++++++++++++
>> >> >>  drivers/gpu/drm/exynos/exynos_drm_drv.h |    1 +
>> >> >>  2 files changed, 16 insertions(+)
>> >> >>
>> >> >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>> >> >> index 1d653f8..2db7f67 100644
>> >> >> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
>> >> >> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>> >> >> @@ -530,12 +530,23 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
>> >> >>               goto err_unregister_ipp_drv;
>> >> >>  #endif
>> >> >>
>> >> >> +#ifdef CONFIG_DRM_PANEL_EXYNOS_DP
>> >> >> +     ret = platform_driver_register(&exynos_dp_panel_driver);
>> >> >> +     if (ret < 0)
>> >> >> +             goto err_unregister_dp_panel;
>> >> >> +#endif
>> >> >
>> >> > No, this is not how you're supposed to use DRM panel drivers. The idea
>> >> > is that you write a standalone driver for a given panel.
>> >> >
>> >> > What you do here has a number of problems. For one it's a driver that's
>> >> > tightly coupled to Exynos SoCs. But if I have a different SoC that uses
>> >> > the same panel I want to be able to use the same driver, and not have to
>> >> > rewrite the driver for my SoC.
>> >> >
>> >> > Another problem is that you're assuming here that the driver is built in
>> >> > and it will break if you try to build either Exynos DRM or the panel
>> >> > driver as a module. This is perhaps nothing you care about right now,
>> >> > but eventually people will want to ship a single kernel that can run on
>> >> > a number of SoCs. But if we keep adding things like this, that kernel
>> >> > will keep growing in size until it no longer fits in any kind of memory.
>> >> >
>> >> > Thierry
>> >>
>> >> I completely agree with you in this!
>> >>
>> >> Yes, this is not acceptable, but I want to know an "acceptable"
>> >> workaround for the situation below:
>> >> I register the driver using module_init().
>> >> And, exynos_drm gets probed much before the panel driver probe happens.
>> >> So, the panel driver hasn't probed yet, but exynos_dp via exynos_drm
>> >> tries to call
>> >> "of_drm_find_panel" which always returns NULL.
>> >
>> > That's a situation that your driver needs to be able to deal with. The
>> > driver registration order doesn't matter one bit. It may happen to work
>> > most of the time, but as soon as one of the resources that your panel
>> > driver needs isn't there when the panel is probed, then it won't be
>> > registered and of_drm_find_panel() will still return NULL.
>> >
>> > Usually the right thing to do in that case would be to return (and
>> > propagate) -EPROBE_DEFER so that your driver's probe is deferred and
>> > retried when other drivers have been probed. That way it should
>> > eventually get a non-NULL panel.
>>
>> So I just gave this (drm_panel + probe deferring) a shot on exynos,
>> and correctly reacting to -EPROBE_DEFER postpones DP initialization by
>> approximately 1.5 second. Is there a good way to handle that? As it
>> stands, this isn't usable.
>
> How much is 1.5 seconds compared to the overall boot time of the device?

1.5s is 15-20% of my boot time (if you count the boot time from
firmware start to login prompt, otherwise it's more). Note that on
other platforms, we've seen this take as much as 5 or 6s, but for the
exynos case it is "only" 1.5s.

> What exactly is causing this 1.5 second delay?

A regulator isn't ready, and then drm_panel returns defer. Then the
whole drm driver init is deferred.

>
> This really is a fundamental issue with deferred probing and the issue
> has come up several times in the past. A couple of possible solutions
> have been proposed, with the latest being here[0] I think. That ended in
> a bit of a debacle, unfortunately, but on of the outcomes was that a lot
> of the ordering problems could be fixed by using phandle references to
> track dependencies. I'm not aware of anyone working on that right now,
> presumably because everyone is busy getting features merged rather than
> optimizing boot speed.

Yes, I don't believe boot time ordering will ever happen upstream, but
then the current implementation with EPROBE_DEFER isn't usable either.
Any ideas? ATM it seems like the only way out is to just write my
own dt format for the panel and ignore drm_panel.

Stéphane
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH V2 4/9] drm/exynos: add exynos_dp_panel driver registration to drm driver
  2014-08-21  8:25             ` Stéphane Marchesin
@ 2014-08-21  9:27               ` Ajay kumar
  2014-08-21  9:37                 ` Thierry Reding
  2014-08-21  9:28               ` Thierry Reding
  1 sibling, 1 reply; 58+ messages in thread
From: Ajay kumar @ 2014-08-21  9:27 UTC (permalink / raw)
  To: Stéphane Marchesin
  Cc: Thierry Reding, linux-samsung-soc, Sean Paul, sunil joshi,
	dri-devel, Prashanth G, Ajay Kumar

On Thu, Aug 21, 2014 at 1:55 PM, Stéphane Marchesin
<stephane.marchesin@gmail.com> wrote:
> On Thu, Aug 21, 2014 at 12:36 AM, Thierry Reding
> <thierry.reding@gmail.com> wrote:
>> On Tue, Aug 19, 2014 at 09:02:39PM -0700, Stéphane Marchesin wrote:
>>> On Tue, Apr 22, 2014 at 8:26 AM, Thierry Reding
>>> <thierry.reding@gmail.com> wrote:
>>> > On Tue, Apr 22, 2014 at 08:33:23PM +0530, Ajay kumar wrote:
>>> >> Hi Thierry,
>>> >>
>>> >> On Tue, Apr 22, 2014 at 2:03 PM, Thierry Reding
>>> >> <thierry.reding@gmail.com> wrote:
>>> >> > On Tue, Apr 22, 2014 at 04:09:13AM +0530, Ajay Kumar wrote:
>>> >> >> Register exynos_dp_panel before the list of exynos crtcs and
>>> >> >> connectors are probed.
>>> >> >>
>>> >> >> This is needed because exynos_dp_panel should be registered to
>>> >> >> the drm_panel list via panel-exynos-dp probe, i.e much before
>>> >> >> exynos_dp_bind calls of_drm_find_panel().
>>> >> >>
>>> >> >> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
>>> >> >> ---
>>> >> >> Changes since V1:
>>> >> >>       Added platform_driver_unregister(&exynos_dp_panel_driver) to
>>> >> >>       exynos_drm_platform_remove as per Jingoo Han's correction
>>> >> >>
>>> >> >>  drivers/gpu/drm/exynos/exynos_drm_drv.c |   15 +++++++++++++++
>>> >> >>  drivers/gpu/drm/exynos/exynos_drm_drv.h |    1 +
>>> >> >>  2 files changed, 16 insertions(+)
>>> >> >>
>>> >> >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>> >> >> index 1d653f8..2db7f67 100644
>>> >> >> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>> >> >> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>> >> >> @@ -530,12 +530,23 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
>>> >> >>               goto err_unregister_ipp_drv;
>>> >> >>  #endif
>>> >> >>
>>> >> >> +#ifdef CONFIG_DRM_PANEL_EXYNOS_DP
>>> >> >> +     ret = platform_driver_register(&exynos_dp_panel_driver);
>>> >> >> +     if (ret < 0)
>>> >> >> +             goto err_unregister_dp_panel;
>>> >> >> +#endif
>>> >> >
>>> >> > No, this is not how you're supposed to use DRM panel drivers. The idea
>>> >> > is that you write a standalone driver for a given panel.
>>> >> >
>>> >> > What you do here has a number of problems. For one it's a driver that's
>>> >> > tightly coupled to Exynos SoCs. But if I have a different SoC that uses
>>> >> > the same panel I want to be able to use the same driver, and not have to
>>> >> > rewrite the driver for my SoC.
>>> >> >
>>> >> > Another problem is that you're assuming here that the driver is built in
>>> >> > and it will break if you try to build either Exynos DRM or the panel
>>> >> > driver as a module. This is perhaps nothing you care about right now,
>>> >> > but eventually people will want to ship a single kernel that can run on
>>> >> > a number of SoCs. But if we keep adding things like this, that kernel
>>> >> > will keep growing in size until it no longer fits in any kind of memory.
>>> >> >
>>> >> > Thierry
>>> >>
>>> >> I completely agree with you in this!
>>> >>
>>> >> Yes, this is not acceptable, but I want to know an "acceptable"
>>> >> workaround for the situation below:
>>> >> I register the driver using module_init().
>>> >> And, exynos_drm gets probed much before the panel driver probe happens.
>>> >> So, the panel driver hasn't probed yet, but exynos_dp via exynos_drm
>>> >> tries to call
>>> >> "of_drm_find_panel" which always returns NULL.
>>> >
>>> > That's a situation that your driver needs to be able to deal with. The
>>> > driver registration order doesn't matter one bit. It may happen to work
>>> > most of the time, but as soon as one of the resources that your panel
>>> > driver needs isn't there when the panel is probed, then it won't be
>>> > registered and of_drm_find_panel() will still return NULL.
>>> >
>>> > Usually the right thing to do in that case would be to return (and
>>> > propagate) -EPROBE_DEFER so that your driver's probe is deferred and
>>> > retried when other drivers have been probed. That way it should
>>> > eventually get a non-NULL panel.
>>>
>>> So I just gave this (drm_panel + probe deferring) a shot on exynos,
>>> and correctly reacting to -EPROBE_DEFER postpones DP initialization by
>>> approximately 1.5 second. Is there a good way to handle that? As it
>>> stands, this isn't usable.
>>
>> How much is 1.5 seconds compared to the overall boot time of the device?
>
> 1.5s is 15-20% of my boot time (if you count the boot time from
> firmware start to login prompt, otherwise it's more). Note that on
> other platforms, we've seen this take as much as 5 or 6s, but for the
> exynos case it is "only" 1.5s.
>
>> What exactly is causing this 1.5 second delay?
>
> A regulator isn't ready, and then drm_panel returns defer. Then the
> whole drm driver init is deferred.
>
>>
>> This really is a fundamental issue with deferred probing and the issue
>> has come up several times in the past. A couple of possible solutions
>> have been proposed, with the latest being here[0] I think. That ended in
>> a bit of a debacle, unfortunately, but on of the outcomes was that a lot
>> of the ordering problems could be fixed by using phandle references to
>> track dependencies. I'm not aware of anyone working on that right now,
>> presumably because everyone is busy getting features merged rather than
>> optimizing boot speed.
>
> Yes, I don't believe boot time ordering will ever happen upstream, but
> then the current implementation with EPROBE_DEFER isn't usable either.
> Any ideas? ATM it seems like the only way out is to just write my
> own dt format for the panel and ignore drm_panel.
Something like this:
https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/gpu/drm/exynos/exynos_dp_core.c

With this way also, we would expect the regulator to come up early(before drm).

Ajay

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

* Re: [PATCH V2 4/9] drm/exynos: add exynos_dp_panel driver registration to drm driver
  2014-08-21  8:25             ` Stéphane Marchesin
  2014-08-21  9:27               ` Ajay kumar
@ 2014-08-21  9:28               ` Thierry Reding
  1 sibling, 0 replies; 58+ messages in thread
From: Thierry Reding @ 2014-08-21  9:28 UTC (permalink / raw)
  To: Stéphane Marchesin
  Cc: linux-samsung-soc, Sean Paul, sunil joshi, dri-devel,
	Ludovic Desroches, Ajay kumar, Prashanth G, Ajay Kumar


[-- Attachment #1.1: Type: text/plain, Size: 8983 bytes --]

Adding Boris and Ludovic since this topic came up in a different thread
as well.

On Thu, Aug 21, 2014 at 01:25:07AM -0700, Stéphane Marchesin wrote:
> On Thu, Aug 21, 2014 at 12:36 AM, Thierry Reding
> <thierry.reding@gmail.com> wrote:
> > On Tue, Aug 19, 2014 at 09:02:39PM -0700, Stéphane Marchesin wrote:
> >> On Tue, Apr 22, 2014 at 8:26 AM, Thierry Reding
> >> <thierry.reding@gmail.com> wrote:
> >> > On Tue, Apr 22, 2014 at 08:33:23PM +0530, Ajay kumar wrote:
> >> >> Hi Thierry,
> >> >>
> >> >> On Tue, Apr 22, 2014 at 2:03 PM, Thierry Reding
> >> >> <thierry.reding@gmail.com> wrote:
> >> >> > On Tue, Apr 22, 2014 at 04:09:13AM +0530, Ajay Kumar wrote:
> >> >> >> Register exynos_dp_panel before the list of exynos crtcs and
> >> >> >> connectors are probed.
> >> >> >>
> >> >> >> This is needed because exynos_dp_panel should be registered to
> >> >> >> the drm_panel list via panel-exynos-dp probe, i.e much before
> >> >> >> exynos_dp_bind calls of_drm_find_panel().
> >> >> >>
> >> >> >> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
> >> >> >> ---
> >> >> >> Changes since V1:
> >> >> >>       Added platform_driver_unregister(&exynos_dp_panel_driver) to
> >> >> >>       exynos_drm_platform_remove as per Jingoo Han's correction
> >> >> >>
> >> >> >>  drivers/gpu/drm/exynos/exynos_drm_drv.c |   15 +++++++++++++++
> >> >> >>  drivers/gpu/drm/exynos/exynos_drm_drv.h |    1 +
> >> >> >>  2 files changed, 16 insertions(+)
> >> >> >>
> >> >> >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >> >> >> index 1d653f8..2db7f67 100644
> >> >> >> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >> >> >> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >> >> >> @@ -530,12 +530,23 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
> >> >> >>               goto err_unregister_ipp_drv;
> >> >> >>  #endif
> >> >> >>
> >> >> >> +#ifdef CONFIG_DRM_PANEL_EXYNOS_DP
> >> >> >> +     ret = platform_driver_register(&exynos_dp_panel_driver);
> >> >> >> +     if (ret < 0)
> >> >> >> +             goto err_unregister_dp_panel;
> >> >> >> +#endif
> >> >> >
> >> >> > No, this is not how you're supposed to use DRM panel drivers. The idea
> >> >> > is that you write a standalone driver for a given panel.
> >> >> >
> >> >> > What you do here has a number of problems. For one it's a driver that's
> >> >> > tightly coupled to Exynos SoCs. But if I have a different SoC that uses
> >> >> > the same panel I want to be able to use the same driver, and not have to
> >> >> > rewrite the driver for my SoC.
> >> >> >
> >> >> > Another problem is that you're assuming here that the driver is built in
> >> >> > and it will break if you try to build either Exynos DRM or the panel
> >> >> > driver as a module. This is perhaps nothing you care about right now,
> >> >> > but eventually people will want to ship a single kernel that can run on
> >> >> > a number of SoCs. But if we keep adding things like this, that kernel
> >> >> > will keep growing in size until it no longer fits in any kind of memory.
> >> >> >
> >> >> > Thierry
> >> >>
> >> >> I completely agree with you in this!
> >> >>
> >> >> Yes, this is not acceptable, but I want to know an "acceptable"
> >> >> workaround for the situation below:
> >> >> I register the driver using module_init().
> >> >> And, exynos_drm gets probed much before the panel driver probe happens.
> >> >> So, the panel driver hasn't probed yet, but exynos_dp via exynos_drm
> >> >> tries to call
> >> >> "of_drm_find_panel" which always returns NULL.
> >> >
> >> > That's a situation that your driver needs to be able to deal with. The
> >> > driver registration order doesn't matter one bit. It may happen to work
> >> > most of the time, but as soon as one of the resources that your panel
> >> > driver needs isn't there when the panel is probed, then it won't be
> >> > registered and of_drm_find_panel() will still return NULL.
> >> >
> >> > Usually the right thing to do in that case would be to return (and
> >> > propagate) -EPROBE_DEFER so that your driver's probe is deferred and
> >> > retried when other drivers have been probed. That way it should
> >> > eventually get a non-NULL panel.
> >>
> >> So I just gave this (drm_panel + probe deferring) a shot on exynos,
> >> and correctly reacting to -EPROBE_DEFER postpones DP initialization by
> >> approximately 1.5 second. Is there a good way to handle that? As it
> >> stands, this isn't usable.
> >
> > How much is 1.5 seconds compared to the overall boot time of the device?
> 
> 1.5s is 15-20% of my boot time (if you count the boot time from
> firmware start to login prompt, otherwise it's more). Note that on
> other platforms, we've seen this take as much as 5 or 6s, but for the
> exynos case it is "only" 1.5s.
> 
> > What exactly is causing this 1.5 second delay?
> 
> A regulator isn't ready, and then drm_panel returns defer. Then the
> whole drm driver init is deferred.

That is the correct way to do this currently. It's unfortunate that it
causes such long delays.

> > This really is a fundamental issue with deferred probing and the issue
> > has come up several times in the past. A couple of possible solutions
> > have been proposed, with the latest being here[0] I think. That ended in
> > a bit of a debacle, unfortunately, but on of the outcomes was that a lot
> > of the ordering problems could be fixed by using phandle references to
> > track dependencies. I'm not aware of anyone working on that right now,
> > presumably because everyone is busy getting features merged rather than
> > optimizing boot speed.
> 
> Yes, I don't believe boot time ordering will ever happen upstream,

Maybe explicit boot time ordering won't happen upstream, but I don't
think anyone would object if you posted patches that decrease boot time
significantly, provided that they're still a generic solution. Tracking
dependencies parse from a device tree sounds like an option that would
be acceptable.

> but then the current implementation with EPROBE_DEFER isn't usable either.

"Isn't usable" is pretty strong. It does get you a working panel, right?
It's perhaps not optimized, but it gives you something that's guaranteed
to work (unless you have an actual bug somewhere), and something that
works in the general case, for everyone.

> Any ideas? ATM it seems like the only way out is to just write my
> own dt format for the panel and ignore drm_panel.

Well, since probe order is currently dependent on device instantiation
order, one possibility would be to rearrange the DT nodes in a way that
dependencies are listed first. So in your case I'd expect the regulator
provider to be listed first, followed by the panel and finally the "DRM"
device. Providing none of them trigger deferred probing themselves they
should probe in exactly that order.

Of course there are no guarantees that the probing order will stay that
way forever and you will only be solving this problem for one device at
a time rather than in general (which is the primary reason for deferred
probing in the first place).

One other possibility I guess would be not to rely on deferred probing
but to enable DRM/KMS to hotplug panels. Theoretically this should be
possible already but it comes at a cost. If I'm not mistaken that's what
Boris implemented for the Atmel HLCDC driver. However if the panel isn't
available at DRM driver ->load() time, then it relies on the hotplugging
mechanism to detect the panel later on, which can itself introduce more
delays. I suspect this could be improved in various ways, but one of the
more complicated issues is that it causes problems when you want to use
the framebuffer console. The issue is that the fbdev helper code is set
up very early and if no outputs with valid modes are found it will
default to a 1024x768 resolution for the framebuffer console. At the
same time it will prevent any outputs that are hotplugged later on to
increase the resolution again. This works fine for monitors which
usually support a number of different modes (1024x768 being standard
enough that probably every monitor in existence supports it). But for
panels this won't work, since they usually support only a single mode.

There are a number of ways that this could be solved I guess. One would
be to defer fbdev creation itself until all outputs have been detected.
This could prove difficult because it may not always be possible to
determine which outputs to wait for. Another solution would be to allow
the framebuffer console to be resized, but I've been told in the past
that it's impractical if not impossible.

Of course if you don't need a framebuffer console (you could use kmscon
or none at all), then things should work for you if you simply consider
the panel as hotpluggable monitor.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH V2 4/9] drm/exynos: add exynos_dp_panel driver registration to drm driver
  2014-08-21  9:27               ` Ajay kumar
@ 2014-08-21  9:37                 ` Thierry Reding
  0 siblings, 0 replies; 58+ messages in thread
From: Thierry Reding @ 2014-08-21  9:37 UTC (permalink / raw)
  To: Ajay kumar
  Cc: linux-samsung-soc, Sean Paul, sunil joshi, dri-devel,
	Prashanth G, Ajay Kumar


[-- Attachment #1.1: Type: text/plain, Size: 6901 bytes --]

On Thu, Aug 21, 2014 at 02:57:21PM +0530, Ajay kumar wrote:
> On Thu, Aug 21, 2014 at 1:55 PM, Stéphane Marchesin
> <stephane.marchesin@gmail.com> wrote:
> > On Thu, Aug 21, 2014 at 12:36 AM, Thierry Reding
> > <thierry.reding@gmail.com> wrote:
> >> On Tue, Aug 19, 2014 at 09:02:39PM -0700, Stéphane Marchesin wrote:
> >>> On Tue, Apr 22, 2014 at 8:26 AM, Thierry Reding
> >>> <thierry.reding@gmail.com> wrote:
> >>> > On Tue, Apr 22, 2014 at 08:33:23PM +0530, Ajay kumar wrote:
> >>> >> Hi Thierry,
> >>> >>
> >>> >> On Tue, Apr 22, 2014 at 2:03 PM, Thierry Reding
> >>> >> <thierry.reding@gmail.com> wrote:
> >>> >> > On Tue, Apr 22, 2014 at 04:09:13AM +0530, Ajay Kumar wrote:
> >>> >> >> Register exynos_dp_panel before the list of exynos crtcs and
> >>> >> >> connectors are probed.
> >>> >> >>
> >>> >> >> This is needed because exynos_dp_panel should be registered to
> >>> >> >> the drm_panel list via panel-exynos-dp probe, i.e much before
> >>> >> >> exynos_dp_bind calls of_drm_find_panel().
> >>> >> >>
> >>> >> >> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com>
> >>> >> >> ---
> >>> >> >> Changes since V1:
> >>> >> >>       Added platform_driver_unregister(&exynos_dp_panel_driver) to
> >>> >> >>       exynos_drm_platform_remove as per Jingoo Han's correction
> >>> >> >>
> >>> >> >>  drivers/gpu/drm/exynos/exynos_drm_drv.c |   15 +++++++++++++++
> >>> >> >>  drivers/gpu/drm/exynos/exynos_drm_drv.h |    1 +
> >>> >> >>  2 files changed, 16 insertions(+)
> >>> >> >>
> >>> >> >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >>> >> >> index 1d653f8..2db7f67 100644
> >>> >> >> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >>> >> >> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >>> >> >> @@ -530,12 +530,23 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
> >>> >> >>               goto err_unregister_ipp_drv;
> >>> >> >>  #endif
> >>> >> >>
> >>> >> >> +#ifdef CONFIG_DRM_PANEL_EXYNOS_DP
> >>> >> >> +     ret = platform_driver_register(&exynos_dp_panel_driver);
> >>> >> >> +     if (ret < 0)
> >>> >> >> +             goto err_unregister_dp_panel;
> >>> >> >> +#endif
> >>> >> >
> >>> >> > No, this is not how you're supposed to use DRM panel drivers. The idea
> >>> >> > is that you write a standalone driver for a given panel.
> >>> >> >
> >>> >> > What you do here has a number of problems. For one it's a driver that's
> >>> >> > tightly coupled to Exynos SoCs. But if I have a different SoC that uses
> >>> >> > the same panel I want to be able to use the same driver, and not have to
> >>> >> > rewrite the driver for my SoC.
> >>> >> >
> >>> >> > Another problem is that you're assuming here that the driver is built in
> >>> >> > and it will break if you try to build either Exynos DRM or the panel
> >>> >> > driver as a module. This is perhaps nothing you care about right now,
> >>> >> > but eventually people will want to ship a single kernel that can run on
> >>> >> > a number of SoCs. But if we keep adding things like this, that kernel
> >>> >> > will keep growing in size until it no longer fits in any kind of memory.
> >>> >> >
> >>> >> > Thierry
> >>> >>
> >>> >> I completely agree with you in this!
> >>> >>
> >>> >> Yes, this is not acceptable, but I want to know an "acceptable"
> >>> >> workaround for the situation below:
> >>> >> I register the driver using module_init().
> >>> >> And, exynos_drm gets probed much before the panel driver probe happens.
> >>> >> So, the panel driver hasn't probed yet, but exynos_dp via exynos_drm
> >>> >> tries to call
> >>> >> "of_drm_find_panel" which always returns NULL.
> >>> >
> >>> > That's a situation that your driver needs to be able to deal with. The
> >>> > driver registration order doesn't matter one bit. It may happen to work
> >>> > most of the time, but as soon as one of the resources that your panel
> >>> > driver needs isn't there when the panel is probed, then it won't be
> >>> > registered and of_drm_find_panel() will still return NULL.
> >>> >
> >>> > Usually the right thing to do in that case would be to return (and
> >>> > propagate) -EPROBE_DEFER so that your driver's probe is deferred and
> >>> > retried when other drivers have been probed. That way it should
> >>> > eventually get a non-NULL panel.
> >>>
> >>> So I just gave this (drm_panel + probe deferring) a shot on exynos,
> >>> and correctly reacting to -EPROBE_DEFER postpones DP initialization by
> >>> approximately 1.5 second. Is there a good way to handle that? As it
> >>> stands, this isn't usable.
> >>
> >> How much is 1.5 seconds compared to the overall boot time of the device?
> >
> > 1.5s is 15-20% of my boot time (if you count the boot time from
> > firmware start to login prompt, otherwise it's more). Note that on
> > other platforms, we've seen this take as much as 5 or 6s, but for the
> > exynos case it is "only" 1.5s.
> >
> >> What exactly is causing this 1.5 second delay?
> >
> > A regulator isn't ready, and then drm_panel returns defer. Then the
> > whole drm driver init is deferred.
> >
> >>
> >> This really is a fundamental issue with deferred probing and the issue
> >> has come up several times in the past. A couple of possible solutions
> >> have been proposed, with the latest being here[0] I think. That ended in
> >> a bit of a debacle, unfortunately, but on of the outcomes was that a lot
> >> of the ordering problems could be fixed by using phandle references to
> >> track dependencies. I'm not aware of anyone working on that right now,
> >> presumably because everyone is busy getting features merged rather than
> >> optimizing boot speed.
> >
> > Yes, I don't believe boot time ordering will ever happen upstream, but
> > then the current implementation with EPROBE_DEFER isn't usable either.
> > Any ideas? ATM it seems like the only way out is to just write my
> > own dt format for the panel and ignore drm_panel.
> Something like this:
> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/gpu/drm/exynos/exynos_dp_core.c
> 
> With this way also, we would expect the regulator to come up early(before drm).

Well, that's pretty much what Stéphane proposed. You don't use DRM panel
at all and instead rely on some "platform data" to get the information
that you need. You're basically throwing everything we've been working
towards with DT for the past three years out the door. Also as soon as
you encounter the first device that's not covered by lcd_vdd, vcd_led
regulators and lcd_en, led_en GPIOs you have a problem. There's also no
reuse across SoCs whatsoever.

Really, we should be finding ways to fix problems, not work around them
in every driver because the real fixes look too daunting at the moment.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

end of thread, other threads:[~2014-08-21  9:37 UTC | newest]

Thread overview: 58+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-04-21 22:39 [PATCH V2 0/9] drm: exynos: few patches to enhance bridge chip support Ajay Kumar
2014-04-21 22:39 ` [PATCH V2 1/9] drm/exynos: dp: support hotplug detection via GPIO Ajay Kumar
2014-04-22  7:13   ` Jingoo Han
2014-05-09  0:57     ` Jingoo Han
2014-04-21 22:39 ` [PATCH V2 2/9] drm/panel: add pre_enable and post_disable routines Ajay Kumar
2014-04-22  8:19   ` Thierry Reding
2014-04-22 14:36     ` Ajay kumar
2014-04-23  7:29       ` Daniel Vetter
2014-04-23  7:42         ` Thierry Reding
2014-04-23 19:26           ` Ajay kumar
2014-04-24 18:31             ` Thierry Reding
2014-04-25  6:04               ` Ajay kumar
2014-04-25 18:16                 ` Ajay kumar
2014-04-29 12:15                   ` Ajay kumar
2014-04-23 19:14         ` Ajay kumar
2014-04-21 22:39 ` [PATCH V2 3/9] drm/panel: Add driver for exynos_dp based panels Ajay Kumar
2014-04-22  7:22   ` Jingoo Han
2014-04-22 14:37     ` Ajay kumar
2014-04-22  8:26   ` Thierry Reding
2014-04-22 14:53     ` Ajay kumar
2014-04-22 15:16       ` Thierry Reding
2014-04-21 22:39 ` [PATCH V2 4/9] drm/exynos: add exynos_dp_panel driver registration to drm driver Ajay Kumar
2014-04-22  8:33   ` Thierry Reding
2014-04-22 15:03     ` Ajay kumar
2014-04-22 15:26       ` Thierry Reding
2014-08-20  4:02         ` Stéphane Marchesin
2014-08-21  7:36           ` Thierry Reding
2014-08-21  8:25             ` Stéphane Marchesin
2014-08-21  9:27               ` Ajay kumar
2014-08-21  9:37                 ` Thierry Reding
2014-08-21  9:28               ` Thierry Reding
2014-04-21 22:39 ` [PATCH V2 5/9] drm/exynos: dp: modify driver to support drm_panel Ajay Kumar
2014-04-22  8:37   ` Thierry Reding
2014-04-21 22:39 ` [PATCH V2 6/9] drm/bridge: ptn3460: enable polling based detection Ajay Kumar
2014-04-22  8:43   ` Thierry Reding
2014-04-21 22:39 ` [PATCH V2 7/9] drm/bridge: ptn3460: add drm_panel controls Ajay Kumar
2014-04-22  8:55   ` Thierry Reding
2014-04-22 11:34   ` Rob Clark
2014-04-22 13:53     ` Daniel Vetter
2014-04-23 18:56     ` Ajay kumar
2014-04-23 19:02       ` Ajay kumar
2014-04-24 16:11         ` Rob Clark
2014-04-24 16:55           ` Ajay kumar
2014-04-24 17:25             ` Rob Clark
2014-04-24 17:38               ` Ajay kumar
2014-04-25  6:10                 ` Ajay kumar
2014-04-25  6:17                   ` Ajay kumar
2014-04-27 12:55                     ` Daniel Vetter
2014-04-28 13:08                       ` Ajay kumar
2014-04-29 13:14                         ` Rob Clark
2014-04-21 22:39 ` [PATCH V2 8/9] drm/bridge: Add PS8622 bridge driver Ajay Kumar
2014-04-22  8:43   ` Jingoo Han
2014-04-22 13:57     ` Ajay kumar
2014-04-22 13:31   ` Sean Paul
2014-04-22 14:03     ` Ajay kumar
2014-04-21 22:39 ` [PATCH V2 9/9] drm/exynos: Add ps8622 lvds bridge discovery to DP driver Ajay Kumar
2014-04-22  9:27   ` Thierry Reding
2014-04-22 17:20     ` Ajay kumar

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.