Permalink
Find file
6623355 Dec 6, 2012
405 lines (385 sloc) 10.3 KB
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
<https://github.com/liskin/hybrid-screenclone>.
If more than one VIRTUAL output is needed, add
Option "Virtuals" "2"
to the Device section of your xorg.conf.
-- Tomáš Janoušek <tomi@nomi.cz>
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;
+}