diff options
Diffstat (limited to 'src/uxa/intel_display.c')
-rw-r--r-- | src/uxa/intel_display.c | 2124 |
1 files changed, 2124 insertions, 0 deletions
diff --git a/src/uxa/intel_display.c b/src/uxa/intel_display.c new file mode 100644 index 00000000..0acb86d4 --- /dev/null +++ b/src/uxa/intel_display.c @@ -0,0 +1,2124 @@ +/* + * 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 <sys/types.h> +#include <sys/stat.h> +#include <sys/poll.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <poll.h> +#include <sys/ioctl.h> + +#include "xorgVersion.h" + +#include "intel.h" +#include "intel_bufmgr.h" +#include "intel_options.h" +#include "xf86drm.h" +#include "xf86drmMode.h" +#include "X11/Xatom.h" +#include "X11/extensions/dpmsconst.h" +#include "xf86DDC.h" +#include "fb.h" +#include "uxa.h" + +#include "intel_glamor.h" + +#define KNOWN_MODE_FLAGS ((1<<14)-1) + +struct intel_mode { + int fd; + uint32_t fb_id; + drmModeResPtr mode_res; + int cpp; + + drmEventContext event_context; + DRI2FrameEventPtr flip_info; + int old_fb_id; + int flip_count; + unsigned int fe_frame; + unsigned int fe_tv_sec; + unsigned int fe_tv_usec; + + struct list outputs; + struct list crtcs; +}; + +struct intel_pageflip { + struct intel_mode *mode; + Bool dispatch_me; +}; + +struct intel_crtc { + struct intel_mode *mode; + drmModeModeInfo kmode; + drmModeCrtcPtr mode_crtc; + int pipe; + dri_bo *cursor; + dri_bo *rotate_bo; + uint32_t rotate_pitch; + uint32_t rotate_fb_id; + xf86CrtcPtr crtc; + struct list link; + PixmapPtr scanout_pixmap; + uint32_t scanout_fb_id; +}; + +struct intel_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 intel_output { + struct intel_mode *mode; + int output_id; + drmModeConnectorPtr mode_output; + drmModeEncoderPtr mode_encoder; + int num_props; + struct intel_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 +intel_output_dpms(xf86OutputPtr output, int mode); + +static void +intel_output_dpms_backlight(xf86OutputPtr output, int oldmode, int mode); + +static inline int +crtc_id(struct intel_crtc *crtc) +{ + return crtc->mode_crtc->crtc_id; +} + +#ifdef __OpenBSD__ + +#include <dev/wscons/wsconsio.h> +#include "xf86Priv.h" + +static void +intel_output_backlight_set(xf86OutputPtr output, int level) +{ + struct intel_output *intel_output = output->driver_private; + struct wsdisplay_param param; + + if (level > intel_output->backlight_max) + level = intel_output->backlight_max; + if (! intel_output->backlight_iface || level < 0) + return; + + param.param = WSDISPLAYIO_PARAM_BRIGHTNESS; + param.curval = level; + if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, ¶m) == -1) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "Failed to set backlight level: %s\n", + strerror(errno)); + } +} + +static int +intel_output_backlight_get(xf86OutputPtr output) +{ + struct wsdisplay_param param; + + param.param = WSDISPLAYIO_PARAM_BRIGHTNESS; + if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, ¶m) == -1) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "Failed to get backlight level: %s\n", + strerror(errno)); + return -1; + } + + return param.curval; +} + +static void +intel_output_backlight_init(xf86OutputPtr output) +{ + struct intel_output *intel_output = output->driver_private; + struct wsdisplay_param param; + + param.param = WSDISPLAYIO_PARAM_BRIGHTNESS; + if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, ¶m) == -1) { + intel_output->backlight_iface = NULL; + return; + } + + intel_output->backlight_iface = "wscons"; + intel_output->backlight_max = param.max; + intel_output->backlight_active_level = param.curval; +} + +#else + +#define BACKLIGHT_CLASS "/sys/class/backlight" + +/* + * List of available kernel interfaces in priority order + */ +static const char *backlight_interfaces[] = { + "gmux_backlight", + "asus-laptop", + "asus-nb-wmi", + "eeepc", + "thinkpad_screen", + "mbp_backlight", + "fujitsu-laptop", + "sony", + "samsung", + "acpi_video1", /* finally fallback to the generic acpi drivers */ + "acpi_video0", + "intel_backlight", + 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 void +intel_output_backlight_set(xf86OutputPtr output, int level) +{ + struct intel_output *intel_output = output->driver_private; + char path[BACKLIGHT_PATH_LEN], val[BACKLIGHT_VALUE_LEN]; + int fd, len, ret; + + if (level > intel_output->backlight_max) + level = intel_output->backlight_max; + if (! intel_output->backlight_iface || level < 0) + return; + + len = snprintf(val, BACKLIGHT_VALUE_LEN, "%d\n", level); + sprintf(path, "%s/%s/brightness", + BACKLIGHT_CLASS, intel_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 +intel_output_backlight_get(xf86OutputPtr output) +{ + struct intel_output *intel_output = output->driver_private; + char path[BACKLIGHT_PATH_LEN], val[BACKLIGHT_VALUE_LEN]; + int fd, level; + + sprintf(path, "%s/%s/actual_brightness", + BACKLIGHT_CLASS, intel_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 > intel_output->backlight_max) + level = intel_output->backlight_max; + if (level < 0) + level = -1; + return level; +} + +static int +intel_output_backlight_get_max(xf86OutputPtr output) +{ + struct intel_output *intel_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, intel_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 +intel_output_backlight_init(xf86OutputPtr output) +{ + struct intel_output *intel_output = output->driver_private; + intel_screen_private *intel = intel_get_screen_private(output->scrn); + char path[BACKLIGHT_PATH_LEN]; + struct stat buf; + char *str; + int i; + + str = xf86GetOptValString(intel->Options, OPTION_BACKLIGHT); + if (str != NULL) { + sprintf(path, "%s/%s", BACKLIGHT_CLASS, str); + if (!stat(path, &buf)) { + intel_output->backlight_iface = str; + intel_output->backlight_max = intel_output_backlight_get_max(output); + if (intel_output->backlight_max > 0) { + intel_output->backlight_active_level = intel_output_backlight_get(output); + xf86DrvMsg(output->scrn->scrnIndex, X_CONFIG, + "found backlight control interface %s\n", path); + return; + } + } + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "unrecognised backlight control interface %s\n", str); + } + + for (i = 0; backlight_interfaces[i] != NULL; i++) { + sprintf(path, "%s/%s", BACKLIGHT_CLASS, backlight_interfaces[i]); + if (!stat(path, &buf)) { + intel_output->backlight_iface = backlight_interfaces[i]; + intel_output->backlight_max = intel_output_backlight_get_max(output); + if (intel_output->backlight_max > 0) { + intel_output->backlight_active_level = intel_output_backlight_get(output); + xf86DrvMsg(output->scrn->scrnIndex, X_PROBED, + "found backlight control interface %s\n", path); + return; + } + } + } + intel_output->backlight_iface = NULL; +} + +#endif + +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; + 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; + + if (mode->status == MODE_OK && kmode->flags & ~KNOWN_MODE_FLAGS) + mode->status = MODE_BAD; /* unknown flags => unhandled */ + + 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; + if (mode->name) + strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN); + kmode->name[DRM_DISPLAY_MODE_LEN-1] = 0; +} + +static void +intel_crtc_dpms(xf86CrtcPtr crtc, int mode) +{ +} + +void +intel_mode_disable_unused_functions(ScrnInfoPtr scrn) +{ + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); + struct intel_mode *mode = intel_get_screen_private(scrn)->modes; + int i; + + /* Force off for consistency between kernel and ddx */ + for (i = 0; i < xf86_config->num_crtc; i++) { + xf86CrtcPtr crtc = xf86_config->crtc[i]; + if (!crtc->enabled) + drmModeSetCrtc(mode->fd, crtc_id(crtc->driver_private), + 0, 0, 0, NULL, 0, NULL); + } +} + +static Bool +intel_crtc_apply(xf86CrtcPtr crtc) +{ + ScrnInfoPtr scrn = crtc->scrn; + struct intel_crtc *intel_crtc = crtc->driver_private; + struct intel_mode *mode = intel_crtc->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 intel_output *intel_output; + + if (output->crtc != crtc) + continue; + + intel_output = output->driver_private; + output_ids[output_count] = + intel_output->mode_output->connector_id; + output_count++; + } + + if (!intel_crtc->scanout_fb_id) { +#if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1,5,99,0,0) + if (!xf86CrtcRotate(crtc, mode, rotation)) + goto done; +#else + if (!xf86CrtcRotate(crtc)) + goto done; +#endif + } + +#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,7,0,0,0) + crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green, + crtc->gamma_blue, crtc->gamma_size); +#endif + + x = crtc->x; + y = crtc->y; + fb_id = mode->fb_id; + if (intel_crtc->rotate_fb_id) { + fb_id = intel_crtc->rotate_fb_id; + x = 0; + y = 0; + } else if (intel_crtc->scanout_fb_id && intel_crtc->scanout_pixmap->drawable.width >= crtc->mode.HDisplay && intel_crtc->scanout_pixmap->drawable.height >= crtc->mode.VDisplay) { + fb_id = intel_crtc->scanout_fb_id; + x = 0; + y = 0; + } + ret = drmModeSetCrtc(mode->fd, crtc_id(intel_crtc), + fb_id, x, y, output_ids, output_count, + &intel_crtc->kmode); + if (ret) { + xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, + "failed to set mode: %s\n", strerror(-ret)); + ret = FALSE; + } else { + ret = TRUE; + + /* Force DPMS to On for all outputs, which the kernel will have done + * with the mode set. Also, restore the backlight level + */ + for (i = 0; i < xf86_config->num_output; i++) { + xf86OutputPtr output = xf86_config->output[i]; + struct intel_output *intel_output; + + if (output->crtc != crtc) + continue; + + intel_output = output->driver_private; + intel_output_dpms_backlight(output, intel_output->dpms_mode, DPMSModeOn); + intel_output->dpms_mode = DPMSModeOn; + } + } + + if (scrn->pScreen) + xf86_reload_cursors(scrn->pScreen); + +done: + free(output_ids); + return ret; +} + +static Bool +intel_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, + Rotation rotation, int x, int y) +{ + ScrnInfoPtr scrn = crtc->scrn; + intel_screen_private *intel = intel_get_screen_private(scrn); + struct intel_crtc *intel_crtc = crtc->driver_private; + struct intel_mode *intel_mode = intel_crtc->mode; + int saved_x, saved_y; + Rotation saved_rotation; + DisplayModeRec saved_mode; + int ret = TRUE; + unsigned int pitch = scrn->displayWidth * intel->cpp; + + if (intel_mode->fb_id == 0) { + ret = drmModeAddFB(intel_mode->fd, + scrn->virtualX, scrn->virtualY, + scrn->depth, scrn->bitsPerPixel, + pitch, intel->front_buffer->handle, + &intel_mode->fb_id); + if (ret < 0) { + ErrorF("failed to add fb\n"); + return FALSE; + } + + drm_intel_bo_disable_reuse(intel->front_buffer); + } + + 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; + + intel_glamor_flush(intel); + intel_batch_submit(crtc->scrn); + + mode_to_kmode(crtc->scrn, &intel_crtc->kmode, mode); + ret = intel_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 +intel_crtc_set_cursor_colors(xf86CrtcPtr crtc, int bg, int fg) +{ + +} + +static void +intel_crtc_set_cursor_position (xf86CrtcPtr crtc, int x, int y) +{ + struct intel_crtc *intel_crtc = crtc->driver_private; + struct intel_mode *mode = intel_crtc->mode; + + drmModeMoveCursor(mode->fd, crtc_id(intel_crtc), x, y); +} + +static void +intel_crtc_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *image) +{ + struct intel_crtc *intel_crtc = crtc->driver_private; + int ret; + + ret = dri_bo_subdata(intel_crtc->cursor, 0, 64*64*4, image); + if (ret) + xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, + "failed to set cursor: %s\n", strerror(-ret)); +} + +static void +intel_crtc_hide_cursor(xf86CrtcPtr crtc) +{ + struct intel_crtc *intel_crtc = crtc->driver_private; + struct intel_mode *mode = intel_crtc->mode; + + drmModeSetCursor(mode->fd, crtc_id(intel_crtc), 0, 64, 64); +} + +static void +intel_crtc_show_cursor(xf86CrtcPtr crtc) +{ + struct intel_crtc *intel_crtc = crtc->driver_private; + struct intel_mode *mode = intel_crtc->mode; + + drmModeSetCursor(mode->fd, crtc_id(intel_crtc), + intel_crtc->cursor->handle, 64, 64); +} + +static void * +intel_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height) +{ + ScrnInfoPtr scrn = crtc->scrn; + struct intel_crtc *intel_crtc = crtc->driver_private; + struct intel_mode *mode = intel_crtc->mode; + unsigned long rotate_pitch; + uint32_t tiling; + int ret; + + intel_crtc->rotate_bo = intel_allocate_framebuffer(scrn, + width, height, + mode->cpp, + &rotate_pitch, + &tiling); + + if (!intel_crtc->rotate_bo) { + xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, + "Couldn't allocate shadow memory for rotated CRTC\n"); + return NULL; + } + + ret = drmModeAddFB(mode->fd, width, height, crtc->scrn->depth, + crtc->scrn->bitsPerPixel, rotate_pitch, + intel_crtc->rotate_bo->handle, + &intel_crtc->rotate_fb_id); + if (ret) { + ErrorF("failed to add rotate fb\n"); + drm_intel_bo_unreference(intel_crtc->rotate_bo); + return NULL; + } + + drm_intel_bo_disable_reuse(intel_crtc->rotate_bo); + + intel_crtc->rotate_pitch = rotate_pitch; + return intel_crtc->rotate_bo; +} + +static PixmapPtr +intel_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height) +{ + ScrnInfoPtr scrn = crtc->scrn; + intel_screen_private *intel = intel_get_screen_private(scrn); + struct intel_crtc *intel_crtc = crtc->driver_private; + PixmapPtr rotate_pixmap; + + if (!data) { + data = intel_crtc_shadow_allocate (crtc, width, height); + if (!data) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "Couldn't allocate shadow pixmap for rotated CRTC\n"); + return NULL; + } + } + if (intel_crtc->rotate_bo == NULL) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "Couldn't allocate shadow pixmap for rotated CRTC\n"); + return NULL; + } + + rotate_pixmap = GetScratchPixmapHeader(scrn->pScreen, + width, height, + scrn->depth, + scrn->bitsPerPixel, + intel_crtc->rotate_pitch, + NULL); + + if (rotate_pixmap == NULL) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "Couldn't allocate shadow pixmap for rotated CRTC\n"); + return NULL; + } + + intel_set_pixmap_bo(rotate_pixmap, intel_crtc->rotate_bo); + + intel->shadow_present = TRUE; + + return rotate_pixmap; +} + +static void +intel_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data) +{ + ScrnInfoPtr scrn = crtc->scrn; + intel_screen_private *intel = intel_get_screen_private(scrn); + struct intel_crtc *intel_crtc = crtc->driver_private; + struct intel_mode *mode = intel_crtc->mode; + + if (rotate_pixmap) { + intel_set_pixmap_bo(rotate_pixmap, NULL); + FreeScratchPixmapHeader(rotate_pixmap); + } + + if (data) { + /* Be sure to sync acceleration before the memory gets + * unbound. */ + drmModeRmFB(mode->fd, intel_crtc->rotate_fb_id); + intel_crtc->rotate_fb_id = 0; + + dri_bo_unreference(intel_crtc->rotate_bo); + intel_crtc->rotate_bo = NULL; + } + + intel->shadow_present = FALSE; +} + +static void +intel_crtc_gamma_set(xf86CrtcPtr crtc, + CARD16 *red, CARD16 *green, CARD16 *blue, int size) +{ + struct intel_crtc *intel_crtc = crtc->driver_private; + struct intel_mode *mode = intel_crtc->mode; + + drmModeCrtcSetGamma(mode->fd, crtc_id(intel_crtc), + size, red, green, blue); +} + +static void +intel_crtc_destroy(xf86CrtcPtr crtc) +{ + struct intel_crtc *intel_crtc = crtc->driver_private; + + if (intel_crtc->cursor) { + drmModeSetCursor(intel_crtc->mode->fd, crtc_id(intel_crtc), 0, 64, 64); + drm_intel_bo_unreference(intel_crtc->cursor); + intel_crtc->cursor = NULL; + } + + list_del(&intel_crtc->link); + free(intel_crtc); + + crtc->driver_private = NULL; +} + +#ifdef INTEL_PIXMAP_SHARING +static Bool +intel_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix) +{ + struct intel_crtc *intel_crtc = crtc->driver_private; + ScrnInfoPtr scrn = crtc->scrn; + intel_screen_private *intel = intel_get_screen_private(scrn); + dri_bo *bo; + + if (ppix == intel_crtc->scanout_pixmap) + return TRUE; + + if (!ppix) { + intel_crtc->scanout_pixmap = NULL; + if (intel_crtc->scanout_fb_id) { + drmModeRmFB(intel->drmSubFD, intel_crtc->scanout_fb_id); + intel_crtc->scanout_fb_id = 0; + } + return TRUE; + } + + bo = intel_get_pixmap_bo(ppix); + if (intel->front_buffer) { + ErrorF("have front buffer\n"); + } + + drm_intel_bo_disable_reuse(bo); + + intel_crtc->scanout_pixmap = ppix; + return drmModeAddFB(intel->drmSubFD, ppix->drawable.width, + ppix->drawable.height, ppix->drawable.depth, + ppix->drawable.bitsPerPixel, ppix->devKind, + bo->handle, &intel_crtc->scanout_fb_id) == 0; +} +#endif + +static const xf86CrtcFuncsRec intel_crtc_funcs = { + .dpms = intel_crtc_dpms, + .set_mode_major = intel_crtc_set_mode_major, + .set_cursor_colors = intel_crtc_set_cursor_colors, + .set_cursor_position = intel_crtc_set_cursor_position, + .show_cursor = intel_crtc_show_cursor, + .hide_cursor = intel_crtc_hide_cursor, + .load_cursor_argb = intel_crtc_load_cursor_argb, + .shadow_create = intel_crtc_shadow_create, + .shadow_allocate = intel_crtc_shadow_allocate, + .shadow_destroy = intel_crtc_shadow_destroy, + .gamma_set = intel_crtc_gamma_set, + .destroy = intel_crtc_destroy, +#ifdef INTEL_PIXMAP_SHARING + .set_scanout_pixmap = intel_set_scanout_pixmap, +#endif +}; + +static void +intel_crtc_init(ScrnInfoPtr scrn, struct intel_mode *mode, int num) +{ + intel_screen_private *intel = intel_get_screen_private(scrn); + xf86CrtcPtr crtc; + struct intel_crtc *intel_crtc; + + intel_crtc = calloc(sizeof(struct intel_crtc), 1); + if (intel_crtc == NULL) + return; + + crtc = xf86CrtcCreate(scrn, &intel_crtc_funcs); + if (crtc == NULL) { + free(intel_crtc); + return; + } + + intel_crtc->mode_crtc = drmModeGetCrtc(mode->fd, + mode->mode_res->crtcs[num]); + if (intel_crtc->mode_crtc == NULL) { + free(intel_crtc); + return; + } + + intel_crtc->mode = mode; + crtc->driver_private = intel_crtc; + + intel_crtc->pipe = drm_intel_get_pipe_from_crtc_id(intel->bufmgr, + crtc_id(intel_crtc)); + + intel_crtc->cursor = drm_intel_bo_alloc(intel->bufmgr, "ARGB cursor", + 4*64*64, 4096); + + intel_crtc->crtc = crtc; + list_add(&intel_crtc->link, &mode->crtcs); +} + +static Bool +is_panel(int type) +{ + return (type == DRM_MODE_CONNECTOR_LVDS || + type == DRM_MODE_CONNECTOR_eDP); +} + +static xf86OutputStatus +intel_output_detect(xf86OutputPtr output) +{ + /* go to the hw and retrieve a new output struct */ + struct intel_output *intel_output = output->driver_private; + struct intel_mode *mode = intel_output->mode; + xf86OutputStatus status; + + drmModeFreeConnector(intel_output->mode_output); + intel_output->mode_output = + drmModeGetConnector(mode->fd, intel_output->output_id); + if (intel_output->mode_output == NULL) { + /* and hope we are safe everywhere else */ + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "drmModeGetConnector failed, reporting output disconnected\n"); + return XF86OutputStatusDisconnected; + } + + switch (intel_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 +intel_output_mode_valid(xf86OutputPtr output, DisplayModePtr pModes) +{ + struct intel_output *intel_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 (intel_output->has_panel_limits) { + if (pModes->HDisplay > intel_output->panel_hdisplay || + pModes->VDisplay > intel_output->panel_vdisplay) + return MODE_PANEL; + } + + return MODE_OK; +} + +static void +intel_output_attach_edid(xf86OutputPtr output) +{ + struct intel_output *intel_output = output->driver_private; + drmModeConnectorPtr koutput = intel_output->mode_output; + struct intel_mode *mode = intel_output->mode; + 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(mode->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(mode->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 +intel_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); + +#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,6,99,0,0) + m = xf86GetDefaultModes(); +#else + m = xf86GetDefaultModes(0,0); +#endif + + 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 +intel_output_get_modes(xf86OutputPtr output) +{ + struct intel_output *intel_output = output->driver_private; + drmModeConnectorPtr koutput = intel_output->mode_output; + DisplayModePtr Modes = NULL; + int i; + + intel_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. + */ + intel_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 > intel_output->panel_hdisplay) + intel_output->panel_hdisplay = mode_ptr->hdisplay; + if (mode_ptr->vdisplay > intel_output->panel_vdisplay) + intel_output->panel_vdisplay = mode_ptr->vdisplay; + } + + intel_output->has_panel_limits = + intel_output->panel_hdisplay && + intel_output->panel_vdisplay; + + Modes = intel_output_panel_edid(output, Modes); + } + + return Modes; +} + +static void +intel_output_destroy(xf86OutputPtr output) +{ + struct intel_output *intel_output = output->driver_private; + int i; + + for (i = 0; i < intel_output->num_props; i++) { + drmModeFreeProperty(intel_output->props[i].mode_prop); + free(intel_output->props[i].atoms); + } + free(intel_output->props); + + drmModeFreeConnector(intel_output->mode_output); + intel_output->mode_output = NULL; + + list_del(&intel_output->link); + free(intel_output); + + output->driver_private = NULL; +} + +static void +intel_output_dpms_backlight(xf86OutputPtr output, int oldmode, int mode) +{ + struct intel_output *intel_output = output->driver_private; + + if (!intel_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) + intel_output_backlight_set(output, + intel_output->backlight_active_level); + } else { + /* Only save the current backlight value if we're going from on to off. */ + if (oldmode == DPMSModeOn) + intel_output->backlight_active_level = intel_output_backlight_get(output); + intel_output_backlight_set(output, 0); + } +} + +static void +intel_output_dpms(xf86OutputPtr output, int dpms) +{ + struct intel_output *intel_output = output->driver_private; + drmModeConnectorPtr koutput = intel_output->mode_output; + struct intel_mode *mode = intel_output->mode; + int i; + + for (i = 0; i < koutput->count_props; i++) { + drmModePropertyPtr props; + + props = drmModeGetProperty(mode->fd, koutput->props[i]); + if (!props) + continue; + + if (!strcmp(props->name, "DPMS")) { + /* Make sure to reverse the order between on and off. */ + if (dpms == DPMSModeOff) + intel_output_dpms_backlight(output, + intel_output->dpms_mode, + dpms); + drmModeConnectorSetProperty(mode->fd, + intel_output->output_id, + props->prop_id, + dpms); + if (dpms != DPMSModeOff) + intel_output_dpms_backlight(output, + intel_output->dpms_mode, + dpms); + intel_output->dpms_mode = dpms; + drmModeFreeProperty(props); + return; + } + + drmModeFreeProperty(props); + } +} + +int +intel_output_dpms_status(xf86OutputPtr output) +{ + struct intel_output *intel_output = output->driver_private; + return intel_output->dpms_mode; +} + +static Bool +intel_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 +intel_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_ERROR, + "RRConfigureOutputProperty error, %d\n", err); + + err = RRChangeOutputProperty(output->randr_output, *atom, XA_INTEGER, + 32, PropModeReplace, 1, &value, FALSE, + TRUE); + if (err != 0) + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "RRChangeOutputProperty error, %d\n", err); +} + +#define BACKLIGHT_NAME "Backlight" +#define BACKLIGHT_DEPRECATED_NAME "BACKLIGHT" +static Atom backlight_atom, backlight_deprecated_atom; + +static void +intel_output_create_resources(xf86OutputPtr output) +{ + struct intel_output *intel_output = output->driver_private; + drmModeConnectorPtr mode_output = intel_output->mode_output; + struct intel_mode *mode = intel_output->mode; + int i, j, err; + + intel_output->props = calloc(mode_output->count_props, + sizeof(struct intel_property)); + if (!intel_output->props) + return; + + intel_output->num_props = 0; + for (i = j = 0; i < mode_output->count_props; i++) { + drmModePropertyPtr drmmode_prop; + + drmmode_prop = drmModeGetProperty(mode->fd, + mode_output->props[i]); + if (intel_property_ignore(drmmode_prop)) { + drmModeFreeProperty(drmmode_prop); + continue; + } + + intel_output->props[j].mode_prop = drmmode_prop; + intel_output->props[j].value = mode_output->prop_values[i]; + j++; + } + intel_output->num_props = j; + + for (i = 0; i < intel_output->num_props; i++) { + struct intel_property *p = &intel_output->props[i]; + drmModePropertyPtr drmmode_prop = p->mode_prop; + + if (drmmode_prop->flags & DRM_MODE_PROP_RANGE) { + p->num_atoms = 1; + p->atoms = calloc(p->num_atoms, sizeof(Atom)); + if (!p->atoms) + continue; + + intel_output_create_ranged_atom(output, &p->atoms[0], + drmmode_prop->name, + drmmode_prop->values[0], + drmmode_prop->values[1], + p->value, + drmmode_prop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE); + + } 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 (intel_output->backlight_iface) { + /* Set up the backlight property, which takes effect + * immediately and accepts values only within the + * backlight_range. + */ + intel_output_create_ranged_atom(output, &backlight_atom, + BACKLIGHT_NAME, 0, + intel_output->backlight_max, + intel_output->backlight_active_level, + FALSE); + intel_output_create_ranged_atom(output, + &backlight_deprecated_atom, + BACKLIGHT_DEPRECATED_NAME, 0, + intel_output->backlight_max, + intel_output->backlight_active_level, + FALSE); + } +} + +static Bool +intel_output_set_property(xf86OutputPtr output, Atom property, + RRPropertyValuePtr value) +{ + struct intel_output *intel_output = output->driver_private; + struct intel_mode *mode = intel_output->mode; + 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 > intel_output->backlight_max) + return FALSE; + + if (intel_output->dpms_mode == DPMSModeOn) + intel_output_backlight_set(output, val); + intel_output->backlight_active_level = val; + return TRUE; + } + + for (i = 0; i < intel_output->num_props; i++) { + struct intel_property *p = &intel_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(mode->fd, intel_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); + if (name == NULL) + return FALSE; + + /* 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(mode->fd, intel_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 +intel_output_get_property(xf86OutputPtr output, Atom property) +{ + struct intel_output *intel_output = output->driver_private; + int err; + + if (property == backlight_atom || property == backlight_deprecated_atom) { + INT32 val; + + if (! intel_output->backlight_iface) + return FALSE; + + val = intel_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 intel_output_funcs = { + .create_resources = intel_output_create_resources, +#ifdef RANDR_12_INTERFACE + .set_property = intel_output_set_property, + .get_property = intel_output_get_property, +#endif + .dpms = intel_output_dpms, +#if 0 + + .save = drmmode_crt_save, + .restore = drmmode_crt_restore, + .mode_fixup = drmmode_crt_mode_fixup, + .prepare = intel_output_prepare, + .mode_set = drmmode_crt_mode_set, + .commit = intel_output_commit, +#endif + .detect = intel_output_detect, + .mode_valid = intel_output_mode_valid, + + .get_modes = intel_output_get_modes, + .destroy = intel_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 +intel_output_init(ScrnInfoPtr scrn, struct intel_mode *mode, int num) +{ + xf86OutputPtr output; + drmModeConnectorPtr koutput; + drmModeEncoderPtr kencoder; + struct intel_output *intel_output; + const char *output_name; + char name[32]; + + koutput = drmModeGetConnector(mode->fd, + mode->mode_res->connectors[num]); + if (!koutput) + return; + + kencoder = drmModeGetEncoder(mode->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, &intel_output_funcs, name); + if (!output) { + drmModeFreeEncoder(kencoder); + drmModeFreeConnector(koutput); + return; + } + + intel_output = calloc(sizeof(struct intel_output), 1); + if (!intel_output) { + xf86OutputDestroy(output); + drmModeFreeConnector(koutput); + drmModeFreeEncoder(kencoder); + return; + } + + intel_output->output_id = mode->mode_res->connectors[num]; + intel_output->mode_output = koutput; + intel_output->mode_encoder = kencoder; + intel_output->mode = mode; + + output->mm_width = koutput->mmWidth; + output->mm_height = koutput->mmHeight; + + output->subpixel_order = subpixel_conv_table[koutput->subpixel]; + output->driver_private = intel_output; + + if (is_panel(koutput->connector_type)) + intel_output_backlight_init(output); + + output->possible_crtcs = kencoder->possible_crtcs; + output->interlaceAllowed = TRUE; + + intel_output->output = output; + list_add(&intel_output->link, &mode->outputs); +} + +static Bool +intel_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height) +{ + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); + struct intel_crtc *intel_crtc = xf86_config->crtc[0]->driver_private; + struct intel_mode *mode = intel_crtc->mode; + intel_screen_private *intel = intel_get_screen_private(scrn); + drm_intel_bo *old_front = NULL; + Bool ret; + uint32_t old_fb_id; + int i, old_width, old_height, old_pitch; + unsigned long pitch; + uint32_t tiling; + ScreenPtr screen; + + if (scrn->virtualX == width && scrn->virtualY == height) + return TRUE; + + intel_glamor_flush(intel); + intel_batch_submit(scrn); + + old_width = scrn->virtualX; + old_height = scrn->virtualY; + old_pitch = scrn->displayWidth; + old_fb_id = mode->fb_id; + old_front = intel->front_buffer; + + if (intel->back_pixmap) { + screen = intel->back_pixmap->drawable.pScreen; + screen->DestroyPixmap(intel->back_pixmap); + intel->back_pixmap = NULL; + } + + if (intel->back_buffer) { + drm_intel_bo_unreference(intel->back_buffer); + intel->back_buffer = NULL; + } + + intel->front_buffer = intel_allocate_framebuffer(scrn, + width, height, + intel->cpp, + &pitch, + &tiling); + if (!intel->front_buffer) + goto fail; + + ret = drmModeAddFB(mode->fd, width, height, scrn->depth, + scrn->bitsPerPixel, pitch, + intel->front_buffer->handle, + &mode->fb_id); + if (ret) + goto fail; + + drm_intel_bo_disable_reuse(intel->front_buffer); + intel->front_pitch = pitch; + intel->front_tiling = tiling; + + scrn->virtualX = width; + scrn->virtualY = height; + + for (i = 0; i < xf86_config->num_crtc; i++) { + xf86CrtcPtr crtc = xf86_config->crtc[i]; + + if (!crtc->enabled) + continue; + + if (!intel_crtc_apply(crtc)) + goto fail; + } + + intel_uxa_create_screen_resources(scrn->pScreen); + + if (old_fb_id) + drmModeRmFB(mode->fd, old_fb_id); + if (old_front) + drm_intel_bo_unreference(old_front); + + return TRUE; + +fail: + if (intel->front_buffer) + drm_intel_bo_unreference(intel->front_buffer); + intel->front_buffer = old_front; + scrn->virtualX = old_width; + scrn->virtualY = old_height; + scrn->displayWidth = old_pitch; + if (old_fb_id != mode->fb_id) + drmModeRmFB(mode->fd, mode->fb_id); + mode->fb_id = old_fb_id; + + return FALSE; +} + +Bool +intel_do_pageflip(intel_screen_private *intel, + dri_bo *new_front, + DRI2FrameEventPtr flip_info, int ref_crtc_hw_id) +{ + ScrnInfoPtr scrn = intel->scrn; + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); + struct intel_crtc *crtc = config->crtc[0]->driver_private; + struct intel_mode *mode = crtc->mode; + unsigned int pitch = scrn->displayWidth * intel->cpp; + struct intel_pageflip *flip; + uint32_t new_fb_id; + int i; + + /* + * Create a new handle for the back buffer + */ + if (drmModeAddFB(mode->fd, scrn->virtualX, scrn->virtualY, + scrn->depth, scrn->bitsPerPixel, pitch, + new_front->handle, &new_fb_id)) + goto error_out; + + drm_intel_bo_disable_reuse(new_front); + intel_glamor_flush(intel); + intel_batch_submit(scrn); + + /* + * 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; + + for (i = 0; i < config->num_crtc; i++) { + if (!intel_crtc_on(config->crtc[i])) + continue; + + mode->flip_info = flip_info; + mode->flip_count++; + + crtc = config->crtc[i]->driver_private; + + flip = calloc(1, sizeof(struct intel_pageflip)); + if (flip == NULL) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "flip queue: carrier alloc failed.\n"); + goto error_undo; + } + + /* Only the reference crtc will finally deliver its page flip + * completion event. All other crtc's events will be discarded. + */ + flip->dispatch_me = (intel_crtc_to_pipe(crtc->crtc) == ref_crtc_hw_id); + flip->mode = mode; + + if (drmModePageFlip(mode->fd, + crtc_id(crtc), + new_fb_id, + DRM_MODE_PAGE_FLIP_EVENT, flip)) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "flip queue failed: %s\n", strerror(errno)); + free(flip); + goto error_undo; + } + } + + mode->old_fb_id = mode->fb_id; + mode->fb_id = new_fb_id; + return TRUE; + +error_undo: + drmModeRmFB(mode->fd, new_fb_id); + for (i = 0; i < config->num_crtc; i++) { + if (config->crtc[i]->enabled) + intel_crtc_apply(config->crtc[i]); + } + +error_out: + xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n", + strerror(errno)); + return FALSE; +} + +static const xf86CrtcConfigFuncsRec intel_xf86crtc_config_funcs = { + intel_xf86crtc_resize +}; + +static void +intel_vblank_handler(int fd, unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event) +{ + I830DRI2FrameEventHandler(frame, tv_sec, tv_usec, event); +} + +static void +intel_page_flip_handler(int fd, unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + struct intel_pageflip *flip = event_data; + struct intel_mode *mode = flip->mode; + + /* Is this the event whose info shall be delivered to higher level? */ + if (flip->dispatch_me) { + /* Yes: Cache msc, ust for later delivery. */ + mode->fe_frame = frame; + mode->fe_tv_sec = tv_sec; + mode->fe_tv_usec = tv_usec; + } + free(flip); + + /* Last crtc completed flip? */ + mode->flip_count--; + if (mode->flip_count > 0) + return; + + /* Release framebuffer */ + drmModeRmFB(mode->fd, mode->old_fb_id); + + if (mode->flip_info == NULL) + return; + + /* Deliver cached msc, ust from reference crtc to flip event handler */ + I830DRI2FlipEventHandler(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 intel_mode *mode; + fd_set *read_mask; + + if (data == NULL || err < 0) + return; + + mode = data; + read_mask = p; + if (FD_ISSET(mode->fd, read_mask)) + drmHandleEvent(mode->fd, &mode->event_context); +} + +static drmModeEncoderPtr +intel_get_kencoder(struct intel_mode *mode, int num) +{ + struct intel_output *iterator; + int id = mode->mode_res->encoders[num]; + + list_for_each_entry(iterator, &mode->outputs, link) + if (iterator->mode_encoder->encoder_id == id) + return iterator->mode_encoder; + + return NULL; +} + +/* + * Libdrm's possible_clones is a mask of encoders, Xorg's possible_clones is a + * mask of outputs. This function sets Xorg's possible_clones based on the + * values read from libdrm. + */ +static void +intel_compute_possible_clones(ScrnInfoPtr scrn, struct intel_mode *mode) +{ + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); + struct intel_output *intel_output, *clone; + drmModeEncoderPtr cloned_encoder; + uint32_t mask; + int i, j, k; + CARD32 possible_clones; + + for (i = 0; i < config->num_output; i++) { + possible_clones = 0; + intel_output = config->output[i]->driver_private; + + mask = intel_output->mode_encoder->possible_clones; + for (j = 0; mask != 0; j++, mask >>= 1) { + + if ((mask & 1) == 0) + continue; + + cloned_encoder = intel_get_kencoder(mode, j); + if (!cloned_encoder) + continue; + + for (k = 0; k < config->num_output; k++) { + clone = config->output[k]->driver_private; + if (clone->mode_encoder->encoder_id == + cloned_encoder->encoder_id) + possible_clones |= (1 << k); + } + } + + config->output[i]->possible_clones = possible_clones; + } +} + +Bool intel_mode_pre_init(ScrnInfoPtr scrn, int fd, int cpp) +{ + intel_screen_private *intel = intel_get_screen_private(scrn); + struct drm_i915_getparam gp; + struct intel_mode *mode; + unsigned int i; + int has_flipping; + + mode = calloc(1, sizeof *mode); + if (!mode) + return FALSE; + + mode->fd = fd; + + list_init(&mode->crtcs); + list_init(&mode->outputs); + + xf86CrtcConfigInit(scrn, &intel_xf86crtc_config_funcs); + + mode->cpp = cpp; + mode->mode_res = drmModeGetResources(mode->fd); + if (!mode->mode_res) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "failed to get resources: %s\n", strerror(errno)); + free(mode); + 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++) + intel_crtc_init(scrn, mode, i); + + for (i = 0; i < mode->mode_res->count_connectors; i++) + intel_output_init(scrn, mode, i); + + intel_compute_possible_clones(scrn, mode); + +#ifdef INTEL_PIXMAP_SHARING + xf86ProviderSetup(scrn, NULL, "Intel"); +#endif + + xf86InitialConfiguration(scrn, TRUE); + + mode->event_context.version = DRM_EVENT_CONTEXT_VERSION; + mode->event_context.vblank_handler = intel_vblank_handler; + mode->event_context.page_flip_handler = intel_page_flip_handler; + + has_flipping = 0; + gp.param = I915_PARAM_HAS_PAGEFLIPPING; + gp.value = &has_flipping; + (void)drmCommandWriteRead(intel->drmSubFD, DRM_I915_GETPARAM, &gp, + sizeof(gp)); + if (has_flipping && intel->swapbuffers_wait) { + xf86DrvMsg(scrn->scrnIndex, X_INFO, + "Kernel page flipping support detected, enabling\n"); + intel->use_pageflipping = TRUE; + } + + intel->modes = mode; + return TRUE; +} + +void +intel_mode_init(struct intel_screen_private *intel) +{ + struct intel_mode *mode = intel->modes; + + /* 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(mode->fd); + RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA, + drm_wakeup_handler, mode); +} + +void +intel_mode_remove_fb(intel_screen_private *intel) +{ + struct intel_mode *mode = intel->modes; + + if (mode->fb_id) { + drmModeRmFB(mode->fd, mode->fb_id); + mode->fb_id = 0; + } +} + +static Bool has_pending_events(int fd) +{ + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLIN; + return poll(&pfd, 1, 0) == 1; +} + +void +intel_mode_close(intel_screen_private *intel) +{ + struct intel_mode *mode = intel->modes; + + if (mode == NULL) + return; + + while (has_pending_events(mode->fd)) + drmHandleEvent(mode->fd, &mode->event_context); + + RemoveBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA, + drm_wakeup_handler, mode); + RemoveGeneralSocket(mode->fd); +} + +void +intel_mode_fini(intel_screen_private *intel) +{ + struct intel_mode *mode = intel->modes; + + if (mode == NULL) + return; + + while(!list_is_empty(&mode->crtcs)) { + xf86CrtcDestroy(list_first_entry(&mode->crtcs, + struct intel_crtc, + link)->crtc); + } + + while(!list_is_empty(&mode->outputs)) { + xf86OutputDestroy(list_first_entry(&mode->outputs, + struct intel_output, + link)->output); + } + + if (mode->fb_id) + drmModeRmFB(mode->fd, mode->fb_id); + + /* mode->rotate_fb_id should have been destroyed already */ + + free(mode); + intel->modes = NULL; +} + +/* for the mode overlay */ +int +intel_crtc_id(xf86CrtcPtr crtc) +{ + return crtc_id(crtc->driver_private); +} + +int intel_crtc_to_pipe(xf86CrtcPtr crtc) +{ + struct intel_crtc *intel_crtc = crtc->driver_private; + return intel_crtc->pipe; +} + +Bool intel_crtc_on(xf86CrtcPtr crtc) +{ + struct intel_crtc *intel_crtc = crtc->driver_private; + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); + drmModeCrtcPtr drm_crtc; + Bool ret; + int i; + + if (!crtc->enabled) + return FALSE; + + /* Kernel manages CRTC status based on output config */ + ret = FALSE; + for (i = 0; i < xf86_config->num_output; i++) { + xf86OutputPtr output = xf86_config->output[i]; + if (output->crtc == crtc && + intel_output_dpms_status(output) == DPMSModeOn) { + ret = TRUE; + break; + } + } + if (!ret) + return FALSE; + + /* And finally check with the kernel that the fb is bound */ + drm_crtc = drmModeGetCrtc(intel_crtc->mode->fd, crtc_id(intel_crtc)); + if (drm_crtc == NULL) + return FALSE; + + ret = (drm_crtc->mode_valid && + intel_crtc->mode->fb_id == drm_crtc->buffer_id); + free(drm_crtc); + + return ret; +} + +static PixmapPtr +intel_create_pixmap_for_bo(ScreenPtr pScreen, dri_bo *bo, + int width, int height, + int depth, int bpp, + int pitch) +{ + PixmapPtr pixmap; + + pixmap = pScreen->CreatePixmap(pScreen, 0, 0, depth, 0); + if (pixmap == NullPixmap) + return pixmap; + + if (!pScreen->ModifyPixmapHeader(pixmap, + width, height, + depth, bpp, + pitch, NULL)) { + pScreen->DestroyPixmap(pixmap); + return NullPixmap; + } + + intel_set_pixmap_bo(pixmap, bo); + return pixmap; +} + +static PixmapPtr +intel_create_pixmap_for_fbcon(ScrnInfoPtr scrn, int fbcon_id) +{ + ScreenPtr pScreen = xf86ScrnToScreen(scrn); + intel_screen_private *intel = intel_get_screen_private(scrn); + struct intel_mode *mode = intel->modes; + int fd = mode->fd; + drmModeFBPtr fbcon; + struct drm_gem_flink flink; + drm_intel_bo *bo; + PixmapPtr pixmap = NullPixmap; + + fbcon = drmModeGetFB(fd, fbcon_id); + if (fbcon == NULL) + return NULL; + + if (fbcon->depth != scrn->depth || + fbcon->width != scrn->virtualX || + fbcon->height != scrn->virtualY) + goto out_free_fb; + + flink.handle = fbcon->handle; + if (ioctl(fd, DRM_IOCTL_GEM_FLINK, &flink) < 0) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "Couldn't flink fbcon handle\n"); + goto out_free_fb; + } + + bo = drm_intel_bo_gem_create_from_name(intel->bufmgr, + "fbcon", flink.name); + if (bo == NULL) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "Couldn't allocate bo for fbcon handle\n"); + goto out_free_fb; + } + + pixmap = intel_create_pixmap_for_bo(pScreen, bo, + fbcon->width, fbcon->height, + fbcon->depth, fbcon->bpp, + fbcon->pitch); + if (pixmap == NullPixmap) + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "Couldn't allocate pixmap fbcon contents\n"); + drm_intel_bo_unreference(bo); +out_free_fb: + drmModeFreeFB(fbcon); + + return pixmap; +} + +void intel_copy_fb(ScrnInfoPtr scrn) +{ + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); + ScreenPtr pScreen = xf86ScrnToScreen(scrn); + intel_screen_private *intel = intel_get_screen_private(scrn); + PixmapPtr src, dst; + unsigned int pitch = scrn->displayWidth * intel->cpp; + struct intel_crtc *intel_crtc; + int i, fbcon_id; + + if (intel->force_fallback) + return; + + fbcon_id = 0; + for (i = 0; i < xf86_config->num_crtc; i++) { + intel_crtc = xf86_config->crtc[i]->driver_private; + if (intel_crtc->mode_crtc->buffer_id) + fbcon_id = intel_crtc->mode_crtc->buffer_id; + } + if (!fbcon_id) + return; + + src = intel_create_pixmap_for_fbcon(scrn, fbcon_id); + if (src == NULL) + return; + + /* We dont have a screen Pixmap yet */ + dst = intel_create_pixmap_for_bo(pScreen, intel->front_buffer, + scrn->virtualX, scrn->virtualY, + scrn->depth, scrn->bitsPerPixel, + pitch); + if (dst == NullPixmap) + goto cleanup_src; + + if (!intel->uxa_driver->prepare_copy(src, dst, + -1, -1, + GXcopy, FB_ALLONES)) + goto cleanup_dst; + + intel->uxa_driver->copy(dst, + 0, 0, + 0, 0, + scrn->virtualX, scrn->virtualY); + intel->uxa_driver->done_copy(dst); +#if ABI_VIDEODRV_VERSION >= SET_ABI_VERSION(10, 0) + pScreen->canDoBGNoneRoot = TRUE; +#endif + +cleanup_dst: + (*pScreen->DestroyPixmap)(dst); +cleanup_src: + (*pScreen->DestroyPixmap)(src); +} |