summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/tdm_drm.c112
-rw-r--r--src/tdm_drm.h11
-rw-r--r--src/tdm_drm_display.c66
3 files changed, 189 insertions, 0 deletions
diff --git a/src/tdm_drm.c b/src/tdm_drm.c
index aaf3b45..8ac23d4 100644
--- a/src/tdm_drm.c
+++ b/src/tdm_drm.c
@@ -64,6 +64,107 @@ _tdm_find_primary_gpu(void)
udev_enumerate_unref(e);
return drm_device;
}
+
+static tdm_error
+_tdm_drm_udev_fd_handler(int fd, tdm_event_loop_mask mask, void *user_data)
+{
+ tdm_drm_data *edata = (tdm_drm_data*)user_data;
+ struct udev_device *dev;
+ const char *hotplug;
+ struct stat s;
+ dev_t udev_devnum;
+ int ret;
+
+ dev = udev_monitor_receive_device(edata->uevent_monitor);
+ if (!dev) {
+ TDM_ERR("couldn't receive device");
+ return TDM_ERROR_OPERATION_FAILED;
+ }
+
+ udev_devnum = udev_device_get_devnum(dev);
+
+ ret = fstat(edata->drm_fd, &s);
+ if (ret == -1) {
+ TDM_ERR("fstat failed");
+ return TDM_ERROR_OPERATION_FAILED;
+ }
+
+ hotplug = udev_device_get_property_value(dev, "HOTPLUG");
+
+ if (memcmp(&s.st_rdev, &udev_devnum, sizeof (dev_t)) == 0 &&
+ hotplug && atoi(hotplug) == 1)
+ {
+ TDM_INFO("HotPlug");
+ tdm_drm_display_update_output_status(edata);
+ }
+
+ udev_device_unref(dev);
+
+ return TDM_ERROR_NONE;
+}
+
+static void
+_tdm_drm_udev_init(tdm_drm_data *edata)
+{
+ struct udev *u = NULL;
+ struct udev_monitor *mon = NULL;
+
+ u = udev_new();
+ if (!u) {
+ TDM_ERR("couldn't create udev");
+ goto failed;
+ }
+
+ mon = udev_monitor_new_from_netlink(u, "udev");
+ if (!mon) {
+ TDM_ERR("couldn't create udev monitor");
+ goto failed;
+ }
+
+ if (udev_monitor_filter_add_match_subsystem_devtype(mon, "drm", "drm_minor") > 0 ||
+ udev_monitor_enable_receiving(mon) < 0) {
+ TDM_ERR("add match subsystem failed");
+ goto failed;
+ }
+
+ edata->uevent_source =
+ tdm_event_loop_add_fd_handler(edata->dpy, udev_monitor_get_fd(mon),
+ TDM_EVENT_LOOP_READABLE,
+ _tdm_drm_udev_fd_handler,
+ edata, NULL);
+ if (!edata->uevent_source) {
+ TDM_ERR("couldn't create udev event source");
+ goto failed;
+ }
+
+ edata->uevent_monitor = mon;
+
+ TDM_INFO("hotplug monitor created");
+
+ return;
+failed:
+ if (mon)
+ udev_monitor_unref(mon);
+ if (u)
+ udev_unref(u);
+}
+
+static void
+_tdm_drm_udev_deinit(tdm_drm_data *edata)
+{
+ if (edata->uevent_source) {
+ tdm_event_loop_source_remove(edata->uevent_source);
+ edata->uevent_source = NULL;
+ }
+
+ if (edata->uevent_monitor) {
+ struct udev *u = udev_monitor_get_udev(edata->uevent_monitor);
+ udev_monitor_unref(edata->uevent_monitor);
+ udev_unref(u);
+ edata->uevent_monitor = NULL;
+ TDM_INFO("hotplug monitor destroyed");
+ }
+}
#endif
static int
@@ -111,6 +212,10 @@ tdm_drm_deinit(tdm_backend_data *bdata)
TDM_INFO("deinit");
+#ifdef HAVE_UDEV
+ _tdm_drm_udev_deinit(drm_data);
+#endif
+
tdm_drm_display_destroy_output_list(drm_data);
if (drm_data->plane_res)
@@ -181,6 +286,9 @@ tdm_drm_init(tdm_display *dpy, tdm_error *error)
drm_func_output.output_get_dpms = drm_output_get_dpms;
drm_func_output.output_set_mode = drm_output_set_mode;
drm_func_output.output_get_mode = drm_output_get_mode;
+#ifdef HAVE_UDEV
+ drm_func_output.output_set_status_handler = drm_output_set_status_handler;
+#endif
memset(&drm_func_layer, 0, sizeof(drm_func_layer));
drm_func_layer.layer_get_capability = drm_layer_get_capability;
@@ -237,6 +345,10 @@ tdm_drm_init(tdm_display *dpy, tdm_error *error)
/* To share the drm master fd with other modules in display server side. */
tdm_helper_set_fd("TDM_DRM_MASTER_FD", drm_data->drm_fd);
+#ifdef HAVE_UDEV
+ _tdm_drm_udev_init(drm_data);
+#endif
+
#if LIBDRM_MAJOR_VERSION >= 2 && LIBDRM_MINOR_VERSION >= 4 && LIBDRM_MICRO_VERSION >= 47
if (drmSetClientCap(drm_data->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) < 0) {
TDM_WRN("Set DRM_CLIENT_CAP_UNIVERSAL_PLANES failed");
diff --git a/src/tdm_drm.h b/src/tdm_drm.h
index 952f959..333c670 100644
--- a/src/tdm_drm.h
+++ b/src/tdm_drm.h
@@ -21,6 +21,10 @@
#include <tdm_log.h>
#include <tdm_list.h>
+#if HAVE_UDEV
+#include <libudev.h>
+#endif
+
/* drm backend functions (display) */
tdm_error drm_display_get_capabilitiy(tdm_backend_data *bdata, tdm_caps_display *caps);
tdm_error drm_display_get_pp_capability(tdm_backend_data *bdata, tdm_caps_pp *caps);
@@ -40,6 +44,7 @@ tdm_error drm_output_set_dpms(tdm_output *output, tdm_output_dpms dpms_value)
tdm_error drm_output_get_dpms(tdm_output *output, tdm_output_dpms *dpms_value);
tdm_error drm_output_set_mode(tdm_output *output, const tdm_output_mode *mode);
tdm_error drm_output_get_mode(tdm_output *output, const tdm_output_mode **mode);
+tdm_error drm_output_set_status_handler(tdm_output *output, tdm_output_status_handler func, void *user_data);
tdm_error drm_layer_get_capability(tdm_layer *layer, tdm_caps_layer *caps);
tdm_error drm_layer_set_property(tdm_layer *layer, unsigned int id, tdm_value value);
tdm_error drm_layer_get_property(tdm_layer *layer, unsigned int id, tdm_value *value);
@@ -99,6 +104,11 @@ typedef struct _tdm_drm_data
int has_universal_plane;
+#if HAVE_UDEV
+ struct udev_monitor *uevent_monitor;
+ tdm_event_loop_source *uevent_source;
+#endif
+
drmModeResPtr mode_res;
drmModePlaneResPtr plane_res;
@@ -109,6 +119,7 @@ typedef struct _tdm_drm_data
uint32_t tdm_drm_format_to_drm_format(tbm_format format);
tbm_format tdm_drm_format_to_tbm_format(uint32_t format);
+void tdm_drm_display_update_output_status(tdm_drm_data *drm_data);
tdm_error tdm_drm_display_create_output_list(tdm_drm_data *drm_data);
void tdm_drm_display_destroy_output_list(tdm_drm_data *drm_data);
tdm_error tdm_drm_display_create_layer_list(tdm_drm_data *drm_data);
diff --git a/src/tdm_drm_display.c b/src/tdm_drm_display.c
index 3ec906c..c8cab14 100644
--- a/src/tdm_drm_display.c
+++ b/src/tdm_drm_display.c
@@ -57,6 +57,8 @@ struct _tdm_drm_output_data {
tdm_output_commit_handler commit_func;
tdm_output_conn_status status;
+ tdm_output_status_handler status_func;
+ void *status_user_data;
int mode_changed;
const tdm_output_mode *current_mode;
@@ -641,6 +643,54 @@ tdm_drm_display_destroy_output_list(tdm_drm_data *drm_data)
}
}
+static tdm_error
+_tdm_drm_output_update_status(tdm_drm_output_data *output_data,
+ tdm_output_conn_status status)
+{
+ RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+
+ if (output_data->status == status)
+ return TDM_ERROR_NONE;
+
+ output_data->status = status;
+
+ if (output_data->status_func)
+ output_data->status_func(output_data, status,
+ output_data->status_user_data);
+
+ return TDM_ERROR_NONE;
+}
+
+void
+tdm_drm_display_update_output_status(tdm_drm_data *drm_data)
+{
+ tdm_drm_output_data *output_data = NULL;
+
+ if (LIST_IS_EMPTY(&drm_data->output_list))
+ return;
+
+ LIST_FOR_EACH_ENTRY(output_data, &drm_data->output_list, link) {
+ drmModeConnectorPtr connector;
+ tdm_output_conn_status new_status;
+
+ connector = drmModeGetConnector(drm_data->drm_fd,
+ output_data->connector_id);
+ if (!connector) {
+ TDM_ERR("no connector: %d", output_data->connector_id);
+ continue;
+ }
+
+ if (connector->connection == DRM_MODE_CONNECTED)
+ new_status = TDM_OUTPUT_CONN_STATUS_CONNECTED;
+ else
+ new_status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
+
+ _tdm_drm_output_update_status(output_data, new_status);
+
+ drmModeFreeConnector(connector);
+ }
+}
+
tdm_error
tdm_drm_display_create_output_list(tdm_drm_data *drm_data)
{
@@ -1314,6 +1364,22 @@ drm_output_get_mode(tdm_output *output, const tdm_output_mode **mode)
}
tdm_error
+drm_output_set_status_handler(tdm_output *output,
+ tdm_output_status_handler func,
+ void *user_data)
+{
+ tdm_drm_output_data *output_data = output;
+
+ RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+ RETURN_VAL_IF_FAIL(func, TDM_ERROR_INVALID_PARAMETER);
+
+ output_data->status_func = func;
+ output_data->status_user_data = user_data;
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
drm_layer_get_capability(tdm_layer *layer, tdm_caps_layer *caps)
{
tdm_drm_layer_data *layer_data = layer;