Commit 53ced169 authored by Laurent Pinchart's avatar Laurent Pinchart

drm: rcar-du: Fix crash when using LVDS1 clock for CRTC

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: 8e8fddab ("drm: rcar-du: Skip LVDS1 output on Gen3 when using dual-link LVDS mode")
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Reviewed-by: default avatarJacopo Mondi <jacopo+renesas@jmondi.org>
Reviewed-by: default avatarKieran Bingham <kieran.bingham+renesas@ideasonboard.com>
parent 2551952e
...@@ -730,13 +730,10 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc, ...@@ -730,13 +730,10 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
*/ */
if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index) && if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index) &&
rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0)) { rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0)) {
struct rcar_du_encoder *encoder = struct drm_bridge *bridge = rcdu->lvds[rcrtc->index];
rcdu->encoders[RCAR_DU_OUTPUT_LVDS0 + rcrtc->index];
const struct drm_display_mode *mode = const struct drm_display_mode *mode =
&crtc->state->adjusted_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); rcar_lvds_clk_enable(bridge, mode->clock * 1000);
} }
...@@ -764,15 +761,12 @@ static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc, ...@@ -764,15 +761,12 @@ static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index) && if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index) &&
rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0)) { rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0)) {
struct rcar_du_encoder *encoder = struct drm_bridge *bridge = rcdu->lvds[rcrtc->index];
rcdu->encoders[RCAR_DU_OUTPUT_LVDS0 + rcrtc->index];
struct drm_bridge *bridge;
/* /*
* Disable the LVDS clock output, see * Disable the LVDS clock output, see
* rcar_du_crtc_atomic_enable(). * rcar_du_crtc_atomic_enable().
*/ */
bridge = drm_bridge_chain_get_first_bridge(&encoder->base);
rcar_lvds_clk_disable(bridge); rcar_lvds_clk_disable(bridge);
} }
......
...@@ -20,10 +20,10 @@ ...@@ -20,10 +20,10 @@
struct clk; struct clk;
struct device; struct device;
struct drm_bridge;
struct drm_device; struct drm_device;
struct drm_property; struct drm_property;
struct rcar_du_device; 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_CRTC_IRQ_CLOCK BIT(0) /* Per-CRTC IRQ and clock */
#define RCAR_DU_FEATURE_VSP1_SOURCE BIT(1) /* Has inputs from VSP1 */ #define RCAR_DU_FEATURE_VSP1_SOURCE BIT(1) /* Has inputs from VSP1 */
...@@ -71,6 +71,7 @@ struct rcar_du_device_info { ...@@ -71,6 +71,7 @@ struct rcar_du_device_info {
#define RCAR_DU_MAX_CRTCS 4 #define RCAR_DU_MAX_CRTCS 4
#define RCAR_DU_MAX_GROUPS DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2) #define RCAR_DU_MAX_GROUPS DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2)
#define RCAR_DU_MAX_VSPS 4 #define RCAR_DU_MAX_VSPS 4
#define RCAR_DU_MAX_LVDS 2
struct rcar_du_device { struct rcar_du_device {
struct device *dev; struct device *dev;
...@@ -83,11 +84,10 @@ struct rcar_du_device { ...@@ -83,11 +84,10 @@ struct rcar_du_device {
struct rcar_du_crtc crtcs[RCAR_DU_MAX_CRTCS]; struct rcar_du_crtc crtcs[RCAR_DU_MAX_CRTCS];
unsigned int num_crtcs; unsigned int num_crtcs;
struct rcar_du_encoder *encoders[RCAR_DU_OUTPUT_MAX];
struct rcar_du_group groups[RCAR_DU_MAX_GROUPS]; struct rcar_du_group groups[RCAR_DU_MAX_GROUPS];
struct platform_device *cmms[RCAR_DU_MAX_CRTCS]; struct platform_device *cmms[RCAR_DU_MAX_CRTCS];
struct rcar_du_vsp vsps[RCAR_DU_MAX_VSPS]; struct rcar_du_vsp vsps[RCAR_DU_MAX_VSPS];
struct drm_bridge *lvds[RCAR_DU_MAX_LVDS];
struct { struct {
struct drm_property *colorkey; struct drm_property *colorkey;
......
...@@ -57,7 +57,6 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, ...@@ -57,7 +57,6 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
if (renc == NULL) if (renc == NULL)
return -ENOMEM; return -ENOMEM;
rcdu->encoders[output] = renc;
renc->output = output; renc->output = output;
encoder = rcar_encoder_to_drm_encoder(renc); encoder = rcar_encoder_to_drm_encoder(renc);
...@@ -91,6 +90,10 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, ...@@ -91,6 +90,10 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
ret = -EPROBE_DEFER; ret = -EPROBE_DEFER;
goto done; goto done;
} }
if (output == RCAR_DU_OUTPUT_LVDS0 ||
output == RCAR_DU_OUTPUT_LVDS1)
rcdu->lvds[output - RCAR_DU_OUTPUT_LVDS0] = bridge;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment