clear-pkgs-linux-iot-lts2018/0682-drm-i915-Add-HDCP-SRM-...

240 lines
6.8 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: "Romli, Khairul Anuar" <khairul.anuar.romli@intel.com>
Date: Wed, 20 Dec 2017 21:47:01 +0530
Subject: [PATCH] drm/i915: Add HDCP SRM Blob parsing
This patch adds a drm blob property to selected connectors.
And also adds capability to parse the new srm blob passed through
cp_srm_property.
The revocated KSV list and their counts are stored in
the intel_connector. This list should be used for revocation check
of BKSVs in first stage HDCP authentication and for revocation check of
ksv_fifo in second stage authentication.
Signed-off-by: Ramalingam C <ramalingam.c@intel.com>
---
drivers/gpu/drm/i915/intel_drv.h | 5 +
drivers/gpu/drm/i915/intel_hdcp.c | 146 ++++++++++++++++++++++++++++++
include/drm/drm_hdcp.h | 14 +++
3 files changed, 165 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 10883e04bfe6..8bc603ec9b66 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -429,6 +429,11 @@ struct intel_connector {
struct delayed_work hdcp_check_work;
struct work_struct hdcp_prop_work;
struct work_struct hdcp_enable_work;
+
+ /* list of Revocated KSVs and their count from SRM blob Parsing */
+ unsigned int revocated_ksv_cnt;
+ u8 *revocated_ksv_list;
+ u32 srm_blob_id;
};
struct intel_digital_connector_state {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index a03a745544e2..ca2c73469cb2 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -10,6 +10,7 @@
#include <drm/drm_hdcp.h>
#include <linux/i2c.h>
#include <linux/random.h>
+#include <uapi/linux/swab.h>
#include "intel_drv.h"
#include "i915_reg.h"
@@ -732,6 +733,10 @@ int intel_hdcp_init(struct intel_connector *connector,
if (ret)
return ret;
+ ret = drm_connector_attach_cp_srm_property(&connector->base);
+ if (ret)
+ return ret;
+
connector->hdcp_shim = hdcp_shim;
mutex_init(&connector->hdcp_mutex);
INIT_DELAYED_WORK(&connector->hdcp_check_work, intel_hdcp_check_work);
@@ -818,11 +823,152 @@ void intel_hdcp_atomic_pre_commit(struct drm_connector *connector,
intel_hdcp_disable(to_intel_connector(connector));
}
+static u32 intel_hdcp_get_revocated_ksv_count(u8 *buf, u32 vrls_length)
+{
+ u32 parsed_bytes = 0, ksv_count = 0, vrl_ksv_cnt, vrl_sz;
+
+ do {
+ vrl_ksv_cnt = *buf;
+ ksv_count += vrl_ksv_cnt;
+
+ vrl_sz = (vrl_ksv_cnt * DRM_HDCP_KSV_LEN) + 1;
+ buf += vrl_sz;
+ parsed_bytes += vrl_sz;
+ } while (parsed_bytes < vrls_length);
+
+ return ksv_count;
+}
+
+static u32 intel_hdcp_get_revocated_ksvs(u8 *ksv_list, const u8 *buf,
+ u32 vrls_length)
+{
+ u32 parsed_bytes = 0, ksv_count = 0;
+ u32 vrl_ksv_cnt, vrl_ksv_sz, vrl_idx = 0;
+
+ do {
+ vrl_ksv_cnt = *buf;
+ vrl_ksv_sz = vrl_ksv_cnt * DRM_HDCP_KSV_LEN;
+
+ buf++;
+
+ DRM_INFO("vrl: %d, Revoked KSVs: %d\n", vrl_idx++,
+ vrl_ksv_cnt);
+ memcpy(ksv_list, buf, vrl_ksv_sz);
+
+ ksv_count += vrl_ksv_cnt;
+ ksv_list += vrl_ksv_sz;
+ buf += vrl_ksv_sz;
+
+ parsed_bytes += (vrl_ksv_sz + 1);
+ } while (parsed_bytes < vrls_length);
+
+ return ksv_count;
+}
+
+static int intel_hdcp_parse_srm(struct drm_connector *connector,
+ struct drm_property_blob *blob)
+{
+ struct intel_connector *intel_connector = to_intel_connector(connector);
+ struct cp_srm_header *header;
+ u32 vrl_length, ksv_count;
+ u8 *buf;
+
+ if (blob->length < (sizeof(struct cp_srm_header) +
+ DRM_HDCP_1_4_VRL_LENGTH_SIZE +
+ DRM_HDCP_1_4_DCP_SIG_SIZE)) {
+ DRM_ERROR("Invalid blob length\n");
+ return -EINVAL;
+ }
+
+ header = (struct cp_srm_header *)blob->data;
+
+ DRM_INFO("SRM ID: 0x%x, SRM Ver: 0x%x, SRM Gen No: 0x%x\n",
+ header->spec_indicator.srm_id,
+ __swab16(header->srm_version),
+ header->srm_gen_no);
+
+ WARN_ON(header->spec_indicator.reserved_hi ||
+ header->spec_indicator.reserved_lo);
+
+ if (header->spec_indicator.srm_id != DRM_HDCP_1_4_SRM_ID) {
+ DRM_ERROR("Invalid srm_id\n");
+ return -EINVAL;
+ }
+
+ buf = blob->data + sizeof(*header);
+
+ vrl_length = (*buf << 16 | *(buf + 1) << 8 | *(buf + 2));
+
+ if (blob->length < (sizeof(struct cp_srm_header) + vrl_length) ||
+ vrl_length < (DRM_HDCP_1_4_VRL_LENGTH_SIZE +
+ DRM_HDCP_1_4_DCP_SIG_SIZE)) {
+ DRM_ERROR("Invalid blob length or vrl length\n");
+ return -EINVAL;
+ }
+
+ /* Length of the all vrls combined */
+ vrl_length -= (DRM_HDCP_1_4_VRL_LENGTH_SIZE +
+ DRM_HDCP_1_4_DCP_SIG_SIZE);
+
+ if (!vrl_length) {
+ DRM_DEBUG("No vrl found\n");
+ return -EINVAL;
+ }
+
+ buf += DRM_HDCP_1_4_VRL_LENGTH_SIZE;
+
+
+ ksv_count = intel_hdcp_get_revocated_ksv_count(buf, vrl_length);
+ if (!ksv_count) {
+ DRM_INFO("Revocated KSV count is 0\n");
+ return 0;
+ }
+
+ kfree(intel_connector->revocated_ksv_list);
+ intel_connector->revocated_ksv_list = kzalloc(ksv_count *
+ DRM_HDCP_KSV_LEN, GFP_KERNEL);
+ if (!intel_connector->revocated_ksv_list) {
+ DRM_ERROR("Out of Memory\n");
+ return -ENOMEM;
+ }
+
+ if (intel_hdcp_get_revocated_ksvs(intel_connector->revocated_ksv_list,
+ buf, vrl_length) != ksv_count) {
+ intel_connector->revocated_ksv_cnt = 0;
+ kfree(intel_connector->revocated_ksv_list);
+ return -EINVAL;
+ }
+
+ intel_connector->revocated_ksv_cnt = ksv_count;
+ return 0;
+}
+
+static void intel_hdcp_update_srm(struct drm_connector *connector,
+ u32 srm_blob_id)
+{
+ struct intel_connector *intel_connector = to_intel_connector(connector);
+ struct drm_property_blob *blob;
+
+ blob = drm_property_lookup_blob(connector->dev, srm_blob_id);
+ if (!blob || !blob->data)
+ return;
+
+ if (!intel_hdcp_parse_srm(connector, blob))
+ intel_connector->srm_blob_id = srm_blob_id;
+
+ drm_property_blob_put(blob);
+}
+
void intel_hdcp_atomic_commit(struct drm_connector *connector,
struct drm_connector_state *new_state)
{
+ struct intel_connector *intel_connector = to_intel_connector(connector);
uint64_t new_cp = new_state->content_protection;
+ if (new_state->cp_srm_blob_id &&
+ new_state->cp_srm_blob_id != intel_connector->srm_blob_id)
+ intel_hdcp_update_srm(connector, new_state->cp_srm_blob_id);
+
/* Enable hdcp if it's desired */
if (new_state->crtc && new_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED)
intel_hdcp_enable(to_intel_connector(connector));
diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h
index 98e63d870139..f17eb2910bdb 100644
--- a/include/drm/drm_hdcp.h
+++ b/include/drm/drm_hdcp.h
@@ -38,4 +38,18 @@
#define DRM_HDCP_DDC_BSTATUS 0x41
#define DRM_HDCP_DDC_KSV_FIFO 0x43
+#define DRM_HDCP_1_4_SRM_ID 0x8
+#define DRM_HDCP_1_4_VRL_LENGTH_SIZE 3
+#define DRM_HDCP_1_4_DCP_SIG_SIZE 40
+
+struct cp_srm_header {
+ struct {
+ uint8_t reserved_hi:4;
+ uint8_t srm_id:4;
+ uint8_t reserved_lo;
+ } spec_indicator;
+ uint16_t srm_version;
+ uint8_t srm_gen_no;
+} __packed;
+
#endif
--
https://clearlinux.org