drm/radeon: Hide the HW cursor while it's out of bounds
authorMichel Dänzer <michel.daenzer@amd.com>
Thu, 27 Oct 2016 05:54:31 +0000 (14:54 +0900)
committerAlex Deucher <alexander.deucher@amd.com>
Tue, 6 Dec 2016 23:08:30 +0000 (18:08 -0500)
Fixes hangs in that case under some circumstances.

v2:
* Only use non-0 x/yorigin if the cursor is (partially) outside of the
  top/left edge of the total surface with AVIVO/DCE

Bugzilla: https://bugzilla.suse.com/show_bug.cgi?id=1000433
Cc: stable@vger.kernel.org
Signed-off-by: Michel Dänzer <michel.daenzer@amd.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com> (v1)
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/radeon/radeon_cursor.c
drivers/gpu/drm/radeon/radeon_mode.h

index a3405fc083abe6db84de15aa074be9e1eea119c5..fb16070b266e3f2de51243ba478f57d8fd956420 100644 (file)
@@ -90,6 +90,9 @@ static void radeon_show_cursor(struct drm_crtc *crtc)
        struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
        struct radeon_device *rdev = crtc->dev->dev_private;
 
+       if (radeon_crtc->cursor_out_of_bounds)
+               return;
+
        if (ASIC_IS_DCE4(rdev)) {
                WREG32(EVERGREEN_CUR_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset,
                       upper_32_bits(radeon_crtc->cursor_addr));
@@ -151,16 +154,17 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y)
                x += crtc->x;
                y += crtc->y;
        }
-       DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y);
 
-       if (x < 0) {
+       if (x < 0)
                xorigin = min(-x, radeon_crtc->max_cursor_width - 1);
-               x = 0;
-       }
-       if (y < 0) {
+       if (y < 0)
                yorigin = min(-y, radeon_crtc->max_cursor_height - 1);
-               y = 0;
+
+       if (!ASIC_IS_AVIVO(rdev)) {
+               x += crtc->x;
+               y += crtc->y;
        }
+       DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y);
 
        /* fixed on DCE6 and newer */
        if (ASIC_IS_AVIVO(rdev) && !ASIC_IS_DCE6(rdev)) {
@@ -183,27 +187,31 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y)
                if (i > 1) {
                        int cursor_end, frame_end;
 
-                       cursor_end = x - xorigin + w;
+                       cursor_end = x + w;
                        frame_end = crtc->x + crtc->mode.crtc_hdisplay;
                        if (cursor_end >= frame_end) {
                                w = w - (cursor_end - frame_end);
                                if (!(frame_end & 0x7f))
                                        w--;
-                       } else {
-                               if (!(cursor_end & 0x7f))
-                                       w--;
+                       } else if (cursor_end <= 0) {
+                               goto out_of_bounds;
+                       } else if (!(cursor_end & 0x7f)) {
+                               w--;
                        }
                        if (w <= 0) {
-                               w = 1;
-                               cursor_end = x - xorigin + w;
-                               if (!(cursor_end & 0x7f)) {
-                                       x--;
-                                       WARN_ON_ONCE(x < 0);
-                               }
+                               goto out_of_bounds;
                        }
                }
        }
 
+       if (x <= (crtc->x - w) || y <= (crtc->y - radeon_crtc->cursor_height) ||
+           x >= (crtc->x + crtc->mode.crtc_hdisplay) ||
+           y >= (crtc->y + crtc->mode.crtc_vdisplay))
+               goto out_of_bounds;
+
+       x += xorigin;
+       y += yorigin;
+
        if (ASIC_IS_DCE4(rdev)) {
                WREG32(EVERGREEN_CUR_POSITION + radeon_crtc->crtc_offset, (x << 16) | y);
                WREG32(EVERGREEN_CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin);
@@ -215,6 +223,9 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y)
                WREG32(AVIVO_D1CUR_SIZE + radeon_crtc->crtc_offset,
                       ((w - 1) << 16) | (radeon_crtc->cursor_height - 1));
        } else {
+               x -= crtc->x;
+               y -= crtc->y;
+
                if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)
                        y *= 2;
 
@@ -232,6 +243,19 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y)
                       yorigin * 256);
        }
 
+       if (radeon_crtc->cursor_out_of_bounds) {
+               radeon_crtc->cursor_out_of_bounds = false;
+               if (radeon_crtc->cursor_bo)
+                       radeon_show_cursor(crtc);
+       }
+
+       return 0;
+
+ out_of_bounds:
+       if (!radeon_crtc->cursor_out_of_bounds) {
+               radeon_hide_cursor(crtc);
+               radeon_crtc->cursor_out_of_bounds = true;
+       }
        return 0;
 }
 
@@ -308,12 +332,12 @@ int radeon_crtc_cursor_set2(struct drm_crtc *crtc,
                x = radeon_crtc->cursor_x + radeon_crtc->cursor_hot_x - hot_x;
                y = radeon_crtc->cursor_y + radeon_crtc->cursor_hot_y - hot_y;
 
-               radeon_cursor_move_locked(crtc, x, y);
-
                radeon_crtc->cursor_width = width;
                radeon_crtc->cursor_height = height;
                radeon_crtc->cursor_hot_x = hot_x;
                radeon_crtc->cursor_hot_y = hot_y;
+
+               radeon_cursor_move_locked(crtc, x, y);
        }
 
        radeon_show_cursor(crtc);
index bb75201a24ba923fd124c39fedbe860bdcbe3e07..f1da484864a9c554ee4d7f846c3a75b63740e005 100644 (file)
@@ -330,6 +330,7 @@ struct radeon_crtc {
        u16 lut_r[256], lut_g[256], lut_b[256];
        bool enabled;
        bool can_tile;
+       bool cursor_out_of_bounds;
        uint32_t crtc_offset;
        struct drm_gem_object *cursor_bo;
        uint64_t cursor_addr;