All of lore.kernel.org
 help / color / mirror / Atom feed
From: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
To: airlied-cv59FeDIM0c@public.gmane.org,
	mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org
Cc: robdclark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
Subject: [PATCH RFC v2 08/12] drm/rockchip: lvds: register a bridge when no panel is set
Date: Wed,  1 Apr 2015 12:09:42 +0200	[thread overview]
Message-ID: <1427882986-19110-9-git-send-email-heiko@sntech.de> (raw)
In-Reply-To: <1427882986-19110-1-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>

On socs using the lvds components it also controls the use of the
general rgb outputs and must thus be configured for things like
external encoders.

Therefore register a drm_bridge in this case and try to find
the encoder in the output port.

Signed-off-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
---
 drivers/gpu/drm/rockchip/rockchip_lvds.c | 255 ++++++++++++++++++++++++++++---
 1 file changed, 233 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
index 657609e..5ffd70a 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -43,6 +43,9 @@
 #define encoder_to_lvds(c) \
 		container_of(c, struct rockchip_lvds, encoder)
 
+#define bridge_to_lvds(c) \
+		container_of(c, struct rockchip_lvds, bridge)
+
 /*
  * @grf_offset: offset inside the grf regmap for setting the rockchip lvds
  */
@@ -68,6 +71,8 @@ struct rockchip_lvds {
 	struct drm_panel *panel;
 	struct drm_connector connector;
 	struct drm_encoder encoder;
+	struct drm_bridge bridge;
+	struct drm_encoder *ext_encoder;
 
 	struct mutex suspend_lock;
 	int suspend;
@@ -248,11 +253,10 @@ rockchip_lvds_encoder_mode_fixup(struct drm_encoder *encoder,
 	return true;
 }
 
-static void rockchip_lvds_encoder_mode_set(struct drm_encoder *encoder,
-					  struct drm_display_mode *mode,
-					  struct drm_display_mode *adjusted)
+static void rockchip_lvds_mode_set(struct rockchip_lvds *lvds,
+				   struct drm_display_mode *mode,
+				   struct drm_display_mode *adjusted)
 {
-	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
 	u32 h_bp = mode->htotal - mode->hsync_start;
 	u8 pin_hsync = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0;
 	u8 pin_dclk = (mode->flags & DRM_MODE_FLAG_PCSYNC) ? 1 : 0;
@@ -347,32 +351,52 @@ static void rockchip_lvds_encoder_mode_set(struct drm_encoder *encoder,
 	dsb();
 }
 
-static void rockchip_lvds_encoder_prepare(struct drm_encoder *encoder)
+static void rockchip_lvds_encoder_mode_set(struct drm_encoder *encoder,
+					   struct drm_display_mode *mode,
+					   struct drm_display_mode *adjusted)
+{
+	rockchip_lvds_mode_set(encoder_to_lvds(encoder), mode, adjusted);
+}
+
+static int rockchip_lvds_set_vop_source(struct rockchip_lvds *lvds,
+					struct drm_encoder *encoder)
 {
-	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
 	u32 val;
 	int ret;
 
-	ret = rockchip_drm_crtc_mode_config(encoder->crtc,
-						lvds->connector.connector_type,
-						ROCKCHIP_OUT_MODE_P888);
-	if (ret < 0) {
-		dev_err(lvds->dev, "Could not set crtc mode config: %d\n", ret);
-		return;
-	}
-
 	ret = rockchip_drm_encoder_get_mux_id(lvds->dev->of_node, encoder);
 	if (ret < 0)
-		return;
+		return ret;
 
 	if (ret)
 		val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT |
 		      (RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16);
 	else
 		val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16;
+
 	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con6, val);
-	if (ret != 0) {
-		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void rockchip_lvds_encoder_prepare(struct drm_encoder *encoder)
+{
+	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
+	int ret;
+
+	ret = rockchip_drm_crtc_mode_config(encoder->crtc,
+						lvds->connector.connector_type,
+						ROCKCHIP_OUT_MODE_P888);
+	if (ret < 0) {
+		dev_err(lvds->dev, "Could not set crtc mode config: %d\n", ret);
+		return;
+	}
+
+	ret = rockchip_lvds_set_vop_source(lvds, encoder);
+	if (ret < 0) {
+		dev_err(lvds->dev, "Could not set vop source: %d\n", ret);
 		return;
 	}
 }
@@ -405,6 +429,97 @@ static struct drm_encoder_funcs rockchip_lvds_encoder_funcs = {
 	.destroy = rockchip_lvds_encoder_destroy,
 };
 
+static void rockchip_lvds_bridge_mode_set(struct drm_bridge *bridge,
+					  struct drm_display_mode *mode,
+					  struct drm_display_mode *adjusted)
+{
+	rockchip_lvds_mode_set(bridge_to_lvds(bridge), mode, adjusted);
+}
+
+static void rockchip_lvds_bridge_pre_enable(struct drm_bridge *bridge)
+{
+}
+
+/*
+ * post_disable is called right after encoder prepare, so do lvds and crtc
+ * mode config here.
+ */
+static void rockchip_lvds_bridge_post_disable(struct drm_bridge *bridge)
+{
+	struct rockchip_lvds *lvds = bridge_to_lvds(bridge);
+	struct drm_connector *connector;
+	int ret, connector_type = DRM_MODE_CONNECTOR_Unknown;
+
+	if (!bridge->encoder->crtc)
+		return;
+
+	list_for_each_entry(connector, &bridge->dev->mode_config.connector_list,
+			head) {
+		if (connector->encoder == bridge->encoder)
+			connector_type = connector->connector_type;
+	}
+
+	ret = rockchip_drm_crtc_mode_config(bridge->encoder->crtc,
+						connector_type,
+						ROCKCHIP_OUT_MODE_P888);
+	if (ret < 0) {
+		dev_err(lvds->dev, "Could not set crtc mode config: %d\n", ret);
+		return;
+	}
+
+	ret = rockchip_lvds_set_vop_source(lvds, bridge->encoder);
+	if (ret < 0) {
+		dev_err(lvds->dev, "Could not set vop source: %d\n", ret);
+		return;
+	}
+}
+
+static void rockchip_lvds_bridge_enable(struct drm_bridge *bridge)
+{
+	struct rockchip_lvds *lvds = bridge_to_lvds(bridge);
+	int ret;
+
+	mutex_lock(&lvds->suspend_lock);
+
+	if (!lvds->suspend)
+		goto out;
+
+	ret = rockchip_lvds_poweron(lvds);
+	if (ret < 0) {
+		dev_err(lvds->dev, "could not enable lvds\n");
+		goto out;
+	}
+
+	lvds->suspend = false;
+
+out:
+	mutex_unlock(&lvds->suspend_lock);
+}
+
+static void rockchip_lvds_bridge_disable(struct drm_bridge *bridge)
+{
+	struct rockchip_lvds *lvds = bridge_to_lvds(bridge);
+
+	mutex_lock(&lvds->suspend_lock);
+
+	if (lvds->suspend)
+		goto out;
+
+	rockchip_lvds_poweroff(lvds);
+	lvds->suspend = true;
+
+out:
+	mutex_unlock(&lvds->suspend_lock);
+}
+
+static struct drm_bridge_funcs rockchip_lvds_bridge_funcs = {
+	.mode_set = rockchip_lvds_bridge_mode_set,
+	.enable = rockchip_lvds_bridge_enable,
+	.disable = rockchip_lvds_bridge_disable,
+	.pre_enable = rockchip_lvds_bridge_pre_enable,
+	.post_disable = rockchip_lvds_bridge_post_disable,
+};
+
 static struct rockchip_lvds_soc_data rk3288_lvds_data = {
 	.grf_soc_con6 = 0x025c,
 	.grf_soc_con7 = 0x0260,
@@ -430,6 +545,35 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
 
 	lvds->drm_dev = drm_dev;
 
+	if (!lvds->panel) {
+		struct drm_bridge *bridge = &lvds->bridge;
+
+		if (!lvds->ext_encoder->of_node)
+			return -ENODEV;
+
+		ret = component_bind_all(dev, drm_dev);
+		if (ret < 0)
+			return ret;
+
+		/**
+		 * Override any possible crtcs set by the encoder itself,
+		 * as they are connected to the lvds instead.
+		 */
+		encoder = of_drm_find_encoder(lvds->ext_encoder->of_node);
+		encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+								dev->of_node);
+
+		encoder->bridge = bridge;
+		bridge->encoder = encoder;
+		ret = drm_bridge_attach(drm_dev, bridge);
+		if (ret < 0) {
+			component_unbind_all(dev, drm_dev);
+			return ret;
+		}
+
+		return 0;
+	}
+
 	encoder = &lvds->encoder;
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
 							     dev->of_node);
@@ -482,6 +626,7 @@ static void rockchip_lvds_unbind(struct device *dev, struct device *master,
 				void *data)
 {
 	struct rockchip_lvds *lvds = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
 
 	if (lvds->panel) {
 		rockchip_lvds_encoder_dpms(&lvds->encoder, DRM_MODE_DPMS_OFF);
@@ -490,6 +635,8 @@ static void rockchip_lvds_unbind(struct device *dev, struct device *master,
 
 		drm_connector_cleanup(&lvds->connector);
 		drm_encoder_cleanup(&lvds->encoder);
+	} else {
+		component_unbind_all(dev, drm_dev);
 	}
 }
 static const struct component_ops rockchip_lvds_component_ops = {
@@ -497,6 +644,26 @@ static const struct component_ops rockchip_lvds_component_ops = {
 	.unbind = rockchip_lvds_unbind,
 };
 
+static int compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static int rockchip_lvds_master_bind(struct device *dev)
+{
+	return 0;
+}
+
+static void rockchip_lvds_master_unbind(struct device *dev)
+{
+	/* do nothing */
+}
+
+static const struct component_master_ops rockchip_lvds_master_ops = {
+	.bind = rockchip_lvds_master_bind,
+	.unbind = rockchip_lvds_master_unbind,
+};
+
 static int rockchip_lvds_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -596,18 +763,58 @@ static int rockchip_lvds_probe(struct platform_device *pdev)
 
 	if (!output_node) {
 		dev_err(&pdev->dev, "no output defined\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto err_unprepare_pclk;
 	}
 
 	lvds->panel = of_drm_find_panel(output_node);
-	of_node_put(output_node);
 	if (!lvds->panel) {
-		dev_err(&pdev->dev, "panel not found\n");
-		return -EPROBE_DEFER;
+		struct drm_encoder *encoder;
+		struct component_match *match = NULL;
+
+		/* Try to find an encoder in the output node */
+		encoder = of_drm_find_encoder(output_node);
+		if (!encoder) {
+			dev_err(&pdev->dev, "neither panel nor encoder found\n");
+			of_node_put(output_node);
+			ret = -EPROBE_DEFER;
+			goto err_unprepare_pclk;
+		}
+
+		lvds->ext_encoder = encoder;
+
+		component_match_add(dev, &match, compare_of, output_node);
+		of_node_put(output_node);
+
+		lvds->bridge.funcs = &rockchip_lvds_bridge_funcs;
+		lvds->bridge.of_node = dev->of_node;
+		ret = drm_bridge_add(&lvds->bridge);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to add bridge %d\n", ret);
+			goto err_unprepare_pclk;
+		}
+
+		ret = component_master_add_with_match(dev,
+						      &rockchip_lvds_master_ops,
+						      match);
+		if (ret < 0)
+			goto err_bridge_remove;
+	} else {
+		of_node_put(output_node);
 	}
 
-	return component_add(&pdev->dev, &rockchip_lvds_component_ops);
+	ret = component_add(&pdev->dev, &rockchip_lvds_component_ops);
+	if (ret < 0)
+		goto err_master_remove;
+
+	return 0;
 
+err_master_remove:
+	if (!lvds->panel)
+		component_master_del(&pdev->dev, &rockchip_lvds_master_ops);
+err_bridge_remove:
+	if (!lvds->panel)
+		drm_bridge_remove(&lvds->bridge);
 err_unprepare_pclk:
 	clk_unprepare(lvds->pclk);
 	return ret;
@@ -618,6 +825,10 @@ static int rockchip_lvds_remove(struct platform_device *pdev)
 	struct rockchip_lvds *lvds = dev_get_drvdata(&pdev->dev);
 
 	component_del(&pdev->dev, &rockchip_lvds_component_ops);
+	if (!lvds->panel) {
+		component_master_del(&pdev->dev, &rockchip_lvds_master_ops);
+		drm_bridge_remove(&lvds->bridge);
+	}
 
 	clk_unprepare(lvds->pclk);
 
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

WARNING: multiple messages have this Message-ID (diff)
From: heiko@sntech.de (Heiko Stuebner)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH RFC v2 08/12] drm/rockchip: lvds: register a bridge when no panel is set
Date: Wed,  1 Apr 2015 12:09:42 +0200	[thread overview]
Message-ID: <1427882986-19110-9-git-send-email-heiko@sntech.de> (raw)
In-Reply-To: <1427882986-19110-1-git-send-email-heiko@sntech.de>

On socs using the lvds components it also controls the use of the
general rgb outputs and must thus be configured for things like
external encoders.

Therefore register a drm_bridge in this case and try to find
the encoder in the output port.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 drivers/gpu/drm/rockchip/rockchip_lvds.c | 255 ++++++++++++++++++++++++++++---
 1 file changed, 233 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
index 657609e..5ffd70a 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -43,6 +43,9 @@
 #define encoder_to_lvds(c) \
 		container_of(c, struct rockchip_lvds, encoder)
 
+#define bridge_to_lvds(c) \
+		container_of(c, struct rockchip_lvds, bridge)
+
 /*
  * @grf_offset: offset inside the grf regmap for setting the rockchip lvds
  */
@@ -68,6 +71,8 @@ struct rockchip_lvds {
 	struct drm_panel *panel;
 	struct drm_connector connector;
 	struct drm_encoder encoder;
+	struct drm_bridge bridge;
+	struct drm_encoder *ext_encoder;
 
 	struct mutex suspend_lock;
 	int suspend;
@@ -248,11 +253,10 @@ rockchip_lvds_encoder_mode_fixup(struct drm_encoder *encoder,
 	return true;
 }
 
-static void rockchip_lvds_encoder_mode_set(struct drm_encoder *encoder,
-					  struct drm_display_mode *mode,
-					  struct drm_display_mode *adjusted)
+static void rockchip_lvds_mode_set(struct rockchip_lvds *lvds,
+				   struct drm_display_mode *mode,
+				   struct drm_display_mode *adjusted)
 {
-	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
 	u32 h_bp = mode->htotal - mode->hsync_start;
 	u8 pin_hsync = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0;
 	u8 pin_dclk = (mode->flags & DRM_MODE_FLAG_PCSYNC) ? 1 : 0;
@@ -347,32 +351,52 @@ static void rockchip_lvds_encoder_mode_set(struct drm_encoder *encoder,
 	dsb();
 }
 
-static void rockchip_lvds_encoder_prepare(struct drm_encoder *encoder)
+static void rockchip_lvds_encoder_mode_set(struct drm_encoder *encoder,
+					   struct drm_display_mode *mode,
+					   struct drm_display_mode *adjusted)
+{
+	rockchip_lvds_mode_set(encoder_to_lvds(encoder), mode, adjusted);
+}
+
+static int rockchip_lvds_set_vop_source(struct rockchip_lvds *lvds,
+					struct drm_encoder *encoder)
 {
-	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
 	u32 val;
 	int ret;
 
-	ret = rockchip_drm_crtc_mode_config(encoder->crtc,
-						lvds->connector.connector_type,
-						ROCKCHIP_OUT_MODE_P888);
-	if (ret < 0) {
-		dev_err(lvds->dev, "Could not set crtc mode config: %d\n", ret);
-		return;
-	}
-
 	ret = rockchip_drm_encoder_get_mux_id(lvds->dev->of_node, encoder);
 	if (ret < 0)
-		return;
+		return ret;
 
 	if (ret)
 		val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT |
 		      (RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16);
 	else
 		val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16;
+
 	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con6, val);
-	if (ret != 0) {
-		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void rockchip_lvds_encoder_prepare(struct drm_encoder *encoder)
+{
+	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
+	int ret;
+
+	ret = rockchip_drm_crtc_mode_config(encoder->crtc,
+						lvds->connector.connector_type,
+						ROCKCHIP_OUT_MODE_P888);
+	if (ret < 0) {
+		dev_err(lvds->dev, "Could not set crtc mode config: %d\n", ret);
+		return;
+	}
+
+	ret = rockchip_lvds_set_vop_source(lvds, encoder);
+	if (ret < 0) {
+		dev_err(lvds->dev, "Could not set vop source: %d\n", ret);
 		return;
 	}
 }
@@ -405,6 +429,97 @@ static struct drm_encoder_funcs rockchip_lvds_encoder_funcs = {
 	.destroy = rockchip_lvds_encoder_destroy,
 };
 
+static void rockchip_lvds_bridge_mode_set(struct drm_bridge *bridge,
+					  struct drm_display_mode *mode,
+					  struct drm_display_mode *adjusted)
+{
+	rockchip_lvds_mode_set(bridge_to_lvds(bridge), mode, adjusted);
+}
+
+static void rockchip_lvds_bridge_pre_enable(struct drm_bridge *bridge)
+{
+}
+
+/*
+ * post_disable is called right after encoder prepare, so do lvds and crtc
+ * mode config here.
+ */
+static void rockchip_lvds_bridge_post_disable(struct drm_bridge *bridge)
+{
+	struct rockchip_lvds *lvds = bridge_to_lvds(bridge);
+	struct drm_connector *connector;
+	int ret, connector_type = DRM_MODE_CONNECTOR_Unknown;
+
+	if (!bridge->encoder->crtc)
+		return;
+
+	list_for_each_entry(connector, &bridge->dev->mode_config.connector_list,
+			head) {
+		if (connector->encoder == bridge->encoder)
+			connector_type = connector->connector_type;
+	}
+
+	ret = rockchip_drm_crtc_mode_config(bridge->encoder->crtc,
+						connector_type,
+						ROCKCHIP_OUT_MODE_P888);
+	if (ret < 0) {
+		dev_err(lvds->dev, "Could not set crtc mode config: %d\n", ret);
+		return;
+	}
+
+	ret = rockchip_lvds_set_vop_source(lvds, bridge->encoder);
+	if (ret < 0) {
+		dev_err(lvds->dev, "Could not set vop source: %d\n", ret);
+		return;
+	}
+}
+
+static void rockchip_lvds_bridge_enable(struct drm_bridge *bridge)
+{
+	struct rockchip_lvds *lvds = bridge_to_lvds(bridge);
+	int ret;
+
+	mutex_lock(&lvds->suspend_lock);
+
+	if (!lvds->suspend)
+		goto out;
+
+	ret = rockchip_lvds_poweron(lvds);
+	if (ret < 0) {
+		dev_err(lvds->dev, "could not enable lvds\n");
+		goto out;
+	}
+
+	lvds->suspend = false;
+
+out:
+	mutex_unlock(&lvds->suspend_lock);
+}
+
+static void rockchip_lvds_bridge_disable(struct drm_bridge *bridge)
+{
+	struct rockchip_lvds *lvds = bridge_to_lvds(bridge);
+
+	mutex_lock(&lvds->suspend_lock);
+
+	if (lvds->suspend)
+		goto out;
+
+	rockchip_lvds_poweroff(lvds);
+	lvds->suspend = true;
+
+out:
+	mutex_unlock(&lvds->suspend_lock);
+}
+
+static struct drm_bridge_funcs rockchip_lvds_bridge_funcs = {
+	.mode_set = rockchip_lvds_bridge_mode_set,
+	.enable = rockchip_lvds_bridge_enable,
+	.disable = rockchip_lvds_bridge_disable,
+	.pre_enable = rockchip_lvds_bridge_pre_enable,
+	.post_disable = rockchip_lvds_bridge_post_disable,
+};
+
 static struct rockchip_lvds_soc_data rk3288_lvds_data = {
 	.grf_soc_con6 = 0x025c,
 	.grf_soc_con7 = 0x0260,
@@ -430,6 +545,35 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
 
 	lvds->drm_dev = drm_dev;
 
+	if (!lvds->panel) {
+		struct drm_bridge *bridge = &lvds->bridge;
+
+		if (!lvds->ext_encoder->of_node)
+			return -ENODEV;
+
+		ret = component_bind_all(dev, drm_dev);
+		if (ret < 0)
+			return ret;
+
+		/**
+		 * Override any possible crtcs set by the encoder itself,
+		 * as they are connected to the lvds instead.
+		 */
+		encoder = of_drm_find_encoder(lvds->ext_encoder->of_node);
+		encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+								dev->of_node);
+
+		encoder->bridge = bridge;
+		bridge->encoder = encoder;
+		ret = drm_bridge_attach(drm_dev, bridge);
+		if (ret < 0) {
+			component_unbind_all(dev, drm_dev);
+			return ret;
+		}
+
+		return 0;
+	}
+
 	encoder = &lvds->encoder;
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
 							     dev->of_node);
@@ -482,6 +626,7 @@ static void rockchip_lvds_unbind(struct device *dev, struct device *master,
 				void *data)
 {
 	struct rockchip_lvds *lvds = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
 
 	if (lvds->panel) {
 		rockchip_lvds_encoder_dpms(&lvds->encoder, DRM_MODE_DPMS_OFF);
@@ -490,6 +635,8 @@ static void rockchip_lvds_unbind(struct device *dev, struct device *master,
 
 		drm_connector_cleanup(&lvds->connector);
 		drm_encoder_cleanup(&lvds->encoder);
+	} else {
+		component_unbind_all(dev, drm_dev);
 	}
 }
 static const struct component_ops rockchip_lvds_component_ops = {
@@ -497,6 +644,26 @@ static const struct component_ops rockchip_lvds_component_ops = {
 	.unbind = rockchip_lvds_unbind,
 };
 
+static int compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static int rockchip_lvds_master_bind(struct device *dev)
+{
+	return 0;
+}
+
+static void rockchip_lvds_master_unbind(struct device *dev)
+{
+	/* do nothing */
+}
+
+static const struct component_master_ops rockchip_lvds_master_ops = {
+	.bind = rockchip_lvds_master_bind,
+	.unbind = rockchip_lvds_master_unbind,
+};
+
 static int rockchip_lvds_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -596,18 +763,58 @@ static int rockchip_lvds_probe(struct platform_device *pdev)
 
 	if (!output_node) {
 		dev_err(&pdev->dev, "no output defined\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto err_unprepare_pclk;
 	}
 
 	lvds->panel = of_drm_find_panel(output_node);
-	of_node_put(output_node);
 	if (!lvds->panel) {
-		dev_err(&pdev->dev, "panel not found\n");
-		return -EPROBE_DEFER;
+		struct drm_encoder *encoder;
+		struct component_match *match = NULL;
+
+		/* Try to find an encoder in the output node */
+		encoder = of_drm_find_encoder(output_node);
+		if (!encoder) {
+			dev_err(&pdev->dev, "neither panel nor encoder found\n");
+			of_node_put(output_node);
+			ret = -EPROBE_DEFER;
+			goto err_unprepare_pclk;
+		}
+
+		lvds->ext_encoder = encoder;
+
+		component_match_add(dev, &match, compare_of, output_node);
+		of_node_put(output_node);
+
+		lvds->bridge.funcs = &rockchip_lvds_bridge_funcs;
+		lvds->bridge.of_node = dev->of_node;
+		ret = drm_bridge_add(&lvds->bridge);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to add bridge %d\n", ret);
+			goto err_unprepare_pclk;
+		}
+
+		ret = component_master_add_with_match(dev,
+						      &rockchip_lvds_master_ops,
+						      match);
+		if (ret < 0)
+			goto err_bridge_remove;
+	} else {
+		of_node_put(output_node);
 	}
 
-	return component_add(&pdev->dev, &rockchip_lvds_component_ops);
+	ret = component_add(&pdev->dev, &rockchip_lvds_component_ops);
+	if (ret < 0)
+		goto err_master_remove;
+
+	return 0;
 
+err_master_remove:
+	if (!lvds->panel)
+		component_master_del(&pdev->dev, &rockchip_lvds_master_ops);
+err_bridge_remove:
+	if (!lvds->panel)
+		drm_bridge_remove(&lvds->bridge);
 err_unprepare_pclk:
 	clk_unprepare(lvds->pclk);
 	return ret;
@@ -618,6 +825,10 @@ static int rockchip_lvds_remove(struct platform_device *pdev)
 	struct rockchip_lvds *lvds = dev_get_drvdata(&pdev->dev);
 
 	component_del(&pdev->dev, &rockchip_lvds_component_ops);
+	if (!lvds->panel) {
+		component_master_del(&pdev->dev, &rockchip_lvds_master_ops);
+		drm_bridge_remove(&lvds->bridge);
+	}
 
 	clk_unprepare(lvds->pclk);
 
-- 
2.1.4

  parent reply	other threads:[~2015-04-01 10:09 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-04-01 10:09 [PATCH RFC v2 00/12] drm/rockchip: add support for lvds controller and external encoders Heiko Stuebner
2015-04-01 10:09 ` Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 01/12] drm/encoder: add functionality to register encoders to a global list Heiko Stuebner
2015-04-01 10:09   ` Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 04/12] drm/components: add generic vga encoder driver Heiko Stuebner
2015-04-01 10:09   ` Heiko Stuebner
     [not found]   ` <1427882986-19110-5-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
2015-04-01 10:27     ` Russell King - ARM Linux
2015-04-01 10:27       ` Russell King - ARM Linux
2015-04-01 10:40       ` Heiko Stübner
2015-04-01 10:40         ` Heiko Stübner
2015-04-01 10:09 ` [PATCH RFC v2 06/12] dt-bindings: Add documentation for rockchip lvds Heiko Stuebner
2015-04-01 10:09   ` Heiko Stuebner
     [not found] ` <1427882986-19110-1-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
2015-04-01 10:09   ` [PATCH RFC v2 02/12] drm/connector: add functionality to register connectors to a global list Heiko Stuebner
2015-04-01 10:09     ` Heiko Stuebner
2015-04-01 10:09   ` [PATCH RFC v2 03/12] drm: add components subdirectory and infrastructure Heiko Stuebner
2015-04-01 10:09     ` Heiko Stuebner
2015-04-01 10:09   ` [PATCH RFC v2 05/12] drm/components: add generic vga connector driver Heiko Stuebner
2015-04-01 10:09     ` Heiko Stuebner
2015-04-01 10:09   ` [PATCH RFC v2 07/12] drm/rockchip: Add support for Rockchip Soc LVDS Heiko Stuebner
2015-04-01 10:09     ` Heiko Stuebner
2015-04-01 10:09   ` Heiko Stuebner [this message]
2015-04-01 10:09     ` [PATCH RFC v2 08/12] drm/rockchip: lvds: register a bridge when no panel is set Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 09/12] drm/rockchip: enable rgb output of vops for all other connectors Heiko Stuebner
2015-04-01 10:09   ` Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 10/12] ARM: dts: rockchip: add rk3288 lcdc0 pinmux settings Heiko Stuebner
2015-04-01 10:09   ` Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 11/12] ARM: dts: rockchip: add rk3288 lvds node Heiko Stuebner
2015-04-01 10:09   ` Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 12/12] ARM: dts: rockchip: add vga encoder and enable lvds on rk3288-firefly Heiko Stuebner
2015-04-01 10:09   ` Heiko Stuebner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1427882986-19110-9-git-send-email-heiko@sntech.de \
    --to=heiko-4mtyjxux2i+zqb+pc5nmwq@public.gmane.org \
    --cc=airlied-cv59FeDIM0c@public.gmane.org \
    --cc=devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=djkurtz-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org \
    --cc=dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org \
    --cc=laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org \
    --cc=linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org \
    --cc=linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org \
    --cc=mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org \
    --cc=robdclark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.