drm: rcar-du: Turn LVDS clock output on/off for DPAD0 output on D3/E3
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Wed, 16 Jan 2019 23:59:34 +0000 (01:59 +0200)
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Fri, 8 Feb 2019 00:25:56 +0000 (02:25 +0200)
On the D3 and E3 SoCs the LVDS PLL clock output provides the dot clock
to the DU channels, even when the LVDS outputs are not in use. Enable
and disable the LVDS clock output when enabling or disabling a CRTC
connected to the DPAD0 output.

Signed-off-by: Laurent Pinchart <laurent.pinchart+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 93ee0020c9b14939605165b932db0d53d8753e01..96175d48a9022494105cfd534b8d6f4c03631aff 100644 (file)
@@ -25,6 +25,7 @@
 #include "rcar_du_plane.h"
 #include "rcar_du_regs.h"
 #include "rcar_du_vsp.h"
+#include "rcar_lvds.h"
 
 static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg)
 {
@@ -656,8 +657,27 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
                                       struct drm_crtc_state *old_state)
 {
        struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+       struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(crtc->state);
+       struct rcar_du_device *rcdu = rcrtc->group->dev;
 
        rcar_du_crtc_get(rcrtc);
+
+       /*
+        * On D3/E3 the dot clock is provided by the LVDS encoder attached to
+        * the DU channel. We need to enable its clock output explicitly if
+        * the LVDS output is disabled.
+        */
+       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];
+               const struct drm_display_mode *mode =
+                       &crtc->state->adjusted_mode;
+
+               rcar_lvds_clk_enable(encoder->base.bridge,
+                                    mode->clock * 1000);
+       }
+
        rcar_du_crtc_start(rcrtc);
 }
 
@@ -665,10 +685,24 @@ static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
                                        struct drm_crtc_state *old_state)
 {
        struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+       struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(old_state);
+       struct rcar_du_device *rcdu = rcrtc->group->dev;
 
        rcar_du_crtc_stop(rcrtc);
        rcar_du_crtc_put(rcrtc);
 
+       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];
+
+               /*
+                * Disable the LVDS clock output, see
+                * rcar_du_crtc_atomic_enable().
+                */
+               rcar_lvds_clk_disable(encoder->base.bridge);
+       }
+
        spin_lock_irq(&crtc->dev->event_lock);
        if (crtc->state->event) {
                drm_crtc_send_vblank_event(crtc, crtc->state->event);
index 6c187d0bf7c29e546bf27d7c468151186a74c135..1327cd0df90a4b2f9fcf9632fc379aceba9f2e5d 100644 (file)
@@ -22,6 +22,7 @@ struct device;
 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 */
@@ -81,6 +82,8 @@ 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 rcar_du_vsp vsps[RCAR_DU_MAX_VSPS];
 
index ed7ccee705cb73f2e5fc556f8d9717756383ea52..8ee4e762f4e5599715973b61f945703ace6ae723 100644 (file)
@@ -41,6 +41,7 @@ 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);