Hi Alex,

Below is a patch against the master branch of xf86-video-ati that adds 
support for waits on vblank events on CRTCs that are greater than 1 (and 
thus cannot be represented using current primary/secondary flags 
interface). The patch makes use of GET_CAP ioctl to check whether
vblanks on crtc > 1 are supported by the kernel. If they are not
falls back to legacy behavior. Otherwise, it uses correct crtc numbers
when waiting on vblank and thus corrects the problem seen in certain 
multiscreen configurations.

The issue was discussed on the dri-devel list in these two threads

http://lists.freedesktop.org/archives/dri-devel/2011-March/009009.html
http://lists.freedesktop.org/archives/dri-devel/2011-March/009025.html

regards,

Ilija

Reviewed-by: Mario Kleiner <mario.kleiner at tuebingen.mpg.de>
Acked-by: Mario Kleiner <mario.kleiner at tuebingen.mpg.de>

diff --git a/src/radeon.h b/src/radeon.h
index a6d20d7..1a746c7 100644
--- a/src/radeon.h
+++ b/src/radeon.h
@@ -931,6 +931,9 @@ typedef struct {

      RADEONFBLayout    CurrentLayout;

+#ifdef RADEON_DRI2
+    Bool              high_crtc_works;
+#endif
  #ifdef XF86DRI
      Bool              directRenderingEnabled;
      Bool              directRenderingInited;
diff --git a/src/radeon_dri2.c b/src/radeon_dri2.c
index 66df03c..ed27dad 100644
--- a/src/radeon_dri2.c
+++ b/src/radeon_dri2.c
@@ -783,6 +783,7 @@ static int radeon_dri2_get_msc(DrawablePtr draw, CARD64 
*ust, CARD64 *msc)
      drmVBlank vbl;
      int ret;
      int crtc = radeon_dri2_drawable_crtc(draw);
+    int high_crtc = 0;

      /* Drawable not displayed, make up a value */
      if (crtc == -1) {
@@ -791,8 +792,16 @@ static int radeon_dri2_get_msc(DrawablePtr draw, CARD64 
*ust, CARD64 *msc)
          return TRUE;
      }
      vbl.request.type = DRM_VBLANK_RELATIVE;
-    if (crtc > 0)
+    if (crtc == 1)
          vbl.request.type |= DRM_VBLANK_SECONDARY;
+    else if (crtc > 1) {
+       if (info->high_crtc_works) {
+           high_crtc = (crtc << DRM_VBLANK_HIGH_CRTC_SHIFT) &
+               DRM_VBLANK_HIGH_CRTC_MASK;
+       } else
+           vbl.request.type |= DRM_VBLANK_SECONDARY;
+    }
+    vbl.request.type |= high_crtc;
      vbl.request.sequence = 0;

      ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
@@ -825,6 +834,7 @@ static int radeon_dri2_schedule_wait_msc(ClientPtr client, 
DrawablePtr draw,
      drmVBlank vbl;
      int ret, crtc = radeon_dri2_drawable_crtc(draw);
      CARD64 current_msc;
+    int high_crtc = 0;

      /* Truncate to match kernel interfaces; means occasional overflow
       * misses, but that's generally not a big deal */
@@ -855,8 +865,16 @@ static int radeon_dri2_schedule_wait_msc(ClientPtr client, 
DrawablePtr draw,

      /* Get current count */
      vbl.request.type = DRM_VBLANK_RELATIVE;
-    if (crtc > 0)
+    if (crtc == 1)
          vbl.request.type |= DRM_VBLANK_SECONDARY;
+    else if (crtc > 1) {
+       if (info->high_crtc_works) {
+           high_crtc = (crtc << DRM_VBLANK_HIGH_CRTC_SHIFT) &
+               DRM_VBLANK_HIGH_CRTC_MASK;
+       } else
+           vbl.request.type |= DRM_VBLANK_SECONDARY;
+    }
+    vbl.request.type |= high_crtc;
      vbl.request.sequence = 0;
      ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
      if (ret) {
@@ -882,8 +900,16 @@ static int radeon_dri2_schedule_wait_msc(ClientPtr client, 
DrawablePtr draw,
          if (current_msc >= target_msc)
              target_msc = current_msc;
          vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
-        if (crtc > 0)
+        if (crtc == 1)
              vbl.request.type |= DRM_VBLANK_SECONDARY;
+       else if (crtc > 1) {
+           if (info->high_crtc_works) {
+               high_crtc = (crtc << DRM_VBLANK_HIGH_CRTC_SHIFT) &
+                   DRM_VBLANK_HIGH_CRTC_MASK;
+           } else
+               vbl.request.type |= DRM_VBLANK_SECONDARY;
+       }
+       vbl.request.type |= high_crtc;
          vbl.request.sequence = target_msc;
          vbl.request.signal = (unsigned long)wait_info;
          ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
@@ -903,8 +929,16 @@ static int radeon_dri2_schedule_wait_msc(ClientPtr client, 
DrawablePtr draw,
       * so we queue an event that will satisfy the divisor/remainder equation.
       */
      vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
-    if (crtc > 0)
+    if (crtc == 1)
          vbl.request.type |= DRM_VBLANK_SECONDARY;
+    else if (crtc > 1) {
+       if (info->high_crtc_works) {
+           high_crtc = (crtc << DRM_VBLANK_HIGH_CRTC_SHIFT) &
+               DRM_VBLANK_HIGH_CRTC_MASK;
+       } else
+           vbl.request.type |= DRM_VBLANK_SECONDARY;
+    }
+    vbl.request.type |= high_crtc;

      vbl.request.sequence = current_msc - (current_msc % divisor) +
          remainder;
@@ -1029,6 +1063,7 @@ static int radeon_dri2_schedule_swap(ClientPtr client, 
DrawablePtr draw,
      CARD64 current_msc;
      BoxRec box;
      RegionRec region;
+    int high_crtc = 0;

      /* Truncate to match kernel interfaces; means occasional overflow
       * misses, but that's generally not a big deal */
@@ -1068,8 +1103,16 @@ static int radeon_dri2_schedule_swap(ClientPtr client, 
DrawablePtr draw,

      /* Get current count */
      vbl.request.type = DRM_VBLANK_RELATIVE;
-    if (crtc > 0)
+    if (crtc == 1)
          vbl.request.type |= DRM_VBLANK_SECONDARY;
+    else if (crtc > 1) {
+       if (info->high_crtc_works) {
+           high_crtc = (crtc << DRM_VBLANK_HIGH_CRTC_SHIFT) &
+               DRM_VBLANK_HIGH_CRTC_MASK;
+       } else
+           vbl.request.type |= DRM_VBLANK_SECONDARY;
+    }
+    vbl.request.type |= high_crtc;
      vbl.request.sequence = 0;
      ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
      if (ret) {
@@ -1111,8 +1154,16 @@ static int radeon_dri2_schedule_swap(ClientPtr client, 
DrawablePtr draw,
           */
          if (flip == 0)
              vbl.request.type |= DRM_VBLANK_NEXTONMISS;
-        if (crtc > 0)
+        if (crtc == 1)
              vbl.request.type |= DRM_VBLANK_SECONDARY;
+       else if (crtc > 1) {
+           if (info->high_crtc_works) {
+               high_crtc = (crtc << DRM_VBLANK_HIGH_CRTC_SHIFT) &
+                   DRM_VBLANK_HIGH_CRTC_MASK;
+           } else
+               vbl.request.type |= DRM_VBLANK_SECONDARY;
+       }
+       vbl.request.type |= high_crtc;

          /* If target_msc already reached or passed, set it to
           * current_msc to ensure we return a reasonable value back
@@ -1145,8 +1196,16 @@ static int radeon_dri2_schedule_swap(ClientPtr client, 
DrawablePtr draw,
      vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
      if (flip == 0)
          vbl.request.type |= DRM_VBLANK_NEXTONMISS;
-    if (crtc > 0)
+    if (crtc == 1)
          vbl.request.type |= DRM_VBLANK_SECONDARY;
+    else if (crtc > 1) {
+       if (info->high_crtc_works) {
+           high_crtc = (crtc << DRM_VBLANK_HIGH_CRTC_SHIFT) &
+               DRM_VBLANK_HIGH_CRTC_MASK;
+       } else
+           vbl.request.type |= DRM_VBLANK_SECONDARY;
+    }
+    vbl.request.type |= high_crtc;

      vbl.request.sequence = current_msc - (current_msc % divisor) +
          remainder;
@@ -1217,6 +1276,7 @@ radeon_dri2_screen_init(ScreenPtr pScreen)
      DRI2InfoRec dri2_info = { 0 };
  #ifdef USE_DRI2_SCHEDULING
      const char *driverNames[1];
+    uint64_t cap_value;
  #endif

      if (!info->useEXA) {
@@ -1248,6 +1308,7 @@ radeon_dri2_screen_init(ScreenPtr pScreen)
  #endif
      dri2_info.CopyRegion = radeon_dri2_copy_region;

+    info->high_crtc_works = FALSE;
  #ifdef USE_DRI2_SCHEDULING
      if (info->dri->pKernelDRMVersion->version_minor >= 4) {
          dri2_info.version = 4;
@@ -1261,6 +1322,20 @@ radeon_dri2_screen_init(ScreenPtr pScreen)
          xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "You need a newer kernel for 
sync extension\n");
      }

+    if (info->drmmode.mode_res->count_crtcs > 2) {
+       if (drmGetCap(info->dri2.drm_fd, DRM_CAP_HIGH_CRTC, &cap_value)) {
+           info->high_crtc_works = FALSE;
+           xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "You need a newer kernel 
for VBLANKs on CRTC>1\n");
+       } else {
+           if (cap_value) {
+               info->high_crtc_works = TRUE;
+           } else {
+               xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Your kernel does not 
handle VBLANKs on CRTC>1\n");
+               info->high_crtc_works = FALSE;
+           }
+       }
+    }
+
      if (pRADEONEnt->dri2_info_cnt == 0) {
  #if HAS_DIXREGISTERPRIVATEKEY
        if (!dixRegisterPrivateKey(DRI2ClientEventsPrivateKey, PRIVATE_CLIENT, 
sizeof(DRI2ClientEventsRec))) {

Reply via email to