From 09f914107310ba5378e4c4229b0fc39f7a00c7f8 Mon Sep 17 00:00:00 2001 From: Chaoyi Chen Date: Tue, 12 Sep 2023 16:32:53 +0800 Subject: [PATCH 1/3] drm/rockchip: vop2: update for dual lvds Signed-off-by: Chaoyi Chen Change-Id: Icec9c9c4fac5c014561dc26be72e62bb88a2f249 --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 24 ++++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 5256fc6f23a2..52dbcca75a6d 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -7515,6 +7515,14 @@ static void vop2_setup_dual_channel_if(struct drm_crtc *crtc) struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); struct vop2 *vop2 = vp->vop2; + if (vcstate->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE) { + VOP_CTRL_SET(vop2, lvds_dual_en, 1); + VOP_CTRL_SET(vop2, lvds_dual_mode, 0); + if (vcstate->output_flags & ROCKCHIP_OUTPUT_DATA_SWAP) + VOP_CTRL_SET(vop2, lvds_dual_channel_swap, 1); + return; + } + VOP_MODULE_SET(vop2, vp, dual_channel_en, 1); if (vcstate->output_flags & ROCKCHIP_OUTPUT_DATA_SWAP) VOP_MODULE_SET(vop2, vp, dual_channel_swap, 1); @@ -7531,6 +7539,10 @@ static void vop2_setup_dual_channel_if(struct drm_crtc *crtc) else if (vcstate->output_if & VOP_OUTPUT_IF_MIPI1 && !vop2_mark_as_left_panel(vcstate, VOP_OUTPUT_IF_MIPI1)) VOP_CTRL_SET(vop2, mipi_dual_en, 1); + else if (vcstate->output_if & VOP_OUTPUT_IF_LVDS1) { + VOP_CTRL_SET(vop2, lvds_dual_en, 1); + VOP_CTRL_SET(vop2, lvds_dual_mode, 1); + } } /* @@ -7840,15 +7852,6 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state VOP_CTRL_SET(vop2, lvds_dclk_pol, dclk_inv); } - if (vcstate->output_flags & (ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE | - ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE)) { - VOP_CTRL_SET(vop2, lvds_dual_en, 1); - if (vcstate->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE) - VOP_CTRL_SET(vop2, lvds_dual_mode, 1); - if (vcstate->output_flags & ROCKCHIP_OUTPUT_DATA_SWAP) - VOP_CTRL_SET(vop2, lvds_dual_channel_swap, 1); - } - if (vcstate->output_if & VOP_OUTPUT_IF_MIPI0) { ret = vop2_calc_cru_cfg(crtc, VOP_OUTPUT_IF_MIPI0, &if_pixclk, &if_dclk); if (ret < 0) @@ -7892,7 +7895,8 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state } } - if (vcstate->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE) + if (vcstate->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE || + vcstate->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE) vop2_setup_dual_channel_if(crtc); if (vcstate->output_if & VOP_OUTPUT_IF_eDP0) { From ce438ac73dca61c4307770e053af4c1aae881bc6 Mon Sep 17 00:00:00 2001 From: Chaoyi Chen Date: Tue, 12 Sep 2023 16:34:34 +0800 Subject: [PATCH 2/3] drm/rockchip: lvds: add support lvds1 for rk3567 Signed-off-by: Chaoyi Chen Change-Id: I788e9d755d95d986fda1c8ddbb8914912a5404a7 --- drivers/gpu/drm/rockchip/rockchip_lvds.c | 32 ++++++++++++++++++------ 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c index cd74e98e2f8c..bd281402d075 100644 --- a/drivers/gpu/drm/rockchip/rockchip_lvds.c +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c @@ -646,7 +646,7 @@ static const struct rockchip_lvds_funcs rk3368_lvds_funcs = { .disable = rk3368_lvds_disable, }; -static int __maybe_unused rockchip_secondary_lvds_probe(struct rockchip_lvds *lvds) +static int rk3568_lvds_probe(struct rockchip_lvds *lvds) { if (lvds->dual_channel) { struct rockchip_lvds *secondary = NULL; @@ -692,19 +692,37 @@ static const struct rockchip_lvds_funcs rk3562_lvds_funcs = { static void rk3568_lvds_enable(struct rockchip_lvds *lvds) { - regmap_write(lvds->grf, RK3568_GRF_VO_CON2, - RK3568_LVDS0_MODE_EN(1) | RK3568_LVDS0_P2S_EN(1) | - RK3568_LVDS0_DCLK_INV_SEL(1)); - regmap_write(lvds->grf, RK3568_GRF_VO_CON0, - RK3568_LVDS0_SELECT(lvds->format) | RK3568_LVDS0_MSBSEL(1)); + if (lvds->id) { + regmap_write(lvds->grf, RK3568_GRF_VO_CON3, + RK3568_LVDS1_MODE_EN(1) | + RK3568_LVDS1_P2S_EN(1) | + RK3568_LVDS1_DCLK_INV_SEL(1)); + regmap_write(lvds->grf, RK3568_GRF_VO_CON0, + RK3568_LVDS1_SELECT(lvds->format) | + RK3568_LVDS1_MSBSEL(1)); + } else { + regmap_write(lvds->grf, RK3568_GRF_VO_CON2, + RK3568_LVDS0_MODE_EN(1) | + RK3568_LVDS0_P2S_EN(1) | + RK3568_LVDS0_DCLK_INV_SEL(1)); + regmap_write(lvds->grf, RK3568_GRF_VO_CON0, + RK3568_LVDS0_SELECT(lvds->format) | + RK3568_LVDS0_MSBSEL(1)); + } } static void rk3568_lvds_disable(struct rockchip_lvds *lvds) { - regmap_write(lvds->grf, RK3568_GRF_VO_CON2, RK3568_LVDS0_MODE_EN(0)); + if (lvds->id) + regmap_write(lvds->grf, RK3568_GRF_VO_CON3, + RK3568_LVDS1_MODE_EN(0)); + else + regmap_write(lvds->grf, RK3568_GRF_VO_CON2, + RK3568_LVDS0_MODE_EN(0)); } static const struct rockchip_lvds_funcs rk3568_lvds_funcs = { + .probe = rk3568_lvds_probe, .enable = rk3568_lvds_enable, .disable = rk3568_lvds_disable, }; From e86a496684236d1747856eada1af8c2045df2b62 Mon Sep 17 00:00:00 2001 From: Chaoyi Chen Date: Wed, 13 Sep 2023 09:56:12 +0800 Subject: [PATCH 3/3] drm/rockchip: lvds: add dual lvds left right mode support The lvds two channel can work independent, each lvds channel can connector a single channel lvds panel, so add left and right pixels mode support. Signed-off-by: Chaoyi Chen Change-Id: I7fde54880d97269123688a3246a6db7005ddf254 --- drivers/gpu/drm/rockchip/rockchip_lvds.c | 124 +++++++++++++++++++++-- 1 file changed, 113 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c index bd281402d075..c4d74e1dd786 100644 --- a/drivers/gpu/drm/rockchip/rockchip_lvds.c +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c @@ -89,6 +89,20 @@ enum lvds_format { LVDS_10BIT_MODE_FORMAT_2, }; +enum rockchip_lvds_dual_link_pixels { + ROCKCHIP_LVDS_DUAL_LINK_EVEN_ODD_PIXELS = 0, + ROCKCHIP_LVDS_DUAL_LINK_ODD_EVEN_PIXELS = 1, + ROCKCHIP_LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS = 2, + ROCKCHIP_LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS = 3, +}; + +enum rockchip_of_lvds_pixels { + ROCKCHIP_OF_LVDS_EVEN = BIT(0), + ROCKCHIP_OF_LVDS_ODD = BIT(1), + ROCKCHIP_OF_LVDS_LEFT = BIT(2), + ROCKCHIP_OF_LVDS_RIGHT = BIT(3), +}; + struct rockchip_lvds; struct rockchip_lvds_funcs { @@ -107,7 +121,7 @@ struct rockchip_lvds { bool data_swap; bool dual_channel; bool phy_enabled; - enum drm_lvds_dual_link_pixels pixel_order; + enum rockchip_lvds_dual_link_pixels pixel_order; struct rockchip_lvds *primary; struct rockchip_lvds *secondary; @@ -120,6 +134,99 @@ struct rockchip_lvds { struct rockchip_drm_sub_dev sub_dev; }; +static int rockchip_of_lvds_get_port_pixels_type(struct device_node *port_node) +{ + bool even_pixels = + of_property_read_bool(port_node, "dual-lvds-even-pixels"); + bool odd_pixels = + of_property_read_bool(port_node, "dual-lvds-odd-pixels"); + bool left_pixels = + of_property_read_bool(port_node, "dual-lvds-left-pixels"); + bool right_pixels = + of_property_read_bool(port_node, "dual-lvds-right-pixels"); + + return (even_pixels ? ROCKCHIP_OF_LVDS_EVEN : 0) | + (odd_pixels ? ROCKCHIP_OF_LVDS_ODD : 0) | + (left_pixels ? ROCKCHIP_OF_LVDS_LEFT : 0) | + (right_pixels ? ROCKCHIP_OF_LVDS_RIGHT : 0); +} + +static int rockchip_of_lvds_get_remote_pixels_type( + const struct device_node *port_node) +{ + struct device_node *endpoint = NULL; + int pixels_type = -EPIPE; + + for_each_child_of_node(port_node, endpoint) { + struct device_node *remote_port; + int current_pt; + + if (!of_node_name_eq(endpoint, "endpoint")) + continue; + + remote_port = of_graph_get_remote_port(endpoint); + if (!remote_port) { + of_node_put(endpoint); + return -EPIPE; + } + + current_pt = rockchip_of_lvds_get_port_pixels_type(remote_port); + of_node_put(remote_port); + if (pixels_type < 0) + pixels_type = current_pt; + + /* + * Sanity check, ensure that all remote endpoints have the same + * pixel type. We may lift this restriction later if we need to + * support multiple sinks with different dual-link + * configurations by passing the endpoints explicitly to + * rockchip_of_lvds_get_dual_link_pixel_order(). + */ + if (!current_pt || pixels_type != current_pt) { + of_node_put(endpoint); + return -EINVAL; + } + } + + return pixels_type; +} + +static int rockchip_of_lvds_get_dual_link_pixel_order(const struct device_node *port1, + const struct device_node *port2) +{ + int remote_p1_pt, remote_p2_pt; + + if (!port1 || !port2) + return -EINVAL; + + remote_p1_pt = rockchip_of_lvds_get_remote_pixels_type(port1); + if (remote_p1_pt < 0) + return remote_p1_pt; + + remote_p2_pt = rockchip_of_lvds_get_remote_pixels_type(port2); + if (remote_p2_pt < 0) + return remote_p2_pt; + + /* + * A valid dual-lVDS bus is found when one remote port is marked with + * "dual-lvds-even-pixels" or "dual-lvds-left-pixels", and the other + * remote port is marked with "dual-lvds-odd-pixels"or + * "dual-lvds-right-pixels", bail out if the markers are not right. + */ + if ((remote_p1_pt + remote_p2_pt != ROCKCHIP_OF_LVDS_EVEN + ROCKCHIP_OF_LVDS_ODD) && + (remote_p1_pt + remote_p2_pt != ROCKCHIP_OF_LVDS_LEFT + ROCKCHIP_OF_LVDS_RIGHT)) + return -EINVAL; + + if (remote_p1_pt == ROCKCHIP_OF_LVDS_EVEN) + return ROCKCHIP_LVDS_DUAL_LINK_EVEN_ODD_PIXELS; + else if (remote_p1_pt == ROCKCHIP_OF_LVDS_ODD) + return ROCKCHIP_LVDS_DUAL_LINK_ODD_EVEN_PIXELS; + else if (remote_p1_pt == ROCKCHIP_OF_LVDS_LEFT) + return ROCKCHIP_LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS; + else + return ROCKCHIP_LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS; +} + static inline struct rockchip_lvds *connector_to_lvds(struct drm_connector *c) { return container_of(c, struct rockchip_lvds, connector); @@ -236,29 +343,24 @@ rockchip_lvds_encoder_atomic_check(struct drm_encoder *encoder, s->color_space = V4L2_COLORSPACE_DEFAULT; switch (lvds->pixel_order) { - case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS: + case ROCKCHIP_LVDS_DUAL_LINK_ODD_EVEN_PIXELS: s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE; s->output_if |= VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0; break; - case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS: + case ROCKCHIP_LVDS_DUAL_LINK_EVEN_ODD_PIXELS: s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE; s->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP; s->output_if |= VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0; break; -/* - * Fix me: To do it with a GKI compatible version. - */ -#if 0 - case DRM_LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS: + case ROCKCHIP_LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS: s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE; s->output_if |= VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0; break; - case DRM_LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS: + case ROCKCHIP_LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS: s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE; s->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP; s->output_if |= VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0; break; -#endif default: if (lvds->id) s->output_if |= VOP_OUTPUT_IF_LVDS1; @@ -659,7 +761,7 @@ static int rk3568_lvds_probe(struct rockchip_lvds *lvds) port0 = of_graph_get_port_by_id(lvds->dev->of_node, 1); port1 = of_graph_get_port_by_id(secondary->dev->of_node, 1); - pixel_order = drm_of_lvds_get_dual_link_pixel_order(port0, port1); + pixel_order = rockchip_of_lvds_get_dual_link_pixel_order(port0, port1); of_node_put(port1); of_node_put(port0);