drm/rockchip: lvds: register a bridge when no panel is

A patch from »drm/rockchip: add support for lvds controller and external encoders« in state Rework for linux-kernel

From: Heiko Stuebner <heiko@...> Date: Sun, 25 Jan 2015 20:14:39 +0100

Commit-Message

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, an encoder can attach to. Signed-off-by: Heiko Stuebner <heiko@...>

Patch-Comment

.../devicetree/bindings/video/rockchip-lvds.txt | 8 +- drivers/gpu/drm/rockchip/rockchip_lvds.c | 167 ++++++++++++++++++--- 2 files changed, 153 insertions(+), 22 deletions(-)

Statistics

  • 153 lines added
  • 22 lines removed

Changes

---------- Documentation/devicetree/bindings/video/rockchip-lvds.txt -----------
index 1616d7e..6ab69b4 100644
@@ -15,8 +15,6 @@ Required properties:
- avdd3v3-supply: regulator phandle for 3.3V analog power
- rockchip,grf: phandle to the general register files syscon
-- rockchip,panel: phandle to a panel node as described by
- Documentation/devicetree/bindings/panel/*
- rockchip,data-mapping: should be "vesa" or "jeida",
This describes how the color bits are laid out in the
@@ -28,6 +26,12 @@ Required properties:
- ports: contain a port node with endpoint definitions as defined in
Documentation/devicetree/bindings/media/video-interfaces.txt.
+Optional properties:
+- rockchip,panel: phandle to a panel node as described by
+ Documentation/devicetree/bindings/panel/*
+ If no panel reference is set the lvds will act like a
+ bridge for other outbound interfaces.
+
Example:
lvds: lvds@ff96c000 {
compatible = "rockchip,rk3288-lvds";
------------------- drivers/gpu/drm/rockchip/rockchip_lvds.c -------------------
index a01a3cb..46308fb 100644
@@ -42,6 +42,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
*/
@@ -67,6 +70,7 @@ struct rockchip_lvds {
struct drm_panel *panel;
struct drm_connector connector;
struct drm_encoder encoder;
+ struct drm_bridge bridge;
struct mutex suspend_lock;
int suspend;
@@ -247,11 +251,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;
@@ -346,32 +349,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;
}
}
@@ -404,6 +427,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 alled 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,
@@ -525,9 +639,20 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
lvds->panel = panel;
} else {
- dev_err(&pdev->dev, "no panel node found\n");
- ret = -EINVAL;
- goto err_unprepare_pclk;
+ /*
+ * When no panel is found, register a bridge instead.
+ * We expect the code handling external encoders to
+ * connect encoder and bridge.
+ */
+ 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;
+ }
+
+ return 0;
}
encoder = &lvds->encoder;
@@ -592,6 +717,8 @@ static void rockchip_lvds_unbind(struct device *dev, struct device *master,
drm_connector_cleanup(&lvds->connector);
drm_encoder_cleanup(&lvds->encoder);
+ } else {
+ drm_bridge_remove(&lvds->bridge);
}
clk_unprepare(lvds->pclk);
 
 

Recent Patches

About Us

Sed lacus. Donec lectus. Nullam pretium nibh ut turpis. Nam bibendum. In nulla tortor, elementum vel, tempor at, varius non, purus. Mauris vitae nisl nec metus placerat consectetuer.

Read More...