diff --git a/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt b/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt index 4f2ba8c13d9259c528322070790f92da84b3bb58..4a0f4f7682ad4a742f32e66125a424e5dc6837f7 100644 --- a/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt +++ b/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt @@ -5,6 +5,7 @@ Required properties for dp-controller: platform specific such as: * "samsung,exynos5-dp" * "rockchip,rk3288-dp" + * "rockchip,rk3399-edp" -reg: physical base address of the controller and length of memory mapped region. diff --git a/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt index e832ff98fd61c1e527ac40a92cf80f1df0e20594..01cced1c2a180cfe4307cc5cfb0f70b8616f3d81 100644 --- a/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt +++ b/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt @@ -2,7 +2,8 @@ Rockchip RK3288 specific extensions to the Analogix Display Port ================================ Required properties: -- compatible: "rockchip,rk3288-edp"; +- compatible: "rockchip,rk3288-dp", + "rockchip,rk3399-edp"; - reg: physical base address of the controller and length @@ -27,6 +28,12 @@ Required properties: Port 0: contained 2 endpoints, connecting to the output of vop. Port 1: contained 1 endpoint, connecting to the input of panel. +Optional property for different chips: +- clocks: from common clock binding: handle to grf_vio clock. + +- clock-names: from common clock binding: + Required elements: "grf" + For the below properties, please refer to Analogix DP binding document: * Documentation/devicetree/bindings/drm/bridge/analogix_dp.txt - phys (required) diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 7699597070a13d09909e64780faba937a2b13b8c..32715daf73cb70b491c808ef480be679d73db76b 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -938,7 +938,7 @@ int analogix_dp_get_modes(struct drm_connector *connector) num_modes += drm_panel_get_modes(dp->plat_data->panel); if (dp->plat_data->get_modes) - num_modes += dp->plat_data->get_modes(dp->plat_data); + num_modes += dp->plat_data->get_modes(dp->plat_data, connector); return num_modes; } @@ -1208,6 +1208,7 @@ static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp) switch (dp->plat_data->dev_type) { case RK3288_DP: + case RK3399_EDP: /* * Like Rk3288 DisplayPort TRM indicate that "Main link * containing 4 physical lanes of 2.7/1.62 Gbps/lane". diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h index f09275d40f704b5a2aa505b7e7f51493cf80d150..b45638043ec41308681902d06907786a58456522 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h @@ -127,10 +127,10 @@ enum analog_power_block { }; enum dp_irq_type { - DP_IRQ_TYPE_HP_CABLE_IN, - DP_IRQ_TYPE_HP_CABLE_OUT, - DP_IRQ_TYPE_HP_CHANGE, - DP_IRQ_TYPE_UNKNOWN, + DP_IRQ_TYPE_HP_CABLE_IN = BIT(0), + DP_IRQ_TYPE_HP_CABLE_OUT = BIT(1), + DP_IRQ_TYPE_HP_CHANGE = BIT(2), + DP_IRQ_TYPE_UNKNOWN = BIT(3), }; struct video_info { diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c index 49205ef02be3afed044a2f58d4dce13f985278a2..48030f0cf4971672df899c9ca2cf2cac41f07fad 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c @@ -74,8 +74,12 @@ void analogix_dp_init_analog_param(struct analogix_dp_device *dp) reg = SEL_24M | TX_DVDD_BIT_1_0625V; writel(reg, dp->reg_base + ANALOGIX_DP_ANALOG_CTL_2); - if (dp->plat_data && (dp->plat_data->dev_type == RK3288_DP)) { - writel(REF_CLK_24M, dp->reg_base + ANALOGIX_DP_PLL_REG_1); + if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) { + reg = REF_CLK_24M; + if (dp->plat_data->dev_type == RK3288_DP) + reg ^= REF_CLK_MASK; + + writel(reg, dp->reg_base + ANALOGIX_DP_PLL_REG_1); writel(0x95, dp->reg_base + ANALOGIX_DP_PLL_REG_2); writel(0x40, dp->reg_base + ANALOGIX_DP_PLL_REG_3); writel(0x58, dp->reg_base + ANALOGIX_DP_PLL_REG_4); @@ -244,7 +248,7 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp, u32 reg; u32 phy_pd_addr = ANALOGIX_DP_PHY_PD; - if (dp->plat_data && (dp->plat_data->dev_type == RK3288_DP)) + if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) phy_pd_addr = ANALOGIX_DP_PD; switch (block) { @@ -448,7 +452,7 @@ void analogix_dp_init_aux(struct analogix_dp_device *dp) analogix_dp_reset_aux(dp); /* Disable AUX transaction H/W retry */ - if (dp->plat_data && (dp->plat_data->dev_type == RK3288_DP)) + if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) reg = AUX_BIT_PERIOD_EXPECTED_DELAY(0) | AUX_HW_RETRY_COUNT_SEL(3) | AUX_HW_RETRY_INTERVAL_600_MICROSECONDS; diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h index 337912b0aeabc9382f9ffdbd546ecddba1b691da..cdcc6c5add5e9c27befb644520784edaec9c6a4a 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h @@ -163,8 +163,9 @@ #define HSYNC_POLARITY_CFG (0x1 << 0) /* ANALOGIX_DP_PLL_REG_1 */ -#define REF_CLK_24M (0x1 << 1) -#define REF_CLK_27M (0x0 << 1) +#define REF_CLK_24M (0x1 << 0) +#define REF_CLK_27M (0x0 << 0) +#define REF_CLK_MASK (0x1 << 0) /* ANALOGIX_DP_LANE_MAP */ #define LANE3_MAP_LOGIC_LANE_0 (0x0 << 6) diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c index 4c1fb3f8b5a60dfad64dbf5b792f9b6b3127100d..4f0850585b8e0312344757104dc35c0e793234ad 100644 --- a/drivers/gpu/drm/exynos/exynos_dp.c +++ b/drivers/gpu/drm/exynos/exynos_dp.c @@ -67,10 +67,10 @@ static int exynos_dp_poweroff(struct analogix_dp_plat_data *plat_data) return exynos_dp_crtc_clock_enable(plat_data, false); } -static int exynos_dp_get_modes(struct analogix_dp_plat_data *plat_data) +static int exynos_dp_get_modes(struct analogix_dp_plat_data *plat_data, + struct drm_connector *connector) { struct exynos_dp_device *dp = to_dp(plat_data); - struct drm_connector *connector = dp->connector; struct drm_display_mode *mode; int num_modes = 0; diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index c120172add5c04423038adf11bccb4027c16fe3d..e81e19a660adcbd10736a01ab89934c4b06e0bae 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -33,13 +34,28 @@ #include "rockchip_drm_drv.h" #include "rockchip_drm_vop.h" +#define RK3288_GRF_SOC_CON6 0x25c +#define RK3288_EDP_LCDC_SEL BIT(5) +#define RK3399_GRF_SOC_CON20 0x6250 +#define RK3399_EDP_LCDC_SEL BIT(5) + +#define HIWORD_UPDATE(val, mask) (val | (mask) << 16) + #define to_dp(nm) container_of(nm, struct rockchip_dp_device, nm) -/* dp grf register offset */ -#define GRF_SOC_CON6 0x025c -#define GRF_EDP_LCD_SEL_MASK BIT(5) -#define GRF_EDP_SEL_VOP_LIT BIT(5) -#define GRF_EDP_SEL_VOP_BIG 0 +/** + * struct rockchip_dp_chip_data - splite the grf setting of kind of chips + * @lcdsel_grf_reg: grf register offset of lcdc select + * @lcdsel_big: reg value of selecting vop big for eDP + * @lcdsel_lit: reg value of selecting vop little for eDP + * @chip_type: specific chip type + */ +struct rockchip_dp_chip_data { + u32 lcdsel_grf_reg; + u32 lcdsel_big; + u32 lcdsel_lit; + u32 chip_type; +}; struct rockchip_dp_device { struct drm_device *drm_dev; @@ -48,9 +64,12 @@ struct rockchip_dp_device { struct drm_display_mode mode; struct clk *pclk; + struct clk *grfclk; struct regmap *grf; struct reset_control *rst; + const struct rockchip_dp_chip_data *data; + struct analogix_dp_plat_data plat_data; }; @@ -92,6 +111,23 @@ static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data) return 0; } +static int rockchip_dp_get_modes(struct analogix_dp_plat_data *plat_data, + struct drm_connector *connector) +{ + struct drm_display_info *di = &connector->display_info; + /* VOP couldn't output YUV video format for eDP rightly */ + u32 mask = DRM_COLOR_FORMAT_YCRCB444 | DRM_COLOR_FORMAT_YCRCB422; + + if ((di->color_formats & mask)) { + DRM_DEBUG_KMS("Swapping display color format from YUV to RGB\n"); + di->color_formats &= ~mask; + di->color_formats |= DRM_COLOR_FORMAT_RGB444; + di->bpc = 8; + } + + return 0; +} + static bool rockchip_dp_drm_encoder_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, @@ -119,17 +155,23 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder) return; if (ret) - val = GRF_EDP_SEL_VOP_LIT | (GRF_EDP_LCD_SEL_MASK << 16); + val = dp->data->lcdsel_lit; else - val = GRF_EDP_SEL_VOP_BIG | (GRF_EDP_LCD_SEL_MASK << 16); + val = dp->data->lcdsel_big; dev_dbg(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG"); - ret = regmap_write(dp->grf, GRF_SOC_CON6, val); - if (ret != 0) { - dev_err(dp->dev, "Could not write to GRF: %d\n", ret); + ret = clk_prepare_enable(dp->grfclk); + if (ret < 0) { + dev_err(dp->dev, "failed to enable grfclk %d\n", ret); return; } + + ret = regmap_write(dp->grf, dp->data->lcdsel_grf_reg, val); + if (ret != 0) + dev_err(dp->dev, "Could not write to GRF: %d\n", ret); + + clk_disable_unprepare(dp->grfclk); } static void rockchip_dp_drm_encoder_nop(struct drm_encoder *encoder) @@ -143,22 +185,29 @@ rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder, struct drm_connector_state *conn_state) { struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); + struct rockchip_dp_device *dp = to_dp(encoder); + int ret; /* - * FIXME(Yakir): driver should configure the CRTC output video - * mode with the display information which indicated the monitor - * support colorimetry. - * - * But don't know why the CRTC driver seems could only output the - * RGBaaa rightly. For example, if connect the "innolux,n116bge" - * eDP screen, EDID would indicated that screen only accepted the - * 6bpc mode. But if I configure CRTC to RGB666 output, then eDP - * screen would show a blue picture (RGB888 show a green picture). - * But if I configure CTRC to RGBaaa, and eDP driver still keep - * RGB666 input video mode, then screen would works prefect. + * The hardware IC designed that VOP must output the RGB10 video + * format to eDP controller, and if eDP panel only support RGB8, + * then eDP controller should cut down the video data, not via VOP + * controller, that's why we need to hardcode the VOP output mode + * to RGA10 here. */ + s->output_mode = ROCKCHIP_OUT_MODE_AAAA; s->output_type = DRM_MODE_CONNECTOR_eDP; + if (dp->data->chip_type == RK3399_EDP) { + /* + * For RK3399, VOP Lit must code the out mode to RGB888, + * VOP Big must code the out mode to RGB10. + */ + ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, + encoder); + if (ret > 0) + s->output_mode = ROCKCHIP_OUT_MODE_P888; + } return 0; } @@ -192,6 +241,16 @@ static int rockchip_dp_init(struct rockchip_dp_device *dp) return PTR_ERR(dp->grf); } + dp->grfclk = devm_clk_get(dev, "grf"); + if (PTR_ERR(dp->grfclk) == -ENOENT) { + dp->grfclk = NULL; + } else if (PTR_ERR(dp->grfclk) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (IS_ERR(dp->grfclk)) { + dev_err(dev, "failed to get grf clock\n"); + return PTR_ERR(dp->grfclk); + } + dp->pclk = devm_clk_get(dev, "pclk"); if (IS_ERR(dp->pclk)) { dev_err(dev, "failed to get pclk property\n"); @@ -246,6 +305,7 @@ static int rockchip_dp_bind(struct device *dev, struct device *master, void *data) { struct rockchip_dp_device *dp = dev_get_drvdata(dev); + const struct rockchip_dp_chip_data *dp_data; struct drm_device *drm_dev = data; int ret; @@ -256,10 +316,15 @@ static int rockchip_dp_bind(struct device *dev, struct device *master, */ dev_set_drvdata(dev, NULL); + dp_data = of_device_get_match_data(dev); + if (!dp_data) + return -ENODEV; + ret = rockchip_dp_init(dp); if (ret < 0) return ret; + dp->data = dp_data; dp->drm_dev = drm_dev; ret = rockchip_dp_drm_create_encoder(dp); @@ -270,9 +335,10 @@ static int rockchip_dp_bind(struct device *dev, struct device *master, dp->plat_data.encoder = &dp->encoder; - dp->plat_data.dev_type = RK3288_DP; + dp->plat_data.dev_type = dp->data->chip_type; dp->plat_data.power_on = rockchip_dp_poweron; dp->plat_data.power_off = rockchip_dp_powerdown; + dp->plat_data.get_modes = rockchip_dp_get_modes; return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data); } @@ -292,38 +358,33 @@ static int rockchip_dp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *panel_node, *port, *endpoint; + struct drm_panel *panel = NULL; struct rockchip_dp_device *dp; - struct drm_panel *panel; port = of_graph_get_port_by_id(dev->of_node, 1); - if (!port) { - dev_err(dev, "can't find output port\n"); - return -EINVAL; - } - - endpoint = of_get_child_by_name(port, "endpoint"); - of_node_put(port); - if (!endpoint) { - dev_err(dev, "no output endpoint found\n"); - return -EINVAL; - } - - panel_node = of_graph_get_remote_port_parent(endpoint); - of_node_put(endpoint); - if (!panel_node) { - dev_err(dev, "no output node found\n"); - return -EINVAL; - } - - panel = of_drm_find_panel(panel_node); - if (!panel) { - DRM_ERROR("failed to find panel\n"); + if (port) { + endpoint = of_get_child_by_name(port, "endpoint"); + of_node_put(port); + if (!endpoint) { + dev_err(dev, "no output endpoint found\n"); + return -EINVAL; + } + + panel_node = of_graph_get_remote_port_parent(endpoint); + of_node_put(endpoint); + if (!panel_node) { + dev_err(dev, "no output node found\n"); + return -EINVAL; + } + + panel = of_drm_find_panel(panel_node); of_node_put(panel_node); - return -EPROBE_DEFER; + if (!panel) { + DRM_ERROR("failed to find panel\n"); + return -EPROBE_DEFER; + } } - of_node_put(panel_node); - dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL); if (!dp) return -ENOMEM; @@ -356,8 +417,23 @@ static const struct dev_pm_ops rockchip_dp_pm_ops = { #endif }; +static const struct rockchip_dp_chip_data rk3399_edp = { + .lcdsel_grf_reg = RK3399_GRF_SOC_CON20, + .lcdsel_big = HIWORD_UPDATE(0, RK3399_EDP_LCDC_SEL), + .lcdsel_lit = HIWORD_UPDATE(RK3399_EDP_LCDC_SEL, RK3399_EDP_LCDC_SEL), + .chip_type = RK3399_EDP, +}; + +static const struct rockchip_dp_chip_data rk3288_dp = { + .lcdsel_grf_reg = RK3288_GRF_SOC_CON6, + .lcdsel_big = HIWORD_UPDATE(0, RK3288_EDP_LCDC_SEL), + .lcdsel_lit = HIWORD_UPDATE(RK3288_EDP_LCDC_SEL, RK3288_EDP_LCDC_SEL), + .chip_type = RK3288_DP, +}; + static const struct of_device_id rockchip_dp_dt_ids[] = { - {.compatible = "rockchip,rk3288-dp",}, + {.compatible = "rockchip,rk3288-dp", .data = &rk3288_dp }, + {.compatible = "rockchip,rk3399-edp", .data = &rk3399_edp }, {} }; MODULE_DEVICE_TABLE(of, rockchip_dp_dt_ids); diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h index 25afb31f0389edbb1f9f8a9e3558602df38f816b..261b86d20e7718f13c95290c63e277b5fafca96d 100644 --- a/include/drm/bridge/analogix_dp.h +++ b/include/drm/bridge/analogix_dp.h @@ -16,8 +16,14 @@ enum analogix_dp_devtype { EXYNOS_DP, RK3288_DP, + RK3399_EDP, }; +static inline bool is_rockchip(enum analogix_dp_devtype type) +{ + return type == RK3288_DP || type == RK3399_EDP; +} + struct analogix_dp_plat_data { enum analogix_dp_devtype dev_type; struct drm_panel *panel; @@ -28,7 +34,8 @@ struct analogix_dp_plat_data { int (*power_off)(struct analogix_dp_plat_data *); int (*attach)(struct analogix_dp_plat_data *, struct drm_bridge *, struct drm_connector *); - int (*get_modes)(struct analogix_dp_plat_data *); + int (*get_modes)(struct analogix_dp_plat_data *, + struct drm_connector *); }; int analogix_dp_resume(struct device *dev);