[PATCH weston v11 03/13] compositor-drm: Add DRM property cache
Pekka Paalanen
ppaalanen at gmail.com
Wed Jul 19 14:43:36 UTC 2017
On Tue, 18 Jul 2017 14:14:25 +0100
Daniel Stone <daniels at collabora.com> wrote:
> Add a cache for DRM property IDs and values, and use it for the two
> connector properties we currently update: DPMS and EDID.
>
> As DRM property ID values are not stable, we need to do a name -> ID
> lookup each run in order to discover the property IDs and enum values to
> use for those properties. Rather than open-coding this, add a property
> cache which we can use across multiple different object types.
>
> This patch takes substantial work from the universal planes support
> originally authored by Pekka Paalanen, though it has been heavily
> reworked.
>
> Signed-off-by: Daniel Stone <daniels at collabora.com>
> Co-authored-with: Pekka Paalanen <pekka.paalanen at collabora.co.uk>
> ---
> libweston/compositor-drm.c | 341 ++++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 289 insertions(+), 52 deletions(-)
Hi,
strictly speaking, the code below is correct. I pointed out some issues
with documentation, and made some questions/suggestion to make even
more robust, but all in all, this looks good.
You are free to slap my
Reviewed-by: Pekka Paalanen <pekka.paalanen at collabora.co.uk>
after dealing with my comments as you see fit.
>
> diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
> index 54b50d9d..e7e8e821 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;
> @@ -311,6 +351,212 @@ 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;
When we have an enum property whose value we cannot interpret, would
this be a place for an assert(0) or an scream in the Weston log? It is
either a bug in Weston, libdrm, or in the kernel, right?
> + }
> +
> + return def;
> +}
> +
> +/**
> + * Cache DRM property values
> + *
> + * Update a per-object-type array of drm_property_info structures, given the
> + * current property values for a particular object.
I think that should instead say:
* Cache DRM property IDs
*
* Update a per-object array of drm_property_info structures, given the
* DRM properties of the object.
The original implementation I had actually cached the current values as
well rather than only property IDs and enum value IDs, but we do not do
that anymore.
> + *
> + * 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).
I believe this should shorten to: "Call this for every DRM object
individually once."
The language about status changes is probably a remnant of the value
caching days.
> + *
> + * 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 above is good, but the below...
> + * The values given in enum_names are searched for, and stored in the
> + * same-indexed field of the map array.
> + *
> + * For example, if the DRM driver exposes 'Foo' as value 7 and 'Bar' as value
> + * 19, passing in enum_names containing 'Foo' and 'Bar' in that order, will
> + * populate map with 7 and 19, in that order.
> + *
> + * This should always be used with static C enums to represent each value, e.g.
> + * WDRM_PLANE_MISC_FOO, WDRM_PLANE_MISC_BAR.
Not sure this hunk is too clear or even matching today's code in more
than spirit. Remove or rewrite? I'd be fine with just removing.
> + *
> + * @param b DRM backend object
> + * @param src DRM property info array to source from
> + * @param dst DRM property info array to copy into
'dst' is actually 'info' in the code, rename?
> + * @param num_infos Number of entries in DRM property info array
and in the src array.
> + * @param props DRM object properties for the object
> + *
> + * @returns Bitmask of populated entries in map
Returns void.
> + */
> +static void
> +drm_property_info_update(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;
> + }
> +
> + info[j].prop_id = props->props[i];
> +
> + if (info[j].num_enum_values == 0) {
Would we want to ensure here that the property is not an enum?
> + 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;
> +
> + if (info[j].enum_values[k].valid)
This should never be true, should it?
If it was, it would seem odd to skip updating it, when we happily
update everything else.
> + continue;
> +
> + 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
> +}
Anyway, drm_property_info_update() code looks correct. Is the name
accurate though? How about drm_property_info_populate()? We do not
expect to call it more than once for each DRM object.
> +
> +/**
> + * 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);
>
> @@ -2036,39 +2282,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);
> @@ -2423,26 +2651,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;
>
> @@ -2733,19 +2955,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) {
> @@ -2768,7 +2988,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 = 1;
> @@ -2795,9 +3014,7 @@ drm_output_enable(struct weston_output *base)
>
> return 0;
>
> -err_free:
> - drmModeFreeProperty(output->dpms_prop);
> -
> +err:
> return -1;
> }
>
> @@ -2822,8 +3039,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);
> }
> @@ -2864,6 +3079,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)
> @@ -2915,9 +3132,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");
> @@ -2946,6 +3169,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_update(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);
> @@ -2987,6 +3221,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)
> @@ -2995,9 +3231,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");
This change seems to be extra, but doesn't hurt.
> } else {
> drmModeFreeConnector(connector);
> }
Thanks,
pq
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 833 bytes
Desc: OpenPGP digital signature
URL: <https://lists.freedesktop.org/archives/wayland-devel/attachments/20170719/6610c824/attachment-0001.sig>
More information about the wayland-devel
mailing list