xserver/hw/xfree86/drivers/modesetting/driver.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

2338 lines
68 KiB
C
Raw Normal View History

/*
* Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
2011-09-29 12:34:17 +01:00
* Copyright 2011 Dave Airlie
* Copyright 2019 NVIDIA CORPORATION
* All Rights Reserved.
*
* 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, sub license, 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 (including the
* next paragraph) 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 NON-INFRINGEMENT.
* IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS 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.
*
*
2011-09-29 12:34:17 +01:00
* Original Author: Alan Hourihane <alanh@tungstengraphics.com>
* Rewrite: Dave Airlie <airlied@redhat.com>
* Additional contributors:
* Aaron Plattner <aplattner@nvidia.com>
*
*/
#include "dix-config.h"
#include <errno.h>
2011-09-29 14:13:58 +01:00
#include <unistd.h>
#include <fcntl.h>
#include <X11/extensions/randr.h>
#include <X11/extensions/Xv.h>
#include "dix/dix_priv.h"
#include "xf86.h"
#include "xf86Priv.h"
#include "xf86_OSproc.h"
#include "compiler.h"
#include "xf86Pci.h"
#include "mipointer.h"
#include "mipointrst.h"
#include "micmap.h"
#include "fb.h"
#include "edid.h"
#include "xf86i2c.h"
#include "xf86Crtc.h"
#include "miscstruct.h"
#include "dixstruct.h"
#include "xf86xv.h"
#include <xorg-config.h>
2012-06-05 14:43:21 +01:00
#ifdef XSERVER_PLATFORM_BUS
#include "xf86platformBus.h"
#endif
#ifdef XSERVER_LIBPCIACCESS
#include <pciaccess.h>
#endif
#include "driver.h"
static void AdjustFrame(ScrnInfoPtr pScrn, int x, int y);
static Bool CloseScreen(ScreenPtr pScreen);
static Bool EnterVT(ScrnInfoPtr pScrn);
static void Identify(int flags);
static const OptionInfoRec *AvailableOptions(int chipid, int busid);
static ModeStatus ValidMode(ScrnInfoPtr pScrn, DisplayModePtr mode,
Bool verbose, int flags);
static void FreeScreen(ScrnInfoPtr pScrn);
static void LeaveVT(ScrnInfoPtr pScrn);
static Bool SwitchMode(ScrnInfoPtr pScrn, DisplayModePtr mode);
static Bool ScreenInit(ScreenPtr pScreen, int argc, char **argv);
static Bool PreInit(ScrnInfoPtr pScrn, int flags);
2008-05-28 15:55:36 +01:00
static Bool Probe(DriverPtr drv, int flags);
static Bool ms_pci_probe(DriverPtr driver,
int entity_num, struct pci_device *device,
intptr_t match_data);
static Bool ms_driver_func(ScrnInfoPtr scrn, xorgDriverFuncOp op, void *data);
/* window wrapper functions used to get the notification when
* the window property changes */
static Atom vrr_atom;
static Bool property_vectors_wrapped;
static Bool restore_property_vector;
static int (*saved_change_property) (ClientPtr client);
static int (*saved_delete_property) (ClientPtr client);
#ifdef XSERVER_LIBPCIACCESS
static const struct pci_id_match ms_device_match[] = {
{
PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY,
0x00030000, 0x00ff0000, 0},
{0, 0, 0},
};
#endif
#ifndef XSERVER_PLATFORM_BUS
struct xf86_platform_device;
#endif
2012-06-05 14:43:21 +01:00
#ifdef XSERVER_PLATFORM_BUS
static Bool ms_platform_probe(DriverPtr driver,
int entity_num, int flags,
struct xf86_platform_device *device,
intptr_t match_data);
2012-06-05 14:43:21 +01:00
#endif
_X_EXPORT DriverRec modesetting = {
2008-05-28 15:55:36 +01:00
1,
"modesetting",
Identify,
Probe,
AvailableOptions,
NULL,
0,
ms_driver_func,
ms_device_match,
ms_pci_probe,
2012-06-05 14:43:21 +01:00
#ifdef XSERVER_PLATFORM_BUS
ms_platform_probe,
#endif
};
static SymTabRec Chipsets[] = {
{0, "kms"},
2008-05-28 15:55:36 +01:00
{-1, NULL}
};
static const OptionInfoRec Options[] = {
2008-05-28 15:55:36 +01:00
{OPTION_SW_CURSOR, "SWcursor", OPTV_BOOLEAN, {0}, FALSE},
{OPTION_DEVICE_PATH, "kmsdev", OPTV_STRING, {0}, FALSE},
{OPTION_SHADOW_FB, "ShadowFB", OPTV_BOOLEAN, {0}, FALSE},
{OPTION_ACCEL_METHOD, "AccelMethod", OPTV_STRING, {0}, FALSE},
{OPTION_PAGEFLIP, "PageFlip", OPTV_BOOLEAN, {0}, FALSE},
{OPTION_ZAPHOD_HEADS, "ZaphodHeads", OPTV_STRING, {0}, FALSE},
{OPTION_DOUBLE_SHADOW, "DoubleShadow", OPTV_BOOLEAN, {0}, FALSE},
{OPTION_ATOMIC, "Atomic", OPTV_BOOLEAN, {0}, FALSE},
{OPTION_VARIABLE_REFRESH, "VariableRefresh", OPTV_BOOLEAN, {0}, FALSE},
{OPTION_USE_GAMMA_LUT, "UseGammaLUT", OPTV_BOOLEAN, {0}, FALSE},
modesetting: Add option for non-vsynced flips for "secondary" outputs. Whenever an unredirected fullscreen window uses pageflipping for a DRI3/Present PresentPixmap() operation and the X-Screen has more than one active output, multiple crtc's need to execute pageflips. Only after the last flip has completed can the PresentPixmap operation as a whole complete. If a sync_flip is requested for the present, then the current implementation will synchronize each pageflip to the vblank of its associated crtc. This provides tear-free image presentation across all outputs, but introduces a different artifact, if not all outputs run at the same refresh rate with perfect synchrony: The slowest output throttles the presentation rate, and present completion is delayed to flip completion of the "latest" output to complete. This means degraded performance, e.g., a dual-display setup with a 144 Hz monitor and a 60 Hz monitor will always be throttled to at most 60 fps. It also means non-constant present rate if refresh cycles drift against each other, creating complex "beat patterns", tremors, stutters and periodic slowdowns - quite irritating! Such a scenario will be especially annoying if one uses multiple outputs in "mirror mode" aka "clone mode". One output will usually be the "production output" with the highest quality and fastest display attached, whereas a secondary mirror output just has a cheaper display for monitoring attached. Users care about perfect and perfectly timed tear-free presentation on the "production output", but cares less about quality on the secondary "mirror output". They are willing to trade quality on secondary outputs away in exchange for better presentation timing on the "production output". One example use case for such production + monitoring displays are neuroscience / medical science applications where one high quality display device is used to present visual animations to test subjects or patients in a fMRI scanner room (production display), whereas an operator monitors the same visual animations from a control room on a lower quality display. Presentation timing needs to be perfect, and animations high-speed and tear-free for the production display, whereas quality and timing don't matter for the monitoring display. This commit gives users the option to choose such a trade-off as opt-in: It adds a new boolean option "AsyncFlipSecondaries" to the device section of xorg.conf. If this option is specified as true, then DRI3 pageflip behaviour changes as follows: 1. The "reference crtc" for a windows PresentPixmap operation does a vblank synced flip, or a DRM_MODE_PAGE_FLIP_ASYNC non-synchronized flip, as requested by the caller, just as in the past. Typically flips will be requested to be vblank synchronized for tear-free presentation. The "reference crtc" is the one chosen by the caller to drive presentation timing (as specified by PresentPixmap()'s "target_msc", "divisor", "remainder" parameters and implemented by vblank events) and to deliver Present completion timestamps (msc and ust) extracted from its pageflip completion event. 2. All other crtc's, which also page-flip in a multi-display configuration, will try to flip with DRM_MODE_PAGE_FLIP_ASYNC, ie. immediately and not synchronized to vblank. This allows the PresentPixmap operation to complete with little delay compared to a single-display present, especially if the different crtc's run at different video refresh rates or their refresh cycles are not perfectly synchronized, but drift against each other. The downside is potential tearing artifacts on all outputs apart from the one of the "reference crtc". Successfully tested on a AMD gpu with single-display, dual-display and triple-display setups, and with single-X-Screen as well as dual-X-Screen "ZaphodHeads" configurations. Please consider merging this commit for the upcoming server 1.21 branch. Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com>
2021-08-30 05:42:04 +02:00
{OPTION_ASYNC_FLIP_SECONDARIES, "AsyncFlipSecondaries", OPTV_BOOLEAN, {0}, FALSE},
{OPTION_TEARFREE, "TearFree", OPTV_BOOLEAN, {0}, FALSE},
2008-05-28 15:55:36 +01:00
{-1, NULL, OPTV_NONE, {0}, FALSE}
};
int ms_entity_index = -1;
static MODULESETUPPROTO(Setup);
static XF86ModuleVersionInfo VersRec = {
2008-05-28 15:55:36 +01:00
"modesetting",
MODULEVENDORSTRING,
MODINFOSTRING1,
MODINFOSTRING2,
XORG_VERSION_CURRENT,
XORG_VERSION_MAJOR,
XORG_VERSION_MINOR,
XORG_VERSION_PATCH,
2008-05-28 15:55:36 +01:00
ABI_CLASS_VIDEODRV,
ABI_VIDEODRV_VERSION,
MOD_CLASS_VIDEODRV,
{0, 0, 0, 0}
};
_X_EXPORT XF86ModuleData modesettingModuleData = { &VersRec, Setup, NULL };
static void *
Setup(void *module, void *opts, int *errmaj, int *errmin)
{
2008-05-28 15:55:36 +01:00
static Bool setupDone = 0;
/* This module should be loaded only once, but check to be sure.
*/
if (!setupDone) {
setupDone = 1;
xf86AddDriver(&modesetting, module, HaveDriverFuncs);
/*
* The return value must be non-NULL on success even though there
* is no TearDownProc.
*/
return (void *) 1;
}
else {
if (errmaj)
*errmaj = LDR_ONCEONLY;
return NULL;
2008-05-28 15:55:36 +01:00
}
}
static void
Identify(int flags)
{
2008-05-28 15:55:36 +01:00
xf86PrintChipsets("modesetting", "Driver for Modesetting Kernel Drivers",
Chipsets);
}
modesettingEntPtr ms_ent_priv(ScrnInfoPtr scrn)
{
DevUnion *pPriv;
modesettingPtr ms = modesettingPTR(scrn);
pPriv = xf86GetEntityPrivate(ms->pEnt->index,
ms_entity_index);
return pPriv->ptr;
}
static int
get_passed_fd(void)
{
if (xf86DRMMasterFd >= 0) {
xf86DrvMsg(-1, X_INFO, "Using passed DRM master file descriptor %d\n", xf86DRMMasterFd);
return dup(xf86DRMMasterFd);
}
return -1;
}
static int
open_hw(const char *dev)
{
int fd;
if ((fd = get_passed_fd()) != -1)
return fd;
if (dev)
fd = open(dev, O_RDWR | O_CLOEXEC, 0);
else {
dev = getenv("KMSDEVICE");
if ((NULL == dev) || ((fd = open(dev, O_RDWR | O_CLOEXEC, 0)) == -1)) {
dev = "/dev/dri/card0";
fd = open(dev, O_RDWR | O_CLOEXEC, 0);
}
}
if (fd == -1)
xf86DrvMsg(-1, X_ERROR, "open %s: %s\n", dev, strerror(errno));
return fd;
}
static int
check_outputs(int fd, int *count)
{
drmModeResPtr res = drmModeGetResources(fd);
int ret;
if (!res)
return FALSE;
if (count)
*count = res->count_connectors;
ret = res->count_connectors > 0;
#if defined(GLAMOR_HAS_GBM_LINEAR)
if (ret == FALSE) {
uint64_t value = 0;
if (drmGetCap(fd, DRM_CAP_PRIME, &value) == 0 &&
(value & DRM_PRIME_CAP_EXPORT))
ret = TRUE;
}
#endif
drmModeFreeResources(res);
return ret;
}
static Bool
probe_hw(const char *dev, struct xf86_platform_device *platform_dev)
{
int fd;
#ifdef XF86_PDEV_SERVER_FD
if (platform_dev && (platform_dev->flags & XF86_PDEV_SERVER_FD)) {
fd = xf86_platform_device_odev_attributes(platform_dev)->fd;
if (fd == -1)
return FALSE;
return check_outputs(fd, NULL);
}
#endif
fd = open_hw(dev);
if (fd != -1) {
int ret = check_outputs(fd, NULL);
close(fd);
return ret;
}
return FALSE;
}
static char *
ms_DRICreatePCIBusID(const struct pci_device *dev)
{
char *busID;
if (asprintf(&busID, "pci:%04x:%02x:%02x.%d",
dev->domain, dev->bus, dev->dev, dev->func) == -1)
return NULL;
return busID;
}
static Bool
probe_hw_pci(const char *dev, struct pci_device *pdev)
{
int ret = FALSE, fd = open_hw(dev);
char *id, *devid;
drmSetVersion sv;
if (fd == -1)
return FALSE;
sv.drm_di_major = 1;
sv.drm_di_minor = 4;
sv.drm_dd_major = -1;
sv.drm_dd_minor = -1;
if (drmSetInterfaceVersion(fd, &sv)) {
close(fd);
return FALSE;
}
id = drmGetBusid(fd);
devid = ms_DRICreatePCIBusID(pdev);
if (id && devid && !strcmp(id, devid))
ret = check_outputs(fd, NULL);
close(fd);
free(id);
free(devid);
return ret;
}
static const OptionInfoRec *
AvailableOptions(int chipid, int busid)
{
2008-05-28 15:55:36 +01:00
return Options;
}
static Bool
ms_driver_func(ScrnInfoPtr scrn, xorgDriverFuncOp op, void *data)
{
xorgHWFlags *flag;
switch (op) {
case GET_REQUIRED_HW_INTERFACES:
flag = (CARD32 *) data;
(*flag) = 0;
return TRUE;
case SUPPORTS_SERVER_FDS:
return TRUE;
default:
return FALSE;
}
}
static void
ms_setup_scrn_hooks(ScrnInfoPtr scrn)
{
scrn->driverVersion = 1;
scrn->driverName = "modesetting";
scrn->name = "modeset";
scrn->Probe = NULL;
scrn->PreInit = PreInit;
scrn->ScreenInit = ScreenInit;
scrn->SwitchMode = SwitchMode;
scrn->AdjustFrame = AdjustFrame;
scrn->EnterVT = EnterVT;
scrn->LeaveVT = LeaveVT;
scrn->FreeScreen = FreeScreen;
scrn->ValidMode = ValidMode;
}
static void
ms_setup_entity(ScrnInfoPtr scrn, int entity_num)
{
DevUnion *pPriv;
xf86SetEntitySharable(entity_num);
if (ms_entity_index == -1)
ms_entity_index = xf86AllocateEntityPrivateIndex();
pPriv = xf86GetEntityPrivate(entity_num,
ms_entity_index);
xf86SetEntityInstanceForScreen(scrn, entity_num, xf86GetNumEntityInstances(entity_num) - 1);
if (!pPriv->ptr)
pPriv->ptr = XNFcallocarray(1, sizeof(modesettingEntRec));
}
#ifdef XSERVER_LIBPCIACCESS
static Bool
ms_pci_probe(DriverPtr driver,
int entity_num, struct pci_device *dev, intptr_t match_data)
{
2008-05-28 15:55:36 +01:00
ScrnInfoPtr scrn = NULL;
scrn = xf86ConfigPciEntity(scrn, 0, entity_num, NULL,
NULL, NULL, NULL, NULL, NULL);
if (scrn) {
const char *devpath;
GDevPtr devSection = xf86GetDevFromEntity(scrn->entityList[0],
scrn->entityInstanceList[0]);
devpath = xf86FindOptionValue(devSection->options, "kmsdev");
if (probe_hw_pci(devpath, dev)) {
ms_setup_scrn_hooks(scrn);
xf86DrvMsg(scrn->scrnIndex, X_CONFIG,
"claimed PCI slot %d@%d:%d:%d\n",
dev->bus, dev->domain, dev->dev, dev->func);
xf86DrvMsg(scrn->scrnIndex, X_INFO,
"using %s\n", devpath ? devpath : "default device");
ms_setup_entity(scrn, entity_num);
}
else
scrn = NULL;
2008-05-28 15:55:36 +01:00
}
return scrn != NULL;
}
#endif
2012-06-05 14:43:21 +01:00
#ifdef XSERVER_PLATFORM_BUS
static Bool
ms_platform_probe(DriverPtr driver,
int entity_num, int flags, struct xf86_platform_device *dev,
intptr_t match_data)
2012-06-05 14:43:21 +01:00
{
ScrnInfoPtr scrn = NULL;
const char *path = xf86_platform_device_odev_attributes(dev)->path;
2012-06-05 14:43:21 +01:00
int scr_flags = 0;
if (flags & PLATFORM_PROBE_GPU_SCREEN)
scr_flags = XF86_ALLOCATE_GPU_SCREEN;
2012-06-05 14:43:21 +01:00
if (probe_hw(path, dev)) {
2012-06-05 14:43:21 +01:00
scrn = xf86AllocateScreen(driver, scr_flags);
if (xf86IsEntitySharable(entity_num))
xf86SetEntityShared(entity_num);
2012-06-05 14:43:21 +01:00
xf86AddEntityToScreen(scrn, entity_num);
ms_setup_scrn_hooks(scrn);
2012-06-05 14:43:21 +01:00
xf86DrvMsg(scrn->scrnIndex, X_INFO,
"using drv %s\n", path ? path : "default device");
ms_setup_entity(scrn, entity_num);
2012-06-05 14:43:21 +01:00
}
return scrn != NULL;
}
#endif
static Bool
Probe(DriverPtr drv, int flags)
{
2011-09-29 14:13:58 +01:00
int i, numDevSections;
2008-05-28 15:55:36 +01:00
GDevPtr *devSections;
Bool foundScreen = FALSE;
const char *dev;
2011-09-29 14:13:58 +01:00
ScrnInfoPtr scrn = NULL;
/* For now, just bail out for PROBE_DETECT. */
if (flags & PROBE_DETECT)
return FALSE;
2008-05-28 15:55:36 +01:00
/*
* Find the config file Device sections that match this
* driver, and return if there are none.
*/
if ((numDevSections = xf86MatchDevice("modesetting", &devSections)) <= 0) {
return FALSE;
2008-05-28 15:55:36 +01:00
}
for (i = 0; i < numDevSections; i++) {
int entity_num;
dev = xf86FindOptionValue(devSections[i]->options, "kmsdev");
if (probe_hw(dev, NULL)) {
entity_num = xf86ClaimFbSlot(drv, 0, devSections[i], TRUE);
scrn = xf86ConfigFbEntity(scrn, 0, entity_num, NULL, NULL, NULL, NULL);
}
if (scrn) {
foundScreen = TRUE;
ms_setup_scrn_hooks(scrn);
scrn->Probe = Probe;
xf86DrvMsg(scrn->scrnIndex, X_INFO,
"using %s\n", dev ? dev : "default device");
ms_setup_entity(scrn, entity_num);
}
2008-05-28 15:55:36 +01:00
}
free(devSections);
2008-05-28 15:55:36 +01:00
return foundScreen;
}
static Bool
GetRec(ScrnInfoPtr pScrn)
{
2008-05-28 15:55:36 +01:00
if (pScrn->driverPrivate)
return TRUE;
pScrn->driverPrivate = XNFcallocarray(1, sizeof(modesettingRec));
2008-05-28 15:55:36 +01:00
return TRUE;
}
static int
rotate_clip(PixmapPtr pixmap, xf86CrtcPtr crtc, BoxPtr rect, drmModeClip *clip,
Rotation rotation, int x, int y)
{
int w, h;
int x1, y1, x2, y2;
if (rotation == RR_Rotate_90 || rotation == RR_Rotate_270) {
/* width and height are swapped if rotated 90 or 270 degrees */
w = pixmap->drawable.height;
h = pixmap->drawable.width;
} else {
w = pixmap->drawable.width;
h = pixmap->drawable.height;
}
/* check if the given rect covers any area in FB of the crtc */
if (rect->x2 > crtc->x && rect->x1 < crtc->x + w &&
rect->y2 > crtc->y && rect->y1 < crtc->y + h) {
/* new coordinate of the partial rect on the crtc area
* + x/y offsets in the framebuffer */
x1 = max(rect->x1 - crtc->x, 0) + x;
y1 = max(rect->y1 - crtc->y, 0) + y;
x2 = min(rect->x2 - crtc->x, w) + x;
y2 = min(rect->y2 - crtc->y, h) + y;
/* coordinate transposing/inversion and offset adjustment */
if (rotation == RR_Rotate_90) {
clip->x1 = y1;
clip->y1 = w - x2;
clip->x2 = y2;
clip->y2 = w - x1;
} else if (rotation == RR_Rotate_180) {
clip->x1 = w - x2;
clip->y1 = h - y2;
clip->x2 = w - x1;
clip->y2 = h - y1;
} else if (rotation == RR_Rotate_270) {
clip->x1 = h - y2;
clip->y1 = x1;
clip->x2 = h - y1;;
clip->y2 = x2;
} else {
clip->x1 = x1;
clip->y1 = y1;
clip->x2 = x2;
clip->y2 = y2;
}
} else {
return -1;
}
return 0;
}
static int
dispatch_damages(ScrnInfoPtr scrn, xf86CrtcPtr crtc, RegionPtr dirty,
PixmapPtr pixmap, DamagePtr damage, int fb_id, int x, int y)
2011-09-29 12:38:26 +01:00
{
modesettingPtr ms = modesettingPTR(scrn);
unsigned num_cliprects = REGION_NUM_RECTS(dirty);
int ret = 0;
2011-09-29 12:38:26 +01:00
if (!ms->dirty_enabled)
return 0;
2011-09-29 12:38:26 +01:00
if (num_cliprects) {
drmModeClip *clip = xallocarray(num_cliprects, sizeof(drmModeClip));
BoxPtr rect = REGION_RECTS(dirty);
int i;
int c = 0;
if (!clip)
return -ENOMEM;
/* Create clips for the given rects in case the rect covers any
* area in the FB.
*/
for (i = 0; i < num_cliprects; i++, rect++) {
if (rotate_clip(pixmap, crtc, rect, &clip[c], crtc->rotation, x, y) < 0)
continue;
c++;
}
if (!c)
return 0;
/* TODO query connector property to see if this is needed */
ret = drmModeDirtyFB(ms->fd, fb_id, clip, c);
/* if we're swamping it with work, try one at a time */
if (ret == -EINVAL) {
for (i = 0; i < c; i++) {
if ((ret = drmModeDirtyFB(ms->fd, fb_id, &clip[i], 1)) < 0)
break;
}
}
if (ret == -EINVAL || ret == -ENOSYS) {
xf86DrvMsg(scrn->scrnIndex, X_INFO,
"Disabling kernel dirty updates, not required.\n");
ms->dirty_enabled = FALSE;
}
free(clip);
}
return ret;
}
static int
dispatch_dirty_region(ScrnInfoPtr scrn, xf86CrtcPtr crtc,
PixmapPtr pixmap, DamagePtr damage,
int fb_id, int x, int y)
{
return dispatch_damages(scrn, crtc, DamageRegion(damage),
pixmap, damage, fb_id, x, y);
}
static void
ms_tearfree_update_damages(ScreenPtr pScreen)
{
ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
modesettingPtr ms = modesettingPTR(scrn);
RegionPtr dirty = DamageRegion(ms->damage);
int c, i;
if (RegionNil(dirty))
return;
for (c = 0; c < xf86_config->num_crtc; c++) {
xf86CrtcPtr crtc = xf86_config->crtc[c];
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
RegionRec region;
/* Compute how much of the damage intersects with this CRTC */
RegionInit(&region, &crtc->bounds, 0);
RegionIntersect(&region, &region, dirty);
if (trf->buf[0].px) {
for (i = 0; i < ARRAY_SIZE(trf->buf); i++)
RegionUnion(&trf->buf[i].dmg, &trf->buf[i].dmg, &region);
} else {
/* Just notify the kernel of the damages if TearFree isn't used */
dispatch_damages(scrn, crtc, &region,
pScreen->GetScreenPixmap(pScreen),
NULL, ms->drmmode.fb_id, 0, 0);
}
}
DamageEmpty(ms->damage);
}
static void
ms_tearfree_do_flips(ScreenPtr pScreen)
{
#ifdef GLAMOR_HAS_GBM
ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
modesettingPtr ms = modesettingPTR(scrn);
int c;
if (!ms->drmmode.tearfree_enable)
return;
for (c = 0; c < xf86_config->num_crtc; c++) {
xf86CrtcPtr crtc = xf86_config->crtc[c];
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree;
modesetting: Support accurate DRI presentation timing with TearFree When using TearFree, DRI clients have no way of accurately knowing when their copied pixmaps appear on the display without utilizing the kernel driver's notification indicating that the TearFree flip containing their pixmap is complete. This is because the target CRTC's MSC can change while the predicted completion MSC is calculated and even while the page flip IOCTL is sent to the kernel due to scheduling delays and/or unfortunate timing. Even worse, a page flip isn't actually guaranteed to be finished after one vblank; it may be several MSCs until a flip actually finishes depending on delays and load in hardware. As a result, DRI clients may be off by one or more MSCs when they naively expect their pixmaps to be visible at MSC+1 with TearFree enabled. This, for example, makes it impossible for DRI clients to achieve precise A/V synchronization when TearFree is enabled. This change therefore adds a way for DRI clients to receive a notification straight from the TearFree flip-done handler to know exactly when their pixmaps appear on the display. This is done by checking for a NULL pixmap pointer to modesetting's DRI flip routine, which indicates that the DRI client has copied its pixmap and wants TearFree to send a notification when the copied pixmap appears on the display as part of a TearFree flip. The existing PageFlip scaffolding is reused to achieve this with minimal churn. The Present extension will be updated in an upcoming change to utilize this new mechanism for DRI clients' presentations. Signed-off-by: Sultan Alsawaf <sultan@kerneltoast.com> Acked-by: Martin Roukala <martin.roukala@mupuf.org>
2023-02-14 19:41:39 -08:00
if (!ms_tearfree_is_active_on_crtc(crtc)) {
/* Notify any lingering DRI clients waiting for a flip to finish */
ms_tearfree_dri_abort_all(crtc);
continue;
modesetting: Support accurate DRI presentation timing with TearFree When using TearFree, DRI clients have no way of accurately knowing when their copied pixmaps appear on the display without utilizing the kernel driver's notification indicating that the TearFree flip containing their pixmap is complete. This is because the target CRTC's MSC can change while the predicted completion MSC is calculated and even while the page flip IOCTL is sent to the kernel due to scheduling delays and/or unfortunate timing. Even worse, a page flip isn't actually guaranteed to be finished after one vblank; it may be several MSCs until a flip actually finishes depending on delays and load in hardware. As a result, DRI clients may be off by one or more MSCs when they naively expect their pixmaps to be visible at MSC+1 with TearFree enabled. This, for example, makes it impossible for DRI clients to achieve precise A/V synchronization when TearFree is enabled. This change therefore adds a way for DRI clients to receive a notification straight from the TearFree flip-done handler to know exactly when their pixmaps appear on the display. This is done by checking for a NULL pixmap pointer to modesetting's DRI flip routine, which indicates that the DRI client has copied its pixmap and wants TearFree to send a notification when the copied pixmap appears on the display as part of a TearFree flip. The existing PageFlip scaffolding is reused to achieve this with minimal churn. The Present extension will be updated in an upcoming change to utilize this new mechanism for DRI clients' presentations. Signed-off-by: Sultan Alsawaf <sultan@kerneltoast.com> Acked-by: Martin Roukala <martin.roukala@mupuf.org>
2023-02-14 19:41:39 -08:00
}
/* Skip if the last flip is still pending, a DRI client is flipping, or
* there isn't any damage on the front buffer.
*/
if (trf->flip_seq || ms->drmmode.dri2_flipping ||
ms->drmmode.present_flipping ||
RegionNil(&trf->buf[trf->back_idx ^ 1].dmg))
continue;
/* Flip. If it fails, notify the kernel of the front buffer damages */
if (ms_do_tearfree_flip(pScreen, crtc)) {
dispatch_damages(scrn, crtc, &trf->buf[trf->back_idx ^ 1].dmg,
trf->buf[trf->back_idx ^ 1].px, NULL,
trf->buf[trf->back_idx ^ 1].fb_id, 0, 0);
RegionEmpty(&trf->buf[trf->back_idx ^ 1].dmg);
}
}
#endif
}
static void
dispatch_dirty(ScreenPtr pScreen)
{
ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
modesettingPtr ms = modesettingPTR(scrn);
PixmapPtr pixmap = pScreen->GetScreenPixmap(pScreen);
uint32_t fb_id;
int ret, c, x, y ;
for (c = 0; c < xf86_config->num_crtc; c++) {
xf86CrtcPtr crtc = xf86_config->crtc[c];
PixmapPtr pmap;
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
if (!drmmode_crtc)
continue;
drmmode_crtc_get_fb_id(crtc, &fb_id, &x, &y);
if (crtc->rotatedPixmap)
pmap = crtc->rotatedPixmap;
else
pmap = pixmap;
ret = dispatch_dirty_region(scrn, crtc, pmap, ms->damage, fb_id, x, y);
if (ret == -EINVAL || ret == -ENOSYS) {
DamageUnregister(ms->damage);
DamageDestroy(ms->damage);
ms->damage = NULL;
return;
}
}
}
static void
modesetting: Implement PRIME syncing as a sink Implements (Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage, the sink functions for PRIME synchronization and double buffering. Allows modesetting driver to be used as a sink with PRIME synchronization. Changes dispatch_slave_dirty to flush damage from both scanout pixmaps. Changes drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target. Then, treat *target as it did prime_pixmap. This allows me to use it to explicitly set both prime_pixmap and prime_pixmap_back individually. drmmode_set_scanout_pixmap() without the extra parameter remains to cover the single-buffered case, but only works if we aren't already double buffered. driver.c: Add plumbing for rr(Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage. Change dispatch_dirty_crtc to dispatch_dirty_pixmap, which functions the same but flushes damage associated with a ppriv instead of the crtc, and chanage dispatch_slave_dirty to use it on both scanout pixmaps if applicable. drmmode_display.h: Add flip_seq field to msPixmapPrivRec to keep track of the event handler associated with a given pixmap, if any. Add wait_for_damage field to msPixmapPrivRec to keep track if we have requested a damage notification from the source. Add enable_flipping field to drmmode_crtc_private_rec to keep track if flipping is enabled or disabled. Add prime_pixmap_back to drmmode_crtc_private_rec to keep track of back buffer internally. Add declarations for drmmode_SetupPageFlipFence(), drmmode_EnableSharedPixmapFlipping(), drmmode_DisableSharedPixmapFlipping, drmmode_SharedPixmapFlip(), and drmmode_SharedPixmapPresentOnVBlank(). Move slave damage from crtc to ppriv. drmmode_display.c: Change drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target for explicitly setting different scanout pixmaps. Add definitions for functions drmmode_SharedPixmapFlip(), drmmode_SharedPixmapPresentOnVBlank(), drmmode_SharedPixmapPresent(), drmmode_SharedPixmapVBlankEventHandler(), drmmode_SharedPixmapVBlankEventAbort(), drmmode_EnableSharedPixmapFlipping(), and drmmode_DisableSharedPixmapFlipping, drmmode_InitSharedPixmapFlipping(), and drmmode_FiniSharedPixmapFlipping, along with struct vblank_event_args. The control flow is as follows: pScrPriv->rrEnableSharedPixmapFlipping() makes its way to drmmode_EnableSharedPixmapFlipping(), which sets enable_flipping to TRUE and sets both scanout pixmaps prime_pixmap and prime_pixmap_back. When setting a mode, if prime_pixmap is defined, modesetting driver will call drmmode_InitSharedPixmapFlipping(), which if flipping is enabled will call drmmode_SharedPixmapPresent() on scanout_pixmap_back. drmmode_SharedPixmapPresent() requests that for the source to present on the given buffer using master->PresentSharedPixmap(). If it succeeds, it will then attempt to flip to that buffer using drmmode_SharedPixmapFlip(). Flipping shouldn't fail, but if it does, it will raise a warning and try drmmode_SharedPixmapPresent() again on the next vblank using drmmode_SharedPixmapPresentOnVBlank(). master->PresentSharedPixmap() could fail, in most cases because there is no outstanding damage on the mscreenpix tracked by the shared pixmap. In this case, drmmode_SharedPixmapPresent() will attempt to use master->RequestSharedPixmapNotifyDamage() to request for the source driver to call slave->SharedPixmapNotifyDamage() in response to damage on mscreenpix. This will ultimately call into drmmode_SharedPixmapPresentOnVBlank() to retry drmmode_SharedPixmapPresent() on the next vblank after accumulating damage. drmmode_SharedPixmapFlip() sets up page flip event handler by packing struct vblank_event_args with the necessary parameters, and registering drmmode_SharedPixmapVBlankEventHandler() and drmmode_SharedPixmapVBlankEventAbort() with the modesetting DRM event handler queue. Then, it uses the drmModePageFlip() to flip on the next vblank and raise an event. drmmode_SharedPixmapPresentOnVBlank() operates similarly to drmmode_SharedPixmapFlip(), but uses drmWaitVBlank() instead of drmModePageFlip() to raise the event without flipping. On the next vblank, DRM will raise an event that will ultimately be handled by drmmode_SharedPixmapVBlankEventHandler(). If we flipped, it will update prime_pixmap and prime_pixmap_back to reflect that frontTarget is now being displayed, and use drmmode_SharedPixmapPresent(backTarget) to start the process again on the now-hidden shared pixmap. If we didn't flip, it will just use drmmode_SharedPixmapPresent(frontTarget) to start the process again on the still-hidden shared pixmap. Note that presentation generally happens asynchronously, so with these changes alone tearing is reduced, but we can't always guarantee that the present will finish before the flip. These changes are meant to be paired with changes to the sink DRM driver that makes flips wait on fences attached to dmabuf backed buffers. The source driver is responsible for attaching the fences and signaling them when presentation is finished. Note that because presentation is requested in response to a vblank, PRIME sources will now conform to the sink's refresh rate. At teardown, pScrPriv->rrDisableSharedPixmapFlipping() will be called, making its way to drmmode_FiniSharedPixmapFlipping(). There, the event handlers for prime_pixmap and prime_pixmap_back are aborted, freeing the left over parameter structure. Then, prime_pixmap and prime_pixmap back are unset as scanout pixmaps. Register and tear down slave damage per-scanout pixmap instead of per-crtc. v1: Initial commit v2: Renamed PresentTrackedFlippingPixmap to PresentSharedPixmap Renamed flipSeq to flip_seq Warn if flip failed Use SharedPixmapNotifyDamage to retry on next vblank after damage v3: Refactor to accomodate moving (rr)StartFlippingPixmapTracking and (rr)(Enable/Disable)SharedPixmapFlipping to rrScrPrivRec from ScreenRec Do damage tracking on both scanout pixmaps v4: Tweaks to commit message v5: Revise for internal storage of prime pixmap ptrs Move disabling for reverse PRIME from source commit to here Use drmmode_set_target_scanout_pixmap*() to set scanout pixmaps internally to EnableSharedPixmapFlipping(). Don't support flipping if ms->drmmode.pageflip == FALSE. Move flipping_active check to this commit v6: Rebase onto ToT v7: Unchanged Reviewed-by: Dave Airlie <airlied@redhat.com> Signed-off-by: Alex Goins <agoins@nvidia.com>
2016-06-16 20:06:52 -07:00
dispatch_dirty_pixmap(ScrnInfoPtr scrn, xf86CrtcPtr crtc, PixmapPtr ppix)
{
modesettingPtr ms = modesettingPTR(scrn);
modesetting: Implement PRIME syncing as a sink Implements (Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage, the sink functions for PRIME synchronization and double buffering. Allows modesetting driver to be used as a sink with PRIME synchronization. Changes dispatch_slave_dirty to flush damage from both scanout pixmaps. Changes drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target. Then, treat *target as it did prime_pixmap. This allows me to use it to explicitly set both prime_pixmap and prime_pixmap_back individually. drmmode_set_scanout_pixmap() without the extra parameter remains to cover the single-buffered case, but only works if we aren't already double buffered. driver.c: Add plumbing for rr(Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage. Change dispatch_dirty_crtc to dispatch_dirty_pixmap, which functions the same but flushes damage associated with a ppriv instead of the crtc, and chanage dispatch_slave_dirty to use it on both scanout pixmaps if applicable. drmmode_display.h: Add flip_seq field to msPixmapPrivRec to keep track of the event handler associated with a given pixmap, if any. Add wait_for_damage field to msPixmapPrivRec to keep track if we have requested a damage notification from the source. Add enable_flipping field to drmmode_crtc_private_rec to keep track if flipping is enabled or disabled. Add prime_pixmap_back to drmmode_crtc_private_rec to keep track of back buffer internally. Add declarations for drmmode_SetupPageFlipFence(), drmmode_EnableSharedPixmapFlipping(), drmmode_DisableSharedPixmapFlipping, drmmode_SharedPixmapFlip(), and drmmode_SharedPixmapPresentOnVBlank(). Move slave damage from crtc to ppriv. drmmode_display.c: Change drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target for explicitly setting different scanout pixmaps. Add definitions for functions drmmode_SharedPixmapFlip(), drmmode_SharedPixmapPresentOnVBlank(), drmmode_SharedPixmapPresent(), drmmode_SharedPixmapVBlankEventHandler(), drmmode_SharedPixmapVBlankEventAbort(), drmmode_EnableSharedPixmapFlipping(), and drmmode_DisableSharedPixmapFlipping, drmmode_InitSharedPixmapFlipping(), and drmmode_FiniSharedPixmapFlipping, along with struct vblank_event_args. The control flow is as follows: pScrPriv->rrEnableSharedPixmapFlipping() makes its way to drmmode_EnableSharedPixmapFlipping(), which sets enable_flipping to TRUE and sets both scanout pixmaps prime_pixmap and prime_pixmap_back. When setting a mode, if prime_pixmap is defined, modesetting driver will call drmmode_InitSharedPixmapFlipping(), which if flipping is enabled will call drmmode_SharedPixmapPresent() on scanout_pixmap_back. drmmode_SharedPixmapPresent() requests that for the source to present on the given buffer using master->PresentSharedPixmap(). If it succeeds, it will then attempt to flip to that buffer using drmmode_SharedPixmapFlip(). Flipping shouldn't fail, but if it does, it will raise a warning and try drmmode_SharedPixmapPresent() again on the next vblank using drmmode_SharedPixmapPresentOnVBlank(). master->PresentSharedPixmap() could fail, in most cases because there is no outstanding damage on the mscreenpix tracked by the shared pixmap. In this case, drmmode_SharedPixmapPresent() will attempt to use master->RequestSharedPixmapNotifyDamage() to request for the source driver to call slave->SharedPixmapNotifyDamage() in response to damage on mscreenpix. This will ultimately call into drmmode_SharedPixmapPresentOnVBlank() to retry drmmode_SharedPixmapPresent() on the next vblank after accumulating damage. drmmode_SharedPixmapFlip() sets up page flip event handler by packing struct vblank_event_args with the necessary parameters, and registering drmmode_SharedPixmapVBlankEventHandler() and drmmode_SharedPixmapVBlankEventAbort() with the modesetting DRM event handler queue. Then, it uses the drmModePageFlip() to flip on the next vblank and raise an event. drmmode_SharedPixmapPresentOnVBlank() operates similarly to drmmode_SharedPixmapFlip(), but uses drmWaitVBlank() instead of drmModePageFlip() to raise the event without flipping. On the next vblank, DRM will raise an event that will ultimately be handled by drmmode_SharedPixmapVBlankEventHandler(). If we flipped, it will update prime_pixmap and prime_pixmap_back to reflect that frontTarget is now being displayed, and use drmmode_SharedPixmapPresent(backTarget) to start the process again on the now-hidden shared pixmap. If we didn't flip, it will just use drmmode_SharedPixmapPresent(frontTarget) to start the process again on the still-hidden shared pixmap. Note that presentation generally happens asynchronously, so with these changes alone tearing is reduced, but we can't always guarantee that the present will finish before the flip. These changes are meant to be paired with changes to the sink DRM driver that makes flips wait on fences attached to dmabuf backed buffers. The source driver is responsible for attaching the fences and signaling them when presentation is finished. Note that because presentation is requested in response to a vblank, PRIME sources will now conform to the sink's refresh rate. At teardown, pScrPriv->rrDisableSharedPixmapFlipping() will be called, making its way to drmmode_FiniSharedPixmapFlipping(). There, the event handlers for prime_pixmap and prime_pixmap_back are aborted, freeing the left over parameter structure. Then, prime_pixmap and prime_pixmap back are unset as scanout pixmaps. Register and tear down slave damage per-scanout pixmap instead of per-crtc. v1: Initial commit v2: Renamed PresentTrackedFlippingPixmap to PresentSharedPixmap Renamed flipSeq to flip_seq Warn if flip failed Use SharedPixmapNotifyDamage to retry on next vblank after damage v3: Refactor to accomodate moving (rr)StartFlippingPixmapTracking and (rr)(Enable/Disable)SharedPixmapFlipping to rrScrPrivRec from ScreenRec Do damage tracking on both scanout pixmaps v4: Tweaks to commit message v5: Revise for internal storage of prime pixmap ptrs Move disabling for reverse PRIME from source commit to here Use drmmode_set_target_scanout_pixmap*() to set scanout pixmaps internally to EnableSharedPixmapFlipping(). Don't support flipping if ms->drmmode.pageflip == FALSE. Move flipping_active check to this commit v6: Rebase onto ToT v7: Unchanged Reviewed-by: Dave Airlie <airlied@redhat.com> Signed-off-by: Alex Goins <agoins@nvidia.com>
2016-06-16 20:06:52 -07:00
msPixmapPrivPtr ppriv = msGetPixmapPriv(&ms->drmmode, ppix);
DamagePtr damage = ppriv->secondary_damage;
int fb_id = ppriv->fb_id;
dispatch_dirty_region(scrn, crtc, ppix, damage, fb_id, 0, 0);
}
static void
dispatch_secondary_dirty(ScreenPtr pScreen)
{
ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
int c;
for (c = 0; c < xf86_config->num_crtc; c++) {
xf86CrtcPtr crtc = xf86_config->crtc[c];
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
if (!drmmode_crtc)
continue;
modesetting: Implement PRIME syncing as a sink Implements (Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage, the sink functions for PRIME synchronization and double buffering. Allows modesetting driver to be used as a sink with PRIME synchronization. Changes dispatch_slave_dirty to flush damage from both scanout pixmaps. Changes drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target. Then, treat *target as it did prime_pixmap. This allows me to use it to explicitly set both prime_pixmap and prime_pixmap_back individually. drmmode_set_scanout_pixmap() without the extra parameter remains to cover the single-buffered case, but only works if we aren't already double buffered. driver.c: Add plumbing for rr(Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage. Change dispatch_dirty_crtc to dispatch_dirty_pixmap, which functions the same but flushes damage associated with a ppriv instead of the crtc, and chanage dispatch_slave_dirty to use it on both scanout pixmaps if applicable. drmmode_display.h: Add flip_seq field to msPixmapPrivRec to keep track of the event handler associated with a given pixmap, if any. Add wait_for_damage field to msPixmapPrivRec to keep track if we have requested a damage notification from the source. Add enable_flipping field to drmmode_crtc_private_rec to keep track if flipping is enabled or disabled. Add prime_pixmap_back to drmmode_crtc_private_rec to keep track of back buffer internally. Add declarations for drmmode_SetupPageFlipFence(), drmmode_EnableSharedPixmapFlipping(), drmmode_DisableSharedPixmapFlipping, drmmode_SharedPixmapFlip(), and drmmode_SharedPixmapPresentOnVBlank(). Move slave damage from crtc to ppriv. drmmode_display.c: Change drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target for explicitly setting different scanout pixmaps. Add definitions for functions drmmode_SharedPixmapFlip(), drmmode_SharedPixmapPresentOnVBlank(), drmmode_SharedPixmapPresent(), drmmode_SharedPixmapVBlankEventHandler(), drmmode_SharedPixmapVBlankEventAbort(), drmmode_EnableSharedPixmapFlipping(), and drmmode_DisableSharedPixmapFlipping, drmmode_InitSharedPixmapFlipping(), and drmmode_FiniSharedPixmapFlipping, along with struct vblank_event_args. The control flow is as follows: pScrPriv->rrEnableSharedPixmapFlipping() makes its way to drmmode_EnableSharedPixmapFlipping(), which sets enable_flipping to TRUE and sets both scanout pixmaps prime_pixmap and prime_pixmap_back. When setting a mode, if prime_pixmap is defined, modesetting driver will call drmmode_InitSharedPixmapFlipping(), which if flipping is enabled will call drmmode_SharedPixmapPresent() on scanout_pixmap_back. drmmode_SharedPixmapPresent() requests that for the source to present on the given buffer using master->PresentSharedPixmap(). If it succeeds, it will then attempt to flip to that buffer using drmmode_SharedPixmapFlip(). Flipping shouldn't fail, but if it does, it will raise a warning and try drmmode_SharedPixmapPresent() again on the next vblank using drmmode_SharedPixmapPresentOnVBlank(). master->PresentSharedPixmap() could fail, in most cases because there is no outstanding damage on the mscreenpix tracked by the shared pixmap. In this case, drmmode_SharedPixmapPresent() will attempt to use master->RequestSharedPixmapNotifyDamage() to request for the source driver to call slave->SharedPixmapNotifyDamage() in response to damage on mscreenpix. This will ultimately call into drmmode_SharedPixmapPresentOnVBlank() to retry drmmode_SharedPixmapPresent() on the next vblank after accumulating damage. drmmode_SharedPixmapFlip() sets up page flip event handler by packing struct vblank_event_args with the necessary parameters, and registering drmmode_SharedPixmapVBlankEventHandler() and drmmode_SharedPixmapVBlankEventAbort() with the modesetting DRM event handler queue. Then, it uses the drmModePageFlip() to flip on the next vblank and raise an event. drmmode_SharedPixmapPresentOnVBlank() operates similarly to drmmode_SharedPixmapFlip(), but uses drmWaitVBlank() instead of drmModePageFlip() to raise the event without flipping. On the next vblank, DRM will raise an event that will ultimately be handled by drmmode_SharedPixmapVBlankEventHandler(). If we flipped, it will update prime_pixmap and prime_pixmap_back to reflect that frontTarget is now being displayed, and use drmmode_SharedPixmapPresent(backTarget) to start the process again on the now-hidden shared pixmap. If we didn't flip, it will just use drmmode_SharedPixmapPresent(frontTarget) to start the process again on the still-hidden shared pixmap. Note that presentation generally happens asynchronously, so with these changes alone tearing is reduced, but we can't always guarantee that the present will finish before the flip. These changes are meant to be paired with changes to the sink DRM driver that makes flips wait on fences attached to dmabuf backed buffers. The source driver is responsible for attaching the fences and signaling them when presentation is finished. Note that because presentation is requested in response to a vblank, PRIME sources will now conform to the sink's refresh rate. At teardown, pScrPriv->rrDisableSharedPixmapFlipping() will be called, making its way to drmmode_FiniSharedPixmapFlipping(). There, the event handlers for prime_pixmap and prime_pixmap_back are aborted, freeing the left over parameter structure. Then, prime_pixmap and prime_pixmap back are unset as scanout pixmaps. Register and tear down slave damage per-scanout pixmap instead of per-crtc. v1: Initial commit v2: Renamed PresentTrackedFlippingPixmap to PresentSharedPixmap Renamed flipSeq to flip_seq Warn if flip failed Use SharedPixmapNotifyDamage to retry on next vblank after damage v3: Refactor to accomodate moving (rr)StartFlippingPixmapTracking and (rr)(Enable/Disable)SharedPixmapFlipping to rrScrPrivRec from ScreenRec Do damage tracking on both scanout pixmaps v4: Tweaks to commit message v5: Revise for internal storage of prime pixmap ptrs Move disabling for reverse PRIME from source commit to here Use drmmode_set_target_scanout_pixmap*() to set scanout pixmaps internally to EnableSharedPixmapFlipping(). Don't support flipping if ms->drmmode.pageflip == FALSE. Move flipping_active check to this commit v6: Rebase onto ToT v7: Unchanged Reviewed-by: Dave Airlie <airlied@redhat.com> Signed-off-by: Alex Goins <agoins@nvidia.com>
2016-06-16 20:06:52 -07:00
if (drmmode_crtc->prime_pixmap)
dispatch_dirty_pixmap(scrn, crtc, drmmode_crtc->prime_pixmap);
if (drmmode_crtc->prime_pixmap_back)
dispatch_dirty_pixmap(scrn, crtc, drmmode_crtc->prime_pixmap_back);
2011-09-29 12:38:26 +01:00
}
}
static void
redisplay_dirty(ScreenPtr screen, PixmapDirtyUpdatePtr dirty, int *timeout)
{
RegionRec pixregion;
PixmapRegionInit(&pixregion, dirty->secondary_dst);
DamageRegionAppend(&dirty->secondary_dst->drawable, &pixregion);
PixmapSyncDirtyHelper(dirty);
if (!screen->isGPU) {
#ifdef GLAMOR_HAS_GBM
modesettingPtr ms = modesettingPTR(xf86ScreenToScrn(screen));
/*
* When copying from the primary framebuffer to the shared pixmap,
* we must ensure the copy is complete before the secondary starts a
* copy to its own framebuffer (some secondarys scanout directly from
* the shared pixmap, but not all).
*/
if (ms->drmmode.glamor)
ms->glamor.finish(screen);
#endif
/* Ensure the secondary processes the damage immediately */
if (timeout)
*timeout = 0;
}
DamageRegionProcessPending(&dirty->secondary_dst->drawable);
RegionUninit(&pixregion);
}
static void
ms_dirty_update(ScreenPtr screen, int *timeout)
{
modesettingPtr ms = modesettingPTR(xf86ScreenToScrn(screen));
RegionPtr region;
PixmapDirtyUpdatePtr ent;
if (xorg_list_is_empty(&screen->pixmap_dirty_list))
return;
xorg_list_for_each_entry(ent, &screen->pixmap_dirty_list, ent) {
region = DamageRegion(ent->damage);
if (RegionNotEmpty(region)) {
if (!screen->isGPU) {
msPixmapPrivPtr ppriv =
msGetPixmapPriv(&ms->drmmode, ent->secondary_dst->primary_pixmap);
if (ppriv->notify_on_damage) {
ppriv->notify_on_damage = FALSE;
ent->secondary_dst->drawable.pScreen->
SharedPixmapNotifyDamage(ent->secondary_dst);
}
/* Requested manual updating */
if (ppriv->defer_dirty_update)
continue;
}
redisplay_dirty(screen, ent, timeout);
DamageEmpty(ent->damage);
}
}
}
static PixmapDirtyUpdatePtr
ms_dirty_get_ent(ScreenPtr screen, PixmapPtr secondary_dst)
{
PixmapDirtyUpdatePtr ent;
if (xorg_list_is_empty(&screen->pixmap_dirty_list))
return NULL;
xorg_list_for_each_entry(ent, &screen->pixmap_dirty_list, ent) {
if (ent->secondary_dst == secondary_dst)
return ent;
}
return NULL;
}
static void
msBlockHandler(ScreenPtr pScreen, void *timeout)
2011-09-29 12:38:26 +01:00
{
modesettingPtr ms = modesettingPTR(xf86ScreenToScrn(pScreen));
2011-09-29 12:38:26 +01:00
pScreen->BlockHandler = ms->BlockHandler;
pScreen->BlockHandler(pScreen, timeout);
ms->BlockHandler = pScreen->BlockHandler;
2011-09-29 12:38:26 +01:00
pScreen->BlockHandler = msBlockHandler;
if (pScreen->isGPU && !ms->drmmode.reverse_prime_offload_mode)
dispatch_secondary_dirty(pScreen);
else if (ms->drmmode.tearfree_enable)
ms_tearfree_update_damages(pScreen);
else if (ms->dirty_enabled)
dispatch_dirty(pScreen);
ms_dirty_update(pScreen, timeout);
ms_tearfree_do_flips(pScreen);
2011-09-29 12:38:26 +01:00
}
static void
msBlockHandler_oneshot(ScreenPtr pScreen, void *pTimeout)
{
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
modesettingPtr ms = modesettingPTR(pScrn);
msBlockHandler(pScreen, pTimeout);
modesetting: keep going if a modeset fails on EnterVT There was a time when setting a mode on a CRTC would not depend on the associated connector's state. If a mode had been set successfully once, it would mean it would work later on. This changed with the introduction of new connectors type that now require a link training sequence (DP, HDMI 2.0), and that means that some events may have happened while the X server was not master that would then prevent the mode from successfully be restored to its previous state. This patch relaxes the requirement that all modes should be restored on EnterVT, or the entire X-Server would go down by allowing modesets to fail (with some warnings). If a modeset fails, the CRTC will be disabled, and a RandR event will be sent for the desktop environment to fix the situation as well as possible. Additional patches might be needed to make sure that the user would never be left with all screens black in some scenarios. v2 (Martin Peres): - whitespace fixes - remove the uevent handling (it is done in a previous patch) - improve the commit message - reduce the size of the patch by not changing lines needlessly - return FALSE if one modeset fails in ignore mode - add comments/todos to explain why we do things - disable the CRTCs that failed the modeset Signed-off-by: Kishore Kadiyala <kishore.kadiyala@intel.com> Signed-off-by: Martin Peres <martin.peres@linux.intel.com> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> Tested-by: Kishore Kadiyala <kishore.kadiyala@intel.com> Closes: #1010
2020-09-19 01:28:14 +05:30
drmmode_set_desired_modes(pScrn, &ms->drmmode, TRUE, FALSE);
}
Bool
ms_window_has_variable_refresh(modesettingPtr ms, WindowPtr win) {
struct ms_vrr_priv *priv = dixLookupPrivate(&win->devPrivates, &ms->drmmode.vrrPrivateKeyRec);
return priv->variable_refresh;
}
static void
ms_vrr_property_update(WindowPtr window, Bool variable_refresh)
{
ScrnInfoPtr scrn = xf86ScreenToScrn(window->drawable.pScreen);
modesettingPtr ms = modesettingPTR(scrn);
struct ms_vrr_priv *priv = dixLookupPrivate(&window->devPrivates,
&ms->drmmode.vrrPrivateKeyRec);
priv->variable_refresh = variable_refresh;
if (ms->flip_window == window && ms->drmmode.present_flipping)
ms_present_set_screen_vrr(scrn, variable_refresh);
}
/* Wrapper for xserver/dix/property.c:ProcChangeProperty */
static int
ms_change_property(ClientPtr client)
{
WindowPtr window = NULL;
int ret = 0;
REQUEST(xChangePropertyReq);
client->requestVector[X_ChangeProperty] = saved_change_property;
ret = saved_change_property(client);
if (restore_property_vector)
return ret;
client->requestVector[X_ChangeProperty] = ms_change_property;
if (ret != Success)
return ret;
ret = dixLookupWindow(&window, stuff->window, client, DixSetPropAccess);
if (ret != Success)
return ret;
// Checking for the VRR property change on the window
if (stuff->property == vrr_atom &&
xf86ScreenToScrn(window->drawable.pScreen)->PreInit == PreInit &&
stuff->format == 32 && stuff->nUnits == 1) {
uint32_t *value = (uint32_t *)(stuff + 1);
ms_vrr_property_update(window, *value != 0);
}
return ret;
}
/* Wrapper for xserver/dix/property.c:ProcDeleteProperty */
static int
ms_delete_property(ClientPtr client)
{
WindowPtr window;
int ret;
REQUEST(xDeletePropertyReq);
client->requestVector[X_DeleteProperty] = saved_delete_property;
ret = saved_delete_property(client);
if (restore_property_vector)
return ret;
client->requestVector[X_DeleteProperty] = ms_delete_property;
if (ret != Success)
return ret;
ret = dixLookupWindow(&window, stuff->window, client, DixSetPropAccess);
if (ret != Success)
return ret;
if (stuff->property == vrr_atom &&
xf86ScreenToScrn(window->drawable.pScreen)->PreInit == PreInit)
ms_vrr_property_update(window, FALSE);
return ret;
}
static void
ms_unwrap_property_requests(ScrnInfoPtr scrn)
{
int i;
if (!property_vectors_wrapped)
return;
if (ProcVector[X_ChangeProperty] == ms_change_property)
ProcVector[X_ChangeProperty] = saved_change_property;
else
restore_property_vector = TRUE;
if (ProcVector[X_DeleteProperty] == ms_delete_property)
ProcVector[X_DeleteProperty] = saved_delete_property;
else
restore_property_vector = TRUE;
for (i = 0; i < currentMaxClients; i++) {
if (clients[i]->requestVector[X_ChangeProperty] == ms_change_property) {
clients[i]->requestVector[X_ChangeProperty] = saved_change_property;
} else {
restore_property_vector = TRUE;
}
if (clients[i]->requestVector[X_DeleteProperty] == ms_delete_property) {
clients[i]->requestVector[X_DeleteProperty] = saved_delete_property;
} else {
restore_property_vector = TRUE;
}
}
if (restore_property_vector) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"Couldn't unwrap some window property request vectors\n");
}
property_vectors_wrapped = FALSE;
}
static void
FreeScreen(ScrnInfoPtr pScrn)
{
modesettingPtr ms;
2008-05-28 15:55:36 +01:00
if (!pScrn)
return;
ms = modesettingPTR(pScrn);
if (!ms)
return;
if (ms->fd > 0) {
modesettingEntPtr ms_ent;
int ret;
ms_ent = ms_ent_priv(pScrn);
ms_ent->fd_ref--;
if (!ms_ent->fd_ref) {
ms_unwrap_property_requests(pScrn);
if (ms->pEnt->location.type == BUS_PCI)
ret = drmClose(ms->fd);
else
#ifdef XF86_PDEV_SERVER_FD
if (!(ms->pEnt->location.type == BUS_PLATFORM &&
(ms->pEnt->location.id.plat->flags & XF86_PDEV_SERVER_FD)))
#endif
ret = close(ms->fd);
(void) ret;
ms_ent->fd = 0;
}
}
pScrn->driverPrivate = NULL;
free(ms->drmmode.Options);
free(ms);
}
#ifdef GLAMOR_HAS_GBM
static Bool
load_glamor(ScrnInfoPtr pScrn)
{
void *mod = xf86LoadSubModule(pScrn, GLAMOR_EGL_MODULE_NAME);
modesettingPtr ms = modesettingPTR(pScrn);
if (!mod)
return FALSE;
ms->glamor.back_pixmap_from_fd = LoaderSymbolFromModule(mod, "glamor_back_pixmap_from_fd");
ms->glamor.block_handler = LoaderSymbolFromModule(mod, "glamor_block_handler");
ms->glamor.clear_pixmap = LoaderSymbolFromModule(mod, "glamor_clear_pixmap");
ms->glamor.egl_create_textured_pixmap = LoaderSymbolFromModule(mod, "glamor_egl_create_textured_pixmap");
ms->glamor.egl_create_textured_pixmap_from_gbm_bo = LoaderSymbolFromModule(mod, "glamor_egl_create_textured_pixmap_from_gbm_bo");
ms->glamor.egl_exchange_buffers = LoaderSymbolFromModule(mod, "glamor_egl_exchange_buffers");
ms->glamor.egl_get_gbm_device = LoaderSymbolFromModule(mod, "glamor_egl_get_gbm_device");
ms->glamor.egl_init = LoaderSymbolFromModule(mod, "glamor_egl_init");
ms->glamor.finish = LoaderSymbolFromModule(mod, "glamor_finish");
ms->glamor.gbm_bo_from_pixmap = LoaderSymbolFromModule(mod, "glamor_gbm_bo_from_pixmap");
ms->glamor.init = LoaderSymbolFromModule(mod, "glamor_init");
ms->glamor.name_from_pixmap = LoaderSymbolFromModule(mod, "glamor_name_from_pixmap");
ms->glamor.set_drawable_modifiers_func = LoaderSymbolFromModule(mod, "glamor_set_drawable_modifiers_func");
ms->glamor.shareable_fd_from_pixmap = LoaderSymbolFromModule(mod, "glamor_shareable_fd_from_pixmap");
ms->glamor.supports_pixmap_import_export = LoaderSymbolFromModule(mod, "glamor_supports_pixmap_import_export");
ms->glamor.xv_init = LoaderSymbolFromModule(mod, "glamor_xv_init");
ms->glamor.egl_get_driver_name = LoaderSymbolFromModule(mod, "glamor_egl_get_driver_name");
return TRUE;
}
#endif
static void
try_enable_glamor(ScrnInfoPtr pScrn)
{
modesettingPtr ms = modesettingPTR(pScrn);
const char *accel_method_str = xf86GetOptValString(ms->drmmode.Options,
OPTION_ACCEL_METHOD);
Bool do_glamor = (!accel_method_str ||
strcmp(accel_method_str, "glamor") == 0);
ms->drmmode.glamor = FALSE;
#ifdef GLAMOR_HAS_GBM
if (ms->drmmode.force_24_32) {
xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Cannot use glamor with 24bpp packed fb\n");
return;
}
if (!do_glamor) {
xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "glamor disabled\n");
return;
}
if (load_glamor(pScrn)) {
if (ms->glamor.egl_init(pScrn, ms->fd)) {
xf86DrvMsg(pScrn->scrnIndex, X_INFO, "glamor initialized\n");
ms->drmmode.glamor = TRUE;
} else {
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"glamor initialization failed\n");
}
} else {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Failed to load glamor module.\n");
}
#else
if (do_glamor) {
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"No glamor support in the X Server\n");
}
#endif
}
static Bool
msShouldDoubleShadow(ScrnInfoPtr pScrn, modesettingPtr ms)
{
Bool ret = FALSE, asked;
int from;
drmVersionPtr v;
if (!ms->drmmode.shadow_enable)
return FALSE;
if ((v = drmGetVersion(ms->fd))) {
if (!strcmp(v->name, "mgag200") ||
!strcmp(v->name, "ast")) /* XXX || rn50 */
ret = TRUE;
drmFreeVersion(v);
}
else
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Failed to query DRM version.\n");
asked = xf86GetOptValBool(ms->drmmode.Options, OPTION_DOUBLE_SHADOW, &ret);
if (asked)
from = X_CONFIG;
else
from = X_INFO;
xf86DrvMsg(pScrn->scrnIndex, from,
"Double-buffered shadow updates: %s\n", ret ? "on" : "off");
return ret;
}
static Bool
ms_get_drm_master_fd(ScrnInfoPtr pScrn)
{
2008-05-28 15:55:36 +01:00
EntityInfoPtr pEnt;
modesettingPtr ms;
modesettingEntPtr ms_ent;
2008-05-28 15:55:36 +01:00
ms = modesettingPTR(pScrn);
ms_ent = ms_ent_priv(pScrn);
2008-05-28 15:55:36 +01:00
pEnt = ms->pEnt;
2008-05-28 15:55:36 +01:00
if (ms_ent->fd) {
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
" reusing fd for second head\n");
ms->fd = ms_ent->fd;
ms_ent->fd_ref++;
return TRUE;
2008-05-28 15:55:36 +01:00
}
ms->fd_passed = FALSE;
if ((ms->fd = get_passed_fd()) >= 0) {
ms->fd_passed = TRUE;
return TRUE;
}
#ifdef XSERVER_PLATFORM_BUS
2012-06-05 14:43:21 +01:00
if (pEnt->location.type == BUS_PLATFORM) {
#ifdef XF86_PDEV_SERVER_FD
if (pEnt->location.id.plat->flags & XF86_PDEV_SERVER_FD)
ms->fd =
xf86_platform_device_odev_attributes(pEnt->location.id.plat)->
fd;
else
#endif
{
char *path =
xf86_platform_device_odev_attributes(pEnt->location.id.plat)->
path;
ms->fd = open_hw(path);
}
2012-06-05 14:43:21 +01:00
}
else
2012-06-05 14:43:21 +01:00
#endif
#ifdef XSERVER_LIBPCIACCESS
2012-06-05 14:43:21 +01:00
if (pEnt->location.type == BUS_PCI) {
char *BusID = NULL;
struct pci_device *PciInfo;
PciInfo = xf86GetPciInfoForEntity(ms->pEnt->index);
if (PciInfo) {
if ((BusID = ms_DRICreatePCIBusID(PciInfo)) != NULL) {
ms->fd = drmOpen(NULL, BusID);
free(BusID);
}
2012-06-05 14:43:21 +01:00
}
}
else
#endif
{
const char *devicename;
2012-06-05 14:43:21 +01:00
devicename = xf86FindOptionValue(ms->pEnt->device->options, "kmsdev");
ms->fd = open_hw(devicename);
}
if (ms->fd < 0)
return FALSE;
ms_ent->fd = ms->fd;
ms_ent->fd_ref = 1;
return TRUE;
}
static Bool
PreInit(ScrnInfoPtr pScrn, int flags)
{
modesettingPtr ms;
rgb defaultWeight = { 0, 0, 0 };
EntityInfoPtr pEnt;
uint64_t value = 0;
int ret;
int bppflags, connector_count;
int defaultdepth, defaultbpp;
if (pScrn->numEntities != 1)
return FALSE;
if (flags & PROBE_DETECT) {
return FALSE;
}
/* Allocate driverPrivate */
if (!GetRec(pScrn))
return FALSE;
pEnt = xf86GetEntityInfo(pScrn->entityList[0]);
ms = modesettingPTR(pScrn);
ms->SaveGeneration = -1;
ms->pEnt = pEnt;
ms->drmmode.is_secondary = FALSE;
pScrn->displayWidth = 640; /* default it */
if (xf86IsEntityShared(pScrn->entityList[0])) {
if (xf86IsPrimInitDone(pScrn->entityList[0]))
ms->drmmode.is_secondary = TRUE;
else
xf86SetPrimInitDone(pScrn->entityList[0]);
}
pScrn->monitor = pScrn->confScreen->monitor;
pScrn->progClock = TRUE;
pScrn->rgbBits = 8;
if (!ms_get_drm_master_fd(pScrn))
return FALSE;
ms->drmmode.fd = ms->fd;
if (!check_outputs(ms->fd, &connector_count))
return FALSE;
drmmode_get_default_bpp(pScrn, &ms->drmmode, &defaultdepth, &defaultbpp);
if (defaultdepth == 24 && defaultbpp == 24) {
ms->drmmode.force_24_32 = TRUE;
ms->drmmode.kbpp = 24;
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"Using 24bpp hw front buffer with 32bpp shadow\n");
defaultbpp = 32;
} else {
ms->drmmode.kbpp = 0;
}
bppflags = PreferConvert24to32 | SupportConvert24to32 | Support32bppFb;
if (!xf86SetDepthBpp
(pScrn, defaultdepth, defaultdepth, defaultbpp, bppflags))
return FALSE;
2008-05-28 15:55:36 +01:00
switch (pScrn->depth) {
case 15:
case 16:
case 24:
case 30:
break;
2008-05-28 15:55:36 +01:00
default:
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Given depth (%d) is not supported by the driver\n",
pScrn->depth);
return FALSE;
2008-05-28 15:55:36 +01:00
}
xf86PrintDepthBpp(pScrn);
if (!ms->drmmode.kbpp)
ms->drmmode.kbpp = pScrn->bitsPerPixel;
2008-05-28 15:55:36 +01:00
/* Process the options */
xf86CollectOptions(pScrn, NULL);
if (!(ms->drmmode.Options = malloc(sizeof(Options))))
return FALSE;
memcpy(ms->drmmode.Options, Options, sizeof(Options));
xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, ms->drmmode.Options);
2008-05-28 15:55:36 +01:00
if (!xf86SetWeight(pScrn, defaultWeight, defaultWeight))
return FALSE;
if (!xf86SetDefaultVisual(pScrn, -1))
return FALSE;
if (xf86ReturnOptValBool(ms->drmmode.Options, OPTION_SW_CURSOR, FALSE)) {
ms->drmmode.sw_cursor = TRUE;
2008-05-28 15:55:36 +01:00
}
modesetting: Use a more optimal hw cursor size Try to minimize the used hw cursor size in order to minimize power consumption. There is no kernel query for the minimum so we'll just probe around with setcursor2 (using an invisible cursor image so there will be no visual artifacts). To avoid having to deal with absolutely every size stick to power-of-two numbers. And with a bit of extra effort we can determine whether non-square dimesions will also work, which they do to some degree on current Intel GPUs. On my Alderlake laptop I'm seeing a massive (up to .5W) difference in power consumption between 64x64 vs. 256x256 cursors. While some of that is undoubtedly something that needs to be fixed in i915's display data buffer allocation code, it still makes sense to use as small as possible cursor to minimize the wastege. In case the crtc is rotated just punt to the max cursor size for now since midlayer has already done the coordinate transformations based on that. To make smaller cursors work with rotation we'd either need to make the midlayer(s) aware of the final cursor size, or just handle the whole roation business in modesetting. I suspect the latter option would be easier. v2: Only allow square cursors in most cases for now as eg. on modern Intel hardware non-square only works with wide+short but not with narrow+tall cursors. Non-square size may still be used when maximum limits aren't square and the squared+POT'd dimensions would exceed one of the max limits. Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
2023-02-04 18:25:53 +02:00
ms->max_cursor_width = 64;
ms->max_cursor_height = 64;
ret = drmGetCap(ms->fd, DRM_CAP_CURSOR_WIDTH, &value);
if (!ret) {
modesetting: Use a more optimal hw cursor size Try to minimize the used hw cursor size in order to minimize power consumption. There is no kernel query for the minimum so we'll just probe around with setcursor2 (using an invisible cursor image so there will be no visual artifacts). To avoid having to deal with absolutely every size stick to power-of-two numbers. And with a bit of extra effort we can determine whether non-square dimesions will also work, which they do to some degree on current Intel GPUs. On my Alderlake laptop I'm seeing a massive (up to .5W) difference in power consumption between 64x64 vs. 256x256 cursors. While some of that is undoubtedly something that needs to be fixed in i915's display data buffer allocation code, it still makes sense to use as small as possible cursor to minimize the wastege. In case the crtc is rotated just punt to the max cursor size for now since midlayer has already done the coordinate transformations based on that. To make smaller cursors work with rotation we'd either need to make the midlayer(s) aware of the final cursor size, or just handle the whole roation business in modesetting. I suspect the latter option would be easier. v2: Only allow square cursors in most cases for now as eg. on modern Intel hardware non-square only works with wide+short but not with narrow+tall cursors. Non-square size may still be used when maximum limits aren't square and the squared+POT'd dimensions would exceed one of the max limits. Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
2023-02-04 18:25:53 +02:00
ms->max_cursor_width = value;
}
ret = drmGetCap(ms->fd, DRM_CAP_CURSOR_HEIGHT, &value);
if (!ret) {
modesetting: Use a more optimal hw cursor size Try to minimize the used hw cursor size in order to minimize power consumption. There is no kernel query for the minimum so we'll just probe around with setcursor2 (using an invisible cursor image so there will be no visual artifacts). To avoid having to deal with absolutely every size stick to power-of-two numbers. And with a bit of extra effort we can determine whether non-square dimesions will also work, which they do to some degree on current Intel GPUs. On my Alderlake laptop I'm seeing a massive (up to .5W) difference in power consumption between 64x64 vs. 256x256 cursors. While some of that is undoubtedly something that needs to be fixed in i915's display data buffer allocation code, it still makes sense to use as small as possible cursor to minimize the wastege. In case the crtc is rotated just punt to the max cursor size for now since midlayer has already done the coordinate transformations based on that. To make smaller cursors work with rotation we'd either need to make the midlayer(s) aware of the final cursor size, or just handle the whole roation business in modesetting. I suspect the latter option would be easier. v2: Only allow square cursors in most cases for now as eg. on modern Intel hardware non-square only works with wide+short but not with narrow+tall cursors. Non-square size may still be used when maximum limits aren't square and the squared+POT'd dimensions would exceed one of the max limits. Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
2023-02-04 18:25:53 +02:00
ms->max_cursor_height = value;
}
try_enable_glamor(pScrn);
if (!ms->drmmode.glamor) {
Bool prefer_shadow = TRUE;
if (ms->drmmode.force_24_32) {
prefer_shadow = TRUE;
ms->drmmode.shadow_enable = TRUE;
} else {
ret = drmGetCap(ms->fd, DRM_CAP_DUMB_PREFER_SHADOW, &value);
if (!ret) {
prefer_shadow = !!value;
}
ms->drmmode.shadow_enable =
xf86ReturnOptValBool(ms->drmmode.Options, OPTION_SHADOW_FB,
prefer_shadow);
}
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"ShadowFB: preferred %s, enabled %s\n",
prefer_shadow ? "YES" : "NO",
ms->drmmode.force_24_32 ? "FORCE" :
ms->drmmode.shadow_enable ? "YES" : "NO");
ms->drmmode.shadow_enable2 = msShouldDoubleShadow(pScrn, ms);
} else {
if (!pScrn->is_gpu) {
MessageType from = xf86GetOptValBool(ms->drmmode.Options, OPTION_VARIABLE_REFRESH,
&ms->vrr_support) ? X_CONFIG : X_DEFAULT;
xf86DrvMsg(pScrn->scrnIndex, from, "VariableRefresh: %sabled\n",
ms->vrr_support ? "en" : "dis");
modesetting: Add option for non-vsynced flips for "secondary" outputs. Whenever an unredirected fullscreen window uses pageflipping for a DRI3/Present PresentPixmap() operation and the X-Screen has more than one active output, multiple crtc's need to execute pageflips. Only after the last flip has completed can the PresentPixmap operation as a whole complete. If a sync_flip is requested for the present, then the current implementation will synchronize each pageflip to the vblank of its associated crtc. This provides tear-free image presentation across all outputs, but introduces a different artifact, if not all outputs run at the same refresh rate with perfect synchrony: The slowest output throttles the presentation rate, and present completion is delayed to flip completion of the "latest" output to complete. This means degraded performance, e.g., a dual-display setup with a 144 Hz monitor and a 60 Hz monitor will always be throttled to at most 60 fps. It also means non-constant present rate if refresh cycles drift against each other, creating complex "beat patterns", tremors, stutters and periodic slowdowns - quite irritating! Such a scenario will be especially annoying if one uses multiple outputs in "mirror mode" aka "clone mode". One output will usually be the "production output" with the highest quality and fastest display attached, whereas a secondary mirror output just has a cheaper display for monitoring attached. Users care about perfect and perfectly timed tear-free presentation on the "production output", but cares less about quality on the secondary "mirror output". They are willing to trade quality on secondary outputs away in exchange for better presentation timing on the "production output". One example use case for such production + monitoring displays are neuroscience / medical science applications where one high quality display device is used to present visual animations to test subjects or patients in a fMRI scanner room (production display), whereas an operator monitors the same visual animations from a control room on a lower quality display. Presentation timing needs to be perfect, and animations high-speed and tear-free for the production display, whereas quality and timing don't matter for the monitoring display. This commit gives users the option to choose such a trade-off as opt-in: It adds a new boolean option "AsyncFlipSecondaries" to the device section of xorg.conf. If this option is specified as true, then DRI3 pageflip behaviour changes as follows: 1. The "reference crtc" for a windows PresentPixmap operation does a vblank synced flip, or a DRM_MODE_PAGE_FLIP_ASYNC non-synchronized flip, as requested by the caller, just as in the past. Typically flips will be requested to be vblank synchronized for tear-free presentation. The "reference crtc" is the one chosen by the caller to drive presentation timing (as specified by PresentPixmap()'s "target_msc", "divisor", "remainder" parameters and implemented by vblank events) and to deliver Present completion timestamps (msc and ust) extracted from its pageflip completion event. 2. All other crtc's, which also page-flip in a multi-display configuration, will try to flip with DRM_MODE_PAGE_FLIP_ASYNC, ie. immediately and not synchronized to vblank. This allows the PresentPixmap operation to complete with little delay compared to a single-display present, especially if the different crtc's run at different video refresh rates or their refresh cycles are not perfectly synchronized, but drift against each other. The downside is potential tearing artifacts on all outputs apart from the one of the "reference crtc". Successfully tested on a AMD gpu with single-display, dual-display and triple-display setups, and with single-X-Screen as well as dual-X-Screen "ZaphodHeads" configurations. Please consider merging this commit for the upcoming server 1.21 branch. Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com>
2021-08-30 05:42:04 +02:00
ms->drmmode.async_flip_secondaries = FALSE;
from = xf86GetOptValBool(ms->drmmode.Options, OPTION_ASYNC_FLIP_SECONDARIES,
&ms->drmmode.async_flip_secondaries) ? X_CONFIG : X_DEFAULT;
xf86DrvMsg(pScrn->scrnIndex, from, "AsyncFlipSecondaries: %sabled\n",
ms->drmmode.async_flip_secondaries ? "en" : "dis");
}
}
ms->drmmode.pageflip =
xf86ReturnOptValBool(ms->drmmode.Options, OPTION_PAGEFLIP, TRUE);
pScrn->capabilities = 0;
ret = drmGetCap(ms->fd, DRM_CAP_PRIME, &value);
if (ret == 0) {
if (connector_count && (value & DRM_PRIME_CAP_IMPORT)) {
pScrn->capabilities |= RR_Capability_SinkOutput;
if (ms->drmmode.glamor)
pScrn->capabilities |= RR_Capability_SinkOffload;
}
#ifdef GLAMOR_HAS_GBM_LINEAR
if (value & DRM_PRIME_CAP_EXPORT && ms->drmmode.glamor)
pScrn->capabilities |= RR_Capability_SourceOutput | RR_Capability_SourceOffload;
#endif
}
modesetting: Allow Present flips with mismatched stride on atomic drivers. When using DRI3+Present with PRIME render offload, sometimes there is a mismatch between the stride of the to-be-presented Pixmap and the frontbuffer. The current code would reject a pageflip present in this case if atomic modesetting is not enabled, ie. always, as atomic modesetting is disabled by default due to brokeness in the current modesetting-ddx. Fullscreen presents without page flipping however trigger the copy path as fallback, which causes not only unreliable presentation timing and degraded performance, but also massive tearing artifacts due to rendering to the framebuffer without any hardware sync to vblank. Tearing is extra awful on modesetting-ddx because glamor afaics seems to use drawing of a textured triangle strip for the copy implementation, not a dedicated blitter engine. The rasterization pattern creates extra awful tearing artifacts. We can do better: According to a tip from Michel Daenzer (thanks!), at least atomic modesetting capable kms drivers should be able to reliably change scanout stride during a pageflip, even if atomic modesetting is not actually enabled for the modesetting client. This commit adds detection logic to find out if the underlying kms driver is atomic_modeset_capable, and if so, it no longer rejects page flip presents on mismatched stride between new Pixmap and frontbuffer. We (ab)use a call to drmSetClientCap(ms->fd, DRM_CLIENT_CAP_ATOMIC, 0); for this purpose. The call itself has no practical effect, as it requests disabling atomic mode, although atomic mode is disabled by default. However, the return value of drmSetClientCap() tells us if the underlying kms driver is atomic modesetting capable: An atomic driver will return 0 for success. A legacy non-atomic driver will return a non-zero error code, either -EINVAL for early atomic Linux versions 4.0 - 4.19 (or for non-atomic Linux 3.x and earlier), or -EOPNOTSUPP for Linux 4.20 and later. Testing on a MacBookPro 2017 with Intel Kabylake display server gpu + AMD Polaris11 as prime renderoffload gpu, X-Server master + Mesa 21.0.3 show improvement from unbearable tearing to perfect, despite a stride mismatch between display gpu and Pixmap of 11776 Bytes vs. 11520 Bytes. That this is correct behaviour was also confirmed by comparing the behaviour and .check_flip implementation of the patched modesetting-ddx against the current intel-ddx SNA Present implementation. Please consider merging this patch before the server-1.21 branch point. This patch could also be cherry-picked into the server 1.20 branch to fix the same limitation. Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com>
2021-08-28 02:27:20 +02:00
/*
* Use "atomic modesetting disable" request to detect if the kms driver is
* atomic capable, regardless if we will actually use atomic modesetting.
* This is effectively a no-op, we only care about the return status code.
*/
ret = drmSetClientCap(ms->fd, DRM_CLIENT_CAP_ATOMIC, 0);
ms->atomic_modeset_capable = (ret == 0);
if (xf86ReturnOptValBool(ms->drmmode.Options, OPTION_ATOMIC, FALSE)) {
ret = drmSetClientCap(ms->fd, DRM_CLIENT_CAP_ATOMIC, 1);
ms->atomic_modeset = (ret == 0);
if (!ms->atomic_modeset)
xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Atomic modesetting not supported\n");
} else {
ms->atomic_modeset = FALSE;
}
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"Atomic modesetting %sabled\n", ms->atomic_modeset ? "en" : "dis");
/* TearFree requires glamor and, if PageFlip is enabled, universal planes */
if (xf86ReturnOptValBool(ms->drmmode.Options, OPTION_TEARFREE, TRUE)) {
if (pScrn->is_gpu) {
xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
"TearFree cannot synchronize PRIME; use 'PRIME Synchronization' instead\n");
} else if (ms->drmmode.glamor) {
/* Atomic modesetting implicitly enables universal planes */
if (!ms->drmmode.pageflip || ms->atomic_modeset ||
!drmSetClientCap(ms->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) {
ms->drmmode.tearfree_enable = TRUE;
xf86DrvMsg(pScrn->scrnIndex, X_INFO, "TearFree: enabled\n");
} else {
xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
"TearFree requires either universal planes, or setting 'Option \"PageFlip\" \"off\"'\n");
}
} else {
xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
"TearFree requires Glamor acceleration\n");
}
}
ms->kms_has_modifiers = FALSE;
ret = drmGetCap(ms->fd, DRM_CAP_ADDFB2_MODIFIERS, &value);
if (ret == 0 && value != 0)
ms->kms_has_modifiers = TRUE;
if (drmmode_pre_init(pScrn, &ms->drmmode, pScrn->bitsPerPixel / 8) == FALSE) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "KMS setup failed\n");
goto fail;
2008-05-28 15:55:36 +01:00
}
2008-05-28 15:55:36 +01:00
/*
* If the driver can do gamma correction, it should call xf86SetGamma() here.
*/
{
Gamma zeros = { 0.0, 0.0, 0.0 };
if (!xf86SetGamma(pScrn, zeros)) {
return FALSE;
}
2008-05-28 15:55:36 +01:00
}
if (!(pScrn->is_gpu && connector_count == 0) && pScrn->modes == NULL) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No modes.\n");
return FALSE;
2008-05-28 15:55:36 +01:00
}
2008-05-28 15:55:36 +01:00
pScrn->currentMode = pScrn->modes;
2008-05-28 15:55:36 +01:00
/* Set display resolution */
xf86SetDpi(pScrn, 0, 0);
2008-05-28 15:55:36 +01:00
/* Load the required sub modules */
if (!xf86LoadSubModule(pScrn, "fb")) {
return FALSE;
2008-05-28 15:55:36 +01:00
}
if (ms->drmmode.shadow_enable) {
void *mod = xf86LoadSubModule(pScrn, "shadow");
if (!mod)
return FALSE;
ms->shadow.Setup = LoaderSymbolFromModule(mod, "shadowSetup");
ms->shadow.Add = LoaderSymbolFromModule(mod, "shadowAdd");
ms->shadow.Remove = LoaderSymbolFromModule(mod, "shadowRemove");
ms->shadow.Update32to24 = LoaderSymbolFromModule(mod, "shadowUpdate32to24");
ms->shadow.UpdatePacked = LoaderSymbolFromModule(mod, "shadowUpdatePacked");
}
2008-05-28 15:55:36 +01:00
return TRUE;
fail:
return FALSE;
}
static void *
msShadowWindow(ScreenPtr screen, CARD32 row, CARD32 offset, int mode,
CARD32 *size, void *closure)
{
ScrnInfoPtr pScrn = xf86ScreenToScrn(screen);
modesettingPtr ms = modesettingPTR(pScrn);
int stride;
stride = (pScrn->displayWidth * ms->drmmode.kbpp) / 8;
*size = stride;
return ((uint8_t *) ms->drmmode.front_bo.dumb->ptr + row * stride + offset);
}
/* somewhat arbitrary tile size, in pixels */
#define TILE 16
static int
msUpdateIntersect(modesettingPtr ms, shadowBufPtr pBuf, BoxPtr box,
xRectangle *prect)
{
int i, dirty = 0, stride = pBuf->pPixmap->devKind, cpp = ms->drmmode.cpp;
int width = (box->x2 - box->x1) * cpp;
unsigned char *old, *new;
old = ms->drmmode.shadow_fb2;
old += (box->y1 * stride) + (box->x1 * cpp);
new = ms->drmmode.shadow_fb;
new += (box->y1 * stride) + (box->x1 * cpp);
for (i = box->y2 - box->y1 - 1; i >= 0; i--) {
unsigned char *o = old + i * stride,
*n = new + i * stride;
if (memcmp(o, n, width) != 0) {
dirty = 1;
memcpy(o, n, width);
}
}
if (dirty) {
prect->x = box->x1;
prect->y = box->y1;
prect->width = box->x2 - box->x1;
prect->height = box->y2 - box->y1;
}
return dirty;
}
static void
msUpdatePacked(ScreenPtr pScreen, shadowBufPtr pBuf)
{
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
modesettingPtr ms = modesettingPTR(pScrn);
Bool use_3224 = ms->drmmode.force_24_32 && pScrn->bitsPerPixel == 32;
if (ms->drmmode.shadow_enable2 && ms->drmmode.shadow_fb2) do {
RegionPtr damage = DamageRegion(pBuf->pDamage), tiles;
BoxPtr extents = RegionExtents(damage);
xRectangle *prect;
int nrects;
int i, j, tx1, tx2, ty1, ty2;
tx1 = extents->x1 / TILE;
tx2 = (extents->x2 + TILE - 1) / TILE;
ty1 = extents->y1 / TILE;
ty2 = (extents->y2 + TILE - 1) / TILE;
nrects = (tx2 - tx1) * (ty2 - ty1);
if (!(prect = calloc(nrects, sizeof(xRectangle))))
break;
nrects = 0;
for (j = ty2 - 1; j >= ty1; j--) {
for (i = tx2 - 1; i >= tx1; i--) {
BoxRec box;
box.x1 = max(i * TILE, extents->x1);
box.y1 = max(j * TILE, extents->y1);
box.x2 = min((i+1) * TILE, extents->x2);
box.y2 = min((j+1) * TILE, extents->y2);
if (RegionContainsRect(damage, &box) != rgnOUT) {
if (msUpdateIntersect(ms, pBuf, &box, prect + nrects)) {
nrects++;
}
}
}
}
tiles = RegionFromRects(nrects, prect, CT_NONE);
RegionIntersect(damage, damage, tiles);
RegionDestroy(tiles);
free(prect);
} while (0);
if (use_3224)
ms->shadow.Update32to24(pScreen, pBuf);
else
ms->shadow.UpdatePacked(pScreen, pBuf);
}
modesetting: Implement PRIME syncing as a sink Implements (Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage, the sink functions for PRIME synchronization and double buffering. Allows modesetting driver to be used as a sink with PRIME synchronization. Changes dispatch_slave_dirty to flush damage from both scanout pixmaps. Changes drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target. Then, treat *target as it did prime_pixmap. This allows me to use it to explicitly set both prime_pixmap and prime_pixmap_back individually. drmmode_set_scanout_pixmap() without the extra parameter remains to cover the single-buffered case, but only works if we aren't already double buffered. driver.c: Add plumbing for rr(Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage. Change dispatch_dirty_crtc to dispatch_dirty_pixmap, which functions the same but flushes damage associated with a ppriv instead of the crtc, and chanage dispatch_slave_dirty to use it on both scanout pixmaps if applicable. drmmode_display.h: Add flip_seq field to msPixmapPrivRec to keep track of the event handler associated with a given pixmap, if any. Add wait_for_damage field to msPixmapPrivRec to keep track if we have requested a damage notification from the source. Add enable_flipping field to drmmode_crtc_private_rec to keep track if flipping is enabled or disabled. Add prime_pixmap_back to drmmode_crtc_private_rec to keep track of back buffer internally. Add declarations for drmmode_SetupPageFlipFence(), drmmode_EnableSharedPixmapFlipping(), drmmode_DisableSharedPixmapFlipping, drmmode_SharedPixmapFlip(), and drmmode_SharedPixmapPresentOnVBlank(). Move slave damage from crtc to ppriv. drmmode_display.c: Change drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target for explicitly setting different scanout pixmaps. Add definitions for functions drmmode_SharedPixmapFlip(), drmmode_SharedPixmapPresentOnVBlank(), drmmode_SharedPixmapPresent(), drmmode_SharedPixmapVBlankEventHandler(), drmmode_SharedPixmapVBlankEventAbort(), drmmode_EnableSharedPixmapFlipping(), and drmmode_DisableSharedPixmapFlipping, drmmode_InitSharedPixmapFlipping(), and drmmode_FiniSharedPixmapFlipping, along with struct vblank_event_args. The control flow is as follows: pScrPriv->rrEnableSharedPixmapFlipping() makes its way to drmmode_EnableSharedPixmapFlipping(), which sets enable_flipping to TRUE and sets both scanout pixmaps prime_pixmap and prime_pixmap_back. When setting a mode, if prime_pixmap is defined, modesetting driver will call drmmode_InitSharedPixmapFlipping(), which if flipping is enabled will call drmmode_SharedPixmapPresent() on scanout_pixmap_back. drmmode_SharedPixmapPresent() requests that for the source to present on the given buffer using master->PresentSharedPixmap(). If it succeeds, it will then attempt to flip to that buffer using drmmode_SharedPixmapFlip(). Flipping shouldn't fail, but if it does, it will raise a warning and try drmmode_SharedPixmapPresent() again on the next vblank using drmmode_SharedPixmapPresentOnVBlank(). master->PresentSharedPixmap() could fail, in most cases because there is no outstanding damage on the mscreenpix tracked by the shared pixmap. In this case, drmmode_SharedPixmapPresent() will attempt to use master->RequestSharedPixmapNotifyDamage() to request for the source driver to call slave->SharedPixmapNotifyDamage() in response to damage on mscreenpix. This will ultimately call into drmmode_SharedPixmapPresentOnVBlank() to retry drmmode_SharedPixmapPresent() on the next vblank after accumulating damage. drmmode_SharedPixmapFlip() sets up page flip event handler by packing struct vblank_event_args with the necessary parameters, and registering drmmode_SharedPixmapVBlankEventHandler() and drmmode_SharedPixmapVBlankEventAbort() with the modesetting DRM event handler queue. Then, it uses the drmModePageFlip() to flip on the next vblank and raise an event. drmmode_SharedPixmapPresentOnVBlank() operates similarly to drmmode_SharedPixmapFlip(), but uses drmWaitVBlank() instead of drmModePageFlip() to raise the event without flipping. On the next vblank, DRM will raise an event that will ultimately be handled by drmmode_SharedPixmapVBlankEventHandler(). If we flipped, it will update prime_pixmap and prime_pixmap_back to reflect that frontTarget is now being displayed, and use drmmode_SharedPixmapPresent(backTarget) to start the process again on the now-hidden shared pixmap. If we didn't flip, it will just use drmmode_SharedPixmapPresent(frontTarget) to start the process again on the still-hidden shared pixmap. Note that presentation generally happens asynchronously, so with these changes alone tearing is reduced, but we can't always guarantee that the present will finish before the flip. These changes are meant to be paired with changes to the sink DRM driver that makes flips wait on fences attached to dmabuf backed buffers. The source driver is responsible for attaching the fences and signaling them when presentation is finished. Note that because presentation is requested in response to a vblank, PRIME sources will now conform to the sink's refresh rate. At teardown, pScrPriv->rrDisableSharedPixmapFlipping() will be called, making its way to drmmode_FiniSharedPixmapFlipping(). There, the event handlers for prime_pixmap and prime_pixmap_back are aborted, freeing the left over parameter structure. Then, prime_pixmap and prime_pixmap back are unset as scanout pixmaps. Register and tear down slave damage per-scanout pixmap instead of per-crtc. v1: Initial commit v2: Renamed PresentTrackedFlippingPixmap to PresentSharedPixmap Renamed flipSeq to flip_seq Warn if flip failed Use SharedPixmapNotifyDamage to retry on next vblank after damage v3: Refactor to accomodate moving (rr)StartFlippingPixmapTracking and (rr)(Enable/Disable)SharedPixmapFlipping to rrScrPrivRec from ScreenRec Do damage tracking on both scanout pixmaps v4: Tweaks to commit message v5: Revise for internal storage of prime pixmap ptrs Move disabling for reverse PRIME from source commit to here Use drmmode_set_target_scanout_pixmap*() to set scanout pixmaps internally to EnableSharedPixmapFlipping(). Don't support flipping if ms->drmmode.pageflip == FALSE. Move flipping_active check to this commit v6: Rebase onto ToT v7: Unchanged Reviewed-by: Dave Airlie <airlied@redhat.com> Signed-off-by: Alex Goins <agoins@nvidia.com>
2016-06-16 20:06:52 -07:00
static Bool
msEnableSharedPixmapFlipping(RRCrtcPtr crtc, PixmapPtr front, PixmapPtr back)
{
ScreenPtr screen = crtc->pScreen;
ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
modesettingPtr ms = modesettingPTR(scrn);
xf86CrtcPtr xf86Crtc = crtc->devPrivate;
if (!xf86Crtc)
return FALSE;
/* Not supported if we can't flip */
if (!ms->drmmode.pageflip)
return FALSE;
/* Not currently supported with reverse PRIME */
if (ms->drmmode.reverse_prime_offload_mode)
return FALSE;
#ifdef XSERVER_PLATFORM_BUS
if (ms->pEnt->location.type == BUS_PLATFORM) {
const char *syspath =
xf86_platform_device_odev_attributes(ms->pEnt->location.id.plat)->
syspath;
/* Not supported for devices using USB transport due to misbehaved
* vblank events */
if (syspath && strstr(syspath, "usb"))
return FALSE;
/* EVDI uses USB transport but is platform device, not usb.
* Exclude it explicitly. */
if (syspath && strstr(syspath, "evdi"))
return FALSE;
}
#endif
modesetting: Implement PRIME syncing as a sink Implements (Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage, the sink functions for PRIME synchronization and double buffering. Allows modesetting driver to be used as a sink with PRIME synchronization. Changes dispatch_slave_dirty to flush damage from both scanout pixmaps. Changes drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target. Then, treat *target as it did prime_pixmap. This allows me to use it to explicitly set both prime_pixmap and prime_pixmap_back individually. drmmode_set_scanout_pixmap() without the extra parameter remains to cover the single-buffered case, but only works if we aren't already double buffered. driver.c: Add plumbing for rr(Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage. Change dispatch_dirty_crtc to dispatch_dirty_pixmap, which functions the same but flushes damage associated with a ppriv instead of the crtc, and chanage dispatch_slave_dirty to use it on both scanout pixmaps if applicable. drmmode_display.h: Add flip_seq field to msPixmapPrivRec to keep track of the event handler associated with a given pixmap, if any. Add wait_for_damage field to msPixmapPrivRec to keep track if we have requested a damage notification from the source. Add enable_flipping field to drmmode_crtc_private_rec to keep track if flipping is enabled or disabled. Add prime_pixmap_back to drmmode_crtc_private_rec to keep track of back buffer internally. Add declarations for drmmode_SetupPageFlipFence(), drmmode_EnableSharedPixmapFlipping(), drmmode_DisableSharedPixmapFlipping, drmmode_SharedPixmapFlip(), and drmmode_SharedPixmapPresentOnVBlank(). Move slave damage from crtc to ppriv. drmmode_display.c: Change drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target for explicitly setting different scanout pixmaps. Add definitions for functions drmmode_SharedPixmapFlip(), drmmode_SharedPixmapPresentOnVBlank(), drmmode_SharedPixmapPresent(), drmmode_SharedPixmapVBlankEventHandler(), drmmode_SharedPixmapVBlankEventAbort(), drmmode_EnableSharedPixmapFlipping(), and drmmode_DisableSharedPixmapFlipping, drmmode_InitSharedPixmapFlipping(), and drmmode_FiniSharedPixmapFlipping, along with struct vblank_event_args. The control flow is as follows: pScrPriv->rrEnableSharedPixmapFlipping() makes its way to drmmode_EnableSharedPixmapFlipping(), which sets enable_flipping to TRUE and sets both scanout pixmaps prime_pixmap and prime_pixmap_back. When setting a mode, if prime_pixmap is defined, modesetting driver will call drmmode_InitSharedPixmapFlipping(), which if flipping is enabled will call drmmode_SharedPixmapPresent() on scanout_pixmap_back. drmmode_SharedPixmapPresent() requests that for the source to present on the given buffer using master->PresentSharedPixmap(). If it succeeds, it will then attempt to flip to that buffer using drmmode_SharedPixmapFlip(). Flipping shouldn't fail, but if it does, it will raise a warning and try drmmode_SharedPixmapPresent() again on the next vblank using drmmode_SharedPixmapPresentOnVBlank(). master->PresentSharedPixmap() could fail, in most cases because there is no outstanding damage on the mscreenpix tracked by the shared pixmap. In this case, drmmode_SharedPixmapPresent() will attempt to use master->RequestSharedPixmapNotifyDamage() to request for the source driver to call slave->SharedPixmapNotifyDamage() in response to damage on mscreenpix. This will ultimately call into drmmode_SharedPixmapPresentOnVBlank() to retry drmmode_SharedPixmapPresent() on the next vblank after accumulating damage. drmmode_SharedPixmapFlip() sets up page flip event handler by packing struct vblank_event_args with the necessary parameters, and registering drmmode_SharedPixmapVBlankEventHandler() and drmmode_SharedPixmapVBlankEventAbort() with the modesetting DRM event handler queue. Then, it uses the drmModePageFlip() to flip on the next vblank and raise an event. drmmode_SharedPixmapPresentOnVBlank() operates similarly to drmmode_SharedPixmapFlip(), but uses drmWaitVBlank() instead of drmModePageFlip() to raise the event without flipping. On the next vblank, DRM will raise an event that will ultimately be handled by drmmode_SharedPixmapVBlankEventHandler(). If we flipped, it will update prime_pixmap and prime_pixmap_back to reflect that frontTarget is now being displayed, and use drmmode_SharedPixmapPresent(backTarget) to start the process again on the now-hidden shared pixmap. If we didn't flip, it will just use drmmode_SharedPixmapPresent(frontTarget) to start the process again on the still-hidden shared pixmap. Note that presentation generally happens asynchronously, so with these changes alone tearing is reduced, but we can't always guarantee that the present will finish before the flip. These changes are meant to be paired with changes to the sink DRM driver that makes flips wait on fences attached to dmabuf backed buffers. The source driver is responsible for attaching the fences and signaling them when presentation is finished. Note that because presentation is requested in response to a vblank, PRIME sources will now conform to the sink's refresh rate. At teardown, pScrPriv->rrDisableSharedPixmapFlipping() will be called, making its way to drmmode_FiniSharedPixmapFlipping(). There, the event handlers for prime_pixmap and prime_pixmap_back are aborted, freeing the left over parameter structure. Then, prime_pixmap and prime_pixmap back are unset as scanout pixmaps. Register and tear down slave damage per-scanout pixmap instead of per-crtc. v1: Initial commit v2: Renamed PresentTrackedFlippingPixmap to PresentSharedPixmap Renamed flipSeq to flip_seq Warn if flip failed Use SharedPixmapNotifyDamage to retry on next vblank after damage v3: Refactor to accomodate moving (rr)StartFlippingPixmapTracking and (rr)(Enable/Disable)SharedPixmapFlipping to rrScrPrivRec from ScreenRec Do damage tracking on both scanout pixmaps v4: Tweaks to commit message v5: Revise for internal storage of prime pixmap ptrs Move disabling for reverse PRIME from source commit to here Use drmmode_set_target_scanout_pixmap*() to set scanout pixmaps internally to EnableSharedPixmapFlipping(). Don't support flipping if ms->drmmode.pageflip == FALSE. Move flipping_active check to this commit v6: Rebase onto ToT v7: Unchanged Reviewed-by: Dave Airlie <airlied@redhat.com> Signed-off-by: Alex Goins <agoins@nvidia.com>
2016-06-16 20:06:52 -07:00
return drmmode_EnableSharedPixmapFlipping(xf86Crtc, &ms->drmmode,
front, back);
}
static void
msDisableSharedPixmapFlipping(RRCrtcPtr crtc)
{
ScreenPtr screen = crtc->pScreen;
ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
modesettingPtr ms = modesettingPTR(scrn);
xf86CrtcPtr xf86Crtc = crtc->devPrivate;
if (xf86Crtc)
drmmode_DisableSharedPixmapFlipping(xf86Crtc, &ms->drmmode);
}
static Bool
msStartFlippingPixmapTracking(RRCrtcPtr crtc, DrawablePtr src,
PixmapPtr secondary_dst1, PixmapPtr secondary_dst2,
int x, int y, int dst_x, int dst_y,
Rotation rotation)
{
ScreenPtr pScreen = src->pScreen;
modesettingPtr ms = modesettingPTR(xf86ScreenToScrn(pScreen));
msPixmapPrivPtr ppriv1 = msGetPixmapPriv(&ms->drmmode, secondary_dst1->primary_pixmap),
ppriv2 = msGetPixmapPriv(&ms->drmmode, secondary_dst2->primary_pixmap);
if (!PixmapStartDirtyTracking(src, secondary_dst1, x, y,
dst_x, dst_y, rotation)) {
return FALSE;
}
if (!PixmapStartDirtyTracking(src, secondary_dst2, x, y,
dst_x, dst_y, rotation)) {
PixmapStopDirtyTracking(src, secondary_dst1);
return FALSE;
}
ppriv1->secondary_src = src;
ppriv2->secondary_src = src;
ppriv1->dirty = ms_dirty_get_ent(pScreen, secondary_dst1);
ppriv2->dirty = ms_dirty_get_ent(pScreen, secondary_dst2);
ppriv1->defer_dirty_update = TRUE;
ppriv2->defer_dirty_update = TRUE;
return TRUE;
}
static Bool
msPresentSharedPixmap(PixmapPtr secondary_dst)
{
ScreenPtr pScreen = secondary_dst->primary_pixmap->drawable.pScreen;
modesettingPtr ms = modesettingPTR(xf86ScreenToScrn(pScreen));
msPixmapPrivPtr ppriv = msGetPixmapPriv(&ms->drmmode, secondary_dst->primary_pixmap);
RegionPtr region = DamageRegion(ppriv->dirty->damage);
if (RegionNotEmpty(region)) {
redisplay_dirty(ppriv->secondary_src->pScreen, ppriv->dirty, NULL);
DamageEmpty(ppriv->dirty->damage);
return TRUE;
}
return FALSE;
}
static Bool
msStopFlippingPixmapTracking(DrawablePtr src,
PixmapPtr secondary_dst1, PixmapPtr secondary_dst2)
{
ScreenPtr pScreen = src->pScreen;
modesettingPtr ms = modesettingPTR(xf86ScreenToScrn(pScreen));
msPixmapPrivPtr ppriv1 = msGetPixmapPriv(&ms->drmmode, secondary_dst1->primary_pixmap),
ppriv2 = msGetPixmapPriv(&ms->drmmode, secondary_dst2->primary_pixmap);
Bool ret = TRUE;
ret &= PixmapStopDirtyTracking(src, secondary_dst1);
ret &= PixmapStopDirtyTracking(src, secondary_dst2);
if (ret) {
ppriv1->secondary_src = NULL;
ppriv2->secondary_src = NULL;
ppriv1->dirty = NULL;
ppriv2->dirty = NULL;
ppriv1->defer_dirty_update = FALSE;
ppriv2->defer_dirty_update = FALSE;
}
return ret;
}
static Bool
CreateScreenResources(ScreenPtr pScreen)
{
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
2008-05-28 15:55:36 +01:00
modesettingPtr ms = modesettingPTR(pScrn);
PixmapPtr rootPixmap;
2008-05-28 15:55:36 +01:00
Bool ret;
void *pixels = NULL;
int err;
2008-05-28 15:55:36 +01:00
pScreen->CreateScreenResources = ms->createScreenResources;
ret = pScreen->CreateScreenResources(pScreen);
pScreen->CreateScreenResources = CreateScreenResources;
modesetting: keep going if a modeset fails on EnterVT There was a time when setting a mode on a CRTC would not depend on the associated connector's state. If a mode had been set successfully once, it would mean it would work later on. This changed with the introduction of new connectors type that now require a link training sequence (DP, HDMI 2.0), and that means that some events may have happened while the X server was not master that would then prevent the mode from successfully be restored to its previous state. This patch relaxes the requirement that all modes should be restored on EnterVT, or the entire X-Server would go down by allowing modesets to fail (with some warnings). If a modeset fails, the CRTC will be disabled, and a RandR event will be sent for the desktop environment to fix the situation as well as possible. Additional patches might be needed to make sure that the user would never be left with all screens black in some scenarios. v2 (Martin Peres): - whitespace fixes - remove the uevent handling (it is done in a previous patch) - improve the commit message - reduce the size of the patch by not changing lines needlessly - return FALSE if one modeset fails in ignore mode - add comments/todos to explain why we do things - disable the CRTCs that failed the modeset Signed-off-by: Kishore Kadiyala <kishore.kadiyala@intel.com> Signed-off-by: Martin Peres <martin.peres@linux.intel.com> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> Tested-by: Kishore Kadiyala <kishore.kadiyala@intel.com> Closes: #1010
2020-09-19 01:28:14 +05:30
if (!drmmode_set_desired_modes(pScrn, &ms->drmmode, pScrn->is_gpu, FALSE))
return FALSE;
if (!drmmode_glamor_handle_new_screen_pixmap(&ms->drmmode))
return FALSE;
drmmode_uevent_init(pScrn, &ms->drmmode);
if (!ms->drmmode.sw_cursor)
2011-11-03 13:20:18 +00:00
drmmode_map_cursor_bos(pScrn, &ms->drmmode);
if (!ms->drmmode.gbm) {
pixels = drmmode_map_front_bo(&ms->drmmode);
if (!pixels)
return FALSE;
}
rootPixmap = pScreen->GetScreenPixmap(pScreen);
if (ms->drmmode.shadow_enable)
pixels = ms->drmmode.shadow_fb;
if (ms->drmmode.shadow_enable2) {
ms->drmmode.shadow_fb2 = calloc(1, pScrn->displayWidth * pScrn->virtualY * ((pScrn->bitsPerPixel + 7) >> 3));
if (!ms->drmmode.shadow_fb2)
ms->drmmode.shadow_enable2 = FALSE;
}
if (!pScreen->ModifyPixmapHeader(rootPixmap, -1, -1, -1, -1, -1, pixels))
FatalError("Couldn't adjust screen pixmap\n");
if (ms->drmmode.shadow_enable) {
if (!ms->shadow.Add(pScreen, rootPixmap, msUpdatePacked, msShadowWindow,
0, 0))
return FALSE;
}
err = drmModeDirtyFB(ms->fd, ms->drmmode.fb_id, NULL, 0);
2011-09-29 12:38:26 +01:00
if ((err != -EINVAL && err != -ENOSYS) || ms->drmmode.tearfree_enable) {
ms->damage = DamageCreate(NULL, NULL, DamageReportNone, TRUE,
pScreen, rootPixmap);
if (ms->damage) {
DamageRegister(&rootPixmap->drawable, ms->damage);
ms->dirty_enabled = err != -EINVAL && err != -ENOSYS;
xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Damage tracking initialized\n");
}
else {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Failed to create screen damage record\n");
return FALSE;
}
2011-09-29 12:38:26 +01:00
}
modesetting: Implement PRIME syncing as a sink Implements (Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage, the sink functions for PRIME synchronization and double buffering. Allows modesetting driver to be used as a sink with PRIME synchronization. Changes dispatch_slave_dirty to flush damage from both scanout pixmaps. Changes drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target. Then, treat *target as it did prime_pixmap. This allows me to use it to explicitly set both prime_pixmap and prime_pixmap_back individually. drmmode_set_scanout_pixmap() without the extra parameter remains to cover the single-buffered case, but only works if we aren't already double buffered. driver.c: Add plumbing for rr(Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage. Change dispatch_dirty_crtc to dispatch_dirty_pixmap, which functions the same but flushes damage associated with a ppriv instead of the crtc, and chanage dispatch_slave_dirty to use it on both scanout pixmaps if applicable. drmmode_display.h: Add flip_seq field to msPixmapPrivRec to keep track of the event handler associated with a given pixmap, if any. Add wait_for_damage field to msPixmapPrivRec to keep track if we have requested a damage notification from the source. Add enable_flipping field to drmmode_crtc_private_rec to keep track if flipping is enabled or disabled. Add prime_pixmap_back to drmmode_crtc_private_rec to keep track of back buffer internally. Add declarations for drmmode_SetupPageFlipFence(), drmmode_EnableSharedPixmapFlipping(), drmmode_DisableSharedPixmapFlipping, drmmode_SharedPixmapFlip(), and drmmode_SharedPixmapPresentOnVBlank(). Move slave damage from crtc to ppriv. drmmode_display.c: Change drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target for explicitly setting different scanout pixmaps. Add definitions for functions drmmode_SharedPixmapFlip(), drmmode_SharedPixmapPresentOnVBlank(), drmmode_SharedPixmapPresent(), drmmode_SharedPixmapVBlankEventHandler(), drmmode_SharedPixmapVBlankEventAbort(), drmmode_EnableSharedPixmapFlipping(), and drmmode_DisableSharedPixmapFlipping, drmmode_InitSharedPixmapFlipping(), and drmmode_FiniSharedPixmapFlipping, along with struct vblank_event_args. The control flow is as follows: pScrPriv->rrEnableSharedPixmapFlipping() makes its way to drmmode_EnableSharedPixmapFlipping(), which sets enable_flipping to TRUE and sets both scanout pixmaps prime_pixmap and prime_pixmap_back. When setting a mode, if prime_pixmap is defined, modesetting driver will call drmmode_InitSharedPixmapFlipping(), which if flipping is enabled will call drmmode_SharedPixmapPresent() on scanout_pixmap_back. drmmode_SharedPixmapPresent() requests that for the source to present on the given buffer using master->PresentSharedPixmap(). If it succeeds, it will then attempt to flip to that buffer using drmmode_SharedPixmapFlip(). Flipping shouldn't fail, but if it does, it will raise a warning and try drmmode_SharedPixmapPresent() again on the next vblank using drmmode_SharedPixmapPresentOnVBlank(). master->PresentSharedPixmap() could fail, in most cases because there is no outstanding damage on the mscreenpix tracked by the shared pixmap. In this case, drmmode_SharedPixmapPresent() will attempt to use master->RequestSharedPixmapNotifyDamage() to request for the source driver to call slave->SharedPixmapNotifyDamage() in response to damage on mscreenpix. This will ultimately call into drmmode_SharedPixmapPresentOnVBlank() to retry drmmode_SharedPixmapPresent() on the next vblank after accumulating damage. drmmode_SharedPixmapFlip() sets up page flip event handler by packing struct vblank_event_args with the necessary parameters, and registering drmmode_SharedPixmapVBlankEventHandler() and drmmode_SharedPixmapVBlankEventAbort() with the modesetting DRM event handler queue. Then, it uses the drmModePageFlip() to flip on the next vblank and raise an event. drmmode_SharedPixmapPresentOnVBlank() operates similarly to drmmode_SharedPixmapFlip(), but uses drmWaitVBlank() instead of drmModePageFlip() to raise the event without flipping. On the next vblank, DRM will raise an event that will ultimately be handled by drmmode_SharedPixmapVBlankEventHandler(). If we flipped, it will update prime_pixmap and prime_pixmap_back to reflect that frontTarget is now being displayed, and use drmmode_SharedPixmapPresent(backTarget) to start the process again on the now-hidden shared pixmap. If we didn't flip, it will just use drmmode_SharedPixmapPresent(frontTarget) to start the process again on the still-hidden shared pixmap. Note that presentation generally happens asynchronously, so with these changes alone tearing is reduced, but we can't always guarantee that the present will finish before the flip. These changes are meant to be paired with changes to the sink DRM driver that makes flips wait on fences attached to dmabuf backed buffers. The source driver is responsible for attaching the fences and signaling them when presentation is finished. Note that because presentation is requested in response to a vblank, PRIME sources will now conform to the sink's refresh rate. At teardown, pScrPriv->rrDisableSharedPixmapFlipping() will be called, making its way to drmmode_FiniSharedPixmapFlipping(). There, the event handlers for prime_pixmap and prime_pixmap_back are aborted, freeing the left over parameter structure. Then, prime_pixmap and prime_pixmap back are unset as scanout pixmaps. Register and tear down slave damage per-scanout pixmap instead of per-crtc. v1: Initial commit v2: Renamed PresentTrackedFlippingPixmap to PresentSharedPixmap Renamed flipSeq to flip_seq Warn if flip failed Use SharedPixmapNotifyDamage to retry on next vblank after damage v3: Refactor to accomodate moving (rr)StartFlippingPixmapTracking and (rr)(Enable/Disable)SharedPixmapFlipping to rrScrPrivRec from ScreenRec Do damage tracking on both scanout pixmaps v4: Tweaks to commit message v5: Revise for internal storage of prime pixmap ptrs Move disabling for reverse PRIME from source commit to here Use drmmode_set_target_scanout_pixmap*() to set scanout pixmaps internally to EnableSharedPixmapFlipping(). Don't support flipping if ms->drmmode.pageflip == FALSE. Move flipping_active check to this commit v6: Rebase onto ToT v7: Unchanged Reviewed-by: Dave Airlie <airlied@redhat.com> Signed-off-by: Alex Goins <agoins@nvidia.com>
2016-06-16 20:06:52 -07:00
modesetting: Check whether RandR was initialized before calling rrGetScrPriv Calling rrGetScrPriv when RandR isn't initialized causes an assertion failure that aborts the server: Xorg: ../include/privates.h:121: dixGetPrivateAddr: Assertion `key->initialized' failed. Thread 1 "Xorg" received signal SIGABRT, Aborted. 0x00007ffff78a8f25 in raise () from /usr/lib/libc.so.6 (gdb) bt #0 0x00007ffff78a8f25 in raise () from /usr/lib/libc.so.6 #1 0x00007ffff7892897 in abort () from /usr/lib/libc.so.6 #2 0x00007ffff7892767 in __assert_fail_base.cold () from /usr/lib/libc.so.6 #3 0x00007ffff78a1526 in __assert_fail () from /usr/lib/libc.so.6 #4 0x00007ffff7fb57c1 in dixGetPrivateAddr (privates=0x555555ab1b60, key=0x555555855720 <rrPrivKeyRec>) at ../include/privates.h:121 #5 0x00007ffff7fb5822 in dixGetPrivate (privates=0x555555ab1b60, key=0x555555855720 <rrPrivKeyRec>) at ../include/privates.h:136 #6 0x00007ffff7fb586a in dixLookupPrivate (privates=0x555555ab1b60, key=0x555555855720 <rrPrivKeyRec>) at ../include/privates.h:166 #7 0x00007ffff7fb8445 in CreateScreenResources (pScreen=0x555555ab1790) at ../hw/xfree86/drivers/modesetting/driver.c:1335 #8 0x000055555576c5e4 in xf86CrtcCreateScreenResources (screen=0x555555ab1790) at ../hw/xfree86/modes/xf86Crtc.c:744 #9 0x00005555555d8bb6 in dix_main (argc=4, argv=0x7fffffffead8, envp=0x7fffffffeb00) at ../dix/main.c:214 #10 0x00005555557a4f0b in main (argc=4, argv=0x7fffffffead8, envp=0x7fffffffeb00) at ../dix/stubmain.c:34 This can happen, for example, if the server is configured with Xinerama and there is more than one X screen: Section "ServerLayout" Identifier "crash" Screen 0 "modesetting" Screen 1 "dummy" RightOf "modesetting" Option "Xinerama" EndSection Section "Device" Identifier "modesetting" Driver "modesetting" EndSection Section "Screen" Identifier "modesetting" Device "modesetting" EndSection Section "Device" Identifier "dummy" Driver "dummy" EndSection Section "Screen" Identifier "dummy" Device "dummy" EndSection The problem does not reproduce if there is only one X screen because of this code in xf86RandR12Init: #ifdef PANORAMIX /* XXX disable RandR when using Xinerama */ if (!noPanoramiXExtension) { if (xf86NumScreens == 1) noPanoramiXExtension = TRUE; else return TRUE; } #endif Fix the problem by checking dixPrivateKeyRegistered(rrPrivKey) before calling rrGetScrPriv. This is similar to what the xf86-video-amdgpu driver does: https://gitlab.freedesktop.org/xorg/driver/xf86-video-amdgpu/blob/fd66f5c0bea2b7c22a47bfd5eb1f22d32d166d9c/src/amdgpu_kms.c#L388 Signed-off-by: Aaron Plattner <aplattner@nvidia.com> Reviewed-by: Michel Dänzer <mdaenzer@redhat.com>
2019-12-26 13:40:17 -08:00
if (dixPrivateKeyRegistered(rrPrivKey)) {
rrScrPrivPtr pScrPriv = rrGetScrPriv(pScreen);
modesetting: Implement PRIME syncing as a sink Implements (Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage, the sink functions for PRIME synchronization and double buffering. Allows modesetting driver to be used as a sink with PRIME synchronization. Changes dispatch_slave_dirty to flush damage from both scanout pixmaps. Changes drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target. Then, treat *target as it did prime_pixmap. This allows me to use it to explicitly set both prime_pixmap and prime_pixmap_back individually. drmmode_set_scanout_pixmap() without the extra parameter remains to cover the single-buffered case, but only works if we aren't already double buffered. driver.c: Add plumbing for rr(Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage. Change dispatch_dirty_crtc to dispatch_dirty_pixmap, which functions the same but flushes damage associated with a ppriv instead of the crtc, and chanage dispatch_slave_dirty to use it on both scanout pixmaps if applicable. drmmode_display.h: Add flip_seq field to msPixmapPrivRec to keep track of the event handler associated with a given pixmap, if any. Add wait_for_damage field to msPixmapPrivRec to keep track if we have requested a damage notification from the source. Add enable_flipping field to drmmode_crtc_private_rec to keep track if flipping is enabled or disabled. Add prime_pixmap_back to drmmode_crtc_private_rec to keep track of back buffer internally. Add declarations for drmmode_SetupPageFlipFence(), drmmode_EnableSharedPixmapFlipping(), drmmode_DisableSharedPixmapFlipping, drmmode_SharedPixmapFlip(), and drmmode_SharedPixmapPresentOnVBlank(). Move slave damage from crtc to ppriv. drmmode_display.c: Change drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target for explicitly setting different scanout pixmaps. Add definitions for functions drmmode_SharedPixmapFlip(), drmmode_SharedPixmapPresentOnVBlank(), drmmode_SharedPixmapPresent(), drmmode_SharedPixmapVBlankEventHandler(), drmmode_SharedPixmapVBlankEventAbort(), drmmode_EnableSharedPixmapFlipping(), and drmmode_DisableSharedPixmapFlipping, drmmode_InitSharedPixmapFlipping(), and drmmode_FiniSharedPixmapFlipping, along with struct vblank_event_args. The control flow is as follows: pScrPriv->rrEnableSharedPixmapFlipping() makes its way to drmmode_EnableSharedPixmapFlipping(), which sets enable_flipping to TRUE and sets both scanout pixmaps prime_pixmap and prime_pixmap_back. When setting a mode, if prime_pixmap is defined, modesetting driver will call drmmode_InitSharedPixmapFlipping(), which if flipping is enabled will call drmmode_SharedPixmapPresent() on scanout_pixmap_back. drmmode_SharedPixmapPresent() requests that for the source to present on the given buffer using master->PresentSharedPixmap(). If it succeeds, it will then attempt to flip to that buffer using drmmode_SharedPixmapFlip(). Flipping shouldn't fail, but if it does, it will raise a warning and try drmmode_SharedPixmapPresent() again on the next vblank using drmmode_SharedPixmapPresentOnVBlank(). master->PresentSharedPixmap() could fail, in most cases because there is no outstanding damage on the mscreenpix tracked by the shared pixmap. In this case, drmmode_SharedPixmapPresent() will attempt to use master->RequestSharedPixmapNotifyDamage() to request for the source driver to call slave->SharedPixmapNotifyDamage() in response to damage on mscreenpix. This will ultimately call into drmmode_SharedPixmapPresentOnVBlank() to retry drmmode_SharedPixmapPresent() on the next vblank after accumulating damage. drmmode_SharedPixmapFlip() sets up page flip event handler by packing struct vblank_event_args with the necessary parameters, and registering drmmode_SharedPixmapVBlankEventHandler() and drmmode_SharedPixmapVBlankEventAbort() with the modesetting DRM event handler queue. Then, it uses the drmModePageFlip() to flip on the next vblank and raise an event. drmmode_SharedPixmapPresentOnVBlank() operates similarly to drmmode_SharedPixmapFlip(), but uses drmWaitVBlank() instead of drmModePageFlip() to raise the event without flipping. On the next vblank, DRM will raise an event that will ultimately be handled by drmmode_SharedPixmapVBlankEventHandler(). If we flipped, it will update prime_pixmap and prime_pixmap_back to reflect that frontTarget is now being displayed, and use drmmode_SharedPixmapPresent(backTarget) to start the process again on the now-hidden shared pixmap. If we didn't flip, it will just use drmmode_SharedPixmapPresent(frontTarget) to start the process again on the still-hidden shared pixmap. Note that presentation generally happens asynchronously, so with these changes alone tearing is reduced, but we can't always guarantee that the present will finish before the flip. These changes are meant to be paired with changes to the sink DRM driver that makes flips wait on fences attached to dmabuf backed buffers. The source driver is responsible for attaching the fences and signaling them when presentation is finished. Note that because presentation is requested in response to a vblank, PRIME sources will now conform to the sink's refresh rate. At teardown, pScrPriv->rrDisableSharedPixmapFlipping() will be called, making its way to drmmode_FiniSharedPixmapFlipping(). There, the event handlers for prime_pixmap and prime_pixmap_back are aborted, freeing the left over parameter structure. Then, prime_pixmap and prime_pixmap back are unset as scanout pixmaps. Register and tear down slave damage per-scanout pixmap instead of per-crtc. v1: Initial commit v2: Renamed PresentTrackedFlippingPixmap to PresentSharedPixmap Renamed flipSeq to flip_seq Warn if flip failed Use SharedPixmapNotifyDamage to retry on next vblank after damage v3: Refactor to accomodate moving (rr)StartFlippingPixmapTracking and (rr)(Enable/Disable)SharedPixmapFlipping to rrScrPrivRec from ScreenRec Do damage tracking on both scanout pixmaps v4: Tweaks to commit message v5: Revise for internal storage of prime pixmap ptrs Move disabling for reverse PRIME from source commit to here Use drmmode_set_target_scanout_pixmap*() to set scanout pixmaps internally to EnableSharedPixmapFlipping(). Don't support flipping if ms->drmmode.pageflip == FALSE. Move flipping_active check to this commit v6: Rebase onto ToT v7: Unchanged Reviewed-by: Dave Airlie <airlied@redhat.com> Signed-off-by: Alex Goins <agoins@nvidia.com>
2016-06-16 20:06:52 -07:00
modesetting: Check whether RandR was initialized before calling rrGetScrPriv Calling rrGetScrPriv when RandR isn't initialized causes an assertion failure that aborts the server: Xorg: ../include/privates.h:121: dixGetPrivateAddr: Assertion `key->initialized' failed. Thread 1 "Xorg" received signal SIGABRT, Aborted. 0x00007ffff78a8f25 in raise () from /usr/lib/libc.so.6 (gdb) bt #0 0x00007ffff78a8f25 in raise () from /usr/lib/libc.so.6 #1 0x00007ffff7892897 in abort () from /usr/lib/libc.so.6 #2 0x00007ffff7892767 in __assert_fail_base.cold () from /usr/lib/libc.so.6 #3 0x00007ffff78a1526 in __assert_fail () from /usr/lib/libc.so.6 #4 0x00007ffff7fb57c1 in dixGetPrivateAddr (privates=0x555555ab1b60, key=0x555555855720 <rrPrivKeyRec>) at ../include/privates.h:121 #5 0x00007ffff7fb5822 in dixGetPrivate (privates=0x555555ab1b60, key=0x555555855720 <rrPrivKeyRec>) at ../include/privates.h:136 #6 0x00007ffff7fb586a in dixLookupPrivate (privates=0x555555ab1b60, key=0x555555855720 <rrPrivKeyRec>) at ../include/privates.h:166 #7 0x00007ffff7fb8445 in CreateScreenResources (pScreen=0x555555ab1790) at ../hw/xfree86/drivers/modesetting/driver.c:1335 #8 0x000055555576c5e4 in xf86CrtcCreateScreenResources (screen=0x555555ab1790) at ../hw/xfree86/modes/xf86Crtc.c:744 #9 0x00005555555d8bb6 in dix_main (argc=4, argv=0x7fffffffead8, envp=0x7fffffffeb00) at ../dix/main.c:214 #10 0x00005555557a4f0b in main (argc=4, argv=0x7fffffffead8, envp=0x7fffffffeb00) at ../dix/stubmain.c:34 This can happen, for example, if the server is configured with Xinerama and there is more than one X screen: Section "ServerLayout" Identifier "crash" Screen 0 "modesetting" Screen 1 "dummy" RightOf "modesetting" Option "Xinerama" EndSection Section "Device" Identifier "modesetting" Driver "modesetting" EndSection Section "Screen" Identifier "modesetting" Device "modesetting" EndSection Section "Device" Identifier "dummy" Driver "dummy" EndSection Section "Screen" Identifier "dummy" Device "dummy" EndSection The problem does not reproduce if there is only one X screen because of this code in xf86RandR12Init: #ifdef PANORAMIX /* XXX disable RandR when using Xinerama */ if (!noPanoramiXExtension) { if (xf86NumScreens == 1) noPanoramiXExtension = TRUE; else return TRUE; } #endif Fix the problem by checking dixPrivateKeyRegistered(rrPrivKey) before calling rrGetScrPriv. This is similar to what the xf86-video-amdgpu driver does: https://gitlab.freedesktop.org/xorg/driver/xf86-video-amdgpu/blob/fd66f5c0bea2b7c22a47bfd5eb1f22d32d166d9c/src/amdgpu_kms.c#L388 Signed-off-by: Aaron Plattner <aplattner@nvidia.com> Reviewed-by: Michel Dänzer <mdaenzer@redhat.com>
2019-12-26 13:40:17 -08:00
pScrPriv->rrEnableSharedPixmapFlipping = msEnableSharedPixmapFlipping;
pScrPriv->rrDisableSharedPixmapFlipping = msDisableSharedPixmapFlipping;
pScrPriv->rrStartFlippingPixmapTracking = msStartFlippingPixmapTracking;
}
if (ms->vrr_support &&
!dixRegisterPrivateKey(&ms->drmmode.vrrPrivateKeyRec,
PRIVATE_WINDOW,
sizeof(struct ms_vrr_priv)))
return FALSE;
2008-05-28 15:55:36 +01:00
return ret;
}
static Bool
msSharePixmapBacking(PixmapPtr ppix, ScreenPtr secondary, void **handle)
{
#ifdef GLAMOR_HAS_GBM
modesettingPtr ms =
modesettingPTR(xf86ScreenToScrn(ppix->drawable.pScreen));
int ret;
CARD16 stride;
CARD32 size;
ret = ms->glamor.shareable_fd_from_pixmap(ppix->drawable.pScreen, ppix,
&stride, &size);
if (ret == -1)
return FALSE;
*handle = (void *)(long)(ret);
return TRUE;
#endif
return FALSE;
}
static Bool
msSetSharedPixmapBacking(PixmapPtr ppix, void *fd_handle)
{
#ifdef GLAMOR_HAS_GBM
ScreenPtr screen = ppix->drawable.pScreen;
ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
modesettingPtr ms = modesettingPTR(scrn);
Bool ret;
int ihandle = (int) (long) fd_handle;
if (ihandle == -1)
if (!ms->drmmode.reverse_prime_offload_mode)
return drmmode_SetSlaveBO(ppix, &ms->drmmode, ihandle, 0, 0);
if (ms->drmmode.reverse_prime_offload_mode) {
ret = ms->glamor.back_pixmap_from_fd(ppix, ihandle,
ppix->drawable.width,
ppix->drawable.height,
ppix->devKind,
ppix->drawable.depth,
ppix->drawable.bitsPerPixel);
} else {
int size = ppix->devKind * ppix->drawable.height;
ret = drmmode_SetSlaveBO(ppix, &ms->drmmode, ihandle, ppix->devKind, size);
}
if (ret == FALSE)
return ret;
return TRUE;
#else
return FALSE;
#endif
}
static Bool
msRequestSharedPixmapNotifyDamage(PixmapPtr ppix)
{
ScreenPtr screen = ppix->drawable.pScreen;
ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
modesettingPtr ms = modesettingPTR(scrn);
msPixmapPrivPtr ppriv = msGetPixmapPriv(&ms->drmmode, ppix->primary_pixmap);
ppriv->notify_on_damage = TRUE;
return TRUE;
}
modesetting: Implement PRIME syncing as a sink Implements (Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage, the sink functions for PRIME synchronization and double buffering. Allows modesetting driver to be used as a sink with PRIME synchronization. Changes dispatch_slave_dirty to flush damage from both scanout pixmaps. Changes drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target. Then, treat *target as it did prime_pixmap. This allows me to use it to explicitly set both prime_pixmap and prime_pixmap_back individually. drmmode_set_scanout_pixmap() without the extra parameter remains to cover the single-buffered case, but only works if we aren't already double buffered. driver.c: Add plumbing for rr(Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage. Change dispatch_dirty_crtc to dispatch_dirty_pixmap, which functions the same but flushes damage associated with a ppriv instead of the crtc, and chanage dispatch_slave_dirty to use it on both scanout pixmaps if applicable. drmmode_display.h: Add flip_seq field to msPixmapPrivRec to keep track of the event handler associated with a given pixmap, if any. Add wait_for_damage field to msPixmapPrivRec to keep track if we have requested a damage notification from the source. Add enable_flipping field to drmmode_crtc_private_rec to keep track if flipping is enabled or disabled. Add prime_pixmap_back to drmmode_crtc_private_rec to keep track of back buffer internally. Add declarations for drmmode_SetupPageFlipFence(), drmmode_EnableSharedPixmapFlipping(), drmmode_DisableSharedPixmapFlipping, drmmode_SharedPixmapFlip(), and drmmode_SharedPixmapPresentOnVBlank(). Move slave damage from crtc to ppriv. drmmode_display.c: Change drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target for explicitly setting different scanout pixmaps. Add definitions for functions drmmode_SharedPixmapFlip(), drmmode_SharedPixmapPresentOnVBlank(), drmmode_SharedPixmapPresent(), drmmode_SharedPixmapVBlankEventHandler(), drmmode_SharedPixmapVBlankEventAbort(), drmmode_EnableSharedPixmapFlipping(), and drmmode_DisableSharedPixmapFlipping, drmmode_InitSharedPixmapFlipping(), and drmmode_FiniSharedPixmapFlipping, along with struct vblank_event_args. The control flow is as follows: pScrPriv->rrEnableSharedPixmapFlipping() makes its way to drmmode_EnableSharedPixmapFlipping(), which sets enable_flipping to TRUE and sets both scanout pixmaps prime_pixmap and prime_pixmap_back. When setting a mode, if prime_pixmap is defined, modesetting driver will call drmmode_InitSharedPixmapFlipping(), which if flipping is enabled will call drmmode_SharedPixmapPresent() on scanout_pixmap_back. drmmode_SharedPixmapPresent() requests that for the source to present on the given buffer using master->PresentSharedPixmap(). If it succeeds, it will then attempt to flip to that buffer using drmmode_SharedPixmapFlip(). Flipping shouldn't fail, but if it does, it will raise a warning and try drmmode_SharedPixmapPresent() again on the next vblank using drmmode_SharedPixmapPresentOnVBlank(). master->PresentSharedPixmap() could fail, in most cases because there is no outstanding damage on the mscreenpix tracked by the shared pixmap. In this case, drmmode_SharedPixmapPresent() will attempt to use master->RequestSharedPixmapNotifyDamage() to request for the source driver to call slave->SharedPixmapNotifyDamage() in response to damage on mscreenpix. This will ultimately call into drmmode_SharedPixmapPresentOnVBlank() to retry drmmode_SharedPixmapPresent() on the next vblank after accumulating damage. drmmode_SharedPixmapFlip() sets up page flip event handler by packing struct vblank_event_args with the necessary parameters, and registering drmmode_SharedPixmapVBlankEventHandler() and drmmode_SharedPixmapVBlankEventAbort() with the modesetting DRM event handler queue. Then, it uses the drmModePageFlip() to flip on the next vblank and raise an event. drmmode_SharedPixmapPresentOnVBlank() operates similarly to drmmode_SharedPixmapFlip(), but uses drmWaitVBlank() instead of drmModePageFlip() to raise the event without flipping. On the next vblank, DRM will raise an event that will ultimately be handled by drmmode_SharedPixmapVBlankEventHandler(). If we flipped, it will update prime_pixmap and prime_pixmap_back to reflect that frontTarget is now being displayed, and use drmmode_SharedPixmapPresent(backTarget) to start the process again on the now-hidden shared pixmap. If we didn't flip, it will just use drmmode_SharedPixmapPresent(frontTarget) to start the process again on the still-hidden shared pixmap. Note that presentation generally happens asynchronously, so with these changes alone tearing is reduced, but we can't always guarantee that the present will finish before the flip. These changes are meant to be paired with changes to the sink DRM driver that makes flips wait on fences attached to dmabuf backed buffers. The source driver is responsible for attaching the fences and signaling them when presentation is finished. Note that because presentation is requested in response to a vblank, PRIME sources will now conform to the sink's refresh rate. At teardown, pScrPriv->rrDisableSharedPixmapFlipping() will be called, making its way to drmmode_FiniSharedPixmapFlipping(). There, the event handlers for prime_pixmap and prime_pixmap_back are aborted, freeing the left over parameter structure. Then, prime_pixmap and prime_pixmap back are unset as scanout pixmaps. Register and tear down slave damage per-scanout pixmap instead of per-crtc. v1: Initial commit v2: Renamed PresentTrackedFlippingPixmap to PresentSharedPixmap Renamed flipSeq to flip_seq Warn if flip failed Use SharedPixmapNotifyDamage to retry on next vblank after damage v3: Refactor to accomodate moving (rr)StartFlippingPixmapTracking and (rr)(Enable/Disable)SharedPixmapFlipping to rrScrPrivRec from ScreenRec Do damage tracking on both scanout pixmaps v4: Tweaks to commit message v5: Revise for internal storage of prime pixmap ptrs Move disabling for reverse PRIME from source commit to here Use drmmode_set_target_scanout_pixmap*() to set scanout pixmaps internally to EnableSharedPixmapFlipping(). Don't support flipping if ms->drmmode.pageflip == FALSE. Move flipping_active check to this commit v6: Rebase onto ToT v7: Unchanged Reviewed-by: Dave Airlie <airlied@redhat.com> Signed-off-by: Alex Goins <agoins@nvidia.com>
2016-06-16 20:06:52 -07:00
static Bool
msSharedPixmapNotifyDamage(PixmapPtr ppix)
{
Bool ret = FALSE;
int c;
ScreenPtr screen = ppix->drawable.pScreen;
ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
modesettingPtr ms = modesettingPTR(scrn);
xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
msPixmapPrivPtr ppriv = msGetPixmapPriv(&ms->drmmode, ppix);
if (!ppriv->wait_for_damage)
return ret;
ppriv->wait_for_damage = FALSE;
for (c = 0; c < xf86_config->num_crtc; c++) {
xf86CrtcPtr crtc = xf86_config->crtc[c];
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
if (!drmmode_crtc)
continue;
if (!(drmmode_crtc->prime_pixmap && drmmode_crtc->prime_pixmap_back))
continue;
// Received damage on primary screen pixmap, schedule present on vblank
modesetting: Implement PRIME syncing as a sink Implements (Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage, the sink functions for PRIME synchronization and double buffering. Allows modesetting driver to be used as a sink with PRIME synchronization. Changes dispatch_slave_dirty to flush damage from both scanout pixmaps. Changes drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target. Then, treat *target as it did prime_pixmap. This allows me to use it to explicitly set both prime_pixmap and prime_pixmap_back individually. drmmode_set_scanout_pixmap() without the extra parameter remains to cover the single-buffered case, but only works if we aren't already double buffered. driver.c: Add plumbing for rr(Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage. Change dispatch_dirty_crtc to dispatch_dirty_pixmap, which functions the same but flushes damage associated with a ppriv instead of the crtc, and chanage dispatch_slave_dirty to use it on both scanout pixmaps if applicable. drmmode_display.h: Add flip_seq field to msPixmapPrivRec to keep track of the event handler associated with a given pixmap, if any. Add wait_for_damage field to msPixmapPrivRec to keep track if we have requested a damage notification from the source. Add enable_flipping field to drmmode_crtc_private_rec to keep track if flipping is enabled or disabled. Add prime_pixmap_back to drmmode_crtc_private_rec to keep track of back buffer internally. Add declarations for drmmode_SetupPageFlipFence(), drmmode_EnableSharedPixmapFlipping(), drmmode_DisableSharedPixmapFlipping, drmmode_SharedPixmapFlip(), and drmmode_SharedPixmapPresentOnVBlank(). Move slave damage from crtc to ppriv. drmmode_display.c: Change drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target for explicitly setting different scanout pixmaps. Add definitions for functions drmmode_SharedPixmapFlip(), drmmode_SharedPixmapPresentOnVBlank(), drmmode_SharedPixmapPresent(), drmmode_SharedPixmapVBlankEventHandler(), drmmode_SharedPixmapVBlankEventAbort(), drmmode_EnableSharedPixmapFlipping(), and drmmode_DisableSharedPixmapFlipping, drmmode_InitSharedPixmapFlipping(), and drmmode_FiniSharedPixmapFlipping, along with struct vblank_event_args. The control flow is as follows: pScrPriv->rrEnableSharedPixmapFlipping() makes its way to drmmode_EnableSharedPixmapFlipping(), which sets enable_flipping to TRUE and sets both scanout pixmaps prime_pixmap and prime_pixmap_back. When setting a mode, if prime_pixmap is defined, modesetting driver will call drmmode_InitSharedPixmapFlipping(), which if flipping is enabled will call drmmode_SharedPixmapPresent() on scanout_pixmap_back. drmmode_SharedPixmapPresent() requests that for the source to present on the given buffer using master->PresentSharedPixmap(). If it succeeds, it will then attempt to flip to that buffer using drmmode_SharedPixmapFlip(). Flipping shouldn't fail, but if it does, it will raise a warning and try drmmode_SharedPixmapPresent() again on the next vblank using drmmode_SharedPixmapPresentOnVBlank(). master->PresentSharedPixmap() could fail, in most cases because there is no outstanding damage on the mscreenpix tracked by the shared pixmap. In this case, drmmode_SharedPixmapPresent() will attempt to use master->RequestSharedPixmapNotifyDamage() to request for the source driver to call slave->SharedPixmapNotifyDamage() in response to damage on mscreenpix. This will ultimately call into drmmode_SharedPixmapPresentOnVBlank() to retry drmmode_SharedPixmapPresent() on the next vblank after accumulating damage. drmmode_SharedPixmapFlip() sets up page flip event handler by packing struct vblank_event_args with the necessary parameters, and registering drmmode_SharedPixmapVBlankEventHandler() and drmmode_SharedPixmapVBlankEventAbort() with the modesetting DRM event handler queue. Then, it uses the drmModePageFlip() to flip on the next vblank and raise an event. drmmode_SharedPixmapPresentOnVBlank() operates similarly to drmmode_SharedPixmapFlip(), but uses drmWaitVBlank() instead of drmModePageFlip() to raise the event without flipping. On the next vblank, DRM will raise an event that will ultimately be handled by drmmode_SharedPixmapVBlankEventHandler(). If we flipped, it will update prime_pixmap and prime_pixmap_back to reflect that frontTarget is now being displayed, and use drmmode_SharedPixmapPresent(backTarget) to start the process again on the now-hidden shared pixmap. If we didn't flip, it will just use drmmode_SharedPixmapPresent(frontTarget) to start the process again on the still-hidden shared pixmap. Note that presentation generally happens asynchronously, so with these changes alone tearing is reduced, but we can't always guarantee that the present will finish before the flip. These changes are meant to be paired with changes to the sink DRM driver that makes flips wait on fences attached to dmabuf backed buffers. The source driver is responsible for attaching the fences and signaling them when presentation is finished. Note that because presentation is requested in response to a vblank, PRIME sources will now conform to the sink's refresh rate. At teardown, pScrPriv->rrDisableSharedPixmapFlipping() will be called, making its way to drmmode_FiniSharedPixmapFlipping(). There, the event handlers for prime_pixmap and prime_pixmap_back are aborted, freeing the left over parameter structure. Then, prime_pixmap and prime_pixmap back are unset as scanout pixmaps. Register and tear down slave damage per-scanout pixmap instead of per-crtc. v1: Initial commit v2: Renamed PresentTrackedFlippingPixmap to PresentSharedPixmap Renamed flipSeq to flip_seq Warn if flip failed Use SharedPixmapNotifyDamage to retry on next vblank after damage v3: Refactor to accomodate moving (rr)StartFlippingPixmapTracking and (rr)(Enable/Disable)SharedPixmapFlipping to rrScrPrivRec from ScreenRec Do damage tracking on both scanout pixmaps v4: Tweaks to commit message v5: Revise for internal storage of prime pixmap ptrs Move disabling for reverse PRIME from source commit to here Use drmmode_set_target_scanout_pixmap*() to set scanout pixmaps internally to EnableSharedPixmapFlipping(). Don't support flipping if ms->drmmode.pageflip == FALSE. Move flipping_active check to this commit v6: Rebase onto ToT v7: Unchanged Reviewed-by: Dave Airlie <airlied@redhat.com> Signed-off-by: Alex Goins <agoins@nvidia.com>
2016-06-16 20:06:52 -07:00
ret |= drmmode_SharedPixmapPresentOnVBlank(ppix, crtc, &ms->drmmode);
}
return ret;
}
static Bool
SetMaster(ScrnInfoPtr pScrn)
{
modesettingPtr ms = modesettingPTR(pScrn);
int ret;
#ifdef XF86_PDEV_SERVER_FD
if (ms->pEnt->location.type == BUS_PLATFORM &&
(ms->pEnt->location.id.plat->flags & XF86_PDEV_SERVER_FD))
return TRUE;
#endif
if (ms->fd_passed)
return TRUE;
ret = drmSetMaster(ms->fd);
if (ret)
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "drmSetMaster failed: %s\n",
strerror(errno));
return ret == 0;
}
/* When the root window is created, initialize the screen contents from
* console if -background none was specified on the command line
*/
static Bool
CreateWindow_oneshot(WindowPtr pWin)
{
ScreenPtr pScreen = pWin->drawable.pScreen;
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
modesettingPtr ms = modesettingPTR(pScrn);
Bool ret;
pScreen->CreateWindow = ms->CreateWindow;
ret = pScreen->CreateWindow(pWin);
if (ret)
drmmode_copy_fb(pScrn, &ms->drmmode);
return ret;
}
static Bool
ScreenInit(ScreenPtr pScreen, int argc, char **argv)
{
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
2008-05-28 15:55:36 +01:00
modesettingPtr ms = modesettingPTR(pScrn);
VisualPtr visual;
pScrn->pScreen = pScreen;
if (!SetMaster(pScrn))
2011-09-29 14:05:43 +01:00
return FALSE;
#ifdef GLAMOR_HAS_GBM
if (ms->drmmode.glamor)
ms->drmmode.gbm = ms->glamor.egl_get_gbm_device(pScreen);
#endif
/* HW dependent - FIXME */
pScrn->displayWidth = pScrn->virtualX;
if (!drmmode_create_initial_bos(pScrn, &ms->drmmode))
return FALSE;
2008-05-28 15:55:36 +01:00
if (ms->drmmode.shadow_enable) {
ms->drmmode.shadow_fb =
calloc(1,
pScrn->displayWidth * pScrn->virtualY *
((pScrn->bitsPerPixel + 7) >> 3));
if (!ms->drmmode.shadow_fb)
ms->drmmode.shadow_enable = FALSE;
}
2008-05-28 15:55:36 +01:00
miClearVisualTypes();
if (!miSetVisualTypes(pScrn->depth,
miGetDefaultVisualMask(pScrn->depth),
pScrn->rgbBits, pScrn->defaultVisual))
return FALSE;
2008-05-28 15:55:36 +01:00
if (!miSetPixmapDepths())
return FALSE;
2008-05-28 15:55:36 +01:00
if (!dixRegisterScreenSpecificPrivateKey
(pScreen, &ms->drmmode.pixmapPrivateKeyRec, PRIVATE_PIXMAP,
sizeof(msPixmapPrivRec))) {
return FALSE;
}
2008-05-28 15:55:36 +01:00
pScrn->memPhysBase = 0;
pScrn->fbOffset = 0;
if (!fbScreenInit(pScreen, NULL,
pScrn->virtualX, pScrn->virtualY,
pScrn->xDpi, pScrn->yDpi,
pScrn->displayWidth, pScrn->bitsPerPixel))
return FALSE;
2008-05-28 15:55:36 +01:00
if (pScrn->bitsPerPixel > 8) {
/* Fixup RGB ordering */
visual = pScreen->visuals + pScreen->numVisuals;
while (--visual >= pScreen->visuals) {
if ((visual->class | DynamicClass) == DirectColor) {
visual->offsetRed = pScrn->offset.red;
visual->offsetGreen = pScrn->offset.green;
visual->offsetBlue = pScrn->offset.blue;
visual->redMask = pScrn->mask.red;
visual->greenMask = pScrn->mask.green;
visual->blueMask = pScrn->mask.blue;
}
}
2008-05-28 15:55:36 +01:00
}
fbPictureInit(pScreen, NULL, 0);
if (drmmode_init(pScrn, &ms->drmmode) == FALSE) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Failed to initialize glamor at ScreenInit() time.\n");
return FALSE;
}
if (ms->drmmode.shadow_enable && !ms->shadow.Setup(pScreen)) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "shadow fb init failed\n");
return FALSE;
}
ms->createScreenResources = pScreen->CreateScreenResources;
pScreen->CreateScreenResources = CreateScreenResources;
2008-05-28 15:55:36 +01:00
xf86SetBlackWhitePixels(pScreen);
2008-05-28 15:55:36 +01:00
xf86SetBackingStore(pScreen);
xf86SetSilkenMouse(pScreen);
miDCInitialize(pScreen, xf86GetPointerScreenFuncs());
/* If pageflip is enabled hook the screen's cursor-sprite (swcursor) funcs.
* So that we can disable page-flipping on fallback to a swcursor. */
if (ms->drmmode.pageflip) {
miPointerScreenPtr PointPriv =
dixLookupPrivate(&pScreen->devPrivates, miPointerScreenKey);
if (!dixRegisterScreenPrivateKey(&ms->drmmode.spritePrivateKeyRec,
pScreen, PRIVATE_DEVICE,
sizeof(msSpritePrivRec)))
return FALSE;
ms->SpriteFuncs = PointPriv->spriteFuncs;
PointPriv->spriteFuncs = &drmmode_sprite_funcs;
}
/* Need to extend HWcursor support to handle mask interleave */
if (!ms->drmmode.sw_cursor)
modesetting: Use a more optimal hw cursor size Try to minimize the used hw cursor size in order to minimize power consumption. There is no kernel query for the minimum so we'll just probe around with setcursor2 (using an invisible cursor image so there will be no visual artifacts). To avoid having to deal with absolutely every size stick to power-of-two numbers. And with a bit of extra effort we can determine whether non-square dimesions will also work, which they do to some degree on current Intel GPUs. On my Alderlake laptop I'm seeing a massive (up to .5W) difference in power consumption between 64x64 vs. 256x256 cursors. While some of that is undoubtedly something that needs to be fixed in i915's display data buffer allocation code, it still makes sense to use as small as possible cursor to minimize the wastege. In case the crtc is rotated just punt to the max cursor size for now since midlayer has already done the coordinate transformations based on that. To make smaller cursors work with rotation we'd either need to make the midlayer(s) aware of the final cursor size, or just handle the whole roation business in modesetting. I suspect the latter option would be easier. v2: Only allow square cursors in most cases for now as eg. on modern Intel hardware non-square only works with wide+short but not with narrow+tall cursors. Non-square size may still be used when maximum limits aren't square and the squared+POT'd dimensions would exceed one of the max limits. Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
2023-02-04 18:25:53 +02:00
xf86_cursors_init(pScreen, ms->max_cursor_width, ms->max_cursor_height,
HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_64 |
HARDWARE_CURSOR_UPDATE_UNHIDDEN |
HARDWARE_CURSOR_ARGB);
2008-05-28 19:59:38 +01:00
2008-05-28 15:55:36 +01:00
/* Must force it before EnterVT, so we are in control of VT and
* later memory should be bound when allocating, e.g rotate_mem */
pScrn->vtSema = TRUE;
if (serverGeneration == 1 && bgNoneRoot && ms->drmmode.glamor) {
ms->CreateWindow = pScreen->CreateWindow;
pScreen->CreateWindow = CreateWindow_oneshot;
}
2008-05-28 15:55:36 +01:00
pScreen->SaveScreen = xf86SaveScreen;
ms->CloseScreen = pScreen->CloseScreen;
pScreen->CloseScreen = CloseScreen;
2011-09-29 12:38:26 +01:00
ms->BlockHandler = pScreen->BlockHandler;
pScreen->BlockHandler = msBlockHandler_oneshot;
2011-09-29 12:38:26 +01:00
pScreen->SharePixmapBacking = msSharePixmapBacking;
pScreen->SetSharedPixmapBacking = msSetSharedPixmapBacking;
pScreen->StartPixmapTracking = PixmapStartDirtyTracking;
pScreen->StopPixmapTracking = PixmapStopDirtyTracking;
modesetting: Implement PRIME syncing as a sink Implements (Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage, the sink functions for PRIME synchronization and double buffering. Allows modesetting driver to be used as a sink with PRIME synchronization. Changes dispatch_slave_dirty to flush damage from both scanout pixmaps. Changes drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target. Then, treat *target as it did prime_pixmap. This allows me to use it to explicitly set both prime_pixmap and prime_pixmap_back individually. drmmode_set_scanout_pixmap() without the extra parameter remains to cover the single-buffered case, but only works if we aren't already double buffered. driver.c: Add plumbing for rr(Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage. Change dispatch_dirty_crtc to dispatch_dirty_pixmap, which functions the same but flushes damage associated with a ppriv instead of the crtc, and chanage dispatch_slave_dirty to use it on both scanout pixmaps if applicable. drmmode_display.h: Add flip_seq field to msPixmapPrivRec to keep track of the event handler associated with a given pixmap, if any. Add wait_for_damage field to msPixmapPrivRec to keep track if we have requested a damage notification from the source. Add enable_flipping field to drmmode_crtc_private_rec to keep track if flipping is enabled or disabled. Add prime_pixmap_back to drmmode_crtc_private_rec to keep track of back buffer internally. Add declarations for drmmode_SetupPageFlipFence(), drmmode_EnableSharedPixmapFlipping(), drmmode_DisableSharedPixmapFlipping, drmmode_SharedPixmapFlip(), and drmmode_SharedPixmapPresentOnVBlank(). Move slave damage from crtc to ppriv. drmmode_display.c: Change drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target for explicitly setting different scanout pixmaps. Add definitions for functions drmmode_SharedPixmapFlip(), drmmode_SharedPixmapPresentOnVBlank(), drmmode_SharedPixmapPresent(), drmmode_SharedPixmapVBlankEventHandler(), drmmode_SharedPixmapVBlankEventAbort(), drmmode_EnableSharedPixmapFlipping(), and drmmode_DisableSharedPixmapFlipping, drmmode_InitSharedPixmapFlipping(), and drmmode_FiniSharedPixmapFlipping, along with struct vblank_event_args. The control flow is as follows: pScrPriv->rrEnableSharedPixmapFlipping() makes its way to drmmode_EnableSharedPixmapFlipping(), which sets enable_flipping to TRUE and sets both scanout pixmaps prime_pixmap and prime_pixmap_back. When setting a mode, if prime_pixmap is defined, modesetting driver will call drmmode_InitSharedPixmapFlipping(), which if flipping is enabled will call drmmode_SharedPixmapPresent() on scanout_pixmap_back. drmmode_SharedPixmapPresent() requests that for the source to present on the given buffer using master->PresentSharedPixmap(). If it succeeds, it will then attempt to flip to that buffer using drmmode_SharedPixmapFlip(). Flipping shouldn't fail, but if it does, it will raise a warning and try drmmode_SharedPixmapPresent() again on the next vblank using drmmode_SharedPixmapPresentOnVBlank(). master->PresentSharedPixmap() could fail, in most cases because there is no outstanding damage on the mscreenpix tracked by the shared pixmap. In this case, drmmode_SharedPixmapPresent() will attempt to use master->RequestSharedPixmapNotifyDamage() to request for the source driver to call slave->SharedPixmapNotifyDamage() in response to damage on mscreenpix. This will ultimately call into drmmode_SharedPixmapPresentOnVBlank() to retry drmmode_SharedPixmapPresent() on the next vblank after accumulating damage. drmmode_SharedPixmapFlip() sets up page flip event handler by packing struct vblank_event_args with the necessary parameters, and registering drmmode_SharedPixmapVBlankEventHandler() and drmmode_SharedPixmapVBlankEventAbort() with the modesetting DRM event handler queue. Then, it uses the drmModePageFlip() to flip on the next vblank and raise an event. drmmode_SharedPixmapPresentOnVBlank() operates similarly to drmmode_SharedPixmapFlip(), but uses drmWaitVBlank() instead of drmModePageFlip() to raise the event without flipping. On the next vblank, DRM will raise an event that will ultimately be handled by drmmode_SharedPixmapVBlankEventHandler(). If we flipped, it will update prime_pixmap and prime_pixmap_back to reflect that frontTarget is now being displayed, and use drmmode_SharedPixmapPresent(backTarget) to start the process again on the now-hidden shared pixmap. If we didn't flip, it will just use drmmode_SharedPixmapPresent(frontTarget) to start the process again on the still-hidden shared pixmap. Note that presentation generally happens asynchronously, so with these changes alone tearing is reduced, but we can't always guarantee that the present will finish before the flip. These changes are meant to be paired with changes to the sink DRM driver that makes flips wait on fences attached to dmabuf backed buffers. The source driver is responsible for attaching the fences and signaling them when presentation is finished. Note that because presentation is requested in response to a vblank, PRIME sources will now conform to the sink's refresh rate. At teardown, pScrPriv->rrDisableSharedPixmapFlipping() will be called, making its way to drmmode_FiniSharedPixmapFlipping(). There, the event handlers for prime_pixmap and prime_pixmap_back are aborted, freeing the left over parameter structure. Then, prime_pixmap and prime_pixmap back are unset as scanout pixmaps. Register and tear down slave damage per-scanout pixmap instead of per-crtc. v1: Initial commit v2: Renamed PresentTrackedFlippingPixmap to PresentSharedPixmap Renamed flipSeq to flip_seq Warn if flip failed Use SharedPixmapNotifyDamage to retry on next vblank after damage v3: Refactor to accomodate moving (rr)StartFlippingPixmapTracking and (rr)(Enable/Disable)SharedPixmapFlipping to rrScrPrivRec from ScreenRec Do damage tracking on both scanout pixmaps v4: Tweaks to commit message v5: Revise for internal storage of prime pixmap ptrs Move disabling for reverse PRIME from source commit to here Use drmmode_set_target_scanout_pixmap*() to set scanout pixmaps internally to EnableSharedPixmapFlipping(). Don't support flipping if ms->drmmode.pageflip == FALSE. Move flipping_active check to this commit v6: Rebase onto ToT v7: Unchanged Reviewed-by: Dave Airlie <airlied@redhat.com> Signed-off-by: Alex Goins <agoins@nvidia.com>
2016-06-16 20:06:52 -07:00
pScreen->SharedPixmapNotifyDamage = msSharedPixmapNotifyDamage;
pScreen->RequestSharedPixmapNotifyDamage =
msRequestSharedPixmapNotifyDamage;
pScreen->PresentSharedPixmap = msPresentSharedPixmap;
pScreen->StopFlippingPixmapTracking = msStopFlippingPixmapTracking;
modesetting: Implement PRIME syncing as a sink Implements (Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage, the sink functions for PRIME synchronization and double buffering. Allows modesetting driver to be used as a sink with PRIME synchronization. Changes dispatch_slave_dirty to flush damage from both scanout pixmaps. Changes drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target. Then, treat *target as it did prime_pixmap. This allows me to use it to explicitly set both prime_pixmap and prime_pixmap_back individually. drmmode_set_scanout_pixmap() without the extra parameter remains to cover the single-buffered case, but only works if we aren't already double buffered. driver.c: Add plumbing for rr(Enable/Disable)SharedPixmapFlipping and SharedPixmapNotifyDamage. Change dispatch_dirty_crtc to dispatch_dirty_pixmap, which functions the same but flushes damage associated with a ppriv instead of the crtc, and chanage dispatch_slave_dirty to use it on both scanout pixmaps if applicable. drmmode_display.h: Add flip_seq field to msPixmapPrivRec to keep track of the event handler associated with a given pixmap, if any. Add wait_for_damage field to msPixmapPrivRec to keep track if we have requested a damage notification from the source. Add enable_flipping field to drmmode_crtc_private_rec to keep track if flipping is enabled or disabled. Add prime_pixmap_back to drmmode_crtc_private_rec to keep track of back buffer internally. Add declarations for drmmode_SetupPageFlipFence(), drmmode_EnableSharedPixmapFlipping(), drmmode_DisableSharedPixmapFlipping, drmmode_SharedPixmapFlip(), and drmmode_SharedPixmapPresentOnVBlank(). Move slave damage from crtc to ppriv. drmmode_display.c: Change drmmode_set_scanout_pixmap*() functions to drmmode_set_target_scanout_pixmap*() that take an additional parameter PixmapPtr *target for explicitly setting different scanout pixmaps. Add definitions for functions drmmode_SharedPixmapFlip(), drmmode_SharedPixmapPresentOnVBlank(), drmmode_SharedPixmapPresent(), drmmode_SharedPixmapVBlankEventHandler(), drmmode_SharedPixmapVBlankEventAbort(), drmmode_EnableSharedPixmapFlipping(), and drmmode_DisableSharedPixmapFlipping, drmmode_InitSharedPixmapFlipping(), and drmmode_FiniSharedPixmapFlipping, along with struct vblank_event_args. The control flow is as follows: pScrPriv->rrEnableSharedPixmapFlipping() makes its way to drmmode_EnableSharedPixmapFlipping(), which sets enable_flipping to TRUE and sets both scanout pixmaps prime_pixmap and prime_pixmap_back. When setting a mode, if prime_pixmap is defined, modesetting driver will call drmmode_InitSharedPixmapFlipping(), which if flipping is enabled will call drmmode_SharedPixmapPresent() on scanout_pixmap_back. drmmode_SharedPixmapPresent() requests that for the source to present on the given buffer using master->PresentSharedPixmap(). If it succeeds, it will then attempt to flip to that buffer using drmmode_SharedPixmapFlip(). Flipping shouldn't fail, but if it does, it will raise a warning and try drmmode_SharedPixmapPresent() again on the next vblank using drmmode_SharedPixmapPresentOnVBlank(). master->PresentSharedPixmap() could fail, in most cases because there is no outstanding damage on the mscreenpix tracked by the shared pixmap. In this case, drmmode_SharedPixmapPresent() will attempt to use master->RequestSharedPixmapNotifyDamage() to request for the source driver to call slave->SharedPixmapNotifyDamage() in response to damage on mscreenpix. This will ultimately call into drmmode_SharedPixmapPresentOnVBlank() to retry drmmode_SharedPixmapPresent() on the next vblank after accumulating damage. drmmode_SharedPixmapFlip() sets up page flip event handler by packing struct vblank_event_args with the necessary parameters, and registering drmmode_SharedPixmapVBlankEventHandler() and drmmode_SharedPixmapVBlankEventAbort() with the modesetting DRM event handler queue. Then, it uses the drmModePageFlip() to flip on the next vblank and raise an event. drmmode_SharedPixmapPresentOnVBlank() operates similarly to drmmode_SharedPixmapFlip(), but uses drmWaitVBlank() instead of drmModePageFlip() to raise the event without flipping. On the next vblank, DRM will raise an event that will ultimately be handled by drmmode_SharedPixmapVBlankEventHandler(). If we flipped, it will update prime_pixmap and prime_pixmap_back to reflect that frontTarget is now being displayed, and use drmmode_SharedPixmapPresent(backTarget) to start the process again on the now-hidden shared pixmap. If we didn't flip, it will just use drmmode_SharedPixmapPresent(frontTarget) to start the process again on the still-hidden shared pixmap. Note that presentation generally happens asynchronously, so with these changes alone tearing is reduced, but we can't always guarantee that the present will finish before the flip. These changes are meant to be paired with changes to the sink DRM driver that makes flips wait on fences attached to dmabuf backed buffers. The source driver is responsible for attaching the fences and signaling them when presentation is finished. Note that because presentation is requested in response to a vblank, PRIME sources will now conform to the sink's refresh rate. At teardown, pScrPriv->rrDisableSharedPixmapFlipping() will be called, making its way to drmmode_FiniSharedPixmapFlipping(). There, the event handlers for prime_pixmap and prime_pixmap_back are aborted, freeing the left over parameter structure. Then, prime_pixmap and prime_pixmap back are unset as scanout pixmaps. Register and tear down slave damage per-scanout pixmap instead of per-crtc. v1: Initial commit v2: Renamed PresentTrackedFlippingPixmap to PresentSharedPixmap Renamed flipSeq to flip_seq Warn if flip failed Use SharedPixmapNotifyDamage to retry on next vblank after damage v3: Refactor to accomodate moving (rr)StartFlippingPixmapTracking and (rr)(Enable/Disable)SharedPixmapFlipping to rrScrPrivRec from ScreenRec Do damage tracking on both scanout pixmaps v4: Tweaks to commit message v5: Revise for internal storage of prime pixmap ptrs Move disabling for reverse PRIME from source commit to here Use drmmode_set_target_scanout_pixmap*() to set scanout pixmaps internally to EnableSharedPixmapFlipping(). Don't support flipping if ms->drmmode.pageflip == FALSE. Move flipping_active check to this commit v6: Rebase onto ToT v7: Unchanged Reviewed-by: Dave Airlie <airlied@redhat.com> Signed-off-by: Alex Goins <agoins@nvidia.com>
2016-06-16 20:06:52 -07:00
2008-05-28 15:55:36 +01:00
if (!xf86CrtcScreenInit(pScreen))
return FALSE;
if (!drmmode_setup_colormap(pScreen, pScrn))
return FALSE;
if (ms->atomic_modeset)
xf86DPMSInit(pScreen, drmmode_set_dpms, 0);
else
xf86DPMSInit(pScreen, xf86DPMSSet, 0);
#ifdef GLAMOR_HAS_GBM
if (ms->drmmode.glamor) {
XF86VideoAdaptorPtr glamor_adaptor;
glamor_adaptor = ms->glamor.xv_init(pScreen, 16);
if (glamor_adaptor != NULL)
xf86XVScreenInit(pScreen, &glamor_adaptor, 1);
else
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Failed to initialize XV support.\n");
}
#endif
2008-05-28 15:55:36 +01:00
if (serverGeneration == 1)
xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options);
if (!ms_vblank_screen_init(pScreen)) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Failed to initialize vblank support.\n");
return FALSE;
}
#ifdef GLAMOR_HAS_GBM
if (ms->drmmode.glamor) {
if (!(ms->drmmode.dri2_enable = ms_dri2_screen_init(pScreen))) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Failed to initialize the DRI2 extension.\n");
}
/* enable reverse prime if we are a GPU screen, and accelerated, and not
* i915, evdi or udl. i915 is happy scanning out from sysmem.
* evdi and udl are virtual drivers scanning out from sysmem
* backed dumb buffers.
*/
if (pScreen->isGPU) {
drmVersionPtr version;
/* enable if we are an accelerated GPU screen */
ms->drmmode.reverse_prime_offload_mode = TRUE;
if ((version = drmGetVersion(ms->drmmode.fd))) {
if (!strncmp("i915", version->name, version->name_len)) {
ms->drmmode.reverse_prime_offload_mode = FALSE;
}
if (!strncmp("evdi", version->name, version->name_len)) {
ms->drmmode.reverse_prime_offload_mode = FALSE;
}
if (!strncmp("udl", version->name, version->name_len)) {
ms->drmmode.reverse_prime_offload_mode = FALSE;
}
if (!ms->drmmode.reverse_prime_offload_mode) {
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"Disable reverse prime offload mode for %s.\n", version->name);
}
drmFreeVersion(version);
}
}
}
#endif
if (!(ms->drmmode.present_enable = ms_present_screen_init(pScreen))) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Failed to initialize the Present extension.\n");
}
pScrn->vtSema = TRUE;
if (ms->vrr_support) {
if (!property_vectors_wrapped) {
saved_change_property = ProcVector[X_ChangeProperty];
ProcVector[X_ChangeProperty] = ms_change_property;
saved_delete_property = ProcVector[X_DeleteProperty];
ProcVector[X_DeleteProperty] = ms_delete_property;
property_vectors_wrapped = TRUE;
}
vrr_atom = MakeAtom("_VARIABLE_REFRESH",
strlen("_VARIABLE_REFRESH"), TRUE);
}
return TRUE;
}
static void
AdjustFrame(ScrnInfoPtr pScrn, int x, int y)
{
modesettingPtr ms = modesettingPTR(pScrn);
2008-05-28 15:55:36 +01:00
drmmode_adjust_frame(pScrn, &ms->drmmode, x, y);
}
static void
LeaveVT(ScrnInfoPtr pScrn)
{
modesettingPtr ms = modesettingPTR(pScrn);
2012-04-17 11:48:03 +01:00
xf86_hide_cursors(pScrn);
pScrn->vtSema = FALSE;
#ifdef XF86_PDEV_SERVER_FD
if (ms->pEnt->location.type == BUS_PLATFORM &&
(ms->pEnt->location.id.plat->flags & XF86_PDEV_SERVER_FD))
return;
#endif
if (!ms->fd_passed)
drmDropMaster(ms->fd);
}
/*
* This gets called when gaining control of the VT, and from ScreenInit().
*/
static Bool
EnterVT(ScrnInfoPtr pScrn)
{
2008-05-28 15:55:36 +01:00
modesettingPtr ms = modesettingPTR(pScrn);
2011-09-29 14:05:43 +01:00
pScrn->vtSema = TRUE;
SetMaster(pScrn);
drmmode_update_kms_state(&ms->drmmode);
modesetting: keep going if a modeset fails on EnterVT There was a time when setting a mode on a CRTC would not depend on the associated connector's state. If a mode had been set successfully once, it would mean it would work later on. This changed with the introduction of new connectors type that now require a link training sequence (DP, HDMI 2.0), and that means that some events may have happened while the X server was not master that would then prevent the mode from successfully be restored to its previous state. This patch relaxes the requirement that all modes should be restored on EnterVT, or the entire X-Server would go down by allowing modesets to fail (with some warnings). If a modeset fails, the CRTC will be disabled, and a RandR event will be sent for the desktop environment to fix the situation as well as possible. Additional patches might be needed to make sure that the user would never be left with all screens black in some scenarios. v2 (Martin Peres): - whitespace fixes - remove the uevent handling (it is done in a previous patch) - improve the commit message - reduce the size of the patch by not changing lines needlessly - return FALSE if one modeset fails in ignore mode - add comments/todos to explain why we do things - disable the CRTCs that failed the modeset Signed-off-by: Kishore Kadiyala <kishore.kadiyala@intel.com> Signed-off-by: Martin Peres <martin.peres@linux.intel.com> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> Tested-by: Kishore Kadiyala <kishore.kadiyala@intel.com> Closes: #1010
2020-09-19 01:28:14 +05:30
/* allow not all modes to be set successfully since some events might have
* happened while not being master that could prevent the previous
* configuration from being re-applied.
*/
if (!drmmode_set_desired_modes(pScrn, &ms->drmmode, TRUE, TRUE)) {
xf86DisableUnusedFunctions(pScrn);
/* TODO: check that at least one screen is on, to allow the user to fix
* their setup if all modeset failed...
*/
/* Tell the desktop environment that something changed, so that they
* can hopefully correct the situation
*/
RRSetChanged(xf86ScrnToScreen(pScrn));
RRTellChanged(xf86ScrnToScreen(pScrn));
}
2008-05-28 15:55:36 +01:00
return TRUE;
}
static Bool
SwitchMode(ScrnInfoPtr pScrn, DisplayModePtr mode)
{
2008-05-28 15:55:36 +01:00
return xf86SetSingleMode(pScrn, mode, RR_Rotate_0);
}
static Bool
CloseScreen(ScreenPtr pScreen)
{
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
2008-05-28 15:55:36 +01:00
modesettingPtr ms = modesettingPTR(pScrn);
modesettingEntPtr ms_ent = ms_ent_priv(pScrn);
/* Clear mask of assigned crtc's in this generation */
ms_ent->assigned_crtcs = 0;
#ifdef GLAMOR_HAS_GBM
if (ms->drmmode.dri2_enable) {
ms_dri2_close_screen(pScreen);
}
#endif
ms_vblank_close_screen(pScreen);
2011-09-29 12:38:26 +01:00
if (ms->damage) {
DamageUnregister(ms->damage);
DamageDestroy(ms->damage);
ms->damage = NULL;
2011-09-29 12:38:26 +01:00
}
if (ms->drmmode.shadow_enable) {
ms->shadow.Remove(pScreen, pScreen->GetScreenPixmap(pScreen));
free(ms->drmmode.shadow_fb);
ms->drmmode.shadow_fb = NULL;
free(ms->drmmode.shadow_fb2);
ms->drmmode.shadow_fb2 = NULL;
}
Add RandR leases with modesetting driver support [v6] This adds support for RandR CRTC/Output leases through the modesetting driver, creating a lease using new kernel infrastructure and returning that to a client through an fd which will have access to only those resources. v2: Restore CRTC mode when leases terminate When a lease terminates for a crtc we have saved data for, go ahead and restore the saved mode. v3: Report RR_Rotate_0 rotations for leased crtcs. Ignore leased CRTCs when selecting screen size. Stop leasing encoders, the kernel doesn't do that anymore. Turn off crtc->enabled while leased so that modesetting ignores them. Check lease status before calling any driver mode functions When starting a lease, mark leased CRTCs as disabled and hide their cursors. Also, check to see if there are other non-leased CRTCs which are driving leased Outputs and mark them as disabled as well. Sometimes an application will lease an idle crtc instead of the one already associated with the leased output. When terminating a lease, reset any CRTCs which are driving outputs that are no longer leased so that they start working again. This required splitting the DIX level lease termination code into two pieces, one to remove the lease from the system (RRLeaseTerminated) and a new function that frees the lease data structure (RRLeaseFree). v4: Report RR_Rotate_0 rotation for leased crtcs. v5: Terminate all leases on server reset. Leases hang around after the associated client exits so that the client doesn't need to occupy an X server client slot and consume a file descriptor once it has gotten the output resources necessary. Any leases still hanging around when the X server resets or shuts down need to be cleaned up by calling the kernel to terminate the lease and freeing any DIX structures. Note that we cannot simply use the existing drmmode_terminate_lease function on each lease as that wants to also reset the video mode, and during server shut down that modesetting: Validate leases on VT enter The kernel doesn't allow any master ioctls to run when another VT is active, including simple things like listing the active leases. To deal with that, we check the list of leases whenever the X server VT is activated. xfree86: hide disabled cursors when resetting after lease termination The lessee may well have played with cursors and left one active on our screen. Just tell the kernel to turn it off. v6: Add meson build infrastructure [Also bumped libdrm requirement - ajax] Signed-off-by: Keith Packard <keithp@keithp.com> Reviewed-by: Adam Jackson <ajax@redhat.com>
2018-02-12 13:51:56 -08:00
drmmode_uevent_fini(pScrn, &ms->drmmode);
drmmode_free_bos(pScrn, &ms->drmmode);
if (ms->drmmode.pageflip) {
miPointerScreenPtr PointPriv =
dixLookupPrivate(&pScreen->devPrivates, miPointerScreenKey);
if (PointPriv->spriteFuncs == &drmmode_sprite_funcs)
PointPriv->spriteFuncs = ms->SpriteFuncs;
}
if (pScrn->vtSema) {
LeaveVT(pScrn);
2008-05-28 15:55:36 +01:00
}
pScreen->CreateScreenResources = ms->createScreenResources;
2011-09-29 12:38:26 +01:00
pScreen->BlockHandler = ms->BlockHandler;
2011-09-29 14:05:43 +01:00
2008-05-28 15:55:36 +01:00
pScrn->vtSema = FALSE;
pScreen->CloseScreen = ms->CloseScreen;
return (*pScreen->CloseScreen) (pScreen);
}
static ModeStatus
ValidMode(ScrnInfoPtr arg, DisplayModePtr mode, Bool verbose, int flags)
{
2008-05-28 15:55:36 +01:00
return MODE_OK;
}