xf86-video-intel/src/sna/sna_display.c

8426 lines
220 KiB
C

/*
* Copyright © 2007 Red Hat, Inc.
* Copyright © 2013-2014 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (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 NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Authors:
* Dave Airlie <airlied@redhat.com>
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <poll.h>
#include <ctype.h>
#if HAVE_ALLOCA_H
#include <alloca.h>
#elif defined __GNUC__
#define alloca __builtin_alloca
#elif defined _AIX
#define alloca __alloca
#elif defined _MSC_VER
#include <malloc.h>
#define alloca _alloca
#else
void *alloca(size_t);
#endif
#include "sna.h"
#include "sna_reg.h"
#include "fb/fbpict.h"
#include "intel_options.h"
#include "backlight.h"
#include <xf86Crtc.h>
#include <xf86RandR12.h>
#include <cursorstr.h>
#if XF86_CRTC_VERSION >= 3
#define HAS_GAMMA 1
#else
#define HAS_GAMMA 0
#endif
#include <X11/Xatom.h>
#if defined(HAVE_X11_EXTENSIONS_DPMSCONST_H)
#include <X11/extensions/dpmsconst.h>
#else
#define DPMSModeOn 0
#define DPMSModeOff 3
#endif
#include <xf86DDC.h> /* for xf86InterpretEDID */
#include <xf86drm.h>
#ifdef HAVE_VALGRIND
#include <valgrind.h>
#include <memcheck.h>
#endif
/* Minor discrepancy between 32-bit/64-bit ABI in old kernels */
union compat_mode_get_connector{
struct drm_mode_get_connector conn;
uint32_t pad[20];
};
#define KNOWN_MODE_FLAGS ((1<<14)-1)
#ifndef MONITOR_EDID_COMPLETE_RAWDATA
#define MONITOR_EDID_COMPLETE_RAWDATA 1
#endif
#ifndef DEFAULT_DPI
#define DEFAULT_DPI 96
#endif
#define OUTPUT_STATUS_CACHE_MS 15000
#define DRM_MODE_PAGE_FLIP_ASYNC 0x02
#define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2
#define DRM_PLANE_TYPE_OVERLAY 0
#define DRM_PLANE_TYPE_PRIMARY 1
#define DRM_PLANE_TYPE_CURSOR 2
#define LOCAL_IOCTL_MODE_OBJ_GETPROPERTIES DRM_IOWR(0xb9, struct local_mode_obj_get_properties)
struct local_mode_obj_get_properties {
uint64_t props_ptr;
uint64_t prop_values_ptr;
uint32_t count_props;
uint32_t obj_id;
uint32_t obj_type;
uint32_t pad;
};
#define LOCAL_MODE_OBJECT_PLANE 0xeeeeeeee
#if 0
#define __DBG DBG
#else
#define __DBG(x)
#endif
extern XF86ConfigPtr xf86configptr;
struct sna_cursor {
struct sna_cursor *next;
uint32_t *image;
bool transformed;
Rotation rotation;
int ref;
int size;
int last_width;
int last_height;
unsigned handle;
unsigned serial;
unsigned alloc;
};
struct sna_crtc {
unsigned long flags;
xf86CrtcPtr base;
struct drm_mode_modeinfo kmode;
PixmapPtr slave_pixmap;
DamagePtr slave_damage;
struct kgem_bo *bo, *shadow_bo, *client_bo, *cache_bo;
struct sna_cursor *cursor;
unsigned int last_cursor_size;
uint32_t offset;
bool shadow;
bool fallback_shadow;
bool transform;
bool cursor_transform;
bool hwcursor;
bool flip_pending;
RegionRec client_damage; /* XXX overlap with shadow damage? */
uint16_t shadow_bo_width, shadow_bo_height;
uint32_t rotation;
struct plane {
uint32_t id;
struct {
uint32_t prop;
uint32_t supported;
uint32_t current;
} rotation;
} primary, sprite;
uint32_t mode_serial, flip_serial;
uint32_t last_seq, wrap_seq;
struct ust_msc swap;
sna_flip_handler_t flip_handler;
struct kgem_bo *flip_bo;
void *flip_data;
struct list shadow_link;
};
struct sna_property {
drmModePropertyPtr kprop;
int num_atoms; /* if range prop, num_atoms == 1; if enum prop, num_atoms == num_enums + 1 */
Atom *atoms;
};
struct sna_output {
xf86OutputPtr base;
unsigned id;
unsigned serial;
unsigned possible_encoders;
unsigned attached_encoders;
unsigned int is_panel : 1;
unsigned int add_default_modes : 1;
uint32_t edid_idx;
uint32_t edid_blob_id;
uint32_t edid_len;
void *edid_raw;
bool has_panel_limits;
int panel_hdisplay;
int panel_vdisplay;
uint32_t dpms_id;
int dpms_mode;
struct backlight backlight;
int backlight_active_level;
uint32_t last_detect;
uint32_t status;
bool update_properties;
int num_modes;
struct drm_mode_modeinfo *modes;
int num_props;
uint32_t *prop_ids;
uint64_t *prop_values;
struct sna_property *props;
};
enum { /* XXX copied from hw/xfree86/modes/xf86Crtc.c */
OPTION_PREFERRED_MODE,
#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,14,99,1,0)
OPTION_ZOOM_MODES,
#endif
OPTION_POSITION,
OPTION_BELOW,
OPTION_RIGHT_OF,
OPTION_ABOVE,
OPTION_LEFT_OF,
OPTION_ENABLE,
OPTION_DISABLE,
OPTION_MIN_CLOCK,
OPTION_MAX_CLOCK,
OPTION_IGNORE,
OPTION_ROTATE,
OPTION_PANNING,
OPTION_PRIMARY,
OPTION_DEFAULT_MODES,
};
static void sna_crtc_disable_cursor(struct sna *sna, struct sna_crtc *crtc);
static bool is_zaphod(ScrnInfoPtr scrn)
{
return xf86IsEntityShared(scrn->entityList[0]);
}
inline static unsigned count_to_mask(int x)
{
return (1 << x) - 1;
}
static inline struct sna_output *to_sna_output(xf86OutputPtr output)
{
return output->driver_private;
}
static inline int to_connector_id(xf86OutputPtr output)
{
assert(to_sna_output(output));
assert(to_sna_output(output)->id);
return to_sna_output(output)->id;
}
static inline struct sna_crtc *to_sna_crtc(xf86CrtcPtr crtc)
{
return crtc->driver_private;
}
static inline unsigned __sna_crtc_pipe(struct sna_crtc *crtc)
{
return crtc->flags >> 8 & 0xff;
}
static inline unsigned __sna_crtc_id(struct sna_crtc *crtc)
{
return crtc->flags >> 16 & 0xff;
}
static inline bool event_pending(int fd)
{
struct pollfd pfd;
pfd.fd = fd;
pfd.events = POLLIN;
return poll(&pfd, 1, 0) == 1;
}
static bool sna_mode_wait_for_event(struct sna *sna)
{
struct pollfd pfd;
pfd.fd = sna->kgem.fd;
pfd.events = POLLIN;
return poll(&pfd, 1, -1) == 1;
}
static inline uint32_t fb_id(struct kgem_bo *bo)
{
return bo->delta;
}
uint32_t sna_crtc_to_sprite(xf86CrtcPtr crtc)
{
assert(to_sna_crtc(crtc));
return to_sna_crtc(crtc)->sprite.id;
}
bool sna_crtc_is_transformed(xf86CrtcPtr crtc)
{
assert(to_sna_crtc(crtc));
return to_sna_crtc(crtc)->transform;
}
static inline bool msc64(struct sna_crtc *sna_crtc, uint32_t seq, uint64_t *msc)
{
bool record = true;
if (seq < sna_crtc->last_seq) {
if (sna_crtc->last_seq - seq > 0x40000000) {
sna_crtc->wrap_seq++;
DBG(("%s: pipe=%d wrapped; was %u, now %u, wraps=%u\n",
__FUNCTION__, __sna_crtc_pipe(sna_crtc),
sna_crtc->last_seq, seq, sna_crtc->wrap_seq));
} else {
DBG(("%s: pipe=%d msc went backwards; was %u, now %u; ignoring for last_swap\n",
__FUNCTION__, __sna_crtc_pipe(sna_crtc), sna_crtc->last_seq, seq));
record = false;
}
}
*msc = (uint64_t)sna_crtc->wrap_seq << 32 | seq;
return record;
}
uint64_t sna_crtc_record_swap(xf86CrtcPtr crtc,
int tv_sec, int tv_usec, unsigned seq)
{
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
uint64_t msc;
assert(sna_crtc);
if (msc64(sna_crtc, seq, &msc)) {
DBG(("%s: recording last swap on pipe=%d, frame %d [%08llx], time %d.%06d\n",
__FUNCTION__, __sna_crtc_pipe(sna_crtc), seq, (long long)msc,
tv_sec, tv_usec));
sna_crtc->swap.tv_sec = tv_sec;
sna_crtc->swap.tv_usec = tv_usec;
sna_crtc->swap.msc = msc;
} else {
DBG(("%s: swap event on pipe=%d, frame %d [%08llx], time %d.%06d\n",
__FUNCTION__, __sna_crtc_pipe(sna_crtc), seq, (long long)msc,
tv_sec, tv_usec));
}
return msc;
}
const struct ust_msc *sna_crtc_last_swap(xf86CrtcPtr crtc)
{
static struct ust_msc zero;
if (crtc == NULL) {
return &zero;
} else {
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
assert(sna_crtc);
return &sna_crtc->swap;
}
}
#ifndef NDEBUG
static void gem_close(int fd, uint32_t handle);
static void assert_scanout(struct kgem *kgem, struct kgem_bo *bo,
int width, int height)
{
struct drm_mode_fb_cmd info;
assert(bo->scanout);
VG_CLEAR(info);
info.fb_id = fb_id(bo);
assert(drmIoctl(kgem->fd, DRM_IOCTL_MODE_GETFB, &info) == 0);
gem_close(kgem->fd, info.handle);
assert(width == info.width && height == info.height);
}
#else
#define assert_scanout(k, b, w, h)
#endif
static unsigned get_fb(struct sna *sna, struct kgem_bo *bo,
int width, int height)
{
ScrnInfoPtr scrn = sna->scrn;
struct drm_mode_fb_cmd arg;
assert(bo->refcnt);
assert(bo->proxy == NULL);
assert(!bo->snoop);
assert(8*bo->pitch >= width * scrn->bitsPerPixel);
assert(height * bo->pitch <= kgem_bo_size(bo)); /* XXX crtc offset */
if (fb_id(bo)) {
DBG(("%s: reusing fb=%d for handle=%d\n",
__FUNCTION__, fb_id(bo), bo->handle));
assert_scanout(&sna->kgem, bo, width, height);
return fb_id(bo);
}
DBG(("%s: create fb %dx%d@%d/%d\n",
__FUNCTION__, width, height, scrn->depth, scrn->bitsPerPixel));
assert(bo->tiling != I915_TILING_Y || sna->kgem.can_scanout_y);
assert((bo->pitch & 63) == 0);
assert(scrn->vtSema); /* must be master */
VG_CLEAR(arg);
arg.width = width;
arg.height = height;
arg.pitch = bo->pitch;
arg.bpp = scrn->bitsPerPixel;
arg.depth = scrn->depth;
arg.handle = bo->handle;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_ADDFB, &arg)) {
/* Try again with the fancy version */
struct local_mode_fb_cmd2 {
uint32_t fb_id;
uint32_t width, height;
uint32_t pixel_format;
uint32_t flags;
uint32_t handles[4];
uint32_t pitches[4]; /* pitch for each plane */
uint32_t offsets[4]; /* offset of each plane */
uint64_t modifiers[4];
} f;
#define LOCAL_IOCTL_MODE_ADDFB2 DRM_IOWR(0xb8, struct local_mode_fb_cmd2)
memset(&f, 0, sizeof(f));
f.width = width;
f.height = height;
/* XXX interlaced */
f.flags = 1 << 1; /* +modifiers */
f.handles[0] = bo->handle;
f.pitches[0] = bo->pitch;
switch (bo->tiling) {
case I915_TILING_NONE:
break;
case I915_TILING_X:
/* I915_FORMAT_MOD_X_TILED */
f.modifiers[0] = (uint64_t)1 << 56 | 1;
break;
case I915_TILING_Y:
/* I915_FORMAT_MOD_X_TILED */
f.modifiers[0] = (uint64_t)1 << 56 | 2;
break;
}
#define fourcc(a,b,c,d) ((a) | (b) << 8 | (c) << 16 | (d) << 24)
switch (scrn->depth) {
default:
ERR(("%s: unhandled screen format, depth=%d\n",
__FUNCTION__, scrn->depth));
goto fail;
case 8:
f.pixel_format = fourcc('C', '8', ' ', ' ');
break;
case 15:
f.pixel_format = fourcc('X', 'R', '1', '5');
break;
case 16:
f.pixel_format = fourcc('R', 'G', '1', '6');
break;
case 24:
f.pixel_format = fourcc('X', 'R', '2', '4');
break;
case 30:
f.pixel_format = fourcc('X', 'R', '3', '0');
break;
}
#undef fourcc
if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_ADDFB2, &f)) {
fail:
xf86DrvMsg(scrn->scrnIndex, X_ERROR,
"%s: failed to add fb: %dx%d depth=%d, bpp=%d, pitch=%d: %d\n",
__FUNCTION__, width, height,
scrn->depth, scrn->bitsPerPixel, bo->pitch, errno);
return 0;
}
arg.fb_id = f.fb_id;
}
assert(arg.fb_id != 0);
bo->delta = arg.fb_id;
DBG(("%s: attached fb=%d to handle=%d\n",
__FUNCTION__, bo->delta, arg.handle));
bo->scanout = true;
return bo->delta;
}
static uint32_t gem_create(int fd, int size)
{
struct drm_i915_gem_create create;
assert((size & 4095) == 0);
VG_CLEAR(create);
create.handle = 0;
create.size = size;
(void)drmIoctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create);
return create.handle;
}
static void *gem_mmap(int fd, int handle, int size)
{
struct drm_i915_gem_mmap_gtt mmap_arg;
void *ptr;
VG_CLEAR(mmap_arg);
mmap_arg.handle = handle;
if (drmIoctl(fd, DRM_IOCTL_I915_GEM_MMAP_GTT, &mmap_arg))
return NULL;
ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mmap_arg.offset);
if (ptr == MAP_FAILED)
return NULL;
return ptr;
}
static void gem_close(int fd, uint32_t handle)
{
struct drm_gem_close close;
VG_CLEAR(close);
close.handle = handle;
(void)drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close);
}
#define BACKLIGHT_NAME "Backlight"
#define BACKLIGHT_DEPRECATED_NAME "BACKLIGHT"
static Atom backlight_atom, backlight_deprecated_atom;
#if HAVE_UDEV
static void
sna_backlight_uevent(int fd, void *closure)
{
struct sna *sna = closure;
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
int i;
DBG(("%s()\n", __FUNCTION__));
/* Drain the event queue */
while (event_pending(fd)) {
struct udev_device *dev;
DBG(("%s: waiting for uevent\n", __FUNCTION__));
dev = udev_monitor_receive_device(sna->mode.backlight_monitor);
if (dev == NULL)
break;
udev_device_unref(dev);
}
/* Query all backlights for any changes */
DBG(("%s: probing backlights for changes\n", __FUNCTION__));
for (i = 0; i < sna->mode.num_real_output; i++) {
xf86OutputPtr output = config->output[i];
struct sna_output *sna_output = to_sna_output(output);
int val;
if (sna_output->dpms_mode != DPMSModeOn)
continue;
val = backlight_get(&sna_output->backlight);
if (val < 0)
continue;
DBG(("%s(%s): backlight '%s' was %d, now %d\n",
__FUNCTION__, output->name, sna_output->backlight.iface,
sna_output->backlight_active_level, val));
if (val == sna_output->backlight_active_level)
continue;
sna_output->backlight_active_level = val;
if (output->randr_output) {
DBG(("%s(%s): sending change notification\n", __FUNCTION__, output->name));
RRChangeOutputProperty(output->randr_output,
backlight_atom, XA_INTEGER,
32, PropModeReplace, 1, &val,
TRUE, FALSE);
RRChangeOutputProperty(output->randr_output,
backlight_deprecated_atom, XA_INTEGER,
32, PropModeReplace, 1, &val,
TRUE, FALSE);
}
}
DBG(("%s: complete\n", __FUNCTION__));
}
static void sna_backlight_pre_init(struct sna *sna)
{
struct udev *u;
struct udev_monitor *mon;
#if !USE_BACKLIGHT
return;
#endif
u = udev_new();
if (!u)
return;
mon = udev_monitor_new_from_netlink(u, "udev");
if (!mon)
goto free_udev;
if (udev_monitor_filter_add_match_subsystem_devtype(mon, "backlight", NULL))
goto free_monitor;
if (udev_monitor_enable_receiving(mon))
goto free_monitor;
sna->mode.backlight_handler =
xf86AddGeneralHandler(udev_monitor_get_fd(mon),
sna_backlight_uevent, sna);
if (!sna->mode.backlight_handler)
goto free_monitor;
DBG(("%s: installed backlight monitor\n", __FUNCTION__));
sna->mode.backlight_monitor = mon;
return;
free_monitor:
udev_monitor_unref(mon);
free_udev:
udev_unref(u);
}
static void sna_backlight_drain_uevents(struct sna *sna)
{
if (sna->mode.backlight_monitor == NULL)
return;
DBG(("%s()\n", __FUNCTION__));
sna_backlight_uevent(udev_monitor_get_fd(sna->mode.backlight_monitor),
sna);
}
static void sna_backlight_close(struct sna *sna)
{
struct udev *u;
if (sna->mode.backlight_handler == NULL)
return;
xf86RemoveGeneralHandler(sna->mode.backlight_handler);
u = udev_monitor_get_udev(sna->mode.backlight_monitor);
udev_monitor_unref(sna->mode.backlight_monitor);
udev_unref(u);
sna->mode.backlight_handler = NULL;
sna->mode.backlight_monitor = NULL;
}
#else
static void sna_backlight_pre_init(struct sna *sna) { }
static void sna_backlight_drain_uevents(struct sna *sna) { }
static void sna_backlight_close(struct sna *sna) { }
#endif
static void
sna_output_backlight_disable(struct sna_output *sna_output)
{
xf86OutputPtr output = sna_output->base;
xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
"Failed to set backlight %s for output %s, disabling\n",
sna_output->backlight.iface, output->name);
backlight_disable(&sna_output->backlight);
if (output->randr_output) {
RRDeleteOutputProperty(output->randr_output, backlight_atom);
RRDeleteOutputProperty(output->randr_output, backlight_deprecated_atom);
}
}
static int
sna_output_backlight_set(struct sna_output *sna_output, int level)
{
int ret = 0;
DBG(("%s(%s) level=%d, max=%d\n", __FUNCTION__,
sna_output->base->name, level, sna_output->backlight.max));
if (backlight_set(&sna_output->backlight, level)) {
sna_output_backlight_disable(sna_output);
ret = -1;
}
/* Consume the uevent notification now so that we don't misconstrue
* the change latter when we wake up and the output is in a different
* state.
*/
sna_backlight_drain_uevents(to_sna(sna_output->base->scrn));
return ret;
}
static void
sna_output_backlight_off(struct sna_output *sna_output)
{
DBG(("%s(%s)\n", __FUNCTION__, sna_output->base->name));
backlight_off(&sna_output->backlight);
sna_output_backlight_set(sna_output, 0);
}
static void
sna_output_backlight_on(struct sna_output *sna_output)
{
DBG(("%s(%s)\n", __FUNCTION__, sna_output->base->name));
sna_output_backlight_set(sna_output,
sna_output->backlight_active_level);
if (backlight_on(&sna_output->backlight) < 0)
sna_output_backlight_disable(sna_output);
}
static int
sna_output_backlight_get(xf86OutputPtr output)
{
struct sna_output *sna_output = output->driver_private;
int level = backlight_get(&sna_output->backlight);
DBG(("%s(%s) level=%d, max=%d\n", __FUNCTION__,
output->name, level, sna_output->backlight.max));
return level;
}
static char *
has_user_backlight_override(xf86OutputPtr output)
{
struct sna *sna = to_sna(output->scrn);
const char *str;
str = xf86GetOptValString(sna->Options, OPTION_BACKLIGHT);
if (str == NULL)
return NULL;
DBG(("%s(%s) requested %s\n", __FUNCTION__, output->name, str));
if (*str == '\0')
return (char *)str;
if (backlight_exists(str) == BL_NONE) {
xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
"Unrecognised backlight control interface '%s'\n",
str);
return NULL;
}
return strdup(str);
}
static void
sna_output_backlight_init(xf86OutputPtr output)
{
struct sna_output *sna_output = output->driver_private;
struct pci_device *pci;
MessageType from;
char *best_iface;
#if !USE_BACKLIGHT
return;
#endif
from = X_CONFIG;
best_iface = has_user_backlight_override(output);
if (best_iface)
goto done;
/* XXX detect right backlight for multi-GPU/panels */
from = X_PROBED;
pci = xf86GetPciInfoForEntity(to_sna(output->scrn)->pEnt->index);
if (pci != NULL)
best_iface = backlight_find_for_device(pci);
done:
DBG(("%s(%s) opening backlight %s\n", __FUNCTION__,
output->name, best_iface ?: "none"));
sna_output->backlight_active_level =
backlight_open(&sna_output->backlight, best_iface);
DBG(("%s(%s): initial backlight value %d\n",
__FUNCTION__, output->name, sna_output->backlight_active_level));
if (sna_output->backlight_active_level < 0)
return;
switch (sna_output->backlight.type) {
case BL_FIRMWARE: best_iface = (char *)"firmware"; break;
case BL_PLATFORM: best_iface = (char *)"platform"; break;
case BL_RAW: best_iface = (char *)"raw"; break;
default: best_iface = (char *)"unknown"; break;
}
xf86DrvMsg(output->scrn->scrnIndex, from,
"Found backlight control interface %s (type '%s') for output %s\n",
sna_output->backlight.iface, best_iface, output->name);
}
static char *canonical_kmode_name(const struct drm_mode_modeinfo *kmode)
{
char tmp[32], *buf;
int len;
len = sprintf(tmp, "%dx%d%s",
kmode->hdisplay, kmode->vdisplay,
kmode->flags & V_INTERLACE ? "i" : "");
if ((unsigned)len >= sizeof(tmp))
return NULL;
buf = malloc(len + 1);
if (buf == NULL)
return NULL;
return memcpy(buf, tmp, len + 1);
}
static char *get_kmode_name(const struct drm_mode_modeinfo *kmode)
{
if (*kmode->name == '\0')
return canonical_kmode_name(kmode);
return strdup(kmode->name);
}
static DisplayModePtr
mode_from_kmode(ScrnInfoPtr scrn,
const struct drm_mode_modeinfo *kmode,
DisplayModePtr mode)
{
DBG(("kmode: %s, clock=%d, %d %d %d %d %d, %d %d %d %d %d, flags=%x, type=%x\n",
kmode->name, kmode->clock,
kmode->hdisplay, kmode->hsync_start, kmode->hsync_end, kmode->htotal, kmode->hskew,
kmode->vdisplay, kmode->vsync_start, kmode->vsync_end, kmode->vtotal, kmode->vscan,
kmode->flags, kmode->type));
mode->status = MODE_OK;
mode->Clock = kmode->clock;
mode->HDisplay = kmode->hdisplay;
mode->HSyncStart = kmode->hsync_start;
mode->HSyncEnd = kmode->hsync_end;
mode->HTotal = kmode->htotal;
mode->HSkew = kmode->hskew;
mode->VDisplay = kmode->vdisplay;
mode->VSyncStart = kmode->vsync_start;
mode->VSyncEnd = kmode->vsync_end;
mode->VTotal = kmode->vtotal;
mode->VScan = kmode->vscan;
mode->VRefresh = kmode->vrefresh;
mode->Flags = kmode->flags;
mode->name = get_kmode_name(kmode);
if (kmode->type & DRM_MODE_TYPE_DRIVER)
mode->type = M_T_DRIVER;
if (kmode->type & DRM_MODE_TYPE_PREFERRED)
mode->type |= M_T_PREFERRED;
if (mode->status == MODE_OK && kmode->flags & ~KNOWN_MODE_FLAGS)
mode->status = MODE_BAD; /* unknown flags => unhandled */
xf86SetModeCrtc(mode, scrn->adjustFlags);
return mode;
}
static void
mode_to_kmode(struct drm_mode_modeinfo *kmode, DisplayModePtr mode)
{
memset(kmode, 0, sizeof(*kmode));
kmode->clock = mode->Clock;
kmode->hdisplay = mode->HDisplay;
kmode->hsync_start = mode->HSyncStart;
kmode->hsync_end = mode->HSyncEnd;
kmode->htotal = mode->HTotal;
kmode->hskew = mode->HSkew;
kmode->vdisplay = mode->VDisplay;
kmode->vsync_start = mode->VSyncStart;
kmode->vsync_end = mode->VSyncEnd;
kmode->vtotal = mode->VTotal;
kmode->vscan = mode->VScan;
kmode->vrefresh = mode->VRefresh;
kmode->flags = mode->Flags;
if (mode->name)
strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN);
kmode->name[DRM_DISPLAY_MODE_LEN-1] = 0;
}
static void
sna_crtc_force_outputs_on(xf86CrtcPtr crtc)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
int i;
assert(to_sna_crtc(crtc));
DBG(("%s(pipe=%d)\n", __FUNCTION__, sna_crtc_pipe(crtc)));
/* DPMS handling by the kernel is inconsistent, so after setting a
* mode on an output presume that we intend for it to be on, or that
* the kernel will force it on.
*
* So force DPMS to be on for all connected outputs, and restore
* the backlight.
*/
for (i = 0; i < config->num_output; i++) {
xf86OutputPtr output = config->output[i];
if (output->crtc != crtc)
continue;
output->funcs->dpms(output, DPMSModeOn);
}
#if XF86_CRTC_VERSION >= 3
crtc->active = TRUE;
#endif
}
static void
sna_crtc_force_outputs_off(xf86CrtcPtr crtc)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
int i;
assert(to_sna_crtc(crtc));
DBG(("%s(pipe=%d)\n", __FUNCTION__, sna_crtc_pipe(crtc)));
/* DPMS handling by the kernel is inconsistent, so after setting a
* mode on an output presume that we intend for it to be on, or that
* the kernel will force it on.
*
* So force DPMS to be on for all connected outputs, and restore
* the backlight.
*/
for (i = 0; i < config->num_output; i++) {
xf86OutputPtr output = config->output[i];
if (output->crtc != crtc)
continue;
output->funcs->dpms(output, DPMSModeOff);
}
}
static unsigned
rotation_reduce(struct plane *p, unsigned rotation)
{
unsigned unsupported_rotations = rotation & ~p->rotation.supported;
if (unsupported_rotations == 0)
return rotation;
#define RR_Reflect_XY (RR_Reflect_X | RR_Reflect_Y)
if ((unsupported_rotations & RR_Reflect_XY) == RR_Reflect_XY &&
p->rotation.supported& RR_Rotate_180) {
rotation &= ~RR_Reflect_XY;
rotation ^= RR_Rotate_180;
}
if ((unsupported_rotations & RR_Rotate_180) &&
(p->rotation.supported& RR_Reflect_XY) == RR_Reflect_XY) {
rotation ^= RR_Reflect_XY;
rotation &= ~RR_Rotate_180;
}
#undef RR_Reflect_XY
return rotation;
}
static bool
rotation_set(struct sna *sna, struct plane *p, uint32_t desired)
{
#define LOCAL_IOCTL_MODE_OBJ_SETPROPERTY DRM_IOWR(0xbA, struct local_mode_obj_set_property)
struct local_mode_obj_set_property {
uint64_t value;
uint32_t prop_id;
uint32_t obj_id;
uint32_t obj_type;
uint32_t pad;
} prop;
if (desired == p->rotation.current)
return true;
if ((desired & p->rotation.supported) == 0) {
errno = EINVAL;
return false;
}
DBG(("%s: obj=%d, type=%x prop=%d set-rotation=%x\n",
__FUNCTION__, p->id, LOCAL_MODE_OBJECT_PLANE, p->rotation.prop, desired));
assert(p->id);
assert(p->rotation.prop);
VG_CLEAR(prop);
prop.obj_id = p->id;
prop.obj_type = LOCAL_MODE_OBJECT_PLANE;
prop.prop_id = p->rotation.prop;
prop.value = desired;
if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_OBJ_SETPROPERTY, &prop))
return false;
p->rotation.current = desired;
return true;
}
static void
rotation_reset(struct plane *p)
{
if (p->rotation.prop == 0)
return;
p->rotation.current = 0;
}
bool sna_crtc_set_sprite_rotation(xf86CrtcPtr crtc, uint32_t rotation)
{
assert(to_sna_crtc(crtc));
DBG(("%s: CRTC:%d [pipe=%d], sprite=%u set-rotation=%x\n",
__FUNCTION__,
sna_crtc_id(crtc), sna_crtc_pipe(crtc),
to_sna_crtc(crtc)->sprite.id, rotation));
return rotation_set(to_sna(crtc->scrn),
&to_sna_crtc(crtc)->sprite,
rotation_reduce(&to_sna_crtc(crtc)->sprite, rotation));
}
static bool
sna_crtc_apply(xf86CrtcPtr crtc)
{
struct sna *sna = to_sna(crtc->scrn);
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
struct drm_mode_crtc arg;
uint32_t output_ids[32];
int output_count = 0;
int i;
DBG(("%s CRTC:%d [pipe=%d], handle=%d\n", __FUNCTION__,
__sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc),
sna_crtc->bo->handle));
if (!sna_crtc->kmode.clock) {
ERR(("%s(CRTC:%d [pipe=%d]): attempted to set an invalid mode\n",
__FUNCTION__, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc)));
return false;
}
assert(sna->mode.num_real_output < ARRAY_SIZE(output_ids));
sna_crtc_disable_cursor(sna, sna_crtc);
if (!rotation_set(sna, &sna_crtc->primary, sna_crtc->rotation)) {
ERR(("%s: set-primary-rotation failed (rotation-id=%d, rotation=%d) on CRTC:%d [pipe=%d], errno=%d\n",
__FUNCTION__, sna_crtc->primary.rotation.prop, sna_crtc->rotation, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), errno));
sna_crtc->primary.rotation.supported &= ~sna_crtc->rotation;
return false;
}
DBG(("%s: CRTC:%d [pipe=%d] primary rotation set to %x\n",
__FUNCTION__, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), sna_crtc->rotation));
for (i = 0; i < sna->mode.num_real_output; i++) {
xf86OutputPtr output = config->output[i];
/* Make sure we mark the output as off (and save the backlight)
* before the kernel turns it off due to changing the pipe.
* This is necessary as the kernel may turn off the backlight
* and we lose track of the user settings.
*/
if (output->crtc == NULL)
output->funcs->dpms(output, DPMSModeOff);
if (output->crtc != crtc)
continue;
/* Skip over any hotunplugged outputs so that we can
* recover in cases where the previous mode is now
* only partially valid.
*/
if (!to_sna_output(output)->id)
continue;
DBG(("%s: attaching output '%s' %d [%d] to crtc:%d (pipe %d) (possible crtc:%x, possible clones:%x)\n",
__FUNCTION__, output->name, i, to_connector_id(output),
__sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc),
(uint32_t)output->possible_crtcs,
(uint32_t)output->possible_clones));
assert(output->possible_crtcs & (1 << __sna_crtc_pipe(sna_crtc)) ||
is_zaphod(crtc->scrn));
output_ids[output_count] = to_connector_id(output);
if (++output_count == ARRAY_SIZE(output_ids)) {
DBG(("%s: too many outputs (%d) for me!\n",
__FUNCTION__, output_count));
errno = EINVAL;
return false;
}
}
if (output_count == 0) {
DBG(("%s: no outputs\n", __FUNCTION__));
errno = EINVAL;
return false;
}
VG_CLEAR(arg);
arg.crtc_id = __sna_crtc_id(sna_crtc);
arg.fb_id = fb_id(sna_crtc->bo);
if (sna_crtc->transform || sna_crtc->slave_pixmap) {
arg.x = 0;
arg.y = 0;
sna_crtc->offset = 0;
} else {
arg.x = crtc->x;
arg.y = crtc->y;
sna_crtc->offset = arg.y << 16 | arg.x;
}
arg.set_connectors_ptr = (uintptr_t)output_ids;
arg.count_connectors = output_count;
arg.mode = sna_crtc->kmode;
arg.mode_valid = 1;
DBG(("%s: applying crtc [%d, pipe=%d] mode=%dx%d+%d+%d@%d, fb=%d%s%s update to %d outputs [%d...]\n",
__FUNCTION__, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc),
arg.mode.hdisplay,
arg.mode.vdisplay,
arg.x, arg.y,
arg.mode.clock,
arg.fb_id,
sna_crtc->shadow ? " [shadow]" : "",
sna_crtc->transform ? " [transformed]" : "",
output_count, output_count ? output_ids[0] : 0));
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_SETCRTC, &arg))
return false;
sna_crtc->mode_serial++;
sna_crtc_force_outputs_on(crtc);
return true;
}
static bool overlap(const BoxRec *a, const BoxRec *b)
{
if (a->x1 >= b->x2)
return false;
if (a->x2 <= b->x1)
return false;
if (a->y1 >= b->y2)
return false;
if (a->y2 <= b->y1)
return false;
return true;
}
static bool wait_for_shadow(struct sna *sna,
struct sna_pixmap *priv,
unsigned flags)
{
PixmapPtr pixmap = priv->pixmap;
struct kgem_bo *bo, *tmp;
int flip_active;
bool ret = true;
DBG(("%s: flags=%x, flips=%d, handle=%d, shadow=%d\n",
__FUNCTION__, flags, sna->mode.flip_active,
priv->gpu_bo->handle, sna->mode.shadow->handle));
assert(priv->move_to_gpu_data == sna);
assert(sna->mode.shadow != priv->gpu_bo);
if (flags == 0 || pixmap != sna->front || !sna->mode.shadow_damage)
goto done;
if ((flags & MOVE_WRITE) == 0) {
if ((flags & __MOVE_SCANOUT) == 0) {
struct sna_crtc *crtc;
list_for_each_entry(crtc, &sna->mode.shadow_crtc, shadow_link) {
if (overlap(&sna->mode.shadow_region.extents,
&crtc->base->bounds)) {
DrawableRec draw;
RegionRec region;
draw.width = crtc->base->mode.HDisplay;
draw.height = crtc->base->mode.VDisplay;
draw.depth = sna->front->drawable.depth;
draw.bitsPerPixel = sna->front->drawable.bitsPerPixel;
DBG(("%s: copying replaced CRTC: (%d, %d), (%d, %d), handle=%d\n",
__FUNCTION__,
crtc->base->bounds.x1,
crtc->base->bounds.y1,
crtc->base->bounds.x2,
crtc->base->bounds.y2,
crtc->client_bo->handle));
ret &= sna->render.copy_boxes(sna, GXcopy,
&draw, crtc->client_bo, -crtc->base->bounds.x1, -crtc->base->bounds.y1,
&pixmap->drawable, priv->gpu_bo, 0, 0,
&crtc->base->bounds, 1,
0);
region.extents = crtc->base->bounds;
region.data = NULL;
RegionSubtract(&sna->mode.shadow_region, &sna->mode.shadow_region, &region);
}
}
}
return ret;
}
assert(sna->mode.shadow_active);
sna->mode.shadow_enabled = false;
flip_active = sna->mode.flip_active;
if (flip_active) {
struct sna_crtc *crtc;
list_for_each_entry(crtc, &sna->mode.shadow_crtc, shadow_link)
flip_active -= crtc->flip_pending;
DBG(("%s: %d flips still pending, shadow flip_active=%d\n",
__FUNCTION__, sna->mode.flip_active, flip_active));
}
if (flip_active) {
/* raw cmd to avoid setting wedged in the middle of an op */
drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_THROTTLE, 0);
sna->kgem.need_throttle = false;
while (flip_active && sna_mode_wakeup(sna)) {
struct sna_crtc *crtc;
flip_active = sna->mode.flip_active;
list_for_each_entry(crtc, &sna->mode.shadow_crtc, shadow_link)
flip_active -= crtc->flip_pending;
}
DBG(("%s: after waiting %d flips outstanding, flip_active=%d\n",
__FUNCTION__, sna->mode.flip_active, flip_active));
}
bo = sna->mode.shadow;
if (flip_active) {
bo = kgem_create_2d(&sna->kgem,
pixmap->drawable.width,
pixmap->drawable.height,
pixmap->drawable.bitsPerPixel,
priv->gpu_bo->tiling,
CREATE_EXACT | CREATE_SCANOUT);
if (bo != NULL) {
DBG(("%s: replacing still-attached GPU bo handle=%d, flips=%d\n",
__FUNCTION__, priv->gpu_bo->tiling, sna->mode.flip_active));
RegionUninit(&sna->mode.shadow_region);
sna->mode.shadow_region.extents.x1 = 0;
sna->mode.shadow_region.extents.y1 = 0;
sna->mode.shadow_region.extents.x2 = pixmap->drawable.width;
sna->mode.shadow_region.extents.y2 = pixmap->drawable.height;
sna->mode.shadow_region.data = NULL;
} else {
while (sna->mode.flip_active &&
sna_mode_wait_for_event(sna))
sna_mode_wakeup(sna);
bo = sna->mode.shadow;
}
}
sna->mode.shadow_enabled = true;
if (bo->refcnt > 1) {
bo = kgem_create_2d(&sna->kgem,
pixmap->drawable.width,
pixmap->drawable.height,
pixmap->drawable.bitsPerPixel,
priv->gpu_bo->tiling,
CREATE_EXACT | CREATE_SCANOUT);
if (bo != NULL) {
DBG(("%s: replacing exported GPU bo\n",
__FUNCTION__));
RegionUninit(&sna->mode.shadow_region);
sna->mode.shadow_region.extents.x1 = 0;
sna->mode.shadow_region.extents.y1 = 0;
sna->mode.shadow_region.extents.x2 = pixmap->drawable.width;
sna->mode.shadow_region.extents.y2 = pixmap->drawable.height;
sna->mode.shadow_region.data = NULL;
} else
bo = sna->mode.shadow;
}
RegionSubtract(&sna->mode.shadow_region,
&sna->mode.shadow_region,
&sna->mode.shadow_cancel);
while (!list_is_empty(&sna->mode.shadow_crtc)) {
struct sna_crtc *crtc =
list_first_entry(&sna->mode.shadow_crtc, struct sna_crtc, shadow_link);
if (overlap(&crtc->base->bounds,
&sna->mode.shadow_region.extents)) {
RegionRec region;
DrawableRec draw;
draw.width = crtc->base->mode.HDisplay;
draw.height = crtc->base->mode.VDisplay;
draw.depth = sna->front->drawable.depth;
draw.bitsPerPixel = sna->front->drawable.bitsPerPixel;
DBG(("%s: copying replaced CRTC: (%d, %d), (%d, %d), handle=%d\n",
__FUNCTION__,
crtc->base->bounds.x1,
crtc->base->bounds.y1,
crtc->base->bounds.x2,
crtc->base->bounds.y2,
crtc->client_bo->handle));
ret = sna->render.copy_boxes(sna, GXcopy,
&draw, crtc->client_bo, -crtc->base->bounds.x1, -crtc->base->bounds.y1,
&pixmap->drawable, bo, 0, 0,
&crtc->base->bounds, 1,
0);
region.extents = crtc->base->bounds;
region.data = NULL;
RegionSubtract(&sna->mode.shadow_region, &sna->mode.shadow_region, &region);
}
kgem_bo_destroy(&sna->kgem, crtc->client_bo);
crtc->client_bo = NULL;
list_del(&crtc->shadow_link);
}
if (RegionNotEmpty(&sna->mode.shadow_region)) {
DBG(("%s: copying existing GPU damage: %dx(%d, %d), (%d, %d)\n",
__FUNCTION__, region_num_rects(&sna->mode.shadow_region),
sna->mode.shadow_region.extents.x1,
sna->mode.shadow_region.extents.y1,
sna->mode.shadow_region.extents.x2,
sna->mode.shadow_region.extents.y2));
ret = sna->render.copy_boxes(sna, GXcopy,
&pixmap->drawable, priv->gpu_bo, 0, 0,
&pixmap->drawable, bo, 0, 0,
region_rects(&sna->mode.shadow_region),
region_num_rects(&sna->mode.shadow_region),
0);
}
if (priv->cow)
sna_pixmap_undo_cow(sna, priv, 0);
sna_pixmap_unmap(pixmap, priv);
DBG(("%s: setting front pixmap to handle=%d\n", __FUNCTION__, bo->handle));
tmp = priv->gpu_bo;
priv->gpu_bo = bo;
if (bo != sna->mode.shadow)
kgem_bo_destroy(&sna->kgem, sna->mode.shadow);
sna->mode.shadow = tmp;
sna_dri2_pixmap_update_bo(sna, pixmap, bo);
done:
RegionEmpty(&sna->mode.shadow_cancel);
RegionEmpty(&sna->mode.shadow_region);
sna->mode.shadow_dirty = false;
priv->move_to_gpu_data = NULL;
priv->move_to_gpu = NULL;
return ret;
}
bool sna_pixmap_discard_shadow_damage(struct sna_pixmap *priv,
const RegionRec *region)
{
struct sna *sna;
if (priv->move_to_gpu != wait_for_shadow)
return false;
sna = priv->move_to_gpu_data;
if (region) {
DBG(("%s: discarding region %dx[(%d, %d), (%d, %d)] from damage %dx[(%d, %d], (%d, %d)]\n",
__FUNCTION__,
region_num_rects(region),
region->extents.x1, region->extents.y1,
region->extents.x2, region->extents.y2,
region_num_rects(&sna->mode.shadow_region),
sna->mode.shadow_region.extents.x1, sna->mode.shadow_region.extents.y1,
sna->mode.shadow_region.extents.x2, sna->mode.shadow_region.extents.y2));
RegionSubtract(&sna->mode.shadow_region,
&sna->mode.shadow_region,
(RegionPtr)region);
RegionUnion(&sna->mode.shadow_cancel,
&sna->mode.shadow_cancel,
(RegionPtr)region);
} else {
DBG(("%s: discarding all damage %dx[(%d, %d], (%d, %d)]\n",
__FUNCTION__,
region_num_rects(&sna->mode.shadow_region),
sna->mode.shadow_region.extents.x1, sna->mode.shadow_region.extents.y1,
sna->mode.shadow_region.extents.x2, sna->mode.shadow_region.extents.y2));
RegionEmpty(&sna->mode.shadow_region);
RegionUninit(&sna->mode.shadow_cancel);
sna->mode.shadow_cancel.extents.x1 = 0;
sna->mode.shadow_cancel.extents.y1 = 0;
sna->mode.shadow_cancel.extents.x2 = sna->front->drawable.width;
sna->mode.shadow_cancel.extents.y2 = sna->front->drawable.height;
sna->mode.shadow_cancel.data = NULL;
}
return RegionNil(&sna->mode.shadow_region);
}
static void sna_mode_damage(DamagePtr damage, RegionPtr region, void *closure)
{
/* Throw away the rectangles if the region grows too big */
region = DamageRegion(damage);
if (region->data) {
RegionRec dup;
dup = *region;
RegionUninit(&dup);
region->data = NULL;
}
}
static bool sna_mode_enable_shadow(struct sna *sna)
{
ScreenPtr screen = to_screen_from_sna(sna);
DBG(("%s\n", __FUNCTION__));
assert(sna->mode.shadow == NULL);
assert(sna->mode.shadow_damage == NULL);
assert(sna->mode.shadow_active == 0);
assert(!sna->mode.shadow_enabled);
sna->mode.shadow_damage = DamageCreate(sna_mode_damage, NULL,
DamageReportRawRegion,
TRUE, screen, sna);
if (!sna->mode.shadow_damage)
return false;
DamageRegister(&sna->front->drawable, sna->mode.shadow_damage);
sna->mode.shadow_enabled = true;
return true;
}
static void sna_mode_disable_shadow(struct sna *sna)
{
struct sna_pixmap *priv;
if (!sna->mode.shadow_damage) {
assert(!sna->mode.shadow_enabled);
return;
}
DBG(("%s\n", __FUNCTION__));
priv = sna_pixmap(sna->front);
if (priv->move_to_gpu == wait_for_shadow)
priv->move_to_gpu(sna, priv, 0);
DamageUnregister(&sna->front->drawable, sna->mode.shadow_damage);
DamageDestroy(sna->mode.shadow_damage);
sna->mode.shadow_damage = NULL;
sna->mode.shadow_enabled = false;
if (sna->mode.shadow) {
kgem_bo_destroy(&sna->kgem, sna->mode.shadow);
sna->mode.shadow = NULL;
}
assert(sna->mode.shadow_active == 0);
sna->mode.shadow_dirty = false;
}
static void sna_crtc_slave_damage(DamagePtr damage, RegionPtr region, void *closure)
{
struct sna_crtc *crtc = closure;
struct sna *sna = to_sna(crtc->base->scrn);
RegionPtr scr;
DBG(("%s: pushing damage [(%d, %d), (%d, %d) x %d] to CRTC [pipe=%d] (%d, %d)\n",
__FUNCTION__,
region->extents.x1, region->extents.y1, region->extents.x2, region->extents.y2,
region_num_rects(region),
__sna_crtc_pipe(crtc), crtc->base->x, crtc->base->y));
assert(crtc->slave_damage == damage);
assert(sna->mode.shadow_damage);
RegionTranslate(region, crtc->base->x, crtc->base->y);
scr = DamageRegion(sna->mode.shadow_damage);
RegionUnion(scr, scr, region);
RegionTranslate(region, -crtc->base->x, -crtc->base->y);
}
static bool sna_crtc_enable_shadow(struct sna *sna, struct sna_crtc *crtc)
{
if (crtc->shadow) {
assert(sna->mode.shadow_damage && sna->mode.shadow_active);
return true;
}
DBG(("%s: enabling for crtc %d\n", __FUNCTION__, __sna_crtc_id(crtc)));
if (!sna->mode.shadow_active) {
if (!sna_mode_enable_shadow(sna))
return false;
assert(sna->mode.shadow_damage);
assert(sna->mode.shadow == NULL);
}
if (crtc->slave_pixmap) {
assert(crtc->slave_damage == NULL);
DBG(("%s: enabling PRIME slave tracking on CRTC %d [pipe=%d], pixmap=%ld\n",
__FUNCTION__, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc), crtc->slave_pixmap->drawable.serialNumber));
crtc->slave_damage = DamageCreate(sna_crtc_slave_damage, NULL,
DamageReportRawRegion, TRUE,
to_screen_from_sna(sna),
crtc);
if (crtc->slave_damage == NULL) {
if (!--sna->mode.shadow_active)
sna_mode_disable_shadow(sna);
return false;
}
DamageRegister(&crtc->slave_pixmap->drawable, crtc->slave_damage);
}
crtc->shadow = true;
sna->mode.shadow_active++;
return true;
}
static void sna_crtc_disable_override(struct sna *sna, struct sna_crtc *crtc)
{
if (crtc->client_bo == NULL)
return;
assert(crtc->client_bo->refcnt > crtc->client_bo->active_scanout);
if (!crtc->transform) {
DrawableRec tmp;
tmp.width = crtc->base->mode.HDisplay;
tmp.height = crtc->base->mode.VDisplay;
tmp.depth = sna->front->drawable.depth;
tmp.bitsPerPixel = sna->front->drawable.bitsPerPixel;
sna->render.copy_boxes(sna, GXcopy,
&tmp, crtc->client_bo, -crtc->base->bounds.x1, -crtc->base->bounds.y1,
&sna->front->drawable, __sna_pixmap_get_bo(sna->front), 0, 0,
&crtc->base->bounds, 1, 0);
list_del(&crtc->shadow_link);
}
kgem_bo_destroy(&sna->kgem, crtc->client_bo);
crtc->client_bo = NULL;
}
static void sna_crtc_disable_shadow(struct sna *sna, struct sna_crtc *crtc)
{
crtc->fallback_shadow = false;
if (!crtc->shadow)
return;
DBG(("%s: disabling for crtc %d\n", __FUNCTION__, __sna_crtc_id(crtc)));
assert(sna->mode.shadow_active > 0);
if (crtc->slave_damage) {
assert(crtc->slave_pixmap);
DamageUnregister(&crtc->slave_pixmap->drawable, crtc->slave_damage);
DamageDestroy(crtc->slave_damage);
crtc->slave_damage = NULL;
}
sna_crtc_disable_override(sna, crtc);
if (!--sna->mode.shadow_active)
sna_mode_disable_shadow(sna);
crtc->shadow = false;
}
static void
__sna_crtc_disable(struct sna *sna, struct sna_crtc *sna_crtc)
{
sna_crtc->mode_serial++;
sna_crtc_disable_cursor(sna, sna_crtc);
rotation_set(sna, &sna_crtc->primary, RR_Rotate_0);
sna_crtc_disable_shadow(sna, sna_crtc);
if (sna_crtc->bo) {
DBG(("%s: releasing handle=%d from scanout, active=%d\n",
__FUNCTION__,sna_crtc->bo->handle, sna_crtc->bo->active_scanout-1));
assert(sna_crtc->flags & CRTC_ON);
assert(sna_crtc->bo->active_scanout);
assert(sna_crtc->bo->refcnt >= sna_crtc->bo->active_scanout);
sna_crtc->bo->active_scanout--;
kgem_bo_destroy(&sna->kgem, sna_crtc->bo);
sna_crtc->bo = NULL;
sna_crtc->flags &= ~CRTC_ON;
if (sna->mode.hidden) {
sna->mode.hidden--;
assert(sna->mode.hidden);
assert(sna->mode.front_active == 0);
} else {
assert(sna->mode.front_active);
sna->mode.front_active--;
}
sna->mode.dirty = true;
}
if (sna_crtc->shadow_bo) {
kgem_bo_destroy(&sna->kgem, sna_crtc->shadow_bo);
sna_crtc->shadow_bo = NULL;
}
if (sna_crtc->transform) {
assert(sna->mode.rr_active);
sna->mode.rr_active--;
sna_crtc->transform = false;
}
sna_crtc->cursor_transform = false;
sna_crtc->hwcursor = true;
assert(!sna_crtc->shadow);
}
static void
sna_crtc_disable(xf86CrtcPtr crtc, bool force)
{
struct sna *sna = to_sna(crtc->scrn);
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
struct drm_mode_crtc arg;
if (sna_crtc == NULL)
return;
if (!force && sna_crtc->bo == NULL)
return;
DBG(("%s: disabling crtc [%d, pipe=%d], force?=%d\n", __FUNCTION__,
__sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), force));
sna_crtc_force_outputs_off(crtc);
memset(&arg, 0, sizeof(arg));
arg.crtc_id = __sna_crtc_id(sna_crtc);
(void)drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_SETCRTC, &arg);
__sna_crtc_disable(sna, sna_crtc);
}
static void update_flush_interval(struct sna *sna)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
int i, max_vrefresh = 0;
DBG(("%s: front_active=%d\n", __FUNCTION__, sna->mode.front_active));
for (i = 0; i < sna->mode.num_real_crtc; i++) {
xf86CrtcPtr crtc = config->crtc[i];
assert(to_sna_crtc(crtc) != NULL);
if (!crtc->enabled) {
DBG(("%s: CRTC:%d (pipe %d) disabled\n",
__FUNCTION__,i, sna_crtc_pipe(crtc)));
assert(to_sna_crtc(crtc)->bo == NULL);
continue;
}
if (to_sna_crtc(crtc)->bo == NULL) {
DBG(("%s: CRTC:%d (pipe %d) turned off\n",
__FUNCTION__,i, sna_crtc_pipe(crtc)));
continue;
}
DBG(("%s: CRTC:%d (pipe %d) vrefresh=%f\n",
__FUNCTION__, i, sna_crtc_pipe(crtc),
xf86ModeVRefresh(&crtc->mode)));
max_vrefresh = max(max_vrefresh, xf86ModeVRefresh(&crtc->mode));
}
if (max_vrefresh == 0) {
assert(sna->mode.front_active == 0);
sna->vblank_interval = 0;
} else
sna->vblank_interval = 1000 / max_vrefresh; /* Hz -> ms */
DBG(("max_vrefresh=%d, vblank_interval=%d ms\n",
max_vrefresh, sna->vblank_interval));
}
static struct kgem_bo *sna_create_bo_for_fbcon(struct sna *sna,
const struct drm_mode_fb_cmd *fbcon)
{
struct drm_gem_flink flink;
struct kgem_bo *bo;
int ret;
/* Create a new reference for the fbcon so that we can track it
* using a normal bo and so that when we call gem_close on it we
* delete our reference and not fbcon's!
*/
VG_CLEAR(flink);
flink.handle = fbcon->handle;
ret = drmIoctl(sna->kgem.fd, DRM_IOCTL_GEM_FLINK, &flink);
if (ret)
return NULL;
bo = kgem_create_for_name(&sna->kgem, flink.name);
if (bo == NULL)
return NULL;
bo->pitch = fbcon->pitch;
return bo;
}
/* Copy the current framebuffer contents into the front-buffer for a seamless
* transition from e.g. plymouth.
*/
void sna_copy_fbcon(struct sna *sna)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
struct drm_mode_fb_cmd fbcon;
PixmapRec scratch;
struct sna_pixmap *priv;
struct kgem_bo *bo;
BoxRec box;
bool ok;
int sx, sy;
int dx, dy;
int i;
if (wedged(sna) || isGPU(sna->scrn))
return;
DBG(("%s\n", __FUNCTION__));
assert((sna->flags & SNA_IS_HOSTED) == 0);
priv = sna_pixmap_move_to_gpu(sna->front, MOVE_WRITE | __MOVE_SCANOUT);
if (priv == NULL)
return;
/* Scan the connectors for a framebuffer and assume that is the fbcon */
VG_CLEAR(fbcon);
fbcon.fb_id = 0;
for (i = 0; i < sna->mode.num_real_crtc; i++) {
struct sna_crtc *crtc = to_sna_crtc(config->crtc[i]);
struct drm_mode_crtc mode;
assert(crtc != NULL);
VG_CLEAR(mode);
mode.crtc_id = __sna_crtc_id(crtc);
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode))
continue;
if (!mode.fb_id)
continue;
fbcon.fb_id = mode.fb_id;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETFB, &fbcon)) {
fbcon.fb_id = 0;
continue;
}
break;
}
if (fbcon.fb_id == 0) {
DBG(("%s: no fbcon found\n", __FUNCTION__));
return;
}
if (fbcon.fb_id == fb_id(priv->gpu_bo)) {
DBG(("%s: fb already installed as scanout\n", __FUNCTION__));
return;
}
DBG(("%s: found fbcon, size=%dx%d, depth=%d, bpp=%d\n",
__FUNCTION__, fbcon.width, fbcon.height, fbcon.depth, fbcon.bpp));
bo = sna_create_bo_for_fbcon(sna, &fbcon);
if (bo == NULL)
return;
DBG(("%s: fbcon handle=%d\n", __FUNCTION__, bo->handle));
scratch.drawable.width = fbcon.width;
scratch.drawable.height = fbcon.height;
scratch.drawable.depth = fbcon.depth;
scratch.drawable.bitsPerPixel = fbcon.bpp;
scratch.devPrivate.ptr = NULL;
box.x1 = box.y1 = 0;
box.x2 = min(fbcon.width, sna->front->drawable.width);
box.y2 = min(fbcon.height, sna->front->drawable.height);
sx = dx = 0;
if (box.x2 < (uint16_t)fbcon.width)
sx = (fbcon.width - box.x2) / 2;
if (box.x2 < sna->front->drawable.width)
dx = (sna->front->drawable.width - box.x2) / 2;
sy = dy = 0;
if (box.y2 < (uint16_t)fbcon.height)
sy = (fbcon.height - box.y2) / 2;
if (box.y2 < sna->front->drawable.height)
dy = (sna->front->drawable.height - box.y2) / 2;
ok = sna->render.copy_boxes(sna, GXcopy,
&scratch.drawable, bo, sx, sy,
&sna->front->drawable, priv->gpu_bo, dx, dy,
&box, 1, 0);
if (!DAMAGE_IS_ALL(priv->gpu_damage))
sna_damage_add_box(&priv->gpu_damage, &box);
kgem_bo_destroy(&sna->kgem, bo);
#if ABI_VIDEODRV_VERSION >= SET_ABI_VERSION(10, 0)
to_screen_from_sna(sna)->canDoBGNoneRoot = ok;
#endif
}
static bool use_shadow(struct sna *sna, xf86CrtcPtr crtc)
{
RRTransformPtr transform;
PictTransform crtc_to_fb;
struct pict_f_transform f_crtc_to_fb, f_fb_to_crtc;
unsigned pitch_limit;
BoxRec b;
assert(sna->scrn->virtualX && sna->scrn->virtualY);
if (sna->flags & SNA_FORCE_SHADOW) {
DBG(("%s: forcing shadow\n", __FUNCTION__));
return true;
}
if (to_sna_crtc(crtc)->fallback_shadow) {
DBG(("%s: fallback shadow\n", __FUNCTION__));
return true;
}
if (sna->flags & SNA_TEAR_FREE && to_sna_crtc(crtc)->slave_pixmap) {
DBG(("%s: TearFree shadow required\n", __FUNCTION__));
return true;
}
if (sna->scrn->virtualX > sna->mode.max_crtc_width ||
sna->scrn->virtualY > sna->mode.max_crtc_height) {
DBG(("%s: framebuffer too large (%dx%d) > (%dx%d)\n",
__FUNCTION__,
sna->scrn->virtualX, sna->scrn->virtualY,
sna->mode.max_crtc_width, sna->mode.max_crtc_height));
return true;
}
if (!isGPU(sna->scrn)) {
struct sna_pixmap *priv;
priv = sna_pixmap_force_to_gpu(sna->front, MOVE_READ | __MOVE_SCANOUT);
if (priv == NULL)
return true; /* maybe we can create a bo for the scanout? */
if (sna->kgem.gen == 071)
pitch_limit = priv->gpu_bo->tiling ? 16 * 1024 : 32 * 1024;
else if ((sna->kgem.gen >> 3) > 4)
pitch_limit = 32 * 1024;
else if ((sna->kgem.gen >> 3) == 4)
pitch_limit = priv->gpu_bo->tiling ? 16 * 1024 : 32 * 1024;
else if ((sna->kgem.gen >> 3) == 3)
pitch_limit = priv->gpu_bo->tiling ? 8 * 1024 : 16 * 1024;
else
pitch_limit = 8 * 1024;
DBG(("%s: gpu bo handle=%d tiling=%d pitch=%d, limit=%d\n", __FUNCTION__, priv->gpu_bo->handle, priv->gpu_bo->tiling, priv->gpu_bo->pitch, pitch_limit));
if (priv->gpu_bo->pitch > pitch_limit)
return true;
if (priv->gpu_bo->tiling && sna->flags & SNA_LINEAR_FB) {
DBG(("%s: gpu bo is tiled, need linear, forcing shadow\n", __FUNCTION__));
return true;
}
}
transform = NULL;
if (crtc->transformPresent)
transform = &crtc->transform;
if (RRTransformCompute(crtc->x, crtc->y,
crtc->mode.HDisplay, crtc->mode.VDisplay,
crtc->rotation, transform,
&crtc_to_fb,
&f_crtc_to_fb,
&f_fb_to_crtc)) {
bool needs_transform = true;
unsigned rotation = rotation_reduce(&to_sna_crtc(crtc)->primary, crtc->rotation);
DBG(("%s: natively supported rotation? rotation=%x & supported=%x == %d\n",
__FUNCTION__, crtc->rotation, to_sna_crtc(crtc)->primary.rotation.supported,
!!(crtc->rotation & to_sna_crtc(crtc)->primary.rotation.supported)));
if (to_sna_crtc(crtc)->primary.rotation.supported & rotation)
needs_transform = RRTransformCompute(crtc->x, crtc->y,
crtc->mode.HDisplay, crtc->mode.VDisplay,
RR_Rotate_0, transform,
NULL, NULL, NULL);
if (needs_transform) {
DBG(("%s: RandR transform present\n", __FUNCTION__));
return true;
}
}
/* And finally check that it is entirely visible */
b.x1 = b.y1 = 0;
b.x2 = crtc->mode.HDisplay;
b.y2 = crtc->mode.VDisplay;
pixman_f_transform_bounds(&f_crtc_to_fb, &b);
DBG(("%s? bounds (%d, %d), (%d, %d), framebufer %dx%d\n",
__FUNCTION__, b.x1, b.y1, b.x2, b.y2,
sna->scrn->virtualX, sna->scrn->virtualY));
if (b.x1 < 0 || b.y1 < 0 ||
b.x2 > sna->scrn->virtualX ||
b.y2 > sna->scrn->virtualY) {
DBG(("%s: scanout is partly outside the framebuffer\n",
__FUNCTION__));
return true;
}
return false;
}
static void set_shadow(struct sna *sna, RegionPtr region)
{
struct sna_pixmap *priv = sna_pixmap(sna->front);
assert(priv->gpu_bo);
assert(sna->mode.shadow);
DBG(("%s: waiting for region %dx[(%d, %d), (%d, %d)], front handle=%d, shadow handle=%d\n",
__FUNCTION__,
region_num_rects(region),
region->extents.x1, region->extents.y1,
region->extents.x2, region->extents.y2,
priv->gpu_bo->handle, sna->mode.shadow->handle));
assert(priv->pinned & PIN_SCANOUT);
assert((priv->pinned & PIN_PRIME) == 0);
assert(sna->mode.shadow != priv->gpu_bo);
RegionCopy(&sna->mode.shadow_region, region);
priv->move_to_gpu = wait_for_shadow;
priv->move_to_gpu_data = sna;
}
static struct kgem_bo *
get_scanout_bo(struct sna *sna, PixmapPtr pixmap)
{
struct sna_pixmap *priv;
priv = sna_pixmap_force_to_gpu(pixmap, MOVE_READ | __MOVE_SCANOUT);
if (!priv)
return NULL;
if (priv->gpu_bo->pitch & 63) {
struct kgem_bo *tmp;
BoxRec b;
DBG(("%s: converting to scanout bo due to bad pitch [%d]\n",
__FUNCTION__, priv->gpu_bo->pitch));
if (priv->pinned) {
DBG(("%s: failed as the Pixmap is already pinned [%x]\n",
__FUNCTION__, priv->pinned));
return NULL;
}
tmp = kgem_create_2d(&sna->kgem,
pixmap->drawable.width,
pixmap->drawable.height,
sna->scrn->bitsPerPixel,
priv->gpu_bo->tiling,
CREATE_EXACT | CREATE_SCANOUT);
if (tmp == NULL) {
DBG(("%s: allocation failed\n", __FUNCTION__));
return NULL;
}
b.x1 = 0;
b.y1 = 0;
b.x2 = pixmap->drawable.width;
b.y2 = pixmap->drawable.height;
if (sna->render.copy_boxes(sna, GXcopy,
&pixmap->drawable, priv->gpu_bo, 0, 0,
&pixmap->drawable, tmp, 0, 0,
&b, 1, COPY_LAST)) {
DBG(("%s: copy failed\n", __FUNCTION__));
kgem_bo_destroy(&sna->kgem, tmp);
return NULL;
}
kgem_bo_destroy(&sna->kgem, priv->gpu_bo);
priv->gpu_bo = tmp;
}
priv->pinned |= PIN_SCANOUT;
return priv->gpu_bo;
}
static void shadow_clear(struct sna *sna,
PixmapPtr front, struct kgem_bo *bo,
xf86CrtcPtr crtc)
{
bool ok = false;
if (!wedged(sna))
ok = sna->render.fill_one(sna, front, bo, 0,
0, 0, crtc->mode.HDisplay, crtc->mode.VDisplay,
GXclear);
if (!ok) {
void *ptr = kgem_bo_map__gtt(&sna->kgem, bo);
if (ptr)
memset(ptr, 0, bo->pitch * crtc->mode.HDisplay);
}
sna->mode.shadow_dirty = true;
}
static bool rr_active(xf86CrtcPtr crtc)
{
return crtc->transformPresent || crtc->rotation != RR_Rotate_0;
}
static struct kgem_bo *sna_crtc_attach(xf86CrtcPtr crtc)
{
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
ScrnInfoPtr scrn = crtc->scrn;
struct sna *sna = to_sna(scrn);
struct kgem_bo *bo;
if (sna_crtc->transform) {
assert(sna->mode.rr_active);
sna_crtc->transform = false;
sna->mode.rr_active--;
}
sna_crtc->rotation = RR_Rotate_0;
if (use_shadow(sna, crtc)) {
PixmapPtr front;
unsigned long tiled_limit;
int tiling;
force_shadow:
if (!sna_crtc_enable_shadow(sna, sna_crtc)) {
DBG(("%s: failed to enable crtc shadow\n", __FUNCTION__));
return NULL;
}
DBG(("%s: attaching to per-crtc pixmap %dx%d\n",
__FUNCTION__, crtc->mode.HDisplay, crtc->mode.VDisplay));
bo = sna_crtc->shadow_bo;
if (bo) {
if (sna_crtc->shadow_bo_width == crtc->mode.HDisplay &&
sna_crtc->shadow_bo_height == crtc->mode.VDisplay) {
DBG(("%s: reusing current shadow bo handle=%d\n",
__FUNCTION__, bo->handle));
goto out_shadow;
}
kgem_bo_destroy(&sna->kgem, bo);
sna_crtc->shadow_bo = NULL;
}
tiling = I915_TILING_X;
if (sna->kgem.gen == 071)
tiled_limit = 16 * 1024 * 8;
else if ((sna->kgem.gen >> 3) > 4)
tiled_limit = 32 * 1024 * 8;
else if ((sna->kgem.gen >> 3) == 4)
tiled_limit = 16 * 1024 * 8;
else
tiled_limit = 8 * 1024 * 8;
if ((unsigned long)crtc->mode.HDisplay * scrn->bitsPerPixel > tiled_limit)
tiling = I915_TILING_NONE;
if (sna->flags & SNA_LINEAR_FB)
tiling = I915_TILING_NONE;
bo = kgem_create_2d(&sna->kgem,
crtc->mode.HDisplay, crtc->mode.VDisplay,
scrn->bitsPerPixel,
tiling, CREATE_SCANOUT);
if (bo == NULL) {
DBG(("%s: failed to allocate crtc scanout\n", __FUNCTION__));
return NULL;
}
if (!get_fb(sna, bo, crtc->mode.HDisplay, crtc->mode.VDisplay)) {
DBG(("%s: failed to bind fb for crtc scanout\n", __FUNCTION__));
kgem_bo_destroy(&sna->kgem, bo);
return NULL;
}
front = sna_crtc->slave_pixmap ?: sna->front;
if (__sna_pixmap_get_bo(front) && !rr_active(crtc)) {
BoxRec b;
b.x1 = crtc->x;
b.y1 = crtc->y;
b.x2 = crtc->x + crtc->mode.HDisplay;
b.y2 = crtc->y + crtc->mode.VDisplay;
if (b.x1 < 0)
b.x1 = 0;
if (b.y1 < 0)
b.y1 = 0;
if (b.x2 > scrn->virtualX)
b.x2 = scrn->virtualX;
if (b.y2 > scrn->virtualY)
b.y2 = scrn->virtualY;
if (b.x2 - b.x1 < crtc->mode.HDisplay ||
b.y2 - b.y1 < crtc->mode.VDisplay)
shadow_clear(sna, front, bo, crtc);
if (b.y2 > b.y1 && b.x2 > b.x1) {
DrawableRec tmp;
DBG(("%s: copying onto shadow CRTC: (%d, %d)x(%d, %d) [fb=%dx%d], handle=%d\n",
__FUNCTION__,
b.x1, b.y1,
b.x2-b.x1, b.y2-b.y1,
scrn->virtualX, scrn->virtualY,
bo->handle));
tmp.width = crtc->mode.HDisplay;
tmp.height = crtc->mode.VDisplay;
tmp.depth = front->drawable.depth;
tmp.bitsPerPixel = front->drawable.bitsPerPixel;
if (!sna->render.copy_boxes(sna, GXcopy,
&front->drawable, __sna_pixmap_get_bo(front), 0, 0,
&tmp, bo, -crtc->x, -crtc->y,
&b, 1, COPY_LAST))
shadow_clear(sna, front, bo, crtc);
}
} else
shadow_clear(sna, front, bo, crtc);
sna_crtc->shadow_bo_width = crtc->mode.HDisplay;
sna_crtc->shadow_bo_height = crtc->mode.VDisplay;
sna_crtc->shadow_bo = bo;
out_shadow:
sna_crtc->transform = true;
sna->mode.rr_active++;
return kgem_bo_reference(bo);
} else {
if (sna_crtc->shadow_bo) {
kgem_bo_destroy(&sna->kgem, sna_crtc->shadow_bo);
sna_crtc->shadow_bo = NULL;
}
if (sna_crtc->slave_pixmap) {
DBG(("%s: attaching to scanout pixmap\n", __FUNCTION__));
bo = get_scanout_bo(sna, sna_crtc->slave_pixmap);
if (bo == NULL) {
DBG(("%s: failed to pin crtc scanout\n", __FUNCTION__));
sna_crtc->fallback_shadow = true;
goto force_shadow;
}
if (!get_fb(sna, bo,
sna_crtc->slave_pixmap->drawable.width,
sna_crtc->slave_pixmap->drawable.height)) {
DBG(("%s: failed to bind fb for crtc scanout\n", __FUNCTION__));
sna_crtc->fallback_shadow = true;
goto force_shadow;
}
} else {
DBG(("%s: attaching to framebuffer\n", __FUNCTION__));
bo = get_scanout_bo(sna, sna->front);
if (bo == NULL) {
DBG(("%s: failed to pin framebuffer\n", __FUNCTION__));
sna_crtc->fallback_shadow = true;
goto force_shadow;
}
if (!get_fb(sna, bo, scrn->virtualX, scrn->virtualY)) {
DBG(("%s: failed to bind fb for crtc scanout\n", __FUNCTION__));
sna_crtc->fallback_shadow = true;
goto force_shadow;
}
}
if (sna->flags & SNA_TEAR_FREE) {
assert(sna_crtc->slave_pixmap == NULL);
DBG(("%s: enabling TearFree shadow\n", __FUNCTION__));
if (!sna_crtc_enable_shadow(sna, sna_crtc)) {
DBG(("%s: failed to enable crtc shadow\n", __FUNCTION__));
return NULL;
}
if (sna->mode.shadow == NULL && !wedged(sna)) {
RegionRec region;
struct kgem_bo *shadow;
DBG(("%s: creating TearFree shadow bo\n", __FUNCTION__));
region.extents.x1 = 0;
region.extents.y1 = 0;
region.extents.x2 = sna->scrn->virtualX;
region.extents.y2 = sna->scrn->virtualY;
region.data = NULL;
shadow = kgem_create_2d(&sna->kgem,
region.extents.x2,
region.extents.y2,
scrn->bitsPerPixel,
kgem_choose_tiling(&sna->kgem,
I915_TILING_X,
region.extents.x2,
region.extents.y2,
sna->scrn->bitsPerPixel),
CREATE_SCANOUT);
if (shadow == NULL) {
DBG(("%s: failed to allocate TearFree shadow bo\n", __FUNCTION__));
sna_crtc->fallback_shadow = true;
goto force_shadow;
}
if (!get_fb(sna, shadow,
region.extents.x2,
region.extents.y2)) {
DBG(("%s: failed to bind fb for TearFeee shadow\n", __FUNCTION__));
kgem_bo_destroy(&sna->kgem, shadow);
sna_crtc->fallback_shadow = true;
goto force_shadow;
}
assert(__sna_pixmap_get_bo(sna->front) == NULL ||
__sna_pixmap_get_bo(sna->front)->pitch == shadow->pitch);
sna->mode.shadow = shadow;
set_shadow(sna, &region);
}
sna_crtc_disable_override(sna, sna_crtc);
} else
sna_crtc_disable_shadow(sna, sna_crtc);
sna_crtc->rotation = rotation_reduce(&sna_crtc->primary, crtc->rotation);
assert(sna_crtc->primary.rotation.supported & sna_crtc->rotation);
return kgem_bo_reference(bo);
}
}
#define SCALING_EPSILON (1./256)
static bool
is_affine(const struct pixman_f_transform *t)
{
return (fabs(t->m[2][0]) < SCALING_EPSILON &&
fabs(t->m[2][1]) < SCALING_EPSILON);
}
static double determinant(const struct pixman_f_transform *t)
{
return t->m[0][0]*t->m[1][1] - t->m[1][0]*t->m[0][1];
}
static bool
affine_is_pixel_exact(const struct pixman_f_transform *t)
{
double det = t->m[2][2] * determinant(t);
if (fabs (det * det - 1.0) < SCALING_EPSILON) {
if (fabs(t->m[0][1]) < SCALING_EPSILON &&
fabs(t->m[1][0]) < SCALING_EPSILON)
return true;
if (fabs(t->m[0][0]) < SCALING_EPSILON &&
fabs(t->m[1][1]) < SCALING_EPSILON)
return true;
}
return false;
}
static void sna_crtc_randr(xf86CrtcPtr crtc)
{
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
struct pict_f_transform f_crtc_to_fb, f_fb_to_crtc;
PictTransform crtc_to_fb;
PictFilterPtr filter;
xFixed *params;
int nparams;
RRTransformPtr transform;
int needs_transform;
transform = NULL;
if (crtc->transformPresent)
transform = &crtc->transform;
needs_transform =
RRTransformCompute(crtc->x, crtc->y,
crtc->mode.HDisplay, crtc->mode.VDisplay,
crtc->rotation, transform,
&crtc_to_fb,
&f_crtc_to_fb,
&f_fb_to_crtc);
filter = NULL;
params = NULL;
nparams = 0;
if (sna_crtc->transform) {
#ifdef RANDR_12_INTERFACE
if (transform) {
if (transform->nparams) {
params = malloc(transform->nparams * sizeof(xFixed));
if (params) {
memcpy(params, transform->params,
transform->nparams * sizeof(xFixed));
nparams = transform->nparams;
filter = transform->filter;
}
} else
filter = transform->filter;
}
#endif
crtc->transform_in_use = needs_transform;
} else
crtc->transform_in_use = sna_crtc->rotation != RR_Rotate_0;
if (needs_transform) {
sna_crtc->hwcursor = is_affine(&f_fb_to_crtc);
sna_crtc->cursor_transform =
sna_crtc->hwcursor &&
!affine_is_pixel_exact(&f_fb_to_crtc);
} else {
sna_crtc->hwcursor = true;
sna_crtc->cursor_transform = false;
}
DBG(("%s: hwcursor?=%d, cursor_transform?=%d\n",
__FUNCTION__, sna_crtc->hwcursor, sna_crtc->cursor_transform));
crtc->crtc_to_framebuffer = crtc_to_fb;
crtc->f_crtc_to_framebuffer = f_crtc_to_fb;
crtc->f_framebuffer_to_crtc = f_fb_to_crtc;
free(crtc->params);
crtc->params = params;
crtc->nparams = nparams;
crtc->filter = filter;
if (filter) {
crtc->filter_width = filter->width;
crtc->filter_height = filter->height;
} else {
crtc->filter_width = 0;
crtc->filter_height = 0;
}
crtc->bounds.x1 = 0;
crtc->bounds.x2 = crtc->mode.HDisplay;
crtc->bounds.y1 = 0;
crtc->bounds.y2 = crtc->mode.VDisplay;
pixman_f_transform_bounds(&f_crtc_to_fb, &crtc->bounds);
DBG(("%s: transform? %d, bounds (%d, %d), (%d, %d)\n",
__FUNCTION__, crtc->transform_in_use,
crtc->bounds.x1, crtc->bounds.y1,
crtc->bounds.x2, crtc->bounds.y2));
}
static void
sna_crtc_damage(xf86CrtcPtr crtc)
{
ScreenPtr screen = xf86ScrnToScreen(crtc->scrn);
struct sna *sna = to_sna(crtc->scrn);
RegionRec region, *damage;
region.extents = crtc->bounds;
region.data = NULL;
if (region.extents.x1 < 0)
region.extents.x1 = 0;
if (region.extents.y1 < 0)
region.extents.y1 = 0;
if (region.extents.x2 > screen->width)
region.extents.x2 = screen->width;
if (region.extents.y2 > screen->height)
region.extents.y2 = screen->height;
if (region.extents.x2 <= region.extents.x1 ||
region.extents.y2 <= region.extents.y1) {
DBG(("%s: crtc not damaged, all-clipped\n", __FUNCTION__));
return;
}
DBG(("%s: marking crtc %d as completely damaged (%d, %d), (%d, %d)\n",
__FUNCTION__, sna_crtc_id(crtc),
region.extents.x1, region.extents.y1,
region.extents.x2, region.extents.y2));
to_sna_crtc(crtc)->client_damage = region;
assert(sna->mode.shadow_damage && sna->mode.shadow_active);
damage = DamageRegion(sna->mode.shadow_damage);
RegionUnion(damage, damage, &region);
DBG(("%s: damage now %dx[(%d, %d), (%d, %d)]\n",
__FUNCTION__,
region_num_rects(damage),
damage->extents.x1, damage->extents.y1,
damage->extents.x2, damage->extents.y2));
}
static char *outputs_for_crtc(xf86CrtcPtr crtc, char *outputs, int max)
{
struct sna *sna = to_sna(crtc->scrn);
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
int len, i;
for (i = len = 0; i < sna->mode.num_real_output; i++) {
xf86OutputPtr output = config->output[i];
if (output->crtc != crtc)
continue;
len += snprintf(outputs+len, max-len, "%s, ", output->name);
}
assert(len >= 2);
outputs[len-2] = '\0';
return outputs;
}
static const char *rotation_to_str(Rotation rotation)
{
switch (rotation & RR_Rotate_All) {
case 0:
case RR_Rotate_0: return "normal";
case RR_Rotate_90: return "left";
case RR_Rotate_180: return "inverted";
case RR_Rotate_270: return "right";
default: return "unknown";
}
}
static const char *reflection_to_str(Rotation rotation)
{
switch (rotation & RR_Reflect_All) {
case 0: return "none";
case RR_Reflect_X: return "X axis";
case RR_Reflect_Y: return "Y axis";
case RR_Reflect_X | RR_Reflect_Y: return "X and Y axes";
default: return "invalid";
}
}
static Bool
__sna_crtc_set_mode(xf86CrtcPtr crtc)
{
struct sna *sna = to_sna(crtc->scrn);
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
struct kgem_bo *saved_bo, *bo;
uint32_t saved_offset;
bool saved_transform;
bool saved_hwcursor;
bool saved_cursor_transform;
DBG(("%s: CRTC=%d, pipe=%d, hidden?=%d\n", __FUNCTION__,
__sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), sna->mode.hidden));
if (sna->mode.hidden)
return TRUE;
saved_bo = sna_crtc->bo;
saved_transform = sna_crtc->transform;
saved_cursor_transform = sna_crtc->cursor_transform;
saved_hwcursor = sna_crtc->hwcursor;
saved_offset = sna_crtc->offset;
sna_crtc->fallback_shadow = false;
retry: /* Attach per-crtc pixmap or direct */
bo = sna_crtc_attach(crtc);
if (bo == NULL) {
xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
"unable to attach scanout\n");
goto error;
}
/* Prevent recursion when enabling outputs during execbuffer */
if (bo->exec && RQ(bo->rq)->bo == NULL)
_kgem_submit(&sna->kgem);
sna_crtc->bo = bo;
if (!sna_crtc_apply(crtc)) {
int err = errno;
kgem_bo_destroy(&sna->kgem, bo);
if (!sna_crtc->shadow) {
sna_crtc->fallback_shadow = true;
goto retry;
}
xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
"failed to set mode: %s [%d]\n", strerror(err), err);
goto error;
}
sna_crtc->flags |= CRTC_ON;
bo->active_scanout++;
DBG(("%s: marking handle=%d as active=%d (removing %d from scanout, active=%d)\n",
__FUNCTION__, bo->handle, bo->active_scanout,
saved_bo ? saved_bo->handle : 0, saved_bo ? saved_bo->active_scanout - 1: -1));
if (saved_bo) {
assert(saved_bo->active_scanout);
assert(saved_bo->refcnt >= saved_bo->active_scanout);
saved_bo->active_scanout--;
kgem_bo_destroy(&sna->kgem, saved_bo);
}
sna_crtc_randr(crtc);
if (sna_crtc->transform)
sna_crtc_damage(crtc);
if (sna_crtc->cursor && /* Reload cursor if RandR maybe changed */
(!sna_crtc->hwcursor ||
saved_cursor_transform || sna_crtc->cursor_transform ||
sna_crtc->cursor->rotation != crtc->rotation))
sna_crtc_disable_cursor(sna, sna_crtc);
assert(!sna->mode.hidden);
sna->mode.front_active += saved_bo == NULL;
sna->mode.dirty = true;
DBG(("%s: handle=%d, scanout_active=%d, front_active=%d\n",
__FUNCTION__, bo->handle, bo->active_scanout, sna->mode.front_active));
return TRUE;
error:
sna_crtc->offset = saved_offset;
if (sna_crtc->transform) {
assert(sna->mode.rr_active);
sna->mode.rr_active--;
}
if (saved_transform)
sna->mode.rr_active++;
sna_crtc->transform = saved_transform;
sna_crtc->cursor_transform = saved_cursor_transform;
sna_crtc->hwcursor = saved_hwcursor;
sna_crtc->bo = saved_bo;
sna_mode_discover(sna);
return FALSE;
}
static Bool
sna_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
Rotation rotation, int x, int y)
{
struct sna *sna = to_sna(crtc->scrn);
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
struct drm_mode_modeinfo saved_kmode;
char outputs[256];
if (mode->HDisplay == 0 || mode->VDisplay == 0)
return FALSE;
assert(sna_crtc);
xf86DrvMsg(crtc->scrn->scrnIndex, X_INFO,
"switch to mode %dx%d@%.1f on %s using pipe %d, position (%d, %d), rotation %s, reflection %s\n",
mode->HDisplay, mode->VDisplay, xf86ModeVRefresh(mode),
outputs_for_crtc(crtc, outputs, sizeof(outputs)), __sna_crtc_pipe(sna_crtc),
x, y, rotation_to_str(rotation), reflection_to_str(rotation));
assert(mode->HDisplay <= sna->mode.max_crtc_width &&
mode->VDisplay <= sna->mode.max_crtc_height);
#if HAS_GAMMA
drmModeCrtcSetGamma(sna->kgem.fd, __sna_crtc_id(sna_crtc),
crtc->gamma_size,
crtc->gamma_red,
crtc->gamma_green,
crtc->gamma_blue);
#endif
saved_kmode = sna_crtc->kmode;
mode_to_kmode(&sna_crtc->kmode, mode);
if (__sna_crtc_set_mode(crtc))
return TRUE;
sna_crtc->kmode = saved_kmode;
return FALSE;
}
static void
sna_crtc_dpms(xf86CrtcPtr crtc, int mode)
{
DBG(("%s(pipe %d, dpms mode -> %d):= active=%d\n",
__FUNCTION__, sna_crtc_pipe(crtc), mode, mode == DPMSModeOn));
if (mode == DPMSModeOn && crtc->enabled) {
if (__sna_crtc_set_mode(crtc))
update_flush_interval(to_sna(crtc->scrn));
else
mode = DPMSModeOff;
}
if (mode != DPMSModeOn)
sna_crtc_disable(crtc, false);
}
void sna_mode_adjust_frame(struct sna *sna, int x, int y)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
xf86CrtcPtr crtc;
int saved_x, saved_y;
if ((unsigned)config->compat_output >= config->num_output)
return;
crtc = config->output[config->compat_output]->crtc;
if (crtc == NULL || !crtc->enabled)
return;
if (crtc->x == x && crtc->y == y)
return;
saved_x = crtc->x;
saved_y = crtc->y;
crtc->x = x;
crtc->y = y;
if (to_sna_crtc(crtc) && !__sna_crtc_set_mode(crtc)) {
crtc->x = saved_x;
crtc->y = saved_y;
}
}
static void
sna_crtc_gamma_set(xf86CrtcPtr crtc,
CARD16 *red, CARD16 *green, CARD16 *blue, int size)
{
assert(to_sna_crtc(crtc));
drmModeCrtcSetGamma(to_sna(crtc->scrn)->kgem.fd,
sna_crtc_id(crtc),
size, red, green, blue);
}
static void
sna_crtc_destroy(xf86CrtcPtr crtc)
{
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
if (sna_crtc == NULL)
return;
free(sna_crtc);
crtc->driver_private = NULL;
}
#if HAS_PIXMAP_SHARING
static Bool
sna_crtc_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr pixmap)
{
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
if (sna_crtc == NULL)
return FALSE;
if (pixmap == sna_crtc->slave_pixmap)
return TRUE;
DBG(("%s: CRTC:%d, pipe=%d setting scanout pixmap=%ld\n",
__FUNCTION__, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc),
pixmap ? pixmap->drawable.serialNumber : 0));
/* Disable first so that we can unregister the damage tracking */
sna_crtc_disable_shadow(to_sna(crtc->scrn), sna_crtc);
sna_crtc->slave_pixmap = pixmap;
return TRUE;
}
#endif
static const xf86CrtcFuncsRec sna_crtc_funcs = {
#if XF86_CRTC_VERSION >= 1
.dpms = sna_crtc_dpms,
#endif
.set_mode_major = sna_crtc_set_mode_major,
.gamma_set = sna_crtc_gamma_set,
.destroy = sna_crtc_destroy,
#if HAS_PIXMAP_SHARING
.set_scanout_pixmap = sna_crtc_set_scanout_pixmap,
#endif
};
inline static bool prop_is_rotation(struct drm_mode_get_property *prop)
{
if ((prop->flags & (1 << 5)) == 0)
return false;
if (strcmp(prop->name, "rotation"))
return false;
return true;
}
static int plane_details(struct sna *sna, struct plane *p)
{
struct local_mode_obj_get_properties arg;
uint64_t stack_props[24];
uint32_t *props = (uint32_t *)stack_props;
uint64_t *values = stack_props + 8;
int i, type = DRM_PLANE_TYPE_OVERLAY;
memset(&arg, 0, sizeof(struct local_mode_obj_get_properties));
arg.obj_id = p->id;
arg.obj_type = LOCAL_MODE_OBJECT_PLANE;
arg.props_ptr = (uintptr_t)props;
arg.prop_values_ptr = (uintptr_t)values;
arg.count_props = 16;
if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_OBJ_GETPROPERTIES, &arg))
return -1;
DBG(("%s: object %d (type %x) has %d props\n", __FUNCTION__,
p->id, LOCAL_MODE_OBJECT_PLANE, arg.count_props));
if (arg.count_props > 16) {
props = malloc(2*sizeof(uint64_t)*arg.count_props);
if (props == NULL)
return -1;
values = (uint64_t *)props + arg.count_props;
arg.props_ptr = (uintptr_t)props;
arg.prop_values_ptr = (uintptr_t)values;
if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_OBJ_GETPROPERTIES, &arg))
arg.count_props = 0;
}
VG(VALGRIND_MAKE_MEM_DEFINED(arg.props_ptr, sizeof(uint32_t)*arg.count_props));
VG(VALGRIND_MAKE_MEM_DEFINED(arg.prop_values_ptr, sizeof(uint64_t)*arg.count_props));
for (i = 0; i < arg.count_props; i++) {
struct drm_mode_get_property prop;
memset(&prop, 0, sizeof(prop));
prop.prop_id = props[i];
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPERTY, &prop)) {
ERR(("%s: prop[%d].id=%d GETPROPERTY failed with errno=%d\n",
__FUNCTION__, i, props[i], errno));
continue;
}
DBG(("%s: prop[%d] .id=%ld, .name=%s, .flags=%x, .value=%ld\n", __FUNCTION__, i,
(long)props[i], prop.name, (unsigned)prop.flags, (long)values[i]));
if (strcmp(prop.name, "type") == 0) {
type = values[i];
} else if (prop_is_rotation(&prop)) {
struct drm_mode_property_enum *enums;
p->rotation.prop = props[i];
p->rotation.current = values[i];
DBG(("%s: found rotation property .id=%d, value=%ld, num_enums=%d\n",
__FUNCTION__, prop.prop_id, (long)values[i], prop.count_enum_blobs));
enums = malloc(prop.count_enum_blobs * sizeof(struct drm_mode_property_enum));
if (enums != NULL) {
prop.count_values = 0;
prop.enum_blob_ptr = (uintptr_t)enums;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPERTY, &prop) == 0) {
int j;
/* XXX we assume that the mapping between kernel enum and
* RandR remains fixed for our lifetimes.
*/
VG(VALGRIND_MAKE_MEM_DEFINED(enums, sizeof(*enums)*prop.count_enum_blobs));
for (j = 0; j < prop.count_enum_blobs; j++) {
DBG(("%s: rotation[%d] = %s [%lx]\n", __FUNCTION__,
j, enums[j].name, (long)enums[j].value));
p->rotation.supported |= 1 << enums[j].value;
}
}
free(enums);
}
}
}
if (props != (uint32_t *)stack_props)
free(props);
DBG(("%s: plane=%d type=%d\n", __FUNCTION__, p->id, type));
return type;
}
static void
sna_crtc_find_planes(struct sna *sna, struct sna_crtc *crtc)
{
#define LOCAL_IOCTL_SET_CAP DRM_IOWR(0x0d, struct local_set_cap)
#define LOCAL_IOCTL_MODE_GETPLANERESOURCES DRM_IOWR(0xb5, struct local_mode_get_plane_res)
#define LOCAL_IOCTL_MODE_GETPLANE DRM_IOWR(0xb6, struct local_mode_get_plane)
struct local_set_cap {
uint64_t name;
uint64_t value;
} cap;
struct local_mode_get_plane_res {
uint64_t plane_id_ptr;
uint64_t count_planes;
} r;
uint32_t stack_planes[32];
uint32_t *planes = stack_planes;
int i;
VG_CLEAR(cap);
cap.name = DRM_CLIENT_CAP_UNIVERSAL_PLANES;
cap.value = 1;
(void)drmIoctl(sna->kgem.fd, LOCAL_IOCTL_SET_CAP, &cap);
VG_CLEAR(r);
r.plane_id_ptr = (uintptr_t)planes;
r.count_planes = ARRAY_SIZE(stack_planes);
if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_GETPLANERESOURCES, &r)) {
ERR(("%s: GETPLANERESOURCES failed with errno=%d\n", __FUNCTION__, errno));
return;
}
DBG(("%s: %d planes\n", __FUNCTION__, (int)r.count_planes));
if (r.count_planes > ARRAY_SIZE(stack_planes)) {
planes = malloc(sizeof(uint32_t)*r.count_planes);
if (planes == NULL)
return;
r.plane_id_ptr = (uintptr_t)planes;
if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_GETPLANERESOURCES, &r))
r.count_planes = 0;
}
VG(VALGRIND_MAKE_MEM_DEFINED(planes, sizeof(uint32_t)*r.count_planes));
for (i = 0; i < r.count_planes; i++) {
struct local_mode_get_plane {
uint32_t plane_id;
uint32_t crtc_id;
uint32_t fb_id;
uint32_t possible_crtcs;
uint32_t gamma_size;
uint32_t count_format_types;
uint64_t format_type_ptr;
} p;
struct plane details;
VG_CLEAR(p);
p.plane_id = planes[i];
p.count_format_types = 0;
if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_GETPLANE, &p))
continue;
if ((p.possible_crtcs & (1 << __sna_crtc_pipe(crtc))) == 0)
continue;
DBG(("%s: plane %d is attached to our pipe=%d\n",
__FUNCTION__, planes[i], __sna_crtc_pipe(crtc)));
details.id = p.plane_id;
details.rotation.prop = 0;
details.rotation.supported = RR_Rotate_0;
details.rotation.current = RR_Rotate_0;
switch (plane_details(sna, &details)) {
default:
break;
case DRM_PLANE_TYPE_PRIMARY:
crtc->primary = details;
break;
case DRM_PLANE_TYPE_CURSOR:
break;
case DRM_PLANE_TYPE_OVERLAY:
if (crtc->sprite.id == 0)
crtc->sprite = details;
break;
}
}
if (planes != stack_planes)
free(planes);
}
static void
sna_crtc_init__rotation(struct sna *sna, struct sna_crtc *crtc)
{
crtc->rotation = RR_Rotate_0;
crtc->primary.rotation.supported = RR_Rotate_0;
crtc->primary.rotation.current = RR_Rotate_0;
crtc->sprite.rotation = crtc->primary.rotation;
}
static void
sna_crtc_init__cursor(struct sna *sna, struct sna_crtc *crtc)
{
struct drm_mode_cursor arg;
VG_CLEAR(arg);
arg.flags = DRM_MODE_CURSOR_BO;
arg.crtc_id = __sna_crtc_id(crtc);
arg.width = arg.height = 0;
arg.handle = 0;
(void)drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_CURSOR, &arg);
crtc->hwcursor = true;
}
static bool
sna_crtc_add(ScrnInfoPtr scrn, unsigned id)
{
struct sna *sna = to_sna(scrn);
xf86CrtcPtr crtc;
struct sna_crtc *sna_crtc;
struct drm_i915_get_pipe_from_crtc_id get_pipe;
DBG(("%s(%d)\n", __FUNCTION__, id));
sna_crtc = calloc(sizeof(struct sna_crtc), 1);
if (sna_crtc == NULL)
return false;
assert(id < 256);
sna_crtc->flags = id << 16;
VG_CLEAR(get_pipe);
get_pipe.pipe = 0;
get_pipe.crtc_id = id;
if (drmIoctl(sna->kgem.fd,
DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID,
&get_pipe)) {
free(sna_crtc);
return false;
}
assert((unsigned)get_pipe.pipe < 256);
sna_crtc->flags |= get_pipe.pipe << 8;
if (is_zaphod(scrn) &&
scrn->confScreen->device->screen != get_pipe.pipe) {
free(sna_crtc);
return true;
}
sna_crtc_init__rotation(sna, sna_crtc);
sna_crtc_find_planes(sna, sna_crtc);
DBG(("%s: CRTC:%d [pipe=%d], primary id=%x: supported-rotations=%x, current-rotation=%x, sprite id=%x: supported-rotations=%x, current-rotation=%x\n",
__FUNCTION__, id, get_pipe.pipe,
sna_crtc->primary.id, sna_crtc->primary.rotation.supported, sna_crtc->primary.rotation.current,
sna_crtc->sprite.id, sna_crtc->sprite.rotation.supported, sna_crtc->sprite.rotation.current));
list_init(&sna_crtc->shadow_link);
crtc = xf86CrtcCreate(scrn, &sna_crtc_funcs);
if (crtc == NULL) {
free(sna_crtc);
return false;
}
sna_crtc_init__cursor(sna, sna_crtc);
crtc->driver_private = sna_crtc;
sna_crtc->base = crtc;
DBG(("%s: attached crtc[%d] pipe=%d\n",
__FUNCTION__, id, __sna_crtc_pipe(sna_crtc)));
return true;
}
static bool
is_panel(int type)
{
#define DRM_MODE_CONNECTOR_LVDS 7
#define DRM_MODE_CONNECTOR_eDP 14
#define DRM_MODE_CONNECTOR_DSI 16
return (type == DRM_MODE_CONNECTOR_LVDS ||
type == DRM_MODE_CONNECTOR_eDP ||
type == DRM_MODE_CONNECTOR_DSI);
}
static int
find_property(struct sna *sna, struct sna_output *output, const char *name)
{
struct drm_mode_get_property prop;
int i;
VG_CLEAR(prop);
for (i = 0; i < output->num_props; i++) {
prop.prop_id = output->prop_ids[i];
prop.count_values = 0;
prop.count_enum_blobs = 0;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPERTY, &prop))
continue;
if (strcmp(prop.name, name) == 0)
return i;
}
return -1;
}
static xf86OutputStatus
sna_output_detect(xf86OutputPtr output)
{
struct sna *sna = to_sna(output->scrn);
struct sna_output *sna_output = output->driver_private;
union compat_mode_get_connector compat_conn;
uint32_t now;
DBG(("%s(%s:%d)\n", __FUNCTION__, output->name, sna_output->id));
sna_output->update_properties = false;
if (!sna_output->id) {
DBG(("%s(%s) hiding due to lost connection\n", __FUNCTION__, output->name));
return XF86OutputStatusDisconnected;
}
/* Cache detections for 15s or hotplug event */
now = GetTimeInMillis();
if (sna_output->last_detect != 0 &&
(int32_t)(now - sna_output->last_detect) <= OUTPUT_STATUS_CACHE_MS) {
DBG(("%s(%s) reporting cached status (since %dms): %d\n",
__FUNCTION__, output->name, now - sna_output->last_detect,
sna_output->status));
sna_output->update_properties = true;
return sna_output->status;
}
VG_CLEAR(compat_conn);
compat_conn.conn.connector_id = sna_output->id;
sna_output->num_modes = compat_conn.conn.count_modes = 0; /* reprobe */
compat_conn.conn.count_encoders = 0;
compat_conn.conn.count_props = sna_output->num_props;
compat_conn.conn.props_ptr = (uintptr_t)sna_output->prop_ids;
compat_conn.conn.prop_values_ptr = (uintptr_t)sna_output->prop_values;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCONNECTOR, &compat_conn.conn))
return XF86OutputStatusUnknown;
DBG(("%s(%s): num modes %d -> %d, num props %d -> %d\n",
__FUNCTION__, output->name,
sna_output->num_modes, compat_conn.conn.count_modes,
sna_output->num_props, compat_conn.conn.count_props));
assert(compat_conn.conn.count_props == sna_output->num_props);
while (compat_conn.conn.count_modes && compat_conn.conn.count_modes != sna_output->num_modes) {
struct drm_mode_modeinfo *new_modes;
int old_count;
old_count = sna_output->num_modes;
new_modes = realloc(sna_output->modes,
sizeof(*sna_output->modes)*compat_conn.conn.count_modes);
if (new_modes == NULL)
break;
sna_output->modes = new_modes;
sna_output->num_modes = compat_conn.conn.count_modes;
compat_conn.conn.modes_ptr = (uintptr_t)sna_output->modes;
compat_conn.conn.count_encoders = 0;
compat_conn.conn.count_props = 0;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCONNECTOR, &compat_conn.conn)) {
sna_output->num_modes = min(old_count, sna_output->num_modes);
break;
}
VG(VALGRIND_MAKE_MEM_DEFINED(sna_output->modes, sizeof(*sna_output->modes)*sna_output->num_modes));
}
DBG(("%s(%s): found %d modes, connection status=%d\n",
__FUNCTION__, output->name, sna_output->num_modes, compat_conn.conn.connection));
sna_output->last_detect = now;
switch (compat_conn.conn.connection) {
case DRM_MODE_CONNECTED:
sna_output->status = XF86OutputStatusConnected;
break;
case DRM_MODE_DISCONNECTED:
sna_output->status = XF86OutputStatusDisconnected;
break;
default:
case DRM_MODE_UNKNOWNCONNECTION:
sna_output->status = XF86OutputStatusUnknown;
break;
}
return sna_output->status;
}
static Bool
sna_output_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
{
struct sna_output *sna_output = output->driver_private;
struct sna *sna = to_sna(output->scrn);
if (mode->HDisplay > sna->mode.max_crtc_width)
return MODE_VIRTUAL_X;
if (mode->VDisplay > sna->mode.max_crtc_height)
return MODE_VIRTUAL_Y;
/* Check that we can successfully pin this into the global GTT */
if ((kgem_can_create_2d(&sna->kgem,
mode->HDisplay, mode->VDisplay,
sna->scrn->bitsPerPixel) & KGEM_CAN_CREATE_GTT) == 0)
return MODE_MEM_VIRT;
/*
* If the connector type is a panel, we will use the panel limit to
* verfiy whether the mode is valid.
*/
if (sna_output->has_panel_limits) {
if (mode->HDisplay > sna_output->panel_hdisplay ||
mode->VDisplay > sna_output->panel_vdisplay)
return MODE_PANEL;
}
return MODE_OK;
}
static void
sna_output_attach_edid(xf86OutputPtr output)
{
struct sna *sna = to_sna(output->scrn);
struct sna_output *sna_output = output->driver_private;
struct drm_mode_get_blob blob;
void *old, *raw = NULL;
xf86MonPtr mon = NULL;
if (sna_output->edid_idx == -1)
return;
raw = sna_output->edid_raw;
blob.length = sna_output->edid_len;
if (blob.length && output->MonInfo) {
old = alloca(blob.length);
memcpy(old, raw, blob.length);
} else
old = NULL;
blob.blob_id = sna_output->prop_values[sna_output->edid_idx];
DBG(("%s: attaching EDID id=%d, current=%d\n",
__FUNCTION__, blob.blob_id, sna_output->edid_blob_id));
if (blob.blob_id == sna_output->edid_blob_id && 0) { /* sigh */
if (output->MonInfo) {
/* XXX the property keeps on disappearing... */
RRChangeOutputProperty(output->randr_output,
MakeAtom("EDID", strlen("EDID"), TRUE),
XA_INTEGER, 8, PropModeReplace,
sna_output->edid_len,
sna_output->edid_raw,
FALSE, FALSE);
return;
}
goto skip_read;
}
blob.data = (uintptr_t)raw;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob))
goto done;
DBG(("%s: retrieving blob id=%d, length=%d\n",
__FUNCTION__, blob.blob_id, blob.length));
if (blob.length > sna_output->edid_len) {
raw = realloc(raw, blob.length);
if (raw == NULL)
goto done;
VG(memset(raw, 0, blob.length));
blob.data = (uintptr_t)raw;
}
if (blob.length != sna_output->edid_len &&
drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob))
goto done;
if (old &&
blob.length == sna_output->edid_len &&
memcmp(old, raw, blob.length) == 0) {
assert(sna_output->edid_raw == raw);
sna_output->edid_blob_id = blob.blob_id;
RRChangeOutputProperty(output->randr_output,
MakeAtom("EDID", strlen("EDID"), TRUE),
XA_INTEGER, 8, PropModeReplace,
sna_output->edid_len,
sna_output->edid_raw,
FALSE, FALSE);
return;
}
skip_read:
if (raw) {
mon = xf86InterpretEDID(output->scrn->scrnIndex, raw);
if (mon && blob.length > 128)
mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA;
}
done:
xf86OutputSetEDID(output, mon);
if (raw) {
sna_output->edid_raw = raw;
sna_output->edid_len = blob.length;
sna_output->edid_blob_id = blob.blob_id;
}
}
static void
sna_output_attach_tile(xf86OutputPtr output)
{
#if XF86_OUTPUT_VERSION >= 3
struct sna *sna = to_sna(output->scrn);
struct sna_output *sna_output = output->driver_private;
struct drm_mode_get_blob blob;
struct xf86CrtcTileInfo tile_info, *set = NULL;
char *tile;
int id;
id = find_property(sna, sna_output, "TILE");
DBG(("%s: found? TILE=%d\n", __FUNCTION__, id));
if (id == -1)
goto out;
VG_CLEAR(blob);
blob.blob_id = sna_output->prop_values[id];
blob.length = 0;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob))
goto out;
do {
id = blob.length;
tile = alloca(id + 1);
blob.data = (uintptr_t)tile;
VG(memset(tile, 0, id));
DBG(("%s: reading %d bytes for TILE blob\n", __FUNCTION__, id));
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob))
goto out;
} while (id != blob.length);
tile[blob.length] = '\0'; /* paranoia */
DBG(("%s: TILE='%s'\n", __FUNCTION__, tile));
if (xf86OutputParseKMSTile(tile, blob.length, &tile_info))
set = &tile_info;
out:
xf86OutputSetTile(output, set);
#endif
}
static bool duplicate_mode(DisplayModePtr modes, DisplayModePtr m)
{
if (m == NULL)
return false;
while (modes) {
if (xf86ModesEqual(modes, m))
return true;
modes = modes->next;
}
return false;
}
static struct pixel_count {
int16_t width, height;
} common_16_9[] = {
{ 640, 360 },
{ 720, 405 },
{ 864, 486 },
{ 960, 540 },
{ 1024, 576 },
{ 1280, 720 },
{ 1366, 768 },
{ 1600, 900 },
{ 1920, 1080 },
{ 2048, 1152 },
{ 2560, 1440 },
{ 2880, 1620 },
{ 3200, 1800 },
{ 3840, 2160 },
{ 4096, 2304 },
{ 5120, 2880 },
{ 7680, 4320 },
{ 15360, 8640 },
}, common_16_10[] = {
{ 1280, 800 },
{ 1400, 900 },
{ 1680, 1050 },
{ 1920, 1200 },
{ 2560, 1600 },
};
static DisplayModePtr
default_modes(DisplayModePtr preferred)
{
DisplayModePtr modes;
int n;
#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,6,99,900,0)
modes = xf86GetDefaultModes();
#else
modes = xf86GetDefaultModes(0, 0);
#endif
/* XXX O(n^2) mode list generation :( */
#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,4,99,901,0)
if (preferred) {
DisplayModePtr m;
/* Add a half-resolution mode useful for large panels */
m = xf86GTFMode(preferred->HDisplay/2,
preferred->VDisplay/2,
xf86ModeVRefresh(preferred),
FALSE, FALSE);
if (!duplicate_mode(modes, m))
modes = xf86ModesAdd(modes, m);
else
free(m);
if (preferred->VDisplay * 16 > preferred->HDisplay*9 - preferred->HDisplay/32 &&
preferred->VDisplay * 16 < preferred->HDisplay*9 + preferred->HDisplay/32) {
DBG(("Adding 16:9 modes -- %d < %d > %d\n",
preferred->HDisplay*9 - preferred->HDisplay/32,
preferred->VDisplay * 16,
preferred->HDisplay*9 + preferred->HDisplay/32));
for (n = 0; n < ARRAY_SIZE(common_16_9); n++) {
if (preferred->HDisplay <= common_16_9[n].width ||
preferred->VDisplay <= common_16_9[n].height)
break;
m = xf86GTFMode(common_16_9[n].width,
common_16_9[n].height,
xf86ModeVRefresh(preferred),
FALSE, FALSE);
if (!duplicate_mode(modes, m))
modes = xf86ModesAdd(modes, m);
else
free(m);
}
}
if (preferred->VDisplay * 16 > preferred->HDisplay*10 - preferred->HDisplay/32 &&
preferred->VDisplay * 16 < preferred->HDisplay*10 + preferred->HDisplay/32) {
DBG(("Adding 16:10 modes -- %d < %d > %d\n",
preferred->HDisplay*10 - preferred->HDisplay/32,
preferred->VDisplay * 16,
preferred->HDisplay*10 + preferred->HDisplay/32));
for (n = 0; n < ARRAY_SIZE(common_16_10); n++) {
if (preferred->HDisplay <= common_16_10[n].width ||
preferred->VDisplay <= common_16_10[n].height)
break;
m = xf86GTFMode(common_16_10[n].width,
common_16_10[n].height,
xf86ModeVRefresh(preferred),
FALSE, FALSE);
if (!duplicate_mode(modes, m))
modes = xf86ModesAdd(modes, m);
else
free(m);
}
}
}
#endif
return modes;
}
static DisplayModePtr
sna_output_add_default_modes(xf86OutputPtr output, DisplayModePtr modes)
{
xf86MonPtr mon = output->MonInfo;
DisplayModePtr i, m, preferred = NULL;
int max_x = 0, max_y = 0;
float max_vrefresh = 0.0;
if (mon && GTF_SUPPORTED(mon->features.msc))
return modes;
for (m = modes; m; m = m->next) {
if (m->type & M_T_PREFERRED)
preferred = m;
max_x = max(max_x, m->HDisplay);
max_y = max(max_y, m->VDisplay);
max_vrefresh = max(max_vrefresh, xf86ModeVRefresh(m));
}
max_vrefresh = max(max_vrefresh, 60.0);
max_vrefresh *= (1 + SYNC_TOLERANCE);
m = default_modes(preferred);
xf86ValidateModesSize(output->scrn, m, max_x, max_y, 0);
for (i = m; i; i = i->next) {
if (xf86ModeVRefresh(i) > max_vrefresh)
i->status = MODE_VSYNC;
if (preferred &&
i->HDisplay >= preferred->HDisplay &&
i->VDisplay >= preferred->VDisplay &&
xf86ModeVRefresh(i) >= xf86ModeVRefresh(preferred))
i->status = MODE_PANEL;
}
xf86PruneInvalidModes(output->scrn, &m, FALSE);
return xf86ModesAdd(modes, m);
}
static DisplayModePtr
sna_output_get_modes(xf86OutputPtr output)
{
struct sna_output *sna_output = output->driver_private;
DisplayModePtr Modes = NULL, current = NULL;
int i;
DBG(("%s(%s:%d)\n", __FUNCTION__, output->name, sna_output->id));
assert(sna_output->id);
sna_output_attach_edid(output);
sna_output_attach_tile(output);
if (output->crtc) {
struct drm_mode_crtc mode;
VG_CLEAR(mode);
assert(to_sna_crtc(output->crtc));
mode.crtc_id = sna_crtc_id(output->crtc);
if (drmIoctl(to_sna(output->scrn)->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode) == 0) {
DBG(("%s: CRTC:%d, pipe=%d: has mode?=%d\n", __FUNCTION__,
sna_crtc_id(output->crtc),
sna_crtc_pipe(output->crtc),
mode.mode_valid && mode.mode.clock));
if (mode.mode_valid && mode.mode.clock) {
current = calloc(1, sizeof(DisplayModeRec));
if (current) {
mode_from_kmode(output->scrn, &mode.mode, current);
current->type |= M_T_DRIVER | M_T_PREFERRED;
}
}
}
}
DBG(("%s: adding %d probed modes\n", __FUNCTION__, sna_output->num_modes));
for (i = 0; i < sna_output->num_modes; i++) {
DisplayModePtr mode;
mode = calloc(1, sizeof(DisplayModeRec));
if (mode == NULL)
continue;
mode = mode_from_kmode(output->scrn,
&sna_output->modes[i],
mode);
Modes = xf86ModesAdd(Modes, mode);
if (current && xf86ModesEqual(mode, current)) {
free((void*)current->name);
free(current);
current = NULL;
}
if (current && mode->type & M_T_PREFERRED)
current->type &= ~M_T_PREFERRED;
}
if (current)
Modes = xf86ModesAdd(current, Modes);
/*
* If the connector type is a panel, we will traverse the kernel mode to
* get the panel limit. And then add all the standard modes to fake
* the fullscreen experience.
* If it is incorrect, please fix me.
*/
sna_output->has_panel_limits = false;
if (sna_output->is_panel) {
sna_output->panel_hdisplay = sna_output->panel_vdisplay = 0;
for (i = 0; i < sna_output->num_modes; i++) {
struct drm_mode_modeinfo *m;
m = &sna_output->modes[i];
if (m->hdisplay > sna_output->panel_hdisplay)
sna_output->panel_hdisplay = m->hdisplay;
if (m->vdisplay > sna_output->panel_vdisplay)
sna_output->panel_vdisplay = m->vdisplay;
}
sna_output->has_panel_limits =
sna_output->panel_hdisplay &&
sna_output->panel_vdisplay;
}
if (sna_output->add_default_modes)
Modes = sna_output_add_default_modes(output, Modes);
return Modes;
}
static void
sna_output_destroy(xf86OutputPtr output)
{
struct sna_output *sna_output = output->driver_private;
int i;
if (sna_output == NULL)
return;
free(sna_output->edid_raw);
for (i = 0; i < sna_output->num_props; i++) {
if (sna_output->props[i].kprop == NULL)
continue;
if (sna_output->props[i].atoms) {
if (output->randr_output)
RRDeleteOutputProperty(output->randr_output, sna_output->props[i].atoms[0]);
free(sna_output->props[i].atoms);
}
drmModeFreeProperty(sna_output->props[i].kprop);
}
free(sna_output->props);
free(sna_output->prop_ids);
free(sna_output->prop_values);
backlight_close(&sna_output->backlight);
free(sna_output);
output->driver_private = NULL;
}
static void
sna_output_dpms(xf86OutputPtr output, int dpms)
{
struct sna *sna = to_sna(output->scrn);
struct sna_output *sna_output = output->driver_private;
int old_dpms = sna_output->dpms_mode;
DBG(("%s(%s:%d): dpms=%d (current: %d), active? %d\n",
__FUNCTION__, output->name, sna_output->id,
dpms, sna_output->dpms_mode,
output->crtc != NULL));
if (!sna_output->id)
return;
if (old_dpms == dpms)
return;
/* Record the value of the backlight before turning
* off the display, and reset if after turning it on.
* Order is important as the kernel may record and also
* reset the backlight across DPMS. Hence we need to
* record the value before the kernel modifies it
* and reapply it afterwards.
*/
if (sna_output->backlight.iface && dpms != DPMSModeOn) {
if (old_dpms == DPMSModeOn) {
sna_output->backlight_active_level = sna_output_backlight_get(output);
DBG(("%s: saving current backlight %d\n",
__FUNCTION__, sna_output->backlight_active_level));
}
sna_output->dpms_mode = dpms;
sna_output_backlight_off(sna_output);
}
if (output->crtc &&
drmModeConnectorSetProperty(sna->kgem.fd,
sna_output->id,
sna_output->dpms_id,
dpms))
dpms = old_dpms;
if (sna_output->backlight.iface && dpms == DPMSModeOn) {
DBG(("%s: restoring previous backlight %d\n",
__FUNCTION__, sna_output->backlight_active_level));
sna_output_backlight_on(sna_output);
}
sna_output->dpms_mode = dpms;
}
static bool
sna_property_ignore(drmModePropertyPtr prop)
{
if (!prop)
return true;
/* ignore blob prop */
if (prop->flags & DRM_MODE_PROP_BLOB)
return true;
/* ignore standard property */
if (!strcmp(prop->name, "EDID") ||
!strcmp(prop->name, "DPMS"))
return true;
return false;
}
static void
sna_output_create_ranged_atom(xf86OutputPtr output, Atom *atom,
const char *name, INT32 min, INT32 max,
uint64_t value, Bool immutable)
{
int err;
INT32 atom_range[2];
atom_range[0] = min;
atom_range[1] = max;
*atom = MakeAtom(name, strlen(name), TRUE);
err = RRConfigureOutputProperty(output->randr_output, *atom, FALSE,
TRUE, immutable, 2, atom_range);
if (err != 0)
xf86DrvMsg(output->scrn->scrnIndex, X_WARNING,
"RRConfigureOutputProperty error, %d\n", err);
err = RRChangeOutputProperty(output->randr_output, *atom, XA_INTEGER,
32, PropModeReplace, 1, &value,
FALSE, FALSE);
if (err != 0)
xf86DrvMsg(output->scrn->scrnIndex, X_WARNING,
"RRChangeOutputProperty error, %d\n", err);
}
static void
sna_output_create_resources(xf86OutputPtr output)
{
struct sna *sna = to_sna(output->scrn);
struct sna_output *sna_output = output->driver_private;
int i, j, err;
sna_output->props = calloc(sna_output->num_props,
sizeof(struct sna_property));
if (!sna_output->props)
return;
for (i = 0; i < sna_output->num_props; i++) {
struct sna_property *p = &sna_output->props[i];
p->kprop = drmModeGetProperty(sna->kgem.fd,
sna_output->prop_ids[i]);
if (sna_property_ignore(p->kprop)) {
drmModeFreeProperty(p->kprop);
p->kprop = NULL;
continue;
}
if (p->kprop->flags & DRM_MODE_PROP_RANGE) {
p->num_atoms = 1;
p->atoms = calloc(p->num_atoms, sizeof(Atom));
if (!p->atoms)
continue;
sna_output_create_ranged_atom(output, &p->atoms[0],
p->kprop->name,
p->kprop->values[0],
p->kprop->values[1],
sna_output->prop_values[i],
p->kprop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE);
} else if (p->kprop->flags & DRM_MODE_PROP_ENUM) {
p->num_atoms = p->kprop->count_enums + 1;
p->atoms = calloc(p->num_atoms, sizeof(Atom));
if (!p->atoms)
continue;
p->atoms[0] = MakeAtom(p->kprop->name, strlen(p->kprop->name), TRUE);
for (j = 1; j <= p->kprop->count_enums; j++) {
struct drm_mode_property_enum *e = &p->kprop->enums[j-1];
p->atoms[j] = MakeAtom(e->name, strlen(e->name), TRUE);
}
err = RRConfigureOutputProperty(output->randr_output, p->atoms[0],
FALSE, FALSE,
p->kprop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE,
p->num_atoms - 1, (INT32 *)&p->atoms[1]);
if (err != 0) {
xf86DrvMsg(output->scrn->scrnIndex, X_WARNING,
"RRConfigureOutputProperty error, %d\n", err);
}
for (j = 0; j < p->kprop->count_enums; j++)
if (p->kprop->enums[j].value == sna_output->prop_values[i])
break;
/* there's always a matching value */
err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
XA_ATOM, 32, PropModeReplace, 1, &p->atoms[j+1],
FALSE, FALSE);
if (err != 0) {
xf86DrvMsg(output->scrn->scrnIndex, X_WARNING,
"RRChangeOutputProperty error, %d\n", err);
}
}
}
if (sna_output->backlight.iface) {
/* Set up the backlight property, which takes effect
* immediately and accepts values only within the
* backlight_range.
*/
sna_output_create_ranged_atom(output, &backlight_atom,
BACKLIGHT_NAME, 0,
sna_output->backlight.max,
sna_output->backlight_active_level,
FALSE);
sna_output_create_ranged_atom(output,
&backlight_deprecated_atom,
BACKLIGHT_DEPRECATED_NAME, 0,
sna_output->backlight.max,
sna_output->backlight_active_level,
FALSE);
}
}
static Bool
sna_output_set_property(xf86OutputPtr output, Atom property,
RRPropertyValuePtr value)
{
struct sna *sna = to_sna(output->scrn);
struct sna_output *sna_output = output->driver_private;
int i;
if (property == backlight_atom || property == backlight_deprecated_atom) {
INT32 val;
int ret = 0;
if (value->type != XA_INTEGER || value->format != 32 ||
value->size != 1)
{
return FALSE;
}
val = *(INT32 *)value->data;
DBG(("%s: setting backlight to %d (max=%d)\n",
__FUNCTION__, (int)val, sna_output->backlight.max));
if (val < 0 || val > sna_output->backlight.max)
return FALSE;
sna_output->backlight_active_level = val;
if (sna_output->dpms_mode == DPMSModeOn)
ret = sna_output_backlight_set(sna_output, val);
return ret == 0;
}
if (!sna_output->id)
return TRUE;
for (i = 0; i < sna_output->num_props; i++) {
struct sna_property *p = &sna_output->props[i];
if (p->atoms == NULL || p->atoms[0] != property)
continue;
if (p->kprop->flags & DRM_MODE_PROP_RANGE) {
uint32_t val;
if (value->type != XA_INTEGER || value->format != 32 ||
value->size != 1)
return FALSE;
val = *(uint32_t *)value->data;
drmModeConnectorSetProperty(sna->kgem.fd, sna_output->id,
p->kprop->prop_id, (uint64_t)val);
return TRUE;
} else if (p->kprop->flags & DRM_MODE_PROP_ENUM) {
Atom atom;
const char *name;
int j;
if (value->type != XA_ATOM || value->format != 32 || value->size != 1)
return FALSE;
memcpy(&atom, value->data, 4);
name = NameForAtom(atom);
if (name == NULL)
return FALSE;
/* search for matching name string, then set its value down */
for (j = 0; j < p->kprop->count_enums; j++) {
if (!strcmp(p->kprop->enums[j].name, name)) {
drmModeConnectorSetProperty(sna->kgem.fd, sna_output->id,
p->kprop->prop_id, p->kprop->enums[j].value);
return TRUE;
}
}
return FALSE;
}
}
/* We didn't recognise this property, just report success in order
* to allow the set to continue, otherwise we break setting of
* common properties like EDID.
*/
return TRUE;
}
static void update_properties(struct sna *sna, struct sna_output *output)
{
union compat_mode_get_connector compat_conn;
struct drm_mode_modeinfo dummy;
VG_CLEAR(compat_conn);
compat_conn.conn.connector_id = output->id;
compat_conn.conn.count_props = output->num_props;
compat_conn.conn.props_ptr = (uintptr_t)output->prop_ids;
compat_conn.conn.prop_values_ptr = (uintptr_t)output->prop_values;
compat_conn.conn.count_modes = 1; /* skip detect */
compat_conn.conn.modes_ptr = (uintptr_t)&dummy;
compat_conn.conn.count_encoders = 0;
(void)drmIoctl(sna->kgem.fd,
DRM_IOCTL_MODE_GETCONNECTOR,
&compat_conn.conn);
assert(compat_conn.conn.count_props == output->num_props);
output->update_properties = false;
}
static Bool
sna_output_get_property(xf86OutputPtr output, Atom property)
{
struct sna_output *sna_output = output->driver_private;
int err, i, j;
if (property == backlight_atom || property == backlight_deprecated_atom) {
INT32 val;
if (!sna_output->backlight.iface)
return FALSE;
if (sna_output->dpms_mode == DPMSModeOn) {
val = sna_output_backlight_get(output);
if (val < 0)
return FALSE;
DBG(("%s(%s): output on, reporting actual backlight value [%d]\n",
__FUNCTION__, output->name, val));
} else {
val = sna_output->backlight_active_level;
DBG(("%s(%s): output off, reporting cached backlight value [%d]\n",
__FUNCTION__, output->name, val));
}
err = RRChangeOutputProperty(output->randr_output, property,
XA_INTEGER, 32, PropModeReplace, 1, &val,
FALSE, FALSE);
if (err != 0) {
xf86DrvMsg(output->scrn->scrnIndex, X_WARNING,
"RRChangeOutputProperty error, %d\n", err);
return FALSE;
}
return TRUE;
}
for (i = 0; i < sna_output->num_props; i++) {
struct sna_property *p = &sna_output->props[i];
if (p->atoms == NULL || p->atoms[0] != property)
continue;
if (sna_output->update_properties && output->scrn->vtSema)
update_properties(to_sna(output->scrn), sna_output);
err = 0;
if (p->kprop->flags & DRM_MODE_PROP_RANGE) {
err = RRChangeOutputProperty(output->randr_output,
property, XA_INTEGER, 32,
PropModeReplace, 1,
&sna_output->prop_values[i],
FALSE, FALSE);
} else if (p->kprop->flags & DRM_MODE_PROP_ENUM) {
for (j = 0; j < p->kprop->count_enums; j++) {
if (p->kprop->enums[j].value == sna_output->prop_values[i])
break;
}
err = RRChangeOutputProperty(output->randr_output,
property, XA_ATOM, 32,
PropModeReplace, 1,
&p->atoms[j+1],
FALSE, FALSE);
}
if (err != 0)
xf86DrvMsg(output->scrn->scrnIndex, X_WARNING,
"RRChangeOutputProperty error, %d\n", err);
return TRUE;
}
return FALSE;
}
static const xf86OutputFuncsRec sna_output_funcs = {
.create_resources = sna_output_create_resources,
#ifdef RANDR_12_INTERFACE
.set_property = sna_output_set_property,
.get_property = sna_output_get_property,
#endif
.dpms = sna_output_dpms,
.detect = sna_output_detect,
.mode_valid = sna_output_mode_valid,
.get_modes = sna_output_get_modes,
.destroy = sna_output_destroy
};
static const int subpixel_conv_table[] = {
SubPixelUnknown,
SubPixelHorizontalRGB,
SubPixelHorizontalBGR,
SubPixelVerticalRGB,
SubPixelVerticalBGR,
SubPixelNone
};
static const char * const output_names[] = {
/* DRM_MODE_CONNECTOR_Unknown */ "None",
/* DRM_MODE_CONNECTOR_VGA */ "VGA",
/* DRM_MODE_CONNECTOR_DVII */ "DVI",
/* DRM_MODE_CONNECTOR_DVID */ "DVI",
/* DRM_MODE_CONNECTOR_DVIA */ "DVI",
/* DRM_MODE_CONNECTOR_Composite */ "Composite",
/* DRM_MODE_CONNECTOR_SVIDEO */ "TV",
/* DRM_MODE_CONNECTOR_LVDS */ "LVDS",
/* DRM_MODE_CONNECTOR_Component */ "CTV",
/* DRM_MODE_CONNECTOR_9PinDIN */ "DIN",
/* DRM_MODE_CONNECTOR_DisplayPort */ "DP",
/* DRM_MODE_CONNECTOR_HDMIA */ "HDMI",
/* DRM_MODE_CONNECTOR_HDMIB */ "HDMI",
/* DRM_MODE_CONNECTOR_TV */ "TV",
/* DRM_MODE_CONNECTOR_eDP */ "eDP",
/* DRM_MODE_CONNECTOR_VIRTUAL */ "Virtual",
/* DRM_MODE_CONNECTOR_DSI */ "DSI"
};
static bool
sna_zaphod_match(const char *s, const char *output)
{
char t[20];
unsigned int i = 0;
do {
/* match any outputs in a comma list, stopping at whitespace */
switch (*s) {
case '\0':
t[i] = '\0';
return strcmp(t, output) == 0;
case ',':
t[i] ='\0';
if (strcmp(t, output) == 0)
return TRUE;
i = 0;
break;
case ' ':
case '\t':
case '\n':
case '\r':
break;
default:
t[i++] = *s;
break;
}
s++;
} while (i < sizeof(t));
return false;
}
static bool
output_ignored(ScrnInfoPtr scrn, const char *name)
{
char monitor_name[64];
const char *monitor;
XF86ConfMonitorPtr conf;
snprintf(monitor_name, sizeof(monitor_name), "monitor-%s", name);
monitor = xf86findOptionValue(scrn->options, monitor_name);
if (!monitor)
monitor = name;
conf = xf86findMonitor(monitor,
xf86configptr->conf_monitor_lst);
if (conf == NULL && XF86_CRTC_CONFIG_PTR(scrn)->num_output == 0)
conf = xf86findMonitor(scrn->monitor->id,
xf86configptr->conf_monitor_lst);
if (conf == NULL)
return false;
return xf86CheckBoolOption(conf->mon_option_lst, "Ignore", 0);
}
static bool
gather_encoders(struct sna *sna, uint32_t id, int count,
struct drm_mode_get_encoder *out)
{
union compat_mode_get_connector compat_conn;
struct drm_mode_modeinfo dummy;
struct drm_mode_get_encoder enc;
uint32_t *ids = NULL;
DBG(("%s(%d): expected count=%d\n", __FUNCTION__, id, count));
VG_CLEAR(compat_conn);
memset(out, 0, sizeof(*out));
do {
uint32_t *nids;
nids = realloc(ids, sizeof(*ids) * count);
if (nids == NULL) {
free(ids);
return false;
}
ids = nids;
compat_conn.conn.connector_id = id;
compat_conn.conn.count_props = 0;
compat_conn.conn.count_modes = 1; /* skip detect */
compat_conn.conn.modes_ptr = (uintptr_t)&dummy;
compat_conn.conn.count_encoders = count;
compat_conn.conn.encoders_ptr = (uintptr_t)ids;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCONNECTOR, &compat_conn.conn)) {
DBG(("%s: GETCONNECTOR[%d] failed, ret=%d\n", __FUNCTION__, id, errno));
compat_conn.conn.count_encoders = count = 0;
}
if (count == compat_conn.conn.count_encoders)
break;
count = compat_conn.conn.count_encoders;
} while (1);
DBG(("%s(%d): gathering %d encoders\n", __FUNCTION__, id, count));
for (count = 0; count < compat_conn.conn.count_encoders; count++) {
enc.encoder_id = ids[count];
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETENCODER, &enc)) {
DBG(("%s: GETENCODER[%d] failed, ret=%d\n", __FUNCTION__, ids[count], errno));
count = 0;
break;
}
DBG(("%s(%d): encoder=%d, possible_crtcs=%x, possible_clones=%x\n",
__FUNCTION__, id, enc.encoder_id, enc.possible_crtcs, enc.possible_clones));
out->possible_crtcs |= enc.possible_crtcs;
out->possible_clones |= enc.possible_clones;
for (id = 0; id < sna->mode.num_real_encoder; id++) {
if (enc.encoder_id == sna->mode.encoders[id]) {
out->crtc_id |= 1 << id;
break;
}
}
}
free(ids);
return count > 0;
}
/* We need to map from kms encoder based possible_clones mask to X output based
* possible clones masking. Note that for SDVO and on Haswell with DP/HDMI we
* can have more than one output hanging off the same encoder.
*/
static void
sna_mode_compute_possible_outputs(struct sna *sna)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
int encoder_mask[32];
int i, j;
assert(sna->mode.num_real_output < 32);
assert(sna->mode.num_real_crtc < 32);
for (i = 0; i < sna->mode.num_real_output; i++) {
xf86OutputPtr output = config->output[i];
struct sna_output *sna_output = to_sna_output(output);
assert(sna_output);
if (sna_output->id) {
output->possible_clones = sna_output->possible_encoders;
encoder_mask[i] = sna_output->attached_encoders;
} else {
output->possible_clones = 0;
encoder_mask[i] = 0;
}
}
/* Convert from encoder numbering to output numbering */
for (i = 0; i < sna->mode.num_real_output; i++) {
xf86OutputPtr output = config->output[i];
unsigned clones;
if (output->possible_clones == 0)
continue;
clones = 0;
for (j = 0; j < sna->mode.num_real_output; j++)
if (i != j && output->possible_clones & encoder_mask[j])
clones |= 1 << j;
output->possible_clones = clones;
DBG(("%s: updated output '%s' %d [%d] (possible crtc:%x, possible clones:%x)\n",
__FUNCTION__, output->name, i, to_connector_id(output),
(uint32_t)output->possible_crtcs,
(uint32_t)output->possible_clones));
}
}
static int name_from_path(struct sna *sna,
struct sna_output *sna_output,
char *name)
{
struct drm_mode_get_blob blob;
char *path;
int id;
id = find_property(sna, sna_output, "PATH");
DBG(("%s: found? PATH=%d\n", __FUNCTION__, id));
if (id == -1)
return 0;
VG_CLEAR(blob);
blob.blob_id = sna_output->prop_values[id];
blob.length = 0;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob))
return 0;
do {
id = blob.length;
path = alloca(id + 1);
blob.data = (uintptr_t)path;
VG(memset(path, 0, id));
DBG(("%s: reading %d bytes for path blob\n", __FUNCTION__, id));
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob))
return 0;
} while (id != blob.length);
path[blob.length] = '\0'; /* paranoia */
DBG(("%s: PATH='%s'\n", __FUNCTION__, path));
/* we only handle MST paths for now */
if (strncmp(path, "mst:", 4) == 0) {
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
char tmp[5], *c;
int n;
c = strchr(path + 4, '-');
if (c == NULL)
return 0;
id = c - (path + 4);
if (id + 1> 5)
return 0;
memcpy(tmp, path + 4, id);
tmp[id] = '\0';
id = strtoul(tmp, NULL, 0);
for (n = 0; n < sna->mode.num_real_output; n++) {
if (to_sna_output(config->output[n])->id == id)
return snprintf(name, 32, "%s-%s",
config->output[n]->name, c + 1);
}
}
return 0;
}
static int
sna_output_add(struct sna *sna, unsigned id, unsigned serial)
{
ScrnInfoPtr scrn = sna->scrn;
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
union compat_mode_get_connector compat_conn;
struct drm_mode_get_encoder enc;
struct drm_mode_modeinfo dummy;
struct sna_output *sna_output;
xf86OutputPtr *outputs, output;
unsigned possible_encoders, attached_encoders, possible_crtcs;
const char *output_name;
char name[32];
int path, len, i;
DBG(("%s(%d): serial=%d\n", __FUNCTION__, id, serial));
COMPILE_TIME_ASSERT(sizeof(struct drm_mode_get_connector) <= sizeof(compat_conn.pad));
VG_CLEAR(compat_conn);
memset(&enc, 0, sizeof(enc));
compat_conn.conn.connector_id = id;
compat_conn.conn.count_props = 0;
compat_conn.conn.count_modes = 1; /* skip detect */
compat_conn.conn.modes_ptr = (uintptr_t)&dummy;
compat_conn.conn.count_encoders = 1;
compat_conn.conn.encoders_ptr = (uintptr_t)&enc.encoder_id;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCONNECTOR, &compat_conn.conn)) {
DBG(("%s: GETCONNECTOR[%d] failed, ret=%d\n", __FUNCTION__, id, errno));
return -1;
}
assert(compat_conn.conn.connector_id == id);
DBG(("%s(%d): has %d associated encoders\n", __FUNCTION__, id, compat_conn.conn.count_encoders));
if (compat_conn.conn.connector_type < ARRAY_SIZE(output_names))
output_name = output_names[compat_conn.conn.connector_type];
else
output_name = "UNKNOWN";
len = snprintf(name, 32, "%s%d", output_name, compat_conn.conn.connector_type_id);
if (output_ignored(scrn, name))
return 0;
if (enc.encoder_id) {
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETENCODER, &enc)) {
DBG(("%s: GETENCODER[%d] failed, ret=%d\n", __FUNCTION__, enc.encoder_id, errno));
return 0;
}
possible_encoders = enc.possible_clones;
attached_encoders = 0;
for (i = 0; i < sna->mode.num_real_encoder; i++) {
if (enc.encoder_id == sna->mode.encoders[i]) {
attached_encoders = 1 << i;
break;
}
}
if (attached_encoders == 0) {
DBG(("%s: failed to find attached encoder\n", __FUNCTION__));
return 0;
}
possible_crtcs = enc.possible_crtcs;
assert(enc.encoder_id == compat_conn.conn.encoder_id || compat_conn.conn.encoder_id == 0);
} else {
DBG(("%s: unexpected number [%d] of encoders attached\n",
__FUNCTION__, compat_conn.conn.count_encoders));
if (!gather_encoders(sna, id, compat_conn.conn.count_encoders, &enc)) {
DBG(("%s: gather encoders failed\n", __FUNCTION__));
return 0;
}
possible_encoders = enc.possible_clones;
attached_encoders = enc.crtc_id;
possible_crtcs = enc.possible_crtcs;
memset(&enc, 0, sizeof(enc));
enc.encoder_id = compat_conn.conn.encoder_id;
(void)drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETENCODER, &enc);
}
if (is_zaphod(scrn)) {
const char *str;
str = xf86GetOptValString(sna->Options, OPTION_ZAPHOD);
if (str && !sna_zaphod_match(str, name)) {
DBG(("%s: zaphod mismatch, want %s, have %s\n", __FUNCTION__, str, name));
return 0;
}
if ((possible_crtcs & (1 << scrn->confScreen->device->screen)) == 0) {
if (str) {
xf86DrvMsg(scrn->scrnIndex, X_ERROR,
"%s is an invalid output for screen (pipe) %d\n",
name, scrn->confScreen->device->screen);
return -1;
} else
return 0;
}
possible_crtcs = 1;
}
sna_output = calloc(sizeof(struct sna_output), 1);
if (!sna_output)
return -1;
sna_output->num_props = compat_conn.conn.count_props;
sna_output->prop_ids = malloc(sizeof(uint32_t)*compat_conn.conn.count_props);
sna_output->prop_values = malloc(sizeof(uint64_t)*compat_conn.conn.count_props);
compat_conn.conn.count_encoders = 0;
compat_conn.conn.count_modes = 1;
compat_conn.conn.modes_ptr = (uintptr_t)&dummy;
compat_conn.conn.count_props = sna_output->num_props;
compat_conn.conn.props_ptr = (uintptr_t)sna_output->prop_ids;
compat_conn.conn.prop_values_ptr = (uintptr_t)sna_output->prop_values;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCONNECTOR, &compat_conn.conn)) {
DBG(("%s: second! GETCONNECTOR failed, ret=%d\n", __FUNCTION__, errno));
goto cleanup;
}
assert(compat_conn.conn.connector_id == id);
/* statically constructed property list */
assert(sna_output->num_props == compat_conn.conn.count_props);
VG(VALGRIND_MAKE_MEM_DEFINED(sna_output->prop_ids, sizeof(uint32_t)*sna_output->num_props));
VG(VALGRIND_MAKE_MEM_DEFINED(sna_output->prop_values, sizeof(uint64_t)*sna_output->num_props));
/* Construct name from topology, and recheck if output is acceptable */
path = name_from_path(sna, sna_output, name);
if (path) {
const char *str;
if (output_ignored(scrn, name)) {
len = 0;
goto skip;
}
str = xf86GetOptValString(sna->Options, OPTION_ZAPHOD);
if (str && !sna_zaphod_match(str, name)) {
DBG(("%s: zaphod mismatch, want %s, have %s\n", __FUNCTION__, str, name));
len = 0;
goto skip;
}
len = path;
}
/* Check if we are dynamically reattaching an old connector */
if (serial) {
for (i = 0; i < sna->mode.num_real_output; i++) {
output = config->output[i];
if (strcmp(output->name, name) == 0) {
assert(output->scrn == scrn);
assert(output->funcs == &sna_output_funcs);
assert(to_sna_output(output)->id == 0);
sna_output_destroy(output);
goto reset;
}
}
}
output = calloc(1, sizeof(*output) + len + 1);
if (!output)
goto cleanup;
outputs = realloc(config->output, (config->num_output + 1) * sizeof(output));
if (outputs == NULL) {
free(output);
goto cleanup;
}
output->scrn = scrn;
output->funcs = &sna_output_funcs;
output->name = (char *)(output + 1);
memcpy(output->name, name, len + 1);
output->use_screen_monitor = config->num_output != 0;
xf86OutputUseScreenMonitor(output, !output->use_screen_monitor);
assert(output->options);
DBG(("%s: inserting output #%d of %d\n", __FUNCTION__, sna->mode.num_real_output, config->num_output));
for (i = config->num_output; i > sna->mode.num_real_output; i--) {
outputs[i] = outputs[i-1];
assert(outputs[i]->driver_private == NULL);
outputs[i]->possible_clones <<= 1;
}
if (xf86ReturnOptValBool(output->options, OPTION_PRIMARY, FALSE)) {
memmove(outputs + 1, outputs, sizeof(output)*config->num_output);
outputs[0] = output;
} else
outputs[i] = output;
sna->mode.num_real_output++;
config->num_output++;
config->output = outputs;
reset:
sna_output->id = compat_conn.conn.connector_id;
sna_output->is_panel = is_panel(compat_conn.conn.connector_type);
sna_output->edid_idx = find_property(sna, sna_output, "EDID");
if (find_property(sna, sna_output, "scaling mode") != -1)
sna_output->add_default_modes =
xf86ReturnOptValBool(output->options, OPTION_DEFAULT_MODES, TRUE);
i = find_property(sna, sna_output, "DPMS");
if (i != -1) {
sna_output->dpms_id = sna_output->prop_ids[i];
sna_output->dpms_mode = sna_output->prop_values[i];
DBG(("%s: found 'DPMS' (idx=%d, id=%d), initial value=%d\n",
__FUNCTION__, i, sna_output->dpms_id, sna_output->dpms_mode));
} else {
sna_output->dpms_id = -1;
sna_output->dpms_mode = DPMSModeOff;
}
sna_output->possible_encoders = possible_encoders;
sna_output->attached_encoders = attached_encoders;
output->mm_width = compat_conn.conn.mm_width;
output->mm_height = compat_conn.conn.mm_height;
if (compat_conn.conn.subpixel >= ARRAY_SIZE(subpixel_conv_table))
compat_conn.conn.subpixel = 0;
output->subpixel_order = subpixel_conv_table[compat_conn.conn.subpixel];
output->driver_private = sna_output;
sna_output->base = output;
backlight_init(&sna_output->backlight);
if (sna_output->is_panel)
sna_output_backlight_init(output);
output->possible_crtcs = possible_crtcs & count_to_mask(sna->mode.num_real_crtc);
output->interlaceAllowed = TRUE;
if (serial) {
if (output->randr_output == NULL) {
output->randr_output = RROutputCreate(xf86ScrnToScreen(scrn), name, len, output);
if (output->randr_output == NULL)
goto cleanup;
}
RROutputChanged(output->randr_output, TRUE);
sna_output_create_resources(output);
RRPostPendingProperties(output->randr_output);
sna_output->serial = serial;
} else {
/* stash the active CRTC id for our probe function */
if (compat_conn.conn.connection != DRM_MODE_DISCONNECTED)
output->crtc = (void *)(uintptr_t)enc.crtc_id;
}
DBG(("%s: created output '%s' %d, encoder=%d (possible crtc:%x, attached encoders:%x, possible clones:%x), serial=%d, edid=%d, dpms=%d, crtc=%lu\n",
__FUNCTION__, name, id, enc.encoder_id,
(uint32_t)output->possible_crtcs,
sna_output->attached_encoders,
sna_output->possible_encoders,
serial, sna_output->edid_idx, sna_output->dpms_id,
(unsigned long)(uintptr_t)output->crtc));
assert(sna_output->id == id);
xf86DrvMsg(scrn->scrnIndex, X_INFO,
"Enabled output %s\n",
output->name);
return 1;
cleanup:
len = -1;
skip:
free(sna_output->prop_ids);
free(sna_output->prop_values);
free(sna_output);
return len;
}
static int output_rank(const void *A, const void *B)
{
const xf86OutputPtr *a = A;
const xf86OutputPtr *b = B;
struct sna_output *sa = to_sna_output(*a);
struct sna_output *sb = to_sna_output(*b);
if (sa->is_panel != sb->is_panel)
return sb->is_panel - sa->is_panel;
return strcmp((*a)->name, (*b)->name);
}
static void sort_config_outputs(struct sna *sna)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
qsort(config->output, sna->mode.num_real_output, sizeof(*config->output), output_rank);
config->compat_output = 0; /* make sure it is a sane value */
sna_mode_compute_possible_outputs(sna);
}
static void sort_randr_outputs(struct sna *sna, ScreenPtr screen)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
rrScrPriv(screen);
int i;
assert(pScrPriv->numOutputs == config->num_output);
for (i = 0; i < config->num_output; i++) {
assert(config->output[i]->randr_output);
pScrPriv->outputs[i] = config->output[i]->randr_output;
}
}
static bool disable_unused_crtc(struct sna *sna)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
bool update = false;
int o, c;
DBG(("%s\n", __FUNCTION__));
for (c = 0; c < sna->mode.num_real_crtc; c++) {
xf86CrtcPtr crtc = config->crtc[c];
if (!crtc->enabled)
continue;
for (o = 0; o < sna->mode.num_real_output; o++) {
xf86OutputPtr output = config->output[o];
if (output->crtc == crtc)
break;
}
if (o == sna->mode.num_real_output) {
DBG(("%s: CRTC:%d was enabled with no outputs\n",
__FUNCTION__, sna_crtc_id(crtc)));
crtc->enabled = false;
update = true;
}
}
if (update) {
DBG(("%s: disabling unused functions\n", __FUNCTION__));
xf86DisableUnusedFunctions(sna->scrn);
}
return update;
}
void sna_mode_discover(struct sna *sna)
{
ScreenPtr screen = xf86ScrnToScreen(sna->scrn);
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
struct drm_mode_card_res res;
uint32_t connectors[32];
unsigned changed = 0;
unsigned serial;
int i, j;
DBG(("%s()\n", __FUNCTION__));
VG_CLEAR(connectors);
memset(&res, 0, sizeof(res));
res.count_connectors = 32;
res.connector_id_ptr = (uintptr_t)connectors;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
return;
DBG(("%s: now %d (was %d) connectors, %d encoders, %d crtc\n", __FUNCTION__,
res.count_connectors, sna->mode.num_real_output,
res.count_encoders, res.count_crtcs));
if (res.count_connectors > 32)
return;
assert(sna->mode.num_real_crtc == res.count_crtcs || is_zaphod(sna->scrn));
assert(sna->mode.max_crtc_width == res.max_width);
assert(sna->mode.max_crtc_height == res.max_height);
assert(sna->mode.num_real_encoder == res.count_encoders);
serial = ++sna->mode.serial;
if (serial == 0)
serial = ++sna->mode.serial;
for (i = 0; i < res.count_connectors; i++) {
DBG(("%s: connector[%d] = %d\n", __FUNCTION__, i, connectors[i]));
for (j = 0; j < sna->mode.num_real_output; j++) {
xf86OutputPtr output = config->output[j];
if (to_sna_output(output)->id == connectors[i]) {
DBG(("%s: found %s (id=%d)\n", __FUNCTION__, output->name, connectors[i]));
assert(to_sna_output(output)->id);
to_sna_output(output)->serial = serial;
break;
}
}
if (j == sna->mode.num_real_output) {
DBG(("%s: adding id=%d\n", __FUNCTION__, connectors[i]));
changed |= sna_output_add(sna, connectors[i], serial) > 0;
}
}
for (i = 0; i < sna->mode.num_real_output; i++) {
xf86OutputPtr output = config->output[i];
struct sna_output *sna_output = to_sna_output(output);
if (sna_output->id == 0)
continue;
sna_output->last_detect = 0;
if (sna_output->serial == serial) {
if (sna_output_detect(output) != output->status)
RROutputChanged(output->randr_output, TRUE);
continue;
}
DBG(("%s: removing output %s (id=%d), serial=%u [now %u]\n",
__FUNCTION__, output->name, sna_output->id,
sna_output->serial, serial));
xf86DrvMsg(sna->scrn->scrnIndex, X_INFO,
"Disabled output %s\n",
output->name);
sna_output->id = 0;
output->crtc = NULL;
RROutputChanged(output->randr_output, TRUE);
changed |= 2;
}
if (changed) {
DBG(("%s: outputs changed, broadcasting\n", __FUNCTION__));
sna_mode_set_primary(sna);
/* Reorder user visible listing */
sort_config_outputs(sna);
sort_randr_outputs(sna, screen);
if (changed & 2)
disable_unused_crtc(sna);
xf86RandR12TellChanged(screen);
}
RRTellChanged(screen);
}
static void copy_front(struct sna *sna, PixmapPtr old, PixmapPtr new)
{
struct sna_pixmap *old_priv, *new_priv;
DBG(("%s\n", __FUNCTION__));
if (wedged(sna) || isGPU(sna->scrn))
return;
old_priv = sna_pixmap_force_to_gpu(old, MOVE_READ);
if (!old_priv)
return;
new_priv = sna_pixmap_force_to_gpu(new, MOVE_WRITE | __MOVE_SCANOUT);
if (!new_priv)
return;
if (old_priv->clear) {
bool ok = false;
if (!wedged(sna))
ok = sna->render.fill_one(sna, new, new_priv->gpu_bo,
old_priv->clear_color,
0, 0,
new->drawable.width,
new->drawable.height,
GXcopy);
if (!ok) {
void *ptr = kgem_bo_map__gtt(&sna->kgem, new_priv->gpu_bo);
if (ptr)
memset(ptr, 0, new_priv->gpu_bo->pitch*new->drawable.height);
}
new_priv->clear = true;
new_priv->clear_color = old_priv->clear_color;
} else {
BoxRec box;
int16_t sx, sy, dx, dy;
if (new->drawable.width >= old->drawable.width &&
new->drawable.height >= old->drawable.height)
{
int nx = (new->drawable.width + old->drawable.width - 1) / old->drawable.width;
int ny = (new->drawable.height + old->drawable.height - 1) / old->drawable.height;
box.x1 = box.y1 = 0;
dy = 0;
for (sy = 0; sy < ny; sy++) {
box.y2 = old->drawable.height;
if (box.y2 + dy > new->drawable.height)
box.y2 = new->drawable.height - dy;
dx = 0;
for (sx = 0; sx < nx; sx++) {
box.x2 = old->drawable.width;
if (box.x2 + dx > new->drawable.width)
box.x2 = new->drawable.width - dx;
(void)sna->render.copy_boxes(sna, GXcopy,
&old->drawable, old_priv->gpu_bo, 0, 0,
&new->drawable, new_priv->gpu_bo, dx, dy,
&box, 1, 0);
dx += old->drawable.width;
}
dy += old->drawable.height;
}
} else {
box.x1 = box.y1 = 0;
box.x2 = min(old->drawable.width, new->drawable.width);
box.y2 = min(old->drawable.height, new->drawable.height);
sx = dx = 0;
if (box.x2 < old->drawable.width)
sx = (old->drawable.width - box.x2) / 2;
if (box.x2 < new->drawable.width)
dx = (new->drawable.width - box.x2) / 2;
sy = dy = 0;
if (box.y2 < old->drawable.height)
sy = (old->drawable.height - box.y2) / 2;
if (box.y2 < new->drawable.height)
dy = (new->drawable.height - box.y2) / 2;
DBG(("%s: copying box (%dx%d) from (%d, %d) to (%d, %d)\n",
__FUNCTION__, box.x2, box.y2, sx, sy, dx, dy));
if (box.x2 != new->drawable.width || box.y2 != new->drawable.height) {
bool ok = false;
if (!wedged(sna))
ok = sna->render.fill_one(sna, new, new_priv->gpu_bo, 0,
0, 0,
new->drawable.width,
new->drawable.height,
GXclear);
if (!ok) {
void *ptr = kgem_bo_map__gtt(&sna->kgem, new_priv->gpu_bo);
if (ptr)
memset(ptr, 0, new_priv->gpu_bo->pitch*new->drawable.height);
}
}
(void)sna->render.copy_boxes(sna, GXcopy,
&old->drawable, old_priv->gpu_bo, sx, sy,
&new->drawable, new_priv->gpu_bo, dx, dy,
&box, 1, 0);
}
}
sna_damage_all(&new_priv->gpu_damage, new);
}
static Bool
sna_mode_resize(ScrnInfoPtr scrn, int width, int height)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
struct sna *sna = to_sna(scrn);
ScreenPtr screen = xf86ScrnToScreen(scrn);
PixmapPtr new_front;
int i;
DBG(("%s (%d, %d) -> (%d, %d)\n", __FUNCTION__,
scrn->virtualX, scrn->virtualY,
width, height));
assert((sna->flags & SNA_IS_HOSTED) == 0);
if (scrn->virtualX == width && scrn->virtualY == height)
return TRUE;
/* Paranoid defense against rogue internal calls by Xorg */
if (width == 0 || height == 0)
return FALSE;
assert(sna->front);
assert(screen->GetScreenPixmap(screen) == sna->front);
DBG(("%s: creating new framebuffer %dx%d\n",
__FUNCTION__, width, height));
new_front = screen->CreatePixmap(screen,
width, height, scrn->depth,
SNA_CREATE_FB);
if (!new_front)
return FALSE;
xf86DrvMsg(scrn->scrnIndex, X_INFO,
"resizing framebuffer to %dx%d\n",
width, height);
for (i = 0; i < sna->mode.num_real_crtc; i++)
sna_crtc_disable_shadow(sna, to_sna_crtc(config->crtc[i]));
assert(sna->mode.shadow_active == 0);
assert(!sna->mode.shadow_enabled);
assert(sna->mode.shadow_damage == NULL);
assert(sna->mode.shadow == NULL);
copy_front(sna, sna->front, new_front);
screen->SetScreenPixmap(new_front);
assert(screen->GetScreenPixmap(screen) == new_front);
assert(sna->front == new_front);
screen->DestroyPixmap(new_front); /* owned by screen now */
scrn->virtualX = width;
scrn->virtualY = height;
scrn->displayWidth = width;
/* Flush pending shadow updates */
if (sna->mode.flip_active) {
DBG(("%s: waiting for %d outstanding TearFree flips\n",
__FUNCTION__, sna->mode.flip_active));
while (sna->mode.flip_active && sna_mode_wait_for_event(sna))
sna_mode_wakeup(sna);
}
/* Only update the CRTCs if we are in control */
if (!scrn->vtSema)
return TRUE;
for (i = 0; i < sna->mode.num_real_crtc; i++) {
xf86CrtcPtr crtc = config->crtc[i];
assert(to_sna_crtc(crtc) != NULL);
if (to_sna_crtc(crtc)->bo == NULL)
continue;
if (!__sna_crtc_set_mode(crtc))
sna_crtc_disable(crtc, false);
}
sna_mode_wakeup(sna);
kgem_clean_scanout_cache(&sna->kgem);
return TRUE;
}
/* cursor handling */
static void
rotate_coord(Rotation rotation, int size,
int x_dst, int y_dst,
int *x_src, int *y_src)
{
int t;
switch (rotation & 0xf) {
case RR_Rotate_0:
break;
case RR_Rotate_90:
t = x_dst;
x_dst = size - y_dst - 1;
y_dst = t;
break;
case RR_Rotate_180:
x_dst = size - x_dst - 1;
y_dst = size - y_dst - 1;
break;
case RR_Rotate_270:
t = x_dst;
x_dst = y_dst;
y_dst = size - t - 1;
break;
}
if (rotation & RR_Reflect_X)
x_dst = size - x_dst - 1;
if (rotation & RR_Reflect_Y)
y_dst = size - y_dst - 1;
*x_src = x_dst;
*y_src = y_dst;
}
static void
rotate_coord_back(Rotation rotation, int size, int *x, int *y)
{
int t;
if (rotation & RR_Reflect_X)
*x = size - *x - 1;
if (rotation & RR_Reflect_Y)
*y = size - *y - 1;
switch (rotation & 0xf) {
case RR_Rotate_0:
break;
case RR_Rotate_90:
t = *x;
*x = *y;
*y = size - t - 1;
break;
case RR_Rotate_180:
*x = size - *x - 1;
*y = size - *y - 1;
break;
case RR_Rotate_270:
t = *x;
*x = size - *y - 1;
*y = t;
break;
}
}
static struct sna_cursor *__sna_create_cursor(struct sna *sna, int size)
{
struct sna_cursor *c;
for (c = sna->cursor.cursors; c; c = c->next) {
if (c->ref == 0 && c->alloc >= size) {
__DBG(("%s: stealing handle=%d, serial=%d, rotation=%d, alloc=%d\n",
__FUNCTION__, c->handle, c->serial, c->rotation, c->alloc));
return c;
}
}
__DBG(("%s(size=%d, num_stash=%d)\n", __FUNCTION__, size, sna->cursor.num_stash));
c = sna->cursor.stash;
assert(c);
c->alloc = ALIGN(size, 4096);
c->handle = gem_create(sna->kgem.fd, c->alloc);
if (c->handle == 0)
return NULL;
/* Old hardware uses physical addresses, which the kernel
* implements in an incoherent fashion requiring a pwrite.
*/
if (sna->cursor.use_gtt) {
c->image = gem_mmap(sna->kgem.fd, c->handle, c->alloc);
if (c->image == NULL) {
gem_close(sna->kgem.fd, c->handle);
return NULL;
}
} else
c->image = NULL;
__DBG(("%s: handle=%d, allocated %d\n", __FUNCTION__, c->handle, size));
c->ref = 0;
c->serial = 0;
c->rotation = 0;
c->last_width = c->last_height = 0; /* all clear */
c->size = size;
sna->cursor.num_stash--;
sna->cursor.stash = c->next;
c->next = sna->cursor.cursors;
sna->cursor.cursors = c;
return c;
}
static uint32_t *get_cursor_argb(CursorPtr c)
{
#ifdef ARGB_CURSOR
return (uint32_t *)c->bits->argb;
#else
return NULL;
#endif
}
static int __cursor_size(int width, int height)
{
int i, size;
i = MAX(width, height);
for (size = 64; size < i; size <<= 1)
;
return size;
}
static struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc)
{
struct sna_cursor *cursor;
const uint8_t *source, *mask;
const uint32_t *argb;
uint32_t *image;
int width, height, pitch, size, x, y;
PictTransform cursor_to_fb;
struct pict_f_transform f_cursor_to_fb, f_fb_to_cursor;
bool transformed;
Rotation rotation;
assert(sna->cursor.ref);
cursor = to_sna_crtc(crtc)->cursor;
__DBG(("%s: current cursor handle=%d, serial=%d [expected %d]\n",
__FUNCTION__,
cursor ? cursor->handle : 0,
cursor ? cursor->serial : 0,
sna->cursor.serial));
if (cursor && cursor->serial == sna->cursor.serial) {
assert(cursor->size == sna->cursor.size || cursor->transformed);
assert(cursor->rotation == (!to_sna_crtc(crtc)->cursor_transform && crtc->transform_in_use) ? crtc->rotation : RR_Rotate_0);
assert(cursor->ref);
return cursor;
}
__DBG(("%s: cursor=%dx%d, pitch=%d, serial=%d, argb?=%d\n", __FUNCTION__,
sna->cursor.ref->bits->width,
sna->cursor.ref->bits->height,
get_cursor_argb(sna->cursor.ref) ? 4*sna->cursor.ref->bits->width : BitmapBytePad(sna->cursor.ref->bits->width),
sna->cursor.serial,
get_cursor_argb(sna->cursor.ref) != NULL));
transformed = to_sna_crtc(crtc)->cursor_transform;
rotation = (!transformed && crtc->transform_in_use) ? crtc->rotation : RR_Rotate_0;
/* Don't allow phys cursor sharing */
if (sna->cursor.use_gtt && !transformed) {
for (cursor = sna->cursor.cursors; cursor; cursor = cursor->next) {
if (cursor->serial == sna->cursor.serial &&
cursor->rotation == rotation &&
!cursor->transformed) {
__DBG(("%s: reusing handle=%d, serial=%d, rotation=%d, size=%d\n",
__FUNCTION__, cursor->handle, cursor->serial, cursor->rotation, cursor->size));
assert(cursor->size == sna->cursor.size);
return cursor;
}
}
}
if (transformed) {
struct pixman_box16 box;
box.x1 = box.y1 = 0;
box.x2 = sna->cursor.ref->bits->width;
box.y2 = sna->cursor.ref->bits->height;
pixman_f_transform_bounds(&crtc->f_crtc_to_framebuffer, &box);
size = __cursor_size(box.x2 - box.x1, box.y2 - box.y1);
RRTransformCompute(0, 0,
sna->cursor.ref->bits->width,
sna->cursor.ref->bits->height,
crtc->rotation, &crtc->transform,
&cursor_to_fb,
&f_cursor_to_fb,
&f_fb_to_cursor);
} else
size = sna->cursor.size;
cursor = to_sna_crtc(crtc)->cursor;
if (cursor && cursor->alloc < 4*size*size)
cursor = NULL;
if (cursor == NULL) {
cursor = __sna_create_cursor(sna, 4*size*size);
if (cursor == NULL) {
DBG(("%s: failed to allocate cursor\n", __FUNCTION__));
return NULL;
}
}
width = sna->cursor.ref->bits->width;
height = sna->cursor.ref->bits->height;
source = sna->cursor.ref->bits->source;
mask = sna->cursor.ref->bits->mask;
argb = get_cursor_argb(sna->cursor.ref);
pitch = BitmapBytePad(width);
image = cursor->image;
if (image == NULL || transformed) {
image = sna->cursor.scratch;
cursor->last_width = cursor->last_height = size;
}
if (size > cursor->size ||
width < cursor->last_width ||
height < cursor->last_height ||
rotation != cursor->rotation)
memset(image, 0, 4*size*size);
if (rotation == RR_Rotate_0) {
if (argb == NULL) {
for (y = 0; y < height; y++) {
uint32_t *p = image + y*size;
for (x = 0; x < width; x++) {
int byte = x / 8;
uint8_t bit = 1 << (x & 7);
uint32_t pixel;
if (mask[byte] & bit) {
if (source[byte] & bit)
pixel = sna->cursor.fg;
else
pixel = sna->cursor.bg;
} else
pixel = 0;
*p++ = pixel;
}
mask += pitch;
source += pitch;
}
if (transformed) {
affine_blt(image, cursor->image, 32,
0, 0, width, height, size * 4,
0, 0, width, height, size * 4,
&f_cursor_to_fb);
image = cursor->image;
}
} else if (transformed) {
affine_blt(argb, cursor->image, 32,
0, 0, width, height, width * 4,
0, 0, width, height, size * 4,
&f_cursor_to_fb);
image = cursor->image;
} else
memcpy_blt(argb, image, 32,
width * 4, size * 4,
0, 0,
0, 0,
width, height);
} else {
for (y = 0; y < size; y++)
for (x = 0; x < size; x++) {
uint32_t pixel;
int xin, yin;
rotate_coord(rotation, size, x, y, &xin, &yin);
if (xin < width && yin < height)
if (argb == NULL) {
int byte = xin / 8;
int bit = xin & 7;
if (mask[yin*pitch + byte] & (1 << bit)) {
if (source[yin*pitch + byte] & (1 << bit))
pixel = sna->cursor.fg;
else
pixel = sna->cursor.bg;
} else
pixel = 0;
} else
pixel = argb[yin * width + xin];
else
pixel = 0;
image[y * size + x] = pixel;
}
}
if (image != cursor->image) {
struct drm_i915_gem_pwrite pwrite;
VG_CLEAR(pwrite);
pwrite.handle = cursor->handle;
pwrite.offset = 0;
pwrite.size = 4*size*size;
pwrite.data_ptr = (uintptr_t)image;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_PWRITE, &pwrite))
__DBG(("%s: cursor update (pwrite) failed: %d\n", __FUNCTION__, errno));
}
cursor->size = size;
cursor->rotation = rotation;
cursor->transformed = transformed;
cursor->serial = sna->cursor.serial;
cursor->last_width = width;
cursor->last_height = height;
return cursor;
}
static unsigned char *
sna_realize_cursor(xf86CursorInfoPtr info, CursorPtr cursor)
{
return NULL;
}
#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,12,99,901,0)
static inline int sigio_block(void)
{
OsBlockSIGIO();
return 0;
}
static inline void sigio_unblock(int was_blocked)
{
OsReleaseSIGIO();
(void)was_blocked;
}
#else
#include <xf86_OSproc.h>
static inline int sigio_block(void)
{
return xf86BlockSIGIO();
}
static inline void sigio_unblock(int was_blocked)
{
xf86UnblockSIGIO(was_blocked);
}
#endif
static void enable_fb_access(ScrnInfoPtr scrn, int state)
{
scrn->EnableDisableFBAccess(
#ifdef XF86_HAS_SCRN_CONV
scrn,
#else
scrn->scrnIndex,
#endif
state);
}
static void __restore_swcursor(ScrnInfoPtr scrn)
{
DBG(("%s: attempting to restore SW cursor\n", __FUNCTION__));
enable_fb_access(scrn, FALSE);
enable_fb_access(scrn, TRUE);
RemoveBlockAndWakeupHandlers((BlockHandlerProcPtr)__restore_swcursor,
(WakeupHandlerProcPtr)NoopDDA,
scrn);
}
static void restore_swcursor(struct sna *sna)
{
/* XXX Force the cursor to be restored (avoiding recursion) */
FreeCursor(sna->cursor.ref, None);
sna->cursor.ref = NULL;
RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)__restore_swcursor,
(WakeupHandlerProcPtr)NoopDDA,
sna->scrn);
}
static void
sna_show_cursors(ScrnInfoPtr scrn)
{
xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
struct sna *sna = to_sna(scrn);
int sigio, c;
DBG(("%s: cursor?=%d\n", __FUNCTION__, sna->cursor.ref != NULL));
if (sna->cursor.ref == NULL)
return;
sigio = sigio_block();
for (c = 0; c < sna->mode.num_real_crtc; c++) {
xf86CrtcPtr crtc = xf86_config->crtc[c];
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
struct drm_mode_cursor arg;
struct sna_cursor *cursor;
assert(sna_crtc != NULL);
if (sna_crtc->bo == NULL)
continue;
if (!crtc->cursor_in_range) {
DBG(("%s: skipping cursor outside CRTC (pipe=%d)\n",
__FUNCTION__, sna_crtc_pipe(crtc)));
continue;
}
cursor = __sna_get_cursor(sna, crtc);
if (cursor == NULL ||
(sna_crtc->cursor == cursor && sna_crtc->last_cursor_size == cursor->size)) {
DBG(("%s: skipping cursor already show on CRTC (pipe=%d)\n",
__FUNCTION__, sna_crtc_pipe(crtc)));
continue;
}
DBG(("%s: CRTC pipe=%d, handle->%d\n", __FUNCTION__,
sna_crtc_pipe(crtc), cursor->handle));
VG_CLEAR(arg);
arg.flags = DRM_MODE_CURSOR_BO;
arg.crtc_id = __sna_crtc_id(sna_crtc);
arg.width = arg.height = cursor->size;
arg.handle = cursor->handle;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_CURSOR, &arg) == 0) {
if (sna_crtc->cursor) {
assert(sna_crtc->cursor->ref > 0);
sna_crtc->cursor->ref--;
}
cursor->ref++;
sna_crtc->cursor = cursor;
sna_crtc->last_cursor_size = cursor->size;
} else {
ERR(("%s: failed to show cursor on CRTC:%d [pipe=%d], disabling hwcursor\n",
__FUNCTION__, sna_crtc_id(crtc), sna_crtc_pipe(crtc)));
sna->cursor.disable = true;
}
}
sigio_unblock(sigio);
sna->cursor.active = true;
if (unlikely(sna->cursor.disable))
restore_swcursor(sna);
}
static void
sna_set_cursor_colors(ScrnInfoPtr scrn, int _bg, int _fg)
{
struct sna *sna = to_sna(scrn);
uint32_t fg = _fg, bg = _bg;
__DBG(("%s(bg=%08x, fg=%08x)\n", __FUNCTION__, bg, fg));
/* Save ARGB versions of these colors */
fg |= 0xff000000;
bg |= 0xff000000;
if (fg == sna->cursor.fg && bg == sna->cursor.bg)
return;
sna->cursor.fg = fg;
sna->cursor.bg = bg;
if (sna->cursor.ref == NULL)
return;
if (get_cursor_argb(sna->cursor.ref))
return;
sna->cursor.serial++;
__DBG(("%s: serial->%d\n", __FUNCTION__, sna->cursor.serial));
sna_show_cursors(scrn);
}
static void
sna_crtc_disable_cursor(struct sna *sna, struct sna_crtc *crtc)
{
struct drm_mode_cursor arg;
int sigio;
if (!crtc->cursor)
return;
sigio = sigio_block();
if (crtc->cursor) {
DBG(("%s: CRTC:%d, handle=%d\n", __FUNCTION__, __sna_crtc_id(crtc), crtc->cursor->handle));
assert(crtc->cursor->ref > 0);
crtc->cursor->ref--;
crtc->cursor = NULL;
crtc->last_cursor_size = 0;
VG_CLEAR(arg);
arg.flags = DRM_MODE_CURSOR_BO;
arg.crtc_id = __sna_crtc_id(crtc);
arg.width = arg.height = 0;
arg.handle = 0;
(void)drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_CURSOR, &arg);
}
sigio_unblock(sigio);
}
static void
sna_disable_cursors(ScrnInfoPtr scrn)
{
xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
struct sna *sna = to_sna(scrn);
int sigio, c;
DBG(("%s\n", __FUNCTION__));
sigio = sigio_block();
for (c = 0; c < sna->mode.num_real_crtc; c++) {
assert(to_sna_crtc(xf86_config->crtc[c]));
sna_crtc_disable_cursor(sna, to_sna_crtc(xf86_config->crtc[c]));
}
sigio_unblock(sigio);
}
static void
sna_hide_cursors(ScrnInfoPtr scrn)
{
xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
struct sna *sna = to_sna(scrn);
struct sna_cursor *cursor, **prev;
int sigio, c;
DBG(("%s\n", __FUNCTION__));
sna->cursor.active = false;
sigio = sigio_block();
for (c = 0; c < sna->mode.num_real_crtc; c++) {
assert(to_sna_crtc(xf86_config->crtc[c]));
sna_crtc_disable_cursor(sna, to_sna_crtc(xf86_config->crtc[c]));
}
for (prev = &sna->cursor.cursors; (cursor = *prev) != NULL; ) {
assert(cursor->ref == 0);
if (cursor->serial == sna->cursor.serial) {
prev = &cursor->next;
continue;
}
*prev = cursor->next;
if (cursor->image)
munmap(cursor->image, cursor->alloc);
gem_close(sna->kgem.fd, cursor->handle);
cursor->next = sna->cursor.stash;
sna->cursor.stash = cursor;
sna->cursor.num_stash++;
}
sigio_unblock(sigio);
}
static void
sna_set_cursor_position(ScrnInfoPtr scrn, int x, int y)
{
xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
struct sna *sna = to_sna(scrn);
int sigio, c;
__DBG(("%s(%d, %d), cursor? %d\n", __FUNCTION__,
x, y, sna->cursor.ref!=NULL));
if (sna->cursor.ref == NULL)
return;
sigio = sigio_block();
sna->cursor.last_x = x;
sna->cursor.last_y = y;
/* undo what xf86HWCurs did to the coordinates */
x += scrn->frameX0;
y += scrn->frameY0;
for (c = 0; c < sna->mode.num_real_crtc; c++) {
xf86CrtcPtr crtc = xf86_config->crtc[c];
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
struct sna_cursor *cursor = NULL;
struct drm_mode_cursor arg;
assert(sna_crtc != NULL);
VG_CLEAR(arg);
arg.flags = 0;
arg.crtc_id = __sna_crtc_id(sna_crtc);
arg.handle = 0;
if (sna_crtc->bo == NULL)
goto disable;
if (crtc->transform_in_use) {
int xhot = sna->cursor.ref->bits->xhot;
int yhot = sna->cursor.ref->bits->yhot;
struct pict_f_vector v;
v.v[0] = (x + xhot) + 0.5;
v.v[1] = (y + yhot) + 0.5;
v.v[2] = 1;
pixman_f_transform_point(&crtc->f_framebuffer_to_crtc, &v);
rotate_coord_back(crtc->rotation, sna->cursor.size, &xhot, &yhot);
/* cursor will have 0.5 added to it already so floor is sufficient */
arg.x = floor(v.v[0]) - xhot;
arg.y = floor(v.v[1]) - yhot;
} else {
arg.x = x - crtc->x;
arg.y = y - crtc->y;
}
if (arg.x < crtc->mode.HDisplay && arg.x > -sna->cursor.size &&
arg.y < crtc->mode.VDisplay && arg.y > -sna->cursor.size) {
cursor = __sna_get_cursor(sna, crtc);
if (cursor == NULL)
cursor = sna_crtc->cursor;
if (cursor == NULL) {
__DBG(("%s: failed to grab cursor, disabling\n",
__FUNCTION__));
goto disable;
}
if (sna_crtc->cursor != cursor || sna_crtc->last_cursor_size != cursor->size) {
arg.flags |= DRM_MODE_CURSOR_BO;
arg.handle = cursor->handle;
}
arg.width = arg.height = cursor->size;
arg.flags |= DRM_MODE_CURSOR_MOVE;
crtc->cursor_in_range = true;
} else {
crtc->cursor_in_range = false;
disable:
if (sna_crtc->cursor) {
arg.flags = DRM_MODE_CURSOR_BO;
arg.width = arg.height = 0;
}
cursor = NULL;
}
__DBG(("%s: CRTC:%d (%d, %d), handle=%d, flags=%x (old cursor handle=%d), move? %d, update handle? %d\n",
__FUNCTION__, __sna_crtc_id(sna_crtc), arg.x, arg.y, arg.handle, arg.flags, sna_crtc->cursor ? sna_crtc->cursor->handle : 0,
arg.flags & DRM_MODE_CURSOR_MOVE, arg.flags & DRM_MODE_CURSOR_BO));
if (arg.flags == 0)
continue;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_CURSOR, &arg) == 0) {
if (arg.flags & DRM_MODE_CURSOR_BO) {
if (sna_crtc->cursor) {
assert(sna_crtc->cursor->ref > 0);
sna_crtc->cursor->ref--;
}
sna_crtc->cursor = cursor;
if (cursor) {
sna_crtc->last_cursor_size = cursor->size;
cursor->ref++;
} else
sna_crtc->last_cursor_size = 0;
}
} else {
ERR(("%s: failed to update cursor on CRTC:%d [pipe=%d], disabling hwcursor\n",
__FUNCTION__, sna_crtc_id(crtc), sna_crtc_pipe(crtc)));
/* XXX How to force switch back to SW cursor?
* Right now we just want until the next cursor image
* change, which is fairly frequent.
*/
sna->cursor.disable = true;
}
}
sigio_unblock(sigio);
if (unlikely(sna->cursor.disable))
restore_swcursor(sna);
}
#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,15,99,902,2)
static Bool
sna_load_cursor_argb2(ScrnInfoPtr scrn, CursorPtr cursor)
{
return TRUE;
}
static Bool
sna_load_cursor_image2(ScrnInfoPtr scrn, unsigned char *src)
{
return TRUE;
}
#endif
static void
sna_load_cursor_argb(ScrnInfoPtr scrn, CursorPtr cursor)
{
}
static void
sna_load_cursor_image(ScrnInfoPtr scrn, unsigned char *src)
{
}
static bool
sna_cursor_preallocate(struct sna *sna)
{
while (sna->cursor.num_stash < 0) {
struct sna_cursor *cursor = malloc(sizeof(*cursor));
if (!cursor)
return false;
cursor->next = sna->cursor.stash;
sna->cursor.stash = cursor;
sna->cursor.num_stash++;
}
return true;
}
static bool
transformable_cursor(struct sna *sna, CursorPtr cursor)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
int i;
for (i = 0; i < sna->mode.num_real_crtc; i++) {
xf86CrtcPtr crtc = config->crtc[i];
struct pixman_box16 box;
int size;
if (!to_sna_crtc(crtc)->hwcursor) {
DBG(("%s: hwcursor disabled on CRTC:%d [pipe=%d]\n",
__FUNCTION__, sna_crtc_id(crtc), sna_crtc_pipe(crtc)));
return false;
}
if (!sna->cursor.use_gtt || !sna->cursor.scratch) {
DBG(("%s: unable to use GTT curosor access [%d] or no scratch [%d]\n",
__FUNCTION__, sna->cursor.use_gtt, sna->cursor.scratch));
return false;
}
box.x1 = box.y1 = 0;
box.x2 = cursor->bits->width;
box.y2 = cursor->bits->height;
if (!pixman_f_transform_bounds(&crtc->f_crtc_to_framebuffer,
&box)) {
DBG(("%s: unable to transform bounds\n", __FUNCTION__));
return false;
}
size = __cursor_size(box.x2 - box.x1, box.y2 - box.y1);
if (size > sna->cursor.max_size) {
DBG(("%s: transformed cursor size=%d too large, max=%d\n",
__FUNCTION__, size, sna->cursor.max_size));
return false;
}
}
return true;
}
static Bool
sna_use_hw_cursor(ScreenPtr screen, CursorPtr cursor)
{
struct sna *sna = to_sna_from_screen(screen);
DBG(("%s (%dx%d)?\n", __FUNCTION__,
cursor->bits->width, cursor->bits->height));
if (sna->cursor.disable)
return FALSE;
/* cursors are invariant */
if (cursor == sna->cursor.ref)
return TRUE;
if (sna->cursor.ref) {
FreeCursor(sna->cursor.ref, None);
sna->cursor.ref = NULL;
}
sna->cursor.size =
__cursor_size(cursor->bits->width, cursor->bits->height);
if (sna->cursor.size > sna->cursor.max_size) {
DBG(("%s: cursor size=%d too large, max %d: using sw cursor\n",
__FUNCTION__, sna->cursor.size, sna->cursor.max_size));
return FALSE;
}
if (sna->mode.rr_active && !transformable_cursor(sna, cursor)) {
DBG(("%s: RandR active [%d] and non-transformable cursor: using sw cursor\n",
__FUNCTION__, sna->mode.rr_active));
return FALSE;
}
if (!sna_cursor_preallocate(sna)) {
DBG(("%s: cursor preallocation failed: using sw cursor\n", __FUNCTION__));
return FALSE;
}
sna->cursor.ref = cursor;
cursor->refcnt++;
sna->cursor.serial++;
DBG(("%s(%dx%d): ARGB?=%d, serial->%d, size->%d\n", __FUNCTION__,
cursor->bits->width,
cursor->bits->height,
get_cursor_argb(cursor) != NULL,
sna->cursor.serial,
sna->cursor.size));
return TRUE;
}
static void
sna_cursor_pre_init(struct sna *sna)
{
struct local_get_cap {
uint64_t name;
uint64_t value;
} cap;
int v;
if (sna->mode.num_real_crtc == 0)
return;
#define LOCAL_IOCTL_GET_CAP DRM_IOWR(0x0c, struct local_get_cap)
#define DRM_CAP_CURSOR_WIDTH 8
#define DRM_CAP_CURSOR_HEIGHT 9
#define I915_PARAM_HAS_COHERENT_PHYS_GTT 29
sna->cursor.max_size = 64;
cap.value = 0;
cap.name = DRM_CAP_CURSOR_WIDTH;
if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_GET_CAP, &cap) == 0)
sna->cursor.max_size = cap.value;
cap.name = DRM_CAP_CURSOR_HEIGHT;
if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_GET_CAP, &cap) == 0 &&
cap.value < sna->cursor.max_size)
sna->cursor.max_size = cap.value;
v = -1; /* No param uses the sign bit, reserve it for errors */
if (sna->kgem.gen >= 033) {
v = 1;
} else {
drm_i915_getparam_t gp = {
I915_PARAM_HAS_COHERENT_PHYS_GTT,
&v,
};
(void)drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GETPARAM, &gp);
}
sna->cursor.use_gtt = v > 0;
DBG(("%s: cursor updates use_gtt?=%d\n",
__FUNCTION__, sna->cursor.use_gtt));
sna->cursor.scratch = malloc(sna->cursor.max_size * sna->cursor.max_size * 4);
if (!sna->cursor.scratch && !sna->cursor.use_gtt)
sna->cursor.max_size = 0;
sna->cursor.num_stash = -sna->mode.num_real_crtc;
xf86DrvMsg(sna->scrn->scrnIndex, X_PROBED,
"Using a maximum size of %dx%d for hardware cursors\n",
sna->cursor.max_size, sna->cursor.max_size);
}
static void
sna_cursor_close(struct sna *sna)
{
sna->cursor.serial = 0;
sna_hide_cursors(sna->scrn);
while (sna->cursor.stash) {
struct sna_cursor *cursor = sna->cursor.stash;
sna->cursor.stash = cursor->next;
free(cursor);
}
sna->cursor.num_stash = -sna->mode.num_real_crtc;
}
bool
sna_cursors_init(ScreenPtr screen, struct sna *sna)
{
xf86CursorInfoPtr cursor_info;
if (sna->cursor.max_size == 0)
return false;
cursor_info = xf86CreateCursorInfoRec();
if (cursor_info == NULL)
return false;
cursor_info->MaxWidth = sna->cursor.max_size;
cursor_info->MaxHeight = sna->cursor.max_size;
cursor_info->Flags = (HARDWARE_CURSOR_TRUECOLOR_AT_8BPP |
HARDWARE_CURSOR_UPDATE_UNHIDDEN |
HARDWARE_CURSOR_ARGB);
cursor_info->RealizeCursor = sna_realize_cursor;
cursor_info->SetCursorColors = sna_set_cursor_colors;
cursor_info->SetCursorPosition = sna_set_cursor_position;
cursor_info->LoadCursorImage = sna_load_cursor_image;
cursor_info->HideCursor = sna_hide_cursors;
cursor_info->ShowCursor = sna_show_cursors;
cursor_info->UseHWCursor = sna_use_hw_cursor;
#ifdef ARGB_CURSOR
cursor_info->UseHWCursorARGB = sna_use_hw_cursor;
cursor_info->LoadCursorARGB = sna_load_cursor_argb;
#endif
#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,15,99,902,3)
cursor_info->LoadCursorImageCheck = sna_load_cursor_image2;
#ifdef ARGB_CURSOR
cursor_info->LoadCursorARGBCheck = sna_load_cursor_argb2;
#endif
#endif
if (!xf86InitCursor(screen, cursor_info)) {
xf86DestroyCursorInfoRec(cursor_info);
return false;
}
sna->cursor.info = cursor_info;
return true;
}
static void
sna_cursors_reload(struct sna *sna)
{
DBG(("%s: active?=%d\n", __FUNCTION__, sna->cursor.active));
if (sna->cursor.active)
sna_set_cursor_position(sna->scrn,
sna->cursor.last_x,
sna->cursor.last_y);
}
static void
sna_cursors_fini(struct sna *sna)
{
if (sna->cursor.info) {
xf86DestroyCursorInfoRec(sna->cursor.info);
sna->cursor.info = NULL;
}
if (sna->cursor.ref) {
FreeCursor(sna->cursor.ref, None);
sna->cursor.ref = NULL;
}
}
static bool
sna_crtc_flip(struct sna *sna, struct sna_crtc *crtc, struct kgem_bo *bo, int x, int y)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
struct drm_mode_crtc arg;
uint32_t output_ids[32];
int output_count = 0;
int i;
DBG(("%s CRTC:%d [pipe=%d], handle=%d\n", __FUNCTION__, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc), bo->handle));
assert(sna->mode.num_real_output < ARRAY_SIZE(output_ids));
assert(crtc->bo);
assert(crtc->kmode.clock);
for (i = 0; i < sna->mode.num_real_output; i++) {
xf86OutputPtr output = config->output[i];
if (output->crtc != crtc->base)
continue;
DBG(("%s: attaching output '%s' %d [%d] to crtc:%d (pipe %d) (possible crtc:%x, possible clones:%x)\n",
__FUNCTION__, output->name, i, to_connector_id(output),
__sna_crtc_id(crtc), __sna_crtc_pipe(crtc),
(uint32_t)output->possible_crtcs,
(uint32_t)output->possible_clones));
assert(output->possible_crtcs & (1 << __sna_crtc_pipe(crtc)) ||
is_zaphod(sna->scrn));
output_ids[output_count] = to_connector_id(output);
if (++output_count == ARRAY_SIZE(output_ids))
return false;
}
assert(output_count);
VG_CLEAR(arg);
arg.crtc_id = __sna_crtc_id(crtc);
arg.fb_id = fb_id(bo);
assert(arg.fb_id);
arg.x = x;
arg.y = y;
arg.set_connectors_ptr = (uintptr_t)output_ids;
arg.count_connectors = output_count;
arg.mode = crtc->kmode;
arg.mode_valid = 1;
DBG(("%s: applying crtc [%d, pipe=%d] mode=%dx%d+%d+%d@%d, fb=%d across %d outputs [%d...]\n",
__FUNCTION__, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc),
arg.mode.hdisplay,
arg.mode.vdisplay,
arg.x, arg.y,
arg.mode.clock,
arg.fb_id,
output_count, output_count ? output_ids[0] : 0));
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_SETCRTC, &arg))
return false;
crtc->offset = y << 16 | x;
return true;
}
static void sna_mode_restore(struct sna *sna)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
int error = 0;
int i;
assert(!sna->mode.hidden);
for (i = 0; i < sna->mode.num_real_crtc; i++) {
xf86CrtcPtr crtc = config->crtc[i];
assert(to_sna_crtc(crtc) != NULL);
if (to_sna_crtc(crtc)->bo == NULL)
continue;
assert(crtc->enabled);
if (!__sna_crtc_set_mode(crtc)) {
sna_crtc_disable(crtc, false);
error++;
}
}
sna_mode_wakeup(sna);
update_flush_interval(sna);
sna_cursors_reload(sna);
sna->mode.dirty = false;
if (error)
xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
"Failed to restore display configuration\n");
}
int
sna_page_flip(struct sna *sna,
struct kgem_bo *bo,
sna_flip_handler_t handler,
void *data)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
const int width = sna->scrn->virtualX;
const int height = sna->scrn->virtualY;
int count = 0;
int i;
DBG(("%s: handle %d attached\n", __FUNCTION__, bo->handle));
assert(bo->refcnt);
assert((sna->flags & SNA_IS_HOSTED) == 0);
assert(sna->mode.flip_active == 0);
assert(sna->mode.front_active);
assert(!sna->mode.hidden);
assert(sna->scrn->vtSema);
if ((sna->flags & (data ? SNA_HAS_FLIP : SNA_HAS_ASYNC_FLIP)) == 0)
return 0;
kgem_bo_submit(&sna->kgem, bo);
for (i = 0; i < sna->mode.num_real_crtc; i++) {
struct sna_crtc *crtc = config->crtc[i]->driver_private;
struct drm_mode_crtc_page_flip arg;
uint32_t crtc_offset;
int fixup;
DBG(("%s: crtc %d id=%d, pipe=%d active? %d\n",
__FUNCTION__, i, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc), crtc->bo != NULL));
if (crtc->bo == NULL)
continue;
assert(!crtc->transform);
assert(!crtc->slave_pixmap);
assert(crtc->bo->active_scanout);
assert(crtc->bo->refcnt >= crtc->bo->active_scanout);
assert(crtc->flip_bo == NULL);
if (data == NULL && crtc->bo == bo)
goto next_crtc;
arg.crtc_id = __sna_crtc_id(crtc);
arg.fb_id = get_fb(sna, bo, width, height);
if (arg.fb_id == 0) {
assert(count == 0);
return 0;
}
fixup = 0;
crtc_offset = crtc->base->y << 16 | crtc->base->x;
if (bo->pitch != crtc->bo->pitch || crtc_offset != crtc->offset) {
DBG(("%s: changing pitch (%d == %d) or offset (%x == %x)\n",
__FUNCTION__,
bo->pitch, crtc->bo->pitch,
crtc_offset, crtc->offset));
fixup_flip:
fixup = 1;
if (crtc->bo != bo && sna_crtc_flip(sna, crtc, bo, crtc->base->x, crtc->base->y)) {
update_scanout:
DBG(("%s: removing handle=%d [active_scanout=%d] from scanout, installing handle=%d [active_scanout=%d]\n",
__FUNCTION__, crtc->bo->handle, crtc->bo->active_scanout,
bo->handle, bo->active_scanout));
assert(crtc->bo->active_scanout);
assert(crtc->bo->refcnt >= crtc->bo->active_scanout);
crtc->bo->active_scanout--;
kgem_bo_destroy(&sna->kgem, crtc->bo);
if (crtc->shadow_bo) {
kgem_bo_destroy(&sna->kgem, crtc->shadow_bo);
crtc->shadow_bo = NULL;
}
crtc->bo = kgem_bo_reference(bo);
crtc->bo->active_scanout++;
if (data == NULL)
goto next_crtc;
/* queue a flip in order to send the event */
} else
goto error;
}
/* Only the reference crtc will finally deliver its page flip
* completion event. All other crtc's events will be discarded.
*/
if (data) {
arg.user_data = (uintptr_t)crtc;
arg.flags = DRM_MODE_PAGE_FLIP_EVENT;
} else {
arg.user_data = 0;
arg.flags = DRM_MODE_PAGE_FLIP_ASYNC;
}
arg.reserved = 0;
retry_flip:
DBG(("%s: crtc %d id=%d, pipe=%d --> fb %d\n",
__FUNCTION__, i, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc), arg.fb_id));
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) {
ERR(("%s: pageflip failed with err=%d\n", __FUNCTION__, errno));
if (errno == EBUSY) {
struct drm_mode_crtc mode;
memset(&mode, 0, sizeof(mode));
mode.crtc_id = __sna_crtc_id(crtc);
drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode);
DBG(("%s: crtc=%d, valid?=%d, fb attached?=%d, expected=%d\n",
__FUNCTION__,
mode.crtc_id, mode.mode_valid,
mode.fb_id, fb_id(crtc->bo)));
if (mode.fb_id != fb_id(crtc->bo))
goto fixup_flip;
if (count == 0)
return 0;
DBG(("%s: throttling on busy flip / waiting for kernel to catch up\n", __FUNCTION__));
drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_THROTTLE, 0);
sna->kgem.need_throttle = false;
goto retry_flip;
}
if (!fixup)
goto fixup_flip;
error:
xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
"page flipping failed, on CRTC:%d (pipe=%d), disabling %s page flips\n",
__sna_crtc_id(crtc), __sna_crtc_pipe(crtc), data ? "synchronous": "asynchronous");
if (count)
sna_mode_restore(sna);
sna->flags &= ~(data ? SNA_HAS_FLIP : SNA_HAS_ASYNC_FLIP);
return 0;
}
if (data) {
assert(crtc->flip_bo == NULL);
crtc->flip_handler = handler;
crtc->flip_data = data;
crtc->flip_bo = kgem_bo_reference(bo);
crtc->flip_bo->active_scanout++;
crtc->flip_serial = crtc->mode_serial;
crtc->flip_pending = true;
sna->mode.flip_active++;
DBG(("%s: recording flip on CRTC:%d handle=%d, active_scanout=%d, serial=%d\n",
__FUNCTION__, __sna_crtc_id(crtc), crtc->flip_bo->handle, crtc->flip_bo->active_scanout, crtc->flip_serial));
} else
goto update_scanout;
next_crtc:
count++;
}
DBG(("%s: page flipped %d crtcs\n", __FUNCTION__, count));
return count;
}
static const xf86CrtcConfigFuncsRec sna_mode_funcs = {
sna_mode_resize
};
static void set_size_range(struct sna *sna)
{
/* We lie slightly as we expect no single monitor to exceed the
* crtc limits, so if the mode exceeds the scanout restrictions,
* we will quietly convert that to per-crtc pixmaps.
*/
xf86CrtcSetSizeRange(sna->scrn, 8, 8, INT16_MAX, INT16_MAX);
}
#if HAS_GAMMA
static void set_gamma(uint16_t *curve, int size, double value)
{
int i;
value = 1/value;
for (i = 0; i < size; i++)
curve[i] = 256*(size-1)*pow(i/(double)(size-1), value);
}
static void output_set_gamma(xf86OutputPtr output, xf86CrtcPtr crtc)
{
XF86ConfMonitorPtr mon = output->conf_monitor;
if (!mon)
return;
DBG(("%s: red=%f\n", __FUNCTION__, mon->mon_gamma_red));
if (mon->mon_gamma_red >= GAMMA_MIN &&
mon->mon_gamma_red <= GAMMA_MAX &&
mon->mon_gamma_red != 1.0)
set_gamma(crtc->gamma_red, crtc->gamma_size,
mon->mon_gamma_red);
DBG(("%s: green=%f\n", __FUNCTION__, mon->mon_gamma_green));
if (mon->mon_gamma_green >= GAMMA_MIN &&
mon->mon_gamma_green <= GAMMA_MAX &&
mon->mon_gamma_green != 1.0)
set_gamma(crtc->gamma_green, crtc->gamma_size,
mon->mon_gamma_green);
DBG(("%s: blue=%f\n", __FUNCTION__, mon->mon_gamma_blue));
if (mon->mon_gamma_blue >= GAMMA_MIN &&
mon->mon_gamma_blue <= GAMMA_MAX &&
mon->mon_gamma_blue != 1.0)
set_gamma(crtc->gamma_blue, crtc->gamma_size,
mon->mon_gamma_blue);
}
static void crtc_init_gamma(xf86CrtcPtr crtc)
{
uint16_t *gamma;
/* Initialize the gamma ramps */
gamma = NULL;
if (crtc->gamma_size == 256)
gamma = crtc->gamma_red;
if (gamma == NULL)
gamma = malloc(3 * 256 * sizeof(uint16_t));
if (gamma) {
struct sna *sna = to_sna(crtc->scrn);
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
struct drm_mode_crtc_lut lut;
bool gamma_set = false;
assert(sna_crtc);
lut.crtc_id = __sna_crtc_id(sna_crtc);
lut.gamma_size = 256;
lut.red = (uintptr_t)(gamma);
lut.green = (uintptr_t)(gamma + 256);
lut.blue = (uintptr_t)(gamma + 2 * 256);
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETGAMMA, &lut) == 0) {
VG(VALGRIND_MAKE_MEM_DEFINED(gamma, 3*256*sizeof(gamma[0])));
gamma_set =
gamma[256 - 1] &&
gamma[2*256 - 1] &&
gamma[3*256 - 1];
}
DBG(("%s: CRTC:%d, pipe=%d: gamma set?=%d\n",
__FUNCTION__, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc),
gamma_set));
if (!gamma_set) {
int i;
for (i = 0; i < 256; i++) {
gamma[i] = i << 8;
gamma[256 + i] = i << 8;
gamma[2*256 + i] = i << 8;
}
}
if (gamma != crtc->gamma_red) {
free(crtc->gamma_red);
crtc->gamma_red = gamma;
crtc->gamma_green = gamma + 256;
crtc->gamma_blue = gamma + 2*256;
}
}
}
#else
static void output_set_gamma(xf86OutputPtr output, xf86CrtcPtr crtc) { }
static void crtc_init_gamma(xf86CrtcPtr crtc) { }
#endif
static const char *preferred_mode(xf86OutputPtr output)
{
const char *mode;
mode = xf86GetOptValString(output->options, OPTION_PREFERRED_MODE);
if (mode)
return mode;
if (output->scrn->display->modes && *output->scrn->display->modes)
return *output->scrn->display->modes;
return NULL;
}
static bool sna_probe_initial_configuration(struct sna *sna)
{
ScrnInfoPtr scrn = sna->scrn;
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
int crtc_active, crtc_enabled;
int width, height;
int i, j;
assert((sna->flags & SNA_IS_HOSTED) == 0);
if ((sna->flags & SNA_IS_SLAVED) == 0) {
const int user_overrides[] = {
OPTION_POSITION,
OPTION_BELOW,
OPTION_RIGHT_OF,
OPTION_ABOVE,
OPTION_LEFT_OF,
OPTION_ROTATE,
OPTION_PANNING,
};
if (xf86ReturnOptValBool(sna->Options, OPTION_REPROBE, FALSE)) {
DBG(("%s: user requests reprobing\n", __FUNCTION__));
return false;
}
/* First scan through all outputs and look for user overrides */
for (i = 0; i < sna->mode.num_real_output; i++) {
xf86OutputPtr output = config->output[i];
for (j = 0; j < ARRAY_SIZE(user_overrides); j++) {
if (xf86GetOptValString(output->options, user_overrides[j])) {
DBG(("%s: user placement [%d] for %s\n",
__FUNCTION__,
user_overrides[j],
output->name));
return false;
}
}
}
}
/* Copy the existing modes on each CRTCs */
crtc_active = crtc_enabled = 0;
for (i = 0; i < sna->mode.num_real_crtc; i++) {
xf86CrtcPtr crtc = config->crtc[i];
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
struct drm_mode_crtc mode;
crtc->enabled = FALSE;
crtc->desiredMode.status = MODE_NOMODE;
crtc_init_gamma(crtc);
/* Retrieve the current mode */
VG_CLEAR(mode);
mode.crtc_id = __sna_crtc_id(sna_crtc);
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode))
continue;
DBG(("%s: CRTC:%d, pipe=%d: has mode?=%d\n", __FUNCTION__,
__sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc),
mode.mode_valid && mode.mode.clock));
if (!mode.mode_valid || mode.mode.clock == 0)
continue;
mode_from_kmode(scrn, &mode.mode, &crtc->desiredMode);
crtc->desiredRotation = sna_crtc->primary.rotation.current;
crtc->desiredX = mode.x;
crtc->desiredY = mode.y;
crtc->desiredTransformPresent = FALSE;
crtc_active++;
}
/* Reconstruct outputs pointing to active CRTC */
for (i = 0; i < sna->mode.num_real_output; i++) {
xf86OutputPtr output = config->output[i];
uint32_t crtc_id;
assert(to_sna_output(output));
crtc_id = (uintptr_t)output->crtc;
output->crtc = NULL;
output->status = XF86OutputStatusUnknown;
if (sna->flags & SNA_IS_SLAVED)
continue;
if (crtc_id == 0) {
DBG(("%s: not using output %s, disconnected\n",
__FUNCTION__, output->name));
continue;
}
if (xf86ReturnOptValBool(output->options, OPTION_DISABLE, 0)) {
DBG(("%s: not using output %s, manually disabled\n",
__FUNCTION__, output->name));
continue;
}
for (j = 0; j < sna->mode.num_real_crtc; j++) {
xf86CrtcPtr crtc = config->crtc[j];
assert(to_sna_crtc(crtc));
if (sna_crtc_id(crtc) != crtc_id)
continue;
if (crtc->desiredMode.status == MODE_OK) {
DisplayModePtr M;
const char *pref;
pref = preferred_mode(output);
if (pref && strcmp(pref, crtc->desiredMode.name)) {
DBG(("%s: output %s user requests a different preferred mode %s, found %s\n",
__FUNCTION__, output->name, pref, crtc->desiredMode.name));
return false;
}
xf86DrvMsg(scrn->scrnIndex, X_PROBED,
"Output %s using initial mode %s on pipe %d\n",
output->name,
crtc->desiredMode.name,
sna_crtc_pipe(crtc));
output->crtc = crtc;
output->status = XF86OutputStatusConnected;
crtc->enabled = TRUE;
crtc_enabled++;
output_set_gamma(output, crtc);
if (output->conf_monitor) {
output->mm_width = output->conf_monitor->mon_width;
output->mm_height = output->conf_monitor->mon_height;
}
#if 0
sna_output_attach_edid(output);
sna_output_attach_tile(output);
#endif
if (output->mm_width == 0 || output->mm_height == 0) {
output->mm_height = (crtc->desiredMode.VDisplay * 254) / (10*DEFAULT_DPI);
output->mm_width = (crtc->desiredMode.HDisplay * 254) / (10*DEFAULT_DPI);
}
M = calloc(1, sizeof(DisplayModeRec));
if (M) {
*M = crtc->desiredMode;
M->name = strdup(M->name);
output->probed_modes =
xf86ModesAdd(output->probed_modes, M);
}
}
break;
}
if (j == sna->mode.num_real_crtc) {
/* Can not find the earlier associated CRTC, bail */
DBG(("%s: existing setup conflicts with output assignment (Zaphod), reprobing\n",
__FUNCTION__));
return false;
}
}
if (crtc_active != crtc_enabled) {
DBG(("%s: only enabled %d out of %d active CRTC, forcing a reconfigure\n",
__FUNCTION__, crtc_enabled, crtc_active));
return false;
}
width = height = 0;
for (i = 0; i < sna->mode.num_real_crtc; i++) {
xf86CrtcPtr crtc = config->crtc[i];
int w, h;
if (!crtc->enabled)
continue;
w = crtc->desiredX + crtc->desiredMode.HDisplay;
if (w > width)
width = w;
h = crtc->desiredY + crtc->desiredMode.VDisplay;
if (h > height)
height = h;
}
/* Prefer the native panel size if any */
if (!width || !height) {
for (i = 0; i < sna->mode.num_real_output; i++) {
xf86OutputPtr output = config->output[i];
struct sna_output *sna_output = to_sna_output(output);
if (!sna_output->is_panel)
continue;
DBG(("%s: querying panel '%s' for preferred unattached size\n",
__FUNCTION__, output->name));
if (sna_output_detect(output) != XF86OutputStatusConnected)
continue;
if (sna_output->num_modes == 0)
continue;
width = sna_output->modes[0].hdisplay;
height = sna_output->modes[0].vdisplay;
DBG(("%s: panel '%s' is %dx%d\n",
__FUNCTION__, output->name, width, height));
break;
}
}
if (!width || !height) {
width = 1024;
height = 768;
}
scrn->display->frameX0 = 0;
scrn->display->frameY0 = 0;
scrn->display->virtualX = width;
scrn->display->virtualY = height;
scrn->virtualX = width;
scrn->virtualY = height;
xf86SetScrnInfoModes(sna->scrn);
DBG(("%s: SetScrnInfoModes = %p\n", __FUNCTION__, scrn->modes));
return scrn->modes != NULL;
}
static void
sanitize_outputs(struct sna *sna)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
int i;
for (i = 0; i < config->num_output; i++)
config->output[i]->crtc = NULL;
}
static bool has_flip(struct sna *sna)
{
drm_i915_getparam_t gp;
int v;
if (sna->flags & SNA_NO_FLIP)
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 bool has_flip__async(struct sna *sna)
{
#define DRM_CAP_ASYNC_PAGE_FLIP 0x7
struct local_get_cap {
uint64_t name;
uint64_t value;
} cap = { DRM_CAP_ASYNC_PAGE_FLIP };
if (sna->flags & SNA_NO_FLIP)
return false;
if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_GET_CAP, &cap) == 0)
return cap.value > 0;
return false;
}
static void
probe_capabilities(struct sna *sna)
{
sna->flags &= ~(SNA_HAS_FLIP | SNA_HAS_ASYNC_FLIP);
if (has_flip(sna))
sna->flags |= SNA_HAS_FLIP;
if (has_flip__async(sna) && (sna->flags & SNA_TEAR_FREE) == 0)
sna->flags |= SNA_HAS_ASYNC_FLIP;
DBG(("%s: page flips? %s, async? %s\n", __FUNCTION__,
sna->flags & SNA_HAS_FLIP ? "enabled" : "disabled",
sna->flags & SNA_HAS_ASYNC_FLIP ? "enabled" : "disabled"));
}
void
sna_crtc_config_notify(ScreenPtr screen)
{
struct sna *sna = to_sna_from_screen(screen);
DBG(("%s(dirty?=%d)\n", __FUNCTION__, sna->mode.dirty));
if (!sna->mode.dirty)
return;
if (disable_unused_crtc(sna)) {
/* This will have recursed, so simply bail at this point */
assert(sna->mode.dirty == false);
#ifdef RANDR_12_INTERFACE
xf86RandR12TellChanged(screen);
#endif
return;
}
update_flush_interval(sna);
sna->cursor.disable = false; /* Reset HW cursor until the next fail */
sna_cursors_reload(sna);
probe_capabilities(sna);
sna_present_update(sna);
sna->mode.dirty = false;
}
#if HAS_PIXMAP_SHARING
#define sna_setup_provider(scrn) xf86ProviderSetup(scrn, NULL, "Intel")
#else
#define sna_setup_provider(scrn)
#endif
bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna)
{
drmModeResPtr res;
int num_fake = 0;
int i;
if (sna->flags & SNA_IS_HOSTED) {
sna_setup_provider(scrn);
return true;
}
probe_capabilities(sna);
sna->mode.hidden = 1;
if (!xf86GetOptValInteger(sna->Options, OPTION_VIRTUAL, &num_fake))
num_fake = 1;
res = drmModeGetResources(sna->kgem.fd);
if (res &&
(res->count_crtcs == 0 ||
res->count_encoders == 0 ||
res->count_connectors == 0)) {
drmModeFreeResources(res);
res = NULL;
}
if (res) {
xf86CrtcConfigPtr xf86_config;
DBG(("%s: found %d CRTC, %d encoders, %d connectors\n",
__FUNCTION__, res->count_crtcs, res->count_encoders, res->count_connectors));
assert(res->count_crtcs);
assert(res->count_connectors);
xf86CrtcConfigInit(scrn, &sna_mode_funcs);
xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
xf86_config->xf86_crtc_notify = sna_crtc_config_notify;
xf86_config->compat_output = 0;
for (i = 0; i < res->count_crtcs; i++)
if (!sna_crtc_add(scrn, res->crtcs[i]))
return false;
sna->mode.num_real_crtc = xf86_config->num_crtc;
sna->mode.num_real_encoder = res->count_encoders;
sna->mode.encoders = res->encoders;
res->encoders = NULL;
for (i = 0; i < res->count_connectors; i++)
if (sna_output_add(sna, res->connectors[i], 0) < 0)
return false;
sna->mode.num_real_output = xf86_config->num_output;
sna->mode.max_crtc_width = res->max_width;
sna->mode.max_crtc_height = res->max_height;
RegionEmpty(&sna->mode.shadow_region);
RegionEmpty(&sna->mode.shadow_cancel);
list_init(&sna->mode.shadow_crtc);
drmModeFreeResources(res);
sna_cursor_pre_init(sna);
sna_backlight_pre_init(sna);
set_size_range(sna);
} else {
if (num_fake == 0)
num_fake = 1;
}
if (!sna_mode_fake_init(sna, num_fake))
return false;
if (!sna_probe_initial_configuration(sna)) {
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
sanitize_outputs(sna);
if (config->num_crtc && config->num_output) {
if (!xf86ReturnOptValBool(config->output[0]->options,
OPTION_PRIMARY, FALSE))
sort_config_outputs(sna);
xf86InitialConfiguration(scrn, TRUE);
}
}
sort_config_outputs(sna);
sna_setup_provider(scrn);
return scrn->modes != NULL;
}
bool
sna_mode_wants_tear_free(struct sna *sna)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
int i;
for (i = 0; i < sna->mode.num_real_output; i++) {
struct sna_output *output = to_sna_output(config->output[i]);
int id = find_property(sna, output, "Panel Self-Refresh");
if (id !=-1 && output->prop_values[id] != -1) {
DBG(("%s: Panel Self-Refresh detected on %s\n",
__FUNCTION__, config->output[i]->name));
return true;
}
}
return false;
}
void
sna_mode_set_primary(struct sna *sna)
{
#ifdef RANDR_12_INTERFACE
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
rrScrPrivPtr rr = rrGetScrPriv(xf86ScrnToScreen(sna->scrn));
int i;
if (rr == NULL || rr->primaryOutput)
return;
for (i = 0; i < sna->mode.num_real_output; i++) {
xf86OutputPtr output = config->output[i];
if (!xf86ReturnOptValBool(output->options, OPTION_PRIMARY, FALSE))
continue;
DBG(("%s: setting PrimaryOutput %s\n", __FUNCTION__, output->name));
rr->primaryOutput = output->randr_output;
RROutputChanged(rr->primaryOutput, FALSE);
rr->layoutChanged = TRUE;
break;
}
#endif
}
bool
sna_mode_disable(struct sna *sna)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
int i;
if (sna->flags & SNA_IS_HOSTED)
return false;
if (!sna->scrn->vtSema)
return false;
sna_disable_cursors(sna->scrn);
for (i = 0; i < sna->mode.num_real_crtc; i++)
sna_crtc_disable(config->crtc[i], false);
assert(sna->mode.front_active == 0);
sna_mode_wakeup(sna);
kgem_clean_scanout_cache(&sna->kgem);
return true;
}
void
sna_mode_enable(struct sna *sna)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
int i;
DBG(("%s\n", __FUNCTION__));
if (sna->flags & SNA_IS_HOSTED)
return;
if (!sna->scrn->vtSema)
return;
if (sna->mode.hidden) {
DBG(("%s: hidden outputs\n", __FUNCTION__));
return;
}
for (i = 0; i < sna->mode.num_real_crtc; i++) {
xf86CrtcPtr crtc = config->crtc[i];
DBG(("%s: crtc[%d].enabled?=%d\n", __FUNCTION__, i, crtc->enabled));
assert(to_sna_crtc(crtc) != NULL);
if (!crtc->enabled)
continue;
if (crtc->mode.Clock == 0)
continue;
__sna_crtc_set_mode(crtc);
}
update_flush_interval(sna);
sna_cursors_reload(sna);
sna->mode.dirty = false;
}
static void sna_randr_close(struct sna *sna)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
int n;
/* The RR structs are freed early during CloseScreen as they
* are tracked as Resources. However, we may be tempted to
* access them during shutdown so decouple them now.
*/
for (n = 0; n < config->num_output; n++)
config->output[n]->randr_output = NULL;
for (n = 0; n < config->num_crtc; n++)
config->crtc[n]->randr_crtc = NULL;
}
void
sna_mode_close(struct sna *sna)
{
sna_randr_close(sna);
sna_mode_wakeup(sna);
if (sna->flags & SNA_IS_HOSTED)
return;
sna_mode_reset(sna);
sna_cursor_close(sna);
sna_cursors_fini(sna);
sna_backlight_close(sna);
sna->mode.dirty = false;
}
void
sna_mode_fini(struct sna *sna)
{
free(sna->mode.encoders);
}
static bool sna_box_intersect(BoxPtr r, const BoxRec *a, const BoxRec *b)
{
r->x1 = a->x1 > b->x1 ? a->x1 : b->x1;
r->x2 = a->x2 < b->x2 ? a->x2 : b->x2;
if (r->x1 >= r->x2)
return false;
r->y1 = a->y1 > b->y1 ? a->y1 : b->y1;
r->y2 = a->y2 < b->y2 ? a->y2 : b->y2;
DBG(("%s: (%d, %d), (%d, %d) intersect (%d, %d), (%d, %d) = (%d, %d), (%d, %d)\n",
__FUNCTION__,
a->x1, a->y1, a->x2, a->y2,
b->x1, b->y1, b->x2, b->y2,
r->x1, r->y1, r->x2, r->y2));
if (r->y1 >= r->y2)
return false;
return true;
}
static int sna_box_area(const BoxRec *box)
{
return (int)(box->x2 - box->x1) * (int)(box->y2 - box->y1);
}
/*
* Return the crtc covering 'box'. If two crtcs cover a portion of
* 'box', then prefer 'desired'. If 'desired' is NULL, then prefer the crtc
* with greater coverage
*/
xf86CrtcPtr
sna_covering_crtc(struct sna *sna, const BoxRec *box, xf86CrtcPtr desired)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
xf86CrtcPtr best_crtc = NULL;
int best_coverage = -1, c;
if (sna->flags & SNA_IS_HOSTED)
return NULL;
/* If we do not own the VT, we do not own the CRTC either */
if (!sna->scrn->vtSema) {
DBG(("%s: none, VT switched\n", __FUNCTION__));
return NULL;
}
if (sna->mode.hidden) {
DBG(("%s: none, hidden outputs\n", __FUNCTION__));
return NULL;
}
DBG(("%s for box=(%d, %d), (%d, %d)\n",
__FUNCTION__, box->x1, box->y1, box->x2, box->y2));
if (desired == NULL) {
rrScrPrivPtr rr = rrGetScrPriv(xf86ScrnToScreen(sna->scrn));
if (rr && rr->primaryOutput) {
xf86OutputPtr output = rr->primaryOutput->devPrivate;
DBG(("%s: have PrimaryOutput? %d marking as desired\n", __FUNCTION__, output->crtc != NULL));
desired = output->crtc;
}
}
if (desired && to_sna_crtc(desired) && to_sna_crtc(desired)->bo) {
BoxRec cover_box;
if (sna_box_intersect(&cover_box, &desired->bounds, box)) {
DBG(("%s: box overlaps desired crtc: (%d, %d), (%d, %d)\n",
__FUNCTION__,
cover_box.x1, cover_box.y1,
cover_box.x2, cover_box.y2));
return desired;
}
best_crtc = desired;
best_coverage = 0;
}
for (c = 0; c < sna->mode.num_real_crtc; c++) {
xf86CrtcPtr crtc = config->crtc[c];
BoxRec cover_box;
int coverage;
assert(to_sna_crtc(crtc));
/* If the CRTC is off, treat it as not covering */
if (to_sna_crtc(crtc)->bo == NULL) {
DBG(("%s: crtc %d off, skipping\n", __FUNCTION__, c));
continue;
}
DBG(("%s: crtc %d: (%d, %d), (%d, %d)\n",
__FUNCTION__, c,
crtc->bounds.x1, crtc->bounds.y1,
crtc->bounds.x2, crtc->bounds.y2));
if (*(const uint64_t *)box == *(uint64_t *)&crtc->bounds) {
DBG(("%s: box exactly matches crtc [%d]\n",
__FUNCTION__, c));
return crtc;
}
if (!sna_box_intersect(&cover_box, &crtc->bounds, box))
continue;
DBG(("%s: box instersects (%d, %d), (%d, %d) of crtc %d\n",
__FUNCTION__,
cover_box.x1, cover_box.y1,
cover_box.x2, cover_box.y2,
c));
coverage = sna_box_area(&cover_box);
DBG(("%s: box covers %d of crtc %d\n",
__FUNCTION__, coverage, c));
if (coverage > best_coverage) {
best_crtc = crtc;
best_coverage = coverage;
}
}
DBG(("%s: best crtc = %p, coverage = %d\n",
__FUNCTION__, best_crtc, best_coverage));
return best_crtc;
}
xf86CrtcPtr sna_primary_crtc(struct sna *sna)
{
rrScrPrivPtr rr = rrGetScrPriv(xf86ScrnToScreen(sna->scrn));
if (rr && rr->primaryOutput) {
xf86OutputPtr output = rr->primaryOutput->devPrivate;
if (output->crtc && to_sna_crtc(output->crtc))
return output->crtc;
}
if (sna->mode.num_real_crtc)
return XF86_CRTC_CONFIG_PTR(sna->scrn)->crtc[0];
return NULL;
}
#define MI_LOAD_REGISTER_IMM (0x22<<23)
static bool sna_emit_wait_for_scanline_hsw(struct sna *sna,
xf86CrtcPtr crtc,
int pipe, int y1, int y2,
bool full_height)
{
uint32_t event;
uint32_t *b;
if (!sna->kgem.has_secure_batches)
return false;
b = kgem_get_batch(&sna->kgem);
sna->kgem.nbatch += 17;
switch (pipe) {
default: assert(0);
case 0: event = 1 << 0; break;
case 1: event = 1 << 8; break;
case 2: event = 1 << 14; break;
}
b[0] = MI_LOAD_REGISTER_IMM | 1;
b[1] = 0x44050; /* DERRMR */
b[2] = ~event;
b[3] = MI_LOAD_REGISTER_IMM | 1;
b[4] = 0xa188; /* FORCEWAKE_MT */
b[5] = 2 << 16 | 2;
/* The documentation says that the LOAD_SCAN_LINES command
* always comes in pairs. Don't ask me why. */
switch (pipe) {
default: assert(0);
case 0: event = 0 << 19; break;
case 1: event = 1 << 19; break;
case 2: event = 4 << 19; break;
}
b[8] = b[6] = MI_LOAD_SCAN_LINES_INCL | event;
b[9] = b[7] = (y1 << 16) | (y2-1);
switch (pipe) {
default: assert(0);
case 0: event = 1 << 0; break;
case 1: event = 1 << 8; break;
case 2: event = 1 << 14; break;
}
b[10] = MI_WAIT_FOR_EVENT | event;
b[11] = MI_LOAD_REGISTER_IMM | 1;
b[12] = 0xa188; /* FORCEWAKE_MT */
b[13] = 2 << 16;
b[14] = MI_LOAD_REGISTER_IMM | 1;
b[15] = 0x44050; /* DERRMR */
b[16] = ~0;
sna->kgem.batch_flags |= I915_EXEC_SECURE;
return true;
}
static bool sna_emit_wait_for_scanline_ivb(struct sna *sna,
xf86CrtcPtr crtc,
int pipe, int y1, int y2,
bool full_height)
{
uint32_t event, *b;
if (!sna->kgem.has_secure_batches)
return false;
assert(y1 >= 0);
assert(y2 > y1);
assert(sna->kgem.mode);
/* Always program one less than the desired value */
if (--y1 < 0)
y1 = crtc->bounds.y2;
y2--;
switch (pipe) {
default:
assert(0);
case 0:
event = 1 << (full_height ? 3 : 0);
break;
case 1:
event = 1 << (full_height ? 11 : 8);
break;
case 2:
event = 1 << (full_height ? 21 : 14);
break;
}
b = kgem_get_batch(&sna->kgem);
/* Both the LRI and WAIT_FOR_EVENT must be in the same cacheline */
if (((sna->kgem.nbatch + 6) >> 4) != (sna->kgem.nbatch + 10) >> 4) {
int dw = sna->kgem.nbatch + 6;
dw = ALIGN(dw, 16) - dw;
while (dw--)
*b++ = MI_NOOP;
}
b[0] = MI_LOAD_REGISTER_IMM | 1;
b[1] = 0x44050; /* DERRMR */
b[2] = ~event;
b[3] = MI_LOAD_REGISTER_IMM | 1;
b[4] = 0xa188; /* FORCEWAKE_MT */
b[5] = 2 << 16 | 2;
b[6] = MI_LOAD_REGISTER_IMM | 1;
b[7] = 0x70068 + 0x1000 * pipe;
b[8] = (1 << 31) | (1 << 30) | (y1 << 16) | y2;
b[9] = MI_WAIT_FOR_EVENT | event;
b[10] = MI_LOAD_REGISTER_IMM | 1;
b[11] = 0xa188; /* FORCEWAKE_MT */
b[12] = 2 << 16;
b[13] = MI_LOAD_REGISTER_IMM | 1;
b[14] = 0x44050; /* DERRMR */
b[15] = ~0;
sna->kgem.nbatch = b - sna->kgem.batch + 16;
sna->kgem.batch_flags |= I915_EXEC_SECURE;
return true;
}
static bool sna_emit_wait_for_scanline_gen6(struct sna *sna,
xf86CrtcPtr crtc,
int pipe, int y1, int y2,
bool full_height)
{
uint32_t *b;
uint32_t event;
if (!sna->kgem.has_secure_batches)
return false;
assert(y1 >= 0);
assert(y2 > y1);
assert(sna->kgem.mode == KGEM_RENDER);
/* Always program one less than the desired value */
if (--y1 < 0)
y1 = crtc->bounds.y2;
y2--;
/* The scanline granularity is 3 bits */
y1 &= ~7;
y2 &= ~7;
if (y2 == y1)
return false;
event = 1 << (3*full_height + pipe*8);
b = kgem_get_batch(&sna->kgem);
sna->kgem.nbatch += 16;
b[0] = MI_LOAD_REGISTER_IMM | 1;
b[1] = 0x44050; /* DERRMR */
b[2] = ~event;
b[3] = MI_LOAD_REGISTER_IMM | 1;
b[4] = 0x4f100; /* magic */
b[5] = (1 << 31) | (1 << 30) | pipe << 29 | (y1 << 16) | y2;
b[6] = MI_LOAD_REGISTER_IMM | 1;
b[7] = 0x2050; /* PSMI_CTL(rcs) */
b[8] = 1 << 16 | 1;
b[9] = MI_WAIT_FOR_EVENT | event;
b[10] = MI_LOAD_REGISTER_IMM | 1;
b[11] = 0x2050; /* PSMI_CTL(rcs) */
b[12] = 1 << 16;
b[13] = MI_LOAD_REGISTER_IMM | 1;
b[14] = 0x44050; /* DERRMR */
b[15] = ~0;
sna->kgem.batch_flags |= I915_EXEC_SECURE;
return true;
}
static bool sna_emit_wait_for_scanline_gen4(struct sna *sna,
xf86CrtcPtr crtc,
int pipe, int y1, int y2,
bool full_height)
{
uint32_t event;
uint32_t *b;
if (pipe == 0) {
if (full_height)
event = MI_WAIT_FOR_PIPEA_SVBLANK;
else
event = MI_WAIT_FOR_PIPEA_SCAN_LINE_WINDOW;
} else {
if (full_height)
event = MI_WAIT_FOR_PIPEB_SVBLANK;
else
event = MI_WAIT_FOR_PIPEB_SCAN_LINE_WINDOW;
}
b = kgem_get_batch(&sna->kgem);
sna->kgem.nbatch += 5;
/* The documentation says that the LOAD_SCAN_LINES command
* always comes in pairs. Don't ask me why. */
b[2] = b[0] = MI_LOAD_SCAN_LINES_INCL | pipe << 20;
b[3] = b[1] = (y1 << 16) | (y2-1);
b[4] = MI_WAIT_FOR_EVENT | event;
return true;
}
static bool sna_emit_wait_for_scanline_gen2(struct sna *sna,
xf86CrtcPtr crtc,
int pipe, int y1, int y2,
bool full_height)
{
uint32_t *b;
/*
* Pre-965 doesn't have SVBLANK, so we need a bit
* of extra time for the blitter to start up and
* do its job for a full height blit
*/
if (full_height)
y2 -= 2;
b = kgem_get_batch(&sna->kgem);
sna->kgem.nbatch += 5;
/* The documentation says that the LOAD_SCAN_LINES command
* always comes in pairs. Don't ask me why. */
b[2] = b[0] = MI_LOAD_SCAN_LINES_INCL | pipe << 20;
b[3] = b[1] = (y1 << 16) | (y2-1);
b[4] = MI_WAIT_FOR_EVENT | 1 << (1 + 4*pipe);
return true;
}
bool
sna_wait_for_scanline(struct sna *sna,
PixmapPtr pixmap,
xf86CrtcPtr crtc,
const BoxRec *clip)
{
bool full_height;
int y1, y2, pipe;
bool ret;
assert(crtc != NULL);
assert(to_sna_crtc(crtc) != NULL);
assert(to_sna_crtc(crtc)->bo != NULL);
assert(pixmap == sna->front);
if (sna->flags & SNA_NO_VSYNC)
return false;
/*
* Make sure we don't wait for a scanline that will
* never occur
*/
y1 = clip->y1 - crtc->bounds.y1;
if (y1 < 0)
y1 = 0;
y2 = clip->y2 - crtc->bounds.y1;
if (y2 > crtc->bounds.y2 - crtc->bounds.y1)
y2 = crtc->bounds.y2 - crtc->bounds.y1;
DBG(("%s: clipped range = %d, %d\n", __FUNCTION__, y1, y2));
if (y2 <= y1 + 4)
return false;
full_height = y1 == 0 && y2 == crtc->bounds.y2 - crtc->bounds.y1;
if (crtc->mode.Flags & V_INTERLACE) {
/* DSL count field lines */
y1 /= 2;
y2 /= 2;
}
pipe = sna_crtc_pipe(crtc);
DBG(("%s: pipe=%d, y1=%d, y2=%d, full_height?=%d\n",
__FUNCTION__, pipe, y1, y2, full_height));
if (sna->kgem.gen >= 0110)
ret = false;
else if (sna->kgem.gen == 0101)
ret = false; /* chv, vsync method unknown */
else if (sna->kgem.gen >= 075)
ret = sna_emit_wait_for_scanline_hsw(sna, crtc, pipe, y1, y2, full_height);
else if (sna->kgem.gen == 071)
ret = false; /* vlv, vsync method unknown */
else if (sna->kgem.gen >= 070)
ret = sna_emit_wait_for_scanline_ivb(sna, crtc, pipe, y1, y2, full_height);
else if (sna->kgem.gen >= 060)
ret =sna_emit_wait_for_scanline_gen6(sna, crtc, pipe, y1, y2, full_height);
else if (sna->kgem.gen >= 040)
ret = sna_emit_wait_for_scanline_gen4(sna, crtc, pipe, y1, y2, full_height);
else
ret = sna_emit_wait_for_scanline_gen2(sna, crtc, pipe, y1, y2, full_height);
return ret;
}
void sna_mode_check(struct sna *sna)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
bool disabled = false;
int c, o;
if (sna->flags & SNA_IS_HOSTED)
return;
DBG(("%s: hidden?=%d\n", __FUNCTION__, sna->mode.hidden));
if (sna->mode.hidden)
return;
/* Validate CRTC attachments and force consistency upon the kernel */
for (c = 0; c < sna->mode.num_real_crtc; c++) {
xf86CrtcPtr crtc = config->crtc[c];
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
struct drm_mode_crtc mode;
uint32_t expected[2];
assert(sna_crtc);
#if XF86_CRTC_VERSION >= 3
assert(sna_crtc->bo == NULL || crtc->active);
#endif
expected[0] = sna_crtc->bo ? fb_id(sna_crtc->bo) : 0;
expected[1] = sna_crtc->flip_bo ? fb_id(sna_crtc->flip_bo) : -1;
VG_CLEAR(mode);
mode.crtc_id = __sna_crtc_id(sna_crtc);
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode))
continue;
DBG(("%s: crtc=%d, valid?=%d, fb attached?=%d, expected=(%d or %d)\n",
__FUNCTION__,
mode.crtc_id, mode.mode_valid,
mode.fb_id, expected[0], expected[1]));
if (mode.fb_id != expected[0] && mode.fb_id != expected[1]) {
xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
"%s: invalid state found on pipe %d, disabling CRTC:%d\n",
__FUNCTION__, __sna_crtc_pipe(sna_crtc), __sna_crtc_id(sna_crtc));
sna_crtc_disable(crtc, true);
#if XF86_CRTC_VERSION >= 3
crtc->active = FALSE;
#endif
if (crtc->enabled) {
crtc->enabled = FALSE;
disabled = true;
}
for (o = 0; o < sna->mode.num_real_output; o++) {
xf86OutputPtr output = config->output[o];
if (output->crtc != crtc)
continue;
output->funcs->dpms(output, DPMSModeOff);
output->crtc = NULL;
}
}
}
for (o = 0; o < config->num_output; o++) {
xf86OutputPtr output = config->output[o];
struct sna_output *sna_output;
if (output->crtc)
continue;
sna_output = to_sna_output(output);
if (sna_output == NULL)
continue;
sna_output->dpms_mode = DPMSModeOff;
}
update_flush_interval(sna);
if (disabled)
xf86RandR12TellChanged(xf86ScrnToScreen(sna->scrn));
}
static bool
sna_crtc_hide_planes(struct sna *sna, struct sna_crtc *crtc)
{
#define LOCAL_IOCTL_MODE_SETPLANE DRM_IOWR(0xB7, struct local_mode_set_plane)
struct local_mode_set_plane {
uint32_t plane_id;
uint32_t crtc_id;
uint32_t fb_id; /* fb object contains surface format type */
uint32_t flags;
/* Signed dest location allows it to be partially off screen */
int32_t crtc_x, crtc_y;
uint32_t crtc_w, crtc_h;
/* Source values are 16.16 fixed point */
uint32_t src_x, src_y;
uint32_t src_h, src_w;
} s;
if (crtc->primary.id == 0)
return false;
memset(&s, 0, sizeof(s));
s.plane_id = crtc->primary.id;
if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_SETPLANE, &s))
return false;
s.plane_id = crtc->sprite.id;
(void)drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_SETPLANE, &s);
__sna_crtc_disable(sna, crtc);
return true;
}
void sna_mode_reset(struct sna *sna)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
int i;
if (sna->flags & SNA_IS_HOSTED)
return;
DBG(("%s\n", __FUNCTION__));
sna_disable_cursors(sna->scrn);
for (i = 0; i < sna->mode.num_real_crtc; i++)
if (!sna_crtc_hide_planes(sna, to_sna_crtc(config->crtc[i])))
sna_crtc_disable(config->crtc[i], true);
assert(sna->mode.front_active == 0);
for (i = 0; i < sna->mode.num_real_crtc; i++) {
struct sna_crtc *sna_crtc = to_sna_crtc(config->crtc[i]);
assert(sna_crtc != NULL);
/* Force the rotation property to be reset on next use */
rotation_reset(&sna_crtc->primary);
rotation_reset(&sna_crtc->sprite);
}
/* VT switching, likely to be fbcon so make the backlight usable */
for (i = 0; i < sna->mode.num_real_output; i++) {
struct sna_output *sna_output = to_sna_output(config->output[i]);
assert(sna_output != NULL);
if (sna_output->dpms_mode != DPMSModeOff)
continue;
if (!sna_output->backlight.iface)
continue;
sna_output_backlight_set(sna_output,
sna_output->backlight_active_level);
}
/* drain the event queue */
sna_mode_wakeup(sna);
}
static void transformed_box(BoxRec *box, xf86CrtcPtr crtc)
{
box->x1 -= crtc->filter_width >> 1;
box->x2 += crtc->filter_width >> 1;
box->y1 -= crtc->filter_height >> 1;
box->y2 += crtc->filter_height >> 1;
pixman_f_transform_bounds(&crtc->f_framebuffer_to_crtc, box);
if (box->x1 < 0)
box->x1 = 0;
if (box->y1 < 0)
box->y1 = 0;
if (box->x2 > crtc->mode.HDisplay)
box->x2 = crtc->mode.HDisplay;
if (box->y2 > crtc->mode.VDisplay)
box->y2 = crtc->mode.VDisplay;
}
inline static DrawablePtr crtc_source(xf86CrtcPtr crtc, int16_t *sx, int16_t *sy)
{
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
if (sna_crtc->slave_pixmap) {
DBG(("%s: using slave pixmap=%ld, offset (%d, %d)\n",
__FUNCTION__,
sna_crtc->slave_pixmap->drawable.serialNumber,
-crtc->x, -crtc->y));
*sx = -crtc->x;
*sy = -crtc->y;
return &sna_crtc->slave_pixmap->drawable;
} else {
DBG(("%s: using Screen pixmap=%ld\n",
__FUNCTION__,
to_sna(crtc->scrn)->front->drawable.serialNumber));
*sx = *sy = 0;
return &to_sna(crtc->scrn)->front->drawable;
}
}
static void
sna_crtc_redisplay__fallback(xf86CrtcPtr crtc, RegionPtr region, struct kgem_bo *bo)
{
int16_t sx, sy;
struct sna *sna = to_sna(crtc->scrn);
ScreenPtr screen = xf86ScrnToScreen(crtc->scrn);
DrawablePtr draw = crtc_source(crtc, &sx, &sy);
PictFormatPtr format;
PictTransform T;
PicturePtr src, dst;
PixmapPtr pixmap;
int depth, error;
void *ptr;
DBG(("%s: compositing transformed damage boxes, target handle=%d\n", __FUNCTION__, bo->handle));
error = sna_render_format_for_depth(draw->depth);
depth = PIXMAN_FORMAT_DEPTH(error);
format = PictureMatchFormat(screen, depth, error);
if (format == NULL) {
DBG(("%s: can't find format for depth=%d [%08x]\n",
__FUNCTION__, depth, error));
return;
}
DBG(("%s: dst format=%08x, depth=%d, bpp=%d, pitch=%d, size=%dx%d\n",
__FUNCTION__, format->format, depth, draw->bitsPerPixel,
bo->pitch, crtc->mode.HDisplay, crtc->mode.VDisplay));
if (sx | sy)
RegionTranslate(region, sx, sy);
error = !sna_drawable_move_region_to_cpu(draw, region, MOVE_READ);
if (sx | sy)
RegionTranslate(region, -sx, -sy);
if (error)
return;
ptr = kgem_bo_map__gtt(&sna->kgem, bo);
if (ptr == NULL)
return;
pixmap = sna_pixmap_create_unattached(screen, 0, 0, depth);
if (pixmap == NullPixmap)
return;
if (!screen->ModifyPixmapHeader(pixmap,
crtc->mode.HDisplay, crtc->mode.VDisplay,
depth, draw->bitsPerPixel,
bo->pitch, ptr))
goto free_pixmap;
src = CreatePicture(None, draw, format,
0, NULL, serverClient, &error);
if (!src)
goto free_pixmap;
pixman_transform_init_translate(&T, sx << 16, sy << 16);
pixman_transform_multiply(&T, &T, &crtc->crtc_to_framebuffer);
if (!sna_transform_is_integer_translation(&T, &sx, &sy)) {
#define f2d(x) (((double)(x))/65536.)
DBG(("%s: transform=[[%f %f %f], [%f %f %f], [%f %f %f]] (raw [[%x %x %x], [%x %x %x], [%x %x %x]])\n",
__FUNCTION__,
f2d(T.matrix[0][0]),
f2d(T.matrix[0][1]),
f2d(T.matrix[0][2]),
f2d(T.matrix[1][0]),
f2d(T.matrix[1][1]),
f2d(T.matrix[1][2]),
f2d(T.matrix[2][0]),
f2d(T.matrix[2][1]),
f2d(T.matrix[2][2]),
T.matrix[0][0],
T.matrix[0][1],
T.matrix[0][2],
T.matrix[1][0],
T.matrix[1][1],
T.matrix[1][2],
T.matrix[2][0],
T.matrix[2][1],
T.matrix[2][2]));
#undef f2d
error = SetPictureTransform(src, &T);
if (error)
goto free_src;
sx = sy = 0;
}
if (crtc->filter && crtc->transform_in_use)
SetPicturePictFilter(src, crtc->filter,
crtc->params, crtc->nparams);
dst = CreatePicture(None, &pixmap->drawable, format,
0, NULL, serverClient, &error);
if (!dst)
goto free_src;
kgem_bo_sync__gtt(&sna->kgem, bo);
if (sigtrap_get() == 0) { /* paranoia */
const BoxRec *b = region_rects(region);
int n = region_num_rects(region);
do {
BoxRec box;
box = *b++;
transformed_box(&box, crtc);
DBG(("%s: (%d, %d)x(%d, %d) -> (%d, %d), (%d, %d)\n",
__FUNCTION__,
b[-1].x1, b[-1].y1, b[-1].x2-b[-1].x1, b[-1].y2-b[-1].y1,
box.x1, box.y1, box.x2, box.y2));
fbComposite(PictOpSrc, src, NULL, dst,
box.x1 + sx, box.y1 + sy,
0, 0,
box.x1, box.y1,
box.x2 - box.x1, box.y2 - box.y1);
} while (--n);
sigtrap_put();
}
FreePicture(dst, None);
free_src:
FreePicture(src, None);
free_pixmap:
screen->DestroyPixmap(pixmap);
}
static void
sna_crtc_redisplay__composite(xf86CrtcPtr crtc, RegionPtr region, struct kgem_bo *bo)
{
int16_t sx, sy;
struct sna *sna = to_sna(crtc->scrn);
ScreenPtr screen = xf86ScrnToScreen(crtc->scrn);
DrawablePtr draw = crtc_source(crtc, &sx, &sy);
struct sna_composite_op tmp;
PictFormatPtr format;
PictTransform T;
PicturePtr src, dst;
PixmapPtr pixmap;
const BoxRec *b;
int n, depth, error;
DBG(("%s: compositing transformed damage boxes\n", __FUNCTION__));
error = sna_render_format_for_depth(draw->depth);
depth = PIXMAN_FORMAT_DEPTH(error);
format = PictureMatchFormat(screen, depth, error);
if (format == NULL) {
DBG(("%s: can't find format for depth=%d [%08x]\n",
__FUNCTION__, depth, error));
return;
}
DBG(("%s: dst format=%08x, depth=%d, bpp=%d, pitch=%d, size=%dx%d\n",
__FUNCTION__, format->format, depth, draw->bitsPerPixel,
bo->pitch, crtc->mode.HDisplay, crtc->mode.VDisplay));
pixmap = sna_pixmap_create_unattached(screen, 0, 0, depth);
if (pixmap == NullPixmap)
return;
if (!screen->ModifyPixmapHeader(pixmap,
crtc->mode.HDisplay, crtc->mode.VDisplay,
depth, draw->bitsPerPixel,
bo->pitch, NULL))
goto free_pixmap;
if (!sna_pixmap_attach_to_bo(pixmap, kgem_bo_reference(bo))) {
kgem_bo_destroy(&sna->kgem, bo);
goto free_pixmap;
}
src = CreatePicture(None, draw, format,
0, NULL, serverClient, &error);
if (!src)
goto free_pixmap;
pixman_transform_init_translate(&T, sx << 16, sy << 16);
pixman_transform_multiply(&T, &T, &crtc->crtc_to_framebuffer);
if (!sna_transform_is_integer_translation(&T, &sx, &sy)) {
error = SetPictureTransform(src, &T);
if (error)
goto free_src;
sx = sy = 0;
}
if (crtc->filter && crtc->transform_in_use)
SetPicturePictFilter(src, crtc->filter,
crtc->params, crtc->nparams);
dst = CreatePicture(None, &pixmap->drawable, format,
0, NULL, serverClient, &error);
if (!dst)
goto free_src;
ValidatePicture(src);
ValidatePicture(dst);
if (!sna->render.composite(sna,
PictOpSrc, src, NULL, dst,
sx, sy,
0, 0,
0, 0,
crtc->mode.HDisplay, crtc->mode.VDisplay,
COMPOSITE_PARTIAL, memset(&tmp, 0, sizeof(tmp)))) {
DBG(("%s: unsupported operation!\n", __FUNCTION__));
sna_crtc_redisplay__fallback(crtc, region, bo);
goto free_dst;
}
n = region_num_rects(region);
b = region_rects(region);
do {
BoxRec box;
box = *b++;
transformed_box(&box, crtc);
DBG(("%s: (%d, %d)x(%d, %d) -> (%d, %d), (%d, %d)\n",
__FUNCTION__,
b[-1].x1, b[-1].y1, b[-1].x2-b[-1].x1, b[-1].y2-b[-1].y1,
box.x1, box.y1, box.x2, box.y2));
tmp.box(sna, &tmp, &box);
} while (--n);
tmp.done(sna, &tmp);
free_dst:
FreePicture(dst, None);
free_src:
FreePicture(src, None);
free_pixmap:
screen->DestroyPixmap(pixmap);
}
static void
sna_crtc_redisplay(xf86CrtcPtr crtc, RegionPtr region, struct kgem_bo *bo)
{
int16_t tx, ty, sx, sy;
struct sna *sna = to_sna(crtc->scrn);
DrawablePtr draw = crtc_source(crtc, &sx, &sy);
struct sna_pixmap *priv = sna_pixmap((PixmapPtr)draw);
DBG(("%s: crtc %d [pipe=%d], damage (%d, %d), (%d, %d) x %d\n",
__FUNCTION__, sna_crtc_id(crtc), sna_crtc_pipe(crtc),
region->extents.x1, region->extents.y1,
region->extents.x2, region->extents.y2,
region_num_rects(region)));
assert(!wedged(sna));
if (priv->clear) {
RegionRec whole;
DBG(("%s: clear damage boxes\n", __FUNCTION__));
if (sna_transform_is_integer_translation(&crtc->crtc_to_framebuffer,
&tx, &ty)) {
RegionTranslate(region, -tx, -ty);
} else {
whole.extents = region->extents;
whole.data = NULL;
transformed_box(&whole.extents, crtc);
region = &whole;
}
if (sna_blt_fill_boxes(sna, GXcopy,
bo, draw->bitsPerPixel,
priv->clear_color,
region_rects(region),
region_num_rects(region)))
return;
}
if (crtc->filter == NULL &&
priv->gpu_bo &&
priv->cpu_damage == NULL &&
sna_transform_is_integer_translation(&crtc->crtc_to_framebuffer,
&tx, &ty)) {
DrawableRec tmp;
DBG(("%s: copy damage boxes\n", __FUNCTION__));
tmp.width = crtc->mode.HDisplay;
tmp.height = crtc->mode.VDisplay;
tmp.depth = sna->front->drawable.depth;
tmp.bitsPerPixel = sna->front->drawable.bitsPerPixel;
if (sna->render.copy_boxes(sna, GXcopy,
draw, priv->gpu_bo, sx, sy,
&tmp, bo, -tx, -ty,
region_rects(region), region_num_rects(region), 0))
return;
}
if (can_render(sna))
sna_crtc_redisplay__composite(crtc, region, bo);
else
sna_crtc_redisplay__fallback(crtc, region, bo);
}
static void shadow_flip_handler(struct drm_event_vblank *e,
void *data)
{
sna_mode_redisplay(data);
}
void sna_shadow_set_crtc(struct sna *sna,
xf86CrtcPtr crtc,
struct kgem_bo *bo)
{
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
struct sna_pixmap *priv;
assert(sna_crtc);
DBG(("%s: setting shadow override for CRTC:%d to handle=%d\n",
__FUNCTION__, __sna_crtc_id(sna_crtc), bo->handle));
assert(sna->flags & SNA_TEAR_FREE);
assert(!sna_crtc->transform);
if (sna_crtc->client_bo != bo) {
if (sna_crtc->client_bo) {
assert(sna_crtc->client_bo->refcnt > sna_crtc->client_bo->active_scanout);
kgem_bo_destroy(&sna->kgem, sna_crtc->client_bo);
}
sna_crtc->client_bo = kgem_bo_reference(bo);
assert(sna_crtc->client_bo->refcnt > sna_crtc->client_bo->active_scanout);
sna_crtc_damage(crtc);
}
list_move(&sna_crtc->shadow_link, &sna->mode.shadow_crtc);
sna->mode.shadow_dirty = true;
priv = sna_pixmap(sna->front);
assert(priv->gpu_bo);
priv->move_to_gpu = wait_for_shadow;
priv->move_to_gpu_data = sna;
}
void sna_shadow_steal_crtcs(struct sna *sna, struct list *list)
{
list_init(list);
while (!list_is_empty(&sna->mode.shadow_crtc)) {
RegionRec sub, *damage;
struct sna_crtc *crtc =
list_first_entry(&sna->mode.shadow_crtc,
struct sna_crtc,
shadow_link);
damage = DamageRegion(sna->mode.shadow_damage);
sub.extents = crtc->base->bounds;
sub.data = NULL;
RegionSubtract(damage, damage, &sub);
list_move(&crtc->shadow_link, list);
}
}
void sna_shadow_unsteal_crtcs(struct sna *sna, struct list *list)
{
while (!list_is_empty(list)) {
struct sna_crtc *crtc =
list_first_entry(list,
struct sna_crtc,
shadow_link);
assert(crtc->client_bo);
sna_shadow_set_crtc(sna, crtc->base, crtc->client_bo);
}
}
void sna_shadow_unset_crtc(struct sna *sna,
xf86CrtcPtr crtc)
{
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
DBG(("%s: clearin shadow override for CRTC:%d\n",
__FUNCTION__, __sna_crtc_id(sna_crtc)));
if (sna_crtc->client_bo == NULL)
return;
assert(sna_crtc->client_bo->refcnt > sna_crtc->client_bo->active_scanout);
kgem_bo_destroy(&sna->kgem, sna_crtc->client_bo);
sna_crtc->client_bo = NULL;
list_del(&sna_crtc->shadow_link);
sna->mode.shadow_dirty = true;
sna_crtc_damage(crtc);
}
static bool move_crtc_to_gpu(struct sna *sna)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
int i;
for (i = 0; i < sna->mode.num_real_crtc; i++) {
struct sna_crtc *crtc = to_sna_crtc(config->crtc[i]);
assert(crtc);
if (crtc->bo == NULL)
continue;
if (crtc->slave_pixmap)
continue;
if (crtc->client_bo)
continue;
DBG(("%s: CRTC %d [pipe=%d] requires frontbuffer\n",
__FUNCTION__, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc)));
return sna_pixmap_move_to_gpu(sna->front,
MOVE_READ | MOVE_ASYNC_HINT | __MOVE_SCANOUT);
}
return true;
}
void sna_mode_redisplay(struct sna *sna)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
RegionPtr region;
int i;
if (sna->mode.hidden) {
DBG(("%s: hidden outputs, skipping\n", __FUNCTION__));
return;
}
if (!sna->mode.shadow_enabled)
return;
assert(sna->mode.shadow_damage);
DBG(("%s: posting shadow damage? %d (flips pending? %d, mode reconfiguration pending? %d)\n",
__FUNCTION__,
!RegionNil(DamageRegion(sna->mode.shadow_damage)),
sna->mode.flip_active,
sna->mode.dirty));
assert((sna->flags & SNA_IS_HOSTED) == 0);
assert(sna->mode.shadow_active);
if (sna->mode.dirty)
return;
region = DamageRegion(sna->mode.shadow_damage);
if (RegionNil(region))
return;
DBG(("%s: damage: %dx(%d, %d), (%d, %d)\n",
__FUNCTION__, region_num_rects(region),
region->extents.x1, region->extents.y1,
region->extents.x2, region->extents.y2));
if (sna->mode.flip_active) {
sna->mode.shadow_enabled = false;
while (sna->mode.flip_active && sna_mode_wakeup(sna))
;
sna->mode.shadow_enabled = true;
if (sna->mode.flip_active)
return;
}
if (wedged(sna) || !move_crtc_to_gpu(sna)) {
DBG(("%s: forcing scanout update using the CPU\n", __FUNCTION__));
if (!sna_pixmap_move_to_cpu(sna->front, MOVE_READ))
return;
for (i = 0; i < sna->mode.num_real_crtc; i++) {
xf86CrtcPtr crtc = config->crtc[i];
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
RegionRec damage;
assert(sna_crtc != NULL);
if (!sna_crtc->shadow)
continue;
assert(crtc->enabled);
assert(sna_crtc->transform || sna->flags & SNA_TEAR_FREE);
damage.extents = crtc->bounds;
damage.data = NULL;
RegionIntersect(&damage, &damage, region);
if (!box_empty(&damage.extents)) {
struct kgem_bo *bo = NULL;
DBG(("%s: fallback intersects pipe=%d [(%d, %d), (%d, %d)]\n",
__FUNCTION__, __sna_crtc_pipe(sna_crtc),
damage.extents.x1, damage.extents.y1,
damage.extents.x2, damage.extents.y2));
if (sna->flags & SNA_TEAR_FREE) {
RegionRec new_damage;
RegionNull(&new_damage);
RegionCopy(&new_damage, &damage);
bo = sna_crtc->cache_bo;
if (bo == NULL) {
damage.extents = crtc->bounds;
damage.data = NULL;
bo = kgem_create_2d(&sna->kgem,
crtc->mode.HDisplay,
crtc->mode.VDisplay,
crtc->scrn->bitsPerPixel,
sna_crtc->bo->tiling,
CREATE_SCANOUT);
} else
RegionUnion(&damage, &damage, &sna_crtc->client_damage);
DBG(("%s: TearFree fallback, shadow handle=%d, crtc handle=%d\n", __FUNCTION__, bo->handle, sna_crtc->bo->handle));
sna_crtc->client_damage = new_damage;
}
if (bo == NULL)
bo = sna_crtc->bo;
sna_crtc_redisplay__fallback(crtc, &damage, bo);
if (bo != sna_crtc->bo) {
struct drm_mode_crtc_page_flip arg;
arg.crtc_id = __sna_crtc_id(sna_crtc);
arg.fb_id = get_fb(sna, bo,
crtc->mode.HDisplay,
crtc->mode.VDisplay);
arg.user_data = (uintptr_t)sna_crtc;
arg.flags = DRM_MODE_PAGE_FLIP_EVENT;
arg.reserved = 0;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) {
if (sna_crtc_flip(sna, sna_crtc, bo, 0, 0)) {
DBG(("%s: removing handle=%d [active_scanout=%d] from scanout, installing handle=%d [active_scanout=%d]\n",
__FUNCTION__, sna_crtc->bo->handle, sna_crtc->bo->active_scanout,
bo->handle, bo->active_scanout));
assert(sna_crtc->bo->active_scanout);
assert(sna_crtc->bo->refcnt >= sna_crtc->bo->active_scanout);
sna_crtc->bo->active_scanout--;
kgem_bo_destroy(&sna->kgem, sna_crtc->bo);
sna_crtc->bo = bo;
sna_crtc->bo->active_scanout++;
sna_crtc->cache_bo = NULL;
} else {
DBG(("%s: flip [fb=%d] on crtc %d [%d, pipe=%d] failed - %d\n",
__FUNCTION__, arg.fb_id, i, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), errno));
xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
"Page flipping failed, disabling TearFree\n");
sna->flags &= ~SNA_TEAR_FREE;
damage.extents = crtc->bounds;
damage.data = NULL;
sna_crtc_redisplay__fallback(crtc, &damage, sna_crtc->bo);
kgem_bo_destroy(&sna->kgem, bo);
sna_crtc->cache_bo = NULL;
}
} else {
sna->mode.flip_active++;
assert(sna_crtc->flip_bo == NULL);
sna_crtc->flip_handler = shadow_flip_handler;
sna_crtc->flip_data = sna;
sna_crtc->flip_bo = bo;
sna_crtc->flip_bo->active_scanout++;
sna_crtc->flip_serial = sna_crtc->mode_serial;
sna_crtc->flip_pending = true;
sna_crtc->cache_bo = kgem_bo_reference(sna_crtc->bo);
DBG(("%s: recording flip on CRTC:%d handle=%d, active_scanout=%d, serial=%d\n",
__FUNCTION__, __sna_crtc_id(sna_crtc), sna_crtc->flip_bo->handle, sna_crtc->flip_bo->active_scanout, sna_crtc->flip_serial));
}
}
}
RegionUninit(&damage);
if (sna_crtc->slave_damage)
DamageEmpty(sna_crtc->slave_damage);
}
RegionEmpty(region);
return;
}
{
struct sna_pixmap *priv;
priv = sna_pixmap(sna->front);
assert(priv != NULL);
if (priv->move_to_gpu) {
if (priv->move_to_gpu == wait_for_shadow &&
!sna->mode.shadow_dirty) {
/* No damage written to new scanout
* (backbuffer), ignore redisplay request
* and continue with the current intact
* scanout (frontbuffer).
*/
DBG(("%s: shadow idle, skipping update\n", __FUNCTION__));
RegionEmpty(region);
return;
}
(void)priv->move_to_gpu(sna, priv, 0);
}
assert(priv->move_to_gpu == NULL);
}
for (i = 0; i < sna->mode.num_real_crtc; i++) {
xf86CrtcPtr crtc = config->crtc[i];
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
RegionRec damage;
assert(sna_crtc != NULL);
DBG(("%s: crtc[%d] transformed? %d\n",
__FUNCTION__, i, sna_crtc->transform));
if (!sna_crtc->transform)
continue;
assert(crtc->enabled);
assert(sna_crtc->bo);
damage.extents = crtc->bounds;
damage.data = NULL;
RegionIntersect(&damage, &damage, region);
DBG(("%s: crtc[%d] damage? %d[%d]: %dx[(%d, %d), (%d, %d)]\n",
__FUNCTION__, i,
!box_empty(&damage.extents), RegionNotEmpty(&damage),
region_num_rects(&damage),
damage.extents.x1, damage.extents.y1,
damage.extents.x2, damage.extents.y2));
if (!box_empty(&damage.extents)) {
if (sna->flags & SNA_TEAR_FREE) {
struct drm_mode_crtc_page_flip arg;
struct kgem_bo *bo;
RegionUninit(&damage);
damage.extents = crtc->bounds;
damage.data = NULL;
bo = sna_crtc->cache_bo;
if (bo == NULL)
bo = kgem_create_2d(&sna->kgem,
crtc->mode.HDisplay,
crtc->mode.VDisplay,
crtc->scrn->bitsPerPixel,
sna_crtc->bo->tiling,
CREATE_SCANOUT);
if (bo == NULL)
goto disable1;
sna_crtc_redisplay(crtc, &damage, bo);
kgem_bo_submit(&sna->kgem, bo);
arg.crtc_id = __sna_crtc_id(sna_crtc);
arg.fb_id = get_fb(sna, bo,
crtc->mode.HDisplay,
crtc->mode.VDisplay);
if (arg.fb_id == 0)
goto disable1;
arg.user_data = (uintptr_t)sna_crtc;
arg.flags = DRM_MODE_PAGE_FLIP_EVENT;
arg.reserved = 0;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) {
if (sna_crtc_flip(sna, sna_crtc, bo, 0, 0)) {
DBG(("%s: removing handle=%d [active_scanout=%d] from scanout, installing handle=%d [active_scanout=%d]\n",
__FUNCTION__, sna_crtc->bo->handle, sna_crtc->bo->active_scanout - 1,
bo->handle, bo->active_scanout));
assert(sna_crtc->bo->active_scanout);
assert(sna_crtc->bo->refcnt >= sna_crtc->bo->active_scanout);
sna_crtc->bo->active_scanout--;
kgem_bo_destroy(&sna->kgem, sna_crtc->bo);
sna_crtc->bo = kgem_bo_reference(bo);
sna_crtc->bo->active_scanout++;
sna_crtc->cache_bo = kgem_bo_reference(bo);
} else {
BoxRec box;
DrawableRec tmp;
DBG(("%s: flip [fb=%d] on crtc %d [%d, pipe=%d] failed - %d\n",
__FUNCTION__, arg.fb_id, i, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), errno));
xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
"Page flipping failed, disabling TearFree\n");
sna->flags &= ~SNA_TEAR_FREE;
disable1:
box.x1 = 0;
box.y1 = 0;
tmp.width = box.x2 = crtc->mode.HDisplay;
tmp.height = box.y2 = crtc->mode.VDisplay;
tmp.depth = sna->front->drawable.depth;
tmp.bitsPerPixel = sna->front->drawable.bitsPerPixel;
if (!sna->render.copy_boxes(sna, GXcopy,
&sna->front->drawable, bo, 0, 0,
&tmp, sna_crtc->bo, 0, 0,
&box, 1, COPY_LAST)) {
xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
"%s: page flipping failed, disabling CRTC:%d (pipe=%d)\n",
__FUNCTION__, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc));
sna_crtc_disable(crtc, false);
}
kgem_bo_destroy(&sna->kgem, bo);
sna_crtc->cache_bo = NULL;
}
continue;
}
sna->mode.flip_active++;
assert(sna_crtc->flip_bo == NULL);
sna_crtc->flip_handler = shadow_flip_handler;
sna_crtc->flip_data = sna;
sna_crtc->flip_bo = bo;
sna_crtc->flip_bo->active_scanout++;
sna_crtc->flip_serial = sna_crtc->mode_serial;
sna_crtc->flip_pending = true;
sna_crtc->cache_bo = kgem_bo_reference(sna_crtc->bo);
DBG(("%s: recording flip on CRTC:%d handle=%d, active_scanout=%d, serial=%d\n",
__FUNCTION__, __sna_crtc_id(sna_crtc), sna_crtc->flip_bo->handle, sna_crtc->flip_bo->active_scanout, sna_crtc->flip_serial));
} else {
sna_crtc_redisplay(crtc, &damage, sna_crtc->bo);
kgem_scanout_flush(&sna->kgem, sna_crtc->bo);
}
}
RegionUninit(&damage);
if (sna_crtc->slave_damage)
DamageEmpty(sna_crtc->slave_damage);
}
if (sna->mode.shadow) {
struct kgem_bo *new = __sna_pixmap_get_bo(sna->front);
struct kgem_bo *old = sna->mode.shadow;
struct drm_mode_crtc_page_flip arg;
uint32_t fb = 0;
DBG(("%s: flipping TearFree outputs, current scanout handle=%d [active?=%d], new handle=%d [active=%d]\n",
__FUNCTION__, old->handle, old->active_scanout, new->handle, new->active_scanout));
assert(new != old);
assert(new->refcnt);
arg.flags = DRM_MODE_PAGE_FLIP_EVENT;
arg.reserved = 0;
kgem_bo_submit(&sna->kgem, new);
for (i = 0; i < sna->mode.num_real_crtc; i++) {
struct sna_crtc *crtc = config->crtc[i]->driver_private;
struct kgem_bo *flip_bo;
int x, y;
assert(crtc != NULL);
DBG(("%s: crtc %d [%d, pipe=%d] active? %d, transformed? %d\n",
__FUNCTION__, i, __sna_crtc_id(crtc), crtc->bo ? crtc->bo->handle : 0, crtc->transform));
if (crtc->bo == NULL || crtc->transform)
continue;
assert(config->crtc[i]->enabled);
assert(crtc->flip_bo == NULL);
arg.crtc_id = __sna_crtc_id(crtc);
arg.user_data = (uintptr_t)crtc;
if (crtc->client_bo) {
DBG(("%s: apply shadow override bo for CRTC:%d on pipe=%d, handle=%d\n",
__FUNCTION__, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc), crtc->client_bo->handle));
arg.fb_id = get_fb(sna, crtc->client_bo,
crtc->base->mode.HDisplay,
crtc->base->mode.VDisplay);
assert(arg.fb_id != fb);
flip_bo = crtc->client_bo;
x = y = 0;
} else {
if (fb == 0)
fb = get_fb(sna, new, sna->scrn->virtualX, sna->scrn->virtualY);
if (fb == 0) {
fixup_shadow:
if (sna_pixmap_move_to_gpu(sna->front, MOVE_READ | MOVE_ASYNC_HINT)) {
BoxRec box;
box.x1 = 0;
box.y1 = 0;
box.x2 = sna->scrn->virtualX;
box.y2 = sna->scrn->virtualY;
if (sna->render.copy_boxes(sna, GXcopy,
&sna->front->drawable, __sna_pixmap_get_bo(sna->front), 0, 0,
&sna->front->drawable, old, 0, 0,
&box, 1, COPY_LAST)) {
kgem_submit(&sna->kgem);
RegionEmpty(region);
}
}
return;
}
arg.fb_id = fb;
flip_bo = new;
x = crtc->base->x;
y = crtc->base->y;
}
if (crtc->bo == flip_bo) {
assert(crtc->bo->refcnt >= crtc->bo->active_scanout);
continue;
}
if (flip_bo->pitch != crtc->bo->pitch || (y << 16 | x) != crtc->offset) {
DBG(("%s: changing pitch (new %d =?= old %d) or offset (new %x =?= old %x)\n",
__FUNCTION__,
flip_bo->pitch, crtc->bo->pitch,
y << 16 | x, crtc->offset));
fixup_flip:
if (sna_crtc_flip(sna, crtc, flip_bo, x, y)) {
DBG(("%s: removing handle=%d [active_scanout=%d] from scanout, installing handle=%d [active_scanout=%d]\n",
__FUNCTION__, crtc->bo->handle, crtc->bo->active_scanout-1,
flip_bo->handle, flip_bo->active_scanout));
assert(flip_bo != crtc->bo);
assert(crtc->bo->active_scanout);
assert(crtc->bo->refcnt >= crtc->bo->active_scanout);
crtc->bo->active_scanout--;
kgem_bo_destroy(&sna->kgem, crtc->bo);
if (crtc->shadow_bo) {
kgem_bo_destroy(&sna->kgem, crtc->shadow_bo);
crtc->shadow_bo = NULL;
}
crtc->bo = kgem_bo_reference(flip_bo);
crtc->bo->active_scanout++;
} else {
xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
"Failed to prepare CRTC for page flipping, disabling TearFree\n");
sna->flags &= ~SNA_TEAR_FREE;
if (sna->mode.flip_active == 0) {
DBG(("%s: abandoning flip attempt\n", __FUNCTION__));
goto fixup_shadow;
}
xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
"%s: page flipping failed, disabling CRTC:%d (pipe=%d)\n",
__FUNCTION__, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc));
sna_crtc_disable(crtc->base, false);
}
continue;
}
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) {
ERR(("%s: flip [fb=%d] on crtc %d [%d, pipe=%d] failed - %d\n",
__FUNCTION__, arg.fb_id, i, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc), errno));
goto fixup_flip;
}
sna->mode.flip_active++;
assert(crtc->flip_bo == NULL);
crtc->flip_handler = shadow_flip_handler;
crtc->flip_data = sna;
crtc->flip_bo = kgem_bo_reference(flip_bo);
crtc->flip_bo->active_scanout++;
crtc->flip_serial = crtc->mode_serial;
crtc->flip_pending = true;
DBG(("%s: recording flip on CRTC:%d handle=%d, active_scanout=%d, serial=%d\n",
__FUNCTION__, __sna_crtc_id(crtc), crtc->flip_bo->handle, crtc->flip_bo->active_scanout, crtc->flip_serial));
{
struct drm_i915_gem_busy busy = { flip_bo->handle };
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_BUSY, &busy) == 0) {
if (busy.busy) {
int mode = KGEM_RENDER;
if (busy.busy & (0xfffe << 16))
mode = KGEM_BLT;
DBG(("%s: marking flip bo as busy [%x -> mode=%d]\n", __FUNCTION__, busy.busy, mode));
kgem_bo_mark_busy(&sna->kgem, flip_bo, mode);
} else
__kgem_bo_clear_busy(flip_bo);
}
}
}
DBG(("%s: flipped %d outputs, shadow active? %d\n",
__FUNCTION__,
sna->mode.flip_active,
sna->mode.shadow ? sna->mode.shadow->handle : 0));
if (sna->mode.flip_active) {
assert(old == sna->mode.shadow);
assert(old->refcnt >= 1);
set_shadow(sna, region);
}
} else
kgem_submit(&sna->kgem);
RegionEmpty(region);
}
int sna_mode_wakeup(struct sna *sna)
{
char buffer[1024];
int len, i;
int ret = 0;
again:
/* 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.
*/
if (!event_pending(sna->kgem.fd))
return ret;
/* The DRM read semantics guarantees that we always get only
* complete events.
*/
len = read(sna->kgem.fd, buffer, sizeof (buffer));
if (len < (int)sizeof(struct drm_event))
return ret;
/* Note that we cannot rely on the passed in struct sna matching
* the struct sna used for the vblank event (in case it was submitted
* by a different ZaphodHead). When processing the event, we must
* ensure that we only use the pointer passed along with the event.
*/
DBG(("%s: len=%d\n", __FUNCTION__, len));
i = 0;
while (i < len) {
struct drm_event *e = (struct drm_event *)&buffer[i];
switch (e->type) {
case DRM_EVENT_VBLANK:
if (((uintptr_t)((struct drm_event_vblank *)e)->user_data) & 2)
sna_present_vblank_handler((struct drm_event_vblank *)e);
else
sna_dri2_vblank_handler((struct drm_event_vblank *)e);
break;
case DRM_EVENT_FLIP_COMPLETE:
{
struct drm_event_vblank *vbl = (struct drm_event_vblank *)e;
struct sna_crtc *crtc = (void *)(uintptr_t)vbl->user_data;
uint64_t msc;
/* Beware Zaphod! */
sna = to_sna(crtc->base->scrn);
if (msc64(crtc, vbl->sequence, &msc)) {
DBG(("%s: recording last swap on pipe=%d, frame %d [%08llx], time %d.%06d\n",
__FUNCTION__, __sna_crtc_pipe(crtc), vbl->sequence, (long long)msc, vbl->tv_sec, vbl->tv_usec));
crtc->swap.tv_sec = vbl->tv_sec;
crtc->swap.tv_usec = vbl->tv_usec;
crtc->swap.msc = msc;
}
crtc->flip_pending = false;
assert(crtc->flip_bo);
assert(crtc->flip_bo->active_scanout);
assert(crtc->flip_bo->refcnt >= crtc->flip_bo->active_scanout);
if (crtc->flip_serial == crtc->mode_serial) {
DBG(("%s: removing handle=%d [active_scanout=%d] from scanout, installing handle=%d [active_scanout=%d]\n",
__FUNCTION__, crtc->bo->handle, crtc->bo->active_scanout - 1,
crtc->flip_bo->handle, crtc->flip_bo->active_scanout));
assert(crtc->bo->active_scanout);
assert(crtc->bo->refcnt >= crtc->bo->active_scanout);
crtc->bo->active_scanout--;
kgem_bo_destroy(&sna->kgem, crtc->bo);
if (crtc->shadow_bo) {
kgem_bo_destroy(&sna->kgem, crtc->shadow_bo);
crtc->shadow_bo = NULL;
}
crtc->bo = crtc->flip_bo;
crtc->flip_bo = NULL;
} else {
crtc->flip_bo->active_scanout--;
kgem_bo_destroy(&sna->kgem, crtc->flip_bo);
crtc->flip_bo = NULL;
}
DBG(("%s: flip complete, pending? %d\n", __FUNCTION__, sna->mode.flip_active));
assert(sna->mode.flip_active);
if (--sna->mode.flip_active == 0)
crtc->flip_handler(vbl, crtc->flip_data);
}
break;
default:
break;
}
i += e->length;
ret++;
}
goto again;
}