On Tue, 18 Jul 2017 14:14:25 +0100
Daniel Stone <dani...@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 <dani...@collabora.com>
> Co-authored-with: Pekka Paalanen <pekka.paala...@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.paala...@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

Attachment: pgpx55Jd9IXzS.pgp
Description: OpenPGP digital signature

_______________________________________________
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to