From 714abae67e161f5fdbd4b69bcb5fac4f0f111d48 Mon Sep 17 00:00:00 2001 From: Wyon Bi Date: Fri, 29 Dec 2017 11:06:47 +0800 Subject: [PATCH] drm/rockchip: rk618: vif: Convert to drm_bridge Change-Id: I124bfabf7fe67854f55aef5fec0cad00cd5e2eac Signed-off-by: Wyon Bi --- .../display/rockchip/rockchip,rk618.txt | 67 +++++- drivers/gpu/drm/rockchip/rk618/rk618_output.h | 6 +- drivers/gpu/drm/rockchip/rk618/rk618_vif.c | 209 ++++++++++++++++-- 3 files changed, 254 insertions(+), 28 deletions(-) diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,rk618.txt b/Documentation/devicetree/bindings/display/rockchip/rockchip,rk618.txt index 7a38b0826e48..112d5f57aa30 100644 --- a/Documentation/devicetree/bindings/display/rockchip/rockchip,rk618.txt +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,rk618.txt @@ -1,6 +1,55 @@ Rockchip RK618 display bridge bindings ====================================== +VIF Module +---------- + +Required properties: +- compatible: value should be one of the following: + "rockchip,rk618-vif" +- clocks: must include clock specifiers corresponding to entries in the + clock-names property. + See ../clocks/clock-bindings.txt for details. +- clock-names: list of clock names sorted in the same order as the clocks + property. Must contain "vif", "vif_pre". + +Required nodes: + +The connections to the video ports are modeled using the OF graph +bindings specified in Documentation/devicetree/bindings/graph.txt. + +Example: + +&rk618 { + VIF: vif { + compatible = "rockchip,rk618-vif"; + clocks = <&CRU VIF0_CLK>, <&CRU VIF0_PRE_CLK>; + clock-names = "vif", "vif_pre"; + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + vif_input_hdmi: endpoint { + remote-endpoint = <&hdmi_output_vif>; + }; + }; + + port@1 { + reg = <1>; + + vif_output_lvds: endpoint { + remote-endpoint = <&lvds_input_vif>; + }; + }; + }; + }; +}; + LVDS Encoder ------------ @@ -13,11 +62,12 @@ Required properties: - clock-names: list of clock names sorted in the same order as the clocks property. Must contain "lvds", "dither", "vif", "scaler". -The LVDS has two video ports. Their connections are modeled using the OF -graph bindings specified in Documentation/devicetree/bindings/graph.txt. +Required nodes: -- Video port 0 for DPI input -- Video port 1 for LVDS output +The connections to the video ports are modeled using the OF graph +bindings specified in Documentation/devicetree/bindings/graph.txt. + +Example: &rk618 { status = "okay"; @@ -43,9 +93,14 @@ graph bindings specified in Documentation/devicetree/bindings/graph.txt. remote-endpoint = <&vop_output_lvds>; }; - lvds_input_bridge: endpoint@1 { + lvds_input_vif: endpoint@1 { reg = <1>; - remote-endpoint = <&hdmi_output_bridge>; + remote-endpoint = <&vif_output_lvds>; + }; + + lvds_input_hdmi: endpoint@2 { + reg = <2>; + remote-endpoint = <&hdmi_output_lvds>; }; }; diff --git a/drivers/gpu/drm/rockchip/rk618/rk618_output.h b/drivers/gpu/drm/rockchip/rk618/rk618_output.h index 1e136ce521a6..0e2b0430ba90 100644 --- a/drivers/gpu/drm/rockchip/rk618/rk618_output.h +++ b/drivers/gpu/drm/rockchip/rk618/rk618_output.h @@ -84,6 +84,7 @@ #define MIPI_EDPI_HALT BIT(16) #define HDMI_HSYNC_POL_INV BIT(15) #define HDMI_VSYNC_POL_INV BIT(14) +#define HDMI_CLK_SEL_MASK GENMASK(13, 12) #define HDMI_CLK_SEL_VIDEO_INF0_CLK UPDATE(2, 13, 12) #define HDMI_CLK_SEL_SCALER_CLK UPDATE(1, 13, 12) #define HDMI_CLK_SEL_VIDEO_INF1_CLK 0 @@ -133,11 +134,6 @@ int rk618_output_bind(struct rk618_output *output, struct drm_device *drm, int encoder_type, int connector_type); void rk618_output_unbind(struct rk618_output *output); -void rk618_vif_enable(struct rk618 *rk618); -void rk618_vif_disable(struct rk618 *rk618); -void rk618_vif_configure(struct rk618 *rk618, - const struct drm_display_mode *mode); - void rk618_scaler_enable(struct rk618 *rk618); void rk618_scaler_disable(struct rk618 *rk618); void rk618_scaler_configure(struct rk618 *rk618, diff --git a/drivers/gpu/drm/rockchip/rk618/rk618_vif.c b/drivers/gpu/drm/rockchip/rk618/rk618_vif.c index e8221443a0d1..6f1b012d2d3f 100644 --- a/drivers/gpu/drm/rockchip/rk618/rk618_vif.c +++ b/drivers/gpu/drm/rockchip/rk618/rk618_vif.c @@ -39,20 +39,33 @@ #define RK618_VIF1_REG4 0x0028 #define RK618_VIF1_REG5 0x002c -void rk618_vif_enable(struct rk618 *rk618) -{ - regmap_write(rk618->regmap, RK618_VIF0_REG0, VIF_ENABLE); -} -EXPORT_SYMBOL(rk618_vif_enable); +struct rk618_vif { + struct drm_bridge base; + struct drm_bridge *bridge; + struct drm_display_mode mode; + struct device *dev; + struct regmap *regmap; + struct clk *vif_clk; + struct clk *vif_pre_clk; +}; -void rk618_vif_disable(struct rk618 *rk618) +static inline struct rk618_vif *bridge_to_vif(struct drm_bridge *bridge) { - regmap_write(rk618->regmap, RK618_VIF0_REG0, VIF_DISABLE); + return container_of(bridge, struct rk618_vif, base); } -EXPORT_SYMBOL(rk618_vif_disable); -void rk618_vif_configure(struct rk618 *rk618, - const struct drm_display_mode *mode) +static void rk618_vif_enable(struct rk618_vif *vif) +{ + regmap_write(vif->regmap, RK618_VIF0_REG0, VIF_ENABLE); +} + +static void rk618_vif_disable(struct rk618_vif *vif) +{ + regmap_write(vif->regmap, RK618_VIF0_REG0, VIF_DISABLE); +} + +static void rk618_vif_configure(struct rk618_vif *vif, + const struct drm_display_mode *mode) { struct videomode vm; u32 vif_frame_vst, vif_frame_hst; @@ -76,18 +89,180 @@ void rk618_vif_configure(struct rk618 *rk618, vif_vact_end = vm.vsync_len + vm.vback_porch + vm.vactive; vif_vact_st = vm.vsync_len + vm.vback_porch; - regmap_write(rk618->regmap, RK618_VIF0_REG1, + regmap_write(vif->regmap, RK618_VIF0_REG1, VIF_FRAME_VST(vif_frame_vst) | VIF_FRAME_HST(vif_frame_hst)); - regmap_write(rk618->regmap, RK618_VIF0_REG2, + regmap_write(vif->regmap, RK618_VIF0_REG2, VIF_HS_END(vif_hs_end) | VIF_HTOTAL(vif_htotal)); - regmap_write(rk618->regmap, RK618_VIF0_REG3, + regmap_write(vif->regmap, RK618_VIF0_REG3, VIF_HACT_END(vif_hact_end) | VIF_HACT_ST(vif_hact_st)); - regmap_write(rk618->regmap, RK618_VIF0_REG4, + regmap_write(vif->regmap, RK618_VIF0_REG4, VIF_VS_END(vif_vs_end) | VIF_VTOTAL(vif_vtotal)); - regmap_write(rk618->regmap, RK618_VIF0_REG5, + regmap_write(vif->regmap, RK618_VIF0_REG5, VIF_VACT_END(vif_vact_end) | VIF_VACT_ST(vif_vact_st)); - regmap_write(rk618->regmap, RK618_IO_CON0, + regmap_write(vif->regmap, RK618_IO_CON0, VIF0_SYNC_MODE_ENABLE); } -EXPORT_SYMBOL(rk618_vif_configure); + +static void rk618_vif_bridge_pre_enable(struct drm_bridge *bridge) +{ + struct rk618_vif *vif = bridge_to_vif(bridge); + const struct drm_display_mode *mode = &vif->mode; + long rate; + + clk_set_parent(vif->vif_clk, vif->vif_pre_clk); + + rate = clk_round_rate(vif->vif_clk, mode->clock * 1000); + clk_set_rate(vif->vif_clk, rate); + + rk618_vif_configure(vif, mode); +} + +static void rk618_vif_bridge_enable(struct drm_bridge *bridge) +{ + struct rk618_vif *vif = bridge_to_vif(bridge); + + rk618_vif_enable(vif); + clk_prepare_enable(vif->vif_clk); +} + +static void rk618_vif_bridge_disable(struct drm_bridge *bridge) +{ + struct rk618_vif *vif = bridge_to_vif(bridge); + + rk618_vif_disable(vif); +} + +static void rk618_vif_bridge_post_disable(struct drm_bridge *bridge) +{ + struct rk618_vif *vif = bridge_to_vif(bridge); + + clk_disable_unprepare(vif->vif_clk); +} + +static void rk618_vif_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted) +{ + struct rk618_vif *vif = bridge_to_vif(bridge); + + drm_mode_copy(&vif->mode, adjusted); +} + +static int rk618_vif_bridge_attach(struct drm_bridge *bridge) +{ + struct rk618_vif *vif = bridge_to_vif(bridge); + struct device *dev = vif->dev; + struct device_node *endpoint; + int ret; + + endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1); + if (endpoint && of_device_is_available(endpoint)) { + struct device_node *remote; + + remote = of_graph_get_remote_port_parent(endpoint); + of_node_put(endpoint); + if (!remote || !of_device_is_available(remote)) + return -ENODEV; + + vif->bridge = of_drm_find_bridge(remote); + of_node_put(remote); + if (!vif->bridge) + return -EPROBE_DEFER; + + vif->bridge->encoder = bridge->encoder; + + ret = drm_bridge_attach(bridge->dev, vif->bridge); + if (ret) { + dev_err(dev, "failed to attach bridge\n"); + return ret; + } + + bridge->next = vif->bridge; + } + + return 0; +} + +static const struct drm_bridge_funcs rk618_vif_bridge_funcs = { + .pre_enable = rk618_vif_bridge_pre_enable, + .enable = rk618_vif_bridge_enable, + .disable = rk618_vif_bridge_disable, + .post_disable = rk618_vif_bridge_post_disable, + .mode_set = rk618_vif_bridge_mode_set, + .attach = rk618_vif_bridge_attach, +}; + +static int rk618_vif_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rk618_vif *vif; + int ret; + + if (!of_device_is_available(dev->of_node)) + return -ENODEV; + + vif = devm_kzalloc(dev, sizeof(*vif), GFP_KERNEL); + if (!vif) + return -ENOMEM; + + vif->dev = dev; + platform_set_drvdata(pdev, vif); + + vif->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!vif->regmap) + return -ENODEV; + + vif->vif_clk = devm_clk_get(dev, "vif"); + if (IS_ERR(vif->vif_clk)) { + ret = PTR_ERR(vif->vif_clk); + dev_err(dev, "failed to get vif clock: %d\n", ret); + return ret; + } + + vif->vif_pre_clk = devm_clk_get(dev, "vif_pre"); + if (IS_ERR(vif->vif_pre_clk)) { + ret = PTR_ERR(vif->vif_pre_clk); + dev_err(dev, "failed to get vif pre clock: %d\n", ret); + return ret; + } + + vif->base.funcs = &rk618_vif_bridge_funcs; + vif->base.of_node = dev->of_node; + ret = drm_bridge_add(&vif->base); + if (ret) { + dev_err(dev, "failed to add bridge\n"); + return ret; + } + + return 0; +} + +static int rk618_vif_remove(struct platform_device *pdev) +{ + struct rk618_vif *vif = platform_get_drvdata(pdev); + + drm_bridge_remove(&vif->base); + + return 0; +} + +static const struct of_device_id rk618_vif_of_match[] = { + { .compatible = "rockchip,rk618-vif", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rk618_vif_of_match); + +static struct platform_driver rk618_vif_driver = { + .driver = { + .name = "rk618-vif", + .of_match_table = of_match_ptr(rk618_vif_of_match), + }, + .probe = rk618_vif_probe, + .remove = rk618_vif_remove, +}; +module_platform_driver(rk618_vif_driver); + +MODULE_AUTHOR("Wyon Bi "); +MODULE_DESCRIPTION("Rockchip RK618 VIF driver"); +MODULE_LICENSE("GPL v2");