Merge tag 'drm-vc4-next-2016-08-29' of https://github.com/anholt/linux into drm-next
authorDave Airlie <airlied@redhat.com>
Fri, 2 Sep 2016 05:50:19 +0000 (15:50 +1000)
committerDave Airlie <airlied@redhat.com>
Fri, 2 Sep 2016 05:50:19 +0000 (15:50 +1000)
This pull request brings in interlaced vblank timing and a 3D
rendering memory/CPU overhead reduction.

* tag 'drm-vc4-next-2016-08-29' of https://github.com/anholt/linux:
  drm/vc4: Don't force new binner overflow allocation per draw.
  drm/vc4: Enable/Disable vblanks properly in crtc en/disable.
  drm/vc4: Enable precise vblank timestamping for interlaced modes.
  drm/vc4: Reject doublescan modes.
  drm/vc4: Fix handling of interlaced video modes.
  drm/vc4: Disallow interlaced modes on DPI.

drivers/gpu/drm/vc4/vc4_crtc.c
drivers/gpu/drm/vc4/vc4_dpi.c
drivers/gpu/drm/vc4/vc4_gem.c
drivers/gpu/drm/vc4/vc4_hdmi.c

index 8fc2b73..2682f07 100644 (file)
@@ -163,14 +163,6 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
        int vblank_lines;
        int ret = 0;
 
-       /*
-        * XXX Doesn't work well in interlaced mode yet, partially due
-        * to problems in vc4 kms or drm core interlaced mode handling,
-        * so disable for now in interlaced mode.
-        */
-       if (mode->flags & DRM_MODE_FLAG_INTERLACE)
-               return ret;
-
        /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
 
        /* Get optional system timestamp before query. */
@@ -191,10 +183,15 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
 
        /* Vertical position of hvs composed scanline. */
        *vpos = VC4_GET_FIELD(val, SCALER_DISPSTATX_LINE);
+       *hpos = 0;
 
-       /* No hpos info available. */
-       if (hpos)
-               *hpos = 0;
+       if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+               *vpos /= 2;
+
+               /* Use hpos to correct for field offset in interlaced mode. */
+               if (VC4_GET_FIELD(val, SCALER_DISPSTATX_FRAME_COUNT) % 2)
+                       *hpos += mode->crtc_htotal / 2;
+       }
 
        /* This is the offset we need for translating hvs -> pv scanout pos. */
        fifo_lines = vc4_crtc->cob_size / mode->crtc_hdisplay;
@@ -217,8 +214,6 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
                 * position of the PV.
                 */
                *vpos -= fifo_lines + 1;
-               if (mode->flags & DRM_MODE_FLAG_INTERLACE)
-                       *vpos /= 2;
 
                ret |= DRM_SCANOUTPOS_ACCURATE;
                return ret;
@@ -480,6 +475,9 @@ static void vc4_crtc_disable(struct drm_crtc *crtc)
        int ret;
        require_hvs_enabled(dev);
 
+       /* Disable vblank irq handling before crtc is disabled. */
+       drm_crtc_vblank_off(crtc);
+
        CRTC_WRITE(PV_V_CONTROL,
                   CRTC_READ(PV_V_CONTROL) & ~PV_VCONTROL_VIDEN);
        ret = wait_for(!(CRTC_READ(PV_V_CONTROL) & PV_VCONTROL_VIDEN), 1);
@@ -530,6 +528,33 @@ static void vc4_crtc_enable(struct drm_crtc *crtc)
        /* Turn on the pixel valve, which will emit the vstart signal. */
        CRTC_WRITE(PV_V_CONTROL,
                   CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN);
+
+       /* Enable vblank irq handling after crtc is started. */
+       drm_crtc_vblank_on(crtc);
+}
+
+static bool vc4_crtc_mode_fixup(struct drm_crtc *crtc,
+                               const struct drm_display_mode *mode,
+                               struct drm_display_mode *adjusted_mode)
+{
+       /* Do not allow doublescan modes from user space */
+       if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) {
+               DRM_DEBUG_KMS("[CRTC:%d] Doublescan mode rejected.\n",
+                             crtc->base.id);
+               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;
 }
 
 static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
@@ -819,6 +844,7 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
        .mode_set_nofb = vc4_crtc_mode_set_nofb,
        .disable = vc4_crtc_disable,
        .enable = vc4_crtc_enable,
+       .mode_fixup = vc4_crtc_mode_fixup,
        .atomic_check = vc4_crtc_atomic_check,
        .atomic_flush = vc4_crtc_atomic_flush,
 };
index 275fedb..1e1f6b8 100644 (file)
@@ -340,9 +340,20 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
        }
 }
 
+static bool vc4_dpi_encoder_mode_fixup(struct drm_encoder *encoder,
+                                      const struct drm_display_mode *mode,
+                                      struct drm_display_mode *adjusted_mode)
+{
+       if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)
+               return false;
+
+       return true;
+}
+
 static const struct drm_encoder_helper_funcs vc4_dpi_encoder_helper_funcs = {
        .disable = vc4_dpi_encoder_disable,
        .enable = vc4_dpi_encoder_enable,
+       .mode_fixup = vc4_dpi_encoder_mode_fixup,
 };
 
 static const struct of_device_id vc4_dpi_dt_match[] = {
index 6155e8a..27c52ec 100644 (file)
@@ -419,10 +419,6 @@ again:
 
        vc4_flush_caches(dev);
 
-       /* Disable the binner's pre-loaded overflow memory address */
-       V3D_WRITE(V3D_BPOA, 0);
-       V3D_WRITE(V3D_BPOS, 0);
-
        /* Either put the job in the binner if it uses the binner, or
         * immediately move it to the to-be-rendered queue.
         */
index 4452f36..68ad106 100644 (file)
@@ -208,10 +208,35 @@ 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 = drm_helper_probe_single_connector_modes,
+       .fill_modes = vc4_hdmi_connector_probe_modes,
        .destroy = vc4_hdmi_connector_destroy,
        .reset = drm_atomic_helper_connector_reset,
        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@@ -246,7 +271,7 @@ static struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
        connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
                             DRM_CONNECTOR_POLL_DISCONNECT);
 
-       connector->interlace_allowed = 0;
+       connector->interlace_allowed = 1;
        connector->doublescan_allowed = 0;
 
        drm_mode_connector_attach_encoder(connector, encoder);