diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 8fbfd4c104f5..5fa26a513174 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -7517,6 +7517,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); @@ -7533,6 +7541,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); + } } /* @@ -7843,15 +7855,6 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_sta 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) @@ -7895,7 +7898,8 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_sta } } - 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) { diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c index 02fe810234e6..2ebaa4ace104 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; @@ -492,7 +594,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; @@ -505,7 +607,7 @@ static int __maybe_unused rockchip_secondary_lvds_probe(struct rockchip_lvds *lv 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); @@ -538,19 +640,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, };