summaryrefslogtreecommitdiff
path: root/src/radeon_kms.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/radeon_kms.c')
-rw-r--r--src/radeon_kms.c98
1 files changed, 92 insertions, 6 deletions
diff --git a/src/radeon_kms.c b/src/radeon_kms.c
index ca2d36d0..5410c420 100644
--- a/src/radeon_kms.c
+++ b/src/radeon_kms.c
@@ -32,6 +32,7 @@
#include <sys/ioctl.h>
/* Driver data structures */
#include "radeon.h"
+#include "radeon_bo_helper.h"
#include "radeon_drm_queue.h"
#include "radeon_glamor.h"
#include "radeon_reg.h"
@@ -1158,9 +1159,10 @@ static void RADEONBlockHandler_KMS(BLOCKHANDLER_ARGS_DECL)
(*pScreen->BlockHandler) (BLOCKHANDLER_ARGS);
pScreen->BlockHandler = RADEONBlockHandler_KMS;
- if (!pScrn->vtSema) {
- radeon_cs_flush_indirect(pScrn);
-
+ if (!xf86ScreenToScrn(radeon_master_screen(pScreen))->vtSema) {
+ /* Unreference the all-black FB created by RADEONLeaveVT_KMS. After
+ * this, there should be no FB left created by this driver.
+ */
for (c = 0; c < xf86_config->num_crtc; c++) {
drmmode_crtc_private_ptr drmmode_crtc =
xf86_config->crtc[c]->driver_private;
@@ -2472,21 +2474,105 @@ Bool RADEONEnterVT_KMS(VT_FUNC_ARGS_DECL)
}
+static void
+pixmap_unref_fb(void *value, XID id, void *cdata)
+{
+ PixmapPtr pixmap = value;
+ RADEONEntPtr pRADEONEnt = cdata;
+ struct drmmode_fb **fb_ptr = radeon_pixmap_get_fb_ptr(pixmap);
+
+ if (fb_ptr)
+ drmmode_fb_reference(pRADEONEnt->fd, fb_ptr, NULL);
+}
+
void RADEONLeaveVT_KMS(VT_FUNC_ARGS_DECL)
{
SCRN_INFO_PTR(arg);
RADEONInfoPtr info = RADEONPTR(pScrn);
+ RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn);
+ ScreenPtr pScreen = pScrn->pScreen;
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ struct drmmode_scanout black_scanout = { .pixmap = NULL, .bo = NULL };
+ xf86CrtcPtr crtc;
+ drmmode_crtc_private_ptr drmmode_crtc;
+ unsigned w = 0, h = 0;
+ int i;
xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, RADEON_LOGLEVEL_DEBUG,
"RADEONLeaveVT_KMS\n");
- radeon_drop_drm_master(pScrn);
+ /* Compute maximum scanout dimensions of active CRTCs */
+ for (i = 0; i < xf86_config->num_crtc; i++) {
+ crtc = xf86_config->crtc[i];
+ drmmode_crtc = crtc->driver_private;
+
+ if (!drmmode_crtc->fb)
+ continue;
+
+ w = max(w, crtc->mode.HDisplay);
+ h = max(h, crtc->mode.VDisplay);
+ }
+
+ /* Make all active CRTCs scan out from an all-black framebuffer */
+ if (w > 0 && h > 0) {
+ if (drmmode_crtc_scanout_create(crtc, &black_scanout, w, h)) {
+ struct drmmode_fb *black_fb =
+ radeon_pixmap_get_fb(black_scanout.pixmap);
+
+ radeon_pixmap_clear(black_scanout.pixmap);
+ radeon_cs_flush_indirect(pScrn);
+ radeon_bo_wait(black_scanout.bo);
+
+ for (i = 0; i < xf86_config->num_crtc; i++) {
+ crtc = xf86_config->crtc[i];
+ drmmode_crtc = crtc->driver_private;
+
+ if (drmmode_crtc->fb) {
+ if (black_fb) {
+ drmmode_set_mode(crtc, black_fb, &crtc->mode, 0, 0);
+ } else {
+ drmModeSetCrtc(pRADEONEnt->fd,
+ drmmode_crtc->mode_crtc->crtc_id, 0, 0,
+ 0, NULL, 0, NULL);
+ drmmode_fb_reference(pRADEONEnt->fd, &drmmode_crtc->fb,
+ NULL);
+ }
+
+ if (pScrn->is_gpu) {
+ if (drmmode_crtc->scanout[0].pixmap)
+ pixmap_unref_fb(drmmode_crtc->scanout[0].pixmap,
+ None, pRADEONEnt);
+ if (drmmode_crtc->scanout[1].pixmap)
+ pixmap_unref_fb(drmmode_crtc->scanout[1].pixmap,
+ None, pRADEONEnt);
+ } else {
+ drmmode_crtc_scanout_free(drmmode_crtc);
+ }
+ }
+ }
+ }
+ }
xf86RotateFreeShadow(pScrn);
- if (!pScrn->is_gpu)
- drmmode_scanout_free(pScrn);
+ drmmode_crtc_scanout_destroy(&info->drmmode, &black_scanout);
+
+ /* Unreference FBs of all pixmaps. After this, the only FB remaining
+ * should be the all-black one being scanned out by active CRTCs
+ */
+ for (i = 0; i < currentMaxClients; i++) {
+ if (i > 0 &&
+ (!clients[i] || clients[i]->clientState != ClientStateRunning))
+ continue;
+
+ FindClientResourcesByType(clients[i], RT_PIXMAP, pixmap_unref_fb,
+ pRADEONEnt);
+ }
+ pixmap_unref_fb(pScreen->GetScreenPixmap(pScreen), None, pRADEONEnt);
xf86_hide_cursors (pScrn);
+
+ radeon_drop_drm_master(pScrn);
+
info->accel_state->XInited3D = FALSE;
info->accel_state->engineMode = EXA_ENGINEMODE_UNKNOWN;