This is a patch that adds a single virtual CRTC to an intel driver. The motivation is that this output may be turned on and cloned (in userspace) to another X server, which may be running on a different graphics card. My usecase is a triple-head (with hotplug) setup on a NVIDIA Optimus laptop. See . If more than one VIRTUAL output is needed, add Option "Virtuals" "2" to the Device section of your xorg.conf. -- Tomáš Janoušek diff --git a/src/intel_display.c b/src/intel_display.c index 2c93c2d..57dea60 100644 --- a/src/intel_display.c +++ b/src/intel_display.c @@ -121,6 +121,8 @@ struct intel_output { struct list link; }; +#include "virtual_display.c" + static void intel_output_dpms(xf86OutputPtr output, int mode); @@ -373,6 +375,9 @@ intel_mode_disable_unused_functions(ScrnInfoPtr scrn) /* Force off for consistency between kernel and ddx */ for (i = 0; i < xf86_config->num_crtc; i++) { + if (is_virtual(mode, i)) + continue; + xf86CrtcPtr crtc = xf86_config->crtc[i]; if (!crtc->enabled) drmModeSetCrtc(mode->fd, crtc_id(crtc->driver_private), @@ -1441,7 +1446,7 @@ intel_output_init(ScrnInfoPtr scrn, struct intel_mode *mode, int num) if (is_panel(koutput->connector_type)) intel_output_backlight_init(output); - output->possible_crtcs = kencoder->possible_crtcs; + output->possible_crtcs = kencoder->possible_crtcs & ((1 << mode->mode_res->count_crtcs) - 1); output->interlaceAllowed = TRUE; intel_output->output = output; @@ -1514,6 +1519,9 @@ intel_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height) if (!crtc->enabled) continue; + if (is_virtual(mode, i)) + continue; + if (!intel_crtc_apply(crtc)) goto fail; } @@ -1584,6 +1592,9 @@ intel_do_pageflip(intel_screen_private *intel, if (!intel_crtc_on(config->crtc[i])) continue; + if (is_virtual(mode, i)) + continue; + mode->flip_info = flip_info; mode->flip_count++; @@ -1620,6 +1631,9 @@ intel_do_pageflip(intel_screen_private *intel, error_undo: drmModeRmFB(mode->fd, new_fb_id); for (i = 0; i < config->num_crtc; i++) { + if (is_virtual(mode, i)) + continue; + if (config->crtc[i]->enabled) intel_crtc_apply(config->crtc[i]); } @@ -1748,7 +1762,7 @@ Bool intel_mode_pre_init(ScrnInfoPtr scrn, int fd, int cpp) struct drm_i915_getparam gp; struct intel_mode *mode; unsigned int i; - int has_flipping; + int has_flipping, num_virtual; mode = calloc(1, sizeof *mode); if (!mode) @@ -1780,6 +1794,13 @@ Bool intel_mode_pre_init(ScrnInfoPtr scrn, int fd, int cpp) intel_compute_possible_clones(scrn, mode); + num_virtual = 1; + xf86GetOptValInteger(intel->Options, OPTION_VIRTUALS, &num_virtual); + for (i = 0; i < num_virtual; i++) { + virtual_crtc_init(scrn, mode); + virtual_output_init(scrn, mode, i); + } + #ifdef INTEL_PIXMAP_SHARING xf86ProviderSetup(scrn, NULL, "Intel"); #endif @@ -1904,6 +1925,10 @@ Bool intel_crtc_on(xf86CrtcPtr crtc) if (!crtc->enabled) return FALSE; + /* Virtual CRTC? */ + if (intel_crtc->mode_crtc == NULL) + return FALSE; + /* Kernel manages CRTC status based on output config */ ret = FALSE; for (i = 0; i < xf86_config->num_output; i++) { @@ -2019,6 +2044,10 @@ void intel_copy_fb(ScrnInfoPtr scrn) fbcon_id = 0; for (i = 0; i < xf86_config->num_crtc; i++) { intel_crtc = xf86_config->crtc[i]->driver_private; + + if (is_virtual(intel_crtc->mode, i)) + continue; + if (intel_crtc->mode_crtc->buffer_id) fbcon_id = intel_crtc->mode_crtc->buffer_id; } diff --git a/src/intel_options.c b/src/intel_options.c index 443e84d..fd4fad3 100644 --- a/src/intel_options.c +++ b/src/intel_options.c @@ -35,6 +35,7 @@ const OptionInfoRec intel_options[] = { {OPTION_BUFFER_CACHE, "BufferCache", OPTV_BOOLEAN, {0}, 1}, {OPTION_TRIPLE_BUFFER, "TripleBuffer", OPTV_BOOLEAN, {0}, 1}, #endif + {OPTION_VIRTUALS, "Virtuals", OPTV_INTEGER, {0}, 0}, {-1, NULL, OPTV_NONE, {0}, 0} }; diff --git a/src/intel_options.h b/src/intel_options.h index 3b5262a..3050a56 100644 --- a/src/intel_options.h +++ b/src/intel_options.h @@ -42,6 +42,7 @@ enum intel_options { OPTION_BUFFER_CACHE, OPTION_TRIPLE_BUFFER, #endif + OPTION_VIRTUALS, NUM_OPTIONS, }; diff --git a/src/intel_uxa.c b/src/intel_uxa.c index 6d202c7..f0baef8 100644 --- a/src/intel_uxa.c +++ b/src/intel_uxa.c @@ -230,8 +230,13 @@ intel_uxa_pixmap_compute_size(PixmapPtr pixmap, if (*tiling == I915_TILING_NONE) { /* We only require a 64 byte alignment for scanouts, but * a 256 byte alignment for sharing with PRIME. + * + * XXX: 256 makes XShmGetImage terribly slow with resolutions + * like 1680x1050, hence we revert back to 64. It shouldn't + * break anything as screenclone is a poor man's PRIME and the + * real PRIME isn't needed. */ - *stride = ALIGN(pitch, 256); + *stride = ALIGN(pitch, 64); /* Round the height up so that the GPU's access to a 2x2 aligned * subspan doesn't address an invalid page offset beyond the * end of the GTT. diff --git a/src/virtual_display.c b/src/virtual_display.c new file mode 100644 index 0000000..18f45bb --- /dev/null +++ b/src/virtual_display.c @@ -0,0 +1,234 @@ +static void +virtual_crtc_dpms(xf86CrtcPtr intel_crtc, int mode) +{ + +} + +static Bool +virtual_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, + Rotation rotation, int x, int y) +{ + return TRUE; +} + +static void +virtual_crtc_set_cursor_colors(xf86CrtcPtr crtc, int bg, int fg) +{ + +} + +static void +virtual_crtc_set_cursor_position (xf86CrtcPtr crtc, int x, int y) +{ +} + +static void +virtual_crtc_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *image) +{ +} + +static void +virtual_crtc_hide_cursor(xf86CrtcPtr crtc) +{ +} + +static void +virtual_crtc_show_cursor(xf86CrtcPtr crtc) +{ +} + +static void * +virtual_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height) +{ + return NULL; +} + +static PixmapPtr +virtual_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height) +{ + return NULL; +} + +static void +virtual_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data) +{ +} + +static void +virtual_crtc_gamma_set(xf86CrtcPtr crtc, + CARD16 *red, CARD16 *green, CARD16 *blue, int size) +{ +} + +static void +virtual_crtc_destroy(xf86CrtcPtr crtc) +{ + struct intel_crtc *intel_crtc = crtc->driver_private; + + list_del(&intel_crtc->link); + free(intel_crtc); + + crtc->driver_private = NULL; +} + +static const xf86CrtcFuncsRec virtual_crtc_funcs = { + .dpms = virtual_crtc_dpms, + .set_mode_major = virtual_crtc_set_mode_major, + .set_cursor_colors = virtual_crtc_set_cursor_colors, + .set_cursor_position = virtual_crtc_set_cursor_position, + .show_cursor = virtual_crtc_show_cursor, + .hide_cursor = virtual_crtc_hide_cursor, + .load_cursor_argb = virtual_crtc_load_cursor_argb, + .shadow_create = virtual_crtc_shadow_create, + .shadow_allocate = virtual_crtc_shadow_allocate, + .shadow_destroy = virtual_crtc_shadow_destroy, + .gamma_set = virtual_crtc_gamma_set, + .destroy = virtual_crtc_destroy, +}; + +static void +virtual_crtc_init(ScrnInfoPtr scrn, struct intel_mode *mode) +{ + xf86CrtcPtr crtc; + struct intel_crtc *intel_crtc; + + intel_crtc = calloc(sizeof(struct intel_crtc), 1); + if (intel_crtc == NULL) + return; + + crtc = xf86CrtcCreate(scrn, &virtual_crtc_funcs); + if (crtc == NULL) { + free(intel_crtc); + return; + } + + intel_crtc->mode = mode; + intel_crtc->crtc = crtc; + crtc->driver_private = intel_crtc; + list_add(&intel_crtc->link, &mode->crtcs); +} + +static xf86OutputStatus +virtual_output_detect(xf86OutputPtr output) +{ + // return XF86OutputStatusConnected; + // return XF86OutputStatusDisconnected; + return XF86OutputStatusUnknown; +} + +static Bool +virtual_output_mode_valid(xf86OutputPtr output, DisplayModePtr pModes) +{ + return MODE_OK; +} + +static DisplayModePtr +virtual_output_get_modes(xf86OutputPtr output) +{ + DisplayModePtr i, m, p = NULL; + int max_x = 1920, max_y = 1200; + float max_vrefresh = 60.0; + +#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); + + return m; +} + +static void +virtual_output_destroy(xf86OutputPtr output) +{ + struct intel_output *intel_output = output->driver_private; + + list_del(&intel_output->link); + free(intel_output); + + output->driver_private = NULL; +} + +static void +virtual_output_dpms(xf86OutputPtr output, int dpms) +{ +} + +static void +virtual_output_create_resources(xf86OutputPtr output) +{ +} + +static Bool +virtual_output_set_property(xf86OutputPtr output, Atom property, + RRPropertyValuePtr value) +{ + return TRUE; +} + +static Bool +virtual_output_get_property(xf86OutputPtr output, Atom property) +{ + return FALSE; +} + +static const xf86OutputFuncsRec virtual_output_funcs = { + .create_resources = virtual_output_create_resources, +#ifdef RANDR_12_INTERFACE + .set_property = virtual_output_set_property, + .get_property = virtual_output_get_property, +#endif + .dpms = virtual_output_dpms, + .detect = virtual_output_detect, + .mode_valid = virtual_output_mode_valid, + + .get_modes = virtual_output_get_modes, + .destroy = virtual_output_destroy +}; + +static void +virtual_output_init(ScrnInfoPtr scrn, struct intel_mode *mode, int index) +{ + xf86OutputPtr output; + struct intel_output *intel_output; + char name[32]; + sprintf( name, "VIRTUAL%d", index + 1 ); + + output = xf86OutputCreate (scrn, &virtual_output_funcs, name); + if (!output) { + return; + } + + intel_output = calloc(sizeof(struct intel_output), 1); + if (!intel_output) { + xf86OutputDestroy(output); + return; + } + + output->subpixel_order = SubPixelHorizontalRGB; + output->possible_crtcs = (1 << (mode->mode_res->count_crtcs + index)); + output->driver_private = intel_output; + intel_output->output = output; + intel_output->mode = mode; + + list_add(&intel_output->link, &mode->outputs); +} + +static Bool +is_virtual(struct intel_mode *mode, int i) +{ + return i >= mode->mode_res->count_crtcs; +}