1/* 2 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas. 3 * Copyright 2011 VMWare, Inc. 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sub license, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial portions 16 * of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR 22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 * 26 * 27 * Author: Alan Hourihane <alanh@tungstengraphics.com> 28 * Author: Jakob Bornecrantz <wallbraker@gmail.com> 29 * Author: Thomas Hellstrom <thellstrom@vmware.com> 30 */ 31#ifdef HAVE_CONFIG_H 32#include "config.h" 33#endif 34 35#include <unistd.h> 36#include <string.h> 37#include <assert.h> 38#include <stdlib.h> 39#include <math.h> 40#include <stdint.h> 41 42#include "xorg-server.h" 43#include <xf86.h> 44#include <xf86i2c.h> 45#include <xf86Crtc.h> 46#include <cursorstr.h> 47#include "vmwgfx_driver.h" 48#include "xf86Modes.h" 49#include "vmwgfx_saa.h" 50 51#ifdef HAVE_XEXTPROTO_71 52#include <X11/extensions/dpmsconst.h> 53#else 54#define DPMS_SERVER 55#include <X11/extensions/dpms.h> 56#endif 57 58struct crtc_private 59{ 60 drmModeCrtcPtr drm_crtc; 61 62 /* hwcursor */ 63 struct vmwgfx_dmabuf *cursor_bo; 64 uint32_t scanout_id; 65 unsigned cursor_handle; 66 67 /* Scanout info for pixmaps */ 68 struct vmwgfx_screen_entry entry; 69}; 70 71static void 72crtc_dpms(xf86CrtcPtr crtc, int mode) 73{ 74 struct crtc_private *crtcp = crtc->driver_private; 75 /* ScrnInfoPtr pScrn = crtc->scrn; */ 76 77 switch (mode) { 78 case DPMSModeOn: 79 case DPMSModeStandby: 80 case DPMSModeSuspend: 81 break; 82 case DPMSModeOff: 83 84 /* 85 * The xf86 modesetting code uses DPMS off to turn off 86 * crtcs that are not enabled. However, the DPMS code does the same. 87 * We assume, that if we get this call with the crtc not enabled, 88 * it's a permanent switch off which will only be reversed by a 89 * major modeset. 90 * 91 * If it's a DPMS switch off, (crtc->enabled == TRUE), 92 * the crtc may be turned on again by 93 * another dpms call, so don't release the scanout pixmap ref. 94 */ 95 if (!crtc->enabled && crtcp->entry.pixmap) { 96 vmwgfx_scanout_unref(&crtcp->entry); 97 } 98 break; 99 } 100} 101 102/* 103 * Disable outputs and crtcs and drop the scanout reference from 104 * scanout pixmaps. This will essentially free all kms fb allocations. 105 */ 106 107void 108vmwgfx_disable_scanout(ScrnInfoPtr pScrn) 109{ 110 int i; 111 Bool save_enabled; 112 xf86CrtcPtr crtc; 113 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn); 114 115 xf86DPMSSet(pScrn, DPMSModeOff, 0); 116 for (i=0; i < config->num_crtc; ++i) { 117 crtc = config->crtc[i]; 118 save_enabled = crtc->enabled; 119 crtc->enabled = FALSE; 120 crtc_dpms(crtc, DPMSModeOff); 121 crtc->enabled = save_enabled; 122 } 123 xf86RotateFreeShadow(pScrn); 124} 125 126static Bool 127vmwgfx_scanout_equals_pixmap(DisplayModePtr mode, PixmapPtr pixmap, 128 int x, int y) 129{ 130 return x == 0 && y == 0; 131/* 132 * Mode test is disabled for now, since the X server has a tendency to first 133 * change the pixmap dimensions, then change the mode, keeping the pixmap. 134 * This would lead to a lot of false non-equals, or flickering if we were to 135 * kill the drm fb in between. 136 * However, currently we prefer false equals as long as x == 0 and y == 0. 137 * The false equals will then typically correspond to the primary screen in a 138 * multimon setup. Let's live with that for now. 139 */ 140#if 0 141 && mode->HDisplay == pixmap->drawable.width && 142 mode->VDisplay == pixmap->drawable.height; 143#endif 144} 145 146static Bool 147crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, 148 Rotation rotation, int x, int y) 149{ 150 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn); 151 modesettingPtr ms = modesettingPTR(crtc->scrn); 152 ScreenPtr pScreen = crtc->scrn->pScreen; 153 xf86OutputPtr output = NULL; 154 struct crtc_private *crtcp = crtc->driver_private; 155 drmModeCrtcPtr drm_crtc = crtcp->drm_crtc; 156 drmModeModeInfo drm_mode; 157 int i, ret; 158 unsigned int connector_id; 159 PixmapPtr pixmap; 160 161 for (i = 0; i < config->num_output; output = NULL, i++) { 162 output = config->output[i]; 163 164 if (output->crtc == crtc) 165 break; 166 } 167 168 if (!output) { 169 LogMessage(X_ERROR, "No output for this crtc.\n"); 170 return FALSE; 171 } 172 173 connector_id = xorg_output_get_id(output); 174 175 memset(&drm_mode, 0, sizeof(drm_mode)); 176 drm_mode.clock = mode->Clock; 177 drm_mode.hdisplay = mode->HDisplay; 178 drm_mode.hsync_start = mode->HSyncStart; 179 drm_mode.hsync_end = mode->HSyncEnd; 180 drm_mode.htotal = mode->HTotal; 181 drm_mode.vdisplay = mode->VDisplay; 182 drm_mode.vsync_start = mode->VSyncStart; 183 drm_mode.vsync_end = mode->VSyncEnd; 184 drm_mode.vtotal = mode->VTotal; 185 drm_mode.flags = mode->Flags; 186 drm_mode.hskew = mode->HSkew; 187 drm_mode.vscan = mode->VScan; 188 drm_mode.vrefresh = mode->VRefresh; 189 if (!mode->name) 190 xf86SetModeDefaultName(mode); 191 strncpy(drm_mode.name, mode->name, DRM_DISPLAY_MODE_LEN - 1); 192 drm_mode.name[DRM_DISPLAY_MODE_LEN - 1] = '\0'; 193 194 /* 195 * Check if we need to scanout from something else than the root 196 * pixmap. In that case, xf86CrtcRotate will take care of allocating 197 * new opaque scanout buffer data "crtc->rotatedData". 198 * However, it will not wrap 199 * that data into pixmaps until the first rotated damage composite. 200 * In out case, the buffer data is actually already a pixmap. 201 */ 202 203 if (!xf86CrtcRotate(crtc)) 204 return FALSE; 205 206 if (crtc->transform_in_use && crtc->rotatedData) { 207 x = 0; 208 y = 0; 209 pixmap = (PixmapPtr) crtc->rotatedData; 210 } else 211 pixmap = pScreen->GetScreenPixmap(pScreen); 212 213 if (crtcp->entry.pixmap != pixmap) { 214 if (crtcp->entry.pixmap) 215 vmwgfx_scanout_unref(&crtcp->entry); 216 217 crtcp->entry.pixmap = pixmap; 218 crtcp->scanout_id = vmwgfx_scanout_ref 219 (&crtcp->entry, vmwgfx_scanout_equals_pixmap(mode, pixmap, x, y)); 220 if (crtcp->scanout_id == -1) { 221 crtcp->entry.pixmap = NULL; 222 LogMessage(X_ERROR, "Failed to convert pixmap to scanout.\n"); 223 return FALSE; 224 } 225 } 226 ret = drmModeSetCrtc(ms->fd, drm_crtc->crtc_id, crtcp->scanout_id, x, y, 227 &connector_id, 1, &drm_mode); 228 if (ret) 229 return FALSE; 230 231 vmwgfx_scanout_refresh(pixmap); 232 233 /* Only set gamma when needed, to avoid unneeded delays. */ 234#if defined(XF86_CRTC_VERSION) && XF86_CRTC_VERSION >= 3 235 if (!crtc->active && crtc->version >= 3) 236 crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green, 237 crtc->gamma_blue, crtc->gamma_size); 238 crtc->active = TRUE; 239#endif 240 241 /* 242 * Strictly, this needs to be done only once per configuration change, 243 * not once per crtc, but there's no better place to put this. Since 244 * Intel wrote the crtc code, let's do what the xf86-video-intel driver 245 * does. 246 */ 247#if (GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 23) 248 if (pScreen) 249 xf86_reload_cursors(pScreen); 250#endif 251 252 return TRUE; 253} 254 255static void 256crtc_gamma_set(xf86CrtcPtr crtc, CARD16 * red, CARD16 * green, CARD16 * blue, 257 int size) 258{ 259 modesettingPtr ms = modesettingPTR(crtc->scrn); 260 struct crtc_private *crtcp = crtc->driver_private; 261 262 drmModeCrtcSetGamma(ms->fd, crtcp->drm_crtc->crtc_id, size, red, green, blue); 263} 264 265static void * 266crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height) 267{ 268 ScreenPtr pScreen = crtc->scrn->pScreen; 269 PixmapPtr rootpix = pScreen->GetScreenPixmap(pScreen); 270 271 /* 272 * Use the same depth as for the root pixmap. 273 * The associated kms fb will be created on demand once this pixmap 274 * is used as scanout by a crtc. 275 */ 276 277 return pScreen->CreatePixmap(pScreen, width, height, 278 rootpix->drawable.depth, 0); 279} 280 281static PixmapPtr 282crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height) 283{ 284 return (PixmapPtr) data; 285} 286 287static void 288crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data) 289{ 290 ScreenPtr pScreen; 291 292 if (rotate_pixmap == NULL) 293 return; 294 295 pScreen = rotate_pixmap->drawable.pScreen; 296 pScreen->DestroyPixmap(rotate_pixmap); 297} 298 299 300/* 301 * Cursor functions 302 */ 303 304static void 305crtc_set_cursor_colors(xf86CrtcPtr crtc, int bg, int fg) 306{ 307 /* XXX: See if this one is needed, as we only support ARGB cursors */ 308} 309 310static void 311crtc_set_cursor_position(xf86CrtcPtr crtc, int x, int y) 312{ 313 modesettingPtr ms = modesettingPTR(crtc->scrn); 314 struct crtc_private *crtcp = crtc->driver_private; 315 316 /* Seems like newer X servers try to move cursors without images */ 317 if (!crtcp->cursor_bo) 318 return; 319 320 drmModeMoveCursor(ms->fd, crtcp->drm_crtc->crtc_id, x, y); 321} 322 323static void 324crtc_load_cursor_argb_kms(xf86CrtcPtr crtc, CARD32 * image) 325{ 326 modesettingPtr ms = modesettingPTR(crtc->scrn); 327 struct crtc_private *crtcp = crtc->driver_private; 328 unsigned char *ptr; 329 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn); 330 CursorPtr c = config->cursor; 331 332 if (vmwgfx_cursor_bypass(ms->fd, c->bits->xhot, c->bits->yhot) != 0) { 333 xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, 334 "Failed to set VMWare cursor bypass.\n"); 335 } 336 337 if (!crtcp->cursor_bo) { 338 size_t size = 64*64*4; 339 crtcp->cursor_bo = vmwgfx_dmabuf_alloc(ms->fd, size); 340 if (!crtcp->cursor_bo) { 341 xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, 342 "Failed to create a dmabuf for cursor.\n"); 343 return; 344 } 345 crtcp->cursor_handle = crtcp->cursor_bo->handle; 346 } 347 348 ptr = vmwgfx_dmabuf_map(crtcp->cursor_bo); 349 if (ptr) { 350 memcpy(ptr, image, 64*64*4); 351 vmwgfx_dmabuf_unmap(crtcp->cursor_bo); 352 } else { 353 xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, 354 "Failed to map cursor dmabuf.\n"); 355 } 356 357 if (crtc->cursor_shown) 358 drmModeSetCursor(ms->fd, crtcp->drm_crtc->crtc_id, 359 crtcp->cursor_handle, 64, 64); 360 361 return; 362} 363 364static void 365crtc_load_cursor_argb(xf86CrtcPtr crtc, CARD32 * image) 366{ 367 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn); 368 modesettingPtr ms = modesettingPTR(crtc->scrn); 369 370 /* Older X servers have cursor reference counting bugs leading to use of 371 * freed memory and consequently random crashes. Should be fixed as of 372 * xserver 1.8, but this workaround shouldn't hurt anyway. 373 */ 374 if (config->cursor) 375 config->cursor->refcnt++; 376 377 if (ms->cursor) 378 FreeCursor(ms->cursor, None); 379 380 ms->cursor = config->cursor; 381 crtc_load_cursor_argb_kms(crtc, image); 382} 383 384static void 385crtc_show_cursor(xf86CrtcPtr crtc) 386{ 387 modesettingPtr ms = modesettingPTR(crtc->scrn); 388 struct crtc_private *crtcp = crtc->driver_private; 389 390 if (crtcp->cursor_bo) 391 drmModeSetCursor(ms->fd, crtcp->drm_crtc->crtc_id, 392 crtcp->cursor_handle, 64, 64); 393} 394 395static void 396crtc_hide_cursor(xf86CrtcPtr crtc) 397{ 398 modesettingPtr ms = modesettingPTR(crtc->scrn); 399 struct crtc_private *crtcp = crtc->driver_private; 400 401 drmModeSetCursor(ms->fd, crtcp->drm_crtc->crtc_id, 0, 0, 0); 402} 403 404/** 405 * Called at vt leave 406 */ 407void 408xorg_crtc_cursor_destroy(xf86CrtcPtr crtc) 409{ 410 struct crtc_private *crtcp = crtc->driver_private; 411 412 if (crtcp->cursor_bo) { 413 vmwgfx_dmabuf_destroy(crtcp->cursor_bo); 414 crtcp->cursor_bo = NULL; 415 } 416} 417 418/* 419 * Misc functions 420 */ 421 422static void 423crtc_destroy(xf86CrtcPtr crtc) 424{ 425 struct crtc_private *crtcp = crtc->driver_private; 426 427 if (!WSBMLISTEMPTY(&crtcp->entry.scanout_head)) 428 vmwgfx_scanout_unref(&crtcp->entry); 429 430 xorg_crtc_cursor_destroy(crtc); 431 432 drmModeFreeCrtc(crtcp->drm_crtc); 433 434 free(crtcp); 435 crtc->driver_private = NULL; 436} 437 438static const xf86CrtcFuncsRec crtc_funcs = { 439 .dpms = crtc_dpms, 440 .set_mode_major = crtc_set_mode_major, 441 442 .set_cursor_colors = crtc_set_cursor_colors, 443 .set_cursor_position = crtc_set_cursor_position, 444 .show_cursor = crtc_show_cursor, 445 .hide_cursor = crtc_hide_cursor, 446 .load_cursor_argb = crtc_load_cursor_argb, 447 448 .shadow_create = crtc_shadow_create, 449 .shadow_allocate = crtc_shadow_allocate, 450 .shadow_destroy = crtc_shadow_destroy, 451 452 .gamma_set = crtc_gamma_set, 453 .destroy = crtc_destroy, 454}; 455 456void 457xorg_crtc_init(ScrnInfoPtr pScrn) 458{ 459 modesettingPtr ms = modesettingPTR(pScrn); 460 xf86CrtcPtr crtc; 461 drmModeResPtr res; 462 drmModeCrtcPtr drm_crtc = NULL; 463 struct crtc_private *crtcp; 464 int c; 465 466 res = drmModeGetResources(ms->fd); 467 if (res == 0) { 468 ErrorF("Failed drmModeGetResources %d\n", errno); 469 return; 470 } 471 472 for (c = 0; c < res->count_crtcs; c++) { 473 drm_crtc = drmModeGetCrtc(ms->fd, res->crtcs[c]); 474 475 if (!drm_crtc) 476 continue; 477 478 crtc = xf86CrtcCreate(pScrn, &crtc_funcs); 479 if (crtc == NULL) 480 goto out; 481 482 crtcp = calloc(1, sizeof(struct crtc_private)); 483 if (!crtcp) { 484 xf86CrtcDestroy(crtc); 485 goto out; 486 } 487 488 crtcp->drm_crtc = drm_crtc; 489 crtcp->entry.pixmap = NULL; 490 WSBMINITLISTHEAD(&crtcp->entry.scanout_head); 491 492 crtc->driver_private = crtcp; 493 } 494 495 out: 496 drmModeFreeResources(res); 497} 498 499PixmapPtr 500crtc_get_scanout(xf86CrtcPtr crtc) 501{ 502 struct crtc_private *crtcp = crtc->driver_private; 503 return crtcp->entry.pixmap; 504} 505 506/* vim: set sw=4 ts=8 sts=4: */ 507