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:
parent
18e0cf85d6
commit
a344fe3245
|
@ -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[];
|
||||
|
||||
|
|
|
@ -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))
|
||||
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));
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue