diff --git a/src/Makefile.am b/src/Makefile.am index 45d4e7d69..c98578416 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -457,6 +457,8 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES += \ backends/native/meta-monitor-manager-kms.h \ backends/native/meta-launcher.c \ backends/native/meta-launcher.h \ + backends/native/meta-output-kms.c \ + backends/native/meta-output-kms.h \ backends/native/meta-renderer-native.c \ backends/native/meta-renderer-native.h \ backends/native/meta-stage-native.c \ diff --git a/src/backends/native/gen-default-modes.py b/src/backends/native/gen-default-modes.py index aa731ee10..75c4e242a 100644 --- a/src/backends/native/gen-default-modes.py +++ b/src/backends/native/gen-default-modes.py @@ -51,7 +51,7 @@ common_resolutions = [ output_lines = [ "/* Generated by gen-default-modes.py */\n", - "const drmModeModeInfo meta_default_drm_mode_infos[] = {", + "static const drmModeModeInfo meta_default_drm_mode_infos[] = {", ] def sync_flags(hsync, vsync): diff --git a/src/backends/native/meta-default-modes.h b/src/backends/native/meta-default-modes.h index 155bf9fd8..b111204f2 100644 --- a/src/backends/native/meta-default-modes.h +++ b/src/backends/native/meta-default-modes.h @@ -1,6 +1,6 @@ /* Generated by gen-default-modes.py */ -const drmModeModeInfo meta_default_drm_mode_infos[] = { +static const drmModeModeInfo meta_default_drm_mode_infos[] = { { 38250, 800, 832, 912, 1024, 0, 600, 603, 607, 624, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "800x600_60.00" }, { 63500, 1024, 1072, 1176, 1328, 0, 768, 771, 775, 798, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "1024x768_60.00" }, { 81750, 1152, 1216, 1336, 1520, 0, 864, 867, 871, 897, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "1152x864_60.00" }, diff --git a/src/backends/native/meta-monitor-manager-kms.c b/src/backends/native/meta-monitor-manager-kms.c index bc4df6ab6..8b0f85d49 100644 --- a/src/backends/native/meta-monitor-manager-kms.c +++ b/src/backends/native/meta-monitor-manager-kms.c @@ -29,6 +29,7 @@ #include "meta-output.h" #include "meta-backend-private.h" #include "meta-renderer-native.h" +#include "meta-output-kms.h" #include #include @@ -39,8 +40,6 @@ #include #include #include -#include -#include #include #include @@ -51,33 +50,6 @@ #define ALL_TRANSFORMS (META_MONITOR_TRANSFORM_FLIPPED_270 + 1) #define ALL_TRANSFORMS_MASK ((1 << ALL_TRANSFORMS) - 1) -#define SYNC_TOLERANCE 0.01 /* 1 percent */ - -typedef struct -{ - drmModeConnector *connector; - - unsigned n_encoders; - drmModeEncoderPtr *encoders; - drmModeEncoderPtr current_encoder; - - /* - * Bitmasks of encoder position in the resources array (used during clone - * setup). - */ - uint32_t encoder_mask; - uint32_t enc_clone_mask; - - uint32_t dpms_prop_id; - uint32_t edid_blob_id; - uint32_t tile_blob_id; - - int suggested_x; - int suggested_y; - uint32_t hotplug_mode_update; - - gboolean has_scaling; -} MetaOutputKms; typedef struct { @@ -124,6 +96,12 @@ struct _MetaMonitorManagerKmsClass G_DEFINE_TYPE (MetaMonitorManagerKms, meta_monitor_manager_kms, META_TYPE_MONITOR_MANAGER); +int +meta_monitor_manager_kms_get_fd (MetaMonitorManagerKms *manager_kms) +{ + return manager_kms->fd; +} + static void free_resources (MetaMonitorManagerKms *manager_kms) { @@ -144,54 +122,6 @@ compare_outputs (gconstpointer one, return strcmp (o_one->name, o_two->name); } -static char * -make_output_name (drmModeConnector *connector) -{ - static const char * const connector_type_names[] = { - "None", - "VGA", - "DVI-I", - "DVI-D", - "DVI-A", - "Composite", - "SVIDEO", - "LVDS", - "Component", - "DIN", - "DP", - "HDMI", - "HDMI-B", - "TV", - "eDP", - "Virtual", - "DSI", - }; - - if (connector->connector_type < G_N_ELEMENTS (connector_type_names)) - return g_strdup_printf ("%s-%d", - connector_type_names[connector->connector_type], - connector->connector_type_id); - else - return g_strdup_printf ("Unknown%d-%d", - connector->connector_type, - connector->connector_type_id); -} - -static void -meta_output_destroy_notify (MetaOutput *output) -{ - MetaOutputKms *output_kms; - unsigned i; - - output_kms = output->driver_private; - - for (i = 0; i < output_kms->n_encoders; i++) - drmModeFreeEncoder (output_kms->encoders[i]); - g_free (output_kms->encoders); - - g_slice_free (MetaOutputKms, output_kms); -} - static void meta_monitor_mode_destroy_notify (MetaCrtcMode *mode) { @@ -247,44 +177,6 @@ drm_mode_hash (gconstpointer ptr) return hash; } -static void -find_connector_properties (MetaMonitorManagerKms *manager_kms, - MetaOutputKms *output_kms) -{ - int i; - - output_kms->hotplug_mode_update = 0; - output_kms->suggested_x = -1; - output_kms->suggested_y = -1; - for (i = 0; i < output_kms->connector->count_props; i++) - { - drmModePropertyPtr prop = drmModeGetProperty (manager_kms->fd, output_kms->connector->props[i]); - if (!prop) - continue; - - if ((prop->flags & DRM_MODE_PROP_ENUM) && strcmp (prop->name, "DPMS") == 0) - output_kms->dpms_prop_id = prop->prop_id; - else if ((prop->flags & DRM_MODE_PROP_BLOB) && strcmp (prop->name, "EDID") == 0) - output_kms->edid_blob_id = output_kms->connector->prop_values[i]; - else if ((prop->flags & DRM_MODE_PROP_BLOB) && - strcmp (prop->name, "TILE") == 0) - output_kms->tile_blob_id = output_kms->connector->prop_values[i]; - else if ((prop->flags & DRM_MODE_PROP_RANGE) && - strcmp (prop->name, "suggested X") == 0) - output_kms->suggested_x = output_kms->connector->prop_values[i]; - else if ((prop->flags & DRM_MODE_PROP_RANGE) && - strcmp (prop->name, "suggested Y") == 0) - output_kms->suggested_y = output_kms->connector->prop_values[i]; - else if ((prop->flags & DRM_MODE_PROP_RANGE) && - strcmp (prop->name, "hotplug_mode_update") == 0) - output_kms->hotplug_mode_update = output_kms->connector->prop_values[i]; - else if (strcmp (prop->name, "scaling mode") == 0) - output_kms->has_scaling = TRUE; - - drmModeFreeProperty (prop); - } -} - static void find_crtc_properties (MetaMonitorManagerKms *manager_kms, MetaCrtc *meta_crtc) @@ -316,99 +208,11 @@ find_crtc_properties (MetaMonitorManagerKms *manager_kms, } } -static drmModePropertyBlobPtr -read_edid_blob (MetaMonitorManagerKms *manager_kms, - uint32_t edid_blob_id, - GError **error) -{ - drmModePropertyBlobPtr edid_blob = NULL; - - edid_blob = drmModeGetPropertyBlob (manager_kms->fd, edid_blob_id); - if (!edid_blob) - { - g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), - "%s", strerror (errno)); - return NULL; - } - - return edid_blob; -} - -static GBytes * -read_output_edid (MetaMonitorManagerKms *manager_kms, - MetaOutput *output, - GError **error) -{ - MetaOutputKms *output_kms = output->driver_private; - drmModePropertyBlobPtr edid_blob; - - g_assert (output_kms->edid_blob_id != 0); - - edid_blob = read_edid_blob (manager_kms, output_kms->edid_blob_id, error); - if (!edid_blob) - return NULL; - - if (edid_blob->length == 0) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "EDID blob was empty"); - drmModeFreePropertyBlob (edid_blob); - return NULL; - } - - return g_bytes_new_with_free_func (edid_blob->data, edid_blob->length, - (GDestroyNotify) drmModeFreePropertyBlob, - edid_blob); -} - -static gboolean -output_get_tile_info (MetaMonitorManagerKms *manager_kms, - MetaOutput *output) -{ - MetaOutputKms *output_kms = output->driver_private; - drmModePropertyBlobPtr tile_blob = NULL; - int ret; - - if (output_kms->tile_blob_id == 0) - return FALSE; - - tile_blob = drmModeGetPropertyBlob (manager_kms->fd, output_kms->tile_blob_id); - if (!tile_blob) - { - meta_warning ("Failed to read TILE of output %s: %s\n", output->name, strerror(errno)); - return FALSE; - } - - if (tile_blob->length > 0) - { - ret = sscanf ((char *)tile_blob->data, "%d:%d:%d:%d:%d:%d:%d:%d", - &output->tile_info.group_id, - &output->tile_info.flags, - &output->tile_info.max_h_tiles, - &output->tile_info.max_v_tiles, - &output->tile_info.loc_h_tile, - &output->tile_info.loc_v_tile, - &output->tile_info.tile_w, - &output->tile_info.tile_h); - drmModeFreePropertyBlob (tile_blob); - - if (ret != 8) - { - meta_warning ("Couldn't understand output tile property blob\n"); - return FALSE; - } - return TRUE; - } - else - { - drmModeFreePropertyBlob (tile_blob); - return FALSE; - } -} - -static MetaCrtcMode * -mode_from_drm_mode (MetaMonitorManager *manager, - const drmModeModeInfo *drm_mode) +MetaCrtcMode * +meta_monitor_manager_kms_get_mode_from_drm_mode (MetaMonitorManagerKms *manager_kms, + const drmModeModeInfo *drm_mode) { + MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms); GList *l; for (l = manager->modes; l; l = l->next) @@ -423,8 +227,8 @@ mode_from_drm_mode (MetaMonitorManager *manager, return NULL; } -static float -drm_mode_vrefresh (const drmModeModeInfo *mode) +float +meta_calculate_drm_mode_refresh_rate (const drmModeModeInfo *mode) { float refresh = 0.0; @@ -453,30 +257,13 @@ create_mode (const drmModeModeInfo *drm_mode, mode->width = drm_mode->hdisplay; mode->height = drm_mode->vdisplay; mode->flags = drm_mode->flags; - mode->refresh_rate = drm_mode_vrefresh (drm_mode); + mode->refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode); mode->driver_private = g_slice_dup (drmModeModeInfo, drm_mode); - mode->driver_notify = (GDestroyNotify)meta_monitor_mode_destroy_notify; + mode->driver_notify = (GDestroyNotify) meta_monitor_mode_destroy_notify; return mode; } -static int -compare_modes (const void *one, - const void *two) -{ - MetaCrtcMode *a = *(MetaCrtcMode **) one; - MetaCrtcMode *b = *(MetaCrtcMode **) two; - - if (a->width != b->width) - return a->width > b->width ? -1 : 1; - if (a->height != b->height) - return a->height > b->height ? -1 : 1; - if (a->refresh_rate != b->refresh_rate) - return a->refresh_rate > b->refresh_rate ? -1 : 1; - - return g_strcmp0 (b->name, a->name); -} - static MetaOutput * find_output_by_id (GList *outputs, glong id) @@ -627,49 +414,6 @@ init_crtc_rotations (MetaMonitorManager *manager, drmModeFreePlaneResources (planes); } -static void -add_common_modes (MetaMonitorManager *manager, - MetaOutput *output) -{ - const drmModeModeInfo *drm_mode; - GPtrArray *array; - unsigned i; - unsigned max_hdisplay = 0; - unsigned max_vdisplay = 0; - float max_vrefresh = 0.0; - - for (i = 0; i < output->n_modes; i++) - { - drm_mode = output->modes[i]->driver_private; - max_hdisplay = MAX (max_hdisplay, drm_mode->hdisplay); - max_vdisplay = MAX (max_vdisplay, drm_mode->vdisplay); - max_vrefresh = MAX (max_vrefresh, drm_mode_vrefresh (drm_mode)); - } - - max_vrefresh = MAX (max_vrefresh, 60.0); - max_vrefresh *= (1 + SYNC_TOLERANCE); - - array = g_ptr_array_new (); - for (i = 0; i < G_N_ELEMENTS (meta_default_drm_mode_infos); i++) - { - drm_mode = &meta_default_drm_mode_infos[i]; - if (drm_mode->hdisplay > max_hdisplay || - drm_mode->vdisplay > max_vdisplay || - drm_mode_vrefresh (drm_mode) > max_vrefresh) - continue; - - g_ptr_array_add (array, mode_from_drm_mode (manager, drm_mode)); - } - - output->modes = g_renew (MetaCrtcMode *, output->modes, - output->n_modes + array->len); - memcpy (output->modes + output->n_modes, array->pdata, - array->len * sizeof (MetaCrtcMode *)); - output->n_modes += array->len; - - g_ptr_array_free (array, TRUE); -} - static MetaCrtc * create_crtc (MetaMonitorManager *manager, drmModeCrtc *drm_crtc) @@ -710,268 +454,31 @@ create_crtc (MetaMonitorManager *manager, return crtc; } -static MetaOutput * -create_output (MetaMonitorManager *manager, - drmModeConnector *connector, - MetaOutput *old_output) -{ - MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); - MetaOutput *output; - MetaOutputKms *output_kms; - GArray *crtcs; - GBytes *edid; - GList *l; - unsigned int i; - unsigned int crtc_mask; - - output = g_object_new (META_TYPE_OUTPUT, NULL); - - output_kms = g_slice_new0 (MetaOutputKms); - output->driver_private = output_kms; - output->driver_notify = (GDestroyNotify)meta_output_destroy_notify; - - output->monitor_manager = manager; - output->winsys_id = connector->connector_id; - output->name = make_output_name (connector); - output->width_mm = connector->mmWidth; - output->height_mm = connector->mmHeight; - - switch (connector->subpixel) - { - case DRM_MODE_SUBPIXEL_NONE: - output->subpixel_order = COGL_SUBPIXEL_ORDER_NONE; - break; - case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: - output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB; - break; - case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: - output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR; - break; - case DRM_MODE_SUBPIXEL_VERTICAL_RGB: - output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB; - break; - case DRM_MODE_SUBPIXEL_VERTICAL_BGR: - output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR; - break; - case DRM_MODE_SUBPIXEL_UNKNOWN: - default: - output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN; - break; - } - - output->preferred_mode = NULL; - output->n_modes = connector->count_modes; - output->modes = g_new0 (MetaCrtcMode *, output->n_modes); - for (i = 0; i < output->n_modes; i++) { - output->modes[i] = mode_from_drm_mode (manager, &connector->modes[i]); - if (connector->modes[i].type & DRM_MODE_TYPE_PREFERRED) - output->preferred_mode = output->modes[i]; - } - - if (!output->preferred_mode) - output->preferred_mode = output->modes[0]; - - output_kms->connector = connector; - find_connector_properties (manager_kms, output_kms); - - /* FIXME: MSC feature bit? */ - /* Presume that if the output supports scaling, then we have - * a panel fitter capable of adjusting any mode to suit. - */ - if (output_kms->has_scaling) - add_common_modes (manager, output); - - qsort (output->modes, output->n_modes, sizeof (MetaCrtcMode *), compare_modes); - - output_kms->n_encoders = connector->count_encoders; - output_kms->encoders = g_new0 (drmModeEncoderPtr, output_kms->n_encoders); - - crtc_mask = ~(unsigned int) 0; - for (i = 0; i < output_kms->n_encoders; i++) - { - output_kms->encoders[i] = drmModeGetEncoder (manager_kms->fd, - connector->encoders[i]); - if (!output_kms->encoders[i]) - continue; - - /* We only list CRTCs as supported if they are supported by all encoders - for this connectors. - - This is what xf86-video-modesetting does (see drmmode_output_init()) - */ - crtc_mask &= output_kms->encoders[i]->possible_crtcs; - - if (output_kms->encoders[i]->encoder_id == connector->encoder_id) - output_kms->current_encoder = output_kms->encoders[i]; - } - - crtcs = g_array_new (FALSE, FALSE, sizeof (MetaCrtc*)); - - for (l = manager->crtcs, i = 0; l; l = l->next, i++) - { - if (crtc_mask & (1 << i)) - { - MetaCrtc *crtc = l->data; - - g_array_append_val (crtcs, crtc); - } - } - - output->n_possible_crtcs = crtcs->len; - output->possible_crtcs = (void*)g_array_free (crtcs, FALSE); - - if (output_kms->current_encoder && output_kms->current_encoder->crtc_id != 0) - { - for (l = manager->crtcs; l; l = l->next) - { - MetaCrtc *crtc = l->data; - - if (crtc->crtc_id == output_kms->current_encoder->crtc_id) - { - output->crtc = crtc; - break; - } - } - } - else - { - output->crtc = NULL; - } - - if (old_output) - { - output->is_primary = old_output->is_primary; - output->is_presentation = old_output->is_presentation; - } - else - { - output->is_primary = FALSE; - output->is_presentation = FALSE; - } - - output->suggested_x = output_kms->suggested_x; - output->suggested_y = output_kms->suggested_y; - output->hotplug_mode_update = output_kms->hotplug_mode_update; - - if (output_kms->edid_blob_id != 0) - { - GError *error = NULL; - - edid = read_output_edid (manager_kms, output, &error); - if (!edid) - { - g_warning ("Failed to read EDID blob from %s: %s", - output->name, error->message); - g_error_free (error); - } - } - else - { - edid = NULL; - } - - meta_output_parse_edid (output, edid); - g_bytes_unref (edid); - - /* MetaConnectorType matches DRM's connector types */ - output->connector_type = (MetaConnectorType) connector->connector_type; - - output_get_tile_info (manager_kms, output); - - /* FIXME: backlight is a very driver specific thing unfortunately, - every DDX does its own thing, and the dumb KMS API does not include it. - - For example, xf86-video-intel has a list of paths to probe in /sys/class/backlight - (one for each major HW maker, and then some). - We can't do the same because we're not root. - It might be best to leave backlight out of the story and rely on the setuid - helper in gnome-settings-daemon. - */ - output->backlight_min = 0; - output->backlight_max = 0; - output->backlight = -1; - - return output; -} - static void -detect_and_setup_output_clones (MetaMonitorManager *manager, - drmModeRes *resources) +setup_output_clones (MetaMonitorManager *manager) { - MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); - drmModeEncoder **encoders; - unsigned int i, n_encoders; GList *l; - n_encoders = (unsigned int) resources->count_encoders; - encoders = g_new (drmModeEncoder *, n_encoders); - for (i = 0; i < n_encoders; i++) - encoders[i] = drmModeGetEncoder (manager_kms->fd, resources->encoders[i]); - - /* - * Setup encoder position mask and encoder clone mask. - */ for (l = manager->outputs; l; l = l->next) { MetaOutput *output = l->data; - MetaOutputKms *output_kms = output->driver_private; - unsigned int j; - - output_kms->enc_clone_mask = 0xff; - output_kms->encoder_mask = 0; - - for (j = 0; j < output_kms->n_encoders; j++) - { - unsigned int k; - - for (k = 0; k < n_encoders; k++) - { - if (output_kms->encoders[j] && encoders[k] && - output_kms->encoders[j]->encoder_id == encoders[k]->encoder_id) - { - output_kms->encoder_mask |= (1 << k); - break; - } - } - - output_kms->enc_clone_mask &= output_kms->encoders[j]->possible_clones; - } - } - - for (i = 0; i < (unsigned)resources->count_encoders; i++) - drmModeFreeEncoder (encoders[i]); - g_free (encoders); - - /* - * Setup MetaOutput <-> MetaOutput clone associations. - */ - for (l = manager->outputs; l; l = l->next) - { - MetaOutput *output = l->data; - MetaOutputKms *output_kms = output->driver_private; GList *k; - if (output_kms->enc_clone_mask == 0) - continue; - for (k = manager->outputs; k; k = k->next) { - MetaOutput *clone = k->data; - MetaOutputKms *clone_kms = clone->driver_private; + MetaOutput *other_output = k->data; - if (clone == output) + if (other_output == output) continue; - if (clone_kms->encoder_mask == 0) - continue; - - if (clone_kms->encoder_mask == output_kms->enc_clone_mask) + if (meta_output_kms_can_clone (output, other_output)) { output->n_possible_clones++; output->possible_clones = g_renew (MetaOutput *, output->possible_clones, output->n_possible_clones); - output->possible_clones[output->n_possible_clones - 1] = clone; + output->possible_clones[output->n_possible_clones - 1] = + other_output; } } } @@ -1080,7 +587,7 @@ init_crtcs (MetaMonitorManager *manager, static void init_outputs (MetaMonitorManager *manager, - drmModeRes *resources) + MetaKmsResources *resources) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); GList *old_outputs; @@ -1102,7 +609,7 @@ init_outputs (MetaMonitorManager *manager, MetaOutput *old_output; old_output = find_output_by_id (old_outputs, connector->connector_id); - output = create_output (manager, connector, old_output); + output = meta_create_kms_output (manager, connector, resources, old_output); manager->outputs = g_list_prepend (manager->outputs, output); } } @@ -1111,19 +618,47 @@ init_outputs (MetaMonitorManager *manager, /* Sort the outputs for easier handling in MetaMonitorConfig */ manager->outputs = g_list_sort (manager->outputs, compare_outputs); - detect_and_setup_output_clones (manager, resources); + setup_output_clones (manager); +} + +static void +meta_kms_resources_init (MetaKmsResources *resources, + int fd) +{ + drmModeRes *drm_resources; + unsigned int i; + + drm_resources = drmModeGetResources (fd); + resources->resources = drm_resources; + + resources->n_encoders = (unsigned int) drm_resources->count_encoders; + resources->encoders = g_new (drmModeEncoder *, resources->n_encoders); + for (i = 0; i < resources->n_encoders; i++) + resources->encoders[i] = drmModeGetEncoder (fd, drm_resources->encoders[i]); +} + +static void +meta_kms_resources_release (MetaKmsResources *resources) +{ + unsigned int i; + + for (i = 0; i < resources->n_encoders; i++) + drmModeFreeEncoder (resources->encoders[i]); + g_free (resources->encoders); + + drmModeFreeResources (resources->resources); } static void meta_monitor_manager_kms_read_current (MetaMonitorManager *manager) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); - drmModeRes *resources; + MetaKmsResources resources; - resources = drmModeGetResources (manager_kms->fd); + meta_kms_resources_init (&resources, manager_kms->fd); - manager_kms->max_buffer_width = resources->max_width; - manager_kms->max_buffer_height = resources->max_height; + manager_kms->max_buffer_width = resources.resources->max_width; + manager_kms->max_buffer_height = resources.resources->max_height; manager->power_save_mode = META_POWER_SAVE_ON; @@ -1133,43 +668,25 @@ meta_monitor_manager_kms_read_current (MetaMonitorManager *manager) are freed by the platform-independent layer. */ free_resources (manager_kms); - init_connectors (manager, resources); - init_modes (manager, resources); - init_crtcs (manager, resources); - init_outputs (manager, resources); + init_connectors (manager, resources.resources); + init_modes (manager, resources.resources); + init_crtcs (manager, resources.resources); + init_outputs (manager, &resources); - drmModeFreeResources (resources); + meta_kms_resources_release (&resources); } static GBytes * meta_monitor_manager_kms_read_edid (MetaMonitorManager *manager, MetaOutput *output) { - MetaOutputKms *output_kms = output->driver_private; - MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); - GError *error = NULL; - GBytes *edid; - - if (output_kms->edid_blob_id == 0) - return NULL; - - edid = read_output_edid (manager_kms, output, &error); - if (!edid) - { - g_warning ("Failed to read EDID from '%s': %s", - output->name, error->message); - g_error_free (error); - return NULL; - } - - return edid; + return meta_output_kms_read_edid (output); } static void meta_monitor_manager_kms_set_power_save_mode (MetaMonitorManager *manager, MetaPowerSave mode) { - MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); uint64_t state; GList *l; @@ -1193,18 +710,8 @@ meta_monitor_manager_kms_set_power_save_mode (MetaMonitorManager *manager, for (l = manager->outputs; l; l = l->next) { MetaOutput *output = l->data; - MetaOutputKms *output_kms = output->driver_private; - if (output_kms->dpms_prop_id != 0) - { - int ok = drmModeObjectSetProperty (manager_kms->fd, output->winsys_id, - DRM_MODE_OBJECT_CONNECTOR, - output_kms->dpms_prop_id, state); - - if (ok < 0) - meta_warning ("Failed to set power save mode for output %s: %s\n", - output->name, strerror (errno)); - } + meta_output_kms_set_power_save_mode (output, state); } } diff --git a/src/backends/native/meta-monitor-manager-kms.h b/src/backends/native/meta-monitor-manager-kms.h index 37eabb3c2..24d7c4e36 100644 --- a/src/backends/native/meta-monitor-manager-kms.h +++ b/src/backends/native/meta-monitor-manager-kms.h @@ -23,6 +23,9 @@ #ifndef META_MONITOR_MANAGER_KMS_H #define META_MONITOR_MANAGER_KMS_H +#include +#include + #include "meta-monitor-manager-private.h" #define META_TYPE_MONITOR_MANAGER_KMS (meta_monitor_manager_kms_get_type ()) @@ -30,8 +33,20 @@ G_DECLARE_FINAL_TYPE (MetaMonitorManagerKms, meta_monitor_manager_kms, META, MONITOR_MANAGER_KMS, MetaMonitorManager) +typedef struct _MetaKmsResources +{ + drmModeRes *resources; + drmModeEncoder **encoders; + unsigned int n_encoders; +} MetaKmsResources; + typedef void (*MetaKmsFlipCallback) (void *user_data); +int meta_monitor_manager_kms_get_fd (MetaMonitorManagerKms *manager_kms); + +MetaCrtcMode * meta_monitor_manager_kms_get_mode_from_drm_mode (MetaMonitorManagerKms *manager_kms, + const drmModeModeInfo *drm_mode); + gboolean meta_monitor_manager_kms_apply_crtc_mode (MetaMonitorManagerKms *manager_kms, MetaCrtc *crtc, int x, @@ -55,4 +70,6 @@ void meta_monitor_manager_kms_pause (MetaMonitorManagerKms *manager_kms); void meta_monitor_manager_kms_resume (MetaMonitorManagerKms *manager_kms); +float meta_calculate_drm_mode_refresh_rate (const drmModeModeInfo *mode); + #endif /* META_MONITOR_MANAGER_KMS_H */ diff --git a/src/backends/native/meta-output-kms.c b/src/backends/native/meta-output-kms.c new file mode 100644 index 000000000..6181c8c92 --- /dev/null +++ b/src/backends/native/meta-output-kms.c @@ -0,0 +1,616 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (C) 2013-2017 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "config.h" + +#include "backends/native/meta-output-kms.h" + +#include +#include +#include + +#include "backends/meta-crtc.h" +#include "backends/native/meta-default-modes.h" +#include "backends/native/meta-monitor-manager-kms.h" + +#define SYNC_TOLERANCE 0.01 /* 1 percent */ + +typedef struct _MetaOutputKms +{ + MetaOutput parent; + + drmModeConnector *connector; + + unsigned int n_encoders; + drmModeEncoderPtr *encoders; + drmModeEncoderPtr current_encoder; + + /* + * Bitmasks of encoder position in the resources array (used during clone + * setup). + */ + uint32_t encoder_mask; + uint32_t enc_clone_mask; + + uint32_t dpms_prop_id; + uint32_t edid_blob_id; + uint32_t tile_blob_id; + + int suggested_x; + int suggested_y; + uint32_t hotplug_mode_update; + + gboolean has_scaling; +} MetaOutputKms; + +void +meta_output_kms_set_power_save_mode (MetaOutput *output, + uint64_t state) +{ + MetaOutputKms *output_kms = output->driver_private; + MetaMonitorManager *monitor_manager = + meta_output_get_monitor_manager (output); + MetaMonitorManagerKms *monitor_manager_kms = + META_MONITOR_MANAGER_KMS (monitor_manager); + + if (output_kms->dpms_prop_id != 0) + { + int fd; + + fd = meta_monitor_manager_kms_get_fd (monitor_manager_kms); + if (drmModeObjectSetProperty (fd, output->winsys_id, + DRM_MODE_OBJECT_CONNECTOR, + output_kms->dpms_prop_id, state) < 0) + g_warning ("Failed to set power save mode for output %s: %s", + output->name, strerror (errno)); + } +} + +gboolean +meta_output_kms_can_clone (MetaOutput *output, + MetaOutput *other_output) +{ + MetaOutputKms *output_kms = output->driver_private; + MetaOutputKms *other_output_kms = other_output->driver_private; + + if (output_kms->enc_clone_mask == 0 || + other_output_kms->enc_clone_mask == 0) + return FALSE; + + if (output_kms->encoder_mask != other_output_kms->enc_clone_mask) + return FALSE; + + return TRUE; +} + +static drmModePropertyBlobPtr +read_edid_blob (MetaMonitorManagerKms *monitor_manager_kms, + uint32_t edid_blob_id, + GError **error) +{ + int fd; + drmModePropertyBlobPtr edid_blob = NULL; + + fd = meta_monitor_manager_kms_get_fd (monitor_manager_kms); + edid_blob = drmModeGetPropertyBlob (fd, edid_blob_id); + if (!edid_blob) + { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), + "%s", strerror (errno)); + return NULL; + } + + return edid_blob; +} + +static GBytes * +read_output_edid (MetaMonitorManagerKms *monitor_manager_kms, + MetaOutput *output, + GError **error) +{ + MetaOutputKms *output_kms = output->driver_private; + drmModePropertyBlobPtr edid_blob; + + g_assert (output_kms->edid_blob_id != 0); + + edid_blob = read_edid_blob (monitor_manager_kms, output_kms->edid_blob_id, error); + if (!edid_blob) + return NULL; + + if (edid_blob->length == 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "EDID blob was empty"); + drmModeFreePropertyBlob (edid_blob); + return NULL; + } + + return g_bytes_new_with_free_func (edid_blob->data, edid_blob->length, + (GDestroyNotify) drmModeFreePropertyBlob, + edid_blob); +} + +static gboolean +output_get_tile_info (MetaMonitorManagerKms *monitor_manager_kms, + MetaOutput *output) +{ + MetaOutputKms *output_kms = output->driver_private; + int fd; + drmModePropertyBlobPtr tile_blob = NULL; + + if (output_kms->tile_blob_id == 0) + return FALSE; + + fd = meta_monitor_manager_kms_get_fd (monitor_manager_kms); + tile_blob = drmModeGetPropertyBlob (fd, output_kms->tile_blob_id); + if (!tile_blob) + { + g_warning ("Failed to read TILE of output %s: %s", + output->name, strerror (errno)); + return FALSE; + } + + if (tile_blob->length > 0) + { + int ret; + + ret = sscanf ((char *)tile_blob->data, "%d:%d:%d:%d:%d:%d:%d:%d", + &output->tile_info.group_id, + &output->tile_info.flags, + &output->tile_info.max_h_tiles, + &output->tile_info.max_v_tiles, + &output->tile_info.loc_h_tile, + &output->tile_info.loc_v_tile, + &output->tile_info.tile_w, + &output->tile_info.tile_h); + drmModeFreePropertyBlob (tile_blob); + + if (ret != 8) + { + g_warning ("Couldn't understand output tile property blob"); + return FALSE; + } + return TRUE; + } + else + { + drmModeFreePropertyBlob (tile_blob); + return FALSE; + } +} + +GBytes * +meta_output_kms_read_edid (MetaOutput *output) +{ + MetaOutputKms *output_kms = output->driver_private; + MetaMonitorManager *monitor_manager = + meta_output_get_monitor_manager (output); + MetaMonitorManagerKms *monitor_manager_kms = + META_MONITOR_MANAGER_KMS (monitor_manager); + GError *error = NULL; + GBytes *edid; + + if (output_kms->edid_blob_id == 0) + return NULL; + + edid = read_output_edid (monitor_manager_kms, output, &error); + if (!edid) + { + g_warning ("Failed to read EDID from '%s': %s", + output->name, error->message); + g_error_free (error); + return NULL; + } + + return edid; +} + +static void +find_connector_properties (MetaMonitorManagerKms *monitor_manager_kms, + MetaOutputKms *output_kms) +{ + drmModeConnector *connector = output_kms->connector; + int fd; + int i; + + fd = meta_monitor_manager_kms_get_fd (monitor_manager_kms); + + output_kms->hotplug_mode_update = 0; + output_kms->suggested_x = -1; + output_kms->suggested_y = -1; + + for (i = 0; i < connector->count_props; i++) + { + drmModePropertyPtr prop = drmModeGetProperty (fd, connector->props[i]); + if (!prop) + continue; + + if ((prop->flags & DRM_MODE_PROP_ENUM) && + strcmp (prop->name, "DPMS") == 0) + output_kms->dpms_prop_id = prop->prop_id; + else if ((prop->flags & DRM_MODE_PROP_BLOB) && + strcmp (prop->name, "EDID") == 0) + output_kms->edid_blob_id = connector->prop_values[i]; + else if ((prop->flags & DRM_MODE_PROP_BLOB) && + strcmp (prop->name, "TILE") == 0) + output_kms->tile_blob_id = connector->prop_values[i]; + else if ((prop->flags & DRM_MODE_PROP_RANGE) && + strcmp (prop->name, "suggested X") == 0) + output_kms->suggested_x = connector->prop_values[i]; + else if ((prop->flags & DRM_MODE_PROP_RANGE) && + strcmp (prop->name, "suggested Y") == 0) + output_kms->suggested_y = connector->prop_values[i]; + else if ((prop->flags & DRM_MODE_PROP_RANGE) && + strcmp (prop->name, "hotplug_mode_update") == 0) + output_kms->hotplug_mode_update = connector->prop_values[i]; + else if (strcmp (prop->name, "scaling mode") == 0) + output_kms->has_scaling = TRUE; + + drmModeFreeProperty (prop); + } +} + +static char * +make_output_name (drmModeConnector *connector) +{ + static const char * const connector_type_names[] = { + "None", + "VGA", + "DVI-I", + "DVI-D", + "DVI-A", + "Composite", + "SVIDEO", + "LVDS", + "Component", + "DIN", + "DP", + "HDMI", + "HDMI-B", + "TV", + "eDP", + "Virtual", + "DSI", + }; + + if (connector->connector_type < G_N_ELEMENTS (connector_type_names)) + return g_strdup_printf ("%s-%d", + connector_type_names[connector->connector_type], + connector->connector_type_id); + else + return g_strdup_printf ("Unknown%d-%d", + connector->connector_type, + connector->connector_type_id); +} + +static void +meta_output_destroy_notify (MetaOutput *output) +{ + MetaOutputKms *output_kms; + unsigned i; + + output_kms = output->driver_private; + + for (i = 0; i < output_kms->n_encoders; i++) + drmModeFreeEncoder (output_kms->encoders[i]); + g_free (output_kms->encoders); + + g_slice_free (MetaOutputKms, output_kms); +} + +static void +add_common_modes (MetaOutput *output, + MetaMonitorManagerKms *monitor_manager_kms) +{ + GPtrArray *array; + unsigned i; + unsigned max_hdisplay = 0; + unsigned max_vdisplay = 0; + float max_refresh_rate = 0.0; + + for (i = 0; i < output->n_modes; i++) + { + const drmModeModeInfo *drm_mode; + float refresh_rate; + + drm_mode = output->modes[i]->driver_private; + refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode); + max_hdisplay = MAX (max_hdisplay, drm_mode->hdisplay); + max_vdisplay = MAX (max_vdisplay, drm_mode->vdisplay); + max_refresh_rate = MAX (max_refresh_rate, refresh_rate); + } + + max_refresh_rate = MAX (max_refresh_rate, 60.0); + max_refresh_rate *= (1 + SYNC_TOLERANCE); + + array = g_ptr_array_new (); + for (i = 0; i < G_N_ELEMENTS (meta_default_drm_mode_infos); i++) + { + const drmModeModeInfo *drm_mode; + float refresh_rate; + MetaCrtcMode *crtc_mode; + + drm_mode = &meta_default_drm_mode_infos[i]; + refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode); + if (drm_mode->hdisplay > max_hdisplay || + drm_mode->vdisplay > max_vdisplay || + refresh_rate > max_refresh_rate) + continue; + + crtc_mode = meta_monitor_manager_kms_get_mode_from_drm_mode (monitor_manager_kms, + drm_mode); + g_ptr_array_add (array, crtc_mode); + } + + output->modes = g_renew (MetaCrtcMode *, output->modes, + output->n_modes + array->len); + memcpy (output->modes + output->n_modes, array->pdata, + array->len * sizeof (MetaCrtcMode *)); + output->n_modes += array->len; + + g_ptr_array_free (array, TRUE); +} + +static int +compare_modes (const void *one, + const void *two) +{ + MetaCrtcMode *a = *(MetaCrtcMode **) one; + MetaCrtcMode *b = *(MetaCrtcMode **) two; + + if (a->width != b->width) + return a->width > b->width ? -1 : 1; + if (a->height != b->height) + return a->height > b->height ? -1 : 1; + if (a->refresh_rate != b->refresh_rate) + return a->refresh_rate > b->refresh_rate ? -1 : 1; + + return g_strcmp0 (b->name, a->name); +} + +static void +init_output_modes (MetaOutput *output, + MetaMonitorManagerKms *monitor_manager_kms) +{ + MetaOutputKms *output_kms = output->driver_private; + unsigned int i; + + output->preferred_mode = NULL; + output->n_modes = output_kms->connector->count_modes; + output->modes = g_new0 (MetaCrtcMode *, output->n_modes); + for (i = 0; i < output->n_modes; i++) + { + drmModeModeInfo *drm_mode; + MetaCrtcMode *crtc_mode; + + drm_mode = &output_kms->connector->modes[i]; + crtc_mode = + meta_monitor_manager_kms_get_mode_from_drm_mode (monitor_manager_kms, + drm_mode); + output->modes[i] = crtc_mode; + if (output_kms->connector->modes[i].type & DRM_MODE_TYPE_PREFERRED) + output->preferred_mode = output->modes[i]; + } + + if (!output->preferred_mode) + output->preferred_mode = output->modes[0]; +} + +MetaOutput * +meta_create_kms_output (MetaMonitorManager *monitor_manager, + drmModeConnector *connector, + MetaKmsResources *resources, + MetaOutput *old_output) +{ + MetaMonitorManagerKms *monitor_manager_kms = + META_MONITOR_MANAGER_KMS (monitor_manager); + MetaOutput *output; + MetaOutputKms *output_kms; + GArray *crtcs; + GBytes *edid; + GList *l; + unsigned int i; + unsigned int crtc_mask; + int fd; + + output = g_object_new (META_TYPE_OUTPUT, NULL); + + output_kms = g_slice_new0 (MetaOutputKms); + output->driver_private = output_kms; + output->driver_notify = (GDestroyNotify) meta_output_destroy_notify; + + output->monitor_manager = monitor_manager; + output->winsys_id = connector->connector_id; + output->name = make_output_name (connector); + output->width_mm = connector->mmWidth; + output->height_mm = connector->mmHeight; + + switch (connector->subpixel) + { + case DRM_MODE_SUBPIXEL_NONE: + output->subpixel_order = COGL_SUBPIXEL_ORDER_NONE; + break; + case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: + output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB; + break; + case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: + output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR; + break; + case DRM_MODE_SUBPIXEL_VERTICAL_RGB: + output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB; + break; + case DRM_MODE_SUBPIXEL_VERTICAL_BGR: + output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR; + break; + case DRM_MODE_SUBPIXEL_UNKNOWN: + default: + output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN; + break; + } + + output_kms->connector = connector; + find_connector_properties (monitor_manager_kms, output_kms); + + init_output_modes (output, monitor_manager_kms); + + /* FIXME: MSC feature bit? */ + /* Presume that if the output supports scaling, then we have + * a panel fitter capable of adjusting any mode to suit. + */ + if (output_kms->has_scaling) + add_common_modes (output, monitor_manager_kms); + + qsort (output->modes, output->n_modes, + sizeof (MetaCrtcMode *), compare_modes); + + output_kms->n_encoders = connector->count_encoders; + output_kms->encoders = g_new0 (drmModeEncoderPtr, output_kms->n_encoders); + + fd = meta_monitor_manager_kms_get_fd (monitor_manager_kms); + + crtc_mask = ~(unsigned int) 0; + for (i = 0; i < output_kms->n_encoders; i++) + { + output_kms->encoders[i] = drmModeGetEncoder (fd, connector->encoders[i]); + if (!output_kms->encoders[i]) + continue; + + /* We only list CRTCs as supported if they are supported by all encoders + for this connectors. + + This is what xf86-video-modesetting does (see drmmode_output_init()) + */ + crtc_mask &= output_kms->encoders[i]->possible_crtcs; + + if (output_kms->encoders[i]->encoder_id == connector->encoder_id) + output_kms->current_encoder = output_kms->encoders[i]; + } + + crtcs = g_array_new (FALSE, FALSE, sizeof (MetaCrtc*)); + + for (l = monitor_manager->crtcs, i = 0; l; l = l->next, i++) + { + if (crtc_mask & (1 << i)) + { + MetaCrtc *crtc = l->data; + + g_array_append_val (crtcs, crtc); + } + } + + output->n_possible_crtcs = crtcs->len; + output->possible_crtcs = (void*)g_array_free (crtcs, FALSE); + + if (output_kms->current_encoder && output_kms->current_encoder->crtc_id != 0) + { + for (l = monitor_manager->crtcs; l; l = l->next) + { + MetaCrtc *crtc = l->data; + + if (crtc->crtc_id == output_kms->current_encoder->crtc_id) + { + output->crtc = crtc; + break; + } + } + } + else + { + output->crtc = NULL; + } + + if (old_output) + { + output->is_primary = old_output->is_primary; + output->is_presentation = old_output->is_presentation; + } + else + { + output->is_primary = FALSE; + output->is_presentation = FALSE; + } + + output->suggested_x = output_kms->suggested_x; + output->suggested_y = output_kms->suggested_y; + output->hotplug_mode_update = output_kms->hotplug_mode_update; + + if (output_kms->edid_blob_id != 0) + { + GError *error = NULL; + + edid = read_output_edid (monitor_manager_kms, output, &error); + if (!edid) + { + g_warning ("Failed to read EDID blob from %s: %s", + output->name, error->message); + g_error_free (error); + } + } + else + { + edid = NULL; + } + + meta_output_parse_edid (output, edid); + g_bytes_unref (edid); + + /* MetaConnectorType matches DRM's connector types */ + output->connector_type = (MetaConnectorType) connector->connector_type; + + output_get_tile_info (monitor_manager_kms, output); + + /* FIXME: backlight is a very driver specific thing unfortunately, + every DDX does its own thing, and the dumb KMS API does not include it. + + For example, xf86-video-intel has a list of paths to probe in /sys/class/backlight + (one for each major HW maker, and then some). + We can't do the same because we're not root. + It might be best to leave backlight out of the story and rely on the setuid + helper in gnome-settings-daemon. + */ + output->backlight_min = 0; + output->backlight_max = 0; + output->backlight = -1; + + output_kms->enc_clone_mask = 0xff; + output_kms->encoder_mask = 0; + + for (i = 0; i < output_kms->n_encoders; i++) + { + drmModeEncoder *output_encoder = output_kms->encoders[i]; + unsigned int j; + + for (j = 0; j < resources->n_encoders; j++) + { + drmModeEncoder *encoder = resources->encoders[j]; + + if (output_encoder && encoder && + output_encoder->encoder_id == encoder->encoder_id) + { + output_kms->encoder_mask |= (1 << j); + break; + } + } + + output_kms->enc_clone_mask &= output_encoder->possible_clones; + } + + return output; +} diff --git a/src/backends/native/meta-output-kms.h b/src/backends/native/meta-output-kms.h new file mode 100644 index 000000000..61846a983 --- /dev/null +++ b/src/backends/native/meta-output-kms.h @@ -0,0 +1,41 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (C) 2017 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef META_OUTPUT_KMS_H +#define META_OUTPUT_KMS_H + +#include "backends/meta-output.h" +#include "backends/native/meta-monitor-manager-kms.h" + +void meta_output_kms_set_power_save_mode (MetaOutput *output, + uint64_t state); + +gboolean meta_output_kms_can_clone (MetaOutput *output, + MetaOutput *other_output); + +GBytes * meta_output_kms_read_edid (MetaOutput *output); + +MetaOutput * meta_create_kms_output (MetaMonitorManager *monitor_manager, + drmModeConnector *connector, + MetaKmsResources *resources, + MetaOutput *old_output); + +#endif /* META_OUTPUT_KMS_H */