backend-drm: Add support for content-protection

Currently drm-layer supports HDCP1.4 using connector property:
Content Protection. This property if available for a platform, can be
read and set for requesting content-protection.
Also, the patch series [1] adds HDCP2.2 support in drm, and patch [2]
adds support to send udev events for change in connector properties,
made by the kernel.

This patch adds these HDCP connector properties in weston, and exposes
the content-protection support to the client for drm-backend.

It adds the enums to represent 'Content Protection' and 'Content Type'
connector properties exposed by drm layer. It adds a member
'protection' in drm_output_state, to store the desired protection
from the weston_output in the drm-backend output-repaint cycle. This
is then used to write the HDCP connector properties for the drm_heads
attached to the drm_output.

The kernel sends uevents to the user-space for any change made by it
in the "Content Protection" connector property. No event is sent in
case of change in the property made by the user-space.
It means, when there is a change of the property value from "DESIRED"
to "ENABLE" i.e. successful authentication by the kernel, a uevent
will be generated, but in case of userspace requesting for disabling
the protection by writing "UNDESIRED" into the property, no uevent
will be generated.

This patch also adds support for handling new udev events for HDCP
connector property changes. Any such change, triggers change in the
weston_head's current_protection.

[1] https://patchwork.freedesktop.org/series/57233/#rev7
[2] https://patchwork.freedesktop.org/patch/303903/?series=57233&rev=7

Signed-off-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
This commit is contained in:
Ankit Nautiyal 2019-05-14 18:36:08 +05:30
parent 18e0cf85d6
commit a344fe3245
5 changed files with 327 additions and 2 deletions

View File

@ -185,9 +185,24 @@ enum wdrm_connector_property {
WDRM_CONNECTOR_DPMS,
WDRM_CONNECTOR_CRTC_ID,
WDRM_CONNECTOR_NON_DESKTOP,
WDRM_CONNECTOR_CONTENT_PROTECTION,
WDRM_CONNECTOR_HDCP_CONTENT_TYPE,
WDRM_CONNECTOR__COUNT
};
enum wdrm_content_protection_state {
WDRM_CONTENT_PROTECTION_UNDESIRED = 0,
WDRM_CONTENT_PROTECTION_DESIRED,
WDRM_CONTENT_PROTECTION_ENABLED,
WDRM_CONTENT_PROTECTION__COUNT
};
enum wdrm_hdcp_content_type {
WDRM_HDCP_CONTENT_TYPE0 = 0,
WDRM_HDCP_CONTENT_TYPE1,
WDRM_HDCP_CONTENT_TYPE__COUNT
};
enum wdrm_dpms_state {
WDRM_DPMS_STATE_OFF = 0,
WDRM_DPMS_STATE_ON,
@ -340,6 +355,7 @@ struct drm_output_state {
struct drm_output *output;
struct wl_list link;
enum dpms_enum dpms;
enum weston_hdcp_protection protection;
struct wl_list plane_list;
};
@ -570,6 +586,8 @@ drm_property_info_free(struct drm_property_info *info, int num_props);
extern struct drm_property_enum_info plane_type_enums[];
extern const struct drm_property_info plane_props[];
extern struct drm_property_enum_info dpms_state_enums[];
extern struct drm_property_enum_info content_protection_enums[];
extern struct drm_property_enum_info hdcp_content_type_enums[];
extern const struct drm_property_info connector_props[];
extern const struct drm_property_info crtc_props[];

View File

@ -55,6 +55,7 @@
#include "drm-internal.h"
#include "shared/helpers.h"
#include "shared/timespec-util.h"
#include "shared/string-helpers.h"
#include "renderer-gl/gl-renderer.h"
#include "weston-egl-ext.h"
#include "pixman-renderer.h"
@ -213,6 +214,8 @@ drm_output_get_disable_state(struct drm_pending_state *pending_state,
DRM_OUTPUT_STATE_CLEAR_PLANES);
output_state->dpms = WESTON_DPMS_OFF;
output_state->protection = WESTON_HDCP_DISABLE;
return output_state;
}
@ -425,6 +428,11 @@ drm_output_repaint(struct weston_output *output_base,
DRM_OUTPUT_STATE_CLEAR_PLANES);
state->dpms = WESTON_DPMS_ON;
if (output_base->allow_protection)
state->protection = output_base->desired_protection;
else
state->protection = WESTON_HDCP_DISABLE;
drm_output_render(state, damage);
scanout_state = drm_output_state_get_plane(state,
output->scanout_plane);
@ -2124,6 +2132,85 @@ drm_backend_update_unused_outputs(struct drm_backend *b, drmModeRes *resources)
}
}
/*
* This function converts the protection status from drm values to
* weston_hdcp_protection status. The drm values as read from the connector
* properties "Content Protection" and "HDCP Content Type" need to be converted
* to appropriate weston values, that can be sent to a client application.
*/
static int
get_weston_protection_from_drm(enum wdrm_content_protection_state protection,
enum wdrm_hdcp_content_type type,
enum weston_hdcp_protection *weston_protection)
{
if (protection >= WDRM_CONTENT_PROTECTION__COUNT)
return -1;
if (protection == WDRM_CONTENT_PROTECTION_DESIRED ||
protection == WDRM_CONTENT_PROTECTION_UNDESIRED) {
*weston_protection = WESTON_HDCP_DISABLE;
return 0;
}
if (type >= WDRM_HDCP_CONTENT_TYPE__COUNT)
return -1;
if (type == WDRM_HDCP_CONTENT_TYPE0) {
*weston_protection = WESTON_HDCP_ENABLE_TYPE_0;
return 0;
}
if (type == WDRM_HDCP_CONTENT_TYPE1) {
*weston_protection = WESTON_HDCP_ENABLE_TYPE_1;
return 0;
}
return -1;
}
/**
* Get current content-protection status for a given head.
*
* @param head drm_head, whose protection is to be retrieved
* @param props drm property object of the connector, related to the head
* @return protection status in case of success, -1 otherwise
*/
static enum weston_hdcp_protection
drm_head_get_current_protection(struct drm_head *head,
drmModeObjectProperties *props)
{
struct drm_property_info *info;
enum wdrm_content_protection_state protection;
enum wdrm_hdcp_content_type type;
enum weston_hdcp_protection weston_hdcp = WESTON_HDCP_DISABLE;
info = &head->props_conn[WDRM_CONNECTOR_CONTENT_PROTECTION];
protection = drm_property_get_value(info, props,
WDRM_CONTENT_PROTECTION__COUNT);
if (protection == WDRM_CONTENT_PROTECTION__COUNT)
return WESTON_HDCP_DISABLE;
info = &head->props_conn[WDRM_CONNECTOR_HDCP_CONTENT_TYPE];
type = drm_property_get_value(info, props,
WDRM_HDCP_CONTENT_TYPE__COUNT);
/*
* In case of platforms supporting HDCP1.4, only property
* 'Content Protection' is exposed and not the 'HDCP Content Type'
* for such cases HDCP Type 0 should be considered as the content-type.
*/
if (type == WDRM_HDCP_CONTENT_TYPE__COUNT)
type = WDRM_HDCP_CONTENT_TYPE0;
if (get_weston_protection_from_drm(protection, type,
&weston_hdcp) == -1) {
weston_log("Invalid drm protection:%d type:%d, for head:%s connector-id:%d\n",
protection, type, head->base.name,
head->connector_id);
return WESTON_HDCP_DISABLE;
}
return weston_hdcp;
}
/** Replace connector data and monitor information
*
* @param head The head to update.
@ -2161,6 +2248,9 @@ drm_head_assign_connector_info(struct drm_head *head,
head->props_conn,
WDRM_CONNECTOR__COUNT, props);
update_head_from_connector(head, props);
weston_head_set_content_protection_status(&head->base,
drm_head_get_current_protection(head, props));
drmModeFreeObjectProperties(props);
return 0;
@ -2427,6 +2517,58 @@ drm_backend_update_heads(struct drm_backend *b, struct udev_device *drm_device)
drmModeFreeResources(resources);
}
static enum wdrm_connector_property
drm_head_find_property_by_id(struct drm_head *head, uint32_t property_id)
{
int i;
enum wdrm_connector_property prop = WDRM_CONNECTOR__COUNT;
if (!head || !property_id)
return WDRM_CONNECTOR__COUNT;
for (i = 0; i < WDRM_CONNECTOR__COUNT; i++)
if (head->props_conn[i].prop_id == property_id) {
prop = (enum wdrm_connector_property) i;
break;
}
return prop;
}
static void
drm_backend_update_conn_props(struct drm_backend *b,
uint32_t connector_id,
uint32_t property_id)
{
struct drm_head *head;
enum wdrm_connector_property conn_prop;
drmModeObjectProperties *props;
head = drm_head_find_by_connector(b, connector_id);
if (!head) {
weston_log("DRM: failed to find head for connector id: %d.\n",
connector_id);
return;
}
conn_prop = drm_head_find_property_by_id(head, property_id);
if (conn_prop >= WDRM_CONNECTOR__COUNT)
return;
props = drmModeObjectGetProperties(b->drm.fd,
connector_id,
DRM_MODE_OBJECT_CONNECTOR);
if (!props) {
weston_log("Error: failed to get connector '%s' properties\n",
head->base.name);
return;
}
if (conn_prop == WDRM_CONNECTOR_CONTENT_PROTECTION) {
weston_head_set_content_protection_status(&head->base,
drm_head_get_current_protection(head, props));
}
drmModeFreeObjectProperties(props);
}
static int
udev_event_is_hotplug(struct drm_backend *b, struct udev_device *device)
{
@ -2444,16 +2586,46 @@ udev_event_is_hotplug(struct drm_backend *b, struct udev_device *device)
return strcmp(val, "1") == 0;
}
static int
udev_event_is_conn_prop_change(struct drm_backend *b,
struct udev_device *device,
uint32_t *connector_id,
uint32_t *property_id)
{
const char *val;
int id;
val = udev_device_get_property_value(device, "CONNECTOR");
if (!val || !safe_strtoint(val, &id))
return 0;
else
*connector_id = id;
val = udev_device_get_property_value(device, "PROPERTY");
if (!val || !safe_strtoint(val, &id))
return 0;
else
*property_id = id;
return 1;
}
static int
udev_drm_event(int fd, uint32_t mask, void *data)
{
struct drm_backend *b = data;
struct udev_device *event;
uint32_t conn_id, prop_id;
event = udev_monitor_receive_device(b->udev_monitor);
if (udev_event_is_hotplug(b, event))
drm_backend_update_heads(b, event);
if (udev_event_is_hotplug(b, event)) {
if (udev_event_is_conn_prop_change(b, event, &conn_id, &prop_id))
drm_backend_update_conn_props(b, conn_id, prop_id);
else
drm_backend_update_heads(b, event);
}
udev_device_unref(event);
@ -3367,6 +3539,11 @@ drm_backend_create(struct weston_compositor *compositor,
" synchronization support failed.\n");
}
if (b->atomic_modeset)
if (weston_compositor_enable_content_protection(compositor) < 0)
weston_log("Error: initializing content-protection "
"support failed.\n");
ret = weston_plugin_api_register(compositor, WESTON_DRM_OUTPUT_API_NAME,
&api, sizeof(api));

View File

@ -94,6 +94,27 @@ struct drm_property_enum_info dpms_state_enums[] = {
},
};
struct drm_property_enum_info content_protection_enums[] = {
[WDRM_CONTENT_PROTECTION_UNDESIRED] = {
.name = "Undesired",
},
[WDRM_CONTENT_PROTECTION_DESIRED] = {
.name = "Desired",
},
[WDRM_CONTENT_PROTECTION_ENABLED] = {
.name = "Enabled",
},
};
struct drm_property_enum_info hdcp_content_type_enums[] = {
[WDRM_HDCP_CONTENT_TYPE0] = {
.name = "HDCP Type0",
},
[WDRM_HDCP_CONTENT_TYPE1] = {
.name = "HDCP Type1",
},
};
const struct drm_property_info connector_props[] = {
[WDRM_CONNECTOR_EDID] = { .name = "EDID" },
[WDRM_CONNECTOR_DPMS] = {
@ -103,6 +124,16 @@ const struct drm_property_info connector_props[] = {
},
[WDRM_CONNECTOR_CRTC_ID] = { .name = "CRTC_ID", },
[WDRM_CONNECTOR_NON_DESKTOP] = { .name = "non-desktop", },
[WDRM_CONNECTOR_CONTENT_PROTECTION] = {
.name = "Content Protection",
.enum_values = content_protection_enums,
.num_enum_values = WDRM_CONTENT_PROTECTION__COUNT,
},
[WDRM_CONNECTOR_HDCP_CONTENT_TYPE] = {
.name = "HDCP Content Type",
.enum_values = hdcp_content_type_enums,
.num_enum_values = WDRM_HDCP_CONTENT_TYPE__COUNT,
},
};
const struct drm_property_info crtc_props[] = {
@ -463,6 +494,7 @@ drm_output_assign_state(struct drm_output_state *state,
struct drm_output *output = state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_plane_state *plane_state;
struct drm_head *head;
assert(!output->state_last);
@ -482,6 +514,12 @@ drm_output_assign_state(struct drm_output_state *state,
output->atomic_complete_pending = 1;
}
if (b->atomic_modeset &&
state->protection == WESTON_HDCP_DISABLE)
wl_list_for_each(head, &output->base.head_list, base.output_link)
weston_head_set_content_protection_status(&head->base,
WESTON_HDCP_DISABLE);
/* Replace state_cur on each affected plane with the new state, being
* careful to dispose of orphaned (but only orphaned) previous state.
* If the previous state is not orphaned (still has an output_state
@ -793,6 +831,84 @@ plane_add_damage(drmModeAtomicReq *req, struct drm_backend *backend,
return 0;
}
static bool
drm_head_has_prop(struct drm_head *head,
enum wdrm_connector_property prop)
{
if (head && head->props_conn[prop].prop_id != 0)
return true;
return false;
}
/*
* This function converts the protection requests from weston_hdcp_protection
* corresponding drm values. These values can be set in "Content Protection"
* & "HDCP Content Type" connector properties.
*/
static void
get_drm_protection_from_weston(enum weston_hdcp_protection weston_protection,
enum wdrm_content_protection_state *drm_protection,
enum wdrm_hdcp_content_type *drm_cp_type)
{
switch (weston_protection) {
case WESTON_HDCP_DISABLE:
*drm_protection = WDRM_CONTENT_PROTECTION_UNDESIRED;
*drm_cp_type = WDRM_HDCP_CONTENT_TYPE0;
break;
case WESTON_HDCP_ENABLE_TYPE_0:
*drm_protection = WDRM_CONTENT_PROTECTION_DESIRED;
*drm_cp_type = WDRM_HDCP_CONTENT_TYPE0;
break;
case WESTON_HDCP_ENABLE_TYPE_1:
*drm_protection = WDRM_CONTENT_PROTECTION_DESIRED;
*drm_cp_type = WDRM_HDCP_CONTENT_TYPE1;
break;
default:
assert(0 && "bad weston_hdcp_protection");
}
}
static void
drm_head_set_hdcp_property(struct drm_head *head,
enum weston_hdcp_protection protection,
drmModeAtomicReq *req)
{
int ret;
enum wdrm_content_protection_state drm_protection;
enum wdrm_hdcp_content_type drm_cp_type;
struct drm_property_enum_info *enum_info;
uint64_t prop_val;
get_drm_protection_from_weston(protection, &drm_protection,
&drm_cp_type);
if (!drm_head_has_prop(head, WDRM_CONNECTOR_CONTENT_PROTECTION))
return;
/*
* Content-type property is not exposed for platforms not supporting
* HDCP2.2, therefore, type-1 cannot be supported. The type-0 content
* still can be supported if the content-protection property is exposed.
*/
if (!drm_head_has_prop(head, WDRM_CONNECTOR_HDCP_CONTENT_TYPE) &&
drm_cp_type != WDRM_HDCP_CONTENT_TYPE0)
return;
enum_info = head->props_conn[WDRM_CONNECTOR_CONTENT_PROTECTION].enum_values;
prop_val = enum_info[drm_protection].value;
ret = connector_add_prop(req, head, WDRM_CONNECTOR_CONTENT_PROTECTION,
prop_val);
assert(ret == 0);
enum_info = head->props_conn[WDRM_CONNECTOR_HDCP_CONTENT_TYPE].enum_values;
prop_val = enum_info[drm_cp_type].value;
ret = connector_add_prop(req, head, WDRM_CONNECTOR_HDCP_CONTENT_TYPE,
prop_val);
assert(ret == 0);
}
static int
drm_output_apply_state_atomic(struct drm_output_state *state,
drmModeAtomicReq *req,
@ -839,6 +955,9 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
ret |= connector_add_prop(req, head, WDRM_CONNECTOR_CRTC_ID, 0);
}
wl_list_for_each(head, &output->base.head_list, base.output_link)
drm_head_set_hdcp_property(head, state->protection, req);
if (ret != 0) {
weston_log("couldn't set atomic CRTC/connector state\n");
return ret;

View File

@ -293,6 +293,7 @@ drm_output_state_alloc(struct drm_output *output,
assert(state);
state->output = output;
state->dpms = WESTON_DPMS_OFF;
state->protection = WESTON_HDCP_DISABLE;
state->pending_state = pending_state;
if (pending_state)
wl_list_insert(&pending_state->output_list, &state->link);

View File

@ -572,6 +572,16 @@ drm_output_propose_state(struct weston_output *output_base,
force_renderer = true;
}
/* In case of enforced mode of content-protection do not
* assign planes for a protected surface on an unsecured output.
*/
if (ev->surface->protection_mode == WESTON_SURFACE_PROTECTION_MODE_ENFORCED &&
ev->surface->desired_protection > output_base->current_protection) {
drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
"(enforced protection mode on unsecured output)\n", ev);
force_renderer = true;
}
/* We do not control the stacking order of overlay planes;
* the scanout plane is strictly stacked bottom and the cursor
* plane top, but the ordering of overlay planes with respect