drm/vc4: Fix support for interlaced modes on HDMI.
authorEric Anholt <eric@anholt.net>
Thu, 29 Sep 2016 00:30:25 +0000 (17:30 -0700)
committerEric Anholt <eric@anholt.net>
Thu, 6 Oct 2016 18:58:27 +0000 (11:58 -0700)
We really do need to be using the halved V fields.  I had been
confused by the code I was using as a reference because it stored
halved vsync fields but not halved vdisplay, so it looked like I only
needed to divide vdisplay by 2.

This reverts part of Mario's timestamping fixes that prevented
CRTC_HALVE_V from applying, and instead adjusts the timestamping code
to not use the crtc field in that case.

Fixes locking of 1920x1080x60i on my Dell 2408WFP.  There are black
bars on the top and bottom, but I suspect that might be an
under/overscan flags problem as opposed to video timings.

Signed-off-by: Eric Anholt <eric@anholt.net>
drivers/gpu/drm/vc4/vc4_crtc.c
drivers/gpu/drm/vc4/vc4_hdmi.c
drivers/gpu/drm/vc4/vc4_regs.h

index 2682f07d8f1e6d3dc89ad0858f136c511040b517..83cafea03eff720f9f7b80f484dedf22ac7f283f 100644 (file)
@@ -229,7 +229,7 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
         * and need to make things up in a approximative but consistent way.
         */
        ret |= DRM_SCANOUTPOS_IN_VBLANK;
-       vblank_lines = mode->crtc_vtotal - mode->crtc_vdisplay;
+       vblank_lines = mode->vtotal - mode->vdisplay;
 
        if (flags & DRM_CALLED_FROM_VBLIRQ) {
                /*
@@ -378,7 +378,6 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
        struct drm_crtc_state *state = crtc->state;
        struct drm_display_mode *mode = &state->adjusted_mode;
        bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
-       u32 vactive = (mode->vdisplay >> (interlace ? 1 : 0));
        u32 format = PV_CONTROL_FORMAT_24;
        bool debug_dump_regs = false;
        int clock_select = vc4_get_clock_select(crtc);
@@ -404,32 +403,46 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
                   VC4_SET_FIELD(mode->hdisplay, PV_HORZB_HACTIVE));
 
        CRTC_WRITE(PV_VERTA,
-                  VC4_SET_FIELD(mode->vtotal - mode->vsync_end,
+                  VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end,
                                 PV_VERTA_VBP) |
-                  VC4_SET_FIELD(mode->vsync_end - mode->vsync_start,
+                  VC4_SET_FIELD(mode->crtc_vsync_end - mode->crtc_vsync_start,
                                 PV_VERTA_VSYNC));
        CRTC_WRITE(PV_VERTB,
-                  VC4_SET_FIELD(mode->vsync_start - mode->vdisplay,
+                  VC4_SET_FIELD(mode->crtc_vsync_start - mode->crtc_vdisplay,
                                 PV_VERTB_VFP) |
-                  VC4_SET_FIELD(vactive, PV_VERTB_VACTIVE));
+                  VC4_SET_FIELD(mode->crtc_vdisplay, PV_VERTB_VACTIVE));
 
        if (interlace) {
                CRTC_WRITE(PV_VERTA_EVEN,
-                          VC4_SET_FIELD(mode->vtotal - mode->vsync_end - 1,
+                          VC4_SET_FIELD(mode->crtc_vtotal -
+                                        mode->crtc_vsync_end - 1,
                                         PV_VERTA_VBP) |
-                          VC4_SET_FIELD(mode->vsync_end - mode->vsync_start,
+                          VC4_SET_FIELD(mode->crtc_vsync_end -
+                                        mode->crtc_vsync_start,
                                         PV_VERTA_VSYNC));
                CRTC_WRITE(PV_VERTB_EVEN,
-                          VC4_SET_FIELD(mode->vsync_start - mode->vdisplay,
+                          VC4_SET_FIELD(mode->crtc_vsync_start -
+                                        mode->crtc_vdisplay,
                                         PV_VERTB_VFP) |
-                          VC4_SET_FIELD(vactive, PV_VERTB_VACTIVE));
+                          VC4_SET_FIELD(mode->crtc_vdisplay, PV_VERTB_VACTIVE));
+
+               /* We set up first field even mode for HDMI.  VEC's
+                * NTSC mode would want first field odd instead, once
+                * we support it (to do so, set ODD_FIRST and put the
+                * delay in VSYNCD_EVEN instead).
+                */
+               CRTC_WRITE(PV_V_CONTROL,
+                          PV_VCONTROL_CONTINUOUS |
+                          PV_VCONTROL_INTERLACE |
+                          VC4_SET_FIELD(mode->htotal / 2,
+                                        PV_VCONTROL_ODD_DELAY));
+               CRTC_WRITE(PV_VSYNCD_EVEN, 0);
+       } else {
+               CRTC_WRITE(PV_V_CONTROL, PV_VCONTROL_CONTINUOUS);
        }
 
        CRTC_WRITE(PV_HACT_ACT, mode->hdisplay);
 
-       CRTC_WRITE(PV_V_CONTROL,
-                  PV_VCONTROL_CONTINUOUS |
-                  (interlace ? PV_VCONTROL_INTERLACE : 0));
 
        CRTC_WRITE(PV_CONTROL,
                   VC4_SET_FIELD(format, PV_CONTROL_FORMAT) |
@@ -544,16 +557,6 @@ static bool vc4_crtc_mode_fixup(struct drm_crtc *crtc,
                return false;
        }
 
-       /*
-        * Interlaced video modes got CRTC_INTERLACE_HALVE_V applied when
-        * coming from user space. We don't want this, as it screws up
-        * vblank timestamping, so fix it up.
-        */
-       drm_mode_set_crtcinfo(adjusted_mode, 0);
-
-       DRM_DEBUG_KMS("[CRTC:%d] adjusted_mode :\n", crtc->base.id);
-       drm_mode_debug_printmodeline(adjusted_mode);
-
        return true;
 }
 
index fe1c4e35e68142839756df10ed9b9e56c028d04e..d94108ca961dac9278d2e38813d089b78acbc8ad 100644 (file)
@@ -211,35 +211,10 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
        return ret;
 }
 
-/*
- * drm_helper_probe_single_connector_modes() applies drm_mode_set_crtcinfo to
- * all modes with flag CRTC_INTERLACE_HALVE_V. We don't want this, as it
- * screws up vblank timestamping for interlaced modes, so fix it up.
- */
-static int vc4_hdmi_connector_probe_modes(struct drm_connector *connector,
-                                         uint32_t maxX, uint32_t maxY)
-{
-       struct drm_display_mode *mode;
-       int count;
-
-       count = drm_helper_probe_single_connector_modes(connector, maxX, maxY);
-       if (count == 0)
-               return 0;
-
-       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed adapted modes :\n",
-                     connector->base.id, connector->name);
-       list_for_each_entry(mode, &connector->modes, head) {
-               drm_mode_set_crtcinfo(mode, 0);
-               drm_mode_debug_printmodeline(mode);
-       }
-
-       return count;
-}
-
 static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
        .dpms = drm_atomic_helper_connector_dpms,
        .detect = vc4_hdmi_connector_detect,
-       .fill_modes = vc4_hdmi_connector_probe_modes,
+       .fill_modes = drm_helper_probe_single_connector_modes,
        .destroy = vc4_hdmi_connector_destroy,
        .reset = drm_atomic_helper_connector_reset,
        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@@ -307,16 +282,20 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
        bool debug_dump_regs = false;
        bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
        bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
-       u32 vactive = (mode->vdisplay >>
-                      ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0));
-       u32 verta = (VC4_SET_FIELD(mode->vsync_end - mode->vsync_start,
+       bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
+       u32 verta = (VC4_SET_FIELD(mode->crtc_vsync_end - mode->crtc_vsync_start,
                                   VC4_HDMI_VERTA_VSP) |
-                    VC4_SET_FIELD(mode->vsync_start - mode->vdisplay,
+                    VC4_SET_FIELD(mode->crtc_vsync_start - mode->crtc_vdisplay,
                                   VC4_HDMI_VERTA_VFP) |
-                    VC4_SET_FIELD(vactive, VC4_HDMI_VERTA_VAL));
+                    VC4_SET_FIELD(mode->crtc_vdisplay, VC4_HDMI_VERTA_VAL));
        u32 vertb = (VC4_SET_FIELD(0, VC4_HDMI_VERTB_VSPO) |
-                    VC4_SET_FIELD(mode->vtotal - mode->vsync_end,
+                    VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end,
                                   VC4_HDMI_VERTB_VBP));
+       u32 vertb_even = (VC4_SET_FIELD(0, VC4_HDMI_VERTB_VSPO) |
+                         VC4_SET_FIELD(mode->crtc_vtotal -
+                                       mode->crtc_vsync_end -
+                                       interlaced,
+                                       VC4_HDMI_VERTB_VBP));
        u32 csc_ctl;
 
        if (debug_dump_regs) {
@@ -349,7 +328,7 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
        HDMI_WRITE(VC4_HDMI_VERTA0, verta);
        HDMI_WRITE(VC4_HDMI_VERTA1, verta);
 
-       HDMI_WRITE(VC4_HDMI_VERTB0, vertb);
+       HDMI_WRITE(VC4_HDMI_VERTB0, vertb_even);
        HDMI_WRITE(VC4_HDMI_VERTB1, vertb);
 
        HD_WRITE(VC4_HD_VID_CTL,
index 9ecd6ff3d493ffca5249256ce99dc2de2b21e0ee..c5a423ead86ffdd1ce2abada24f628b19c1ebc08 100644 (file)
 # define PV_CONTROL_EN                         BIT(0)
 
 #define PV_V_CONTROL                           0x04
+# define PV_VCONTROL_ODD_DELAY_MASK            VC4_MASK(22, 6)
+# define PV_VCONTROL_ODD_DELAY_SHIFT           6
+# define PV_VCONTROL_ODD_FIRST                 BIT(5)
 # define PV_VCONTROL_INTERLACE                 BIT(4)
 # define PV_VCONTROL_CONTINUOUS                        BIT(1)
 # define PV_VCONTROL_VIDEN                     BIT(0)