1771fe6b9SJerome Glisse /* 2771fe6b9SJerome Glisse * Copyright 2007-8 Advanced Micro Devices, Inc. 3771fe6b9SJerome Glisse * Copyright 2008 Red Hat Inc. 4771fe6b9SJerome Glisse * 5771fe6b9SJerome Glisse * Permission is hereby granted, free of charge, to any person obtaining a 6771fe6b9SJerome Glisse * copy of this software and associated documentation files (the "Software"), 7771fe6b9SJerome Glisse * to deal in the Software without restriction, including without limitation 8771fe6b9SJerome Glisse * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9771fe6b9SJerome Glisse * and/or sell copies of the Software, and to permit persons to whom the 10771fe6b9SJerome Glisse * Software is furnished to do so, subject to the following conditions: 11771fe6b9SJerome Glisse * 12771fe6b9SJerome Glisse * The above copyright notice and this permission notice shall be included in 13771fe6b9SJerome Glisse * all copies or substantial portions of the Software. 14771fe6b9SJerome Glisse * 15771fe6b9SJerome Glisse * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16771fe6b9SJerome Glisse * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17771fe6b9SJerome Glisse * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18771fe6b9SJerome Glisse * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 19771fe6b9SJerome Glisse * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20771fe6b9SJerome Glisse * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21771fe6b9SJerome Glisse * OTHER DEALINGS IN THE SOFTWARE. 22771fe6b9SJerome Glisse * 23771fe6b9SJerome Glisse * Authors: Dave Airlie 24771fe6b9SJerome Glisse * Alex Deucher 25771fe6b9SJerome Glisse */ 26771fe6b9SJerome Glisse #include "drmP.h" 27771fe6b9SJerome Glisse #include "radeon_drm.h" 28771fe6b9SJerome Glisse #include "radeon.h" 29771fe6b9SJerome Glisse 30771fe6b9SJerome Glisse #define CURSOR_WIDTH 64 31771fe6b9SJerome Glisse #define CURSOR_HEIGHT 64 32771fe6b9SJerome Glisse 33771fe6b9SJerome Glisse static void radeon_lock_cursor(struct drm_crtc *crtc, bool lock) 34771fe6b9SJerome Glisse { 35771fe6b9SJerome Glisse struct radeon_device *rdev = crtc->dev->dev_private; 36771fe6b9SJerome Glisse struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 37771fe6b9SJerome Glisse uint32_t cur_lock; 38771fe6b9SJerome Glisse 39bcc1c2a1SAlex Deucher if (ASIC_IS_DCE4(rdev)) { 40bcc1c2a1SAlex Deucher cur_lock = RREG32(EVERGREEN_CUR_UPDATE + radeon_crtc->crtc_offset); 41bcc1c2a1SAlex Deucher if (lock) 42bcc1c2a1SAlex Deucher cur_lock |= EVERGREEN_CURSOR_UPDATE_LOCK; 43bcc1c2a1SAlex Deucher else 44bcc1c2a1SAlex Deucher cur_lock &= ~EVERGREEN_CURSOR_UPDATE_LOCK; 45bcc1c2a1SAlex Deucher WREG32(EVERGREEN_CUR_UPDATE + radeon_crtc->crtc_offset, cur_lock); 46bcc1c2a1SAlex Deucher } else if (ASIC_IS_AVIVO(rdev)) { 47771fe6b9SJerome Glisse cur_lock = RREG32(AVIVO_D1CUR_UPDATE + radeon_crtc->crtc_offset); 48771fe6b9SJerome Glisse if (lock) 49771fe6b9SJerome Glisse cur_lock |= AVIVO_D1CURSOR_UPDATE_LOCK; 50771fe6b9SJerome Glisse else 51771fe6b9SJerome Glisse cur_lock &= ~AVIVO_D1CURSOR_UPDATE_LOCK; 52771fe6b9SJerome Glisse WREG32(AVIVO_D1CUR_UPDATE + radeon_crtc->crtc_offset, cur_lock); 53771fe6b9SJerome Glisse } else { 54771fe6b9SJerome Glisse cur_lock = RREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset); 55771fe6b9SJerome Glisse if (lock) 56771fe6b9SJerome Glisse cur_lock |= RADEON_CUR_LOCK; 57771fe6b9SJerome Glisse else 58771fe6b9SJerome Glisse cur_lock &= ~RADEON_CUR_LOCK; 59771fe6b9SJerome Glisse WREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset, cur_lock); 60771fe6b9SJerome Glisse } 61771fe6b9SJerome Glisse } 62771fe6b9SJerome Glisse 63771fe6b9SJerome Glisse static void radeon_hide_cursor(struct drm_crtc *crtc) 64771fe6b9SJerome Glisse { 65771fe6b9SJerome Glisse struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 66771fe6b9SJerome Glisse struct radeon_device *rdev = crtc->dev->dev_private; 67771fe6b9SJerome Glisse 68bcc1c2a1SAlex Deucher if (ASIC_IS_DCE4(rdev)) { 69bcc1c2a1SAlex Deucher WREG32(RADEON_MM_INDEX, EVERGREEN_CUR_CONTROL + radeon_crtc->crtc_offset); 70bcc1c2a1SAlex Deucher WREG32(RADEON_MM_DATA, EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT)); 71bcc1c2a1SAlex Deucher } else if (ASIC_IS_AVIVO(rdev)) { 72771fe6b9SJerome Glisse WREG32(RADEON_MM_INDEX, AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset); 73771fe6b9SJerome Glisse WREG32(RADEON_MM_DATA, (AVIVO_D1CURSOR_MODE_24BPP << AVIVO_D1CURSOR_MODE_SHIFT)); 74771fe6b9SJerome Glisse } else { 75771fe6b9SJerome Glisse switch (radeon_crtc->crtc_id) { 76771fe6b9SJerome Glisse case 0: 77771fe6b9SJerome Glisse WREG32(RADEON_MM_INDEX, RADEON_CRTC_GEN_CNTL); 78771fe6b9SJerome Glisse break; 79771fe6b9SJerome Glisse case 1: 80771fe6b9SJerome Glisse WREG32(RADEON_MM_INDEX, RADEON_CRTC2_GEN_CNTL); 81771fe6b9SJerome Glisse break; 82771fe6b9SJerome Glisse default: 83771fe6b9SJerome Glisse return; 84771fe6b9SJerome Glisse } 85771fe6b9SJerome Glisse WREG32_P(RADEON_MM_DATA, 0, ~RADEON_CRTC_CUR_EN); 86771fe6b9SJerome Glisse } 87771fe6b9SJerome Glisse } 88771fe6b9SJerome Glisse 89771fe6b9SJerome Glisse static void radeon_show_cursor(struct drm_crtc *crtc) 90771fe6b9SJerome Glisse { 91771fe6b9SJerome Glisse struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 92771fe6b9SJerome Glisse struct radeon_device *rdev = crtc->dev->dev_private; 93771fe6b9SJerome Glisse 94bcc1c2a1SAlex Deucher if (ASIC_IS_DCE4(rdev)) { 95bcc1c2a1SAlex Deucher WREG32(RADEON_MM_INDEX, EVERGREEN_CUR_CONTROL + radeon_crtc->crtc_offset); 96bcc1c2a1SAlex Deucher WREG32(RADEON_MM_DATA, EVERGREEN_CURSOR_EN | 97bcc1c2a1SAlex Deucher EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT)); 98bcc1c2a1SAlex Deucher } else if (ASIC_IS_AVIVO(rdev)) { 99771fe6b9SJerome Glisse WREG32(RADEON_MM_INDEX, AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset); 100771fe6b9SJerome Glisse WREG32(RADEON_MM_DATA, AVIVO_D1CURSOR_EN | 101771fe6b9SJerome Glisse (AVIVO_D1CURSOR_MODE_24BPP << AVIVO_D1CURSOR_MODE_SHIFT)); 102771fe6b9SJerome Glisse } else { 103771fe6b9SJerome Glisse switch (radeon_crtc->crtc_id) { 104771fe6b9SJerome Glisse case 0: 105771fe6b9SJerome Glisse WREG32(RADEON_MM_INDEX, RADEON_CRTC_GEN_CNTL); 106771fe6b9SJerome Glisse break; 107771fe6b9SJerome Glisse case 1: 108771fe6b9SJerome Glisse WREG32(RADEON_MM_INDEX, RADEON_CRTC2_GEN_CNTL); 109771fe6b9SJerome Glisse break; 110771fe6b9SJerome Glisse default: 111771fe6b9SJerome Glisse return; 112771fe6b9SJerome Glisse } 113771fe6b9SJerome Glisse 114771fe6b9SJerome Glisse WREG32_P(RADEON_MM_DATA, (RADEON_CRTC_CUR_EN | 115771fe6b9SJerome Glisse (RADEON_CRTC_CUR_MODE_24BPP << RADEON_CRTC_CUR_MODE_SHIFT)), 116771fe6b9SJerome Glisse ~(RADEON_CRTC_CUR_EN | RADEON_CRTC_CUR_MODE_MASK)); 117771fe6b9SJerome Glisse } 118771fe6b9SJerome Glisse } 119771fe6b9SJerome Glisse 120771fe6b9SJerome Glisse static void radeon_set_cursor(struct drm_crtc *crtc, struct drm_gem_object *obj, 121f981d463SAlex Deucher uint64_t gpu_addr) 122771fe6b9SJerome Glisse { 123771fe6b9SJerome Glisse struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 124771fe6b9SJerome Glisse struct radeon_device *rdev = crtc->dev->dev_private; 125771fe6b9SJerome Glisse 126bcc1c2a1SAlex Deucher if (ASIC_IS_DCE4(rdev)) { 127f981d463SAlex Deucher WREG32(EVERGREEN_CUR_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset, 128f981d463SAlex Deucher upper_32_bits(gpu_addr)); 129f981d463SAlex Deucher WREG32(EVERGREEN_CUR_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 130f981d463SAlex Deucher gpu_addr & 0xffffffff); 131bcc1c2a1SAlex Deucher } else if (ASIC_IS_AVIVO(rdev)) { 132c290dadfSAlex Deucher if (rdev->family >= CHIP_RV770) { 133c290dadfSAlex Deucher if (radeon_crtc->crtc_id) 134f981d463SAlex Deucher WREG32(R700_D2CUR_SURFACE_ADDRESS_HIGH, upper_32_bits(gpu_addr)); 135c290dadfSAlex Deucher else 136f981d463SAlex Deucher WREG32(R700_D1CUR_SURFACE_ADDRESS_HIGH, upper_32_bits(gpu_addr)); 137c290dadfSAlex Deucher } 138f981d463SAlex Deucher WREG32(AVIVO_D1CUR_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 139f981d463SAlex Deucher gpu_addr & 0xffffffff); 140c290dadfSAlex Deucher } else { 141c836e862SAlex Deucher radeon_crtc->legacy_cursor_offset = gpu_addr - radeon_crtc->legacy_display_base_addr; 142771fe6b9SJerome Glisse /* offset is from DISP(2)_BASE_ADDRESS */ 143c836e862SAlex Deucher WREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset, radeon_crtc->legacy_cursor_offset); 144c836e862SAlex Deucher } 145771fe6b9SJerome Glisse } 146771fe6b9SJerome Glisse 147771fe6b9SJerome Glisse int radeon_crtc_cursor_set(struct drm_crtc *crtc, 148771fe6b9SJerome Glisse struct drm_file *file_priv, 149771fe6b9SJerome Glisse uint32_t handle, 150771fe6b9SJerome Glisse uint32_t width, 151771fe6b9SJerome Glisse uint32_t height) 152771fe6b9SJerome Glisse { 153771fe6b9SJerome Glisse struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 154c4353016SMichel Dänzer struct radeon_device *rdev = crtc->dev->dev_private; 155771fe6b9SJerome Glisse struct drm_gem_object *obj; 156c4353016SMichel Dänzer struct radeon_bo *robj; 157771fe6b9SJerome Glisse uint64_t gpu_addr; 158771fe6b9SJerome Glisse int ret; 159771fe6b9SJerome Glisse 160771fe6b9SJerome Glisse if (!handle) { 161771fe6b9SJerome Glisse /* turn off cursor */ 162771fe6b9SJerome Glisse radeon_hide_cursor(crtc); 163771fe6b9SJerome Glisse obj = NULL; 164771fe6b9SJerome Glisse goto unpin; 165771fe6b9SJerome Glisse } 166771fe6b9SJerome Glisse 167771fe6b9SJerome Glisse if ((width > CURSOR_WIDTH) || (height > CURSOR_HEIGHT)) { 168771fe6b9SJerome Glisse DRM_ERROR("bad cursor width or height %d x %d\n", width, height); 169771fe6b9SJerome Glisse return -EINVAL; 170771fe6b9SJerome Glisse } 171771fe6b9SJerome Glisse 172771fe6b9SJerome Glisse obj = drm_gem_object_lookup(crtc->dev, file_priv, handle); 173771fe6b9SJerome Glisse if (!obj) { 174771fe6b9SJerome Glisse DRM_ERROR("Cannot find cursor object %x for crtc %d\n", handle, radeon_crtc->crtc_id); 175bf79cb91SChris Wilson return -ENOENT; 176771fe6b9SJerome Glisse } 177771fe6b9SJerome Glisse 178c4353016SMichel Dänzer robj = gem_to_radeon_bo(obj); 179c4353016SMichel Dänzer ret = radeon_bo_reserve(robj, false); 180c4353016SMichel Dänzer if (unlikely(ret != 0)) 181c4353016SMichel Dänzer goto fail; 182c4353016SMichel Dänzer /* Only 27 bit offset for legacy cursor */ 183c4353016SMichel Dänzer ret = radeon_bo_pin_restricted(robj, RADEON_GEM_DOMAIN_VRAM, 184c4353016SMichel Dänzer ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27, 185c4353016SMichel Dänzer &gpu_addr); 186c4353016SMichel Dänzer radeon_bo_unreserve(robj); 187771fe6b9SJerome Glisse if (ret) 188771fe6b9SJerome Glisse goto fail; 189771fe6b9SJerome Glisse 19045e5f6a2SIlija Hadzic radeon_crtc->cursor_width = width; 19145e5f6a2SIlija Hadzic radeon_crtc->cursor_height = height; 19245e5f6a2SIlija Hadzic 193771fe6b9SJerome Glisse radeon_lock_cursor(crtc, true); 194771fe6b9SJerome Glisse radeon_set_cursor(crtc, obj, gpu_addr); 195771fe6b9SJerome Glisse radeon_show_cursor(crtc); 196771fe6b9SJerome Glisse radeon_lock_cursor(crtc, false); 197771fe6b9SJerome Glisse 198771fe6b9SJerome Glisse unpin: 199771fe6b9SJerome Glisse if (radeon_crtc->cursor_bo) { 200*654c59cfSMichel Dänzer robj = gem_to_radeon_bo(radeon_crtc->cursor_bo); 201*654c59cfSMichel Dänzer ret = radeon_bo_reserve(robj, false); 202*654c59cfSMichel Dänzer if (likely(ret == 0)) { 203*654c59cfSMichel Dänzer radeon_bo_unpin(robj); 204*654c59cfSMichel Dänzer radeon_bo_unreserve(robj); 205*654c59cfSMichel Dänzer } 206bc9025bdSLuca Barbieri drm_gem_object_unreference_unlocked(radeon_crtc->cursor_bo); 207771fe6b9SJerome Glisse } 208771fe6b9SJerome Glisse 209771fe6b9SJerome Glisse radeon_crtc->cursor_bo = obj; 210771fe6b9SJerome Glisse return 0; 211771fe6b9SJerome Glisse fail: 212bc9025bdSLuca Barbieri drm_gem_object_unreference_unlocked(obj); 213771fe6b9SJerome Glisse 2144cdb82b9SMatt Turner return ret; 215771fe6b9SJerome Glisse } 216771fe6b9SJerome Glisse 217771fe6b9SJerome Glisse int radeon_crtc_cursor_move(struct drm_crtc *crtc, 218771fe6b9SJerome Glisse int x, int y) 219771fe6b9SJerome Glisse { 220771fe6b9SJerome Glisse struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 221771fe6b9SJerome Glisse struct radeon_device *rdev = crtc->dev->dev_private; 222771fe6b9SJerome Glisse int xorigin = 0, yorigin = 0; 2236a2a11dbSAlex Deucher int w = radeon_crtc->cursor_width; 224771fe6b9SJerome Glisse 225b8aee294SMichel Dänzer if (ASIC_IS_AVIVO(rdev)) { 226b8aee294SMichel Dänzer /* avivo cursor are offset into the total surface */ 227b8aee294SMichel Dänzer x += crtc->x; 228b8aee294SMichel Dänzer y += crtc->y; 229b8aee294SMichel Dänzer } 230b8aee294SMichel Dänzer DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y); 231b8aee294SMichel Dänzer 23202e6859eSMichel Dänzer if (x < 0) { 2337d309529SMichel Dänzer xorigin = min(-x, CURSOR_WIDTH - 1); 23402e6859eSMichel Dänzer x = 0; 23502e6859eSMichel Dänzer } 23602e6859eSMichel Dänzer if (y < 0) { 2377d309529SMichel Dänzer yorigin = min(-y, CURSOR_HEIGHT - 1); 23802e6859eSMichel Dänzer y = 0; 23902e6859eSMichel Dänzer } 240771fe6b9SJerome Glisse 2416a2a11dbSAlex Deucher if (ASIC_IS_AVIVO(rdev)) { 242771fe6b9SJerome Glisse int i = 0; 243771fe6b9SJerome Glisse struct drm_crtc *crtc_p; 244771fe6b9SJerome Glisse 24525985edcSLucas De Marchi /* avivo cursor image can't end on 128 pixel boundary or 246771fe6b9SJerome Glisse * go past the end of the frame if both crtcs are enabled 247771fe6b9SJerome Glisse */ 248771fe6b9SJerome Glisse list_for_each_entry(crtc_p, &crtc->dev->mode_config.crtc_list, head) { 249771fe6b9SJerome Glisse if (crtc_p->enabled) 250771fe6b9SJerome Glisse i++; 251771fe6b9SJerome Glisse } 252771fe6b9SJerome Glisse if (i > 1) { 253771fe6b9SJerome Glisse int cursor_end, frame_end; 254771fe6b9SJerome Glisse 255771fe6b9SJerome Glisse cursor_end = x - xorigin + w; 256771fe6b9SJerome Glisse frame_end = crtc->x + crtc->mode.crtc_hdisplay; 257771fe6b9SJerome Glisse if (cursor_end >= frame_end) { 258771fe6b9SJerome Glisse w = w - (cursor_end - frame_end); 259771fe6b9SJerome Glisse if (!(frame_end & 0x7f)) 260771fe6b9SJerome Glisse w--; 261771fe6b9SJerome Glisse } else { 262771fe6b9SJerome Glisse if (!(cursor_end & 0x7f)) 263771fe6b9SJerome Glisse w--; 264771fe6b9SJerome Glisse } 265771fe6b9SJerome Glisse if (w <= 0) 266771fe6b9SJerome Glisse w = 1; 267771fe6b9SJerome Glisse } 2686a2a11dbSAlex Deucher } 269771fe6b9SJerome Glisse 2706a2a11dbSAlex Deucher radeon_lock_cursor(crtc, true); 2716a2a11dbSAlex Deucher if (ASIC_IS_DCE4(rdev)) { 27202e6859eSMichel Dänzer WREG32(EVERGREEN_CUR_POSITION + radeon_crtc->crtc_offset, (x << 16) | y); 2736a2a11dbSAlex Deucher WREG32(EVERGREEN_CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin); 2746a2a11dbSAlex Deucher WREG32(EVERGREEN_CUR_SIZE + radeon_crtc->crtc_offset, 2756a2a11dbSAlex Deucher ((w - 1) << 16) | (radeon_crtc->cursor_height - 1)); 2766a2a11dbSAlex Deucher } else if (ASIC_IS_AVIVO(rdev)) { 27702e6859eSMichel Dänzer WREG32(AVIVO_D1CUR_POSITION + radeon_crtc->crtc_offset, (x << 16) | y); 278771fe6b9SJerome Glisse WREG32(AVIVO_D1CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin); 279771fe6b9SJerome Glisse WREG32(AVIVO_D1CUR_SIZE + radeon_crtc->crtc_offset, 280771fe6b9SJerome Glisse ((w - 1) << 16) | (radeon_crtc->cursor_height - 1)); 281771fe6b9SJerome Glisse } else { 282771fe6b9SJerome Glisse if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN) 283771fe6b9SJerome Glisse y *= 2; 284771fe6b9SJerome Glisse 285771fe6b9SJerome Glisse WREG32(RADEON_CUR_HORZ_VERT_OFF + radeon_crtc->crtc_offset, 286771fe6b9SJerome Glisse (RADEON_CUR_LOCK 287771fe6b9SJerome Glisse | (xorigin << 16) 288771fe6b9SJerome Glisse | yorigin)); 289771fe6b9SJerome Glisse WREG32(RADEON_CUR_HORZ_VERT_POSN + radeon_crtc->crtc_offset, 290771fe6b9SJerome Glisse (RADEON_CUR_LOCK 29102e6859eSMichel Dänzer | (x << 16) 29202e6859eSMichel Dänzer | y)); 293c836e862SAlex Deucher /* offset is from DISP(2)_BASE_ADDRESS */ 294c836e862SAlex Deucher WREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset, (radeon_crtc->legacy_cursor_offset + 295c836e862SAlex Deucher (yorigin * 256))); 296771fe6b9SJerome Glisse } 297771fe6b9SJerome Glisse radeon_lock_cursor(crtc, false); 298771fe6b9SJerome Glisse 299771fe6b9SJerome Glisse return 0; 300771fe6b9SJerome Glisse } 301