drm: rcar-du: Fix crash when using LVDS1 clock for CRTC
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Fri, 4 Dec 2020 11:43:58 +0000 (13:43 +0200)
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Tue, 5 Jan 2021 05:19:56 +0000 (07:19 +0200)
On D3 and E3 platforms, the LVDS encoder includes a PLL that can
generate a clock for the corresponding CRTC, used even when the CRTC
output to a non-LVDS port. This mechanism is supported by the driver,
but the implementation is broken in dual-link LVDS mode. In that case,
the LVDS1 drm_encoder is skipped, which causes a crash when trying to
access its bridge later on.

Fix this by storing bridge pointers internally instead of retrieving
them from the encoder. The rcar_du_device encoders field isn't used
anymore and can be dropped.

Fixes: 8e8fddab0d0a ("drm: rcar-du: Skip LVDS1 output on Gen3 when using dual-link LVDS mode")
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
drivers/gpu/drm/rcar-du/rcar_du_crtc.c
drivers/gpu/drm/rcar-du/rcar_du_drv.h
drivers/gpu/drm/rcar-du/rcar_du_encoder.c

index b5fb941e0f534c590da278445620983599f3e523..e23b9c7b4afebbb332d55616e3cd014fa22a6423 100644 (file)
@@ -730,13 +730,10 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
         */
        if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index) &&
            rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0)) {
-               struct rcar_du_encoder *encoder =
-                       rcdu->encoders[RCAR_DU_OUTPUT_LVDS0 + rcrtc->index];
+               struct drm_bridge *bridge = rcdu->lvds[rcrtc->index];
                const struct drm_display_mode *mode =
                        &crtc->state->adjusted_mode;
-               struct drm_bridge *bridge;
 
-               bridge = drm_bridge_chain_get_first_bridge(&encoder->base);
                rcar_lvds_clk_enable(bridge, mode->clock * 1000);
        }
 
@@ -764,15 +761,12 @@ static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
 
        if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index) &&
            rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0)) {
-               struct rcar_du_encoder *encoder =
-                       rcdu->encoders[RCAR_DU_OUTPUT_LVDS0 + rcrtc->index];
-               struct drm_bridge *bridge;
+               struct drm_bridge *bridge = rcdu->lvds[rcrtc->index];
 
                /*
                 * Disable the LVDS clock output, see
                 * rcar_du_crtc_atomic_enable().
                 */
-               bridge = drm_bridge_chain_get_first_bridge(&encoder->base);
                rcar_lvds_clk_disable(bridge);
        }
 
index 61504c54e2ecf249ca81525ff3116b740b2cfb37..3597a179bfb7873a096e525be3bb942ca89ea801 100644 (file)
 
 struct clk;
 struct device;
+struct drm_bridge;
 struct drm_device;
 struct drm_property;
 struct rcar_du_device;
-struct rcar_du_encoder;
 
 #define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK BIT(0)  /* Per-CRTC IRQ and clock */
 #define RCAR_DU_FEATURE_VSP1_SOURCE    BIT(1)  /* Has inputs from VSP1 */
@@ -71,6 +71,7 @@ struct rcar_du_device_info {
 #define RCAR_DU_MAX_CRTCS              4
 #define RCAR_DU_MAX_GROUPS             DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2)
 #define RCAR_DU_MAX_VSPS               4
+#define RCAR_DU_MAX_LVDS               2
 
 struct rcar_du_device {
        struct device *dev;
@@ -83,11 +84,10 @@ struct rcar_du_device {
        struct rcar_du_crtc crtcs[RCAR_DU_MAX_CRTCS];
        unsigned int num_crtcs;
 
-       struct rcar_du_encoder *encoders[RCAR_DU_OUTPUT_MAX];
-
        struct rcar_du_group groups[RCAR_DU_MAX_GROUPS];
        struct platform_device *cmms[RCAR_DU_MAX_CRTCS];
        struct rcar_du_vsp vsps[RCAR_DU_MAX_VSPS];
+       struct drm_bridge *lvds[RCAR_DU_MAX_LVDS];
 
        struct {
                struct drm_property *colorkey;
index b0335da0c1614609846bbc78594e00970518e82a..50fc14534fa4d8d00ba9aaf790767392c5eb85e2 100644 (file)
@@ -57,7 +57,6 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
        if (renc == NULL)
                return -ENOMEM;
 
-       rcdu->encoders[output] = renc;
        renc->output = output;
        encoder = rcar_encoder_to_drm_encoder(renc);
 
@@ -91,6 +90,10 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
                        ret = -EPROBE_DEFER;
                        goto done;
                }
+
+               if (output == RCAR_DU_OUTPUT_LVDS0 ||
+                   output == RCAR_DU_OUTPUT_LVDS1)
+                       rcdu->lvds[output - RCAR_DU_OUTPUT_LVDS0] = bridge;
        }
 
        /*