/* * Copyright 2008 Advanced Micro Devices, Inc. * Copyright 2008 Red Hat Inc. * Copyright 2009 Jerome Glisse. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: Dave Airlie * Alex Deucher * Jerome Glisse */ #include #include "radeon.h" #include #include "radeon_asic.h" #include #include #include "radeon_kfd.h" #if defined(CONFIG_VGA_SWITCHEROO) bool radeon_has_atpx(void); #else static inline bool radeon_has_atpx(void) { return false; } #endif /* * VBlank related functions. */ /** * radeon_get_vblank_counter_kms - get frame count * * @dev: drm dev pointer * @crtc: crtc to get the frame count from * * Gets the frame count on the requested crtc (all asics). * Returns frame count on success, -EINVAL on failure. */ u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc) { int vpos, hpos, stat; u32 count; struct radeon_device *rdev = dev->dev_private; if (crtc < 0 || crtc >= rdev->num_crtc) { DRM_ERROR("Invalid crtc %d\n", crtc); return -EINVAL; } /* The hw increments its frame counter at start of vsync, not at start * of vblank, as is required by DRM core vblank counter handling. * Cook the hw count here to make it appear to the caller as if it * incremented at start of vblank. We measure distance to start of * vblank in vpos. vpos therefore will be >= 0 between start of vblank * and start of vsync, so vpos >= 0 means to bump the hw frame counter * result by 1 to give the proper appearance to caller. */ if (rdev->mode_info.crtcs[crtc]) { /* Repeat readout if needed to provide stable result if * we cross start of vsync during the queries. */ do { count = radeon_get_vblank_counter(rdev, crtc); /* Ask radeon_get_crtc_scanoutpos to return vpos as * distance to start of vblank, instead of regular * vertical scanout pos. */ stat = radeon_get_crtc_scanoutpos( dev, crtc, GET_DISTANCE_TO_VBLANKSTART, &vpos, &hpos, NULL, NULL, &rdev->mode_info.crtcs[crtc]->base.hwmode); } while (count != radeon_get_vblank_counter(rdev, crtc)); if (((stat & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE)) != (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE))) { DRM_DEBUG_VBL("Query failed! stat %d\n", stat); } else { DRM_DEBUG_VBL("crtc %d: dist from vblank start %d\n", crtc, vpos); /* Bump counter if we are at >= leading edge of vblank, * but before vsync where vpos would turn negative and * the hw counter really increments. */ if (vpos >= 0) count++; } } else { /* Fallback to use value as is. */ count = radeon_get_vblank_counter(rdev, crtc); DRM_DEBUG_VBL("NULL mode info! Returned count may be wrong.\n"); } return count; } /** * radeon_enable_vblank_kms - enable vblank interrupt * * @dev: drm dev pointer * @crtc: crtc to enable vblank interrupt for * * Enable the interrupt on the requested crtc (all asics). * Returns 0 on success, -EINVAL on failure. */ int radeon_enable_vblank_kms(struct drm_device *dev, int crtc) { struct radeon_device *rdev = dev->dev_private; unsigned long irqflags; int r; if (crtc < 0 || crtc >= rdev->num_crtc) { DRM_ERROR("Invalid crtc %d\n", crtc); return -EINVAL; } spin_lock_irqsave(&rdev->irq.lock, irqflags); rdev->irq.crtc_vblank_int[crtc] = true; r = radeon_irq_set(rdev); spin_unlock_irqrestore(&rdev->irq.lock, irqflags); return r; } /** * radeon_disable_vblank_kms - disable vblank interrupt * * @dev: drm dev pointer * @crtc: crtc to disable vblank interrupt for * * Disable the interrupt on the requested crtc (all asics). */ void radeon_disable_vblank_kms(struct drm_device *dev, int crtc) { struct radeon_device *rdev = dev->dev_private; unsigned long irqflags; if (crtc < 0 || crtc >= rdev->num_crtc) { DRM_ERROR("Invalid crtc %d\n", crtc); return; } spin_lock_irqsave(&rdev->irq.lock, irqflags); rdev->irq.crtc_vblank_int[crtc] = false; radeon_irq_set(rdev); spin_unlock_irqrestore(&rdev->irq.lock, irqflags); } /** * radeon_get_vblank_timestamp_kms - get vblank timestamp * * @dev: drm dev pointer * @crtc: crtc to get the timestamp for * @max_error: max error * @vblank_time: time value * @flags: flags passed to the driver * * Gets the timestamp on the requested crtc based on the * scanout position. (all asics). * Returns postive status flags on success, negative error on failure. */ int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc, int *max_error, struct timeval *vblank_time, unsigned flags) { struct drm_crtc *drmcrtc; struct radeon_device *rdev = dev->dev_private; if (crtc < 0 || crtc >= dev->num_crtcs) { DRM_ERROR("Invalid crtc %d\n", crtc); return -EINVAL; } /* Get associated drm_crtc: */ drmcrtc = &rdev->mode_info.crtcs[crtc]->base; if (!drmcrtc) return -EINVAL; /* Helper routine in DRM core does all the work: */ return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error, vblank_time, flags, &drmcrtc->hwmode); }