diff options
Diffstat (limited to 'src/sna/sna_display.c')
-rw-r--r-- | src/sna/sna_display.c | 1656 |
1 files changed, 1656 insertions, 0 deletions
diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c new file mode 100644 index 00000000..d27eafde --- /dev/null +++ b/src/sna/sna_display.c @@ -0,0 +1,1656 @@ +/* + * Copyright © 2007 Red Hat, Inc. + * + * 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 <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <poll.h> + +#include <xorgVersion.h> +#include <X11/Xatom.h> + +#include "sna.h" + +#if DEBUG_DISPLAY +#undef DBG +#define DBG(x) ErrorF x +#endif + +struct sna_crtc { + drmModeModeInfo kmode; + drmModeCrtcPtr mode_crtc; + PixmapPtr shadow; + uint32_t shadow_fb_id; + uint32_t cursor; + xf86CrtcPtr crtc; + int pipe; + int active; + struct list link; +}; + +struct sna_property { + drmModePropertyPtr mode_prop; + uint64_t value; + int num_atoms; /* if range prop, num_atoms == 1; if enum prop, num_atoms == num_enums + 1 */ + Atom *atoms; +}; + +struct sna_output { + struct sna_mode *mode; + int output_id; + drmModeConnectorPtr mode_output; + drmModeEncoderPtr mode_encoder; + int num_props; + struct sna_property *props; + void *private_data; + + Bool has_panel_limits; + int panel_hdisplay; + int panel_vdisplay; + + int dpms_mode; + const char *backlight_iface; + int backlight_active_level; + int backlight_max; + xf86OutputPtr output; + struct list link; +}; + +static void +sna_output_dpms(xf86OutputPtr output, int mode); + +#define BACKLIGHT_CLASS "/sys/class/backlight" + +/* + * List of available kernel interfaces in priority order + */ +static const char *backlight_interfaces[] = { + "sna", /* prefer our own native backlight driver */ + "asus-laptop", + "eeepc", + "thinkpad_screen", + "mbp_backlight", + "fujitsu-laptop", + "sony", + "samsung", + "acpi_video1", /* finally fallback to the generic acpi drivers */ + "acpi_video0", + NULL, +}; +/* + * Must be long enough for BACKLIGHT_CLASS + '/' + longest in above table + + * '/' + "max_backlight" + */ +#define BACKLIGHT_PATH_LEN 80 +/* Enough for 10 digits of backlight + '\n' + '\0' */ +#define BACKLIGHT_VALUE_LEN 12 + +static inline int +crtc_id(struct sna_crtc *crtc) +{ + return crtc->mode_crtc->crtc_id; +} + +int sna_crtc_id(xf86CrtcPtr crtc) +{ + return crtc_id(crtc->driver_private); +} + +int sna_crtc_on(xf86CrtcPtr crtc) +{ + struct sna_crtc *sna_crtc = crtc->driver_private; + return sna_crtc->active; +} + +int sna_crtc_to_pipe(xf86CrtcPtr crtc) +{ + struct sna_crtc *sna_crtc = crtc->driver_private; + return sna_crtc->pipe; +} + +static uint32_t gem_create(int fd, int size) +{ + struct drm_i915_gem_create create; + + create.handle = 0; + create.size = ALIGN(size, 4096); + (void)drmIoctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create); + + return create.handle; +} + +static void gem_close(int fd, uint32_t handle) +{ + struct drm_gem_close close; + + close.handle = handle; + (void)drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close); +} + +static void +sna_output_backlight_set(xf86OutputPtr output, int level) +{ + struct sna_output *sna_output = output->driver_private; + char path[BACKLIGHT_PATH_LEN], val[BACKLIGHT_VALUE_LEN]; + int fd, len, ret; + + if (level > sna_output->backlight_max) + level = sna_output->backlight_max; + if (! sna_output->backlight_iface || level < 0) + return; + + len = snprintf(val, BACKLIGHT_VALUE_LEN, "%d\n", level); + sprintf(path, "%s/%s/brightness", + BACKLIGHT_CLASS, sna_output->backlight_iface); + fd = open(path, O_RDWR); + if (fd == -1) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, "failed to open %s for backlight " + "control: %s\n", path, strerror(errno)); + return; + } + + ret = write(fd, val, len); + if (ret == -1) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, "write to %s for backlight " + "control failed: %s\n", path, strerror(errno)); + } + + close(fd); +} + +static int +sna_output_backlight_get(xf86OutputPtr output) +{ + struct sna_output *sna_output = output->driver_private; + char path[BACKLIGHT_PATH_LEN], val[BACKLIGHT_VALUE_LEN]; + int fd, level; + + sprintf(path, "%s/%s/actual_brightness", + BACKLIGHT_CLASS, sna_output->backlight_iface); + fd = open(path, O_RDONLY); + if (fd == -1) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, "failed to open %s " + "for backlight control: %s\n", path, strerror(errno)); + return -1; + } + + memset(val, 0, sizeof(val)); + if (read(fd, val, BACKLIGHT_VALUE_LEN) == -1) { + close(fd); + return -1; + } + + close(fd); + + level = atoi(val); + if (level > sna_output->backlight_max) + level = sna_output->backlight_max; + if (level < 0) + level = -1; + return level; +} + +static int +sna_output_backlight_get_max(xf86OutputPtr output) +{ + struct sna_output *sna_output = output->driver_private; + char path[BACKLIGHT_PATH_LEN], val[BACKLIGHT_VALUE_LEN]; + int fd, max = 0; + + sprintf(path, "%s/%s/max_brightness", + BACKLIGHT_CLASS, sna_output->backlight_iface); + fd = open(path, O_RDONLY); + if (fd == -1) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, "failed to open %s " + "for backlight control: %s\n", path, strerror(errno)); + return -1; + } + + memset(val, 0, sizeof(val)); + if (read(fd, val, BACKLIGHT_VALUE_LEN) == -1) { + close(fd); + return -1; + } + + close(fd); + + max = atoi(val); + if (max <= 0) + max = -1; + return max; +} + +static void +sna_output_backlight_init(xf86OutputPtr output) +{ + struct sna_output *sna_output = output->driver_private; + int i; + + for (i = 0; backlight_interfaces[i] != NULL; i++) { + char path[BACKLIGHT_PATH_LEN]; + struct stat buf; + + sprintf(path, "%s/%s", BACKLIGHT_CLASS, backlight_interfaces[i]); + if (!stat(path, &buf)) { + sna_output->backlight_iface = backlight_interfaces[i]; + sna_output->backlight_max = sna_output_backlight_get_max(output); + if (sna_output->backlight_max > 0) { + sna_output->backlight_active_level = sna_output_backlight_get(output); + xf86DrvMsg(output->scrn->scrnIndex, X_INFO, + "found backlight control interface %s\n", path); + return; + } + } + } + sna_output->backlight_iface = NULL; +} + + +static void +mode_from_kmode(ScrnInfoPtr scrn, + drmModeModeInfoPtr kmode, + DisplayModePtr mode) +{ + memset(mode, 0, sizeof(DisplayModeRec)); + 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->Flags = kmode->flags; //& FLAG_BITS; + mode->name = strdup(kmode->name); + + if (kmode->type & DRM_MODE_TYPE_DRIVER) + mode->type = M_T_DRIVER; + if (kmode->type & DRM_MODE_TYPE_PREFERRED) + mode->type |= M_T_PREFERRED; + + xf86SetModeCrtc (mode, scrn->adjustFlags); +} + +static void +mode_to_kmode(ScrnInfoPtr scrn, + drmModeModeInfoPtr 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->flags = mode->Flags; //& FLAG_BITS; + if (mode->name) + strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN); + kmode->name[DRM_DISPLAY_MODE_LEN-1] = 0; +} + +static void +sna_crtc_dpms(xf86CrtcPtr crtc, int mode) +{ + struct sna_crtc *sna_crtc = crtc->driver_private; + DBG(("%s(pipe %d, dpms mode -> %d):= active=%d\n", + __FUNCTION__, sna_crtc->pipe, mode, mode == DPMSModeOn)); + sna_crtc->active = mode == DPMSModeOn; +} + +static Bool +sna_crtc_apply(xf86CrtcPtr crtc) +{ + ScrnInfoPtr scrn = crtc->scrn; + struct sna *sna = to_sna(scrn); + struct sna_crtc *sna_crtc = crtc->driver_private; + struct sna_mode *mode = &sna->mode; + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); + uint32_t *output_ids; + int output_count = 0; + int fb_id, x, y; + int i, ret = FALSE; + + output_ids = calloc(sizeof(uint32_t), xf86_config->num_output); + if (!output_ids) + return FALSE; + + for (i = 0; i < xf86_config->num_output; i++) { + xf86OutputPtr output = xf86_config->output[i]; + struct sna_output *sna_output; + + if (output->crtc != crtc) + continue; + + sna_output = output->driver_private; + output_ids[output_count] = + sna_output->mode_output->connector_id; + output_count++; + } + + if (!xf86CrtcRotate(crtc)) + goto done; + + crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green, + crtc->gamma_blue, crtc->gamma_size); + + x = crtc->x; + y = crtc->y; + fb_id = mode->fb_id; + if (sna_crtc->shadow_fb_id) { + fb_id = sna_crtc->shadow_fb_id; + x = 0; + y = 0; + } + ret = drmModeSetCrtc(sna->kgem.fd, crtc_id(sna_crtc), + fb_id, x, y, output_ids, output_count, + &sna_crtc->kmode); + if (ret) { + xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, + "failed to set mode: %s\n", strerror(-ret)); + ret = FALSE; + } else + ret = TRUE; + + if (scrn->pScreen) + xf86_reload_cursors(scrn->pScreen); + +done: + free(output_ids); + return ret; +} + +static Bool +sna_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, + Rotation rotation, int x, int y) +{ + ScrnInfoPtr scrn = crtc->scrn; + struct sna *sna = to_sna(scrn); + struct sna_crtc *sna_crtc = crtc->driver_private; + struct sna_mode *sna_mode = &sna->mode; + int saved_x, saved_y; + Rotation saved_rotation; + DisplayModeRec saved_mode; + int ret = TRUE; + + DBG(("%s(rotation=%d, x=%d, y=%d)\n", + __FUNCTION__, rotation, x, y)); + + if (sna_mode->fb_id == 0) { + struct kgem_bo *bo = sna_pixmap_pin(sna->front); + if (!bo) + return FALSE; + + ret = drmModeAddFB(sna->kgem.fd, + scrn->virtualX, scrn->virtualY, + scrn->depth, scrn->bitsPerPixel, + bo->pitch, bo->handle, + &sna_mode->fb_id); + if (ret < 0) { + ErrorF("%s: failed to add fb: %dx%d depth=%d, bpp=%d, pitch=%d\n", + __FUNCTION__, + scrn->virtualX, scrn->virtualY, + scrn->depth, scrn->bitsPerPixel, bo->pitch); + return FALSE; + } + + DBG(("%s: handle %d attached to fb %d\n", + __FUNCTION__, bo->handle, sna_mode->fb_id)); + } + + saved_mode = crtc->mode; + saved_x = crtc->x; + saved_y = crtc->y; + saved_rotation = crtc->rotation; + + crtc->mode = *mode; + crtc->x = x; + crtc->y = y; + crtc->rotation = rotation; + + kgem_submit(&sna->kgem); + + mode_to_kmode(scrn, &sna_crtc->kmode, mode); + ret = sna_crtc_apply(crtc); + if (!ret) { + crtc->x = saved_x; + crtc->y = saved_y; + crtc->rotation = saved_rotation; + crtc->mode = saved_mode; + } + + return ret; +} + +static void +sna_crtc_set_cursor_colors(xf86CrtcPtr crtc, int bg, int fg) +{ + +} + +static void +sna_crtc_set_cursor_position (xf86CrtcPtr crtc, int x, int y) +{ + struct sna *sna = to_sna(crtc->scrn); + struct sna_crtc *sna_crtc = crtc->driver_private; + + drmModeMoveCursor(sna->kgem.fd, crtc_id(sna_crtc), x, y); +} + +static void +sna_crtc_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *image) +{ + struct sna *sna = to_sna(crtc->scrn); + struct sna_crtc *sna_crtc = crtc->driver_private; + struct drm_i915_gem_pwrite pwrite; + + pwrite.handle = sna_crtc->cursor; + pwrite.offset = 0; + pwrite.size = 64*64*4; + pwrite.data_ptr = (uintptr_t)image; + (void)drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_PWRITE, &pwrite); +} + +static void +sna_crtc_hide_cursor(xf86CrtcPtr crtc) +{ + struct sna *sna = to_sna(crtc->scrn); + struct sna_crtc *sna_crtc = crtc->driver_private; + + drmModeSetCursor(sna->kgem.fd, crtc_id(sna_crtc), 0, 64, 64); +} + +static void +sna_crtc_show_cursor(xf86CrtcPtr crtc) +{ + struct sna *sna = to_sna(crtc->scrn); + struct sna_crtc *sna_crtc = crtc->driver_private; + + drmModeSetCursor(sna->kgem.fd, crtc_id(sna_crtc), + sna_crtc->cursor, 64, 64); +} + +static void * +sna_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height) +{ + ScrnInfoPtr scrn = crtc->scrn; + struct sna *sna = to_sna(scrn); + struct sna_crtc *sna_crtc = crtc->driver_private; + PixmapPtr shadow; + struct kgem_bo *bo; + + DBG(("%s(%d, %d)\n", __FUNCTION__, width, height)); + + shadow = scrn->pScreen->CreatePixmap(scrn->pScreen, width, height, scrn->depth, 0); + if (!shadow) + return NULL; + + bo = sna_pixmap_pin(shadow); + if (!bo) { + scrn->pScreen->DestroyPixmap(shadow); + return NULL; + } + + if (drmModeAddFB(sna->kgem.fd, + width, height, scrn->depth, scrn->bitsPerPixel, + bo->pitch, bo->handle, + &sna_crtc->shadow_fb_id)) { + ErrorF("%s: failed to add rotate fb: %dx%d depth=%d, bpp=%d, pitch=%d\n", + __FUNCTION__, + width, height, + scrn->depth, scrn->bitsPerPixel, bo->pitch); + scrn->pScreen->DestroyPixmap(shadow); + return NULL; + } + + DBG(("%s: attached handle %d to fb %d\n", + __FUNCTION__, bo->handle, sna_crtc->shadow_fb_id)); + return sna_crtc->shadow = shadow; +} + +static PixmapPtr +sna_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height) +{ + return data; +} + +static void +sna_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr pixmap, void *data) +{ + struct sna *sna = to_sna(crtc->scrn); + struct sna_crtc *sna_crtc = crtc->driver_private; + + DBG(("%s(fb=%d, handle=%d)\n", __FUNCTION__, + sna_crtc->shadow_fb_id, sna_pixmap_get_bo(pixmap)->handle)); + + drmModeRmFB(sna->kgem.fd, sna_crtc->shadow_fb_id); + sna_crtc->shadow_fb_id = 0; + + pixmap->drawable.pScreen->DestroyPixmap(pixmap); + sna_crtc->shadow = NULL; +} + +static void +sna_crtc_gamma_set(xf86CrtcPtr crtc, + CARD16 *red, CARD16 *green, CARD16 *blue, int size) +{ + struct sna *sna = to_sna(crtc->scrn); + struct sna_crtc *sna_crtc = crtc->driver_private; + + drmModeCrtcSetGamma(sna->kgem.fd, crtc_id(sna_crtc), + size, red, green, blue); +} + +static void +sna_crtc_destroy(xf86CrtcPtr crtc) +{ + struct sna *sna = to_sna(crtc->scrn); + struct sna_crtc *sna_crtc = crtc->driver_private; + + drmModeSetCursor(sna->kgem.fd, crtc_id(sna_crtc), 0, 64, 64); + gem_close(sna->kgem.fd, sna_crtc->cursor); + + list_del(&sna_crtc->link); + free(sna_crtc); + + crtc->driver_private = NULL; +} + +static const xf86CrtcFuncsRec sna_crtc_funcs = { + .dpms = sna_crtc_dpms, + .set_mode_major = sna_crtc_set_mode_major, + .set_cursor_colors = sna_crtc_set_cursor_colors, + .set_cursor_position = sna_crtc_set_cursor_position, + .show_cursor = sna_crtc_show_cursor, + .hide_cursor = sna_crtc_hide_cursor, + .load_cursor_argb = sna_crtc_load_cursor_argb, + .shadow_create = sna_crtc_shadow_create, + .shadow_allocate = sna_crtc_shadow_allocate, + .shadow_destroy = sna_crtc_shadow_destroy, + .gamma_set = sna_crtc_gamma_set, + .destroy = sna_crtc_destroy, +}; + +static void +sna_crtc_init(ScrnInfoPtr scrn, struct sna_mode *mode, int num) +{ + struct sna *sna = to_sna(scrn); + xf86CrtcPtr crtc; + struct sna_crtc *sna_crtc; + struct drm_i915_get_pipe_from_crtc_id get_pipe; + + sna_crtc = calloc(sizeof(struct sna_crtc), 1); + if (sna_crtc == NULL) + return; + + crtc = xf86CrtcCreate(scrn, &sna_crtc_funcs); + if (crtc == NULL) { + free(sna_crtc); + return; + } + + sna_crtc->mode_crtc = drmModeGetCrtc(sna->kgem.fd, + mode->mode_res->crtcs[num]); + get_pipe.pipe = 0; + get_pipe.crtc_id = sna_crtc->mode_crtc->crtc_id; + drmIoctl(sna->kgem.fd, + DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID, + &get_pipe); + sna_crtc->pipe = get_pipe.pipe; + + crtc->driver_private = sna_crtc; + + sna_crtc->cursor = gem_create(sna->kgem.fd, 64*64*4); + + sna_crtc->crtc = crtc; + list_add(&sna_crtc->link, &mode->crtcs); +} + +static Bool +is_panel(int type) +{ + return (type == DRM_MODE_CONNECTOR_LVDS || + type == DRM_MODE_CONNECTOR_eDP); +} + +static xf86OutputStatus +sna_output_detect(xf86OutputPtr output) +{ + /* go to the hw and retrieve a new output struct */ + struct sna *sna = to_sna(output->scrn); + struct sna_output *sna_output = output->driver_private; + xf86OutputStatus status; + + drmModeFreeConnector(sna_output->mode_output); + sna_output->mode_output = + drmModeGetConnector(sna->kgem.fd, sna_output->output_id); + + switch (sna_output->mode_output->connection) { + case DRM_MODE_CONNECTED: + status = XF86OutputStatusConnected; + break; + case DRM_MODE_DISCONNECTED: + status = XF86OutputStatusDisconnected; + break; + default: + case DRM_MODE_UNKNOWNCONNECTION: + status = XF86OutputStatusUnknown; + break; + } + return status; +} + +static Bool +sna_output_mode_valid(xf86OutputPtr output, DisplayModePtr pModes) +{ + struct sna_output *sna_output = output->driver_private; + + /* + * 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 (pModes->HDisplay > sna_output->panel_hdisplay || + pModes->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; + drmModeConnectorPtr koutput = sna_output->mode_output; + drmModePropertyBlobPtr edid_blob = NULL; + xf86MonPtr mon = NULL; + int i; + + /* look for an EDID property */ + for (i = 0; i < koutput->count_props; i++) { + drmModePropertyPtr props; + + props = drmModeGetProperty(sna->kgem.fd, koutput->props[i]); + if (!props) + continue; + + if (!(props->flags & DRM_MODE_PROP_BLOB)) { + drmModeFreeProperty(props); + continue; + } + + if (!strcmp(props->name, "EDID")) { + drmModeFreePropertyBlob(edid_blob); + edid_blob = + drmModeGetPropertyBlob(sna->kgem.fd, + koutput->prop_values[i]); + } + drmModeFreeProperty(props); + } + + if (edid_blob) { + mon = xf86InterpretEDID(output->scrn->scrnIndex, + edid_blob->data); + + if (mon && edid_blob->length > 128) + mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA; + } + + xf86OutputSetEDID(output, mon); + + if (edid_blob) + drmModeFreePropertyBlob(edid_blob); +} + +static DisplayModePtr +sna_output_panel_edid(xf86OutputPtr output, DisplayModePtr modes) +{ + xf86MonPtr mon = output->MonInfo; + + if (!mon || !GTF_SUPPORTED(mon->features.msc)) { + DisplayModePtr i, m, p = NULL; + int max_x = 0, max_y = 0; + float max_vrefresh = 0.0; + + for (m = modes; m; m = m->next) { + if (m->type & M_T_PREFERRED) + p = 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 = xf86GetDefaultModes(); + 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 (p && i->HDisplay >= p->HDisplay && + i->VDisplay >= p->VDisplay && + xf86ModeVRefresh(i) >= xf86ModeVRefresh(p)) + i->status = MODE_VSYNC; + } + + xf86PruneInvalidModes(output->scrn, &m, FALSE); + + modes = xf86ModesAdd(modes, m); + } + + return modes; +} + +static DisplayModePtr +sna_output_get_modes(xf86OutputPtr output) +{ + struct sna_output *sna_output = output->driver_private; + drmModeConnectorPtr koutput = sna_output->mode_output; + DisplayModePtr Modes = NULL; + int i; + + sna_output_attach_edid(output); + + /* modes should already be available */ + for (i = 0; i < koutput->count_modes; i++) { + DisplayModePtr Mode; + + Mode = calloc(1, sizeof(DisplayModeRec)); + if (Mode) { + mode_from_kmode(output->scrn, &koutput->modes[i], Mode); + Modes = xf86ModesAdd(Modes, Mode); + } + } + + /* + * 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 (is_panel(koutput->connector_type)) { + for (i = 0; i < koutput->count_modes; i++) { + drmModeModeInfo *mode_ptr; + + mode_ptr = &koutput->modes[i]; + if (mode_ptr->hdisplay > sna_output->panel_hdisplay) + sna_output->panel_hdisplay = mode_ptr->hdisplay; + if (mode_ptr->vdisplay > sna_output->panel_vdisplay) + sna_output->panel_vdisplay = mode_ptr->vdisplay; + } + + sna_output->has_panel_limits = + sna_output->panel_hdisplay && + sna_output->panel_vdisplay; + + Modes = sna_output_panel_edid(output, Modes); + } + + return Modes; +} + +static void +sna_output_destroy(xf86OutputPtr output) +{ + struct sna_output *sna_output = output->driver_private; + int i; + + for (i = 0; i < sna_output->num_props; i++) { + drmModeFreeProperty(sna_output->props[i].mode_prop); + free(sna_output->props[i].atoms); + } + free(sna_output->props); + + drmModeFreeConnector(sna_output->mode_output); + sna_output->mode_output = NULL; + + list_del(&sna_output->link); + free(sna_output); + + output->driver_private = NULL; +} + +static void +sna_output_dpms_backlight(xf86OutputPtr output, int oldmode, int mode) +{ + struct sna_output *sna_output = output->driver_private; + + if (!sna_output->backlight_iface) + return; + + if (mode == DPMSModeOn) { + /* If we're going from off->on we may need to turn on the backlight. */ + if (oldmode != DPMSModeOn) + sna_output_backlight_set(output, + sna_output->backlight_active_level); + } else { + /* Only save the current backlight value if we're going from on to off. */ + if (oldmode == DPMSModeOn) + sna_output->backlight_active_level = sna_output_backlight_get(output); + sna_output_backlight_set(output, 0); + } +} + +static void +sna_output_dpms(xf86OutputPtr output, int dpms) +{ + struct sna *sna = to_sna(output->scrn); + struct sna_output *sna_output = output->driver_private; + drmModeConnectorPtr koutput = sna_output->mode_output; + int i; + + for (i = 0; i < koutput->count_props; i++) { + drmModePropertyPtr props; + + props = drmModeGetProperty(sna->kgem.fd, koutput->props[i]); + if (!props) + continue; + + if (!strcmp(props->name, "DPMS")) { + drmModeConnectorSetProperty(sna->kgem.fd, + sna_output->output_id, + props->prop_id, + dpms); + sna_output_dpms_backlight(output, + sna_output->dpms_mode, + dpms); + sna_output->dpms_mode = dpms; + drmModeFreeProperty(props); + return; + } + + drmModeFreeProperty(props); + } +} + +int +sna_output_dpms_status(xf86OutputPtr output) +{ + struct sna_output *sna_output = output->driver_private; + return sna_output->dpms_mode; +} + +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; +} + +#define BACKLIGHT_NAME "Backlight" +#define BACKLIGHT_DEPRECATED_NAME "BACKLIGHT" +static Atom backlight_atom, backlight_deprecated_atom; + +static void +sna_output_create_resources(xf86OutputPtr output) +{ + struct sna *sna = to_sna(output->scrn); + struct sna_output *sna_output = output->driver_private; + drmModeConnectorPtr mode_output = sna_output->mode_output; + int i, j, err; + + sna_output->props = calloc(mode_output->count_props, + sizeof(struct sna_property)); + if (!sna_output->props) + return; + + sna_output->num_props = 0; + for (i = j = 0; i < mode_output->count_props; i++) { + drmModePropertyPtr drmmode_prop; + + drmmode_prop = drmModeGetProperty(sna->kgem.fd, + mode_output->props[i]); + if (sna_property_ignore(drmmode_prop)) { + drmModeFreeProperty(drmmode_prop); + continue; + } + + sna_output->props[j].mode_prop = drmmode_prop; + sna_output->props[j].value = mode_output->prop_values[i]; + j++; + } + sna_output->num_props = j; + + for (i = 0; i < sna_output->num_props; i++) { + struct sna_property *p = &sna_output->props[i]; + drmModePropertyPtr drmmode_prop = p->mode_prop; + + if (drmmode_prop->flags & DRM_MODE_PROP_RANGE) { + INT32 range[2]; + + p->num_atoms = 1; + p->atoms = calloc(p->num_atoms, sizeof(Atom)); + if (!p->atoms) + continue; + + p->atoms[0] = MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE); + range[0] = drmmode_prop->values[0]; + range[1] = drmmode_prop->values[1]; + err = RRConfigureOutputProperty(output->randr_output, p->atoms[0], + FALSE, TRUE, + drmmode_prop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE, + 2, range); + if (err != 0) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "RRConfigureOutputProperty error, %d\n", err); + } + err = RRChangeOutputProperty(output->randr_output, p->atoms[0], + XA_INTEGER, 32, PropModeReplace, 1, &p->value, FALSE, TRUE); + if (err != 0) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "RRChangeOutputProperty error, %d\n", err); + } + } else if (drmmode_prop->flags & DRM_MODE_PROP_ENUM) { + p->num_atoms = drmmode_prop->count_enums + 1; + p->atoms = calloc(p->num_atoms, sizeof(Atom)); + if (!p->atoms) + continue; + + p->atoms[0] = MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE); + for (j = 1; j <= drmmode_prop->count_enums; j++) { + struct drm_mode_property_enum *e = &drmmode_prop->enums[j-1]; + p->atoms[j] = MakeAtom(e->name, strlen(e->name), TRUE); + } + + err = RRConfigureOutputProperty(output->randr_output, p->atoms[0], + FALSE, FALSE, + drmmode_prop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE, + p->num_atoms - 1, (INT32 *)&p->atoms[1]); + if (err != 0) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "RRConfigureOutputProperty error, %d\n", err); + } + + for (j = 0; j < drmmode_prop->count_enums; j++) + if (drmmode_prop->enums[j].value == p->value) + 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, TRUE); + if (err != 0) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "RRChangeOutputProperty error, %d\n", err); + } + } + } + + if (sna_output->backlight_iface) { + INT32 data, backlight_range[2]; + + /* Set up the backlight property, which takes effect + * immediately and accepts values only within the + * backlight_range. + */ + backlight_atom = MakeAtom(BACKLIGHT_NAME, sizeof(BACKLIGHT_NAME) - 1, TRUE); + backlight_deprecated_atom = MakeAtom(BACKLIGHT_DEPRECATED_NAME, + sizeof(BACKLIGHT_DEPRECATED_NAME) - 1, TRUE); + + backlight_range[0] = 0; + backlight_range[1] = sna_output->backlight_max; + err = RRConfigureOutputProperty(output->randr_output, + backlight_atom, + FALSE, TRUE, FALSE, + 2, backlight_range); + if (err != 0) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "RRConfigureOutputProperty error, %d\n", err); + } + err = RRConfigureOutputProperty(output->randr_output, + backlight_deprecated_atom, + FALSE, TRUE, FALSE, + 2, backlight_range); + if (err != 0) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "RRConfigureOutputProperty error, %d\n", err); + } + /* Set the current value of the backlight property */ + data = sna_output->backlight_active_level; + err = RRChangeOutputProperty(output->randr_output, backlight_atom, + XA_INTEGER, 32, PropModeReplace, 1, &data, + FALSE, TRUE); + if (err != 0) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "RRChangeOutputProperty error, %d\n", err); + } + err = RRChangeOutputProperty(output->randr_output, backlight_deprecated_atom, + XA_INTEGER, 32, PropModeReplace, 1, &data, + FALSE, TRUE); + if (err != 0) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "RRChangeOutputProperty error, %d\n", err); + } + } +} + +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; + + if (value->type != XA_INTEGER || value->format != 32 || + value->size != 1) + { + return FALSE; + } + + val = *(INT32 *)value->data; + if (val < 0 || val > sna_output->backlight_max) + return FALSE; + + if (sna_output->dpms_mode == DPMSModeOn) + sna_output_backlight_set(output, val); + sna_output->backlight_active_level = val; + return TRUE; + } + + for (i = 0; i < sna_output->num_props; i++) { + struct sna_property *p = &sna_output->props[i]; + + if (p->atoms[0] != property) + continue; + + if (p->mode_prop->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->output_id, + p->mode_prop->prop_id, (uint64_t)val); + return TRUE; + } else if (p->mode_prop->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); + + /* search for matching name string, then set its value down */ + for (j = 0; j < p->mode_prop->count_enums; j++) { + if (!strcmp(p->mode_prop->enums[j].name, name)) { + drmModeConnectorSetProperty(sna->kgem.fd, sna_output->output_id, + p->mode_prop->prop_id, p->mode_prop->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 Bool +sna_output_get_property(xf86OutputPtr output, Atom property) +{ + struct sna_output *sna_output = output->driver_private; + int err; + + if (property == backlight_atom || property == backlight_deprecated_atom) { + INT32 val; + + if (! sna_output->backlight_iface) + return FALSE; + + val = sna_output_backlight_get(output); + if (val < 0) + return FALSE; + + err = RRChangeOutputProperty(output->randr_output, property, + XA_INTEGER, 32, PropModeReplace, 1, &val, + FALSE, TRUE); + if (err != 0) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "RRChangeOutputProperty error, %d\n", err); + return FALSE; + } + + 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[7] = { + 0, + SubPixelUnknown, + SubPixelHorizontalRGB, + SubPixelHorizontalBGR, + SubPixelVerticalRGB, + SubPixelVerticalBGR, + SubPixelNone +}; + +static const char *output_names[] = { + "None", + "VGA", + "DVI", + "DVI", + "DVI", + "Composite", + "TV", + "LVDS", + "CTV", + "DIN", + "DP", + "HDMI", + "HDMI", + "TV", + "eDP", +}; + +static void +sna_output_init(ScrnInfoPtr scrn, struct sna_mode *mode, int num) +{ + struct sna *sna = to_sna(scrn); + xf86OutputPtr output; + drmModeConnectorPtr koutput; + drmModeEncoderPtr kencoder; + struct sna_output *sna_output; + const char *output_name; + char name[32]; + + koutput = drmModeGetConnector(sna->kgem.fd, + mode->mode_res->connectors[num]); + if (!koutput) + return; + + kencoder = drmModeGetEncoder(sna->kgem.fd, koutput->encoders[0]); + if (!kencoder) { + drmModeFreeConnector(koutput); + return; + } + + if (koutput->connector_type < ARRAY_SIZE(output_names)) + output_name = output_names[koutput->connector_type]; + else + output_name = "UNKNOWN"; + snprintf(name, 32, "%s%d", output_name, koutput->connector_type_id); + + output = xf86OutputCreate (scrn, &sna_output_funcs, name); + if (!output) { + drmModeFreeEncoder(kencoder); + drmModeFreeConnector(koutput); + return; + } + + sna_output = calloc(sizeof(struct sna_output), 1); + if (!sna_output) { + xf86OutputDestroy(output); + drmModeFreeConnector(koutput); + drmModeFreeEncoder(kencoder); + return; + } + + sna_output->output_id = mode->mode_res->connectors[num]; + sna_output->mode_output = koutput; + sna_output->mode_encoder = kencoder; + sna_output->mode = mode; + + output->mm_width = koutput->mmWidth; + output->mm_height = koutput->mmHeight; + + output->subpixel_order = subpixel_conv_table[koutput->subpixel]; + output->driver_private = sna_output; + + if (is_panel(koutput->connector_type)) + sna_output_backlight_init(output); + + output->possible_crtcs = kencoder->possible_crtcs; + output->possible_clones = kencoder->possible_clones; + output->interlaceAllowed = TRUE; + + sna_output->output = output; + list_add(&sna_output->link, &mode->outputs); +} + +struct sna_visit_set_pixmap_window { + PixmapPtr old, new; +}; + +static int +sna_visit_set_window_pixmap(WindowPtr window, pointer data) +{ + struct sna_visit_set_pixmap_window *visit = data; + ScreenPtr screen = window->drawable.pScreen; + + if (screen->GetWindowPixmap(window) == visit->old) { + screen->SetWindowPixmap(window, visit->new); + return WT_WALKCHILDREN; + } + + return WT_DONTWALKCHILDREN; +} + +static void +sn_redirect_screen_pixmap(ScrnInfoPtr scrn, PixmapPtr old, PixmapPtr new) +{ + ScreenPtr screen = scrn->pScreen; + struct sna_visit_set_pixmap_window visit; + + visit.old = old; + visit.new = new; + TraverseTree(screen->root, sna_visit_set_window_pixmap, &visit); + + screen->SetScreenPixmap(new); +} + +static Bool +sna_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height) +{ + struct sna *sna = to_sna(scrn); + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); + struct sna_mode *mode = &sna->mode; + PixmapPtr old_front; + uint32_t old_fb_id; + struct kgem_bo *bo; + int i; + + DBG(("%s (%d, %d) -> (%d, %d)\n", + __FUNCTION__, + scrn->virtualX, scrn->virtualY, + width, height)); + + if (scrn->virtualX == width && scrn->virtualY == height) + return TRUE; + + kgem_submit(&sna->kgem); + + old_fb_id = mode->fb_id; + old_front = sna->front; + + sna->front = scrn->pScreen->CreatePixmap(scrn->pScreen, + width, height, + scrn->depth, + SNA_CREATE_FB); + if (!sna->front) + goto fail; + + bo = sna_pixmap_pin(sna->front); + if (!bo) + goto fail; + + if (drmModeAddFB(sna->kgem.fd, width, height, + scrn->depth, scrn->bitsPerPixel, + bo->pitch, bo->handle, + &mode->fb_id)) { + ErrorF("%s: failed to add fb: %dx%d depth=%d, bpp=%d, pitch=%d\n", + __FUNCTION__, + width, height, + scrn->depth, scrn->bitsPerPixel, bo->pitch); + goto fail; + } + + for (i = 0; i < xf86_config->num_crtc; i++) { + xf86CrtcPtr crtc = xf86_config->crtc[i]; + + if (!crtc->enabled) + continue; + + if (!sna_crtc_apply(crtc)) + goto fail; + } + + scrn->virtualX = width; + scrn->virtualY = height; + scrn->displayWidth = bo->pitch / sna->mode.cpp; + + sn_redirect_screen_pixmap(scrn, old_front, sna->front); + + if (old_fb_id) + drmModeRmFB(sna->kgem.fd, old_fb_id); + scrn->pScreen->DestroyPixmap(old_front); + + return TRUE; + +fail: + if (old_fb_id != mode->fb_id) + drmModeRmFB(sna->kgem.fd, mode->fb_id); + mode->fb_id = old_fb_id; + + if (sna->front) + scrn->pScreen->DestroyPixmap(sna->front); + sna->front = old_front; + return FALSE; +} + +static Bool do_page_flip(struct sna *sna, + int ref_crtc_hw_id) +{ + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); + int i; + + /* + * Queue flips on all enabled CRTCs + * Note that if/when we get per-CRTC buffers, we'll have to update this. + * Right now it assumes a single shared fb across all CRTCs, with the + * kernel fixing up the offset of each CRTC as necessary. + * + * Also, flips queued on disabled or incorrectly configured displays + * may never complete; this is a configuration error. + */ + for (i = 0; i < config->num_crtc; i++) { + struct sna_crtc *crtc = config->crtc[i]->driver_private; + uintptr_t data; + + if (!config->crtc[i]->enabled) + continue; + + /* Only the reference crtc will finally deliver its page flip + * completion event. All other crtc's events will be discarded. + */ + + data = (uintptr_t)sna; + data |= sna_crtc_to_pipe(crtc->crtc) == ref_crtc_hw_id; + + if (drmModePageFlip(sna->kgem.fd, + crtc_id(crtc), + sna->mode.fb_id, + DRM_MODE_PAGE_FLIP_EVENT, + (void*)data)) { + xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING, + "flip queue failed: %s\n", strerror(errno)); + return FALSE; + } + } + + return TRUE; +} + +Bool +sna_do_pageflip(struct sna *sna, + PixmapPtr pixmap, + DRI2FrameEventPtr flip_info, int ref_crtc_hw_id) +{ + ScrnInfoPtr scrn = sna->scrn; + struct sna_mode *mode = &sna->mode; + struct kgem_bo *bo = sna_pixmap_pin(pixmap); + int old_fb_id; + + if (!bo) + return FALSE; + + /* + * Create a new handle for the back buffer + */ + old_fb_id = mode->fb_id; + if (drmModeAddFB(sna->kgem.fd, scrn->virtualX, scrn->virtualY, + scrn->depth, scrn->bitsPerPixel, + bo->pitch, bo->handle, + &mode->fb_id)) { + ErrorF("%s: failed to add fb: %dx%d depth=%d, bpp=%d, pitch=%d\n", + __FUNCTION__, + scrn->virtualX, scrn->virtualY, + scrn->depth, scrn->bitsPerPixel, bo->pitch); + return FALSE; + } + + kgem_submit(&sna->kgem); + + /* + * Queue flips on all enabled CRTCs + * Note that if/when we get per-CRTC buffers, we'll have to update this. + * Right now it assumes a single shared fb across all CRTCs, with the + * kernel fixing up the offset of each CRTC as necessary. + * + * Also, flips queued on disabled or incorrectly configured displays + * may never complete; this is a configuration error. + */ + mode->fe_frame = 0; + mode->fe_tv_sec = 0; + mode->fe_tv_usec = 0; + + mode->flip_info = flip_info; + mode->flip_count++; + + if (do_page_flip(sna, ref_crtc_hw_id)) { + PixmapPtr old_front = sna->front; + + sna->front = pixmap; + pixmap->refcnt++; + sn_redirect_screen_pixmap(scrn, old_front, sna->front); + scrn->displayWidth = bo->pitch / sna->mode.cpp; + + drmModeRmFB(sna->kgem.fd, old_fb_id); + scrn->pScreen->DestroyPixmap(old_front); + return TRUE; + } else { + drmModeRmFB(sna->kgem.fd, mode->fb_id); + mode->fb_id = old_fb_id; + return FALSE; + } +} + +static const xf86CrtcConfigFuncsRec sna_xf86crtc_config_funcs = { + sna_xf86crtc_resize +}; + +static void +sna_vblank_handler(int fd, unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + sna_dri2_frame_event(frame, tv_sec, tv_usec, event_data); +} + +static void +sna_page_flip_handler(int fd, unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + struct sna *sna = (struct sna *)((uintptr_t)event_data & ~1); + struct sna_mode *mode = &sna->mode; + + /* Is this the event whose info shall be delivered to higher level? */ + if ((uintptr_t)event_data & 1) { + /* Yes: Cache msc, ust for later delivery. */ + mode->fe_frame = frame; + mode->fe_tv_sec = tv_sec; + mode->fe_tv_usec = tv_usec; + } + + /* Last crtc completed flip? */ + if (--mode->flip_count > 0) + return; + + if (mode->flip_info == NULL) + return; + + /* Deliver cached msc, ust from reference crtc to flip event handler */ + sna_dri2_flip_event(mode->fe_frame, mode->fe_tv_sec, + mode->fe_tv_usec, mode->flip_info); +} + +static void +drm_wakeup_handler(pointer data, int err, pointer p) +{ + struct sna *sna; + fd_set *read_mask; + + if (data == NULL || err < 0) + return; + + sna = data; + read_mask = p; + if (FD_ISSET(sna->kgem.fd, read_mask)) + drmHandleEvent(sna->kgem.fd, &sna->mode.event_context); +} + +Bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna) +{ + struct sna_mode *mode = &sna->mode; + unsigned int i; + + list_init(&mode->crtcs); + list_init(&mode->outputs); + + xf86CrtcConfigInit(scrn, &sna_xf86crtc_config_funcs); + + mode->mode_res = drmModeGetResources(sna->kgem.fd); + if (!mode->mode_res) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "failed to get resources: %s\n", strerror(errno)); + return FALSE; + } + + xf86CrtcSetSizeRange(scrn, + 320, 200, + mode->mode_res->max_width, + mode->mode_res->max_height); + for (i = 0; i < mode->mode_res->count_crtcs; i++) + sna_crtc_init(scrn, mode, i); + + for (i = 0; i < mode->mode_res->count_connectors; i++) + sna_output_init(scrn, mode, i); + + xf86InitialConfiguration(scrn, TRUE); + + mode->event_context.version = DRM_EVENT_CONTEXT_VERSION; + mode->event_context.vblank_handler = sna_vblank_handler; + mode->event_context.page_flip_handler = sna_page_flip_handler; + + return TRUE; +} + +void +sna_mode_init(struct sna *sna) +{ + struct sna_mode *mode = &sna->mode; + + /* We need to re-register the mode->fd for the synchronisation + * feedback on every server generation, so perform the + * registration within ScreenInit and not PreInit. + */ + mode->flip_count = 0; + AddGeneralSocket(sna->kgem.fd); + RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA, + drm_wakeup_handler, sna); +} + +void +sna_mode_remove_fb(struct sna *sna) +{ + struct sna_mode *mode = &sna->mode; + + if (mode->fb_id) { + drmModeRmFB(sna->kgem.fd, mode->fb_id); + mode->fb_id = 0; + } +} + +void +sna_mode_fini(struct sna *sna) +{ + struct sna_mode *mode = &sna->mode; + +#if 0 + while (!list_is_empty(&mode->crtcs)) { + xf86CrtcDestroy(list_first_entry(&mode->crtcs, + struct sna_crtc, + link)->crtc); + } + + while (!list_is_empty(&mode->outputs)) { + xf86OutputDestroy(list_first_entry(&mode->outputs, + struct sna_output, + link)->output); + } +#endif + + if (mode->fb_id) { + drmModeRmFB(sna->kgem.fd, mode->fb_id); + mode->fb_id = 0; + } + + /* mode->shadow_fb_id should have been destroyed already */ +} |