diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index b707a01fc4829a6db0e8eb699cbe8e008af732a2..83518a9fc16483fada645edc3a1338e1a0d5a46f 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -79,6 +79,44 @@ #define GBM_BO_USE_CURSOR GBM_BO_USE_CURSOR_64X64 #endif +/** + * List of properties attached to a DRM connector + */ +enum wdrm_connector_property { + WDRM_CONNECTOR_EDID = 0, + WDRM_CONNECTOR_DPMS, + WDRM_CONNECTOR__COUNT +}; + +/** + * Represents the values of an enum-type KMS property + */ +struct drm_property_enum_info { + const char *name; /**< name as string (static, not freed) */ + bool valid; /**< true if value is supported; ignore if false */ + uint64_t value; /**< raw value */ +}; + +/** + * Holds information on a DRM property, including its ID and the enum + * values it holds. + * + * DRM properties are allocated dynamically, and maintained as DRM objects + * within the normal object ID space; they thus do not have a stable ID + * to refer to. This includes enum values, which must be referred to by + * integer values, but these are not stable. + * + * drm_property_info allows a cache to be maintained where Weston can use + * enum values internally to refer to properties, with the mapping to DRM + * ID values being maintained internally. + */ +struct drm_property_info { + const char *name; /**< name as string (static, not freed) */ + uint32_t prop_id; /**< KMS property object ID */ + unsigned int num_enum_values; /**< number of enum values */ + struct drm_property_enum_info *enum_values; /**< array of enum values */ +}; + struct drm_backend { struct weston_backend base; struct weston_compositor *compositor; @@ -215,7 +253,9 @@ struct drm_output { uint32_t connector_id; drmModeCrtcPtr original_crtc; struct drm_edid edid; - drmModePropertyPtr dpms_prop; + + /* Holds the properties for the connector */ + struct drm_property_info props_conn[WDRM_CONNECTOR__COUNT]; enum dpms_enum dpms; struct backlight *backlight; @@ -313,6 +353,208 @@ drm_output_pageflip_timer_create(struct drm_output *output) return 0; } +/** + * Get the current value of a KMS property + * + * Given a drmModeObjectGetProperties return, as well as the drm_property_info + * for the target property, return the current value of that property, + * with an optional default. If the property is a KMS enum type, the return + * value will be translated into the appropriate internal enum. + * + * If the property is not present, the default value will be returned. + * + * @param info Internal structure for property to look up + * @param props Raw KMS properties for the target object + * @param def Value to return if property is not found + */ +static uint64_t +drm_property_get_value(struct drm_property_info *info, + drmModeObjectPropertiesPtr props, + uint64_t def) +{ + unsigned int i; + + if (info->prop_id == 0) + return def; + + for (i = 0; i < props->count_props; i++) { + unsigned int j; + + if (props->props[i] != info->prop_id) + continue; + + /* Simple (non-enum) types can return the value directly */ + if (info->num_enum_values == 0) + return props->prop_values[i]; + + /* Map from raw value to enum value */ + for (j = 0; j < info->num_enum_values; j++) { + if (!info->enum_values[j].valid) + continue; + if (info->enum_values[j].value != props->prop_values[i]) + continue; + + return j; + } + + /* We don't have a mapping for this enum; return default. */ + break; + } + + return def; +} + +/** + * Cache DRM property values + * + * Update a per-object array of drm_property_info structures, given the + * DRM properties of the object. + * + * Call this every time an object newly appears (note that only connectors + * can be hotplugged), the first time it is seen, or when its status changes + * in a way which invalidates the potential property values (currently, the + * only case for this is connector hotplug). + * + * This updates the property IDs and enum values within the drm_property_info + * array. + * + * DRM property enum values are dynamic at runtime; the user must query the + * property to find out the desired runtime value for a requested string + * name. Using the 'type' field on planes as an example, there is no single + * hardcoded constant for primary plane types; instead, the property must be + * queried at runtime to find the value associated with the string "Primary". + * + * This helper queries and caches the enum values, to allow us to use a set + * of compile-time-constant enums portably across various implementations. + * The values given in enum_names are searched for, and stored in the + * same-indexed field of the map array. + * + * @param b DRM backend object + * @param src DRM property info array to source from + * @param info DRM property info array to copy into + * @param num_infos Number of entries in the source array + * @param props DRM object properties for the object + */ +static void +drm_property_info_populate(struct drm_backend *b, + const struct drm_property_info *src, + struct drm_property_info *info, + unsigned int num_infos, + drmModeObjectProperties *props) +{ + drmModePropertyRes *prop; + unsigned i, j; + + for (i = 0; i < num_infos; i++) { + unsigned int j; + + info[i].name = src[i].name; + info[i].prop_id = 0; + info[i].num_enum_values = src[i].num_enum_values; + + if (src[i].num_enum_values == 0) + continue; + + info[i].enum_values = + malloc(src[i].num_enum_values * + sizeof(*info[i].enum_values)); + assert(info[i].enum_values); + for (j = 0; j < info[i].num_enum_values; j++) { + info[i].enum_values[j].name = src[i].enum_values[j].name; + info[i].enum_values[j].valid = false; + } + } + + for (i = 0; i < props->count_props; i++) { + unsigned int k; + + prop = drmModeGetProperty(b->drm.fd, props->props[i]); + if (!prop) + continue; + + for (j = 0; j < num_infos; j++) { + if (!strcmp(prop->name, info[j].name)) + break; + } + + /* We don't know/care about this property. */ + if (j == num_infos) { +#ifdef DEBUG + weston_log("DRM debug: unrecognized property %u '%s'\n", + prop->prop_id, prop->name); +#endif + drmModeFreeProperty(prop); + continue; + } + + if (info[j].num_enum_values == 0 && + (prop->flags & DRM_MODE_PROP_ENUM)) { + weston_log("DRM: expected property %s to not be an" + " enum, but it is; ignoring\n", prop->name); + drmModeFreeProperty(prop); + continue; + } + + info[j].prop_id = props->props[i]; + + if (info[j].num_enum_values == 0) { + drmModeFreeProperty(prop); + continue; + } + + if (!(prop->flags & DRM_MODE_PROP_ENUM)) { + weston_log("DRM: expected property %s to be an enum," + " but it is not; ignoring\n", prop->name); + drmModeFreeProperty(prop); + info[j].prop_id = 0; + continue; + } + + for (k = 0; k < info[j].num_enum_values; k++) { + int l; + + for (l = 0; l < prop->count_enums; l++) { + if (!strcmp(prop->enums[l].name, + info[j].enum_values[k].name)) + break; + } + + if (l == prop->count_enums) + continue; + + info[j].enum_values[k].valid = true; + info[j].enum_values[k].value = prop->enums[l].value; + } + + drmModeFreeProperty(prop); + } + +#ifdef DEBUG + for (i = 0; i < num_infos; i++) { + if (info[i].prop_id == 0) + weston_log("DRM warning: property '%s' missing\n", + info[i].name); + } +#endif +} + +/** + * Free DRM property information + * + * Frees all memory associated with a DRM property info array. + * + * @param info DRM property info array + * @param num_props Number of entries in array to free + */ +static void +drm_property_info_free(struct drm_property_info *info, int num_props) +{ + int i; + + for (i = 0; i < num_props; i++) + free(info[i].enum_values); +} + static void drm_output_set_cursor(struct drm_output *output); @@ -2046,39 +2288,21 @@ drm_set_backlight(struct weston_output *output_base, uint32_t value) backlight_set_brightness(output->backlight, new_brightness); } -static drmModePropertyPtr -drm_get_prop(int fd, drmModeConnectorPtr connector, const char *name) -{ - drmModePropertyPtr props; - int i; - - for (i = 0; i < connector->count_props; i++) { - props = drmModeGetProperty(fd, connector->props[i]); - if (!props) - continue; - - if (!strcmp(props->name, name)) - return props; - - drmModeFreeProperty(props); - } - - return NULL; -} - static void drm_set_dpms(struct weston_output *output_base, enum dpms_enum level) { struct drm_output *output = to_drm_output(output_base); struct weston_compositor *ec = output_base->compositor; struct drm_backend *b = to_drm_backend(ec); + struct drm_property_info *prop = + &output->props_conn[WDRM_CONNECTOR_DPMS]; int ret; - if (!output->dpms_prop) + if (!prop->prop_id) return; ret = drmModeConnectorSetProperty(b->drm.fd, output->connector_id, - output->dpms_prop->prop_id, level); + prop->prop_id, level); if (ret) { weston_log("DRM: DPMS: failed property set for %s\n", output->base.name); @@ -2433,26 +2657,20 @@ edid_parse(struct drm_edid *edid, const uint8_t *data, size_t length) } static void -find_and_parse_output_edid(struct drm_backend *b, - struct drm_output *output, - drmModeConnector *connector) +find_and_parse_output_edid(struct drm_backend *b, struct drm_output *output, + drmModeObjectPropertiesPtr props) { drmModePropertyBlobPtr edid_blob = NULL; - drmModePropertyPtr property; - int i; + uint32_t blob_id; int rc; - for (i = 0; i < connector->count_props && !edid_blob; i++) { - property = drmModeGetProperty(b->drm.fd, connector->props[i]); - if (!property) - continue; - if ((property->flags & DRM_MODE_PROP_BLOB) && - !strcmp(property->name, "EDID")) { - edid_blob = drmModeGetPropertyBlob(b->drm.fd, - connector->prop_values[i]); - } - drmModeFreeProperty(property); - } + blob_id = + drm_property_get_value(&output->props_conn[WDRM_CONNECTOR_EDID], + props, 0); + if (!blob_id) + return; + + edid_blob = drmModeGetPropertyBlob(b->drm.fd, blob_id); if (!edid_blob) return; @@ -2743,19 +2961,17 @@ drm_output_enable(struct weston_output *base) struct drm_backend *b = to_drm_backend(base->compositor); struct weston_mode *m; - output->dpms_prop = drm_get_prop(b->drm.fd, output->connector, "DPMS"); - if (b->pageflip_timeout) drm_output_pageflip_timer_create(output); if (b->use_pixman) { if (drm_output_init_pixman(output, b) < 0) { weston_log("Failed to init output pixman state\n"); - goto err_free; + goto err; } } else if (drm_output_init_egl(output, b) < 0) { weston_log("Failed to init output gl state\n"); - goto err_free; + goto err; } if (output->backlight) { @@ -2778,7 +2994,6 @@ drm_output_enable(struct weston_output *base) output->base.subpixel = drm_subpixel_to_wayland(output->connector->subpixel); - find_and_parse_output_edid(b, output, output->connector); if (output->connector->connector_type == DRM_MODE_CONNECTOR_LVDS || output->connector->connector_type == DRM_MODE_CONNECTOR_eDP) output->base.connection_internal = true; @@ -2807,9 +3022,7 @@ drm_output_enable(struct weston_output *base) return 0; -err_free: - drmModeFreeProperty(output->dpms_prop); - +err: return -1; } @@ -2834,8 +3047,6 @@ drm_output_deinit(struct weston_output *base) weston_plane_release(&output->scanout_plane); weston_plane_release(&output->cursor_plane); - drmModeFreeProperty(output->dpms_prop); - /* Turn off hardware cursor */ drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0); } @@ -2876,6 +3087,8 @@ drm_output_destroy(struct weston_output *base) weston_output_destroy(&output->base); + drm_property_info_free(output->props_conn, WDRM_CONNECTOR__COUNT); + drmModeFreeConnector(output->connector); if (output->backlight) @@ -2927,9 +3140,15 @@ create_output_for_connector(struct drm_backend *b, struct udev_device *drm_device) { struct drm_output *output; + drmModeObjectPropertiesPtr props; struct drm_mode *drm_mode; int i; + static const struct drm_property_info connector_props[] = { + [WDRM_CONNECTOR_EDID] = { .name = "EDID" }, + [WDRM_CONNECTOR_DPMS] = { .name = "DPMS" }, + }; + i = find_crtc_for_connector(b, resources, connector); if (i < 0) { weston_log("No usable crtc/encoder pair for connector.\n"); @@ -2958,6 +3177,17 @@ create_output_for_connector(struct drm_backend *b, output->destroy_pending = 0; output->disable_pending = 0; + props = drmModeObjectGetProperties(b->drm.fd, connector->connector_id, + DRM_MODE_OBJECT_CONNECTOR); + if (!props) { + weston_log("failed to get connector properties\n"); + goto err; + } + drm_property_info_populate(b, connector_props, output->props_conn, + WDRM_CONNECTOR__COUNT, props); + find_and_parse_output_edid(b, output, props); + drmModeFreeObjectProperties(props); + weston_output_init(&output->base, b->compositor); wl_list_init(&output->base.mode_list); @@ -2999,6 +3229,8 @@ create_outputs(struct drm_backend *b, struct udev_device *drm_device) b->max_height = resources->max_height; for (i = 0; i < resources->count_connectors; i++) { + int ret; + connector = drmModeGetConnector(b->drm.fd, resources->connectors[i]); if (connector == NULL) @@ -3007,9 +3239,10 @@ create_outputs(struct drm_backend *b, struct udev_device *drm_device) if (connector->connection == DRM_MODE_CONNECTED && (b->connector == 0 || connector->connector_id == b->connector)) { - if (create_output_for_connector(b, resources, - connector, drm_device) < 0) - continue; + ret = create_output_for_connector(b, resources, + connector, drm_device); + if (ret < 0) + weston_log("failed to create new connector\n"); } else { drmModeFreeConnector(connector); }