summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Hellstrom <thellstrom@vmware.com>2014-12-18 10:46:42 +0100
committerThomas Hellstrom <thellstrom@vmware.com>2015-01-28 11:53:26 -0800
commit0cf9b4e5bf4b5f8f1b00a4f9e7ad4bfba963f996 (patch)
treece660608b93f5522f6997649984db738ffeacead
parent3f7defa8dc595342e2502206ed2006f2be20a884 (diff)
vmware/vmwgfx: Expose KMS connector properties and react to hotplug eventsfeature/thellstrom/resolutionKMS
Optionally re-export KMS connector properties as Xserver output properties. Also install an udev monitor that rereads KMS information when it has changed. Some of this code is stolen from the modesetting- and qxl drivers. Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com> Reviewed-by: Jakob Bornecrantz <jakob@vmware.com>
-rw-r--r--configure.ac12
-rw-r--r--vmwgfx/vmwgfx_driver.c2
-rw-r--r--vmwgfx/vmwgfx_driver.h14
-rw-r--r--vmwgfx/vmwgfx_output.c451
4 files changed, 432 insertions, 47 deletions
diff --git a/configure.ac b/configure.ac
index be40ed7..d33d6eb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -142,6 +142,18 @@ AC_MSG_CHECKING([whether to build Kernel Mode Setting and 3D])
if test x$BUILD_VMWGFX = xyes; then
AC_MSG_RESULT([yes])
AC_DEFINE([BUILD_VMWGFX], 1, [Building the vmwgfx driver path])
+ libudev_check=yes
+ AC_ARG_WITH([libudev],
+ [AS_HELP_STRING([--without-libudev],
+ [Use to build without libudev on linux])],
+ [if test x$withval = xno; then libudev_check=no; fi]
+ [])
+ if test $libudev_check != no; then
+ PKG_CHECK_MODULES(LIBUDEV, [libudev],
+ [AC_DEFINE([HAVE_LIBUDEV], 1,
+ [Has libudev installed])],
+ []);
+ fi
else
AC_MSG_RESULT([no])
fi
diff --git a/vmwgfx/vmwgfx_driver.c b/vmwgfx/vmwgfx_driver.c
index 2925227..1852631 100644
--- a/vmwgfx/vmwgfx_driver.c
+++ b/vmwgfx/vmwgfx_driver.c
@@ -798,6 +798,7 @@ drv_create_screen_resources(ScreenPtr pScreen)
return ret;
drv_adjust_frame(ADJUST_FRAME_ARGS(pScrn, pScrn->frameX0, pScrn->frameY0));
+ vmwgfx_uevent_init(pScrn);
return drv_enter_vt(VT_FUNC_ARGS);
}
@@ -1261,6 +1262,7 @@ drv_close_screen(CLOSE_SCREEN_ARGS_DECL)
if (pScrn->vtSema)
pScrn->LeaveVT(VT_FUNC_ARGS);
+ vmwgfx_uevent_fini(pScrn);
pScrn->vtSema = FALSE;
vmwgfx_unwrap(ms, pScrn, EnterVT);
diff --git a/vmwgfx/vmwgfx_driver.h b/vmwgfx/vmwgfx_driver.h
index 31dfc0f..0897d78 100644
--- a/vmwgfx/vmwgfx_driver.h
+++ b/vmwgfx/vmwgfx_driver.h
@@ -54,6 +54,10 @@
#endif
#endif
+#ifdef HAVE_LIBUDEV
+#include <libudev.h>
+#endif
+
#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 12
#define _swapl(x, n) swapl(x,n)
#define _swaps(x, n) swaps(x,n)
@@ -141,11 +145,16 @@ typedef struct _modesettingRec
Bool dri2_available;
char dri2_device_name[VMWGFX_DRI_DEVICE_LEN];
#endif
+#ifdef HAVE_LIBUDEV
+ struct udev_monitor *uevent_monitor;
+ InputHandlerProc uevent_handler;
+#endif
} modesettingRec, *modesettingPtr;
#define modesettingPTR(p) ((modesettingPtr)((p)->driverPrivate))
void xorg_flush(ScreenPtr pScreen);
+
/***********************************************************************
* xorg_dri2.c
*/
@@ -183,7 +192,10 @@ xorg_output_get_id(xf86OutputPtr output);
Bool
vmwgfx_output_explicit_overlap(ScrnInfoPtr pScrn);
-
+void
+vmwgfx_uevent_init(ScrnInfoPtr scrn);
+void
+vmwgfx_uevent_fini(ScrnInfoPtr scrn);
/***********************************************************************
* xorg_xv.c
diff --git a/vmwgfx/vmwgfx_output.c b/vmwgfx/vmwgfx_output.c
index f9e4263..34c82ef 100644
--- a/vmwgfx/vmwgfx_output.c
+++ b/vmwgfx/vmwgfx_output.c
@@ -27,20 +27,19 @@
* Author: Jakob Bornecrantz <wallbraker@gmail.com>
*
*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
#include "xorg-server.h"
-#include <xf86.h>
-#include <xf86i2c.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <xf86str.h>
+#include <xf86RandR12.h>
+#include <randrstr.h>
#include <xf86Crtc.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
+#include <X11/Xatom.h>
+#include <dix.h>
#ifdef HAVE_XEXTPROTO_71
#include <X11/extensions/dpmsconst.h>
@@ -51,13 +50,43 @@
#include "vmwgfx_driver.h"
+/**
+ * struct output_prop - Internal description of an X server output property
+ *
+ * @mode_prop: Corresponding KMS connector property.
+ * @value: Cached value of the property.
+ * @num_atoms: Number of atoms for this property.
+ * @atoms: Array of atoms.
+ * @index: Index into the KMS connector KMS property array for the
+ * corresponding KMS property.
+ */
+struct output_prop {
+ drmModePropertyPtr mode_prop;
+ uint64_t value;
+ int num_atoms;
+ Atom *atoms;
+ int index;
+};
+
+/**
+ * struct output_private - Driver private description of an X server output
+ *
+ * @drm_connector: Pointer to a connector representing the corresponding KMS
+ * connector.
+ * @num_props: Number of struct output_prop in the @props array.
+ * @props: Array of properties for this output.
+ * @is_implicit: Whether this output currently is using implicit placement.
+ * @suggested_x: Shortcut to the suggested X KMS property.
+ * @suggested_y: Shortcut to the suggested Y KMS property.
+ */
struct output_private
{
drmModeConnectorPtr drm_connector;
-
- int c;
-
+ int num_props;
+ struct output_prop *props;
Bool is_implicit;
+ struct output_prop *suggested_x;
+ struct output_prop *suggested_y;
};
static const char *output_enum_list[] = {
@@ -79,11 +108,135 @@ static const char *output_enum_list[] = {
"Virtual",
};
+/**
+ * output_property_ignore - Whether to hide a KMS property
+ *
+ * @prop: Pointer to a KMS property.
+ *
+ * This function returns TRUE if the property is *not* to be exposed as an
+ * Xserver output property.
+ */
+static Bool
+output_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") ||
+ !strcmp(prop->name, "dirty"))
+ return TRUE;
+
+ return FALSE;
+}
+
+
+/**
+ * output_create_resources - post screen creation output initialization
+ *
+ * @output: Output pointer
+ *
+ * This function reads KMS connector property data, initializes internal
+ * data structures and optionally re-exports the property data as
+ * Xserver output properties.
+ */
static void
output_create_resources(xf86OutputPtr output)
{
-#ifdef RANDR_12_INTERFACE
-#endif /* RANDR_12_INTERFACE */
+ modesettingPtr ms = modesettingPTR(output->scrn);
+ struct output_private *vmwgfx_output = output->driver_private;
+ drmModeConnectorPtr drm_connector = vmwgfx_output->drm_connector;
+ drmModePropertyPtr drmmode_prop;
+ struct output_prop *p;
+ int i, j, err;
+
+ vmwgfx_output->props = calloc(drm_connector->count_props,
+ sizeof(struct output_prop));
+ if (!vmwgfx_output->props)
+ return;
+
+ vmwgfx_output->num_props = 0;
+ p = vmwgfx_output->props;
+ for (i = 0; i < drm_connector->count_props; i++) {
+ drmmode_prop = drmModeGetProperty(ms->fd, drm_connector->props[i]);
+ if (output_property_ignore(drmmode_prop)) {
+ drmModeFreeProperty(drmmode_prop);
+ continue;
+ }
+ p->index = i;
+ p->mode_prop = drmmode_prop;
+ p->value = drm_connector->prop_values[i];
+ if (!strcmp(drmmode_prop->name, "suggested X"))
+ vmwgfx_output->suggested_x = p;
+ else if (!strcmp(drmmode_prop->name, "suggested Y"))
+ vmwgfx_output->suggested_y = p;
+ vmwgfx_output->num_props++;
+ p++;
+ }
+
+ p = vmwgfx_output->props;
+ for (i = 0; i < vmwgfx_output->num_props; p++, i++) {
+ drmmode_prop = p->mode_prop;
+
+ if (drmmode_prop->flags & DRM_MODE_PROP_RANGE) {
+ INT32 qrange[2];
+ INT32 value = p->value;
+
+ 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);
+ qrange[0] = drmmode_prop->values[0];
+ qrange[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, qrange);
+ 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, &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);
+ }
+ }
+ }
}
static void
@@ -99,7 +252,7 @@ output_detect(xf86OutputPtr output)
drmModeConnectorPtr drm_connector;
xf86OutputStatus status;
- drm_connector = drmModeGetConnector(ms->fd, priv->drm_connector->connector_id);
+ drm_connector = drmModeGetConnector(ms->fd, xorg_output_get_id(output));
if (drm_connector) {
drmModeFreeConnector(priv->drm_connector);
priv->drm_connector = drm_connector;
@@ -181,25 +334,170 @@ output_mode_valid(xf86OutputPtr output, DisplayModePtr pMode)
}
#ifdef RANDR_12_INTERFACE
+/**
+ * output_set_property - Set the value of a property on an output
+ *
+ * @output: The output we want to set the property on.
+ * @property: Atom representing the property.
+ * @value: Property value.
+ *
+ * Returns FALSE if the value is not within range. TRUE otherwise.
+ */
static Bool
output_set_property(xf86OutputPtr output, Atom property, RRPropertyValuePtr value)
{
+ modesettingPtr ms = modesettingPTR(output->scrn);
+ struct output_private *vmwgfx_output = output->driver_private;
+ int i;
+
+ for (i = 0; i < vmwgfx_output->num_props; i++) {
+ struct output_prop *p = &vmwgfx_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(ms->fd, xorg_output_get_id(output),
+ 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(ms->fd,
+ xorg_output_get_id(output),
+ p->mode_prop->prop_id,
+ p->mode_prop->enums[j].value);
+ return TRUE;
+ }
+ }
+ }
+ }
+
return TRUE;
}
#endif /* RANDR_12_INTERFACE */
#ifdef RANDR_13_INTERFACE
+/**
+ * vmwgfx_refresh_connector - Refresh the KMS connector info of an output
+ *
+ * @output: The output on which to refresh info
+ *
+ * Rereads the connector info from DRM. Returns TRUE if successful,
+ * FALSE otherwise.
+ */
static Bool
-output_get_property(xf86OutputPtr output, Atom property)
+vmwgfx_refresh_connector(xf86OutputPtr output)
{
+ modesettingPtr ms = modesettingPTR(output->scrn);
+ struct output_private *vmwgfx_output = output->driver_private;
+ drmModeConnectorPtr new_connector;
+ int i;
+
+ if (output->scrn->vtSema) {
+ int id = xorg_output_get_id(output);
+
+ new_connector = drmModeGetConnector(ms->fd, id);
+ if (new_connector) {
+ drmModeFreeConnector(vmwgfx_output->drm_connector);
+ vmwgfx_output->drm_connector = drmModeGetConnector(ms->fd, id);
+ } else {
+ return FALSE;
+ }
+ }
+
+ for (i = 0; i < vmwgfx_output->num_props; i++) {
+ struct output_prop *p = &vmwgfx_output->props[i];
+
+ p->value = vmwgfx_output->drm_connector->prop_values[p->index];
+ }
+
return TRUE;
}
+
+/**
+ * output_get_property - Update the value of an XServer property.
+ *
+ * @output: The output we want to set the property on.
+ * @property: Atom representing the property.
+ *
+ * Returns FALSE if the property is not found. TRUE otherwise.
+ */
+static Bool
+output_get_property(xf86OutputPtr output, Atom property)
+{
+ struct output_private *vmwgfx_output = output->driver_private;
+ uint32_t value;
+ int err, i;
+
+ if (!vmwgfx_refresh_connector(output))
+ return FALSE;
+
+ for (i = 0; i < vmwgfx_output->num_props; i++) {
+ struct output_prop *p = &vmwgfx_output->props[i];
+ if (p->atoms[0] != property)
+ continue;
+
+ value = vmwgfx_output->drm_connector->prop_values[p->index];
+ p->value = value;
+
+ if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
+ err = RRChangeOutputProperty(output->randr_output,
+ property, XA_INTEGER, 32,
+ PropModeReplace, 1, &value,
+ FALSE, FALSE);
+
+ return !err;
+ } else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
+ int j;
+
+ /* search for matching name string, then set its value down */
+ for (j = 0; j < p->mode_prop->count_enums; j++) {
+ if (p->mode_prop->enums[j].value == value)
+ break;
+ }
+
+ err = RRChangeOutputProperty(output->randr_output, property,
+ XA_ATOM, 32, PropModeReplace, 1,
+ &p->atoms[j+1], FALSE, FALSE);
+
+ return !err;
+ }
+ }
+
+ return FALSE;
+}
#endif /* RANDR_13_INTERFACE */
static void
output_destroy(xf86OutputPtr output)
{
struct output_private *priv = output->driver_private;
+ int i;
+
+ for (i = 0; i < priv->num_props; i++) {
+ drmModeFreeProperty(priv->props[i].mode_prop);
+ free(priv->props[i].atoms);
+ }
+ free(priv->props);
+
drmModeFreeConnector(priv->drm_connector);
free(priv);
output->driver_private = NULL;
@@ -300,7 +598,7 @@ xorg_output_init(ScrnInfoPtr pScrn)
drmModeEncoderPtr drm_encoder = NULL;
struct output_private *priv;
char name[32];
- int c, p;
+ int c;
res = drmModeGetResources(ms->fd);
if (res == 0) {
@@ -315,32 +613,6 @@ xorg_output_init(ScrnInfoPtr pScrn)
if (!drm_connector)
goto out;
-
- for (p = 0; p < drm_connector->count_props; p++) {
- drmModePropertyPtr prop;
-
- prop = drmModeGetProperty(ms->fd, drm_connector->props[p]);
-
- if (prop) {
-
-#if 0
- /*
- * Disabled until we sort out what the interface should
- * look like.
- */
-
- if (strcmp(prop->name, "implicit placement") == 0) {
- drmModeConnectorSetProperty(ms->fd,
- drm_connector->connector_id,
- prop->prop_id,
- 0);
- is_implicit = FALSE;
- }
-#endif
- drmModeFreeProperty(prop);
- }
- }
-
if (drm_connector->connector_type >=
sizeof(output_enum_list) / sizeof(output_enum_list[0]))
drm_connector->connector_type = 0;
@@ -371,7 +643,6 @@ xorg_output_init(ScrnInfoPtr pScrn)
output->possible_crtcs = 0;
output->possible_clones = 0;
}
- priv->c = c;
priv->drm_connector = drm_connector;
output->driver_private = priv;
output->subpixel_order = SubPixelHorizontalRGB;
@@ -390,4 +661,92 @@ xorg_output_get_id(xf86OutputPtr output)
return priv->drm_connector->connector_id;
}
+
+#ifdef HAVE_LIBUDEV
+/**
+ * vmwgfx_handle_uevents - Udev event handler
+ *
+ * @fd: Currently unused.
+ * @closure: Pointer to the screens ScrnInfoRec, typecast to void.
+ *
+ * Handler that is called when the driver receives an udev event.
+ * It refreshes the RandR information.
+ */
+static void
+vmwgfx_handle_uevents(int fd, void *closure)
+{
+ ScrnInfoPtr scrn = closure;
+ modesettingPtr ms = modesettingPTR(scrn);
+ struct udev_device *dev;
+ rrScrPriv(scrn->pScreen);
+
+ dev = udev_monitor_receive_device(ms->uevent_monitor);
+ if (!dev)
+ return;
+
+ RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
+ udev_device_unref(dev);
+
+ pScrPriv->lastSetTime = currentTime;
+ xf86RandR12TellChanged(scrn->pScreen);
+}
+#endif /* HAVE_LIBUDEV */
+
+/**
+ * vmwgfx_uevent_init - Initialize the udev monitor.
+ *
+ * @scrn: Pointer to the screen's ScrnInfoRec.
+ */
+void vmwgfx_uevent_init(ScrnInfoPtr scrn)
+{
+#ifdef HAVE_LIBUDEV
+ modesettingPtr ms = modesettingPTR(scrn);
+ struct udev *u;
+ struct udev_monitor *mon;
+
+ u = udev_new();
+ if (!u)
+ return;
+ mon = udev_monitor_new_from_netlink(u, "udev");
+ if (!mon) {
+ udev_unref(u);
+ return;
+ }
+
+ if (udev_monitor_filter_add_match_subsystem_devtype(mon,
+ "drm",
+ "drm_minor") < 0 ||
+ udev_monitor_enable_receiving(mon) < 0) {
+ udev_monitor_unref(mon);
+ udev_unref(u);
+ return;
+ }
+
+ ms->uevent_handler = xf86AddGeneralHandler(udev_monitor_get_fd(mon),
+ vmwgfx_handle_uevents,
+ scrn);
+
+ ms->uevent_monitor = mon;
+#endif /* HAVE_LIBUDEV */
+}
+
+/**
+ * vmwgfx_uevent_fini - Take down the udev monitor.
+ *
+ * @scrn: Pointer to the screen's ScrnInfoRec.
+ */
+void vmwgfx_uevent_fini(ScrnInfoPtr scrn)
+{
+#ifdef HAVE_LIBUDEV
+ modesettingPtr ms = modesettingPTR(scrn);
+
+ if (ms->uevent_handler) {
+ struct udev *u = udev_monitor_get_udev(ms->uevent_monitor);
+
+ xf86RemoveGeneralHandler(ms->uevent_handler);
+ udev_monitor_unref(ms->uevent_monitor);
+ udev_unref(u);
+ }
+#endif /* HAVE_LIBUDEV */
+}
/* vim: set sw=4 ts=8 sts=4: */