summaryrefslogtreecommitdiff
path: root/src/sna/sna_driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sna/sna_driver.c')
-rw-r--r--src/sna/sna_driver.c1127
1 files changed, 1127 insertions, 0 deletions
diff --git a/src/sna/sna_driver.c b/src/sna/sna_driver.c
new file mode 100644
index 000000000..1b7e817f5
--- /dev/null
+++ b/src/sna/sna_driver.c
@@ -0,0 +1,1127 @@
+/**************************************************************************
+
+Copyright 2001 VA Linux Systems Inc., Fremont, California.
+Copyright © 2002 by David Dawes
+
+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
+on 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
+THE COPYRIGHT HOLDERS AND/OR THEIR 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.
+
+**************************************************************************/
+
+/*
+ * Authors: Jeff Hartmann <jhartmann@valinux.com>
+ * Abraham van der Merwe <abraham@2d3d.co.za>
+ * David Dawes <dawes@xfree86.org>
+ * Alan Hourihane <alanh@tungstengraphics.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <xf86cmap.h>
+#include <xf86drm.h>
+#include <xf86RandR12.h>
+#include <mi.h>
+#include <micmap.h>
+#include <mipict.h>
+
+#include "compiler.h"
+#include "sna.h"
+#include "sna_module.h"
+#include "sna_video.h"
+
+#include "intel_driver.h"
+#include "intel_options.h"
+
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/poll.h>
+#include "i915_drm.h"
+
+#ifdef HAVE_VALGRIND
+#include <valgrind.h>
+#include <memcheck.h>
+#endif
+
+#if HAVE_DOT_GIT
+#include "git_version.h"
+#endif
+
+DevPrivateKeyRec sna_pixmap_key;
+DevPrivateKeyRec sna_gc_key;
+DevPrivateKeyRec sna_window_key;
+DevPrivateKeyRec sna_glyph_key;
+
+static Bool sna_enter_vt(VT_FUNC_ARGS_DECL);
+
+/* temporary */
+extern void xf86SetCursor(ScreenPtr screen, CursorPtr pCurs, int x, int y);
+
+static void
+sna_load_palette(ScrnInfoPtr scrn, int numColors, int *indices,
+ LOCO * colors, VisualPtr pVisual)
+{
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
+ int i, j, index;
+ int p;
+ uint16_t lut_r[256], lut_g[256], lut_b[256];
+
+ DBG(("%s\n", __FUNCTION__));
+
+ for (p = 0; p < xf86_config->num_crtc; p++) {
+ xf86CrtcPtr crtc = xf86_config->crtc[p];
+
+ switch (scrn->depth) {
+ case 15:
+ for (i = 0; i < numColors; i++) {
+ index = indices[i];
+ for (j = 0; j < 8; j++) {
+ lut_r[index * 8 + j] =
+ colors[index].red << 8;
+ lut_g[index * 8 + j] =
+ colors[index].green << 8;
+ lut_b[index * 8 + j] =
+ colors[index].blue << 8;
+ }
+ }
+ break;
+ case 16:
+ for (i = 0; i < numColors; i++) {
+ index = indices[i];
+
+ if (index <= 31) {
+ for (j = 0; j < 8; j++) {
+ lut_r[index * 8 + j] =
+ colors[index].red << 8;
+ lut_b[index * 8 + j] =
+ colors[index].blue << 8;
+ }
+ }
+
+ for (j = 0; j < 4; j++) {
+ lut_g[index * 4 + j] =
+ colors[index].green << 8;
+ }
+ }
+ break;
+ default:
+ for (i = 0; i < numColors; i++) {
+ index = indices[i];
+ lut_r[index] = colors[index].red << 8;
+ lut_g[index] = colors[index].green << 8;
+ lut_b[index] = colors[index].blue << 8;
+ }
+ break;
+ }
+
+ /* Make the change through RandR */
+#ifdef RANDR_12_INTERFACE
+ RRCrtcGammaSet(crtc->randr_crtc, lut_r, lut_g, lut_b);
+#else
+ crtc->funcs->gamma_set(crtc, lut_r, lut_g, lut_b, 256);
+#endif
+ }
+}
+
+/**
+ * Adjust the screen pixmap for the current location of the front buffer.
+ * This is done at EnterVT when buffers are bound as long as the resources
+ * have already been created, but the first EnterVT happens before
+ * CreateScreenResources.
+ */
+static Bool sna_create_screen_resources(ScreenPtr screen)
+{
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ struct sna *sna = to_sna_from_screen(screen);
+
+ DBG(("%s(%dx%d@%d)\n", __FUNCTION__,
+ screen->width, screen->height, screen->rootDepth));
+
+ free(screen->devPrivate);
+ screen->devPrivate = NULL;
+
+ sna_accel_create(sna);
+
+ sna->front = screen->CreatePixmap(screen,
+ screen->width,
+ screen->height,
+ screen->rootDepth,
+ SNA_CREATE_FB);
+ if (!sna->front) {
+ xf86DrvMsg(screen->myNum, X_ERROR,
+ "[intel] Unable to create front buffer %dx%d at depth %d\n",
+ screen->width,
+ screen->height,
+ screen->rootDepth);
+
+ return FALSE;
+ }
+
+ if (!sna_pixmap_force_to_gpu(sna->front, MOVE_WRITE)) {
+ xf86DrvMsg(screen->myNum, X_ERROR,
+ "[intel] Failed to allocate video resources for front buffer %dx%d at depth %d\n",
+ screen->width,
+ screen->height,
+ screen->rootDepth);
+ goto cleanup_front;
+ }
+
+ screen->SetScreenPixmap(sna->front);
+
+ sna_copy_fbcon(sna);
+
+ if (!sna_enter_vt(VT_FUNC_ARGS(0))) {
+ xf86DrvMsg(screen->myNum, X_ERROR,
+ "[intel] Failed to become DRM master\n");
+ goto cleanup_front;
+ }
+
+ return TRUE;
+
+cleanup_front:
+ screen->SetScreenPixmap(NULL);
+ screen->DestroyPixmap(sna->front);
+ sna->front = NULL;
+ return FALSE;
+}
+
+static void PreInitCleanup(ScrnInfoPtr scrn)
+{
+ if (!scrn || !scrn->driverPrivate)
+ return;
+
+ free(scrn->driverPrivate);
+ scrn->driverPrivate = NULL;
+}
+
+struct sna_device {
+ int fd;
+ int open_count;
+};
+static int sna_device_key = -1;
+
+static inline struct sna_device *sna_device(ScrnInfoPtr scrn)
+{
+ if (scrn->entityList == NULL)
+ return NULL;
+
+ return xf86GetEntityPrivate(scrn->entityList[0], sna_device_key)->ptr;
+}
+
+static inline void sna_set_device(ScrnInfoPtr scrn, struct sna_device *dev)
+{
+ xf86GetEntityPrivate(scrn->entityList[0], sna_device_key)->ptr = dev;
+}
+
+static int sna_open_drm_master(ScrnInfoPtr scrn)
+{
+ struct sna_device *dev;
+ struct sna *sna = to_sna(scrn);
+ struct pci_device *pci = sna->PciInfo;
+ drmSetVersion sv;
+ int err;
+ char busid[20];
+ int fd;
+
+ DBG(("%s\n", __FUNCTION__));
+
+ dev = sna_device(scrn);
+ if (dev) {
+ dev->open_count++;
+ DBG(("%s: reusing device, count=%d\n",
+ __FUNCTION__, dev->open_count));
+ return dev->fd;
+ }
+
+ snprintf(busid, sizeof(busid), "pci:%04x:%02x:%02x.%d",
+ pci->domain, pci->bus, pci->dev, pci->func);
+
+ DBG(("%s: opening device '%s'\n", __FUNCTION__, busid));
+ fd = drmOpen(NULL, busid);
+ if (fd == -1) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "[drm] Failed to open DRM device for %s: %s\n",
+ busid, strerror(errno));
+ return -1;
+ }
+
+ /* Check that what we opened was a master or a master-capable FD,
+ * by setting the version of the interface we'll use to talk to it.
+ * (see DRIOpenDRMMaster() in DRI1)
+ */
+ sv.drm_di_major = 1;
+ sv.drm_di_minor = 1;
+ sv.drm_dd_major = -1;
+ sv.drm_dd_minor = -1;
+ err = drmSetInterfaceVersion(fd, &sv);
+ if (err != 0) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "[drm] failed to set drm interface version: %s [%d].\n",
+ strerror(-err), -err);
+ drmClose(fd);
+ return -1;
+ }
+
+ dev = malloc(sizeof(*dev));
+ if (dev) {
+ int flags;
+
+ /* make the fd nonblocking to handle event loops */
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags != -1)
+ (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+
+ dev->fd = fd;
+ dev->open_count = 1;
+ sna_set_device(scrn, dev);
+ }
+
+ return fd;
+}
+
+static void sna_close_drm_master(ScrnInfoPtr scrn)
+{
+ struct sna_device *dev = sna_device(scrn);
+
+ if (dev == NULL)
+ return;
+
+ DBG(("%s(open_count=%d)\n", __FUNCTION__, dev->open_count));
+ if (--dev->open_count)
+ return;
+
+ drmClose(dev->fd);
+ sna_set_device(scrn, NULL);
+ free(dev);
+}
+
+static void sna_selftest(void)
+{
+ sna_damage_selftest();
+}
+
+static bool has_pageflipping(struct sna *sna)
+{
+ drm_i915_getparam_t gp;
+ int v;
+
+ if (sna->flags & SNA_NO_WAIT)
+ return false;
+
+ v = 0;
+
+ VG_CLEAR(gp);
+ gp.param = I915_PARAM_HAS_PAGEFLIPPING;
+ gp.value = &v;
+
+ if (drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GETPARAM, &gp))
+ return false;
+
+ VG(VALGRIND_MAKE_MEM_DEFINED(&v, sizeof(v)));
+ return v > 0;
+}
+
+static void sna_setup_capabilities(ScrnInfoPtr scrn, int fd)
+{
+#if HAS_PIXMAP_SHARING && defined(DRM_CAP_PRIME)
+ uint64_t value;
+
+ scrn->capabilities = 0;
+ if (drmGetCap(fd, DRM_CAP_PRIME, &value) == 0) {
+ if (value & DRM_PRIME_CAP_EXPORT)
+ scrn->capabilities |= RR_Capability_SourceOutput | RR_Capability_SinkOffload;
+ if (value & DRM_PRIME_CAP_IMPORT)
+ scrn->capabilities |= RR_Capability_SinkOutput;
+ }
+#endif
+}
+
+/**
+ * This is called before ScreenInit to do any require probing of screen
+ * configuration.
+ *
+ * This code generally covers probing, module loading, option handling
+ * card mapping, and RandR setup.
+ *
+ * Since xf86InitialConfiguration ends up requiring that we set video modes
+ * in order to detect configuration, we end up having to do a lot of driver
+ * setup (talking to the DRM, mapping the device, etc.) in this function.
+ * As a result, we want to set up that server initialization once rather
+ * that doing it per generation.
+ */
+static Bool sna_pre_init(ScrnInfoPtr scrn, int flags)
+{
+ struct sna *sna;
+ rgb defaultWeight = { 0, 0, 0 };
+ EntityInfoPtr pEnt;
+ int flags24;
+ Gamma zeros = { 0.0, 0.0, 0.0 };
+ int fd;
+
+ DBG(("%s flags=%x, numEntities=%d\n",
+ __FUNCTION__, flags, scrn->numEntities));
+
+ if (scrn->numEntities != 1)
+ return FALSE;
+
+ pEnt = xf86GetEntityInfo(scrn->entityList[0]);
+ if (pEnt == NULL)
+ return FALSE;
+
+ if (pEnt->location.type != BUS_PCI
+#ifdef XSERVER_PLATFORM_BUS
+ && pEnt->location.type != BUS_PLATFORM
+#endif
+ )
+ return FALSE;
+
+ if (flags & PROBE_DETECT)
+ return TRUE;
+
+ sna_selftest();
+
+ if (((uintptr_t)scrn->driverPrivate) & 1) {
+ sna = xnfcalloc(sizeof(struct sna), 1);
+ if (sna == NULL)
+ return FALSE;
+
+ sna->info = (void *)((uintptr_t)scrn->driverPrivate & ~1);
+ scrn->driverPrivate = sna;
+ }
+ sna = to_sna(scrn);
+ sna->scrn = scrn;
+ sna->pEnt = pEnt;
+
+ scrn->displayWidth = 640; /* default it */
+
+ sna->PciInfo = xf86GetPciInfoForEntity(sna->pEnt->index);
+
+ fd = sna_open_drm_master(scrn);
+ if (fd == -1) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "Failed to become DRM master.\n");
+ return FALSE;
+ }
+
+ scrn->monitor = scrn->confScreen->monitor;
+ scrn->progClock = TRUE;
+ scrn->rgbBits = 8;
+
+ flags24 = Support32bppFb | PreferConvert24to32 | SupportConvert24to32;
+
+ if (!xf86SetDepthBpp(scrn, 0, 0, 0, flags24))
+ return FALSE;
+
+ switch (scrn->depth) {
+ case 8:
+ case 15:
+ case 16:
+ case 24:
+ case 30:
+ break;
+ default:
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "Given depth (%d) is not supported by Intel driver\n",
+ scrn->depth);
+ return FALSE;
+ }
+ xf86PrintDepthBpp(scrn);
+
+ if (!xf86SetWeight(scrn, defaultWeight, defaultWeight))
+ return FALSE;
+ if (!xf86SetDefaultVisual(scrn, -1))
+ return FALSE;
+
+ sna->Options = intel_options_get(scrn);
+ if (sna->Options == NULL)
+ return FALSE;
+
+ sna_setup_capabilities(scrn, fd);
+
+ intel_detect_chipset(scrn, sna->pEnt, sna->PciInfo);
+
+ kgem_init(&sna->kgem, fd, sna->PciInfo, sna->info->gen);
+ if (xf86ReturnOptValBool(sna->Options, OPTION_ACCEL_DISABLE, FALSE)) {
+ xf86DrvMsg(sna->scrn->scrnIndex, X_CONFIG,
+ "Disabling hardware acceleration.\n");
+ sna->kgem.wedged = true;
+ }
+
+ if (!xf86ReturnOptValBool(sna->Options,
+ OPTION_RELAXED_FENCING,
+ sna->kgem.has_relaxed_fencing)) {
+ xf86DrvMsg(scrn->scrnIndex,
+ sna->kgem.has_relaxed_fencing ? X_CONFIG : X_PROBED,
+ "Disabling use of relaxed fencing\n");
+ sna->kgem.has_relaxed_fencing = 0;
+ }
+
+ /* Enable tiling by default */
+ sna->tiling = SNA_TILING_ALL;
+
+ /* Allow user override if they set a value */
+ if (!xf86ReturnOptValBool(sna->Options, OPTION_TILING_2D, TRUE))
+ sna->tiling &= ~SNA_TILING_2D;
+ if (xf86ReturnOptValBool(sna->Options, OPTION_TILING_FB, FALSE))
+ sna->tiling &= ~SNA_TILING_FB;
+
+ sna->flags = 0;
+ if (!xf86ReturnOptValBool(sna->Options, OPTION_THROTTLE, TRUE))
+ sna->flags |= SNA_NO_THROTTLE;
+ if (!xf86ReturnOptValBool(sna->Options, OPTION_DELAYED_FLUSH, TRUE))
+ sna->flags |= SNA_NO_DELAYED_FLUSH;
+ if (!xf86ReturnOptValBool(sna->Options, OPTION_SWAPBUFFERS_WAIT, TRUE))
+ sna->flags |= SNA_NO_WAIT;
+ if (has_pageflipping(sna)) {
+ if (xf86ReturnOptValBool(sna->Options, OPTION_TEAR_FREE, FALSE))
+ sna->flags |= SNA_TEAR_FREE;
+ } else
+ sna->flags |= SNA_NO_FLIP;
+ if (xf86ReturnOptValBool(sna->Options, OPTION_CRTC_PIXMAPS, FALSE))
+ sna->flags |= SNA_FORCE_SHADOW;
+
+ xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "Framebuffer %s\n",
+ sna->tiling & SNA_TILING_FB ? "tiled" : "linear");
+ xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "Pixmaps %s\n",
+ sna->tiling & SNA_TILING_2D ? "tiled" : "linear");
+ xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "3D buffers %s\n",
+ sna->tiling & SNA_TILING_3D ? "tiled" : "linear");
+ xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "Throttling %sabled\n",
+ sna->flags & SNA_NO_THROTTLE ? "dis" : "en");
+ xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "Delayed flush %sabled\n",
+ sna->flags & SNA_NO_DELAYED_FLUSH ? "dis" : "en");
+ xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "\"Tear free\" %sabled\n",
+ sna->flags & SNA_TEAR_FREE ? "en" : "dis");
+ xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "Forcing per-crtc-pixmaps? %s\n",
+ sna->flags & SNA_FORCE_SHADOW ? "yes" : "no");
+
+ if (!sna_mode_pre_init(scrn, sna)) {
+ PreInitCleanup(scrn);
+ return FALSE;
+ }
+
+ if (!xf86SetGamma(scrn, zeros)) {
+ PreInitCleanup(scrn);
+ return FALSE;
+ }
+
+ if (scrn->modes == NULL) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR, "No modes.\n");
+ PreInitCleanup(scrn);
+ return FALSE;
+ }
+ scrn->currentMode = scrn->modes;
+
+ /* Set display resolution */
+ xf86SetDpi(scrn, 0, 0);
+
+ sna->dri_available = false;
+ if (xf86ReturnOptValBool(sna->Options, OPTION_DRI, TRUE))
+ sna->dri_available = !!xf86LoadSubModule(scrn, "dri2");
+
+ return TRUE;
+}
+
+static void
+sna_block_handler(BLOCKHANDLER_ARGS_DECL)
+{
+ SCREEN_PTR(arg);
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ struct sna *sna = to_sna(scrn);
+ struct timeval **tv = timeout;
+
+ DBG(("%s (tv=%ld.%06ld)\n", __FUNCTION__,
+ *tv ? (*tv)->tv_sec : -1, *tv ? (*tv)->tv_usec : 0));
+
+ sna->BlockHandler(BLOCKHANDLER_ARGS);
+
+ if (*tv == NULL || ((*tv)->tv_usec | (*tv)->tv_sec))
+ sna_accel_block_handler(sna, tv);
+}
+
+static void
+sna_wakeup_handler(WAKEUPHANDLER_ARGS_DECL)
+{
+ SCREEN_PTR(arg);
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ struct sna *sna = to_sna(scrn);
+
+ DBG(("%s\n", __FUNCTION__));
+
+ /* despite all appearances, result is just a signed int */
+ if ((int)result < 0)
+ return;
+
+ sna->WakeupHandler(WAKEUPHANDLER_ARGS);
+
+ sna_accel_wakeup_handler(sna);
+
+ if (FD_ISSET(sna->kgem.fd, (fd_set*)read_mask))
+ sna_mode_wakeup(sna);
+}
+
+#if HAVE_UDEV
+static void
+sna_handle_uevents(int fd, void *closure)
+{
+ ScrnInfoPtr scrn = closure;
+ struct sna *sna = to_sna(scrn);
+ struct udev_device *dev;
+ const char *hotplug;
+ struct stat s;
+ dev_t udev_devnum;
+
+ DBG(("%s\n", __FUNCTION__));
+
+ dev = udev_monitor_receive_device(sna->uevent_monitor);
+ if (!dev)
+ return;
+
+ udev_devnum = udev_device_get_devnum(dev);
+ if (fstat(sna->kgem.fd, &s)) {
+ udev_device_unref(dev);
+ return;
+ }
+
+ /*
+ * Check to make sure this event is directed at our
+ * device (by comparing dev_t values), then make
+ * sure it's a hotplug event (HOTPLUG=1)
+ */
+
+ hotplug = udev_device_get_property_value(dev, "HOTPLUG");
+
+ if (memcmp(&s.st_rdev, &udev_devnum, sizeof (dev_t)) == 0 &&
+ hotplug && atoi(hotplug) == 1) {
+ DBG(("%s: hotplug event\n", __FUNCTION__));
+ sna_mode_update(sna);
+ RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
+ }
+
+ udev_device_unref(dev);
+}
+
+static void
+sna_uevent_init(ScrnInfoPtr scrn)
+{
+ struct sna *sna = to_sna(scrn);
+ struct udev *u;
+ struct udev_monitor *mon;
+ Bool hotplug;
+ MessageType from = X_CONFIG;
+
+ DBG(("%s\n", __FUNCTION__));
+
+ if (!xf86GetOptValBool(sna->Options, OPTION_HOTPLUG, &hotplug)) {
+ from = X_DEFAULT;
+ hotplug = TRUE;
+ }
+
+ xf86DrvMsg(scrn->scrnIndex, from, "hotplug detection: \"%s\"\n",
+ hotplug ? "enabled" : "disabled");
+ if (!hotplug)
+ return;
+
+ u = udev_new();
+ if (!u)
+ return;
+
+ mon = udev_monitor_new_from_netlink(u, "udev");
+
+ if (!mon) {
+ udev_unref(u);
+ return;
+ }
+
+ if (udev_monitor_filter_add_match_subsystem_devtype(mon,
+ "drm",
+ "drm_minor") < 0 ||
+ udev_monitor_enable_receiving(mon) < 0)
+ {
+ udev_monitor_unref(mon);
+ udev_unref(u);
+ return;
+ }
+
+ sna->uevent_handler =
+ xf86AddGeneralHandler(udev_monitor_get_fd(mon),
+ sna_handle_uevents,
+ scrn);
+ if (!sna->uevent_handler) {
+ udev_monitor_unref(mon);
+ udev_unref(u);
+ return;
+ }
+
+ sna->uevent_monitor = mon;
+}
+
+static void
+sna_uevent_fini(ScrnInfoPtr scrn)
+{
+ struct sna *sna = to_sna(scrn);
+
+ if (sna->uevent_handler) {
+ struct udev *u = udev_monitor_get_udev(sna->uevent_monitor);
+
+ xf86RemoveGeneralHandler(sna->uevent_handler);
+
+ udev_monitor_unref(sna->uevent_monitor);
+ udev_unref(u);
+ sna->uevent_handler = NULL;
+ sna->uevent_monitor = NULL;
+ }
+}
+#else
+static void sna_uevent_fini(ScrnInfoPtr scrn) { }
+#endif /* HAVE_UDEV */
+
+static void sna_leave_vt(VT_FUNC_ARGS_DECL)
+{
+ SCRN_INFO_PTR(arg);
+ struct sna *sna = to_sna(scrn);
+
+ DBG(("%s\n", __FUNCTION__));
+
+ xf86_hide_cursors(scrn);
+
+ if (drmDropMaster(sna->kgem.fd))
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "drmDropMaster failed: %s\n", strerror(errno));
+}
+
+/* In order to workaround a kernel bug in not honouring O_NONBLOCK,
+ * check that the fd is readable before attempting to read the next
+ * event from drm.
+ */
+static Bool sna_mode_has_pending_events(struct sna *sna)
+{
+ struct pollfd pfd;
+ pfd.fd = sna->kgem.fd;
+ pfd.events = POLLIN;
+ return poll(&pfd, 1, 0) == 1;
+}
+
+static Bool sna_early_close_screen(CLOSE_SCREEN_ARGS_DECL)
+{
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ struct sna *sna = to_sna(scrn);
+
+ DBG(("%s\n", __FUNCTION__));
+
+ xf86_hide_cursors(scrn);
+ sna_uevent_fini(scrn);
+
+ /* drain the event queues */
+ if (sna_mode_has_pending_events(sna))
+ sna_mode_wakeup(sna);
+
+ if (sna->dri_open) {
+ sna_dri_close(sna, screen);
+ sna->dri_open = false;
+ }
+
+ if (sna->front) {
+ screen->DestroyPixmap(sna->front);
+ sna->front = NULL;
+ }
+
+ drmDropMaster(sna->kgem.fd);
+ scrn->vtSema = FALSE;
+
+ xf86_cursors_fini(screen);
+
+ return TRUE;
+}
+
+static Bool sna_late_close_screen(CLOSE_SCREEN_ARGS_DECL)
+{
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ struct sna *sna = to_sna(scrn);
+ DepthPtr depths;
+ int d;
+
+ DBG(("%s\n", __FUNCTION__));
+
+ sna_accel_close(sna);
+
+ depths = screen->allowedDepths;
+ for (d = 0; d < screen->numDepths; d++)
+ free(depths[d].vids);
+ free(depths);
+
+ free(screen->visuals);
+
+ return TRUE;
+}
+
+static void sna_mode_set(ScrnInfoPtr scrn)
+{
+ struct sna *sna = to_sna(scrn);
+
+ DBG(("%s\n", __FUNCTION__));
+ sna_mode_update(sna);
+}
+
+static Bool
+sna_register_all_privates(void)
+{
+ if (!dixRegisterPrivateKey(&sna_pixmap_key, PRIVATE_PIXMAP,
+ 3*sizeof(void *)))
+ return FALSE;
+
+ if (!dixRegisterPrivateKey(&sna_gc_key, PRIVATE_GC,
+ sizeof(FbGCPrivate)))
+ return FALSE;
+
+ if (!dixRegisterPrivateKey(&sna_glyph_key, PRIVATE_GLYPH,
+ sizeof(struct sna_glyph)))
+ return FALSE;
+
+ if (!dixRegisterPrivateKey(&sna_window_key, PRIVATE_WINDOW,
+ 2*sizeof(void *)))
+ return FALSE;
+
+ return TRUE;
+}
+
+static size_t
+agp_aperture_size(struct pci_device *dev, int gen)
+{
+ return dev->regions[gen < 30 ? 0 : 2].size;
+}
+
+static Bool
+sna_screen_init(SCREEN_INIT_ARGS_DECL)
+{
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ struct sna *sna = to_sna(scrn);
+ VisualPtr visuals;
+ DepthPtr depths;
+ int nvisuals;
+ int ndepths;
+ int rootdepth;
+ VisualID defaultVisual;
+
+ DBG(("%s\n", __FUNCTION__));
+
+ if (!sna_register_all_privates())
+ return FALSE;
+
+ scrn->videoRam = agp_aperture_size(sna->PciInfo, sna->kgem.gen) / 1024;
+
+ miClearVisualTypes();
+ if (!miSetVisualTypes(scrn->depth,
+ miGetDefaultVisualMask(scrn->depth),
+ scrn->rgbBits, scrn->defaultVisual))
+ return FALSE;
+ if (!miSetPixmapDepths())
+ return FALSE;
+
+ rootdepth = 0;
+ if (!miInitVisuals(&visuals, &depths, &nvisuals, &ndepths, &rootdepth,
+ &defaultVisual,
+ ((unsigned long)1 << (scrn->bitsPerPixel - 1)),
+ 8, -1))
+ return FALSE;
+
+ if (!miScreenInit(screen, NULL,
+ scrn->virtualX, scrn->virtualY,
+ scrn->xDpi, scrn->yDpi, 0,
+ rootdepth, ndepths, depths,
+ defaultVisual, nvisuals, visuals))
+ return FALSE;
+
+ if (scrn->bitsPerPixel > 8) {
+ /* Fixup RGB ordering */
+ VisualPtr visual = screen->visuals + screen->numVisuals;
+ while (--visual >= screen->visuals) {
+ if ((visual->class | DynamicClass) == DirectColor) {
+ visual->offsetRed = scrn->offset.red;
+ visual->offsetGreen = scrn->offset.green;
+ visual->offsetBlue = scrn->offset.blue;
+ visual->redMask = scrn->mask.red;
+ visual->greenMask = scrn->mask.green;
+ visual->blueMask = scrn->mask.blue;
+ }
+ }
+ }
+
+ assert(screen->CloseScreen == NULL);
+ screen->CloseScreen = sna_late_close_screen;
+ if (!sna_accel_init(screen, sna)) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "Hardware acceleration initialization failed\n");
+ return FALSE;
+ }
+
+ xf86SetBlackWhitePixels(screen);
+
+ xf86SetBackingStore(screen);
+ xf86SetSilkenMouse(screen);
+ if (!miDCInitialize(screen, xf86GetPointerScreenFuncs()))
+ return FALSE;
+
+ if (xf86_cursors_init(screen, SNA_CURSOR_X, SNA_CURSOR_Y,
+ HARDWARE_CURSOR_TRUECOLOR_AT_8BPP |
+ HARDWARE_CURSOR_BIT_ORDER_MSBFIRST |
+ HARDWARE_CURSOR_INVERT_MASK |
+ HARDWARE_CURSOR_SWAP_SOURCE_AND_MASK |
+ HARDWARE_CURSOR_AND_SOURCE_WITH_MASK |
+ HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_64 |
+ HARDWARE_CURSOR_UPDATE_UNHIDDEN |
+ HARDWARE_CURSOR_ARGB))
+ xf86DrvMsg(scrn->scrnIndex, X_INFO, "HW Cursor enabled\n");
+
+ /* Must force it before EnterVT, so we are in control of VT and
+ * later memory should be bound when allocating, e.g rotate_mem */
+ scrn->vtSema = TRUE;
+
+ sna->BlockHandler = screen->BlockHandler;
+ screen->BlockHandler = sna_block_handler;
+
+ sna->WakeupHandler = screen->WakeupHandler;
+ screen->WakeupHandler = sna_wakeup_handler;
+
+ screen->SaveScreen = xf86SaveScreen;
+ screen->CreateScreenResources = sna_create_screen_resources;
+
+ sna->CloseScreen = screen->CloseScreen;
+ screen->CloseScreen = sna_early_close_screen;
+
+ if (!xf86CrtcScreenInit(screen))
+ return FALSE;
+
+ xf86RandR12SetRotations(screen,
+ RR_Rotate_0 | RR_Rotate_90 | RR_Rotate_180 | RR_Rotate_270 |
+ RR_Reflect_X | RR_Reflect_Y);
+ xf86RandR12SetTransformSupport(screen, TRUE);
+
+ if (!miCreateDefColormap(screen))
+ return FALSE;
+
+ if (!xf86HandleColormaps(screen, 256, 8, sna_load_palette, NULL,
+ CMAP_RELOAD_ON_MODE_SWITCH |
+ CMAP_PALETTED_TRUECOLOR)) {
+ return FALSE;
+ }
+
+ xf86DPMSInit(screen, xf86DPMSSet, 0);
+
+ sna_video_init(sna, screen);
+ if (sna->dri_available)
+ sna->dri_open = sna_dri_open(sna, screen);
+ if (sna->dri_open)
+ xf86DrvMsg(scrn->scrnIndex, X_INFO,
+ "direct rendering: DRI2 Enabled\n");
+
+ if (serverGeneration == 1)
+ xf86ShowUnusedOptions(scrn->scrnIndex, scrn->options);
+
+ sna->suspended = FALSE;
+
+#if HAVE_UDEV
+ sna_uevent_init(scrn);
+#endif
+
+ return TRUE;
+}
+
+static void sna_adjust_frame(ADJUST_FRAME_ARGS_DECL)
+{
+ SCRN_INFO_PTR(arg);
+ DBG(("%s(%d, %d)\n", __FUNCTION__, x, y));
+ sna_mode_adjust_frame(to_sna(scrn), x, y);
+}
+
+static void sna_free_screen(FREE_SCREEN_ARGS_DECL)
+{
+ SCRN_INFO_PTR(arg);
+ struct sna *sna = to_sna(scrn);
+
+ DBG(("%s\n", __FUNCTION__));
+
+ if (sna && ((intptr_t)sna & 1) == 0) {
+ sna_mode_fini(sna);
+ free(sna);
+ }
+ scrn->driverPrivate = NULL;
+
+ sna_close_drm_master(scrn);
+}
+
+/*
+ * This gets called when gaining control of the VT, and from ScreenInit().
+ */
+static Bool sna_enter_vt(VT_FUNC_ARGS_DECL)
+{
+ SCRN_INFO_PTR(arg);
+ struct sna *sna = to_sna(scrn);
+
+ DBG(("%s\n", __FUNCTION__));
+
+ if (drmSetMaster(sna->kgem.fd)) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "drmSetMaster failed: %s\n",
+ strerror(errno));
+ return FALSE;
+ }
+
+ if (!xf86SetDesiredModes(scrn))
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "failed to restore desired modes on VT switch\n");
+
+ sna_mode_disable_unused(sna);
+
+ return TRUE;
+}
+
+static Bool sna_switch_mode(SWITCH_MODE_ARGS_DECL)
+{
+ SCRN_INFO_PTR(arg);
+ DBG(("%s\n", __FUNCTION__));
+ return xf86SetSingleMode(scrn, mode, RR_Rotate_0);
+}
+
+static ModeStatus
+sna_valid_mode(SCRN_ARG_TYPE arg, DisplayModePtr mode, Bool verbose, int flags)
+{
+ return MODE_OK;
+}
+
+#ifndef SUSPEND_SLEEP
+#define SUSPEND_SLEEP 0
+#endif
+#ifndef RESUME_SLEEP
+#define RESUME_SLEEP 0
+#endif
+
+/*
+ * This function is only required if we need to do anything differently from
+ * DoApmEvent() in common/xf86PM.c, including if we want to see events other
+ * than suspend/resume.
+ */
+static Bool sna_pm_event(SCRN_ARG_TYPE arg, pmEvent event, Bool undo)
+{
+ SCRN_INFO_PTR(arg);
+ struct sna *sna = to_sna(scrn);
+
+ DBG(("%s\n", __FUNCTION__));
+
+ switch (event) {
+ case XF86_APM_SYS_SUSPEND:
+ case XF86_APM_CRITICAL_SUSPEND: /*do we want to delay a critical suspend? */
+ case XF86_APM_USER_SUSPEND:
+ case XF86_APM_SYS_STANDBY:
+ case XF86_APM_USER_STANDBY:
+ if (!undo && !sna->suspended) {
+ scrn->LeaveVT(VT_FUNC_ARGS(0));
+ sna->suspended = TRUE;
+ sleep(SUSPEND_SLEEP);
+ } else if (undo && sna->suspended) {
+ sleep(RESUME_SLEEP);
+ scrn->EnterVT(VT_FUNC_ARGS(0));
+ sna->suspended = FALSE;
+ }
+ break;
+ case XF86_APM_STANDBY_RESUME:
+ case XF86_APM_NORMAL_RESUME:
+ case XF86_APM_CRITICAL_RESUME:
+ if (sna->suspended) {
+ sleep(RESUME_SLEEP);
+ scrn->EnterVT(VT_FUNC_ARGS(0));
+ sna->suspended = FALSE;
+ /*
+ * Turn the screen saver off when resuming. This seems to be
+ * needed to stop xscreensaver kicking in (when used).
+ *
+ * XXX DoApmEvent() should probably call this just like
+ * xf86VTSwitch() does. Maybe do it here only in 4.2
+ * compatibility mode.
+ */
+ SaveScreens(SCREEN_SAVER_FORCER, ScreenSaverReset);
+ }
+ break;
+ /* This is currently used for ACPI */
+ case XF86_APM_CAPABILITY_CHANGED:
+ SaveScreens(SCREEN_SAVER_FORCER, ScreenSaverReset);
+ break;
+
+ default:
+ ErrorF("sna_pm_event: received APM event %d\n", event);
+ }
+ return TRUE;
+}
+
+Bool sna_init_scrn(ScrnInfoPtr scrn, int entity_num)
+{
+ DBG(("%s: entity_num=%d\n", __FUNCTION__, entity_num));
+#if defined(USE_GIT_DESCRIBE)
+ xf86DrvMsg(scrn->scrnIndex, X_INFO,
+ "SNA compiled from %s\n", git_version);
+#elif defined(BUILDER_DESCRIPTION)
+ xf86DrvMsg(scrn->scrnIndex, X_INFO,
+ "SNA compiled: %s\n", BUILDER_DESCRIPTION);
+#endif
+#if !NDEBUG
+ xf86DrvMsg(scrn->scrnIndex, X_INFO,
+ "SNA compiled with assertions enabled\n");
+#endif
+#if DEBUG_MEMORY
+ xf86DrvMsg(scrn->scrnIndex, X_INFO,
+ "SNA compiled with memory allocation reporting enabled\n");
+#endif
+#if DEBUG_PIXMAP
+ xf86DrvMsg(scrn->scrnIndex, X_INFO,
+ "SNA compiled with extra pixmap/damage validation\n");
+#endif
+ DBG(("pixman version: %s\n", pixman_version_string()));
+
+ if (sna_device_key == -1)
+ sna_device_key = xf86AllocateEntityPrivateIndex();
+
+ scrn->PreInit = sna_pre_init;
+ scrn->ScreenInit = sna_screen_init;
+ scrn->SwitchMode = sna_switch_mode;
+ scrn->AdjustFrame = sna_adjust_frame;
+ scrn->EnterVT = sna_enter_vt;
+ scrn->LeaveVT = sna_leave_vt;
+ scrn->FreeScreen = sna_free_screen;
+ scrn->ValidMode = sna_valid_mode;
+ scrn->PMEvent = sna_pm_event;
+
+ scrn->ModeSet = sna_mode_set;
+
+ xf86SetEntitySharable(entity_num);
+ xf86SetEntityInstanceForScreen(scrn, entity_num,
+ xf86GetNumEntityInstances(entity_num)-1);
+
+ return TRUE;
+}