diff options
-rw-r--r-- | src/tdm_drm.c | 112 | ||||
-rw-r--r-- | src/tdm_drm.h | 11 | ||||
-rw-r--r-- | src/tdm_drm_display.c | 66 |
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; |