All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/6] drm/i915: Implement HDCP
@ 2017-11-30  3:08 Sean Paul
  2017-11-30  3:08   ` Sean Paul
                   ` (9 more replies)
  0 siblings, 10 replies; 62+ messages in thread
From: Sean Paul @ 2017-11-30  3:08 UTC (permalink / raw)
  To: dri-devel, intel-gfx

Here's the RFC for my i915 HDCP patchset. The UABI is based on what we've been
using in Chrome for the past 3 years. I posted the property to the list back
then, but never had a mainline driver to implement it. I do now :-)

Things are mostly in place, danvet gave me some feedback that I will
incorporate in v1. However, in the interest of gaining more early feedback, I'm
posting this now.

TODO:
- Add kerneldoc for property
- Fix '//' comments
- Change to MIT license
- Rebase on Ville's gmbus fixes (thanks Ville)
- Improve documentation on drm_intel_hdcp_shim
- Fix async commit locking (ie: don't use connection_mutex)
- Don't change connector->state in enable, defer to worker
- Add igt coverage for the feature.

Thanks!

Sean


Sean Paul (6):
  drm: Add Content Protection property
  drm: Add some HDCP related #defines
  drm/i915: Add HDCP framework + base implementation
  drm/i915: Add function to output Aksv over GMBUS
  drm/i915: Implement HDCP for HDMI
  drm/i915: Implement HDCP for DisplayPort

 drivers/gpu/drm/drm_atomic.c        |   8 +
 drivers/gpu/drm/drm_connector.c     |  43 +++
 drivers/gpu/drm/drm_sysfs.c         |  29 ++
 drivers/gpu/drm/i915/Makefile       |   1 +
 drivers/gpu/drm/i915/i915_drv.h     |   1 +
 drivers/gpu/drm/i915/i915_reg.h     |  85 +++++
 drivers/gpu/drm/i915/intel_atomic.c |  26 +-
 drivers/gpu/drm/i915/intel_ddi.c    |  64 ++++
 drivers/gpu/drm/i915/intel_dp.c     | 243 +++++++++++++-
 drivers/gpu/drm/i915/intel_drv.h    |  53 +++
 drivers/gpu/drm/i915/intel_hdcp.c   | 636 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_hdmi.c   | 253 ++++++++++++++
 drivers/gpu/drm/i915/intel_i2c.c    |  54 ++-
 include/drm/drm_connector.h         |  16 +
 include/drm/drm_dp_helper.h         |  17 +
 include/drm/drm_hdcp.h              |  44 +++
 include/uapi/drm/drm_mode.h         |   4 +
 17 files changed, 1560 insertions(+), 17 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c
 create mode 100644 include/drm/drm_hdcp.h

-- 
2.15.0.531.g2ccb3012c9-goog

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

^ permalink raw reply	[flat|nested] 62+ messages in thread

* [RFC PATCH 1/6] drm: Add Content Protection property
  2017-11-30  3:08 [RFC PATCH 0/6] drm/i915: Implement HDCP Sean Paul
  2017-11-30  3:08   ` Sean Paul
@ 2017-11-30  3:08   ` Sean Paul
  2017-11-30  3:08   ` Sean Paul
                     ` (7 subsequent siblings)
  9 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-11-30  3:08 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: Sean Paul, Daniel Vetter, Jani Nikula, Gustavo Padovan,
	David Airlie, linux-kernel, linux-arm-kernel, linux-mediatek

This patch adds a new optional connector property to allow userspace to enable
protection over the content it is displaying. This will typically be implemented
by the driver using HDCP.

The property is a tri-state with the following values:
- OFF: Self explanatory, no content protection
- DESIRED: Userspace requests that the driver enable protection
- ENABLED: Once the driver has authenticated the link, it sets this value

The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
unprotected. The driver should also maintain the desiredness of protection
across hotplug/dpms/suspend.

If this looks familiar, I posted [1] this 3 years ago. We have been using this
in ChromeOS across exynos, mediatek, and rockchip over that time.

Signed-off-by: Sean Paul <seanpaul@chromium.org>

[1] https://lists.freedesktop.org/archives/dri-devel/2014-December/073336.html
---
 drivers/gpu/drm/drm_atomic.c    |  8 ++++++++
 drivers/gpu/drm/drm_connector.c | 43 +++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_sysfs.c     | 29 +++++++++++++++++++++++++++
 include/drm/drm_connector.h     | 16 +++++++++++++++
 include/uapi/drm/drm_mode.h     |  4 ++++
 5 files changed, 100 insertions(+)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 37445d50816a..2212793eefa6 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1185,6 +1185,12 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector,
 		state->picture_aspect_ratio = val;
 	} else if (property == connector->scaling_mode_property) {
 		state->scaling_mode = val;
+	} else if (property == connector->content_protection_property) {
+		if (val == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
+			DRM_DEBUG_KMS("only drivers can set CP Enabled\n");
+			return -EINVAL;
+		}
+		state->content_protection = val;
 	} else if (connector->funcs->atomic_set_property) {
 		return connector->funcs->atomic_set_property(connector,
 				state, property, val);
@@ -1264,6 +1270,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector,
 		*val = state->picture_aspect_ratio;
 	} else if (property == connector->scaling_mode_property) {
 		*val = state->scaling_mode;
+	} else if (property == connector->content_protection_property) {
+		*val = state->content_protection;
 	} else if (connector->funcs->atomic_get_property) {
 		return connector->funcs->atomic_get_property(connector,
 				state, property, val);
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 704fc8934616..de2345a4a125 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -698,6 +698,13 @@ static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = {
 DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
 		 drm_tv_subconnector_enum_list)
 
+static struct drm_prop_enum_list drm_cp_enum_list[] = {
+        { DRM_MODE_CONTENT_PROTECTION_OFF, "Off" },
+        { DRM_MODE_CONTENT_PROTECTION_DESIRED, "Desired" },
+        { DRM_MODE_CONTENT_PROTECTION_ENABLED, "Enabled" },
+};
+DRM_ENUM_NAME_FN(drm_get_content_protection_name, drm_cp_enum_list)
+
 /**
  * DOC: standard connector properties
  *
@@ -1046,6 +1053,42 @@ int drm_connector_attach_scaling_mode_property(struct drm_connector *connector,
 }
 EXPORT_SYMBOL(drm_connector_attach_scaling_mode_property);
 
+/**
+ * drm_connector_attach_content_protection_property - attach content protection
+ * property
+ *
+ * @connector: connector to attach CP property on.
+ *
+ * This is used to add support for content protection on select connectors.
+ * Content Protection is intentionally vague to allow for different underlying
+ * technologies, however it is most implemented by HDCP.
+ *
+ * The content protection will be set to &drm_connector_state.content_protection
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_connector_attach_content_protection_property(
+		struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+	struct drm_property *prop;
+
+	prop = drm_property_create_enum(dev, 0, "Content Protection",
+					drm_cp_enum_list,
+					ARRAY_SIZE(drm_cp_enum_list));
+	if (!prop)
+		return -ENOMEM;
+
+	drm_object_attach_property(&connector->base, prop,
+				   DRM_MODE_CONTENT_PROTECTION_OFF);
+
+	connector->content_protection_property = prop;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_connector_attach_content_protection_property);
+
 /**
  * drm_mode_create_aspect_ratio_property - create aspect ratio property
  * @dev: DRM device
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index 1c5b5ce1fd7f..e8e15756dc59 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -21,6 +21,7 @@
 #include <drm/drm_sysfs.h>
 #include <drm/drmP.h>
 #include "drm_internal.h"
+#include "drm_crtc_internal.h"
 
 #define to_drm_minor(d) dev_get_drvdata(d)
 #define to_drm_connector(d) dev_get_drvdata(d)
@@ -229,16 +230,44 @@ static ssize_t modes_show(struct device *device,
 	return written;
 }
 
+static ssize_t content_protection_show(struct device *device,
+				       struct device_attribute *attr, char *buf)
+{
+	struct drm_connector *connector = to_drm_connector(device);
+	struct drm_device *dev = connector->dev;
+	struct drm_property *prop;
+	uint64_t cp;
+	int ret;
+
+	drm_modeset_lock_all(dev);
+
+	prop = connector->content_protection_property;
+	if (!prop) {
+		drm_modeset_unlock_all(dev);
+		return 0;
+	}
+
+	ret = drm_object_property_get_value(&connector->base, prop, &cp);
+	drm_modeset_unlock_all(dev);
+	if (ret)
+		return 0;
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			drm_get_content_protection_name((int)cp));
+}
+
 static DEVICE_ATTR_RW(status);
 static DEVICE_ATTR_RO(enabled);
 static DEVICE_ATTR_RO(dpms);
 static DEVICE_ATTR_RO(modes);
+static DEVICE_ATTR_RO(content_protection);
 
 static struct attribute *connector_dev_attrs[] = {
 	&dev_attr_status.attr,
 	&dev_attr_enabled.attr,
 	&dev_attr_dpms.attr,
 	&dev_attr_modes.attr,
+	&dev_attr_content_protection.attr,
 	NULL
 };
 
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 1543212b0449..ec6ea3953002 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -375,6 +375,12 @@ struct drm_connector_state {
 	 * upscaling, mostly used for built-in panels.
 	 */
 	unsigned int scaling_mode;
+
+	/**
+	 * @content_protection: Connector property to request content
+	 * protection. This is most commonly used for HDCP.
+	 */
+	unsigned int content_protection;
 };
 
 /**
@@ -722,6 +728,7 @@ struct drm_cmdline_mode {
  * @tile_h_size: horizontal size of this tile.
  * @tile_v_size: vertical size of this tile.
  * @scaling_mode_property:  Optional atomic property to control the upscaling.
+ * @content_protection_property: Optional property to control content protection
  *
  * Each connector may be connected to one or more CRTCs, or may be clonable by
  * another connector if they can share a CRTC.  Each connector also has a specific
@@ -812,6 +819,12 @@ struct drm_connector {
 
 	struct drm_property *scaling_mode_property;
 
+	/**
+	 * @content_protection_property: DRM ENUM property for content
+	 * protection
+	 */
+	struct drm_property *content_protection_property;
+
 	/**
 	 * @path_blob_ptr:
 	 *
@@ -1012,6 +1025,7 @@ const char *drm_get_dvi_i_subconnector_name(int val);
 const char *drm_get_dvi_i_select_name(int val);
 const char *drm_get_tv_subconnector_name(int val);
 const char *drm_get_tv_select_name(int val);
+const char *drm_get_content_protection_name(int val);
 
 int drm_mode_create_dvi_i_properties(struct drm_device *dev);
 int drm_mode_create_tv_properties(struct drm_device *dev,
@@ -1020,6 +1034,8 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
 int drm_mode_create_scaling_mode_property(struct drm_device *dev);
 int drm_connector_attach_scaling_mode_property(struct drm_connector *connector,
 					       u32 scaling_mode_mask);
+int drm_connector_attach_content_protection_property(
+		struct drm_connector *connector);
 int drm_mode_create_aspect_ratio_property(struct drm_device *dev);
 int drm_mode_create_suggested_offset_properties(struct drm_device *dev);
 
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 5597a87154e5..03f4d22305c2 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -173,6 +173,10 @@ extern "C" {
 		DRM_MODE_REFLECT_X | \
 		DRM_MODE_REFLECT_Y)
 
+/* Content Protection Flags */
+#define DRM_MODE_CONTENT_PROTECTION_OFF		0
+#define DRM_MODE_CONTENT_PROTECTION_DESIRED     1
+#define DRM_MODE_CONTENT_PROTECTION_ENABLED     2
 
 struct drm_mode_modeinfo {
 	__u32 clock;
-- 
2.15.0.531.g2ccb3012c9-goog

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-11-30  3:08   ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-11-30  3:08 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: David Airlie, linux-mediatek, linux-kernel, Daniel Vetter,
	linux-arm-kernel

This patch adds a new optional connector property to allow userspace to enable
protection over the content it is displaying. This will typically be implemented
by the driver using HDCP.

The property is a tri-state with the following values:
- OFF: Self explanatory, no content protection
- DESIRED: Userspace requests that the driver enable protection
- ENABLED: Once the driver has authenticated the link, it sets this value

The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
unprotected. The driver should also maintain the desiredness of protection
across hotplug/dpms/suspend.

If this looks familiar, I posted [1] this 3 years ago. We have been using this
in ChromeOS across exynos, mediatek, and rockchip over that time.

Signed-off-by: Sean Paul <seanpaul@chromium.org>

[1] https://lists.freedesktop.org/archives/dri-devel/2014-December/073336.html
---
 drivers/gpu/drm/drm_atomic.c    |  8 ++++++++
 drivers/gpu/drm/drm_connector.c | 43 +++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_sysfs.c     | 29 +++++++++++++++++++++++++++
 include/drm/drm_connector.h     | 16 +++++++++++++++
 include/uapi/drm/drm_mode.h     |  4 ++++
 5 files changed, 100 insertions(+)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 37445d50816a..2212793eefa6 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1185,6 +1185,12 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector,
 		state->picture_aspect_ratio = val;
 	} else if (property == connector->scaling_mode_property) {
 		state->scaling_mode = val;
+	} else if (property == connector->content_protection_property) {
+		if (val == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
+			DRM_DEBUG_KMS("only drivers can set CP Enabled\n");
+			return -EINVAL;
+		}
+		state->content_protection = val;
 	} else if (connector->funcs->atomic_set_property) {
 		return connector->funcs->atomic_set_property(connector,
 				state, property, val);
@@ -1264,6 +1270,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector,
 		*val = state->picture_aspect_ratio;
 	} else if (property == connector->scaling_mode_property) {
 		*val = state->scaling_mode;
+	} else if (property == connector->content_protection_property) {
+		*val = state->content_protection;
 	} else if (connector->funcs->atomic_get_property) {
 		return connector->funcs->atomic_get_property(connector,
 				state, property, val);
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 704fc8934616..de2345a4a125 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -698,6 +698,13 @@ static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = {
 DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
 		 drm_tv_subconnector_enum_list)
 
+static struct drm_prop_enum_list drm_cp_enum_list[] = {
+        { DRM_MODE_CONTENT_PROTECTION_OFF, "Off" },
+        { DRM_MODE_CONTENT_PROTECTION_DESIRED, "Desired" },
+        { DRM_MODE_CONTENT_PROTECTION_ENABLED, "Enabled" },
+};
+DRM_ENUM_NAME_FN(drm_get_content_protection_name, drm_cp_enum_list)
+
 /**
  * DOC: standard connector properties
  *
@@ -1046,6 +1053,42 @@ int drm_connector_attach_scaling_mode_property(struct drm_connector *connector,
 }
 EXPORT_SYMBOL(drm_connector_attach_scaling_mode_property);
 
+/**
+ * drm_connector_attach_content_protection_property - attach content protection
+ * property
+ *
+ * @connector: connector to attach CP property on.
+ *
+ * This is used to add support for content protection on select connectors.
+ * Content Protection is intentionally vague to allow for different underlying
+ * technologies, however it is most implemented by HDCP.
+ *
+ * The content protection will be set to &drm_connector_state.content_protection
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_connector_attach_content_protection_property(
+		struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+	struct drm_property *prop;
+
+	prop = drm_property_create_enum(dev, 0, "Content Protection",
+					drm_cp_enum_list,
+					ARRAY_SIZE(drm_cp_enum_list));
+	if (!prop)
+		return -ENOMEM;
+
+	drm_object_attach_property(&connector->base, prop,
+				   DRM_MODE_CONTENT_PROTECTION_OFF);
+
+	connector->content_protection_property = prop;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_connector_attach_content_protection_property);
+
 /**
  * drm_mode_create_aspect_ratio_property - create aspect ratio property
  * @dev: DRM device
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index 1c5b5ce1fd7f..e8e15756dc59 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -21,6 +21,7 @@
 #include <drm/drm_sysfs.h>
 #include <drm/drmP.h>
 #include "drm_internal.h"
+#include "drm_crtc_internal.h"
 
 #define to_drm_minor(d) dev_get_drvdata(d)
 #define to_drm_connector(d) dev_get_drvdata(d)
@@ -229,16 +230,44 @@ static ssize_t modes_show(struct device *device,
 	return written;
 }
 
+static ssize_t content_protection_show(struct device *device,
+				       struct device_attribute *attr, char *buf)
+{
+	struct drm_connector *connector = to_drm_connector(device);
+	struct drm_device *dev = connector->dev;
+	struct drm_property *prop;
+	uint64_t cp;
+	int ret;
+
+	drm_modeset_lock_all(dev);
+
+	prop = connector->content_protection_property;
+	if (!prop) {
+		drm_modeset_unlock_all(dev);
+		return 0;
+	}
+
+	ret = drm_object_property_get_value(&connector->base, prop, &cp);
+	drm_modeset_unlock_all(dev);
+	if (ret)
+		return 0;
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			drm_get_content_protection_name((int)cp));
+}
+
 static DEVICE_ATTR_RW(status);
 static DEVICE_ATTR_RO(enabled);
 static DEVICE_ATTR_RO(dpms);
 static DEVICE_ATTR_RO(modes);
+static DEVICE_ATTR_RO(content_protection);
 
 static struct attribute *connector_dev_attrs[] = {
 	&dev_attr_status.attr,
 	&dev_attr_enabled.attr,
 	&dev_attr_dpms.attr,
 	&dev_attr_modes.attr,
+	&dev_attr_content_protection.attr,
 	NULL
 };
 
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 1543212b0449..ec6ea3953002 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -375,6 +375,12 @@ struct drm_connector_state {
 	 * upscaling, mostly used for built-in panels.
 	 */
 	unsigned int scaling_mode;
+
+	/**
+	 * @content_protection: Connector property to request content
+	 * protection. This is most commonly used for HDCP.
+	 */
+	unsigned int content_protection;
 };
 
 /**
@@ -722,6 +728,7 @@ struct drm_cmdline_mode {
  * @tile_h_size: horizontal size of this tile.
  * @tile_v_size: vertical size of this tile.
  * @scaling_mode_property:  Optional atomic property to control the upscaling.
+ * @content_protection_property: Optional property to control content protection
  *
  * Each connector may be connected to one or more CRTCs, or may be clonable by
  * another connector if they can share a CRTC.  Each connector also has a specific
@@ -812,6 +819,12 @@ struct drm_connector {
 
 	struct drm_property *scaling_mode_property;
 
+	/**
+	 * @content_protection_property: DRM ENUM property for content
+	 * protection
+	 */
+	struct drm_property *content_protection_property;
+
 	/**
 	 * @path_blob_ptr:
 	 *
@@ -1012,6 +1025,7 @@ const char *drm_get_dvi_i_subconnector_name(int val);
 const char *drm_get_dvi_i_select_name(int val);
 const char *drm_get_tv_subconnector_name(int val);
 const char *drm_get_tv_select_name(int val);
+const char *drm_get_content_protection_name(int val);
 
 int drm_mode_create_dvi_i_properties(struct drm_device *dev);
 int drm_mode_create_tv_properties(struct drm_device *dev,
@@ -1020,6 +1034,8 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
 int drm_mode_create_scaling_mode_property(struct drm_device *dev);
 int drm_connector_attach_scaling_mode_property(struct drm_connector *connector,
 					       u32 scaling_mode_mask);
+int drm_connector_attach_content_protection_property(
+		struct drm_connector *connector);
 int drm_mode_create_aspect_ratio_property(struct drm_device *dev);
 int drm_mode_create_suggested_offset_properties(struct drm_device *dev);
 
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 5597a87154e5..03f4d22305c2 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -173,6 +173,10 @@ extern "C" {
 		DRM_MODE_REFLECT_X | \
 		DRM_MODE_REFLECT_Y)
 
+/* Content Protection Flags */
+#define DRM_MODE_CONTENT_PROTECTION_OFF		0
+#define DRM_MODE_CONTENT_PROTECTION_DESIRED     1
+#define DRM_MODE_CONTENT_PROTECTION_ENABLED     2
 
 struct drm_mode_modeinfo {
 	__u32 clock;
-- 
2.15.0.531.g2ccb3012c9-goog

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

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-11-30  3:08   ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-11-30  3:08 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds a new optional connector property to allow userspace to enable
protection over the content it is displaying. This will typically be implemented
by the driver using HDCP.

The property is a tri-state with the following values:
- OFF: Self explanatory, no content protection
- DESIRED: Userspace requests that the driver enable protection
- ENABLED: Once the driver has authenticated the link, it sets this value

The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
unprotected. The driver should also maintain the desiredness of protection
across hotplug/dpms/suspend.

If this looks familiar, I posted [1] this 3 years ago. We have been using this
in ChromeOS across exynos, mediatek, and rockchip over that time.

Signed-off-by: Sean Paul <seanpaul@chromium.org>

[1] https://lists.freedesktop.org/archives/dri-devel/2014-December/073336.html
---
 drivers/gpu/drm/drm_atomic.c    |  8 ++++++++
 drivers/gpu/drm/drm_connector.c | 43 +++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_sysfs.c     | 29 +++++++++++++++++++++++++++
 include/drm/drm_connector.h     | 16 +++++++++++++++
 include/uapi/drm/drm_mode.h     |  4 ++++
 5 files changed, 100 insertions(+)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 37445d50816a..2212793eefa6 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1185,6 +1185,12 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector,
 		state->picture_aspect_ratio = val;
 	} else if (property == connector->scaling_mode_property) {
 		state->scaling_mode = val;
+	} else if (property == connector->content_protection_property) {
+		if (val == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
+			DRM_DEBUG_KMS("only drivers can set CP Enabled\n");
+			return -EINVAL;
+		}
+		state->content_protection = val;
 	} else if (connector->funcs->atomic_set_property) {
 		return connector->funcs->atomic_set_property(connector,
 				state, property, val);
@@ -1264,6 +1270,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector,
 		*val = state->picture_aspect_ratio;
 	} else if (property == connector->scaling_mode_property) {
 		*val = state->scaling_mode;
+	} else if (property == connector->content_protection_property) {
+		*val = state->content_protection;
 	} else if (connector->funcs->atomic_get_property) {
 		return connector->funcs->atomic_get_property(connector,
 				state, property, val);
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 704fc8934616..de2345a4a125 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -698,6 +698,13 @@ static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = {
 DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
 		 drm_tv_subconnector_enum_list)
 
+static struct drm_prop_enum_list drm_cp_enum_list[] = {
+        { DRM_MODE_CONTENT_PROTECTION_OFF, "Off" },
+        { DRM_MODE_CONTENT_PROTECTION_DESIRED, "Desired" },
+        { DRM_MODE_CONTENT_PROTECTION_ENABLED, "Enabled" },
+};
+DRM_ENUM_NAME_FN(drm_get_content_protection_name, drm_cp_enum_list)
+
 /**
  * DOC: standard connector properties
  *
@@ -1046,6 +1053,42 @@ int drm_connector_attach_scaling_mode_property(struct drm_connector *connector,
 }
 EXPORT_SYMBOL(drm_connector_attach_scaling_mode_property);
 
+/**
+ * drm_connector_attach_content_protection_property - attach content protection
+ * property
+ *
+ * @connector: connector to attach CP property on.
+ *
+ * This is used to add support for content protection on select connectors.
+ * Content Protection is intentionally vague to allow for different underlying
+ * technologies, however it is most implemented by HDCP.
+ *
+ * The content protection will be set to &drm_connector_state.content_protection
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_connector_attach_content_protection_property(
+		struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+	struct drm_property *prop;
+
+	prop = drm_property_create_enum(dev, 0, "Content Protection",
+					drm_cp_enum_list,
+					ARRAY_SIZE(drm_cp_enum_list));
+	if (!prop)
+		return -ENOMEM;
+
+	drm_object_attach_property(&connector->base, prop,
+				   DRM_MODE_CONTENT_PROTECTION_OFF);
+
+	connector->content_protection_property = prop;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_connector_attach_content_protection_property);
+
 /**
  * drm_mode_create_aspect_ratio_property - create aspect ratio property
  * @dev: DRM device
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index 1c5b5ce1fd7f..e8e15756dc59 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -21,6 +21,7 @@
 #include <drm/drm_sysfs.h>
 #include <drm/drmP.h>
 #include "drm_internal.h"
+#include "drm_crtc_internal.h"
 
 #define to_drm_minor(d) dev_get_drvdata(d)
 #define to_drm_connector(d) dev_get_drvdata(d)
@@ -229,16 +230,44 @@ static ssize_t modes_show(struct device *device,
 	return written;
 }
 
+static ssize_t content_protection_show(struct device *device,
+				       struct device_attribute *attr, char *buf)
+{
+	struct drm_connector *connector = to_drm_connector(device);
+	struct drm_device *dev = connector->dev;
+	struct drm_property *prop;
+	uint64_t cp;
+	int ret;
+
+	drm_modeset_lock_all(dev);
+
+	prop = connector->content_protection_property;
+	if (!prop) {
+		drm_modeset_unlock_all(dev);
+		return 0;
+	}
+
+	ret = drm_object_property_get_value(&connector->base, prop, &cp);
+	drm_modeset_unlock_all(dev);
+	if (ret)
+		return 0;
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			drm_get_content_protection_name((int)cp));
+}
+
 static DEVICE_ATTR_RW(status);
 static DEVICE_ATTR_RO(enabled);
 static DEVICE_ATTR_RO(dpms);
 static DEVICE_ATTR_RO(modes);
+static DEVICE_ATTR_RO(content_protection);
 
 static struct attribute *connector_dev_attrs[] = {
 	&dev_attr_status.attr,
 	&dev_attr_enabled.attr,
 	&dev_attr_dpms.attr,
 	&dev_attr_modes.attr,
+	&dev_attr_content_protection.attr,
 	NULL
 };
 
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 1543212b0449..ec6ea3953002 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -375,6 +375,12 @@ struct drm_connector_state {
 	 * upscaling, mostly used for built-in panels.
 	 */
 	unsigned int scaling_mode;
+
+	/**
+	 * @content_protection: Connector property to request content
+	 * protection. This is most commonly used for HDCP.
+	 */
+	unsigned int content_protection;
 };
 
 /**
@@ -722,6 +728,7 @@ struct drm_cmdline_mode {
  * @tile_h_size: horizontal size of this tile.
  * @tile_v_size: vertical size of this tile.
  * @scaling_mode_property:  Optional atomic property to control the upscaling.
+ * @content_protection_property: Optional property to control content protection
  *
  * Each connector may be connected to one or more CRTCs, or may be clonable by
  * another connector if they can share a CRTC.  Each connector also has a specific
@@ -812,6 +819,12 @@ struct drm_connector {
 
 	struct drm_property *scaling_mode_property;
 
+	/**
+	 * @content_protection_property: DRM ENUM property for content
+	 * protection
+	 */
+	struct drm_property *content_protection_property;
+
 	/**
 	 * @path_blob_ptr:
 	 *
@@ -1012,6 +1025,7 @@ const char *drm_get_dvi_i_subconnector_name(int val);
 const char *drm_get_dvi_i_select_name(int val);
 const char *drm_get_tv_subconnector_name(int val);
 const char *drm_get_tv_select_name(int val);
+const char *drm_get_content_protection_name(int val);
 
 int drm_mode_create_dvi_i_properties(struct drm_device *dev);
 int drm_mode_create_tv_properties(struct drm_device *dev,
@@ -1020,6 +1034,8 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
 int drm_mode_create_scaling_mode_property(struct drm_device *dev);
 int drm_connector_attach_scaling_mode_property(struct drm_connector *connector,
 					       u32 scaling_mode_mask);
+int drm_connector_attach_content_protection_property(
+		struct drm_connector *connector);
 int drm_mode_create_aspect_ratio_property(struct drm_device *dev);
 int drm_mode_create_suggested_offset_properties(struct drm_device *dev);
 
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 5597a87154e5..03f4d22305c2 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -173,6 +173,10 @@ extern "C" {
 		DRM_MODE_REFLECT_X | \
 		DRM_MODE_REFLECT_Y)
 
+/* Content Protection Flags */
+#define DRM_MODE_CONTENT_PROTECTION_OFF		0
+#define DRM_MODE_CONTENT_PROTECTION_DESIRED     1
+#define DRM_MODE_CONTENT_PROTECTION_ENABLED     2
 
 struct drm_mode_modeinfo {
 	__u32 clock;
-- 
2.15.0.531.g2ccb3012c9-goog

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [RFC PATCH 2/6] drm: Add some HDCP related #defines
  2017-11-30  3:08 [RFC PATCH 0/6] drm/i915: Implement HDCP Sean Paul
@ 2017-11-30  3:08   ` Sean Paul
  2017-11-30  3:08   ` Sean Paul
                     ` (8 subsequent siblings)
  9 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-11-30  3:08 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: Sean Paul, Daniel Vetter, Jani Nikula, Gustavo Padovan,
	David Airlie, linux-kernel

In preparation for implementing HDCP in i915, add some HDCP related
register offsets and defines. The dpcd register offsets will go in
drm_dp_helper.h whereas the ddc offsets along with generic HDCP stuff
will get stuffed in drm_hdcp.h, which is new.

Signed-off-by: Sean Paul <seanpaul@chromium.org>
---
 include/drm/drm_dp_helper.h | 17 +++++++++++++++++
 include/drm/drm_hdcp.h      | 44 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+)
 create mode 100644 include/drm/drm_hdcp.h

diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index 9049ef133d69..4671d41a2fe1 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -816,6 +816,23 @@
 #define DP_CEC_TX_MESSAGE_BUFFER               0x3020
 #define DP_CEC_MESSAGE_BUFFER_LENGTH             0x10
 
+#define DP_AUX_HDCP_BKSV		0x68000
+#define DP_AUX_HDCP_RI_PRIME		0x68005
+#define DP_AUX_HDCP_AKSV		0x68007
+#define DP_AUX_HDCP_AN			0x6800C
+#define DP_AUX_HDCP_V_PRIME(h)		(0x68014 + h * 4)
+#define DP_AUX_HDCP_BCAPS		0x68028
+# define DP_BCAPS_REPEATER_PRESENT	BIT(1)
+# define DP_BCAPS_HDCP_CAPABLE		BIT(0)
+#define DP_AUX_HDCP_BSTATUS		0x68029
+# define DP_BSTATUS_REAUTH_REQ		BIT(3)
+# define DP_BSTATUS_LINK_FAILURE	BIT(2)
+# define DP_BSTATUS_R0_PRIME_READY	BIT(1)
+# define DP_BSTATUS_READY		BIT(0)
+#define DP_AUX_HDCP_BINFO		0x6802A
+#define DP_AUX_HDCP_KSV_FIFO		0x6802C
+#define DP_AUX_HDCP_AINFO		0x6803B
+
 /* DP 1.2 Sideband message defines */
 /* peer device type - DP 1.2a Table 2-92 */
 #define DP_PEER_DEVICE_NONE		0x0
diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h
new file mode 100644
index 000000000000..d9b0947b6f09
--- /dev/null
+++ b/include/drm/drm_hdcp.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DRM_HDCP_H_INCLUDED_
+#define _DRM_HDCP_H_INCLUDED_
+
+/* Period of hdcp checks (to ensure we're still authenticated) */
+#define DRM_HDCP_CHECK_PERIOD_MS		(128 * 16)
+
+/* Shared lengths/masks between HDMI/DVI/DisplayPort */
+#define DRM_HDCP_AN_LEN				8
+#define DRM_HDCP_BSTATUS_LEN			2
+#define DRM_HDCP_KSV_LEN			5
+#define DRM_HDCP_RI_LEN				2
+#define DRM_HDCP_V_PRIME_PART_LEN		4
+#define DRM_HDCP_V_PRIME_NUM_PARTS		5
+#define DRM_HDCP_NUM_DOWNSTREAM(x)		(x & 0x3f)
+
+/* Slave address for the HDCP registers in the receiver */
+#define DRM_HDCP_DDC_ADDR			0x3A
+
+/* HDCP register offsets for HDMI/DVI devices */
+#define DRM_HDCP_DDC_BKSV			0x00
+#define DRM_HDCP_DDC_RI_PRIME			0x08
+#define DRM_HDCP_DDC_AKSV			0x10
+#define DRM_HDCP_DDC_AN				0x18
+#define DRM_HDCP_DDC_V_PRIME(h)			(0x20 + h * 4)
+#define DRM_HDCP_DDC_BCAPS			0x40
+#define  DRM_HDCP_DDC_BCAPS_REPEATER_PRESENT	BIT(6)
+#define  DRM_HDCP_DDC_BCAPS_KSV_FIFO_READY	BIT(5)
+#define DRM_HDCP_DDC_BSTATUS			0x41
+#define DRM_HDCP_DDC_KSV_FIFO			0x43
+
+#endif
-- 
2.15.0.531.g2ccb3012c9-goog

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [RFC PATCH 2/6] drm: Add some HDCP related #defines
@ 2017-11-30  3:08   ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-11-30  3:08 UTC (permalink / raw)
  To: dri-devel, intel-gfx; +Cc: David Airlie, linux-kernel, Daniel Vetter

In preparation for implementing HDCP in i915, add some HDCP related
register offsets and defines. The dpcd register offsets will go in
drm_dp_helper.h whereas the ddc offsets along with generic HDCP stuff
will get stuffed in drm_hdcp.h, which is new.

Signed-off-by: Sean Paul <seanpaul@chromium.org>
---
 include/drm/drm_dp_helper.h | 17 +++++++++++++++++
 include/drm/drm_hdcp.h      | 44 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+)
 create mode 100644 include/drm/drm_hdcp.h

diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index 9049ef133d69..4671d41a2fe1 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -816,6 +816,23 @@
 #define DP_CEC_TX_MESSAGE_BUFFER               0x3020
 #define DP_CEC_MESSAGE_BUFFER_LENGTH             0x10
 
+#define DP_AUX_HDCP_BKSV		0x68000
+#define DP_AUX_HDCP_RI_PRIME		0x68005
+#define DP_AUX_HDCP_AKSV		0x68007
+#define DP_AUX_HDCP_AN			0x6800C
+#define DP_AUX_HDCP_V_PRIME(h)		(0x68014 + h * 4)
+#define DP_AUX_HDCP_BCAPS		0x68028
+# define DP_BCAPS_REPEATER_PRESENT	BIT(1)
+# define DP_BCAPS_HDCP_CAPABLE		BIT(0)
+#define DP_AUX_HDCP_BSTATUS		0x68029
+# define DP_BSTATUS_REAUTH_REQ		BIT(3)
+# define DP_BSTATUS_LINK_FAILURE	BIT(2)
+# define DP_BSTATUS_R0_PRIME_READY	BIT(1)
+# define DP_BSTATUS_READY		BIT(0)
+#define DP_AUX_HDCP_BINFO		0x6802A
+#define DP_AUX_HDCP_KSV_FIFO		0x6802C
+#define DP_AUX_HDCP_AINFO		0x6803B
+
 /* DP 1.2 Sideband message defines */
 /* peer device type - DP 1.2a Table 2-92 */
 #define DP_PEER_DEVICE_NONE		0x0
diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h
new file mode 100644
index 000000000000..d9b0947b6f09
--- /dev/null
+++ b/include/drm/drm_hdcp.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DRM_HDCP_H_INCLUDED_
+#define _DRM_HDCP_H_INCLUDED_
+
+/* Period of hdcp checks (to ensure we're still authenticated) */
+#define DRM_HDCP_CHECK_PERIOD_MS		(128 * 16)
+
+/* Shared lengths/masks between HDMI/DVI/DisplayPort */
+#define DRM_HDCP_AN_LEN				8
+#define DRM_HDCP_BSTATUS_LEN			2
+#define DRM_HDCP_KSV_LEN			5
+#define DRM_HDCP_RI_LEN				2
+#define DRM_HDCP_V_PRIME_PART_LEN		4
+#define DRM_HDCP_V_PRIME_NUM_PARTS		5
+#define DRM_HDCP_NUM_DOWNSTREAM(x)		(x & 0x3f)
+
+/* Slave address for the HDCP registers in the receiver */
+#define DRM_HDCP_DDC_ADDR			0x3A
+
+/* HDCP register offsets for HDMI/DVI devices */
+#define DRM_HDCP_DDC_BKSV			0x00
+#define DRM_HDCP_DDC_RI_PRIME			0x08
+#define DRM_HDCP_DDC_AKSV			0x10
+#define DRM_HDCP_DDC_AN				0x18
+#define DRM_HDCP_DDC_V_PRIME(h)			(0x20 + h * 4)
+#define DRM_HDCP_DDC_BCAPS			0x40
+#define  DRM_HDCP_DDC_BCAPS_REPEATER_PRESENT	BIT(6)
+#define  DRM_HDCP_DDC_BCAPS_KSV_FIFO_READY	BIT(5)
+#define DRM_HDCP_DDC_BSTATUS			0x41
+#define DRM_HDCP_DDC_KSV_FIFO			0x43
+
+#endif
-- 
2.15.0.531.g2ccb3012c9-goog

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [RFC PATCH 3/6] drm/i915: Add HDCP framework + base implementation
  2017-11-30  3:08 [RFC PATCH 0/6] drm/i915: Implement HDCP Sean Paul
@ 2017-11-30  3:08   ` Sean Paul
  2017-11-30  3:08   ` Sean Paul
                     ` (8 subsequent siblings)
  9 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-11-30  3:08 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: Sean Paul, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
	David Airlie, linux-kernel

This patch adds the framework required to add HDCP support to intel
connectors. It implements Aksv loading from fuse, and parts 1/2/3
of the HDCP authentication scheme.

Note that without shim implementations, this does not actually implement
HDCP. That will come in subsequent patches.

Signed-off-by: Sean Paul <seanpaul@chromium.org>
---
 drivers/gpu/drm/i915/Makefile       |   1 +
 drivers/gpu/drm/i915/i915_reg.h     |  83 +++++
 drivers/gpu/drm/i915/intel_atomic.c |  26 +-
 drivers/gpu/drm/i915/intel_ddi.c    |  14 +
 drivers/gpu/drm/i915/intel_drv.h    |  53 +++
 drivers/gpu/drm/i915/intel_hdcp.c   | 636 ++++++++++++++++++++++++++++++++++++
 6 files changed, 811 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c

diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 6c3b0481ef82..1e745508e437 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -87,6 +87,7 @@ i915-y += intel_audio.o \
 	  intel_fbc.o \
 	  intel_fifo_underrun.o \
 	  intel_frontbuffer.o \
+	  intel_hdcp.o \
 	  intel_hotplug.o \
 	  intel_modes.o \
 	  intel_overlay.o \
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 68a58cce6ab1..43128030171d 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -7991,6 +7991,7 @@ enum {
 #define     GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT	8
 #define     GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT	16
 #define     GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT	24
+#define   SKL_PCODE_LOAD_HDCP_KEYS		0x5
 #define   SKL_PCODE_CDCLK_CONTROL		0x7
 #define     SKL_CDCLK_PREPARE_FOR_CHANGE	0x3
 #define     SKL_CDCLK_READY_FOR_CHANGE		0x1
@@ -8285,6 +8286,88 @@ enum skl_power_gate {
 #define  SKL_PW_TO_PG(pw)			((pw) - SKL_DISP_PW_1 + SKL_PG1)
 #define  SKL_FUSE_PG_DIST_STATUS(pg)		(1 << (27 - (pg)))
 
+
+/* HDCP Key Registers */
+#define SKL_HDCP_KEY_CONF		_MMIO(0x66c00)
+#define	 SKL_HDCP_AKSV_SEND_TRIGGER	BIT(31)
+#define  SKL_HDCP_CLEAR_KEYS_TRIGGER	BIT(30)
+#define SKL_HDCP_KEY_STATUS		_MMIO(0x66c04)
+#define  SKL_HDCP_FUSE_IN_PROGRESS	BIT(7)
+#define  SKL_HDCP_FUSE_ERROR		BIT(6)
+#define  SKL_HDCP_FUSE_DONE		BIT(5)
+#define  SKL_HDCP_KEY_LOAD_STATUS	BIT(1)
+#define  SKL_HDCP_KEY_LOAD_DONE		BIT(0)
+#define SKL_HDCP_AKSV_LO		_MMIO(0x66c10)
+#define SKL_HDCP_AKSV_HI		_MMIO(0x66c14)
+
+/* HDCP Repeater Registers */
+#define SKL_HDCP_REP_CTL		_MMIO(0x66d00)
+#define  SKL_HDCP_DDIB_REP_PRESENT	BIT(30)
+#define  SKL_HDCP_DDIA_REP_PRESENT	BIT(29)
+#define  SKL_HDCP_DDIC_REP_PRESENT	BIT(28)
+#define  SKL_HDCP_DDID_REP_PRESENT	BIT(27)
+#define  SKL_HDCP_DDIF_REP_PRESENT	BIT(26)
+#define  SKL_HDCP_DDIE_REP_PRESENT	BIT(25)
+#define  SKL_HDCP_DDIB_SHA1_M0		(1 << 20)
+#define  SKL_HDCP_DDIA_SHA1_M0		(2 << 20)
+#define  SKL_HDCP_DDIC_SHA1_M0		(3 << 20)
+#define  SKL_HDCP_DDID_SHA1_M0		(4 << 20)
+#define  SKL_HDCP_DDIF_SHA1_M0		(5 << 20)
+#define  SKL_HDCP_DDIE_SHA1_M0		(6 << 20) // Bspec says 5?
+#define  SKL_HDCP_SHA1_BUSY		BIT(16)
+#define  SKL_HDCP_SHA1_READY		BIT(17)
+#define  SKL_HDCP_SHA1_COMPLETE		BIT(18)
+#define  SKL_HDCP_SHA1_V_MATCH		BIT(19)
+#define  SKL_HDCP_SHA1_TEXT_32		(1 << 1)
+#define  SKL_HDCP_SHA1_COMPLETE_HASH	(2 << 1)
+#define  SKL_HDCP_SHA1_TEXT_24		(4 << 1)
+#define  SKL_HDCP_SHA1_TEXT_16		(5 << 1)
+#define  SKL_HDCP_SHA1_TEXT_8		(6 << 1)
+#define  SKL_HDCP_SHA1_TEXT_0		(7 << 1)
+#define SKL_HDCP_SHA_V_PRIME_H0		_MMIO(0x66d04)
+#define SKL_HDCP_SHA_V_PRIME_H1		_MMIO(0x66d08)
+#define SKL_HDCP_SHA_V_PRIME_H2		_MMIO(0x66d0C)
+#define SKL_HDCP_SHA_V_PRIME_H3		_MMIO(0x66d10)
+#define SKL_HDCP_SHA_V_PRIME_H4		_MMIO(0x66d14)
+#define SKL_HDCP_SHA_V_PRIME(h)		_MMIO((0x66d04 + h * 4))
+#define SKL_HDCP_SHA_TEXT		_MMIO(0x66d18)
+
+/* HDCP Auth Registers */
+#define _SKL_PORTA_HDCP_AUTHENC		0x66800
+#define _SKL_PORTB_HDCP_AUTHENC		0x66500
+#define _SKL_PORTC_HDCP_AUTHENC		0x66600
+#define _SKL_PORTD_HDCP_AUTHENC		0x66700
+#define _SKL_PORTE_HDCP_AUTHENC		0x66A00
+#define _SKL_PORTF_HDCP_AUTHENC		0x66900
+#define _SKL_PORT_HDCP_AUTHENC(port, x)	_MMIO(_PICK(port, \
+					  _SKL_PORTA_HDCP_AUTHENC, \
+					  _SKL_PORTB_HDCP_AUTHENC, \
+					  _SKL_PORTC_HDCP_AUTHENC, \
+					  _SKL_PORTD_HDCP_AUTHENC, \
+					  _SKL_PORTE_HDCP_AUTHENC, \
+					  _SKL_PORTF_HDCP_AUTHENC) + x)
+#define SKL_PORT_HDCP_CONF(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x0)
+#define  SKL_HDCP_CONF_CAPTURE_AN	BIT(0)
+#define  SKL_HDCP_CONF_AUTH_AND_ENC	(BIT(1) | BIT(0))
+#define SKL_PORT_HDCP_ANINIT(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x4)
+#define SKL_PORT_HDCP_ANLO(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x8)
+#define SKL_PORT_HDCP_ANHI(port)	_SKL_PORT_HDCP_AUTHENC(port, 0xC)
+#define SKL_PORT_HDCP_BKSVLO(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x10)
+#define SKL_PORT_HDCP_BKSVHI(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x14)
+#define SKL_PORT_HDCP_RPRIME(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x18)
+#define SKL_PORT_HDCP_STATUS(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x1C)
+#define  SKL_HDCP_STATUS_STREAM_A_ENC	BIT(31)
+#define  SKL_HDCP_STATUS_STREAM_B_ENC	BIT(30)
+#define  SKL_HDCP_STATUS_STREAM_C_ENC	BIT(29)
+#define  SKL_HDCP_STATUS_STREAM_D_ENC	BIT(28)
+#define  SKL_HDCP_STATUS_AUTH		BIT(21)
+#define  SKL_HDCP_STATUS_ENC		BIT(20)
+#define  SKL_HDCP_STATUS_RI_MATCH	BIT(19)
+#define  SKL_HDCP_STATUS_R0_READY	BIT(18)
+#define  SKL_HDCP_STATUS_AN_READY	BIT(17)
+#define  SKL_HDCP_STATUS_CIPHER		BIT(16)
+#define  SKL_HDCP_STATUS_FRAME_CNT(x)	((x >> 8) & 0xff)
+
 /* Per-pipe DDI Function Control */
 #define _TRANS_DDI_FUNC_CTL_A		0x60400
 #define _TRANS_DDI_FUNC_CTL_B		0x61400
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
index 36d4e635e4ce..ddf08227d9cb 100644
--- a/drivers/gpu/drm/i915/intel_atomic.c
+++ b/drivers/gpu/drm/i915/intel_atomic.c
@@ -109,12 +109,34 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
 	struct intel_digital_connector_state *old_conn_state =
 		to_intel_digital_connector_state(old_state);
 	struct drm_crtc_state *crtc_state;
-
-	if (!new_state->crtc)
+	uint64_t old_cp = old_conn_state->base.content_protection;
+	uint64_t new_cp = new_state->content_protection;
+
+	if (!new_state->crtc) {
+		/* 
+		 * If the connector is being disabled with CP enabled, mark it
+		 * desired so it's re-enabled when the connector is brought back
+		 */
+		if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
+			new_state->content_protection =
+				DRM_MODE_CONTENT_PROTECTION_DESIRED;
 		return 0;
+	}
 
 	crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc);
 
+	if (new_cp != old_cp) {
+		/* Only drivers can set content protection enabled */
+		if (new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
+			new_state->content_protection =
+				DRM_MODE_CONTENT_PROTECTION_DESIRED;
+
+		/* Involve the encoder/connector to enable/disable CP */
+		if (new_cp == DRM_MODE_CONTENT_PROTECTION_OFF ||
+		    old_cp == DRM_MODE_CONTENT_PROTECTION_OFF)
+			crtc_state->mode_changed = true;
+	}
+
 	/*
 	 * These properties are handled by fastset, and might not end
 	 * up in a modeset.
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index 933c18fd4258..0e69337f410d 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -2432,10 +2432,17 @@ static void intel_enable_ddi(struct intel_encoder *encoder,
 			     const struct intel_crtc_state *crtc_state,
 			     const struct drm_connector_state *conn_state)
 {
+	struct drm_connector *connector = conn_state->connector;
+	struct intel_connector *intel_connector = to_intel_connector(connector);
+
 	if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
 		intel_enable_ddi_hdmi(encoder, crtc_state, conn_state);
 	else
 		intel_enable_ddi_dp(encoder, crtc_state, conn_state);
+
+	if (conn_state->content_protection ==
+			DRM_MODE_CONTENT_PROTECTION_DESIRED)
+		intel_hdcp_enable(intel_connector);
 }
 
 static void intel_disable_ddi_dp(struct intel_encoder *encoder,
@@ -2468,10 +2475,17 @@ static void intel_disable_ddi(struct intel_encoder *encoder,
 			      const struct intel_crtc_state *old_crtc_state,
 			      const struct drm_connector_state *old_conn_state)
 {
+	struct drm_connector *connector = old_conn_state->connector;
+	struct intel_connector *intel_connector = to_intel_connector(connector);
+
 	if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
 		intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state);
 	else
 		intel_disable_ddi_dp(encoder, old_crtc_state, old_conn_state);
+
+	if (old_conn_state->content_protection !=
+			DRM_MODE_CONTENT_PROTECTION_OFF)
+		intel_hdcp_disable(intel_connector);
 }
 
 static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder,
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 47d022d48718..8924004575b8 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -299,6 +299,49 @@ struct intel_panel {
 	} backlight;
 };
 
+struct intel_hdcp_shim {
+	/* Outputs the transmitter's An and Aksv values to the receiver. */
+	int (*write_an_aksv)(struct intel_digital_port *intel_dig_port, u8 *an);
+
+	/* Reads the receiver's key selection vector */
+	int (*read_bksv)(struct intel_digital_port *intel_dig_port, u8 *bksv);
+
+	/*
+	 * Reads BINFO from DP receivers and BSTATUS from HDMI receivers. The
+	 * definitions are the same in the respective specs, but the names are
+	 * different. Call it BSTATUS since that's the name the HDMI spec
+	 * uses and it was there first.
+	 */
+	int (*read_bstatus)(struct intel_digital_port *intel_dig_port,
+			    u8 *bstatus);
+
+	/* Determines whether a repeater is present downstream */
+	int (*repeater_present)(struct intel_digital_port *intel_dig_port,
+				bool *repeater_present);
+
+	/* Reads the receiver's Ri' value */
+	int (*read_ri_prime)(struct intel_digital_port *intel_dig_port, u8 *ri);
+
+	/* Determines if the receiver's KSV FIFO is ready for consumption */
+	int (*read_ksv_ready)(struct intel_digital_port *intel_dig_port,
+			      bool *ksv_ready);
+
+	/* Reads the ksv fifo for num_downstream devices */
+	int (*read_ksv_fifo)(struct intel_digital_port *intel_dig_port,
+			     int num_downstream, u8 *ksv_fifo);
+
+	/* Reads a 32-bit part of V' from the receiver */
+	int (*read_v_prime_part)(struct intel_digital_port *intel_dig_port,
+				 int i, u32 *part);
+
+	/* Enables HDCP signalling on the port */
+	int (*toggle_signalling)(struct intel_digital_port *intel_dig_port,
+				 bool enable);
+
+	/* Ensures the link is still protected */
+	bool (*check_link)(struct intel_digital_port *intel_dig_port);
+};
+
 struct intel_connector {
 	struct drm_connector base;
 	/*
@@ -330,6 +373,9 @@ struct intel_connector {
 
 	/* Work struct to schedule a uevent on link train failure */
 	struct work_struct modeset_retry_work;
+
+	const struct intel_hdcp_shim *hdcp_shim;
+	struct delayed_work hdcp_work;
 };
 
 struct intel_digital_connector_state {
@@ -1295,6 +1341,8 @@ void intel_ddi_set_vc_payload_alloc(const struct intel_crtc_state *crtc_state,
 				    bool state);
 u32 bxt_signal_levels(struct intel_dp *intel_dp);
 uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
+int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder);
+int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder);
 u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
 
 unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
@@ -1746,6 +1794,11 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
 }
 #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
 
+/* intel_hdcp.c */
+int intel_hdcp_enable(struct intel_connector *connector);
+int intel_hdcp_disable(struct intel_connector *connector);
+int intel_hdcp_check_link(struct intel_connector *connector);
+void intel_hdcp_work(struct work_struct *work);
 
 /* intel_psr.c */
 void intel_psr_enable(struct intel_dp *intel_dp,
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
new file mode 100644
index 000000000000..a2a575ed657e
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -0,0 +1,636 @@
+/*
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_hdcp.h>
+#include <linux/i2c.h>
+#include <linux/random.h>
+
+#include "intel_drv.h"
+#include "i915_reg.h"
+
+#define KEY_LOAD_TRIES	5
+
+static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
+				    const struct intel_hdcp_shim *shim)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies_timeout(500);
+	int ret;
+	bool ksv_ready;
+
+	while (true) {
+		ret = shim->read_ksv_ready(intel_dig_port, &ksv_ready);
+		if (ret)
+			return ret;
+		if (ksv_ready)
+			break;
+		if (time_after(jiffies, timeout))
+			return -ETIMEDOUT;
+		msleep(100);
+	}
+	return 0;
+}
+
+static void intel_hdcp_clear_keys(struct drm_i915_private *dev_priv)
+{
+	I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_CLEAR_KEYS_TRIGGER);
+	I915_WRITE(SKL_HDCP_KEY_STATUS,
+		   SKL_HDCP_KEY_LOAD_DONE | SKL_HDCP_KEY_LOAD_STATUS |
+		   SKL_HDCP_FUSE_IN_PROGRESS | SKL_HDCP_FUSE_ERROR |
+		   SKL_HDCP_FUSE_DONE);
+}
+
+static int intel_hdcp_load_keys(struct drm_i915_private *dev_priv)
+{
+	unsigned long timeout;
+	int ret;
+	u32 val;
+
+	// Initiate loading the HDCP key from fuses
+	mutex_lock(&dev_priv->pcu_lock);
+	ret = sandybridge_pcode_write(dev_priv, SKL_PCODE_LOAD_HDCP_KEYS, 1);
+	mutex_unlock(&dev_priv->pcu_lock);
+	if (ret) {
+		DRM_ERROR("Failed to initiate HDCP key load (%d)\n", ret);
+		return ret;
+	}
+
+	// Wait for the keys to load (500us)
+	timeout = jiffies + nsecs_to_jiffies_timeout(500 * 1000);
+	while (true) {
+		val = I915_READ(SKL_HDCP_KEY_STATUS);
+		if (val & SKL_HDCP_KEY_LOAD_DONE)
+			break;
+		if (time_after(jiffies, timeout))
+			return -ETIMEDOUT;
+		usleep_range(50, 100);
+	}
+	if (!(val & SKL_HDCP_KEY_LOAD_STATUS))
+		return -ENXIO;
+
+	// Send Aksv over to PCH display for use in authentication
+	I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_AKSV_SEND_TRIGGER);
+
+	return 0;
+}
+
+/* Returns updated SHA-1 index */
+static int intel_write_sha_text(struct drm_i915_private *dev_priv, u32 sha_text)
+{
+	I915_WRITE(SKL_HDCP_SHA_TEXT, sha_text);
+	if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_READY, 1)) {
+		DRM_ERROR("Timed out waiting for SHA1 ready\n");
+		return -ETIMEDOUT;
+	}
+	return 0;
+}
+
+static
+u32 intel_hdcp_get_repeater_ctl(struct intel_digital_port *intel_dig_port)
+{
+	enum port port = intel_dig_port->port;
+	switch(port) {
+	case PORT_A:
+		return SKL_HDCP_DDIA_REP_PRESENT | SKL_HDCP_DDIA_SHA1_M0;
+	case PORT_B:
+		return SKL_HDCP_DDIB_REP_PRESENT | SKL_HDCP_DDIB_SHA1_M0;
+	case PORT_C:
+		return SKL_HDCP_DDIC_REP_PRESENT | SKL_HDCP_DDIC_SHA1_M0;
+	case PORT_D:
+		return SKL_HDCP_DDID_REP_PRESENT | SKL_HDCP_DDID_SHA1_M0;
+	case PORT_E:
+		return SKL_HDCP_DDIE_REP_PRESENT | SKL_HDCP_DDIE_SHA1_M0;
+	default:
+		break;
+	}
+	DRM_ERROR("Unknown port %d\n", port);
+	return -EINVAL;
+}
+
+/* Implements Part 2 of the HDCP authorization procedure */
+static
+int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
+			       const struct intel_hdcp_shim *shim)
+{
+	struct drm_i915_private *dev_priv;
+	u32 vprime, sha_text, sha_leftovers, rep_ctl;
+	u8 bstatus[2], num_downstream, *ksv_fifo;
+	int ret, i, j, sha_idx;
+
+	dev_priv = intel_dig_port->base.base.dev->dev_private;
+
+	ret = shim->read_bstatus(intel_dig_port, bstatus);
+	if (ret)
+		return ret;
+
+	/* If there are no downstream devices, we're all done. */
+	num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
+	if (num_downstream == 0) {
+		DRM_INFO("HDCP is enabled (no downstream devices)\n");
+		return 0;
+	}
+
+	// Poll for ksv list ready (spec says max time allowed is 5s)
+	ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim);
+	if (ret) {
+		DRM_ERROR("KSV list failed to become ready (%d)\n", ret);
+		return ret;
+	}
+
+	ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL);
+	if (!ksv_fifo)
+		return -ENOMEM;
+
+	ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo);
+	if (ret)
+		return ret;
+
+	// Process V' values from the receiver
+	for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
+		ret = shim->read_v_prime_part(intel_dig_port, i, &vprime);
+		if (ret)
+			return ret;
+		I915_WRITE(SKL_HDCP_SHA_V_PRIME(i), vprime);
+	}
+
+	/*
+	 * We need to write the concatenation of all device KSVs, BINFO (DP) ||
+	 * BSTATUS (HDMI), and M0 (which is added via HDCP_REP_CTL). This byte
+	 * stream is written via the HDCP_SHA_TEXT register in 32-bit
+	 * increments. Every 64 bytes, we need to write HDCP_REP_CTL again. This
+	 * index will keep track of our progress through the 64 bytes as well as
+	 * helping us work the 40-bit KSVs through our 32-bit register.
+	 *
+	 * NOTE: data passed via HDCP_SHA_TEXT should be big-endian
+	 */
+	sha_idx = 0;
+	sha_text = 0;
+	sha_leftovers = 0;
+	rep_ctl = intel_hdcp_get_repeater_ctl(intel_dig_port);
+	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
+	for (i = 0; i < num_downstream; i++) {
+		unsigned sha_empty;
+		u8 *ksv = &ksv_fifo[i * DRM_HDCP_KSV_LEN];
+
+		// Fill up the empty slots in sha_text and write it out
+		sha_empty = sizeof(sha_text) - sha_leftovers;
+		for (j = 0; j < sha_empty; j++)
+			sha_text |= ksv[j] << ((sizeof(sha_text) - j - 1) * 8);
+
+		ret = intel_write_sha_text(dev_priv, sha_text);
+		if (ret < 0)
+			return ret;
+
+		// Programming guide writes this every 64 bytes
+		sha_idx += sizeof(sha_text);
+		if (!(sha_idx % 64))
+			I915_WRITE(SKL_HDCP_REP_CTL,
+				   rep_ctl | SKL_HDCP_SHA1_TEXT_32);
+
+		// Store the leftover bytes from the ksv in sha_text
+		sha_leftovers = DRM_HDCP_KSV_LEN - sha_empty;
+		sha_text = 0;
+		for (j = 0; j < sha_leftovers; j++)
+			sha_text |= ksv[sha_empty + j] <<
+					((sizeof(sha_text) - j - 1) * 8);
+
+		/*
+		 * If we still have room in sha_text for more data, continue.
+		 * Otherwise, write it out immediately.
+		 */
+		if (sizeof(sha_text) > sha_leftovers)
+			continue;
+
+		ret = intel_write_sha_text(dev_priv, sha_text);
+		if (ret < 0)
+			return ret;
+		sha_leftovers = 0;
+		sha_text = 0;
+		sha_idx += sizeof(sha_text);
+	}
+
+	/*
+	 * We need to write BINFO/BSTATUS, and M0 now. Depending on how many
+	 * bytes are leftover from the last ksv, we might be able to fit them
+	 * all in sha_text (first 2 cases), or we might need to split them up
+	 * into 2 writes (last 2 cases).
+	 */
+	if (sha_leftovers == 0) {
+		// Write 16 bits of text, 16 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
+		ret = intel_write_sha_text(dev_priv,
+					   bstatus[0] << 8 | bstatus[1]);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+		// Write 32 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
+		ret = intel_write_sha_text(dev_priv, 0);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+		// Write 16 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
+		ret = intel_write_sha_text(dev_priv, 0);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+	} else if (sha_leftovers == 1) {
+		// Write 24 bits of text, 8 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
+		sha_text |= bstatus[0] << 16 | bstatus[1] << 8;
+		// Only 24-bits of data, must be in the LSB
+		sha_text = (sha_text & 0xffffff00) >> 8;
+		ret = intel_write_sha_text(dev_priv, sha_text);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+		// Write 32 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
+		ret = intel_write_sha_text(dev_priv, 0);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+		// Write 24 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
+		ret = intel_write_sha_text(dev_priv, 0);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+	} else if (sha_leftovers == 2) {
+		// Write 32 bits of text
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
+		sha_text |= bstatus[0] << 24 | bstatus[1] << 16;
+		ret = intel_write_sha_text(dev_priv, sha_text);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+		// Write 64 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
+		for (i = 0; i < 2; i++) {
+			ret = intel_write_sha_text(dev_priv, 0);
+			if (ret < 0)
+				return ret;
+			sha_idx += sizeof(sha_text);
+		}
+	} else if (sha_leftovers == 3) {
+		// Write 32 bits of text
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
+		sha_text |= bstatus[0] << 24;
+		ret = intel_write_sha_text(dev_priv, sha_text);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+		// Write 8 bits of text, 24 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
+		ret = intel_write_sha_text(dev_priv, bstatus[1]);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+		// Write 32 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
+		ret = intel_write_sha_text(dev_priv, 0);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+		// Write 8 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
+		ret = intel_write_sha_text(dev_priv, 0);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+	} else {
+		DRM_ERROR("Invalid number of leftovers %d\n", sha_leftovers);
+		return -EINVAL;
+	}
+
+	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
+	// Fill up to 64 - 4 bytes with zeros (leave the last write for length)
+	while ((sha_idx % 64) < (64 - sizeof(sha_text))) {
+		ret = intel_write_sha_text(dev_priv, 0);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+	}
+
+	/*
+	 * Last write gets the length of the concatenation in bits. That is:
+	 *  - 5 bytes per device
+	 *  - 10 bytes for BINFO/BSTATUS(2), M0(8)
+	 */
+	sha_text = (num_downstream * 5 + 10) * 8;
+	ret = intel_write_sha_text(dev_priv, sha_text);
+	if (ret < 0)
+		return ret;
+
+	// Finally, tell the HW we're done with the hash and wait for it to ACK
+	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_COMPLETE_HASH);
+	if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_COMPLETE, 1)) {
+		DRM_ERROR("Timed out waiting for SHA1 complete\n");
+		return -ETIMEDOUT;
+	}
+	if (!(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_V_MATCH)) {
+		DRM_ERROR("SHA-1 mismatch, HDCP failed\n");
+		return -ENXIO;
+	}
+
+	DRM_INFO("HDCP is enabled (%d downstream devices)\n", num_downstream);
+	return 0;
+}
+
+/* Implements Part 1 of the HDCP authorization procedure */
+static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
+			   const struct intel_hdcp_shim *shim)
+{
+	struct drm_i915_private *dev_priv;
+	enum port port;
+	unsigned long r0_prime_gen_start;
+	int ret, i;
+	union {
+		u32 reg[2];
+		u8 shim[DRM_HDCP_AN_LEN];
+	} an;
+	union {
+		u32 reg[2];
+		u8 shim[DRM_HDCP_KSV_LEN];
+	} bksv;
+	union {
+		u32 reg;
+		u8 shim[DRM_HDCP_RI_LEN];
+	} ri;
+	bool repeater_present;
+
+	dev_priv = intel_dig_port->base.base.dev->dev_private;
+
+	port = intel_dig_port->port;
+
+	// Initialize An with 2 random values and acquire it
+	for (i = 0; i < 2; i++)
+		I915_WRITE(SKL_PORT_HDCP_ANINIT(port), get_random_long());
+	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_CAPTURE_AN);
+
+	// Wait for An to be acquired
+	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
+		     SKL_HDCP_STATUS_AN_READY, 1)) {
+		DRM_ERROR("Timed out waiting for An\n");
+		return -ETIMEDOUT;
+	}
+
+	an.reg[0] = I915_READ(SKL_PORT_HDCP_ANLO(port));
+	an.reg[1] = I915_READ(SKL_PORT_HDCP_ANHI(port));
+	ret = shim->write_an_aksv(intel_dig_port, an.shim);
+	if (ret)
+		return ret;
+
+	r0_prime_gen_start = jiffies;
+
+	memset(&bksv, 0, sizeof(bksv));
+	ret = shim->read_bksv(intel_dig_port, bksv.shim);
+	if (ret)
+		return ret;
+
+	I915_WRITE(SKL_PORT_HDCP_BKSVLO(port), bksv.reg[0]);
+	I915_WRITE(SKL_PORT_HDCP_BKSVHI(port), bksv.reg[1]);
+
+	ret = shim->repeater_present(intel_dig_port, &repeater_present);
+	if (ret)
+		return ret;
+	if (repeater_present)
+		I915_WRITE(SKL_HDCP_REP_CTL,
+			   intel_hdcp_get_repeater_ctl(intel_dig_port));
+
+	ret = shim->toggle_signalling(intel_dig_port, true);
+	if (ret)
+		return ret;
+
+	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_AUTH_AND_ENC);
+
+	// Wait for R0 ready
+	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
+		     (SKL_HDCP_STATUS_R0_READY | SKL_HDCP_STATUS_ENC), 1)) {
+		DRM_ERROR("Timed out waiting for R0 ready\n");
+		return -ETIMEDOUT;
+	}
+
+	/*
+	 * Wait for R0' to become available, the spec says 100ms from Aksv
+	 * write. On DP, there's an R0_READY bit available but no such bit
+	 * exists on HDMI. Since the upper-bound is the same, we'll just do
+	 * the stupid thing instead of polling on one and not the other.
+	 */
+	wait_remaining_ms_from_jiffies(r0_prime_gen_start, 100);
+
+	ri.reg = 0;
+	ret = shim->read_ri_prime(intel_dig_port, ri.shim);
+	if (ret)
+		return ret;
+	I915_WRITE(SKL_PORT_HDCP_RPRIME(port), ri.reg);
+
+	// Wait for Ri prime match
+	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
+		     (SKL_HDCP_STATUS_RI_MATCH | SKL_HDCP_STATUS_ENC), 1)) {
+		DRM_ERROR("Timed out waiting for Ri prime match (%x)\n",
+			  I915_READ(SKL_PORT_HDCP_STATUS(port)));
+		return -ETIMEDOUT;
+	}
+
+	// Wait for encryption confirmation
+	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
+		      SKL_HDCP_STATUS_ENC, 20)) {
+		DRM_ERROR("Timed out waiting for encryption\n");
+		return -ETIMEDOUT;
+	}
+
+	/*
+	 * XXX: If we have MST-connected devices, we need to enable encryption
+	 * on those as well.
+	 */
+
+	return intel_hdcp_auth_downstream(intel_dig_port, shim);
+}
+
+static
+struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector)
+{
+	return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base);
+}
+
+static int _intel_hdcp_disable(struct intel_connector *connector)
+{
+	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
+	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+	enum port port = intel_dig_port->port;
+	int ret;
+
+	I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
+	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) == 0, 20)) {
+		DRM_ERROR("Failed to disable HDCP, timeout clearing status\n");
+		return -ETIMEDOUT;
+	}
+
+	intel_hdcp_clear_keys(dev_priv);
+
+	ret = connector->hdcp_shim->toggle_signalling(intel_dig_port, false);
+	if (ret) {
+		DRM_ERROR("Failed to disable HDCP signalling\n");
+		return ret;
+	}
+
+	DRM_INFO("HDCP is disabled\n");
+	return 0;
+}
+
+static int _intel_hdcp_enable(struct intel_connector *connector)
+{
+	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
+	int i, ret;
+
+	if (!(I915_READ(SKL_FUSE_STATUS) & SKL_FUSE_PG_DIST_STATUS(1))) {
+		DRM_ERROR("PG1 is disabled, cannot load keys\n");
+		return -ENXIO;
+	}
+
+	for (i = 0; i < KEY_LOAD_TRIES; i++) {
+		ret = intel_hdcp_load_keys(dev_priv);
+		if (!ret)
+			break;
+		intel_hdcp_clear_keys(dev_priv);
+	}
+	if (ret) {
+		DRM_ERROR("Could not load HDCP keys, (%d)\n", ret);
+		return ret;
+	}
+
+	ret = intel_hdcp_auth(conn_to_dig_port(connector),
+			      connector->hdcp_shim);
+	if (ret) {
+		DRM_ERROR("Failed to authenticate HDCP (%d)\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+void intel_hdcp_work(struct work_struct *work)
+{
+	struct intel_connector *connector = container_of(to_delayed_work(work),
+							 struct intel_connector,
+						         hdcp_work);
+	struct drm_device *dev = connector->base.dev;
+	int ret;
+
+	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+
+	ret = intel_hdcp_check_link(connector);
+	if (!ret)
+		schedule_delayed_work(&connector->hdcp_work,
+				      DRM_HDCP_CHECK_PERIOD_MS);
+
+	drm_modeset_unlock(&dev->mode_config.connection_mutex);
+}
+
+int intel_hdcp_enable(struct intel_connector *connector)
+{
+	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
+	struct drm_device *dev = &dev_priv->drm;
+	struct drm_connector_state *state = connector->base.state;
+	int ret;
+
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+
+	if (!connector->hdcp_shim)
+		return -ENOENT;
+
+	ret = _intel_hdcp_enable(connector);
+	if (ret)
+		return ret;
+
+	state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+
+	schedule_delayed_work(&connector->hdcp_work, DRM_HDCP_CHECK_PERIOD_MS);
+	return 0;
+}
+
+int intel_hdcp_disable(struct intel_connector *connector)
+{
+	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
+	struct drm_device *dev = &dev_priv->drm;
+
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+
+	if (!connector->hdcp_shim)
+		return -ENOENT;
+
+	cancel_delayed_work(&connector->hdcp_work);
+
+	return _intel_hdcp_disable(connector);
+}
+
+/* Implements Part 3 of the HDCP authorization procedure */
+int intel_hdcp_check_link(struct intel_connector *connector)
+{
+	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
+	struct drm_device *dev = &dev_priv->drm;
+	struct drm_connector_state *state = connector->base.state;
+	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+	enum port port = intel_dig_port->port;
+	int ret;
+
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+
+	if (state->content_protection != DRM_MODE_CONTENT_PROTECTION_ENABLED)
+		return 0;
+
+	if (!connector->hdcp_shim)
+		return -ENOENT;
+
+	if (!(I915_READ(SKL_PORT_HDCP_STATUS(port)) & SKL_HDCP_STATUS_ENC)) {
+		DRM_ERROR("HDCP check failed: link is not encrypted, %x\n",
+			   I915_READ(SKL_PORT_HDCP_STATUS(port)));
+		ret = -ENXIO;
+		goto fail;
+	}
+
+	if (connector->hdcp_shim->check_link(intel_dig_port))
+		return 0;
+
+	DRM_INFO("HDCP link failed, retrying authentication\n");
+
+	ret = _intel_hdcp_disable(connector);
+	if (ret) {
+		DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
+		goto fail;
+	}
+
+	ret = _intel_hdcp_enable(connector);
+	if (ret) {
+		DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+	return ret;
+}
-- 
2.15.0.531.g2ccb3012c9-goog

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [RFC PATCH 3/6] drm/i915: Add HDCP framework + base implementation
@ 2017-11-30  3:08   ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-11-30  3:08 UTC (permalink / raw)
  To: dri-devel, intel-gfx; +Cc: David Airlie, linux-kernel, Rodrigo Vivi

This patch adds the framework required to add HDCP support to intel
connectors. It implements Aksv loading from fuse, and parts 1/2/3
of the HDCP authentication scheme.

Note that without shim implementations, this does not actually implement
HDCP. That will come in subsequent patches.

Signed-off-by: Sean Paul <seanpaul@chromium.org>
---
 drivers/gpu/drm/i915/Makefile       |   1 +
 drivers/gpu/drm/i915/i915_reg.h     |  83 +++++
 drivers/gpu/drm/i915/intel_atomic.c |  26 +-
 drivers/gpu/drm/i915/intel_ddi.c    |  14 +
 drivers/gpu/drm/i915/intel_drv.h    |  53 +++
 drivers/gpu/drm/i915/intel_hdcp.c   | 636 ++++++++++++++++++++++++++++++++++++
 6 files changed, 811 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c

diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 6c3b0481ef82..1e745508e437 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -87,6 +87,7 @@ i915-y += intel_audio.o \
 	  intel_fbc.o \
 	  intel_fifo_underrun.o \
 	  intel_frontbuffer.o \
+	  intel_hdcp.o \
 	  intel_hotplug.o \
 	  intel_modes.o \
 	  intel_overlay.o \
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 68a58cce6ab1..43128030171d 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -7991,6 +7991,7 @@ enum {
 #define     GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT	8
 #define     GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT	16
 #define     GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT	24
+#define   SKL_PCODE_LOAD_HDCP_KEYS		0x5
 #define   SKL_PCODE_CDCLK_CONTROL		0x7
 #define     SKL_CDCLK_PREPARE_FOR_CHANGE	0x3
 #define     SKL_CDCLK_READY_FOR_CHANGE		0x1
@@ -8285,6 +8286,88 @@ enum skl_power_gate {
 #define  SKL_PW_TO_PG(pw)			((pw) - SKL_DISP_PW_1 + SKL_PG1)
 #define  SKL_FUSE_PG_DIST_STATUS(pg)		(1 << (27 - (pg)))
 
+
+/* HDCP Key Registers */
+#define SKL_HDCP_KEY_CONF		_MMIO(0x66c00)
+#define	 SKL_HDCP_AKSV_SEND_TRIGGER	BIT(31)
+#define  SKL_HDCP_CLEAR_KEYS_TRIGGER	BIT(30)
+#define SKL_HDCP_KEY_STATUS		_MMIO(0x66c04)
+#define  SKL_HDCP_FUSE_IN_PROGRESS	BIT(7)
+#define  SKL_HDCP_FUSE_ERROR		BIT(6)
+#define  SKL_HDCP_FUSE_DONE		BIT(5)
+#define  SKL_HDCP_KEY_LOAD_STATUS	BIT(1)
+#define  SKL_HDCP_KEY_LOAD_DONE		BIT(0)
+#define SKL_HDCP_AKSV_LO		_MMIO(0x66c10)
+#define SKL_HDCP_AKSV_HI		_MMIO(0x66c14)
+
+/* HDCP Repeater Registers */
+#define SKL_HDCP_REP_CTL		_MMIO(0x66d00)
+#define  SKL_HDCP_DDIB_REP_PRESENT	BIT(30)
+#define  SKL_HDCP_DDIA_REP_PRESENT	BIT(29)
+#define  SKL_HDCP_DDIC_REP_PRESENT	BIT(28)
+#define  SKL_HDCP_DDID_REP_PRESENT	BIT(27)
+#define  SKL_HDCP_DDIF_REP_PRESENT	BIT(26)
+#define  SKL_HDCP_DDIE_REP_PRESENT	BIT(25)
+#define  SKL_HDCP_DDIB_SHA1_M0		(1 << 20)
+#define  SKL_HDCP_DDIA_SHA1_M0		(2 << 20)
+#define  SKL_HDCP_DDIC_SHA1_M0		(3 << 20)
+#define  SKL_HDCP_DDID_SHA1_M0		(4 << 20)
+#define  SKL_HDCP_DDIF_SHA1_M0		(5 << 20)
+#define  SKL_HDCP_DDIE_SHA1_M0		(6 << 20) // Bspec says 5?
+#define  SKL_HDCP_SHA1_BUSY		BIT(16)
+#define  SKL_HDCP_SHA1_READY		BIT(17)
+#define  SKL_HDCP_SHA1_COMPLETE		BIT(18)
+#define  SKL_HDCP_SHA1_V_MATCH		BIT(19)
+#define  SKL_HDCP_SHA1_TEXT_32		(1 << 1)
+#define  SKL_HDCP_SHA1_COMPLETE_HASH	(2 << 1)
+#define  SKL_HDCP_SHA1_TEXT_24		(4 << 1)
+#define  SKL_HDCP_SHA1_TEXT_16		(5 << 1)
+#define  SKL_HDCP_SHA1_TEXT_8		(6 << 1)
+#define  SKL_HDCP_SHA1_TEXT_0		(7 << 1)
+#define SKL_HDCP_SHA_V_PRIME_H0		_MMIO(0x66d04)
+#define SKL_HDCP_SHA_V_PRIME_H1		_MMIO(0x66d08)
+#define SKL_HDCP_SHA_V_PRIME_H2		_MMIO(0x66d0C)
+#define SKL_HDCP_SHA_V_PRIME_H3		_MMIO(0x66d10)
+#define SKL_HDCP_SHA_V_PRIME_H4		_MMIO(0x66d14)
+#define SKL_HDCP_SHA_V_PRIME(h)		_MMIO((0x66d04 + h * 4))
+#define SKL_HDCP_SHA_TEXT		_MMIO(0x66d18)
+
+/* HDCP Auth Registers */
+#define _SKL_PORTA_HDCP_AUTHENC		0x66800
+#define _SKL_PORTB_HDCP_AUTHENC		0x66500
+#define _SKL_PORTC_HDCP_AUTHENC		0x66600
+#define _SKL_PORTD_HDCP_AUTHENC		0x66700
+#define _SKL_PORTE_HDCP_AUTHENC		0x66A00
+#define _SKL_PORTF_HDCP_AUTHENC		0x66900
+#define _SKL_PORT_HDCP_AUTHENC(port, x)	_MMIO(_PICK(port, \
+					  _SKL_PORTA_HDCP_AUTHENC, \
+					  _SKL_PORTB_HDCP_AUTHENC, \
+					  _SKL_PORTC_HDCP_AUTHENC, \
+					  _SKL_PORTD_HDCP_AUTHENC, \
+					  _SKL_PORTE_HDCP_AUTHENC, \
+					  _SKL_PORTF_HDCP_AUTHENC) + x)
+#define SKL_PORT_HDCP_CONF(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x0)
+#define  SKL_HDCP_CONF_CAPTURE_AN	BIT(0)
+#define  SKL_HDCP_CONF_AUTH_AND_ENC	(BIT(1) | BIT(0))
+#define SKL_PORT_HDCP_ANINIT(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x4)
+#define SKL_PORT_HDCP_ANLO(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x8)
+#define SKL_PORT_HDCP_ANHI(port)	_SKL_PORT_HDCP_AUTHENC(port, 0xC)
+#define SKL_PORT_HDCP_BKSVLO(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x10)
+#define SKL_PORT_HDCP_BKSVHI(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x14)
+#define SKL_PORT_HDCP_RPRIME(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x18)
+#define SKL_PORT_HDCP_STATUS(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x1C)
+#define  SKL_HDCP_STATUS_STREAM_A_ENC	BIT(31)
+#define  SKL_HDCP_STATUS_STREAM_B_ENC	BIT(30)
+#define  SKL_HDCP_STATUS_STREAM_C_ENC	BIT(29)
+#define  SKL_HDCP_STATUS_STREAM_D_ENC	BIT(28)
+#define  SKL_HDCP_STATUS_AUTH		BIT(21)
+#define  SKL_HDCP_STATUS_ENC		BIT(20)
+#define  SKL_HDCP_STATUS_RI_MATCH	BIT(19)
+#define  SKL_HDCP_STATUS_R0_READY	BIT(18)
+#define  SKL_HDCP_STATUS_AN_READY	BIT(17)
+#define  SKL_HDCP_STATUS_CIPHER		BIT(16)
+#define  SKL_HDCP_STATUS_FRAME_CNT(x)	((x >> 8) & 0xff)
+
 /* Per-pipe DDI Function Control */
 #define _TRANS_DDI_FUNC_CTL_A		0x60400
 #define _TRANS_DDI_FUNC_CTL_B		0x61400
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
index 36d4e635e4ce..ddf08227d9cb 100644
--- a/drivers/gpu/drm/i915/intel_atomic.c
+++ b/drivers/gpu/drm/i915/intel_atomic.c
@@ -109,12 +109,34 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
 	struct intel_digital_connector_state *old_conn_state =
 		to_intel_digital_connector_state(old_state);
 	struct drm_crtc_state *crtc_state;
-
-	if (!new_state->crtc)
+	uint64_t old_cp = old_conn_state->base.content_protection;
+	uint64_t new_cp = new_state->content_protection;
+
+	if (!new_state->crtc) {
+		/* 
+		 * If the connector is being disabled with CP enabled, mark it
+		 * desired so it's re-enabled when the connector is brought back
+		 */
+		if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
+			new_state->content_protection =
+				DRM_MODE_CONTENT_PROTECTION_DESIRED;
 		return 0;
+	}
 
 	crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc);
 
+	if (new_cp != old_cp) {
+		/* Only drivers can set content protection enabled */
+		if (new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
+			new_state->content_protection =
+				DRM_MODE_CONTENT_PROTECTION_DESIRED;
+
+		/* Involve the encoder/connector to enable/disable CP */
+		if (new_cp == DRM_MODE_CONTENT_PROTECTION_OFF ||
+		    old_cp == DRM_MODE_CONTENT_PROTECTION_OFF)
+			crtc_state->mode_changed = true;
+	}
+
 	/*
 	 * These properties are handled by fastset, and might not end
 	 * up in a modeset.
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index 933c18fd4258..0e69337f410d 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -2432,10 +2432,17 @@ static void intel_enable_ddi(struct intel_encoder *encoder,
 			     const struct intel_crtc_state *crtc_state,
 			     const struct drm_connector_state *conn_state)
 {
+	struct drm_connector *connector = conn_state->connector;
+	struct intel_connector *intel_connector = to_intel_connector(connector);
+
 	if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
 		intel_enable_ddi_hdmi(encoder, crtc_state, conn_state);
 	else
 		intel_enable_ddi_dp(encoder, crtc_state, conn_state);
+
+	if (conn_state->content_protection ==
+			DRM_MODE_CONTENT_PROTECTION_DESIRED)
+		intel_hdcp_enable(intel_connector);
 }
 
 static void intel_disable_ddi_dp(struct intel_encoder *encoder,
@@ -2468,10 +2475,17 @@ static void intel_disable_ddi(struct intel_encoder *encoder,
 			      const struct intel_crtc_state *old_crtc_state,
 			      const struct drm_connector_state *old_conn_state)
 {
+	struct drm_connector *connector = old_conn_state->connector;
+	struct intel_connector *intel_connector = to_intel_connector(connector);
+
 	if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
 		intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state);
 	else
 		intel_disable_ddi_dp(encoder, old_crtc_state, old_conn_state);
+
+	if (old_conn_state->content_protection !=
+			DRM_MODE_CONTENT_PROTECTION_OFF)
+		intel_hdcp_disable(intel_connector);
 }
 
 static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder,
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 47d022d48718..8924004575b8 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -299,6 +299,49 @@ struct intel_panel {
 	} backlight;
 };
 
+struct intel_hdcp_shim {
+	/* Outputs the transmitter's An and Aksv values to the receiver. */
+	int (*write_an_aksv)(struct intel_digital_port *intel_dig_port, u8 *an);
+
+	/* Reads the receiver's key selection vector */
+	int (*read_bksv)(struct intel_digital_port *intel_dig_port, u8 *bksv);
+
+	/*
+	 * Reads BINFO from DP receivers and BSTATUS from HDMI receivers. The
+	 * definitions are the same in the respective specs, but the names are
+	 * different. Call it BSTATUS since that's the name the HDMI spec
+	 * uses and it was there first.
+	 */
+	int (*read_bstatus)(struct intel_digital_port *intel_dig_port,
+			    u8 *bstatus);
+
+	/* Determines whether a repeater is present downstream */
+	int (*repeater_present)(struct intel_digital_port *intel_dig_port,
+				bool *repeater_present);
+
+	/* Reads the receiver's Ri' value */
+	int (*read_ri_prime)(struct intel_digital_port *intel_dig_port, u8 *ri);
+
+	/* Determines if the receiver's KSV FIFO is ready for consumption */
+	int (*read_ksv_ready)(struct intel_digital_port *intel_dig_port,
+			      bool *ksv_ready);
+
+	/* Reads the ksv fifo for num_downstream devices */
+	int (*read_ksv_fifo)(struct intel_digital_port *intel_dig_port,
+			     int num_downstream, u8 *ksv_fifo);
+
+	/* Reads a 32-bit part of V' from the receiver */
+	int (*read_v_prime_part)(struct intel_digital_port *intel_dig_port,
+				 int i, u32 *part);
+
+	/* Enables HDCP signalling on the port */
+	int (*toggle_signalling)(struct intel_digital_port *intel_dig_port,
+				 bool enable);
+
+	/* Ensures the link is still protected */
+	bool (*check_link)(struct intel_digital_port *intel_dig_port);
+};
+
 struct intel_connector {
 	struct drm_connector base;
 	/*
@@ -330,6 +373,9 @@ struct intel_connector {
 
 	/* Work struct to schedule a uevent on link train failure */
 	struct work_struct modeset_retry_work;
+
+	const struct intel_hdcp_shim *hdcp_shim;
+	struct delayed_work hdcp_work;
 };
 
 struct intel_digital_connector_state {
@@ -1295,6 +1341,8 @@ void intel_ddi_set_vc_payload_alloc(const struct intel_crtc_state *crtc_state,
 				    bool state);
 u32 bxt_signal_levels(struct intel_dp *intel_dp);
 uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
+int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder);
+int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder);
 u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
 
 unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
@@ -1746,6 +1794,11 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
 }
 #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
 
+/* intel_hdcp.c */
+int intel_hdcp_enable(struct intel_connector *connector);
+int intel_hdcp_disable(struct intel_connector *connector);
+int intel_hdcp_check_link(struct intel_connector *connector);
+void intel_hdcp_work(struct work_struct *work);
 
 /* intel_psr.c */
 void intel_psr_enable(struct intel_dp *intel_dp,
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
new file mode 100644
index 000000000000..a2a575ed657e
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -0,0 +1,636 @@
+/*
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_hdcp.h>
+#include <linux/i2c.h>
+#include <linux/random.h>
+
+#include "intel_drv.h"
+#include "i915_reg.h"
+
+#define KEY_LOAD_TRIES	5
+
+static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
+				    const struct intel_hdcp_shim *shim)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies_timeout(500);
+	int ret;
+	bool ksv_ready;
+
+	while (true) {
+		ret = shim->read_ksv_ready(intel_dig_port, &ksv_ready);
+		if (ret)
+			return ret;
+		if (ksv_ready)
+			break;
+		if (time_after(jiffies, timeout))
+			return -ETIMEDOUT;
+		msleep(100);
+	}
+	return 0;
+}
+
+static void intel_hdcp_clear_keys(struct drm_i915_private *dev_priv)
+{
+	I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_CLEAR_KEYS_TRIGGER);
+	I915_WRITE(SKL_HDCP_KEY_STATUS,
+		   SKL_HDCP_KEY_LOAD_DONE | SKL_HDCP_KEY_LOAD_STATUS |
+		   SKL_HDCP_FUSE_IN_PROGRESS | SKL_HDCP_FUSE_ERROR |
+		   SKL_HDCP_FUSE_DONE);
+}
+
+static int intel_hdcp_load_keys(struct drm_i915_private *dev_priv)
+{
+	unsigned long timeout;
+	int ret;
+	u32 val;
+
+	// Initiate loading the HDCP key from fuses
+	mutex_lock(&dev_priv->pcu_lock);
+	ret = sandybridge_pcode_write(dev_priv, SKL_PCODE_LOAD_HDCP_KEYS, 1);
+	mutex_unlock(&dev_priv->pcu_lock);
+	if (ret) {
+		DRM_ERROR("Failed to initiate HDCP key load (%d)\n", ret);
+		return ret;
+	}
+
+	// Wait for the keys to load (500us)
+	timeout = jiffies + nsecs_to_jiffies_timeout(500 * 1000);
+	while (true) {
+		val = I915_READ(SKL_HDCP_KEY_STATUS);
+		if (val & SKL_HDCP_KEY_LOAD_DONE)
+			break;
+		if (time_after(jiffies, timeout))
+			return -ETIMEDOUT;
+		usleep_range(50, 100);
+	}
+	if (!(val & SKL_HDCP_KEY_LOAD_STATUS))
+		return -ENXIO;
+
+	// Send Aksv over to PCH display for use in authentication
+	I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_AKSV_SEND_TRIGGER);
+
+	return 0;
+}
+
+/* Returns updated SHA-1 index */
+static int intel_write_sha_text(struct drm_i915_private *dev_priv, u32 sha_text)
+{
+	I915_WRITE(SKL_HDCP_SHA_TEXT, sha_text);
+	if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_READY, 1)) {
+		DRM_ERROR("Timed out waiting for SHA1 ready\n");
+		return -ETIMEDOUT;
+	}
+	return 0;
+}
+
+static
+u32 intel_hdcp_get_repeater_ctl(struct intel_digital_port *intel_dig_port)
+{
+	enum port port = intel_dig_port->port;
+	switch(port) {
+	case PORT_A:
+		return SKL_HDCP_DDIA_REP_PRESENT | SKL_HDCP_DDIA_SHA1_M0;
+	case PORT_B:
+		return SKL_HDCP_DDIB_REP_PRESENT | SKL_HDCP_DDIB_SHA1_M0;
+	case PORT_C:
+		return SKL_HDCP_DDIC_REP_PRESENT | SKL_HDCP_DDIC_SHA1_M0;
+	case PORT_D:
+		return SKL_HDCP_DDID_REP_PRESENT | SKL_HDCP_DDID_SHA1_M0;
+	case PORT_E:
+		return SKL_HDCP_DDIE_REP_PRESENT | SKL_HDCP_DDIE_SHA1_M0;
+	default:
+		break;
+	}
+	DRM_ERROR("Unknown port %d\n", port);
+	return -EINVAL;
+}
+
+/* Implements Part 2 of the HDCP authorization procedure */
+static
+int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
+			       const struct intel_hdcp_shim *shim)
+{
+	struct drm_i915_private *dev_priv;
+	u32 vprime, sha_text, sha_leftovers, rep_ctl;
+	u8 bstatus[2], num_downstream, *ksv_fifo;
+	int ret, i, j, sha_idx;
+
+	dev_priv = intel_dig_port->base.base.dev->dev_private;
+
+	ret = shim->read_bstatus(intel_dig_port, bstatus);
+	if (ret)
+		return ret;
+
+	/* If there are no downstream devices, we're all done. */
+	num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
+	if (num_downstream == 0) {
+		DRM_INFO("HDCP is enabled (no downstream devices)\n");
+		return 0;
+	}
+
+	// Poll for ksv list ready (spec says max time allowed is 5s)
+	ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim);
+	if (ret) {
+		DRM_ERROR("KSV list failed to become ready (%d)\n", ret);
+		return ret;
+	}
+
+	ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL);
+	if (!ksv_fifo)
+		return -ENOMEM;
+
+	ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo);
+	if (ret)
+		return ret;
+
+	// Process V' values from the receiver
+	for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
+		ret = shim->read_v_prime_part(intel_dig_port, i, &vprime);
+		if (ret)
+			return ret;
+		I915_WRITE(SKL_HDCP_SHA_V_PRIME(i), vprime);
+	}
+
+	/*
+	 * We need to write the concatenation of all device KSVs, BINFO (DP) ||
+	 * BSTATUS (HDMI), and M0 (which is added via HDCP_REP_CTL). This byte
+	 * stream is written via the HDCP_SHA_TEXT register in 32-bit
+	 * increments. Every 64 bytes, we need to write HDCP_REP_CTL again. This
+	 * index will keep track of our progress through the 64 bytes as well as
+	 * helping us work the 40-bit KSVs through our 32-bit register.
+	 *
+	 * NOTE: data passed via HDCP_SHA_TEXT should be big-endian
+	 */
+	sha_idx = 0;
+	sha_text = 0;
+	sha_leftovers = 0;
+	rep_ctl = intel_hdcp_get_repeater_ctl(intel_dig_port);
+	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
+	for (i = 0; i < num_downstream; i++) {
+		unsigned sha_empty;
+		u8 *ksv = &ksv_fifo[i * DRM_HDCP_KSV_LEN];
+
+		// Fill up the empty slots in sha_text and write it out
+		sha_empty = sizeof(sha_text) - sha_leftovers;
+		for (j = 0; j < sha_empty; j++)
+			sha_text |= ksv[j] << ((sizeof(sha_text) - j - 1) * 8);
+
+		ret = intel_write_sha_text(dev_priv, sha_text);
+		if (ret < 0)
+			return ret;
+
+		// Programming guide writes this every 64 bytes
+		sha_idx += sizeof(sha_text);
+		if (!(sha_idx % 64))
+			I915_WRITE(SKL_HDCP_REP_CTL,
+				   rep_ctl | SKL_HDCP_SHA1_TEXT_32);
+
+		// Store the leftover bytes from the ksv in sha_text
+		sha_leftovers = DRM_HDCP_KSV_LEN - sha_empty;
+		sha_text = 0;
+		for (j = 0; j < sha_leftovers; j++)
+			sha_text |= ksv[sha_empty + j] <<
+					((sizeof(sha_text) - j - 1) * 8);
+
+		/*
+		 * If we still have room in sha_text for more data, continue.
+		 * Otherwise, write it out immediately.
+		 */
+		if (sizeof(sha_text) > sha_leftovers)
+			continue;
+
+		ret = intel_write_sha_text(dev_priv, sha_text);
+		if (ret < 0)
+			return ret;
+		sha_leftovers = 0;
+		sha_text = 0;
+		sha_idx += sizeof(sha_text);
+	}
+
+	/*
+	 * We need to write BINFO/BSTATUS, and M0 now. Depending on how many
+	 * bytes are leftover from the last ksv, we might be able to fit them
+	 * all in sha_text (first 2 cases), or we might need to split them up
+	 * into 2 writes (last 2 cases).
+	 */
+	if (sha_leftovers == 0) {
+		// Write 16 bits of text, 16 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
+		ret = intel_write_sha_text(dev_priv,
+					   bstatus[0] << 8 | bstatus[1]);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+		// Write 32 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
+		ret = intel_write_sha_text(dev_priv, 0);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+		// Write 16 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
+		ret = intel_write_sha_text(dev_priv, 0);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+	} else if (sha_leftovers == 1) {
+		// Write 24 bits of text, 8 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
+		sha_text |= bstatus[0] << 16 | bstatus[1] << 8;
+		// Only 24-bits of data, must be in the LSB
+		sha_text = (sha_text & 0xffffff00) >> 8;
+		ret = intel_write_sha_text(dev_priv, sha_text);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+		// Write 32 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
+		ret = intel_write_sha_text(dev_priv, 0);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+		// Write 24 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
+		ret = intel_write_sha_text(dev_priv, 0);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+	} else if (sha_leftovers == 2) {
+		// Write 32 bits of text
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
+		sha_text |= bstatus[0] << 24 | bstatus[1] << 16;
+		ret = intel_write_sha_text(dev_priv, sha_text);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+		// Write 64 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
+		for (i = 0; i < 2; i++) {
+			ret = intel_write_sha_text(dev_priv, 0);
+			if (ret < 0)
+				return ret;
+			sha_idx += sizeof(sha_text);
+		}
+	} else if (sha_leftovers == 3) {
+		// Write 32 bits of text
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
+		sha_text |= bstatus[0] << 24;
+		ret = intel_write_sha_text(dev_priv, sha_text);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+		// Write 8 bits of text, 24 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
+		ret = intel_write_sha_text(dev_priv, bstatus[1]);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+		// Write 32 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
+		ret = intel_write_sha_text(dev_priv, 0);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+
+		// Write 8 bits of M0
+		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
+		ret = intel_write_sha_text(dev_priv, 0);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+	} else {
+		DRM_ERROR("Invalid number of leftovers %d\n", sha_leftovers);
+		return -EINVAL;
+	}
+
+	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
+	// Fill up to 64 - 4 bytes with zeros (leave the last write for length)
+	while ((sha_idx % 64) < (64 - sizeof(sha_text))) {
+		ret = intel_write_sha_text(dev_priv, 0);
+		if (ret < 0)
+			return ret;
+		sha_idx += sizeof(sha_text);
+	}
+
+	/*
+	 * Last write gets the length of the concatenation in bits. That is:
+	 *  - 5 bytes per device
+	 *  - 10 bytes for BINFO/BSTATUS(2), M0(8)
+	 */
+	sha_text = (num_downstream * 5 + 10) * 8;
+	ret = intel_write_sha_text(dev_priv, sha_text);
+	if (ret < 0)
+		return ret;
+
+	// Finally, tell the HW we're done with the hash and wait for it to ACK
+	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_COMPLETE_HASH);
+	if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_COMPLETE, 1)) {
+		DRM_ERROR("Timed out waiting for SHA1 complete\n");
+		return -ETIMEDOUT;
+	}
+	if (!(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_V_MATCH)) {
+		DRM_ERROR("SHA-1 mismatch, HDCP failed\n");
+		return -ENXIO;
+	}
+
+	DRM_INFO("HDCP is enabled (%d downstream devices)\n", num_downstream);
+	return 0;
+}
+
+/* Implements Part 1 of the HDCP authorization procedure */
+static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
+			   const struct intel_hdcp_shim *shim)
+{
+	struct drm_i915_private *dev_priv;
+	enum port port;
+	unsigned long r0_prime_gen_start;
+	int ret, i;
+	union {
+		u32 reg[2];
+		u8 shim[DRM_HDCP_AN_LEN];
+	} an;
+	union {
+		u32 reg[2];
+		u8 shim[DRM_HDCP_KSV_LEN];
+	} bksv;
+	union {
+		u32 reg;
+		u8 shim[DRM_HDCP_RI_LEN];
+	} ri;
+	bool repeater_present;
+
+	dev_priv = intel_dig_port->base.base.dev->dev_private;
+
+	port = intel_dig_port->port;
+
+	// Initialize An with 2 random values and acquire it
+	for (i = 0; i < 2; i++)
+		I915_WRITE(SKL_PORT_HDCP_ANINIT(port), get_random_long());
+	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_CAPTURE_AN);
+
+	// Wait for An to be acquired
+	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
+		     SKL_HDCP_STATUS_AN_READY, 1)) {
+		DRM_ERROR("Timed out waiting for An\n");
+		return -ETIMEDOUT;
+	}
+
+	an.reg[0] = I915_READ(SKL_PORT_HDCP_ANLO(port));
+	an.reg[1] = I915_READ(SKL_PORT_HDCP_ANHI(port));
+	ret = shim->write_an_aksv(intel_dig_port, an.shim);
+	if (ret)
+		return ret;
+
+	r0_prime_gen_start = jiffies;
+
+	memset(&bksv, 0, sizeof(bksv));
+	ret = shim->read_bksv(intel_dig_port, bksv.shim);
+	if (ret)
+		return ret;
+
+	I915_WRITE(SKL_PORT_HDCP_BKSVLO(port), bksv.reg[0]);
+	I915_WRITE(SKL_PORT_HDCP_BKSVHI(port), bksv.reg[1]);
+
+	ret = shim->repeater_present(intel_dig_port, &repeater_present);
+	if (ret)
+		return ret;
+	if (repeater_present)
+		I915_WRITE(SKL_HDCP_REP_CTL,
+			   intel_hdcp_get_repeater_ctl(intel_dig_port));
+
+	ret = shim->toggle_signalling(intel_dig_port, true);
+	if (ret)
+		return ret;
+
+	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_AUTH_AND_ENC);
+
+	// Wait for R0 ready
+	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
+		     (SKL_HDCP_STATUS_R0_READY | SKL_HDCP_STATUS_ENC), 1)) {
+		DRM_ERROR("Timed out waiting for R0 ready\n");
+		return -ETIMEDOUT;
+	}
+
+	/*
+	 * Wait for R0' to become available, the spec says 100ms from Aksv
+	 * write. On DP, there's an R0_READY bit available but no such bit
+	 * exists on HDMI. Since the upper-bound is the same, we'll just do
+	 * the stupid thing instead of polling on one and not the other.
+	 */
+	wait_remaining_ms_from_jiffies(r0_prime_gen_start, 100);
+
+	ri.reg = 0;
+	ret = shim->read_ri_prime(intel_dig_port, ri.shim);
+	if (ret)
+		return ret;
+	I915_WRITE(SKL_PORT_HDCP_RPRIME(port), ri.reg);
+
+	// Wait for Ri prime match
+	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
+		     (SKL_HDCP_STATUS_RI_MATCH | SKL_HDCP_STATUS_ENC), 1)) {
+		DRM_ERROR("Timed out waiting for Ri prime match (%x)\n",
+			  I915_READ(SKL_PORT_HDCP_STATUS(port)));
+		return -ETIMEDOUT;
+	}
+
+	// Wait for encryption confirmation
+	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
+		      SKL_HDCP_STATUS_ENC, 20)) {
+		DRM_ERROR("Timed out waiting for encryption\n");
+		return -ETIMEDOUT;
+	}
+
+	/*
+	 * XXX: If we have MST-connected devices, we need to enable encryption
+	 * on those as well.
+	 */
+
+	return intel_hdcp_auth_downstream(intel_dig_port, shim);
+}
+
+static
+struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector)
+{
+	return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base);
+}
+
+static int _intel_hdcp_disable(struct intel_connector *connector)
+{
+	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
+	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+	enum port port = intel_dig_port->port;
+	int ret;
+
+	I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
+	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) == 0, 20)) {
+		DRM_ERROR("Failed to disable HDCP, timeout clearing status\n");
+		return -ETIMEDOUT;
+	}
+
+	intel_hdcp_clear_keys(dev_priv);
+
+	ret = connector->hdcp_shim->toggle_signalling(intel_dig_port, false);
+	if (ret) {
+		DRM_ERROR("Failed to disable HDCP signalling\n");
+		return ret;
+	}
+
+	DRM_INFO("HDCP is disabled\n");
+	return 0;
+}
+
+static int _intel_hdcp_enable(struct intel_connector *connector)
+{
+	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
+	int i, ret;
+
+	if (!(I915_READ(SKL_FUSE_STATUS) & SKL_FUSE_PG_DIST_STATUS(1))) {
+		DRM_ERROR("PG1 is disabled, cannot load keys\n");
+		return -ENXIO;
+	}
+
+	for (i = 0; i < KEY_LOAD_TRIES; i++) {
+		ret = intel_hdcp_load_keys(dev_priv);
+		if (!ret)
+			break;
+		intel_hdcp_clear_keys(dev_priv);
+	}
+	if (ret) {
+		DRM_ERROR("Could not load HDCP keys, (%d)\n", ret);
+		return ret;
+	}
+
+	ret = intel_hdcp_auth(conn_to_dig_port(connector),
+			      connector->hdcp_shim);
+	if (ret) {
+		DRM_ERROR("Failed to authenticate HDCP (%d)\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+void intel_hdcp_work(struct work_struct *work)
+{
+	struct intel_connector *connector = container_of(to_delayed_work(work),
+							 struct intel_connector,
+						         hdcp_work);
+	struct drm_device *dev = connector->base.dev;
+	int ret;
+
+	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+
+	ret = intel_hdcp_check_link(connector);
+	if (!ret)
+		schedule_delayed_work(&connector->hdcp_work,
+				      DRM_HDCP_CHECK_PERIOD_MS);
+
+	drm_modeset_unlock(&dev->mode_config.connection_mutex);
+}
+
+int intel_hdcp_enable(struct intel_connector *connector)
+{
+	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
+	struct drm_device *dev = &dev_priv->drm;
+	struct drm_connector_state *state = connector->base.state;
+	int ret;
+
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+
+	if (!connector->hdcp_shim)
+		return -ENOENT;
+
+	ret = _intel_hdcp_enable(connector);
+	if (ret)
+		return ret;
+
+	state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+
+	schedule_delayed_work(&connector->hdcp_work, DRM_HDCP_CHECK_PERIOD_MS);
+	return 0;
+}
+
+int intel_hdcp_disable(struct intel_connector *connector)
+{
+	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
+	struct drm_device *dev = &dev_priv->drm;
+
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+
+	if (!connector->hdcp_shim)
+		return -ENOENT;
+
+	cancel_delayed_work(&connector->hdcp_work);
+
+	return _intel_hdcp_disable(connector);
+}
+
+/* Implements Part 3 of the HDCP authorization procedure */
+int intel_hdcp_check_link(struct intel_connector *connector)
+{
+	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
+	struct drm_device *dev = &dev_priv->drm;
+	struct drm_connector_state *state = connector->base.state;
+	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+	enum port port = intel_dig_port->port;
+	int ret;
+
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+
+	if (state->content_protection != DRM_MODE_CONTENT_PROTECTION_ENABLED)
+		return 0;
+
+	if (!connector->hdcp_shim)
+		return -ENOENT;
+
+	if (!(I915_READ(SKL_PORT_HDCP_STATUS(port)) & SKL_HDCP_STATUS_ENC)) {
+		DRM_ERROR("HDCP check failed: link is not encrypted, %x\n",
+			   I915_READ(SKL_PORT_HDCP_STATUS(port)));
+		ret = -ENXIO;
+		goto fail;
+	}
+
+	if (connector->hdcp_shim->check_link(intel_dig_port))
+		return 0;
+
+	DRM_INFO("HDCP link failed, retrying authentication\n");
+
+	ret = _intel_hdcp_disable(connector);
+	if (ret) {
+		DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
+		goto fail;
+	}
+
+	ret = _intel_hdcp_enable(connector);
+	if (ret) {
+		DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+	return ret;
+}
-- 
2.15.0.531.g2ccb3012c9-goog

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [RFC PATCH 4/6] drm/i915: Add function to output Aksv over GMBUS
  2017-11-30  3:08 [RFC PATCH 0/6] drm/i915: Implement HDCP Sean Paul
@ 2017-11-30  3:08   ` Sean Paul
  2017-11-30  3:08   ` Sean Paul
                     ` (8 subsequent siblings)
  9 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-11-30  3:08 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: Sean Paul, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
	David Airlie, linux-kernel

Once the Aksv is available in the PCH, we need to get it on the wire to
the receiver via DDC. The hardware doesn't allow us to read the value
directly, so we need to tell GMBUS to source the Aksv internally and
send it to the right offset on the receiver.

The way we do this is to initiate an indexed write where the index is
the Aksv register offset. We write dummy values to GMBUS3 as if we were
sending the key, and the hardware slips in the "real" values when it
goes out.

Signed-off-by: Sean Paul <seanpaul@chromium.org>
---
 drivers/gpu/drm/i915/i915_drv.h  |  1 +
 drivers/gpu/drm/i915/i915_reg.h  |  1 +
 drivers/gpu/drm/i915/intel_i2c.c | 54 ++++++++++++++++++++++++++++++++++------
 3 files changed, 48 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 54b5d4c582b6..30fcd856ec23 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -4024,6 +4024,7 @@ extern int intel_setup_gmbus(struct drm_i915_private *dev_priv);
 extern void intel_teardown_gmbus(struct drm_i915_private *dev_priv);
 extern bool intel_gmbus_is_valid_pin(struct drm_i915_private *dev_priv,
 				     unsigned int pin);
+extern int intel_gmbus_output_aksv(struct i2c_adapter *adapter);
 
 extern struct i2c_adapter *
 intel_gmbus_get_adapter(struct drm_i915_private *dev_priv, unsigned int pin);
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 43128030171d..78370877fea3 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -3009,6 +3009,7 @@ enum i915_power_well_id {
 # define GPIO_DATA_PULLUP_DISABLE	(1 << 13)
 
 #define GMBUS0			_MMIO(dev_priv->gpio_mmio_base + 0x5100) /* clock/port select */
+#define   GMBUS_AKSV_SELECT	(1<<11)
 #define   GMBUS_RATE_100KHZ	(0<<8)
 #define   GMBUS_RATE_50KHZ	(1<<8)
 #define   GMBUS_RATE_400KHZ	(2<<8) /* reserved on Pineview */
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index eb5827110d8f..c01156bf0f27 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -30,6 +30,7 @@
 #include <linux/i2c-algo-bit.h>
 #include <linux/export.h>
 #include <drm/drmP.h>
+#include <drm/drm_hdcp.h>
 #include "intel_drv.h"
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
@@ -373,7 +374,8 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
 
 static int
 gmbus_xfer_write_chunk(struct drm_i915_private *dev_priv,
-		       unsigned short addr, u8 *buf, unsigned int len)
+		       unsigned short addr, u8 *buf, unsigned int len,
+		       u32 gmbus1_index)
 {
 	unsigned int chunk_size = len;
 	u32 val, loop;
@@ -386,7 +388,7 @@ gmbus_xfer_write_chunk(struct drm_i915_private *dev_priv,
 
 	I915_WRITE_FW(GMBUS3, val);
 	I915_WRITE_FW(GMBUS1,
-		      GMBUS_CYCLE_WAIT |
+		      gmbus1_index | GMBUS_CYCLE_WAIT |
 		      (chunk_size << GMBUS_BYTE_COUNT_SHIFT) |
 		      (addr << GMBUS_SLAVE_ADDR_SHIFT) |
 		      GMBUS_SLAVE_WRITE | GMBUS_SW_RDY);
@@ -409,7 +411,8 @@ gmbus_xfer_write_chunk(struct drm_i915_private *dev_priv,
 }
 
 static int
-gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
+gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
+		 u32 gmbus1_index)
 {
 	u8 *buf = msg->buf;
 	unsigned int tx_size = msg->len;
@@ -419,7 +422,8 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
 	do {
 		len = min(tx_size, GMBUS_BYTE_COUNT_MAX);
 
-		ret = gmbus_xfer_write_chunk(dev_priv, msg->addr, buf, len);
+		ret = gmbus_xfer_write_chunk(dev_priv, msg->addr, buf, len,
+					     gmbus1_index);
 		if (ret)
 			return ret;
 
@@ -470,7 +474,8 @@ gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
 }
 
 static int
-do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
+do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num,
+	      u32 gmbus0_source, u32 gmbus1_index)
 {
 	struct intel_gmbus *bus = container_of(adapter,
 					       struct intel_gmbus,
@@ -480,7 +485,7 @@ do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
 	int ret = 0;
 
 retry:
-	I915_WRITE_FW(GMBUS0, bus->reg0);
+	I915_WRITE_FW(GMBUS0, gmbus0_source | bus->reg0);
 
 	for (; i < num; i += inc) {
 		inc = 1;
@@ -490,7 +495,8 @@ do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
 		} else if (msgs[i].flags & I2C_M_RD) {
 			ret = gmbus_xfer_read(dev_priv, &msgs[i], 0);
 		} else {
-			ret = gmbus_xfer_write(dev_priv, &msgs[i]);
+			ret = gmbus_xfer_write(dev_priv, &msgs[i],
+					       gmbus1_index);
 		}
 
 		if (!ret)
@@ -598,7 +604,7 @@ gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
 		if (ret < 0)
 			bus->force_bit &= ~GMBUS_FORCE_BIT_RETRY;
 	} else {
-		ret = do_gmbus_xfer(adapter, msgs, num);
+		ret = do_gmbus_xfer(adapter, msgs, num, 0, 0);
 		if (ret == -EAGAIN)
 			bus->force_bit |= GMBUS_FORCE_BIT_RETRY;
 	}
@@ -608,6 +614,38 @@ gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
 	return ret;
 }
 
+int intel_gmbus_output_aksv(struct i2c_adapter *adapter)
+{
+	struct intel_gmbus *bus = container_of(adapter, struct intel_gmbus,
+					       adapter);
+	struct drm_i915_private *dev_priv = bus->dev_priv;
+	int ret;
+	u8 buf[DRM_HDCP_KSV_LEN] = { 0 };
+	struct i2c_msg msg = {
+		.addr = DRM_HDCP_DDC_ADDR,
+		.flags = 0,
+		.len = sizeof(buf),
+		.buf = buf,
+	};
+
+	intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
+	mutex_lock(&dev_priv->gmbus_mutex);
+
+	/*
+	 * In order to output Aksv to the receiver, use an indexed write to
+	 * pass the i2c command, and tell GMBUS to use the HW-provided value
+	 * instead of sourcing GMBUS3 for the data.
+	 */
+	ret = do_gmbus_xfer(adapter, &msg, 1, GMBUS_AKSV_SELECT,
+			    GMBUS_CYCLE_INDEX |
+			    (DRM_HDCP_DDC_AKSV << GMBUS_SLAVE_INDEX_SHIFT));
+
+	mutex_unlock(&dev_priv->gmbus_mutex);
+	intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
+
+	return ret;
+}
+
 static u32 gmbus_func(struct i2c_adapter *adapter)
 {
 	return i2c_bit_algo.functionality(adapter) &
-- 
2.15.0.531.g2ccb3012c9-goog

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [RFC PATCH 4/6] drm/i915: Add function to output Aksv over GMBUS
@ 2017-11-30  3:08   ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-11-30  3:08 UTC (permalink / raw)
  To: dri-devel, intel-gfx; +Cc: David Airlie, linux-kernel, Rodrigo Vivi

Once the Aksv is available in the PCH, we need to get it on the wire to
the receiver via DDC. The hardware doesn't allow us to read the value
directly, so we need to tell GMBUS to source the Aksv internally and
send it to the right offset on the receiver.

The way we do this is to initiate an indexed write where the index is
the Aksv register offset. We write dummy values to GMBUS3 as if we were
sending the key, and the hardware slips in the "real" values when it
goes out.

Signed-off-by: Sean Paul <seanpaul@chromium.org>
---
 drivers/gpu/drm/i915/i915_drv.h  |  1 +
 drivers/gpu/drm/i915/i915_reg.h  |  1 +
 drivers/gpu/drm/i915/intel_i2c.c | 54 ++++++++++++++++++++++++++++++++++------
 3 files changed, 48 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 54b5d4c582b6..30fcd856ec23 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -4024,6 +4024,7 @@ extern int intel_setup_gmbus(struct drm_i915_private *dev_priv);
 extern void intel_teardown_gmbus(struct drm_i915_private *dev_priv);
 extern bool intel_gmbus_is_valid_pin(struct drm_i915_private *dev_priv,
 				     unsigned int pin);
+extern int intel_gmbus_output_aksv(struct i2c_adapter *adapter);
 
 extern struct i2c_adapter *
 intel_gmbus_get_adapter(struct drm_i915_private *dev_priv, unsigned int pin);
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 43128030171d..78370877fea3 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -3009,6 +3009,7 @@ enum i915_power_well_id {
 # define GPIO_DATA_PULLUP_DISABLE	(1 << 13)
 
 #define GMBUS0			_MMIO(dev_priv->gpio_mmio_base + 0x5100) /* clock/port select */
+#define   GMBUS_AKSV_SELECT	(1<<11)
 #define   GMBUS_RATE_100KHZ	(0<<8)
 #define   GMBUS_RATE_50KHZ	(1<<8)
 #define   GMBUS_RATE_400KHZ	(2<<8) /* reserved on Pineview */
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index eb5827110d8f..c01156bf0f27 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -30,6 +30,7 @@
 #include <linux/i2c-algo-bit.h>
 #include <linux/export.h>
 #include <drm/drmP.h>
+#include <drm/drm_hdcp.h>
 #include "intel_drv.h"
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
@@ -373,7 +374,8 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
 
 static int
 gmbus_xfer_write_chunk(struct drm_i915_private *dev_priv,
-		       unsigned short addr, u8 *buf, unsigned int len)
+		       unsigned short addr, u8 *buf, unsigned int len,
+		       u32 gmbus1_index)
 {
 	unsigned int chunk_size = len;
 	u32 val, loop;
@@ -386,7 +388,7 @@ gmbus_xfer_write_chunk(struct drm_i915_private *dev_priv,
 
 	I915_WRITE_FW(GMBUS3, val);
 	I915_WRITE_FW(GMBUS1,
-		      GMBUS_CYCLE_WAIT |
+		      gmbus1_index | GMBUS_CYCLE_WAIT |
 		      (chunk_size << GMBUS_BYTE_COUNT_SHIFT) |
 		      (addr << GMBUS_SLAVE_ADDR_SHIFT) |
 		      GMBUS_SLAVE_WRITE | GMBUS_SW_RDY);
@@ -409,7 +411,8 @@ gmbus_xfer_write_chunk(struct drm_i915_private *dev_priv,
 }
 
 static int
-gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
+gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
+		 u32 gmbus1_index)
 {
 	u8 *buf = msg->buf;
 	unsigned int tx_size = msg->len;
@@ -419,7 +422,8 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
 	do {
 		len = min(tx_size, GMBUS_BYTE_COUNT_MAX);
 
-		ret = gmbus_xfer_write_chunk(dev_priv, msg->addr, buf, len);
+		ret = gmbus_xfer_write_chunk(dev_priv, msg->addr, buf, len,
+					     gmbus1_index);
 		if (ret)
 			return ret;
 
@@ -470,7 +474,8 @@ gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
 }
 
 static int
-do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
+do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num,
+	      u32 gmbus0_source, u32 gmbus1_index)
 {
 	struct intel_gmbus *bus = container_of(adapter,
 					       struct intel_gmbus,
@@ -480,7 +485,7 @@ do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
 	int ret = 0;
 
 retry:
-	I915_WRITE_FW(GMBUS0, bus->reg0);
+	I915_WRITE_FW(GMBUS0, gmbus0_source | bus->reg0);
 
 	for (; i < num; i += inc) {
 		inc = 1;
@@ -490,7 +495,8 @@ do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
 		} else if (msgs[i].flags & I2C_M_RD) {
 			ret = gmbus_xfer_read(dev_priv, &msgs[i], 0);
 		} else {
-			ret = gmbus_xfer_write(dev_priv, &msgs[i]);
+			ret = gmbus_xfer_write(dev_priv, &msgs[i],
+					       gmbus1_index);
 		}
 
 		if (!ret)
@@ -598,7 +604,7 @@ gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
 		if (ret < 0)
 			bus->force_bit &= ~GMBUS_FORCE_BIT_RETRY;
 	} else {
-		ret = do_gmbus_xfer(adapter, msgs, num);
+		ret = do_gmbus_xfer(adapter, msgs, num, 0, 0);
 		if (ret == -EAGAIN)
 			bus->force_bit |= GMBUS_FORCE_BIT_RETRY;
 	}
@@ -608,6 +614,38 @@ gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
 	return ret;
 }
 
+int intel_gmbus_output_aksv(struct i2c_adapter *adapter)
+{
+	struct intel_gmbus *bus = container_of(adapter, struct intel_gmbus,
+					       adapter);
+	struct drm_i915_private *dev_priv = bus->dev_priv;
+	int ret;
+	u8 buf[DRM_HDCP_KSV_LEN] = { 0 };
+	struct i2c_msg msg = {
+		.addr = DRM_HDCP_DDC_ADDR,
+		.flags = 0,
+		.len = sizeof(buf),
+		.buf = buf,
+	};
+
+	intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
+	mutex_lock(&dev_priv->gmbus_mutex);
+
+	/*
+	 * In order to output Aksv to the receiver, use an indexed write to
+	 * pass the i2c command, and tell GMBUS to use the HW-provided value
+	 * instead of sourcing GMBUS3 for the data.
+	 */
+	ret = do_gmbus_xfer(adapter, &msg, 1, GMBUS_AKSV_SELECT,
+			    GMBUS_CYCLE_INDEX |
+			    (DRM_HDCP_DDC_AKSV << GMBUS_SLAVE_INDEX_SHIFT));
+
+	mutex_unlock(&dev_priv->gmbus_mutex);
+	intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
+
+	return ret;
+}
+
 static u32 gmbus_func(struct i2c_adapter *adapter)
 {
 	return i2c_bit_algo.functionality(adapter) &
-- 
2.15.0.531.g2ccb3012c9-goog

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [RFC PATCH 5/6] drm/i915: Implement HDCP for HDMI
  2017-11-30  3:08 [RFC PATCH 0/6] drm/i915: Implement HDCP Sean Paul
@ 2017-11-30  3:09   ` Sean Paul
  2017-11-30  3:08   ` Sean Paul
                     ` (8 subsequent siblings)
  9 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-11-30  3:09 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: Sean Paul, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
	David Airlie, linux-kernel

This patch adds HDCP support for HDMI connectors by implementing
the intel_hdcp_shim.

Nothing too special, just a bunch of DDC reads/writes.

Signed-off-by: Sean Paul <seanpaul@chromium.org>
---
 drivers/gpu/drm/i915/i915_reg.h   |   1 +
 drivers/gpu/drm/i915/intel_ddi.c  |  50 ++++++++
 drivers/gpu/drm/i915/intel_hdmi.c | 253 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 304 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 78370877fea3..061c52a5d7d7 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -8400,6 +8400,7 @@ enum skl_power_gate {
 #define  TRANS_DDI_EDP_INPUT_A_ONOFF	(4<<12)
 #define  TRANS_DDI_EDP_INPUT_B_ONOFF	(5<<12)
 #define  TRANS_DDI_EDP_INPUT_C_ONOFF	(6<<12)
+#define  TRANS_DDI_HDCP_SIGNALLING	(1<<9)
 #define  TRANS_DDI_DP_VC_PAYLOAD_ALLOC	(1<<8)
 #define  TRANS_DDI_HDMI_SCRAMBLER_CTS_ENABLE (1<<7)
 #define  TRANS_DDI_HDMI_SCRAMBLER_RESET_FREQ (1<<6)
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index 0e69337f410d..0324b09ecf76 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -1650,6 +1650,56 @@ void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
 	I915_WRITE(reg, val);
 }
 
+int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder)
+{
+	struct drm_device *dev = intel_encoder->base.dev;
+	struct drm_i915_private *dev_priv = to_i915(dev);
+	enum pipe pipe = 0;
+	int ret = 0;
+	uint32_t tmp;
+
+	if (!intel_display_power_get_if_enabled(dev_priv,
+						intel_encoder->power_domain))
+		return -ENXIO;
+
+	if (!intel_encoder->get_hw_state(intel_encoder, &pipe)) {
+		ret = -EIO;
+		goto out;
+	}
+
+	tmp = I915_READ(TRANS_DDI_FUNC_CTL(pipe));
+	tmp &= ~TRANS_DDI_HDCP_SIGNALLING;
+	I915_WRITE(TRANS_DDI_FUNC_CTL(pipe), tmp);
+out:
+	intel_display_power_put(dev_priv, intel_encoder->power_domain);
+	return ret;
+}
+
+int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder)
+{
+	struct drm_device *dev = intel_encoder->base.dev;
+	struct drm_i915_private *dev_priv = to_i915(dev);
+	enum pipe pipe = 0;
+	int ret = 0;
+	uint32_t tmp;
+
+	if (!intel_display_power_get_if_enabled(dev_priv,
+						intel_encoder->power_domain))
+		return -ENXIO;
+
+	if (!intel_encoder->get_hw_state(intel_encoder, &pipe)) {
+		ret = -EIO;
+		goto out;
+	}
+
+	tmp = I915_READ(TRANS_DDI_FUNC_CTL(pipe));
+	tmp |= TRANS_DDI_HDCP_SIGNALLING;
+	I915_WRITE(TRANS_DDI_FUNC_CTL(pipe), tmp);
+out:
+	intel_display_power_put(dev_priv, intel_encoder->power_domain);
+	return ret;
+}
+
 bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector)
 {
 	struct drm_device *dev = intel_connector->base.dev;
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 59247a49a077..563168897dd9 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -34,6 +34,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_edid.h>
+#include <drm/drm_hdcp.h>
 #include <drm/drm_scdc_helper.h>
 #include "intel_drv.h"
 #include <drm/i915_drm.h>
@@ -875,6 +876,252 @@ void intel_dp_dual_mode_set_tmds_output(struct intel_hdmi *hdmi, bool enable)
 					 adapter, enable);
 }
 
+static int intel_hdmi_hdcp_read(struct intel_digital_port *intel_dig_port,
+			        unsigned int offset, void *buffer, size_t size)
+{
+	struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
+	struct drm_i915_private *dev_priv =
+		intel_dig_port->base.base.dev->dev_private;
+	struct i2c_adapter *adapter = intel_gmbus_get_adapter(dev_priv,
+							      hdmi->ddc_bus);
+	int ret;
+	u8 start = offset & 0xff;
+	struct i2c_msg msgs[] = {
+		{
+			.addr = DRM_HDCP_DDC_ADDR,
+			.flags = 0,
+			.len = 1,
+			.buf = &start,
+		},
+		{
+			.addr = DRM_HDCP_DDC_ADDR,
+			.flags = I2C_M_RD,
+			.len = size,
+			.buf = buffer
+		}
+	};
+	ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret == ARRAY_SIZE(msgs))
+		return 0;
+	return ret >= 0 ? -EIO : ret;
+}
+
+static int intel_hdmi_hdcp_write(struct intel_digital_port *intel_dig_port,
+			         unsigned int offset, void *buffer, size_t size)
+{
+	struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
+	struct drm_i915_private *dev_priv =
+		intel_dig_port->base.base.dev->dev_private;
+	struct i2c_adapter *adapter = intel_gmbus_get_adapter(dev_priv,
+							      hdmi->ddc_bus);
+	int ret;
+	u8 *write_buf;
+	struct i2c_msg msg;
+
+	write_buf = kzalloc(size + 1, GFP_KERNEL);
+	if (!write_buf)
+		return -ENOMEM;
+
+	write_buf[0] = offset & 0xff;
+	memcpy(&write_buf[1], buffer, size);
+
+	msg.addr = DRM_HDCP_DDC_ADDR;
+	msg.flags = 0,
+	msg.len = size + 1,
+	msg.buf = write_buf;
+
+	ret = i2c_transfer(adapter, &msg, 1);
+	if (ret == 1)
+		return 0;
+	return ret >= 0 ? -EIO : ret;
+}
+
+static
+int intel_hdmi_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port,
+				  u8 *an)
+{
+	struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
+	struct drm_i915_private *dev_priv =
+		intel_dig_port->base.base.dev->dev_private;
+	struct i2c_adapter *adapter = intel_gmbus_get_adapter(dev_priv,
+							      hdmi->ddc_bus);
+	int ret;
+
+	ret = intel_hdmi_hdcp_write(intel_dig_port, DRM_HDCP_DDC_AN, an,
+				    DRM_HDCP_AN_LEN);
+	if (ret) {
+		DRM_ERROR("Write An over DDC failed (%d)\n", ret);
+		return ret;
+	}
+
+	ret = intel_gmbus_output_aksv(adapter);
+	if (ret < 0) {
+		DRM_ERROR("Failed to output aksv (%d)\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int intel_hdmi_hdcp_read_bksv(struct intel_digital_port *intel_dig_port,
+				     u8 *bksv)
+{
+	int ret;
+	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BKSV, bksv,
+				   DRM_HDCP_KSV_LEN);
+	if (ret)
+		DRM_ERROR("Read Bksv over DDC failed (%d)\n", ret);
+	return ret;
+}
+
+static
+int intel_hdmi_hdcp_read_bstatus(struct intel_digital_port *intel_dig_port,
+				 u8 *bstatus)
+{
+	int ret;
+	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BSTATUS,
+				   bstatus, DRM_HDCP_BSTATUS_LEN);
+	if (ret)
+		DRM_ERROR("Read bstatus over DDC failed (%d)\n", ret);
+	return ret;
+}
+
+static
+int intel_hdmi_hdcp_repeater_present(struct intel_digital_port *intel_dig_port,
+				     bool *repeater_present)
+{
+	int ret;
+	u8 val;
+
+	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BCAPS, &val, 1);
+	if (ret) {
+		DRM_ERROR("Read bcaps over DDC failed (%d)\n", ret);
+		return ret;
+	}
+	*repeater_present = val & DRM_HDCP_DDC_BCAPS_REPEATER_PRESENT;
+	return 0;
+}
+
+static
+int intel_hdmi_hdcp_read_ri_prime(struct intel_digital_port *intel_dig_port,
+				  u8 *ri_prime)
+{
+	int ret;
+	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_RI_PRIME,
+				   ri_prime, DRM_HDCP_RI_LEN);
+	if (ret)
+		DRM_ERROR("Read Ri' over DDC failed (%d)\n", ret);
+	return ret;
+}
+
+static
+int intel_hdmi_hdcp_read_ksv_ready(struct intel_digital_port *intel_dig_port,
+				   bool *ksv_ready)
+{
+	int ret;
+	u8 val;
+
+	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BCAPS, &val, 1);
+	if (ret) {
+		DRM_ERROR("Read bcaps over DDC failed (%d)\n", ret);
+		return ret;
+	}
+	*ksv_ready = val & DRM_HDCP_DDC_BCAPS_KSV_FIFO_READY;
+	return 0;
+}
+
+static
+int intel_hdmi_hdcp_read_ksv_fifo(struct intel_digital_port *intel_dig_port,
+			          int num_downstream, u8 *ksv_fifo)
+{
+	int ret;
+	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_KSV_FIFO,
+				   ksv_fifo, num_downstream * DRM_HDCP_KSV_LEN);
+	if (ret) {
+		DRM_ERROR("Read ksv fifo over DDC failed (%d)\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static
+int intel_hdmi_hdcp_read_v_prime_part(struct intel_digital_port *intel_dig_port,
+				      int i, u32 *part)
+{
+	int ret;
+
+	if (i >= DRM_HDCP_V_PRIME_NUM_PARTS)
+		return -EINVAL;
+
+	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_V_PRIME(i),
+				   part, DRM_HDCP_V_PRIME_PART_LEN);
+	if (ret)
+		DRM_ERROR("Read V'[%d] over DDC failed (%d)\n", i, ret);
+	return ret;
+}
+
+static
+int intel_hdmi_hdcp_toggle_signalling(struct intel_digital_port *intel_dig_port,
+				      bool enable)
+{
+	int ret;
+	if (enable) {
+		ret = intel_ddi_enable_hdcp_signalling(&intel_dig_port->base);
+		if (ret) {
+			DRM_ERROR("Enable HDCP signalling failed (%d)\n", ret);
+			return ret;
+		}
+	} else {
+		usleep_range(6, 60); /* Bspec says >= 6us */
+		ret = intel_ddi_disable_hdcp_signalling(&intel_dig_port->base);
+		if (ret) {
+			DRM_ERROR("Enable HDCP signalling failed (%d)\n", ret);
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static
+bool intel_hdmi_hdcp_check_link(struct intel_digital_port *intel_dig_port)
+{
+	struct drm_i915_private *dev_priv =
+		intel_dig_port->base.base.dev->dev_private;
+	enum port port = intel_dig_port->port;
+	int ret;
+	union {
+		u32 reg;
+		u8 shim[DRM_HDCP_RI_LEN];
+	} ri;
+
+	ret = intel_hdmi_hdcp_read_ri_prime(intel_dig_port, ri.shim);
+	if (ret)
+		return false;
+
+	I915_WRITE(SKL_PORT_HDCP_RPRIME(port), ri.reg);
+
+	// Wait for Ri prime match
+	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
+		     (SKL_HDCP_STATUS_RI_MATCH | SKL_HDCP_STATUS_ENC), 1)) {
+		DRM_ERROR("Ri' mismatch detected, link check failed (%x)\n",
+			  I915_READ(SKL_PORT_HDCP_STATUS(port)));
+		return false;
+	}
+	return true;
+}
+
+static const struct intel_hdcp_shim intel_hdmi_hdcp_shim = {
+	.write_an_aksv = intel_hdmi_hdcp_write_an_aksv,
+	.read_bksv = intel_hdmi_hdcp_read_bksv,
+	.read_bstatus = intel_hdmi_hdcp_read_bstatus,
+	.repeater_present = intel_hdmi_hdcp_repeater_present,
+	.read_ri_prime = intel_hdmi_hdcp_read_ri_prime,
+	.read_ksv_ready = intel_hdmi_hdcp_read_ksv_ready,
+	.read_ksv_fifo = intel_hdmi_hdcp_read_ksv_fifo,
+	.read_v_prime_part = intel_hdmi_hdcp_read_v_prime_part,
+	.toggle_signalling = intel_hdmi_hdcp_toggle_signalling,
+	.check_link = intel_hdmi_hdcp_check_link,
+};
+
 static void intel_hdmi_prepare(struct intel_encoder *encoder,
 			       const struct intel_crtc_state *crtc_state)
 {
@@ -2039,6 +2286,12 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
 
 	intel_hdmi_add_properties(intel_hdmi, connector);
 
+	if (INTEL_GEN(dev_priv) >= 9) {
+		drm_connector_attach_content_protection_property(connector);
+		intel_connector->hdcp_shim = &intel_hdmi_hdcp_shim;
+		INIT_DELAYED_WORK(&intel_connector->hdcp_work, intel_hdcp_work);
+	}
+
 	intel_connector_attach_encoder(intel_connector, intel_encoder);
 	intel_hdmi->attached_connector = intel_connector;
 
-- 
2.15.0.531.g2ccb3012c9-goog

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [RFC PATCH 5/6] drm/i915: Implement HDCP for HDMI
@ 2017-11-30  3:09   ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-11-30  3:09 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: David Airlie, Joonas Lahtinen, linux-kernel, Rodrigo Vivi

This patch adds HDCP support for HDMI connectors by implementing
the intel_hdcp_shim.

Nothing too special, just a bunch of DDC reads/writes.

Signed-off-by: Sean Paul <seanpaul@chromium.org>
---
 drivers/gpu/drm/i915/i915_reg.h   |   1 +
 drivers/gpu/drm/i915/intel_ddi.c  |  50 ++++++++
 drivers/gpu/drm/i915/intel_hdmi.c | 253 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 304 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 78370877fea3..061c52a5d7d7 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -8400,6 +8400,7 @@ enum skl_power_gate {
 #define  TRANS_DDI_EDP_INPUT_A_ONOFF	(4<<12)
 #define  TRANS_DDI_EDP_INPUT_B_ONOFF	(5<<12)
 #define  TRANS_DDI_EDP_INPUT_C_ONOFF	(6<<12)
+#define  TRANS_DDI_HDCP_SIGNALLING	(1<<9)
 #define  TRANS_DDI_DP_VC_PAYLOAD_ALLOC	(1<<8)
 #define  TRANS_DDI_HDMI_SCRAMBLER_CTS_ENABLE (1<<7)
 #define  TRANS_DDI_HDMI_SCRAMBLER_RESET_FREQ (1<<6)
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index 0e69337f410d..0324b09ecf76 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -1650,6 +1650,56 @@ void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
 	I915_WRITE(reg, val);
 }
 
+int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder)
+{
+	struct drm_device *dev = intel_encoder->base.dev;
+	struct drm_i915_private *dev_priv = to_i915(dev);
+	enum pipe pipe = 0;
+	int ret = 0;
+	uint32_t tmp;
+
+	if (!intel_display_power_get_if_enabled(dev_priv,
+						intel_encoder->power_domain))
+		return -ENXIO;
+
+	if (!intel_encoder->get_hw_state(intel_encoder, &pipe)) {
+		ret = -EIO;
+		goto out;
+	}
+
+	tmp = I915_READ(TRANS_DDI_FUNC_CTL(pipe));
+	tmp &= ~TRANS_DDI_HDCP_SIGNALLING;
+	I915_WRITE(TRANS_DDI_FUNC_CTL(pipe), tmp);
+out:
+	intel_display_power_put(dev_priv, intel_encoder->power_domain);
+	return ret;
+}
+
+int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder)
+{
+	struct drm_device *dev = intel_encoder->base.dev;
+	struct drm_i915_private *dev_priv = to_i915(dev);
+	enum pipe pipe = 0;
+	int ret = 0;
+	uint32_t tmp;
+
+	if (!intel_display_power_get_if_enabled(dev_priv,
+						intel_encoder->power_domain))
+		return -ENXIO;
+
+	if (!intel_encoder->get_hw_state(intel_encoder, &pipe)) {
+		ret = -EIO;
+		goto out;
+	}
+
+	tmp = I915_READ(TRANS_DDI_FUNC_CTL(pipe));
+	tmp |= TRANS_DDI_HDCP_SIGNALLING;
+	I915_WRITE(TRANS_DDI_FUNC_CTL(pipe), tmp);
+out:
+	intel_display_power_put(dev_priv, intel_encoder->power_domain);
+	return ret;
+}
+
 bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector)
 {
 	struct drm_device *dev = intel_connector->base.dev;
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 59247a49a077..563168897dd9 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -34,6 +34,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_edid.h>
+#include <drm/drm_hdcp.h>
 #include <drm/drm_scdc_helper.h>
 #include "intel_drv.h"
 #include <drm/i915_drm.h>
@@ -875,6 +876,252 @@ void intel_dp_dual_mode_set_tmds_output(struct intel_hdmi *hdmi, bool enable)
 					 adapter, enable);
 }
 
+static int intel_hdmi_hdcp_read(struct intel_digital_port *intel_dig_port,
+			        unsigned int offset, void *buffer, size_t size)
+{
+	struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
+	struct drm_i915_private *dev_priv =
+		intel_dig_port->base.base.dev->dev_private;
+	struct i2c_adapter *adapter = intel_gmbus_get_adapter(dev_priv,
+							      hdmi->ddc_bus);
+	int ret;
+	u8 start = offset & 0xff;
+	struct i2c_msg msgs[] = {
+		{
+			.addr = DRM_HDCP_DDC_ADDR,
+			.flags = 0,
+			.len = 1,
+			.buf = &start,
+		},
+		{
+			.addr = DRM_HDCP_DDC_ADDR,
+			.flags = I2C_M_RD,
+			.len = size,
+			.buf = buffer
+		}
+	};
+	ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret == ARRAY_SIZE(msgs))
+		return 0;
+	return ret >= 0 ? -EIO : ret;
+}
+
+static int intel_hdmi_hdcp_write(struct intel_digital_port *intel_dig_port,
+			         unsigned int offset, void *buffer, size_t size)
+{
+	struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
+	struct drm_i915_private *dev_priv =
+		intel_dig_port->base.base.dev->dev_private;
+	struct i2c_adapter *adapter = intel_gmbus_get_adapter(dev_priv,
+							      hdmi->ddc_bus);
+	int ret;
+	u8 *write_buf;
+	struct i2c_msg msg;
+
+	write_buf = kzalloc(size + 1, GFP_KERNEL);
+	if (!write_buf)
+		return -ENOMEM;
+
+	write_buf[0] = offset & 0xff;
+	memcpy(&write_buf[1], buffer, size);
+
+	msg.addr = DRM_HDCP_DDC_ADDR;
+	msg.flags = 0,
+	msg.len = size + 1,
+	msg.buf = write_buf;
+
+	ret = i2c_transfer(adapter, &msg, 1);
+	if (ret == 1)
+		return 0;
+	return ret >= 0 ? -EIO : ret;
+}
+
+static
+int intel_hdmi_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port,
+				  u8 *an)
+{
+	struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
+	struct drm_i915_private *dev_priv =
+		intel_dig_port->base.base.dev->dev_private;
+	struct i2c_adapter *adapter = intel_gmbus_get_adapter(dev_priv,
+							      hdmi->ddc_bus);
+	int ret;
+
+	ret = intel_hdmi_hdcp_write(intel_dig_port, DRM_HDCP_DDC_AN, an,
+				    DRM_HDCP_AN_LEN);
+	if (ret) {
+		DRM_ERROR("Write An over DDC failed (%d)\n", ret);
+		return ret;
+	}
+
+	ret = intel_gmbus_output_aksv(adapter);
+	if (ret < 0) {
+		DRM_ERROR("Failed to output aksv (%d)\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int intel_hdmi_hdcp_read_bksv(struct intel_digital_port *intel_dig_port,
+				     u8 *bksv)
+{
+	int ret;
+	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BKSV, bksv,
+				   DRM_HDCP_KSV_LEN);
+	if (ret)
+		DRM_ERROR("Read Bksv over DDC failed (%d)\n", ret);
+	return ret;
+}
+
+static
+int intel_hdmi_hdcp_read_bstatus(struct intel_digital_port *intel_dig_port,
+				 u8 *bstatus)
+{
+	int ret;
+	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BSTATUS,
+				   bstatus, DRM_HDCP_BSTATUS_LEN);
+	if (ret)
+		DRM_ERROR("Read bstatus over DDC failed (%d)\n", ret);
+	return ret;
+}
+
+static
+int intel_hdmi_hdcp_repeater_present(struct intel_digital_port *intel_dig_port,
+				     bool *repeater_present)
+{
+	int ret;
+	u8 val;
+
+	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BCAPS, &val, 1);
+	if (ret) {
+		DRM_ERROR("Read bcaps over DDC failed (%d)\n", ret);
+		return ret;
+	}
+	*repeater_present = val & DRM_HDCP_DDC_BCAPS_REPEATER_PRESENT;
+	return 0;
+}
+
+static
+int intel_hdmi_hdcp_read_ri_prime(struct intel_digital_port *intel_dig_port,
+				  u8 *ri_prime)
+{
+	int ret;
+	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_RI_PRIME,
+				   ri_prime, DRM_HDCP_RI_LEN);
+	if (ret)
+		DRM_ERROR("Read Ri' over DDC failed (%d)\n", ret);
+	return ret;
+}
+
+static
+int intel_hdmi_hdcp_read_ksv_ready(struct intel_digital_port *intel_dig_port,
+				   bool *ksv_ready)
+{
+	int ret;
+	u8 val;
+
+	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BCAPS, &val, 1);
+	if (ret) {
+		DRM_ERROR("Read bcaps over DDC failed (%d)\n", ret);
+		return ret;
+	}
+	*ksv_ready = val & DRM_HDCP_DDC_BCAPS_KSV_FIFO_READY;
+	return 0;
+}
+
+static
+int intel_hdmi_hdcp_read_ksv_fifo(struct intel_digital_port *intel_dig_port,
+			          int num_downstream, u8 *ksv_fifo)
+{
+	int ret;
+	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_KSV_FIFO,
+				   ksv_fifo, num_downstream * DRM_HDCP_KSV_LEN);
+	if (ret) {
+		DRM_ERROR("Read ksv fifo over DDC failed (%d)\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static
+int intel_hdmi_hdcp_read_v_prime_part(struct intel_digital_port *intel_dig_port,
+				      int i, u32 *part)
+{
+	int ret;
+
+	if (i >= DRM_HDCP_V_PRIME_NUM_PARTS)
+		return -EINVAL;
+
+	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_V_PRIME(i),
+				   part, DRM_HDCP_V_PRIME_PART_LEN);
+	if (ret)
+		DRM_ERROR("Read V'[%d] over DDC failed (%d)\n", i, ret);
+	return ret;
+}
+
+static
+int intel_hdmi_hdcp_toggle_signalling(struct intel_digital_port *intel_dig_port,
+				      bool enable)
+{
+	int ret;
+	if (enable) {
+		ret = intel_ddi_enable_hdcp_signalling(&intel_dig_port->base);
+		if (ret) {
+			DRM_ERROR("Enable HDCP signalling failed (%d)\n", ret);
+			return ret;
+		}
+	} else {
+		usleep_range(6, 60); /* Bspec says >= 6us */
+		ret = intel_ddi_disable_hdcp_signalling(&intel_dig_port->base);
+		if (ret) {
+			DRM_ERROR("Enable HDCP signalling failed (%d)\n", ret);
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static
+bool intel_hdmi_hdcp_check_link(struct intel_digital_port *intel_dig_port)
+{
+	struct drm_i915_private *dev_priv =
+		intel_dig_port->base.base.dev->dev_private;
+	enum port port = intel_dig_port->port;
+	int ret;
+	union {
+		u32 reg;
+		u8 shim[DRM_HDCP_RI_LEN];
+	} ri;
+
+	ret = intel_hdmi_hdcp_read_ri_prime(intel_dig_port, ri.shim);
+	if (ret)
+		return false;
+
+	I915_WRITE(SKL_PORT_HDCP_RPRIME(port), ri.reg);
+
+	// Wait for Ri prime match
+	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
+		     (SKL_HDCP_STATUS_RI_MATCH | SKL_HDCP_STATUS_ENC), 1)) {
+		DRM_ERROR("Ri' mismatch detected, link check failed (%x)\n",
+			  I915_READ(SKL_PORT_HDCP_STATUS(port)));
+		return false;
+	}
+	return true;
+}
+
+static const struct intel_hdcp_shim intel_hdmi_hdcp_shim = {
+	.write_an_aksv = intel_hdmi_hdcp_write_an_aksv,
+	.read_bksv = intel_hdmi_hdcp_read_bksv,
+	.read_bstatus = intel_hdmi_hdcp_read_bstatus,
+	.repeater_present = intel_hdmi_hdcp_repeater_present,
+	.read_ri_prime = intel_hdmi_hdcp_read_ri_prime,
+	.read_ksv_ready = intel_hdmi_hdcp_read_ksv_ready,
+	.read_ksv_fifo = intel_hdmi_hdcp_read_ksv_fifo,
+	.read_v_prime_part = intel_hdmi_hdcp_read_v_prime_part,
+	.toggle_signalling = intel_hdmi_hdcp_toggle_signalling,
+	.check_link = intel_hdmi_hdcp_check_link,
+};
+
 static void intel_hdmi_prepare(struct intel_encoder *encoder,
 			       const struct intel_crtc_state *crtc_state)
 {
@@ -2039,6 +2286,12 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
 
 	intel_hdmi_add_properties(intel_hdmi, connector);
 
+	if (INTEL_GEN(dev_priv) >= 9) {
+		drm_connector_attach_content_protection_property(connector);
+		intel_connector->hdcp_shim = &intel_hdmi_hdcp_shim;
+		INIT_DELAYED_WORK(&intel_connector->hdcp_work, intel_hdcp_work);
+	}
+
 	intel_connector_attach_encoder(intel_connector, intel_encoder);
 	intel_hdmi->attached_connector = intel_connector;
 
-- 
2.15.0.531.g2ccb3012c9-goog

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

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [RFC PATCH 6/6] drm/i915: Implement HDCP for DisplayPort
  2017-11-30  3:08 [RFC PATCH 0/6] drm/i915: Implement HDCP Sean Paul
                   ` (4 preceding siblings ...)
  2017-11-30  3:09   ` Sean Paul
@ 2017-11-30  3:09 ` Sean Paul
  2017-11-30  7:50 ` [Intel-gfx] [RFC PATCH 0/6] drm/i915: Implement HDCP Daniel Vetter
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-11-30  3:09 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: Sean Paul, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
	David Airlie, linux-kernel

This patch adds HDCP support for DisplayPort connectors by implementing
the intel_hdcp_shim.

Most of this is straightforward read/write from/to DPCD registers. One
thing worth pointing out is the Aksv output bit. It wasn't easily
separable like it's HDMI counterpart, so it's crammed in with the rest
of it.

Signed-off-by: Sean Paul <seanpaul@chromium.org>
---
 drivers/gpu/drm/i915/intel_dp.c | 243 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 236 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index d11281004109..091c8bcc4645 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -36,7 +36,9 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
 #include <drm/drm_edid.h>
+#include <drm/drm_hdcp.h>
 #include "intel_drv.h"
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
@@ -1039,10 +1041,29 @@ static uint32_t skl_get_aux_send_ctl(struct intel_dp *intel_dp,
 	       DP_AUX_CH_CTL_SYNC_PULSE_SKL(32);
 }
 
+static uint32_t intel_dp_get_aux_send_ctl(struct intel_dp *intel_dp,
+				          bool has_aux_irq,
+				          int send_bytes,
+				          uint32_t aux_clock_divider,
+					  bool aksv_write)
+{
+	uint32_t val = 0;
+
+	if (aksv_write) {
+		send_bytes += 5;
+		val |= DP_AUX_CH_CTL_AUX_AKSV_SELECT;
+	}
+
+	return val | intel_dp->get_aux_send_ctl(intel_dp,
+						has_aux_irq,
+						send_bytes,
+						aux_clock_divider);
+}
+
 static int
 intel_dp_aux_ch(struct intel_dp *intel_dp,
 		const uint8_t *send, int send_bytes,
-		uint8_t *recv, int recv_size)
+		uint8_t *recv, int recv_size, bool aksv_write)
 {
 	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
 	struct drm_i915_private *dev_priv =
@@ -1102,10 +1123,11 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
 	}
 
 	while ((aux_clock_divider = intel_dp->get_aux_clock_divider(intel_dp, clock++))) {
-		u32 send_ctl = intel_dp->get_aux_send_ctl(intel_dp,
-							  has_aux_irq,
-							  send_bytes,
-							  aux_clock_divider);
+		u32 send_ctl = intel_dp_get_aux_send_ctl(intel_dp,
+							 has_aux_irq,
+							 send_bytes,
+							 aux_clock_divider,
+							 aksv_write);
 
 		/* Must try at least 3 times according to DP spec */
 		for (try = 0; try < 5; try++) {
@@ -1242,7 +1264,8 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
 		if (msg->buffer)
 			memcpy(txbuf + HEADER_SIZE, msg->buffer, msg->size);
 
-		ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize);
+		ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize,
+				      false);
 		if (ret > 0) {
 			msg->reply = rxbuf[0] >> 4;
 
@@ -1264,7 +1287,8 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
 		if (WARN_ON(rxsize > 20))
 			return -E2BIG;
 
-		ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize);
+		ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize,
+				      false);
 		if (ret > 0) {
 			msg->reply = rxbuf[0] >> 4;
 			/*
@@ -4310,6 +4334,8 @@ intel_dp_check_link_status(struct intel_dp *intel_dp)
 
 		intel_dp_retrain_link(intel_dp);
 	}
+
+	intel_hdcp_check_link(intel_dp->attached_connector);
 }
 
 /*
@@ -4977,6 +5003,203 @@ void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder)
 	pps_unlock(intel_dp);
 }
 
+static
+int intel_dp_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port,
+				u8 *an)
+{
+	struct intel_dp *intel_dp = enc_to_intel_dp(&intel_dig_port->base.base);
+	uint8_t txbuf[4], rxbuf[2], reply = 0;
+	ssize_t dpcd_ret;
+	int ret;
+
+	/* Output An first, that's easy */
+	dpcd_ret = drm_dp_dpcd_write(&intel_dig_port->dp.aux, DP_AUX_HDCP_AN,
+				     an, DRM_HDCP_AN_LEN);
+	if (dpcd_ret != DRM_HDCP_AN_LEN) {
+		DRM_ERROR("Failed to write An over DP/AUX (%ld)\n", dpcd_ret);
+		return dpcd_ret >= 0 ? -EIO : dpcd_ret;
+	}
+
+	/*
+	 * Since Aksv is Oh-So-Secret, we can't access it in software. So in
+	 * order to get it on the wire, we need to create the AUX header as if
+	 * we were writing the data, and then tickle the hardware to output the
+	 * data once the header is sent out.
+	 */
+	txbuf[0] = (DP_AUX_NATIVE_WRITE << 4) |
+		   ((DP_AUX_HDCP_AKSV >> 16) & 0xf);
+	txbuf[1] = (DP_AUX_HDCP_AKSV >> 8) & 0xff;
+	txbuf[2] = DP_AUX_HDCP_AKSV & 0xff;
+	txbuf[3] = DRM_HDCP_KSV_LEN - 1;
+
+	ret = intel_dp_aux_ch(intel_dp, txbuf, sizeof(txbuf), rxbuf,
+			      sizeof(rxbuf), true);
+	if (ret < 0) {
+		DRM_ERROR("Write Aksv over DP/AUX failed (%d)\n", ret);
+		return ret;
+	} else if (ret == 0) {
+		DRM_ERROR("Aksv write over DP/AUX was empty\n");
+		return -EIO;
+	}
+
+	reply = (rxbuf[0] >> 4) & DP_AUX_NATIVE_REPLY_MASK;
+	return reply == DP_AUX_NATIVE_REPLY_ACK ? 0 : -EIO;
+}
+
+static int intel_dp_hdcp_read_bksv(struct intel_digital_port *intel_dig_port,
+				   u8 *bksv)
+{
+	ssize_t ret;
+	ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, DP_AUX_HDCP_BKSV, bksv,
+			       DRM_HDCP_KSV_LEN);
+	if (ret != DRM_HDCP_KSV_LEN) {
+		DRM_ERROR("Read Bksv from DP/AUX failed (%ld)\n", ret);
+		return ret >= 0 ? -EIO : ret;
+	}
+	return 0;
+}
+
+static int intel_dp_hdcp_read_bstatus(struct intel_digital_port *intel_dig_port,
+				      u8 *bstatus)
+{
+	ssize_t ret;
+	/*
+	 * For some reason the HDMI and DP HDCP specs call this register
+	 * definition by different names. In the HDMI spec, it's called BSTATUS,
+	 * but in DP it's called BINFO.
+	 */
+	ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, DP_AUX_HDCP_BINFO,
+			       bstatus, DRM_HDCP_BSTATUS_LEN);
+	if (ret != DRM_HDCP_BSTATUS_LEN) {
+		DRM_ERROR("Read bstatus from DP/AUX failed (%ld)\n", ret);
+		return ret >= 0 ? -EIO : ret;
+	}
+	return 0;
+}
+
+static
+int intel_dp_hdcp_repeater_present(struct intel_digital_port *intel_dig_port,
+				   bool *repeater_present)
+{
+	ssize_t ret;
+	u8 bcaps;
+	ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, DP_AUX_HDCP_BCAPS,
+			       &bcaps, 1);
+	if (ret != 1) {
+		DRM_ERROR("Read bcaps from DP/AUX failed (%ld)\n", ret);
+		return ret >= 0 ? -EIO : ret;
+	}
+	*repeater_present = bcaps & DP_BCAPS_REPEATER_PRESENT;
+	return 0;
+}
+
+static
+int intel_dp_hdcp_read_ri_prime(struct intel_digital_port *intel_dig_port,
+				u8 *ri_prime)
+{
+	ssize_t ret;
+	ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, DP_AUX_HDCP_RI_PRIME,
+			       ri_prime, DRM_HDCP_RI_LEN);
+	if (ret != DRM_HDCP_RI_LEN) {
+		DRM_ERROR("Read Ri' from DP/AUX failed (%ld)\n", ret);
+		return ret >= 0 ? -EIO : ret;
+	}
+	return 0;
+}
+
+static
+int intel_dp_hdcp_read_ksv_ready(struct intel_digital_port *intel_dig_port,
+				 bool *ksv_ready)
+{
+	ssize_t ret;
+	u8 bstatus;
+	ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, DP_AUX_HDCP_BSTATUS,
+			       &bstatus, 1);
+	if (ret != 1) {
+		DRM_ERROR("Read bstatus from DP/AUX failed (%ld)\n", ret);
+		return ret >= 0 ? -EIO : ret;
+	}
+	*ksv_ready = bstatus & DP_BSTATUS_READY;
+	return 0;
+}
+
+static
+int intel_dp_hdcp_read_ksv_fifo(struct intel_digital_port *intel_dig_port,
+			        int num_downstream, u8 *ksv_fifo)
+{
+	ssize_t ret;
+	int i;
+
+	// KSV list is read via 15 byte window (3 entries @ 5 bytes each)
+	for (i = 0; i < num_downstream; i += 3) {
+		size_t len = min(num_downstream - i, 3) * DRM_HDCP_KSV_LEN;
+		ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
+				       DP_AUX_HDCP_KSV_FIFO,
+				       ksv_fifo + i * DRM_HDCP_KSV_LEN,
+				       len);
+		if (ret != len) {
+			DRM_ERROR("Read ksv[%d] from DP/AUX failed (%ld)\n", i,
+				  ret);
+			return ret >= 0 ? -EIO : ret;
+		}
+	}
+	return 0;
+}
+
+static
+int intel_dp_hdcp_read_v_prime_part(struct intel_digital_port *intel_dig_port,
+				    int i, u32 *part)
+{
+	ssize_t ret;
+
+	if (i >= DRM_HDCP_V_PRIME_NUM_PARTS)
+		return -EINVAL;
+
+	ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
+			       DP_AUX_HDCP_V_PRIME(i), part,
+			       DRM_HDCP_V_PRIME_PART_LEN);
+	if (ret != DRM_HDCP_V_PRIME_PART_LEN) {
+		DRM_ERROR("Read v'[%d] from DP/AUX failed (%ld)\n", i, ret);
+		return ret >= 0 ? -EIO : ret;
+	}
+	return 0;
+}
+
+static
+int intel_dp_hdcp_toggle_signalling(struct intel_digital_port *intel_dig_port,
+				    bool enable)
+{
+	/* Not used for single stream DisplayPort setups */
+	return 0;
+}
+
+static
+bool intel_dp_hdcp_check_link(struct intel_digital_port *intel_dig_port)
+{
+	ssize_t ret;
+	u8 bstatus;
+	ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, DP_AUX_HDCP_BSTATUS,
+			       &bstatus, 1);
+	if (ret != 1) {
+		DRM_ERROR("Read bstatus from DP/AUX failed (%ld)\n", ret);
+		return ret >= 0 ? -EIO : ret;
+	}
+	return !(bstatus & DP_BSTATUS_LINK_FAILURE);
+}
+
+static const struct intel_hdcp_shim intel_dp_hdcp_shim = {
+	.write_an_aksv = intel_dp_hdcp_write_an_aksv,
+	.read_bksv = intel_dp_hdcp_read_bksv,
+	.read_bstatus = intel_dp_hdcp_read_bstatus,
+	.repeater_present = intel_dp_hdcp_repeater_present,
+	.read_ri_prime = intel_dp_hdcp_read_ri_prime,
+	.read_ksv_ready = intel_dp_hdcp_read_ksv_ready,
+	.read_ksv_fifo = intel_dp_hdcp_read_ksv_fifo,
+	.read_v_prime_part = intel_dp_hdcp_read_v_prime_part,
+	.toggle_signalling = intel_dp_hdcp_toggle_signalling,
+	.check_link = intel_dp_hdcp_check_link,
+};
+
 static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp)
 {
 	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
@@ -6097,6 +6320,12 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
 
 	intel_dp_add_properties(intel_dp, connector);
 
+	if (INTEL_GEN(dev_priv) >= 9 && !intel_dp_is_edp(intel_dp)) {
+		drm_connector_attach_content_protection_property(connector);
+		intel_connector->hdcp_shim = &intel_dp_hdcp_shim;
+		INIT_DELAYED_WORK(&intel_connector->hdcp_work, intel_hdcp_work);
+	}
+
 	/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
 	 * 0xd.  Failure to do so will result in spurious interrupts being
 	 * generated on the port when a cable is not attached.
-- 
2.15.0.531.g2ccb3012c9-goog

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* Re: [Intel-gfx] [RFC PATCH 0/6] drm/i915: Implement HDCP
  2017-11-30  3:08 [RFC PATCH 0/6] drm/i915: Implement HDCP Sean Paul
                   ` (5 preceding siblings ...)
  2017-11-30  3:09 ` [RFC PATCH 6/6] drm/i915: Implement HDCP for DisplayPort Sean Paul
@ 2017-11-30  7:50 ` Daniel Vetter
  2017-12-05 13:45   ` Ville Syrjälä
  2017-11-30  9:07 ` ✗ Fi.CI.BAT: failure for " Patchwork
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 62+ messages in thread
From: Daniel Vetter @ 2017-11-30  7:50 UTC (permalink / raw)
  To: Sean Paul; +Cc: intel-gfx, dri-devel

On Wed, Nov 29, 2017 at 10:08:55PM -0500, Sean Paul wrote:
> Here's the RFC for my i915 HDCP patchset. The UABI is based on what we've been
> using in Chrome for the past 3 years. I posted the property to the list back
> then, but never had a mainline driver to implement it. I do now :-)
> 
> Things are mostly in place, danvet gave me some feedback that I will
> incorporate in v1. However, in the interest of gaining more early feedback, I'm
> posting this now.
> 
> TODO:
> - Add kerneldoc for property

The big thing I'd like to see here is clear description of the flows
between kernel and userspace (since there's no helpers on the kernel side
to standardize this).

One thing we discussed in that context is the addition of an uevent (like
we do for anything else that changes with connectors, link_status is one
example). But since the hdcp spec explicitly demands that the video player
must poll the status an event is moot and won't be used. And I'm no fan of
speculative uapi :-)

> - Fix '//' comments
> - Change to MIT license
> - Rebase on Ville's gmbus fixes (thanks Ville)
> - Improve documentation on drm_intel_hdcp_shim
> - Fix async commit locking (ie: don't use connection_mutex)
> - Don't change connector->state in enable, defer to worker

Same holds for the disable callback, you can't touch state in there.

With the link_status prop (which is somewhat similar) we only reset it in
atomic_check (where we hold the state locks) and in the async worker (same
applies).
-Daniel

> - Add igt coverage for the feature.
> 
> Thanks!
> 
> Sean
> 
> 
> Sean Paul (6):
>   drm: Add Content Protection property
>   drm: Add some HDCP related #defines
>   drm/i915: Add HDCP framework + base implementation
>   drm/i915: Add function to output Aksv over GMBUS
>   drm/i915: Implement HDCP for HDMI
>   drm/i915: Implement HDCP for DisplayPort
> 
>  drivers/gpu/drm/drm_atomic.c        |   8 +
>  drivers/gpu/drm/drm_connector.c     |  43 +++
>  drivers/gpu/drm/drm_sysfs.c         |  29 ++
>  drivers/gpu/drm/i915/Makefile       |   1 +
>  drivers/gpu/drm/i915/i915_drv.h     |   1 +
>  drivers/gpu/drm/i915/i915_reg.h     |  85 +++++
>  drivers/gpu/drm/i915/intel_atomic.c |  26 +-
>  drivers/gpu/drm/i915/intel_ddi.c    |  64 ++++
>  drivers/gpu/drm/i915/intel_dp.c     | 243 +++++++++++++-
>  drivers/gpu/drm/i915/intel_drv.h    |  53 +++
>  drivers/gpu/drm/i915/intel_hdcp.c   | 636 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/i915/intel_hdmi.c   | 253 ++++++++++++++
>  drivers/gpu/drm/i915/intel_i2c.c    |  54 ++-
>  include/drm/drm_connector.h         |  16 +
>  include/drm/drm_dp_helper.h         |  17 +
>  include/drm/drm_hdcp.h              |  44 +++
>  include/uapi/drm/drm_mode.h         |   4 +
>  17 files changed, 1560 insertions(+), 17 deletions(-)
>  create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c
>  create mode 100644 include/drm/drm_hdcp.h
> 
> -- 
> 2.15.0.531.g2ccb3012c9-goog
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 62+ messages in thread

* ✗ Fi.CI.BAT: failure for drm/i915: Implement HDCP
  2017-11-30  3:08 [RFC PATCH 0/6] drm/i915: Implement HDCP Sean Paul
                   ` (6 preceding siblings ...)
  2017-11-30  7:50 ` [Intel-gfx] [RFC PATCH 0/6] drm/i915: Implement HDCP Daniel Vetter
@ 2017-11-30  9:07 ` Patchwork
  2017-11-30 10:05 ` Patchwork
  2017-11-30 15:15 ` Patchwork
  9 siblings, 0 replies; 62+ messages in thread
From: Patchwork @ 2017-11-30  9:07 UTC (permalink / raw)
  To: Sean Paul; +Cc: intel-gfx

== Series Details ==

Series: drm/i915: Implement HDCP
URL   : https://patchwork.freedesktop.org/series/34671/
State : failure

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Intel-gfx] [RFC PATCH 3/6] drm/i915: Add HDCP framework + base implementation
  2017-11-30  3:08   ` Sean Paul
  (?)
@ 2017-11-30  9:12   ` Chris Wilson
  -1 siblings, 0 replies; 62+ messages in thread
From: Chris Wilson @ 2017-11-30  9:12 UTC (permalink / raw)
  To: Sean Paul, dri-devel, intel-gfx; +Cc: David Airlie, linux-kernel, Rodrigo Vivi

Quoting Sean Paul (2017-11-30 03:08:58)
> This patch adds the framework required to add HDCP support to intel
> connectors. It implements Aksv loading from fuse, and parts 1/2/3
> of the HDCP authentication scheme.
> 
> Note that without shim implementations, this does not actually implement
> HDCP. That will come in subsequent patches.
> 
> Signed-off-by: Sean Paul <seanpaul@chromium.org>
> ---
>  drivers/gpu/drm/i915/Makefile       |   1 +
>  drivers/gpu/drm/i915/i915_reg.h     |  83 +++++
>  drivers/gpu/drm/i915/intel_atomic.c |  26 +-
>  drivers/gpu/drm/i915/intel_ddi.c    |  14 +
>  drivers/gpu/drm/i915/intel_drv.h    |  53 +++
>  drivers/gpu/drm/i915/intel_hdcp.c   | 636 ++++++++++++++++++++++++++++++++++++
>  6 files changed, 811 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c
> 
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> index 6c3b0481ef82..1e745508e437 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -87,6 +87,7 @@ i915-y += intel_audio.o \
>           intel_fbc.o \
>           intel_fifo_underrun.o \
>           intel_frontbuffer.o \
> +         intel_hdcp.o \
>           intel_hotplug.o \
>           intel_modes.o \
>           intel_overlay.o \
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index 68a58cce6ab1..43128030171d 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -7991,6 +7991,7 @@ enum {
>  #define     GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT   8
>  #define     GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT   16
>  #define     GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT   24
> +#define   SKL_PCODE_LOAD_HDCP_KEYS             0x5
>  #define   SKL_PCODE_CDCLK_CONTROL              0x7
>  #define     SKL_CDCLK_PREPARE_FOR_CHANGE       0x3
>  #define     SKL_CDCLK_READY_FOR_CHANGE         0x1
> @@ -8285,6 +8286,88 @@ enum skl_power_gate {
>  #define  SKL_PW_TO_PG(pw)                      ((pw) - SKL_DISP_PW_1 + SKL_PG1)
>  #define  SKL_FUSE_PG_DIST_STATUS(pg)           (1 << (27 - (pg)))
>  
> +
> +/* HDCP Key Registers */
> +#define SKL_HDCP_KEY_CONF              _MMIO(0x66c00)
> +#define         SKL_HDCP_AKSV_SEND_TRIGGER     BIT(31)
> +#define  SKL_HDCP_CLEAR_KEYS_TRIGGER   BIT(30)
> +#define SKL_HDCP_KEY_STATUS            _MMIO(0x66c04)
> +#define  SKL_HDCP_FUSE_IN_PROGRESS     BIT(7)
> +#define  SKL_HDCP_FUSE_ERROR           BIT(6)
> +#define  SKL_HDCP_FUSE_DONE            BIT(5)
> +#define  SKL_HDCP_KEY_LOAD_STATUS      BIT(1)
> +#define  SKL_HDCP_KEY_LOAD_DONE                BIT(0)
> +#define SKL_HDCP_AKSV_LO               _MMIO(0x66c10)
> +#define SKL_HDCP_AKSV_HI               _MMIO(0x66c14)
> +
> +/* HDCP Repeater Registers */
> +#define SKL_HDCP_REP_CTL               _MMIO(0x66d00)
> +#define  SKL_HDCP_DDIB_REP_PRESENT     BIT(30)
> +#define  SKL_HDCP_DDIA_REP_PRESENT     BIT(29)
> +#define  SKL_HDCP_DDIC_REP_PRESENT     BIT(28)
> +#define  SKL_HDCP_DDID_REP_PRESENT     BIT(27)
> +#define  SKL_HDCP_DDIF_REP_PRESENT     BIT(26)
> +#define  SKL_HDCP_DDIE_REP_PRESENT     BIT(25)
> +#define  SKL_HDCP_DDIB_SHA1_M0         (1 << 20)
> +#define  SKL_HDCP_DDIA_SHA1_M0         (2 << 20)
> +#define  SKL_HDCP_DDIC_SHA1_M0         (3 << 20)
> +#define  SKL_HDCP_DDID_SHA1_M0         (4 << 20)
> +#define  SKL_HDCP_DDIF_SHA1_M0         (5 << 20)
> +#define  SKL_HDCP_DDIE_SHA1_M0         (6 << 20) // Bspec says 5?
> +#define  SKL_HDCP_SHA1_BUSY            BIT(16)
> +#define  SKL_HDCP_SHA1_READY           BIT(17)
> +#define  SKL_HDCP_SHA1_COMPLETE                BIT(18)
> +#define  SKL_HDCP_SHA1_V_MATCH         BIT(19)
> +#define  SKL_HDCP_SHA1_TEXT_32         (1 << 1)
> +#define  SKL_HDCP_SHA1_COMPLETE_HASH   (2 << 1)
> +#define  SKL_HDCP_SHA1_TEXT_24         (4 << 1)
> +#define  SKL_HDCP_SHA1_TEXT_16         (5 << 1)
> +#define  SKL_HDCP_SHA1_TEXT_8          (6 << 1)
> +#define  SKL_HDCP_SHA1_TEXT_0          (7 << 1)
> +#define SKL_HDCP_SHA_V_PRIME_H0                _MMIO(0x66d04)
> +#define SKL_HDCP_SHA_V_PRIME_H1                _MMIO(0x66d08)
> +#define SKL_HDCP_SHA_V_PRIME_H2                _MMIO(0x66d0C)
> +#define SKL_HDCP_SHA_V_PRIME_H3                _MMIO(0x66d10)
> +#define SKL_HDCP_SHA_V_PRIME_H4                _MMIO(0x66d14)
> +#define SKL_HDCP_SHA_V_PRIME(h)                _MMIO((0x66d04 + h * 4))
> +#define SKL_HDCP_SHA_TEXT              _MMIO(0x66d18)
> +
> +/* HDCP Auth Registers */
> +#define _SKL_PORTA_HDCP_AUTHENC                0x66800
> +#define _SKL_PORTB_HDCP_AUTHENC                0x66500
> +#define _SKL_PORTC_HDCP_AUTHENC                0x66600
> +#define _SKL_PORTD_HDCP_AUTHENC                0x66700
> +#define _SKL_PORTE_HDCP_AUTHENC                0x66A00
> +#define _SKL_PORTF_HDCP_AUTHENC                0x66900
> +#define _SKL_PORT_HDCP_AUTHENC(port, x)        _MMIO(_PICK(port, \
> +                                         _SKL_PORTA_HDCP_AUTHENC, \
> +                                         _SKL_PORTB_HDCP_AUTHENC, \
> +                                         _SKL_PORTC_HDCP_AUTHENC, \
> +                                         _SKL_PORTD_HDCP_AUTHENC, \
> +                                         _SKL_PORTE_HDCP_AUTHENC, \
> +                                         _SKL_PORTF_HDCP_AUTHENC) + x)
> +#define SKL_PORT_HDCP_CONF(port)       _SKL_PORT_HDCP_AUTHENC(port, 0x0)
> +#define  SKL_HDCP_CONF_CAPTURE_AN      BIT(0)
> +#define  SKL_HDCP_CONF_AUTH_AND_ENC    (BIT(1) | BIT(0))
> +#define SKL_PORT_HDCP_ANINIT(port)     _SKL_PORT_HDCP_AUTHENC(port, 0x4)
> +#define SKL_PORT_HDCP_ANLO(port)       _SKL_PORT_HDCP_AUTHENC(port, 0x8)
> +#define SKL_PORT_HDCP_ANHI(port)       _SKL_PORT_HDCP_AUTHENC(port, 0xC)
> +#define SKL_PORT_HDCP_BKSVLO(port)     _SKL_PORT_HDCP_AUTHENC(port, 0x10)
> +#define SKL_PORT_HDCP_BKSVHI(port)     _SKL_PORT_HDCP_AUTHENC(port, 0x14)
> +#define SKL_PORT_HDCP_RPRIME(port)     _SKL_PORT_HDCP_AUTHENC(port, 0x18)
> +#define SKL_PORT_HDCP_STATUS(port)     _SKL_PORT_HDCP_AUTHENC(port, 0x1C)
> +#define  SKL_HDCP_STATUS_STREAM_A_ENC  BIT(31)
> +#define  SKL_HDCP_STATUS_STREAM_B_ENC  BIT(30)
> +#define  SKL_HDCP_STATUS_STREAM_C_ENC  BIT(29)
> +#define  SKL_HDCP_STATUS_STREAM_D_ENC  BIT(28)
> +#define  SKL_HDCP_STATUS_AUTH          BIT(21)
> +#define  SKL_HDCP_STATUS_ENC           BIT(20)
> +#define  SKL_HDCP_STATUS_RI_MATCH      BIT(19)
> +#define  SKL_HDCP_STATUS_R0_READY      BIT(18)
> +#define  SKL_HDCP_STATUS_AN_READY      BIT(17)
> +#define  SKL_HDCP_STATUS_CIPHER                BIT(16)
> +#define  SKL_HDCP_STATUS_FRAME_CNT(x)  ((x >> 8) & 0xff)
> +
>  /* Per-pipe DDI Function Control */
>  #define _TRANS_DDI_FUNC_CTL_A          0x60400
>  #define _TRANS_DDI_FUNC_CTL_B          0x61400
> diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
> index 36d4e635e4ce..ddf08227d9cb 100644
> --- a/drivers/gpu/drm/i915/intel_atomic.c
> +++ b/drivers/gpu/drm/i915/intel_atomic.c
> @@ -109,12 +109,34 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
>         struct intel_digital_connector_state *old_conn_state =
>                 to_intel_digital_connector_state(old_state);
>         struct drm_crtc_state *crtc_state;
> -
> -       if (!new_state->crtc)
> +       uint64_t old_cp = old_conn_state->base.content_protection;
> +       uint64_t new_cp = new_state->content_protection;
> +
> +       if (!new_state->crtc) {
> +               /* 
> +                * If the connector is being disabled with CP enabled, mark it
> +                * desired so it's re-enabled when the connector is brought back
> +                */
> +               if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
> +                       new_state->content_protection =
> +                               DRM_MODE_CONTENT_PROTECTION_DESIRED;
>                 return 0;
> +       }
>  
>         crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc);
>  
> +       if (new_cp != old_cp) {
> +               /* Only drivers can set content protection enabled */
> +               if (new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
> +                       new_state->content_protection =
> +                               DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +
> +               /* Involve the encoder/connector to enable/disable CP */
> +               if (new_cp == DRM_MODE_CONTENT_PROTECTION_OFF ||
> +                   old_cp == DRM_MODE_CONTENT_PROTECTION_OFF)
> +                       crtc_state->mode_changed = true;
> +       }
> +
>         /*
>          * These properties are handled by fastset, and might not end
>          * up in a modeset.
> diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
> index 933c18fd4258..0e69337f410d 100644
> --- a/drivers/gpu/drm/i915/intel_ddi.c
> +++ b/drivers/gpu/drm/i915/intel_ddi.c
> @@ -2432,10 +2432,17 @@ static void intel_enable_ddi(struct intel_encoder *encoder,
>                              const struct intel_crtc_state *crtc_state,
>                              const struct drm_connector_state *conn_state)
>  {
> +       struct drm_connector *connector = conn_state->connector;
> +       struct intel_connector *intel_connector = to_intel_connector(connector);
> +
>         if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
>                 intel_enable_ddi_hdmi(encoder, crtc_state, conn_state);
>         else
>                 intel_enable_ddi_dp(encoder, crtc_state, conn_state);
> +
> +       if (conn_state->content_protection ==
> +                       DRM_MODE_CONTENT_PROTECTION_DESIRED)
> +               intel_hdcp_enable(intel_connector);
>  }
>  
>  static void intel_disable_ddi_dp(struct intel_encoder *encoder,
> @@ -2468,10 +2475,17 @@ static void intel_disable_ddi(struct intel_encoder *encoder,
>                               const struct intel_crtc_state *old_crtc_state,
>                               const struct drm_connector_state *old_conn_state)
>  {
> +       struct drm_connector *connector = old_conn_state->connector;
> +       struct intel_connector *intel_connector = to_intel_connector(connector);
> +
>         if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
>                 intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state);
>         else
>                 intel_disable_ddi_dp(encoder, old_crtc_state, old_conn_state);
> +
> +       if (old_conn_state->content_protection !=
> +                       DRM_MODE_CONTENT_PROTECTION_OFF)
> +               intel_hdcp_disable(intel_connector);
>  }
>  
>  static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder,
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 47d022d48718..8924004575b8 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -299,6 +299,49 @@ struct intel_panel {
>         } backlight;
>  };
>  
> +struct intel_hdcp_shim {
> +       /* Outputs the transmitter's An and Aksv values to the receiver. */
> +       int (*write_an_aksv)(struct intel_digital_port *intel_dig_port, u8 *an);
> +
> +       /* Reads the receiver's key selection vector */
> +       int (*read_bksv)(struct intel_digital_port *intel_dig_port, u8 *bksv);
> +
> +       /*
> +        * Reads BINFO from DP receivers and BSTATUS from HDMI receivers. The
> +        * definitions are the same in the respective specs, but the names are
> +        * different. Call it BSTATUS since that's the name the HDMI spec
> +        * uses and it was there first.
> +        */
> +       int (*read_bstatus)(struct intel_digital_port *intel_dig_port,
> +                           u8 *bstatus);
> +
> +       /* Determines whether a repeater is present downstream */
> +       int (*repeater_present)(struct intel_digital_port *intel_dig_port,
> +                               bool *repeater_present);
> +
> +       /* Reads the receiver's Ri' value */
> +       int (*read_ri_prime)(struct intel_digital_port *intel_dig_port, u8 *ri);
> +
> +       /* Determines if the receiver's KSV FIFO is ready for consumption */
> +       int (*read_ksv_ready)(struct intel_digital_port *intel_dig_port,
> +                             bool *ksv_ready);
> +
> +       /* Reads the ksv fifo for num_downstream devices */
> +       int (*read_ksv_fifo)(struct intel_digital_port *intel_dig_port,
> +                            int num_downstream, u8 *ksv_fifo);
> +
> +       /* Reads a 32-bit part of V' from the receiver */
> +       int (*read_v_prime_part)(struct intel_digital_port *intel_dig_port,
> +                                int i, u32 *part);
> +
> +       /* Enables HDCP signalling on the port */
> +       int (*toggle_signalling)(struct intel_digital_port *intel_dig_port,
> +                                bool enable);
> +
> +       /* Ensures the link is still protected */
> +       bool (*check_link)(struct intel_digital_port *intel_dig_port);
> +};
> +
>  struct intel_connector {
>         struct drm_connector base;
>         /*
> @@ -330,6 +373,9 @@ struct intel_connector {
>  
>         /* Work struct to schedule a uevent on link train failure */
>         struct work_struct modeset_retry_work;
> +
> +       const struct intel_hdcp_shim *hdcp_shim;
> +       struct delayed_work hdcp_work;
>  };
>  
>  struct intel_digital_connector_state {
> @@ -1295,6 +1341,8 @@ void intel_ddi_set_vc_payload_alloc(const struct intel_crtc_state *crtc_state,
>                                     bool state);
>  u32 bxt_signal_levels(struct intel_dp *intel_dp);
>  uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
> +int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder);
> +int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder);
>  u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
>  
>  unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
> @@ -1746,6 +1794,11 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
>  }
>  #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
>  
> +/* intel_hdcp.c */
> +int intel_hdcp_enable(struct intel_connector *connector);
> +int intel_hdcp_disable(struct intel_connector *connector);
> +int intel_hdcp_check_link(struct intel_connector *connector);
> +void intel_hdcp_work(struct work_struct *work);
>  
>  /* intel_psr.c */
>  void intel_psr_enable(struct intel_dp *intel_dp,
> diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
> new file mode 100644
> index 000000000000..a2a575ed657e
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_hdcp.c
> @@ -0,0 +1,636 @@
> +/*
> + * Copyright (C) 2017 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_hdcp.h>
> +#include <linux/i2c.h>
> +#include <linux/random.h>
> +
> +#include "intel_drv.h"
> +#include "i915_reg.h"
> +
> +#define KEY_LOAD_TRIES 5
> +
> +static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
> +                                   const struct intel_hdcp_shim *shim)
> +{
> +       unsigned long timeout = jiffies + msecs_to_jiffies_timeout(500);
> +       int ret;
> +       bool ksv_ready;
> +
> +       while (true) {
> +               ret = shim->read_ksv_ready(intel_dig_port, &ksv_ready);
> +               if (ret)
> +                       return ret;
> +               if (ksv_ready)
> +                       break;
> +               if (time_after(jiffies, timeout))
> +                       return -ETIMEDOUT;
> +               msleep(100);
> +       }

__wait_for() (useful so that we can track all the horrible polling
code).  I hope nothing time critical contends with this path! ;)

> +       return 0;
> +}
> +
> +static void intel_hdcp_clear_keys(struct drm_i915_private *dev_priv)
> +{
> +       I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_CLEAR_KEYS_TRIGGER);
> +       I915_WRITE(SKL_HDCP_KEY_STATUS,
> +                  SKL_HDCP_KEY_LOAD_DONE | SKL_HDCP_KEY_LOAD_STATUS |
> +                  SKL_HDCP_FUSE_IN_PROGRESS | SKL_HDCP_FUSE_ERROR |
> +                  SKL_HDCP_FUSE_DONE);
> +}
> +
> +static int intel_hdcp_load_keys(struct drm_i915_private *dev_priv)
> +{
> +       unsigned long timeout;
> +       int ret;
> +       u32 val;
> +
> +       // Initiate loading the HDCP key from fuses
> +       mutex_lock(&dev_priv->pcu_lock);
> +       ret = sandybridge_pcode_write(dev_priv, SKL_PCODE_LOAD_HDCP_KEYS, 1);
> +       mutex_unlock(&dev_priv->pcu_lock);
> +       if (ret) {
> +               DRM_ERROR("Failed to initiate HDCP key load (%d)\n", ret);
> +               return ret;
> +       }
> +
> +       // Wait for the keys to load (500us)
> +       timeout = jiffies + nsecs_to_jiffies_timeout(500 * 1000);
> +       while (true) {
> +               val = I915_READ(SKL_HDCP_KEY_STATUS);
> +               if (val & SKL_HDCP_KEY_LOAD_DONE)
> +                       break;
> +               if (time_after(jiffies, timeout))
> +                       return -ETIMEDOUT;
> +               usleep_range(50, 100);
> +       }
> +       if (!(val & SKL_HDCP_KEY_LOAD_STATUS))
> +               return -ENXIO;

err = __intel_wait_for_register(dev_priv,
				SKL_HDCP_KEY_STATUS,
				SKL_HDCP_KEY_LOAD_DONE,
				SKL_HDCP_KEY_LOAD_DONE,
				10, 1000, &val);
if (err)
	return err;

if (!(val & SKL_HDCP_KEY_LOAD_STATUS))
	return -ENXIO;

> +
> +       // Send Aksv over to PCH display for use in authentication
> +       I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_AKSV_SEND_TRIGGER);
> +
> +       return 0;
> +}
> +
> +/* Returns updated SHA-1 index */
> +static int intel_write_sha_text(struct drm_i915_private *dev_priv, u32 sha_text)
> +{
> +       I915_WRITE(SKL_HDCP_SHA_TEXT, sha_text);

> +       if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_READY, 1)) {

	intel_wait_for_register(dev_priv,
			       SKL_HDCP_REP_CTL,
			       SKL_HDCP_SHA1_READY,
			       1,
			       10, 1000);

The wait_for() macro is massive. If it can be done using
intel_wait_for_register, do so. If not, try to provide a function that
encapsulates the wait_for() with the functionality you need.

The advantage of intel_wait_for_register() is that switches between
polling for a fast response and sleeping for a slow response. For most
registers, they typically respond quickly.
-Chris

^ permalink raw reply	[flat|nested] 62+ messages in thread

* ✗ Fi.CI.BAT: failure for drm/i915: Implement HDCP
  2017-11-30  3:08 [RFC PATCH 0/6] drm/i915: Implement HDCP Sean Paul
                   ` (7 preceding siblings ...)
  2017-11-30  9:07 ` ✗ Fi.CI.BAT: failure for " Patchwork
@ 2017-11-30 10:05 ` Patchwork
  2017-11-30 15:15 ` Patchwork
  9 siblings, 0 replies; 62+ messages in thread
From: Patchwork @ 2017-11-30 10:05 UTC (permalink / raw)
  To: Sean Paul; +Cc: intel-gfx

== Series Details ==

Series: drm/i915: Implement HDCP
URL   : https://patchwork.freedesktop.org/series/34671/
State : failure

== Summary ==

  CHK     include/config/kernel.release
  CHK     include/generated/uapi/linux/version.h
  CHK     include/generated/utsrelease.h
  CHK     include/generated/bounds.h
  CHK     include/generated/timeconst.h
  CHK     include/generated/asm-offsets.h
  CALL    scripts/checksyscalls.sh
  DESCEND  objtool
  CHK     scripts/mod/devicetable-offsets.h
  CHK     include/generated/compile.h
  CHK     kernel/config_data.h
  CC [M]  drivers/gpu/drm/i915/intel_hdcp.o
drivers/gpu/drm/i915/intel_hdcp.c: In function ‘intel_hdcp_get_repeater_ctl’:
drivers/gpu/drm/i915/intel_hdcp.c:101:33: error: ‘struct intel_digital_port’ has no member named ‘port’
  enum port port = intel_dig_port->port;
                                 ^~
drivers/gpu/drm/i915/intel_hdcp.c: In function ‘intel_hdcp_auth’:
drivers/gpu/drm/i915/intel_hdcp.c:385:23: error: ‘struct intel_digital_port’ has no member named ‘port’
  port = intel_dig_port->port;
                       ^~
drivers/gpu/drm/i915/intel_hdcp.c: In function ‘_intel_hdcp_disable’:
drivers/gpu/drm/i915/intel_hdcp.c:482:33: error: ‘struct intel_digital_port’ has no member named ‘port’
  enum port port = intel_dig_port->port;
                                 ^~
drivers/gpu/drm/i915/intel_hdcp.c: In function ‘intel_hdcp_check_link’:
drivers/gpu/drm/i915/intel_hdcp.c:596:33: error: ‘struct intel_digital_port’ has no member named ‘port’
  enum port port = intel_dig_port->port;
                                 ^~
scripts/Makefile.build:310: recipe for target 'drivers/gpu/drm/i915/intel_hdcp.o' failed
make[4]: *** [drivers/gpu/drm/i915/intel_hdcp.o] Error 1
scripts/Makefile.build:569: recipe for target 'drivers/gpu/drm/i915' failed
make[3]: *** [drivers/gpu/drm/i915] Error 2
scripts/Makefile.build:569: recipe for target 'drivers/gpu/drm' failed
make[2]: *** [drivers/gpu/drm] Error 2
scripts/Makefile.build:569: recipe for target 'drivers/gpu' failed
make[1]: *** [drivers/gpu] Error 2
Makefile:1012: recipe for target 'drivers' failed
make: *** [drivers] Error 2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* ✗ Fi.CI.BAT: failure for drm/i915: Implement HDCP
  2017-11-30  3:08 [RFC PATCH 0/6] drm/i915: Implement HDCP Sean Paul
                   ` (8 preceding siblings ...)
  2017-11-30 10:05 ` Patchwork
@ 2017-11-30 15:15 ` Patchwork
  9 siblings, 0 replies; 62+ messages in thread
From: Patchwork @ 2017-11-30 15:15 UTC (permalink / raw)
  To: Sean Paul; +Cc: intel-gfx

== Series Details ==

Series: drm/i915: Implement HDCP
URL   : https://patchwork.freedesktop.org/series/34671/
State : failure

== Summary ==

  CHK     include/config/kernel.release
  CHK     include/generated/uapi/linux/version.h
  CHK     include/generated/utsrelease.h
  CHK     include/generated/bounds.h
  CHK     include/generated/timeconst.h
  CHK     include/generated/asm-offsets.h
  CALL    scripts/checksyscalls.sh
  DESCEND  objtool
  CHK     scripts/mod/devicetable-offsets.h
  CHK     include/generated/compile.h
  CHK     kernel/config_data.h
  CC [M]  drivers/gpu/drm/i915/intel_hdcp.o
drivers/gpu/drm/i915/intel_hdcp.c: In function ‘intel_hdcp_get_repeater_ctl’:
drivers/gpu/drm/i915/intel_hdcp.c:101:33: error: ‘struct intel_digital_port’ has no member named ‘port’
  enum port port = intel_dig_port->port;
                                 ^~
drivers/gpu/drm/i915/intel_hdcp.c: In function ‘intel_hdcp_auth’:
drivers/gpu/drm/i915/intel_hdcp.c:385:23: error: ‘struct intel_digital_port’ has no member named ‘port’
  port = intel_dig_port->port;
                       ^~
drivers/gpu/drm/i915/intel_hdcp.c: In function ‘_intel_hdcp_disable’:
drivers/gpu/drm/i915/intel_hdcp.c:482:33: error: ‘struct intel_digital_port’ has no member named ‘port’
  enum port port = intel_dig_port->port;
                                 ^~
drivers/gpu/drm/i915/intel_hdcp.c: In function ‘intel_hdcp_check_link’:
drivers/gpu/drm/i915/intel_hdcp.c:596:33: error: ‘struct intel_digital_port’ has no member named ‘port’
  enum port port = intel_dig_port->port;
                                 ^~
scripts/Makefile.build:310: recipe for target 'drivers/gpu/drm/i915/intel_hdcp.o' failed
make[4]: *** [drivers/gpu/drm/i915/intel_hdcp.o] Error 1
scripts/Makefile.build:569: recipe for target 'drivers/gpu/drm/i915' failed
make[3]: *** [drivers/gpu/drm/i915] Error 2
scripts/Makefile.build:569: recipe for target 'drivers/gpu/drm' failed
make[2]: *** [drivers/gpu/drm] Error 2
scripts/Makefile.build:569: recipe for target 'drivers/gpu' failed
make[1]: *** [drivers/gpu] Error 2
Makefile:1012: recipe for target 'drivers' failed
make: *** [drivers] Error 2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 3/6] drm/i915: Add HDCP framework + base implementation
  2017-11-30  3:08   ` Sean Paul
@ 2017-12-01  7:23     ` Ramalingam C
  -1 siblings, 0 replies; 62+ messages in thread
From: Ramalingam C @ 2017-12-01  7:23 UTC (permalink / raw)
  To: Sean Paul, dri-devel, intel-gfx
  Cc: David Airlie, Joonas Lahtinen, linux-kernel, Rodrigo Vivi

Sean,

IMHO, it will good if we can have all generic hdcp1.4 authentication 
flow in drm helpers and all interested display drivers to use them.

This Design will make the extending of hdcp easy for other display 
drivers based on DRM.

We can have the required drm_hdcp_shim type of implementation at drm 
structure which will be called for platform specific operations (like 
prepare an, send aksv, program bksv/repeater/r0 and verify sha1 etc)?


On Thursday 30 November 2017 08:38 AM, Sean Paul wrote:
> This patch adds the framework required to add HDCP support to intel
> connectors. It implements Aksv loading from fuse, and parts 1/2/3
> of the HDCP authentication scheme.
>
> Note that without shim implementations, this does not actually implement
> HDCP. That will come in subsequent patches.
>
> Signed-off-by: Sean Paul <seanpaul@chromium.org>
> ---
>   drivers/gpu/drm/i915/Makefile       |   1 +
>   drivers/gpu/drm/i915/i915_reg.h     |  83 +++++
>   drivers/gpu/drm/i915/intel_atomic.c |  26 +-
>   drivers/gpu/drm/i915/intel_ddi.c    |  14 +
>   drivers/gpu/drm/i915/intel_drv.h    |  53 +++
>   drivers/gpu/drm/i915/intel_hdcp.c   | 636 ++++++++++++++++++++++++++++++++++++
>   6 files changed, 811 insertions(+), 2 deletions(-)
>   create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c
>
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> index 6c3b0481ef82..1e745508e437 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -87,6 +87,7 @@ i915-y += intel_audio.o \
>   	  intel_fbc.o \
>   	  intel_fifo_underrun.o \
>   	  intel_frontbuffer.o \
> +	  intel_hdcp.o \
>   	  intel_hotplug.o \
>   	  intel_modes.o \
>   	  intel_overlay.o \
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index 68a58cce6ab1..43128030171d 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -7991,6 +7991,7 @@ enum {
>   #define     GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT	8
>   #define     GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT	16
>   #define     GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT	24
> +#define   SKL_PCODE_LOAD_HDCP_KEYS		0x5
>   #define   SKL_PCODE_CDCLK_CONTROL		0x7
>   #define     SKL_CDCLK_PREPARE_FOR_CHANGE	0x3
>   #define     SKL_CDCLK_READY_FOR_CHANGE		0x1
> @@ -8285,6 +8286,88 @@ enum skl_power_gate {
>   #define  SKL_PW_TO_PG(pw)			((pw) - SKL_DISP_PW_1 + SKL_PG1)
>   #define  SKL_FUSE_PG_DIST_STATUS(pg)		(1 << (27 - (pg)))
>   
> +
> +/* HDCP Key Registers */
> +#define SKL_HDCP_KEY_CONF		_MMIO(0x66c00)
> +#define	 SKL_HDCP_AKSV_SEND_TRIGGER	BIT(31)
> +#define  SKL_HDCP_CLEAR_KEYS_TRIGGER	BIT(30)
> +#define SKL_HDCP_KEY_STATUS		_MMIO(0x66c04)
> +#define  SKL_HDCP_FUSE_IN_PROGRESS	BIT(7)
> +#define  SKL_HDCP_FUSE_ERROR		BIT(6)
> +#define  SKL_HDCP_FUSE_DONE		BIT(5)
> +#define  SKL_HDCP_KEY_LOAD_STATUS	BIT(1)
> +#define  SKL_HDCP_KEY_LOAD_DONE		BIT(0)
> +#define SKL_HDCP_AKSV_LO		_MMIO(0x66c10)
> +#define SKL_HDCP_AKSV_HI		_MMIO(0x66c14)
> +
> +/* HDCP Repeater Registers */
> +#define SKL_HDCP_REP_CTL		_MMIO(0x66d00)
> +#define  SKL_HDCP_DDIB_REP_PRESENT	BIT(30)
> +#define  SKL_HDCP_DDIA_REP_PRESENT	BIT(29)
> +#define  SKL_HDCP_DDIC_REP_PRESENT	BIT(28)
> +#define  SKL_HDCP_DDID_REP_PRESENT	BIT(27)
> +#define  SKL_HDCP_DDIF_REP_PRESENT	BIT(26)
> +#define  SKL_HDCP_DDIE_REP_PRESENT	BIT(25)
> +#define  SKL_HDCP_DDIB_SHA1_M0		(1 << 20)
> +#define  SKL_HDCP_DDIA_SHA1_M0		(2 << 20)
> +#define  SKL_HDCP_DDIC_SHA1_M0		(3 << 20)
> +#define  SKL_HDCP_DDID_SHA1_M0		(4 << 20)
> +#define  SKL_HDCP_DDIF_SHA1_M0		(5 << 20)
> +#define  SKL_HDCP_DDIE_SHA1_M0		(6 << 20) // Bspec says 5?
> +#define  SKL_HDCP_SHA1_BUSY		BIT(16)
> +#define  SKL_HDCP_SHA1_READY		BIT(17)
> +#define  SKL_HDCP_SHA1_COMPLETE		BIT(18)
> +#define  SKL_HDCP_SHA1_V_MATCH		BIT(19)
> +#define  SKL_HDCP_SHA1_TEXT_32		(1 << 1)
> +#define  SKL_HDCP_SHA1_COMPLETE_HASH	(2 << 1)
> +#define  SKL_HDCP_SHA1_TEXT_24		(4 << 1)
> +#define  SKL_HDCP_SHA1_TEXT_16		(5 << 1)
> +#define  SKL_HDCP_SHA1_TEXT_8		(6 << 1)
> +#define  SKL_HDCP_SHA1_TEXT_0		(7 << 1)
> +#define SKL_HDCP_SHA_V_PRIME_H0		_MMIO(0x66d04)
> +#define SKL_HDCP_SHA_V_PRIME_H1		_MMIO(0x66d08)
> +#define SKL_HDCP_SHA_V_PRIME_H2		_MMIO(0x66d0C)
> +#define SKL_HDCP_SHA_V_PRIME_H3		_MMIO(0x66d10)
> +#define SKL_HDCP_SHA_V_PRIME_H4		_MMIO(0x66d14)
> +#define SKL_HDCP_SHA_V_PRIME(h)		_MMIO((0x66d04 + h * 4))
> +#define SKL_HDCP_SHA_TEXT		_MMIO(0x66d18)
> +
> +/* HDCP Auth Registers */
> +#define _SKL_PORTA_HDCP_AUTHENC		0x66800
> +#define _SKL_PORTB_HDCP_AUTHENC		0x66500
> +#define _SKL_PORTC_HDCP_AUTHENC		0x66600
> +#define _SKL_PORTD_HDCP_AUTHENC		0x66700
> +#define _SKL_PORTE_HDCP_AUTHENC		0x66A00
> +#define _SKL_PORTF_HDCP_AUTHENC		0x66900
> +#define _SKL_PORT_HDCP_AUTHENC(port, x)	_MMIO(_PICK(port, \
> +					  _SKL_PORTA_HDCP_AUTHENC, \
> +					  _SKL_PORTB_HDCP_AUTHENC, \
> +					  _SKL_PORTC_HDCP_AUTHENC, \
> +					  _SKL_PORTD_HDCP_AUTHENC, \
> +					  _SKL_PORTE_HDCP_AUTHENC, \
> +					  _SKL_PORTF_HDCP_AUTHENC) + x)
> +#define SKL_PORT_HDCP_CONF(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x0)
> +#define  SKL_HDCP_CONF_CAPTURE_AN	BIT(0)
> +#define  SKL_HDCP_CONF_AUTH_AND_ENC	(BIT(1) | BIT(0))
> +#define SKL_PORT_HDCP_ANINIT(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x4)
> +#define SKL_PORT_HDCP_ANLO(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x8)
> +#define SKL_PORT_HDCP_ANHI(port)	_SKL_PORT_HDCP_AUTHENC(port, 0xC)
> +#define SKL_PORT_HDCP_BKSVLO(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x10)
> +#define SKL_PORT_HDCP_BKSVHI(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x14)
> +#define SKL_PORT_HDCP_RPRIME(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x18)
> +#define SKL_PORT_HDCP_STATUS(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x1C)
> +#define  SKL_HDCP_STATUS_STREAM_A_ENC	BIT(31)
> +#define  SKL_HDCP_STATUS_STREAM_B_ENC	BIT(30)
> +#define  SKL_HDCP_STATUS_STREAM_C_ENC	BIT(29)
> +#define  SKL_HDCP_STATUS_STREAM_D_ENC	BIT(28)
> +#define  SKL_HDCP_STATUS_AUTH		BIT(21)
> +#define  SKL_HDCP_STATUS_ENC		BIT(20)
> +#define  SKL_HDCP_STATUS_RI_MATCH	BIT(19)
> +#define  SKL_HDCP_STATUS_R0_READY	BIT(18)
> +#define  SKL_HDCP_STATUS_AN_READY	BIT(17)
> +#define  SKL_HDCP_STATUS_CIPHER		BIT(16)
> +#define  SKL_HDCP_STATUS_FRAME_CNT(x)	((x >> 8) & 0xff)
> +
>   /* Per-pipe DDI Function Control */
>   #define _TRANS_DDI_FUNC_CTL_A		0x60400
>   #define _TRANS_DDI_FUNC_CTL_B		0x61400
> diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
> index 36d4e635e4ce..ddf08227d9cb 100644
> --- a/drivers/gpu/drm/i915/intel_atomic.c
> +++ b/drivers/gpu/drm/i915/intel_atomic.c
> @@ -109,12 +109,34 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
>   	struct intel_digital_connector_state *old_conn_state =
>   		to_intel_digital_connector_state(old_state);
>   	struct drm_crtc_state *crtc_state;
> -
> -	if (!new_state->crtc)
> +	uint64_t old_cp = old_conn_state->base.content_protection;
> +	uint64_t new_cp = new_state->content_protection;
> +
> +	if (!new_state->crtc) {
> +		/*
> +		 * If the connector is being disabled with CP enabled, mark it
> +		 * desired so it's re-enabled when the connector is brought back
> +		 */
> +		if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
> +			new_state->content_protection =
> +				DRM_MODE_CONTENT_PROTECTION_DESIRED;
>   		return 0;
> +	}
>   
>   	crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc);
>   
> +	if (new_cp != old_cp) {
> +		/* Only drivers can set content protection enabled */
> +		if (new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
> +			new_state->content_protection =
> +				DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +
> +		/* Involve the encoder/connector to enable/disable CP */
> +		if (new_cp == DRM_MODE_CONTENT_PROTECTION_OFF ||
> +		    old_cp == DRM_MODE_CONTENT_PROTECTION_OFF)
> +			crtc_state->mode_changed = true;

We need not perform the mode set for hdcp enable/disable.
Authentication and encryption can be started on active port.

> +	}
> +
>   	/*
>   	 * These properties are handled by fastset, and might not end
>   	 * up in a modeset.
> diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
> index 933c18fd4258..0e69337f410d 100644
> --- a/drivers/gpu/drm/i915/intel_ddi.c
> +++ b/drivers/gpu/drm/i915/intel_ddi.c
> @@ -2432,10 +2432,17 @@ static void intel_enable_ddi(struct intel_encoder *encoder,
>   			     const struct intel_crtc_state *crtc_state,
>   			     const struct drm_connector_state *conn_state)
>   {
> +	struct drm_connector *connector = conn_state->connector;
> +	struct intel_connector *intel_connector = to_intel_connector(connector);
> +
>   	if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
>   		intel_enable_ddi_hdmi(encoder, crtc_state, conn_state);
>   	else
>   		intel_enable_ddi_dp(encoder, crtc_state, conn_state);
> +
> +	if (conn_state->content_protection ==
> +			DRM_MODE_CONTENT_PROTECTION_DESIRED)
> +		intel_hdcp_enable(intel_connector);
>   }
>   
>   static void intel_disable_ddi_dp(struct intel_encoder *encoder,
> @@ -2468,10 +2475,17 @@ static void intel_disable_ddi(struct intel_encoder *encoder,
>   			      const struct intel_crtc_state *old_crtc_state,
>   			      const struct drm_connector_state *old_conn_state)
>   {
> +	struct drm_connector *connector = old_conn_state->connector;
> +	struct intel_connector *intel_connector = to_intel_connector(connector);
> +
>   	if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
>   		intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state);
>   	else
>   		intel_disable_ddi_dp(encoder, old_crtc_state, old_conn_state);
> +
> +	if (old_conn_state->content_protection !=
> +			DRM_MODE_CONTENT_PROTECTION_OFF)
> +		intel_hdcp_disable(intel_connector);
We might want to disable the hdcp before disabling the DDI. Actually we 
could trigger hdcp disable at connector state change(due to hot-unplug) 
also.

Thanks,
--Ram
>   }
>   
>   static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder,
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 47d022d48718..8924004575b8 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -299,6 +299,49 @@ struct intel_panel {
>   	} backlight;
>   };
>   
> +struct intel_hdcp_shim {
> +	/* Outputs the transmitter's An and Aksv values to the receiver. */
> +	int (*write_an_aksv)(struct intel_digital_port *intel_dig_port, u8 *an);
> +
> +	/* Reads the receiver's key selection vector */
> +	int (*read_bksv)(struct intel_digital_port *intel_dig_port, u8 *bksv);
> +
> +	/*
> +	 * Reads BINFO from DP receivers and BSTATUS from HDMI receivers. The
> +	 * definitions are the same in the respective specs, but the names are
> +	 * different. Call it BSTATUS since that's the name the HDMI spec
> +	 * uses and it was there first.
> +	 */
> +	int (*read_bstatus)(struct intel_digital_port *intel_dig_port,
> +			    u8 *bstatus);
> +
> +	/* Determines whether a repeater is present downstream */
> +	int (*repeater_present)(struct intel_digital_port *intel_dig_port,
> +				bool *repeater_present);
> +
> +	/* Reads the receiver's Ri' value */
> +	int (*read_ri_prime)(struct intel_digital_port *intel_dig_port, u8 *ri);
> +
> +	/* Determines if the receiver's KSV FIFO is ready for consumption */
> +	int (*read_ksv_ready)(struct intel_digital_port *intel_dig_port,
> +			      bool *ksv_ready);
> +
> +	/* Reads the ksv fifo for num_downstream devices */
> +	int (*read_ksv_fifo)(struct intel_digital_port *intel_dig_port,
> +			     int num_downstream, u8 *ksv_fifo);
> +
> +	/* Reads a 32-bit part of V' from the receiver */
> +	int (*read_v_prime_part)(struct intel_digital_port *intel_dig_port,
> +				 int i, u32 *part);
> +
> +	/* Enables HDCP signalling on the port */
> +	int (*toggle_signalling)(struct intel_digital_port *intel_dig_port,
> +				 bool enable);
> +
> +	/* Ensures the link is still protected */
> +	bool (*check_link)(struct intel_digital_port *intel_dig_port);
> +};
> +
>   struct intel_connector {
>   	struct drm_connector base;
>   	/*
> @@ -330,6 +373,9 @@ struct intel_connector {
>   
>   	/* Work struct to schedule a uevent on link train failure */
>   	struct work_struct modeset_retry_work;
> +
> +	const struct intel_hdcp_shim *hdcp_shim;
> +	struct delayed_work hdcp_work;
>   };
>   
>   struct intel_digital_connector_state {
> @@ -1295,6 +1341,8 @@ void intel_ddi_set_vc_payload_alloc(const struct intel_crtc_state *crtc_state,
>   				    bool state);
>   u32 bxt_signal_levels(struct intel_dp *intel_dp);
>   uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
> +int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder);
> +int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder);
>   u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
>   
>   unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
> @@ -1746,6 +1794,11 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
>   }
>   #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
>   
> +/* intel_hdcp.c */
> +int intel_hdcp_enable(struct intel_connector *connector);
> +int intel_hdcp_disable(struct intel_connector *connector);
> +int intel_hdcp_check_link(struct intel_connector *connector);
> +void intel_hdcp_work(struct work_struct *work);
>   
>   /* intel_psr.c */
>   void intel_psr_enable(struct intel_dp *intel_dp,
> diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
> new file mode 100644
> index 000000000000..a2a575ed657e
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_hdcp.c
> @@ -0,0 +1,636 @@
> +/*
> + * Copyright (C) 2017 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_hdcp.h>
> +#include <linux/i2c.h>
> +#include <linux/random.h>
> +
> +#include "intel_drv.h"
> +#include "i915_reg.h"
> +
> +#define KEY_LOAD_TRIES	5
> +
> +static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
> +				    const struct intel_hdcp_shim *shim)
> +{
> +	unsigned long timeout = jiffies + msecs_to_jiffies_timeout(500);
> +	int ret;
> +	bool ksv_ready;
> +
> +	while (true) {
> +		ret = shim->read_ksv_ready(intel_dig_port, &ksv_ready);
> +		if (ret)
> +			return ret;
> +		if (ksv_ready)
> +			break;
> +		if (time_after(jiffies, timeout))
> +			return -ETIMEDOUT;
> +		msleep(100);
> +	}
> +	return 0;
> +}
> +
> +static void intel_hdcp_clear_keys(struct drm_i915_private *dev_priv)
> +{
> +	I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_CLEAR_KEYS_TRIGGER);
> +	I915_WRITE(SKL_HDCP_KEY_STATUS,
> +		   SKL_HDCP_KEY_LOAD_DONE | SKL_HDCP_KEY_LOAD_STATUS |
> +		   SKL_HDCP_FUSE_IN_PROGRESS | SKL_HDCP_FUSE_ERROR |
> +		   SKL_HDCP_FUSE_DONE);
> +}
> +
> +static int intel_hdcp_load_keys(struct drm_i915_private *dev_priv)
> +{
> +	unsigned long timeout;
> +	int ret;
> +	u32 val;
> +
> +	// Initiate loading the HDCP key from fuses
> +	mutex_lock(&dev_priv->pcu_lock);
> +	ret = sandybridge_pcode_write(dev_priv, SKL_PCODE_LOAD_HDCP_KEYS, 1);
> +	mutex_unlock(&dev_priv->pcu_lock);
> +	if (ret) {
> +		DRM_ERROR("Failed to initiate HDCP key load (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	// Wait for the keys to load (500us)
> +	timeout = jiffies + nsecs_to_jiffies_timeout(500 * 1000);
> +	while (true) {
> +		val = I915_READ(SKL_HDCP_KEY_STATUS);
> +		if (val & SKL_HDCP_KEY_LOAD_DONE)
> +			break;
> +		if (time_after(jiffies, timeout))
> +			return -ETIMEDOUT;
> +		usleep_range(50, 100);
> +	}
> +	if (!(val & SKL_HDCP_KEY_LOAD_STATUS))
> +		return -ENXIO;
> +
> +	// Send Aksv over to PCH display for use in authentication
> +	I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_AKSV_SEND_TRIGGER);
> +
> +	return 0;
> +}
> +
> +/* Returns updated SHA-1 index */
> +static int intel_write_sha_text(struct drm_i915_private *dev_priv, u32 sha_text)
> +{
> +	I915_WRITE(SKL_HDCP_SHA_TEXT, sha_text);
> +	if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_READY, 1)) {
> +		DRM_ERROR("Timed out waiting for SHA1 ready\n");
> +		return -ETIMEDOUT;
> +	}
> +	return 0;
> +}
> +
> +static
> +u32 intel_hdcp_get_repeater_ctl(struct intel_digital_port *intel_dig_port)
> +{
> +	enum port port = intel_dig_port->port;
> +	switch(port) {
> +	case PORT_A:
> +		return SKL_HDCP_DDIA_REP_PRESENT | SKL_HDCP_DDIA_SHA1_M0;
> +	case PORT_B:
> +		return SKL_HDCP_DDIB_REP_PRESENT | SKL_HDCP_DDIB_SHA1_M0;
> +	case PORT_C:
> +		return SKL_HDCP_DDIC_REP_PRESENT | SKL_HDCP_DDIC_SHA1_M0;
> +	case PORT_D:
> +		return SKL_HDCP_DDID_REP_PRESENT | SKL_HDCP_DDID_SHA1_M0;
> +	case PORT_E:
> +		return SKL_HDCP_DDIE_REP_PRESENT | SKL_HDCP_DDIE_SHA1_M0;
> +	default:
> +		break;
> +	}
> +	DRM_ERROR("Unknown port %d\n", port);
> +	return -EINVAL;
> +}
> +
> +/* Implements Part 2 of the HDCP authorization procedure */
> +static
> +int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
> +			       const struct intel_hdcp_shim *shim)
> +{
> +	struct drm_i915_private *dev_priv;
> +	u32 vprime, sha_text, sha_leftovers, rep_ctl;
> +	u8 bstatus[2], num_downstream, *ksv_fifo;
> +	int ret, i, j, sha_idx;
> +
> +	dev_priv = intel_dig_port->base.base.dev->dev_private;
> +
> +	ret = shim->read_bstatus(intel_dig_port, bstatus);
> +	if (ret)
> +		return ret;
> +
> +	/* If there are no downstream devices, we're all done. */
> +	num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
> +	if (num_downstream == 0) {
> +		DRM_INFO("HDCP is enabled (no downstream devices)\n");
> +		return 0;
> +	}
> +
> +	// Poll for ksv list ready (spec says max time allowed is 5s)
> +	ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim);
> +	if (ret) {
> +		DRM_ERROR("KSV list failed to become ready (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL);
> +	if (!ksv_fifo)
> +		return -ENOMEM;
> +
> +	ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo);
> +	if (ret)
> +		return ret;
> +
> +	// Process V' values from the receiver
> +	for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
> +		ret = shim->read_v_prime_part(intel_dig_port, i, &vprime);
> +		if (ret)
> +			return ret;
> +		I915_WRITE(SKL_HDCP_SHA_V_PRIME(i), vprime);
> +	}
> +
> +	/*
> +	 * We need to write the concatenation of all device KSVs, BINFO (DP) ||
> +	 * BSTATUS (HDMI), and M0 (which is added via HDCP_REP_CTL). This byte
> +	 * stream is written via the HDCP_SHA_TEXT register in 32-bit
> +	 * increments. Every 64 bytes, we need to write HDCP_REP_CTL again. This
> +	 * index will keep track of our progress through the 64 bytes as well as
> +	 * helping us work the 40-bit KSVs through our 32-bit register.
> +	 *
> +	 * NOTE: data passed via HDCP_SHA_TEXT should be big-endian
> +	 */
> +	sha_idx = 0;
> +	sha_text = 0;
> +	sha_leftovers = 0;
> +	rep_ctl = intel_hdcp_get_repeater_ctl(intel_dig_port);
> +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> +	for (i = 0; i < num_downstream; i++) {
> +		unsigned sha_empty;
> +		u8 *ksv = &ksv_fifo[i * DRM_HDCP_KSV_LEN];
> +
> +		// Fill up the empty slots in sha_text and write it out
> +		sha_empty = sizeof(sha_text) - sha_leftovers;
> +		for (j = 0; j < sha_empty; j++)
> +			sha_text |= ksv[j] << ((sizeof(sha_text) - j - 1) * 8);
> +
> +		ret = intel_write_sha_text(dev_priv, sha_text);
> +		if (ret < 0)
> +			return ret;
> +
> +		// Programming guide writes this every 64 bytes
> +		sha_idx += sizeof(sha_text);
> +		if (!(sha_idx % 64))
> +			I915_WRITE(SKL_HDCP_REP_CTL,
> +				   rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> +
> +		// Store the leftover bytes from the ksv in sha_text
> +		sha_leftovers = DRM_HDCP_KSV_LEN - sha_empty;
> +		sha_text = 0;
> +		for (j = 0; j < sha_leftovers; j++)
> +			sha_text |= ksv[sha_empty + j] <<
> +					((sizeof(sha_text) - j - 1) * 8);
> +
> +		/*
> +		 * If we still have room in sha_text for more data, continue.
> +		 * Otherwise, write it out immediately.
> +		 */
> +		if (sizeof(sha_text) > sha_leftovers)
> +			continue;
> +
> +		ret = intel_write_sha_text(dev_priv, sha_text);
> +		if (ret < 0)
> +			return ret;
> +		sha_leftovers = 0;
> +		sha_text = 0;
> +		sha_idx += sizeof(sha_text);
> +	}
> +
> +	/*
> +	 * We need to write BINFO/BSTATUS, and M0 now. Depending on how many
> +	 * bytes are leftover from the last ksv, we might be able to fit them
> +	 * all in sha_text (first 2 cases), or we might need to split them up
> +	 * into 2 writes (last 2 cases).
> +	 */
> +	if (sha_leftovers == 0) {
> +		// Write 16 bits of text, 16 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
> +		ret = intel_write_sha_text(dev_priv,
> +					   bstatus[0] << 8 | bstatus[1]);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +		// Write 32 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
> +		ret = intel_write_sha_text(dev_priv, 0);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +		// Write 16 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
> +		ret = intel_write_sha_text(dev_priv, 0);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +	} else if (sha_leftovers == 1) {
> +		// Write 24 bits of text, 8 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
> +		sha_text |= bstatus[0] << 16 | bstatus[1] << 8;
> +		// Only 24-bits of data, must be in the LSB
> +		sha_text = (sha_text & 0xffffff00) >> 8;
> +		ret = intel_write_sha_text(dev_priv, sha_text);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +		// Write 32 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
> +		ret = intel_write_sha_text(dev_priv, 0);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +		// Write 24 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
> +		ret = intel_write_sha_text(dev_priv, 0);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +	} else if (sha_leftovers == 2) {
> +		// Write 32 bits of text
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> +		sha_text |= bstatus[0] << 24 | bstatus[1] << 16;
> +		ret = intel_write_sha_text(dev_priv, sha_text);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +		// Write 64 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
> +		for (i = 0; i < 2; i++) {
> +			ret = intel_write_sha_text(dev_priv, 0);
> +			if (ret < 0)
> +				return ret;
> +			sha_idx += sizeof(sha_text);
> +		}
> +	} else if (sha_leftovers == 3) {
> +		// Write 32 bits of text
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> +		sha_text |= bstatus[0] << 24;
> +		ret = intel_write_sha_text(dev_priv, sha_text);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +		// Write 8 bits of text, 24 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
> +		ret = intel_write_sha_text(dev_priv, bstatus[1]);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +		// Write 32 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
> +		ret = intel_write_sha_text(dev_priv, 0);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +		// Write 8 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
> +		ret = intel_write_sha_text(dev_priv, 0);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +	} else {
> +		DRM_ERROR("Invalid number of leftovers %d\n", sha_leftovers);
> +		return -EINVAL;
> +	}
> +
> +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> +	// Fill up to 64 - 4 bytes with zeros (leave the last write for length)
> +	while ((sha_idx % 64) < (64 - sizeof(sha_text))) {
> +		ret = intel_write_sha_text(dev_priv, 0);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +	}
> +
> +	/*
> +	 * Last write gets the length of the concatenation in bits. That is:
> +	 *  - 5 bytes per device
> +	 *  - 10 bytes for BINFO/BSTATUS(2), M0(8)
> +	 */
> +	sha_text = (num_downstream * 5 + 10) * 8;
> +	ret = intel_write_sha_text(dev_priv, sha_text);
> +	if (ret < 0)
> +		return ret;
> +
> +	// Finally, tell the HW we're done with the hash and wait for it to ACK
> +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_COMPLETE_HASH);
> +	if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_COMPLETE, 1)) {
> +		DRM_ERROR("Timed out waiting for SHA1 complete\n");
> +		return -ETIMEDOUT;
> +	}
> +	if (!(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_V_MATCH)) {
> +		DRM_ERROR("SHA-1 mismatch, HDCP failed\n");
> +		return -ENXIO;
> +	}
> +
> +	DRM_INFO("HDCP is enabled (%d downstream devices)\n", num_downstream);
> +	return 0;
> +}
> +
> +/* Implements Part 1 of the HDCP authorization procedure */
> +static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
> +			   const struct intel_hdcp_shim *shim)
> +{
> +	struct drm_i915_private *dev_priv;
> +	enum port port;
> +	unsigned long r0_prime_gen_start;
> +	int ret, i;
> +	union {
> +		u32 reg[2];
> +		u8 shim[DRM_HDCP_AN_LEN];
> +	} an;
> +	union {
> +		u32 reg[2];
> +		u8 shim[DRM_HDCP_KSV_LEN];
> +	} bksv;
> +	union {
> +		u32 reg;
> +		u8 shim[DRM_HDCP_RI_LEN];
> +	} ri;
> +	bool repeater_present;
> +
> +	dev_priv = intel_dig_port->base.base.dev->dev_private;
> +
> +	port = intel_dig_port->port;
> +
> +	// Initialize An with 2 random values and acquire it
> +	for (i = 0; i < 2; i++)
> +		I915_WRITE(SKL_PORT_HDCP_ANINIT(port), get_random_long());
> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_CAPTURE_AN);
> +
> +	// Wait for An to be acquired
> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> +		     SKL_HDCP_STATUS_AN_READY, 1)) {
> +		DRM_ERROR("Timed out waiting for An\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	an.reg[0] = I915_READ(SKL_PORT_HDCP_ANLO(port));
> +	an.reg[1] = I915_READ(SKL_PORT_HDCP_ANHI(port));
> +	ret = shim->write_an_aksv(intel_dig_port, an.shim);
> +	if (ret)
> +		return ret;
> +
> +	r0_prime_gen_start = jiffies;
> +
> +	memset(&bksv, 0, sizeof(bksv));
> +	ret = shim->read_bksv(intel_dig_port, bksv.shim);
> +	if (ret)
> +		return ret;
> +
> +	I915_WRITE(SKL_PORT_HDCP_BKSVLO(port), bksv.reg[0]);
> +	I915_WRITE(SKL_PORT_HDCP_BKSVHI(port), bksv.reg[1]);
> +
> +	ret = shim->repeater_present(intel_dig_port, &repeater_present);
> +	if (ret)
> +		return ret;
> +	if (repeater_present)
> +		I915_WRITE(SKL_HDCP_REP_CTL,
> +			   intel_hdcp_get_repeater_ctl(intel_dig_port));
> +
> +	ret = shim->toggle_signalling(intel_dig_port, true);
> +	if (ret)
> +		return ret;
> +
> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_AUTH_AND_ENC);
> +
> +	// Wait for R0 ready
> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> +		     (SKL_HDCP_STATUS_R0_READY | SKL_HDCP_STATUS_ENC), 1)) {
> +		DRM_ERROR("Timed out waiting for R0 ready\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	/*
> +	 * Wait for R0' to become available, the spec says 100ms from Aksv
> +	 * write. On DP, there's an R0_READY bit available but no such bit
> +	 * exists on HDMI. Since the upper-bound is the same, we'll just do
> +	 * the stupid thing instead of polling on one and not the other.
> +	 */
> +	wait_remaining_ms_from_jiffies(r0_prime_gen_start, 100);
> +
> +	ri.reg = 0;
> +	ret = shim->read_ri_prime(intel_dig_port, ri.shim);
> +	if (ret)
> +		return ret;
> +	I915_WRITE(SKL_PORT_HDCP_RPRIME(port), ri.reg);
> +
> +	// Wait for Ri prime match
> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> +		     (SKL_HDCP_STATUS_RI_MATCH | SKL_HDCP_STATUS_ENC), 1)) {
> +		DRM_ERROR("Timed out waiting for Ri prime match (%x)\n",
> +			  I915_READ(SKL_PORT_HDCP_STATUS(port)));
> +		return -ETIMEDOUT;
> +	}
> +
> +	// Wait for encryption confirmation
> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> +		      SKL_HDCP_STATUS_ENC, 20)) {
> +		DRM_ERROR("Timed out waiting for encryption\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	/*
> +	 * XXX: If we have MST-connected devices, we need to enable encryption
> +	 * on those as well.
> +	 */
> +
> +	return intel_hdcp_auth_downstream(intel_dig_port, shim);
> +}
> +
> +static
> +struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector)
> +{
> +	return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base);
> +}
> +
> +static int _intel_hdcp_disable(struct intel_connector *connector)
> +{
> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> +	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
> +	enum port port = intel_dig_port->port;
> +	int ret;
> +
> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) == 0, 20)) {
> +		DRM_ERROR("Failed to disable HDCP, timeout clearing status\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	intel_hdcp_clear_keys(dev_priv);
> +
> +	ret = connector->hdcp_shim->toggle_signalling(intel_dig_port, false);
> +	if (ret) {
> +		DRM_ERROR("Failed to disable HDCP signalling\n");
> +		return ret;
> +	}
> +
> +	DRM_INFO("HDCP is disabled\n");
> +	return 0;
> +}
> +
> +static int _intel_hdcp_enable(struct intel_connector *connector)
> +{
> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> +	int i, ret;
> +
> +	if (!(I915_READ(SKL_FUSE_STATUS) & SKL_FUSE_PG_DIST_STATUS(1))) {
> +		DRM_ERROR("PG1 is disabled, cannot load keys\n");
> +		return -ENXIO;
> +	}
> +
> +	for (i = 0; i < KEY_LOAD_TRIES; i++) {
> +		ret = intel_hdcp_load_keys(dev_priv);
> +		if (!ret)
> +			break;
> +		intel_hdcp_clear_keys(dev_priv);
> +	}
> +	if (ret) {
> +		DRM_ERROR("Could not load HDCP keys, (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = intel_hdcp_auth(conn_to_dig_port(connector),
> +			      connector->hdcp_shim);
> +	if (ret) {
> +		DRM_ERROR("Failed to authenticate HDCP (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +void intel_hdcp_work(struct work_struct *work)
> +{
> +	struct intel_connector *connector = container_of(to_delayed_work(work),
> +							 struct intel_connector,
> +						         hdcp_work);
> +	struct drm_device *dev = connector->base.dev;
> +	int ret;
> +
> +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> +
> +	ret = intel_hdcp_check_link(connector);
> +	if (!ret)
> +		schedule_delayed_work(&connector->hdcp_work,
> +				      DRM_HDCP_CHECK_PERIOD_MS);
> +
> +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
> +}
> +
> +int intel_hdcp_enable(struct intel_connector *connector)
> +{
> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> +	struct drm_device *dev = &dev_priv->drm;
> +	struct drm_connector_state *state = connector->base.state;
> +	int ret;
> +
> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	ret = _intel_hdcp_enable(connector);
> +	if (ret)
> +		return ret;
> +
> +	state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
> +
> +	schedule_delayed_work(&connector->hdcp_work, DRM_HDCP_CHECK_PERIOD_MS);
> +	return 0;
> +}
> +
> +int intel_hdcp_disable(struct intel_connector *connector)
> +{
> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> +	struct drm_device *dev = &dev_priv->drm;
> +
> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	cancel_delayed_work(&connector->hdcp_work);
> +
> +	return _intel_hdcp_disable(connector);
> +}
> +
> +/* Implements Part 3 of the HDCP authorization procedure */
> +int intel_hdcp_check_link(struct intel_connector *connector)
> +{
> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> +	struct drm_device *dev = &dev_priv->drm;
> +	struct drm_connector_state *state = connector->base.state;
> +	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
> +	enum port port = intel_dig_port->port;
> +	int ret;
> +
> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
> +
> +	if (state->content_protection != DRM_MODE_CONTENT_PROTECTION_ENABLED)
> +		return 0;
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	if (!(I915_READ(SKL_PORT_HDCP_STATUS(port)) & SKL_HDCP_STATUS_ENC)) {
> +		DRM_ERROR("HDCP check failed: link is not encrypted, %x\n",
> +			   I915_READ(SKL_PORT_HDCP_STATUS(port)));
> +		ret = -ENXIO;
> +		goto fail;
> +	}
> +
> +	if (connector->hdcp_shim->check_link(intel_dig_port))
> +		return 0;
> +
> +	DRM_INFO("HDCP link failed, retrying authentication\n");
> +
> +	ret = _intel_hdcp_disable(connector);
> +	if (ret) {
> +		DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
> +		goto fail;
> +	}
> +
> +	ret = _intel_hdcp_enable(connector);
> +	if (ret) {
> +		DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
> +		goto fail;
> +	}
> +
> +	return 0;
> +
> +fail:
> +	state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +	return ret;
> +}

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 3/6] drm/i915: Add HDCP framework + base implementation
@ 2017-12-01  7:23     ` Ramalingam C
  0 siblings, 0 replies; 62+ messages in thread
From: Ramalingam C @ 2017-12-01  7:23 UTC (permalink / raw)
  To: Sean Paul, dri-devel, intel-gfx; +Cc: David Airlie, linux-kernel, Rodrigo Vivi

Sean,

IMHO, it will good if we can have all generic hdcp1.4 authentication 
flow in drm helpers and all interested display drivers to use them.

This Design will make the extending of hdcp easy for other display 
drivers based on DRM.

We can have the required drm_hdcp_shim type of implementation at drm 
structure which will be called for platform specific operations (like 
prepare an, send aksv, program bksv/repeater/r0 and verify sha1 etc)?


On Thursday 30 November 2017 08:38 AM, Sean Paul wrote:
> This patch adds the framework required to add HDCP support to intel
> connectors. It implements Aksv loading from fuse, and parts 1/2/3
> of the HDCP authentication scheme.
>
> Note that without shim implementations, this does not actually implement
> HDCP. That will come in subsequent patches.
>
> Signed-off-by: Sean Paul <seanpaul@chromium.org>
> ---
>   drivers/gpu/drm/i915/Makefile       |   1 +
>   drivers/gpu/drm/i915/i915_reg.h     |  83 +++++
>   drivers/gpu/drm/i915/intel_atomic.c |  26 +-
>   drivers/gpu/drm/i915/intel_ddi.c    |  14 +
>   drivers/gpu/drm/i915/intel_drv.h    |  53 +++
>   drivers/gpu/drm/i915/intel_hdcp.c   | 636 ++++++++++++++++++++++++++++++++++++
>   6 files changed, 811 insertions(+), 2 deletions(-)
>   create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c
>
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> index 6c3b0481ef82..1e745508e437 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -87,6 +87,7 @@ i915-y += intel_audio.o \
>   	  intel_fbc.o \
>   	  intel_fifo_underrun.o \
>   	  intel_frontbuffer.o \
> +	  intel_hdcp.o \
>   	  intel_hotplug.o \
>   	  intel_modes.o \
>   	  intel_overlay.o \
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index 68a58cce6ab1..43128030171d 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -7991,6 +7991,7 @@ enum {
>   #define     GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT	8
>   #define     GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT	16
>   #define     GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT	24
> +#define   SKL_PCODE_LOAD_HDCP_KEYS		0x5
>   #define   SKL_PCODE_CDCLK_CONTROL		0x7
>   #define     SKL_CDCLK_PREPARE_FOR_CHANGE	0x3
>   #define     SKL_CDCLK_READY_FOR_CHANGE		0x1
> @@ -8285,6 +8286,88 @@ enum skl_power_gate {
>   #define  SKL_PW_TO_PG(pw)			((pw) - SKL_DISP_PW_1 + SKL_PG1)
>   #define  SKL_FUSE_PG_DIST_STATUS(pg)		(1 << (27 - (pg)))
>   
> +
> +/* HDCP Key Registers */
> +#define SKL_HDCP_KEY_CONF		_MMIO(0x66c00)
> +#define	 SKL_HDCP_AKSV_SEND_TRIGGER	BIT(31)
> +#define  SKL_HDCP_CLEAR_KEYS_TRIGGER	BIT(30)
> +#define SKL_HDCP_KEY_STATUS		_MMIO(0x66c04)
> +#define  SKL_HDCP_FUSE_IN_PROGRESS	BIT(7)
> +#define  SKL_HDCP_FUSE_ERROR		BIT(6)
> +#define  SKL_HDCP_FUSE_DONE		BIT(5)
> +#define  SKL_HDCP_KEY_LOAD_STATUS	BIT(1)
> +#define  SKL_HDCP_KEY_LOAD_DONE		BIT(0)
> +#define SKL_HDCP_AKSV_LO		_MMIO(0x66c10)
> +#define SKL_HDCP_AKSV_HI		_MMIO(0x66c14)
> +
> +/* HDCP Repeater Registers */
> +#define SKL_HDCP_REP_CTL		_MMIO(0x66d00)
> +#define  SKL_HDCP_DDIB_REP_PRESENT	BIT(30)
> +#define  SKL_HDCP_DDIA_REP_PRESENT	BIT(29)
> +#define  SKL_HDCP_DDIC_REP_PRESENT	BIT(28)
> +#define  SKL_HDCP_DDID_REP_PRESENT	BIT(27)
> +#define  SKL_HDCP_DDIF_REP_PRESENT	BIT(26)
> +#define  SKL_HDCP_DDIE_REP_PRESENT	BIT(25)
> +#define  SKL_HDCP_DDIB_SHA1_M0		(1 << 20)
> +#define  SKL_HDCP_DDIA_SHA1_M0		(2 << 20)
> +#define  SKL_HDCP_DDIC_SHA1_M0		(3 << 20)
> +#define  SKL_HDCP_DDID_SHA1_M0		(4 << 20)
> +#define  SKL_HDCP_DDIF_SHA1_M0		(5 << 20)
> +#define  SKL_HDCP_DDIE_SHA1_M0		(6 << 20) // Bspec says 5?
> +#define  SKL_HDCP_SHA1_BUSY		BIT(16)
> +#define  SKL_HDCP_SHA1_READY		BIT(17)
> +#define  SKL_HDCP_SHA1_COMPLETE		BIT(18)
> +#define  SKL_HDCP_SHA1_V_MATCH		BIT(19)
> +#define  SKL_HDCP_SHA1_TEXT_32		(1 << 1)
> +#define  SKL_HDCP_SHA1_COMPLETE_HASH	(2 << 1)
> +#define  SKL_HDCP_SHA1_TEXT_24		(4 << 1)
> +#define  SKL_HDCP_SHA1_TEXT_16		(5 << 1)
> +#define  SKL_HDCP_SHA1_TEXT_8		(6 << 1)
> +#define  SKL_HDCP_SHA1_TEXT_0		(7 << 1)
> +#define SKL_HDCP_SHA_V_PRIME_H0		_MMIO(0x66d04)
> +#define SKL_HDCP_SHA_V_PRIME_H1		_MMIO(0x66d08)
> +#define SKL_HDCP_SHA_V_PRIME_H2		_MMIO(0x66d0C)
> +#define SKL_HDCP_SHA_V_PRIME_H3		_MMIO(0x66d10)
> +#define SKL_HDCP_SHA_V_PRIME_H4		_MMIO(0x66d14)
> +#define SKL_HDCP_SHA_V_PRIME(h)		_MMIO((0x66d04 + h * 4))
> +#define SKL_HDCP_SHA_TEXT		_MMIO(0x66d18)
> +
> +/* HDCP Auth Registers */
> +#define _SKL_PORTA_HDCP_AUTHENC		0x66800
> +#define _SKL_PORTB_HDCP_AUTHENC		0x66500
> +#define _SKL_PORTC_HDCP_AUTHENC		0x66600
> +#define _SKL_PORTD_HDCP_AUTHENC		0x66700
> +#define _SKL_PORTE_HDCP_AUTHENC		0x66A00
> +#define _SKL_PORTF_HDCP_AUTHENC		0x66900
> +#define _SKL_PORT_HDCP_AUTHENC(port, x)	_MMIO(_PICK(port, \
> +					  _SKL_PORTA_HDCP_AUTHENC, \
> +					  _SKL_PORTB_HDCP_AUTHENC, \
> +					  _SKL_PORTC_HDCP_AUTHENC, \
> +					  _SKL_PORTD_HDCP_AUTHENC, \
> +					  _SKL_PORTE_HDCP_AUTHENC, \
> +					  _SKL_PORTF_HDCP_AUTHENC) + x)
> +#define SKL_PORT_HDCP_CONF(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x0)
> +#define  SKL_HDCP_CONF_CAPTURE_AN	BIT(0)
> +#define  SKL_HDCP_CONF_AUTH_AND_ENC	(BIT(1) | BIT(0))
> +#define SKL_PORT_HDCP_ANINIT(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x4)
> +#define SKL_PORT_HDCP_ANLO(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x8)
> +#define SKL_PORT_HDCP_ANHI(port)	_SKL_PORT_HDCP_AUTHENC(port, 0xC)
> +#define SKL_PORT_HDCP_BKSVLO(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x10)
> +#define SKL_PORT_HDCP_BKSVHI(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x14)
> +#define SKL_PORT_HDCP_RPRIME(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x18)
> +#define SKL_PORT_HDCP_STATUS(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x1C)
> +#define  SKL_HDCP_STATUS_STREAM_A_ENC	BIT(31)
> +#define  SKL_HDCP_STATUS_STREAM_B_ENC	BIT(30)
> +#define  SKL_HDCP_STATUS_STREAM_C_ENC	BIT(29)
> +#define  SKL_HDCP_STATUS_STREAM_D_ENC	BIT(28)
> +#define  SKL_HDCP_STATUS_AUTH		BIT(21)
> +#define  SKL_HDCP_STATUS_ENC		BIT(20)
> +#define  SKL_HDCP_STATUS_RI_MATCH	BIT(19)
> +#define  SKL_HDCP_STATUS_R0_READY	BIT(18)
> +#define  SKL_HDCP_STATUS_AN_READY	BIT(17)
> +#define  SKL_HDCP_STATUS_CIPHER		BIT(16)
> +#define  SKL_HDCP_STATUS_FRAME_CNT(x)	((x >> 8) & 0xff)
> +
>   /* Per-pipe DDI Function Control */
>   #define _TRANS_DDI_FUNC_CTL_A		0x60400
>   #define _TRANS_DDI_FUNC_CTL_B		0x61400
> diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
> index 36d4e635e4ce..ddf08227d9cb 100644
> --- a/drivers/gpu/drm/i915/intel_atomic.c
> +++ b/drivers/gpu/drm/i915/intel_atomic.c
> @@ -109,12 +109,34 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
>   	struct intel_digital_connector_state *old_conn_state =
>   		to_intel_digital_connector_state(old_state);
>   	struct drm_crtc_state *crtc_state;
> -
> -	if (!new_state->crtc)
> +	uint64_t old_cp = old_conn_state->base.content_protection;
> +	uint64_t new_cp = new_state->content_protection;
> +
> +	if (!new_state->crtc) {
> +		/*
> +		 * If the connector is being disabled with CP enabled, mark it
> +		 * desired so it's re-enabled when the connector is brought back
> +		 */
> +		if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
> +			new_state->content_protection =
> +				DRM_MODE_CONTENT_PROTECTION_DESIRED;
>   		return 0;
> +	}
>   
>   	crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc);
>   
> +	if (new_cp != old_cp) {
> +		/* Only drivers can set content protection enabled */
> +		if (new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
> +			new_state->content_protection =
> +				DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +
> +		/* Involve the encoder/connector to enable/disable CP */
> +		if (new_cp == DRM_MODE_CONTENT_PROTECTION_OFF ||
> +		    old_cp == DRM_MODE_CONTENT_PROTECTION_OFF)
> +			crtc_state->mode_changed = true;

We need not perform the mode set for hdcp enable/disable.
Authentication and encryption can be started on active port.

> +	}
> +
>   	/*
>   	 * These properties are handled by fastset, and might not end
>   	 * up in a modeset.
> diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
> index 933c18fd4258..0e69337f410d 100644
> --- a/drivers/gpu/drm/i915/intel_ddi.c
> +++ b/drivers/gpu/drm/i915/intel_ddi.c
> @@ -2432,10 +2432,17 @@ static void intel_enable_ddi(struct intel_encoder *encoder,
>   			     const struct intel_crtc_state *crtc_state,
>   			     const struct drm_connector_state *conn_state)
>   {
> +	struct drm_connector *connector = conn_state->connector;
> +	struct intel_connector *intel_connector = to_intel_connector(connector);
> +
>   	if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
>   		intel_enable_ddi_hdmi(encoder, crtc_state, conn_state);
>   	else
>   		intel_enable_ddi_dp(encoder, crtc_state, conn_state);
> +
> +	if (conn_state->content_protection ==
> +			DRM_MODE_CONTENT_PROTECTION_DESIRED)
> +		intel_hdcp_enable(intel_connector);
>   }
>   
>   static void intel_disable_ddi_dp(struct intel_encoder *encoder,
> @@ -2468,10 +2475,17 @@ static void intel_disable_ddi(struct intel_encoder *encoder,
>   			      const struct intel_crtc_state *old_crtc_state,
>   			      const struct drm_connector_state *old_conn_state)
>   {
> +	struct drm_connector *connector = old_conn_state->connector;
> +	struct intel_connector *intel_connector = to_intel_connector(connector);
> +
>   	if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
>   		intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state);
>   	else
>   		intel_disable_ddi_dp(encoder, old_crtc_state, old_conn_state);
> +
> +	if (old_conn_state->content_protection !=
> +			DRM_MODE_CONTENT_PROTECTION_OFF)
> +		intel_hdcp_disable(intel_connector);
We might want to disable the hdcp before disabling the DDI. Actually we 
could trigger hdcp disable at connector state change(due to hot-unplug) 
also.

Thanks,
--Ram
>   }
>   
>   static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder,
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 47d022d48718..8924004575b8 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -299,6 +299,49 @@ struct intel_panel {
>   	} backlight;
>   };
>   
> +struct intel_hdcp_shim {
> +	/* Outputs the transmitter's An and Aksv values to the receiver. */
> +	int (*write_an_aksv)(struct intel_digital_port *intel_dig_port, u8 *an);
> +
> +	/* Reads the receiver's key selection vector */
> +	int (*read_bksv)(struct intel_digital_port *intel_dig_port, u8 *bksv);
> +
> +	/*
> +	 * Reads BINFO from DP receivers and BSTATUS from HDMI receivers. The
> +	 * definitions are the same in the respective specs, but the names are
> +	 * different. Call it BSTATUS since that's the name the HDMI spec
> +	 * uses and it was there first.
> +	 */
> +	int (*read_bstatus)(struct intel_digital_port *intel_dig_port,
> +			    u8 *bstatus);
> +
> +	/* Determines whether a repeater is present downstream */
> +	int (*repeater_present)(struct intel_digital_port *intel_dig_port,
> +				bool *repeater_present);
> +
> +	/* Reads the receiver's Ri' value */
> +	int (*read_ri_prime)(struct intel_digital_port *intel_dig_port, u8 *ri);
> +
> +	/* Determines if the receiver's KSV FIFO is ready for consumption */
> +	int (*read_ksv_ready)(struct intel_digital_port *intel_dig_port,
> +			      bool *ksv_ready);
> +
> +	/* Reads the ksv fifo for num_downstream devices */
> +	int (*read_ksv_fifo)(struct intel_digital_port *intel_dig_port,
> +			     int num_downstream, u8 *ksv_fifo);
> +
> +	/* Reads a 32-bit part of V' from the receiver */
> +	int (*read_v_prime_part)(struct intel_digital_port *intel_dig_port,
> +				 int i, u32 *part);
> +
> +	/* Enables HDCP signalling on the port */
> +	int (*toggle_signalling)(struct intel_digital_port *intel_dig_port,
> +				 bool enable);
> +
> +	/* Ensures the link is still protected */
> +	bool (*check_link)(struct intel_digital_port *intel_dig_port);
> +};
> +
>   struct intel_connector {
>   	struct drm_connector base;
>   	/*
> @@ -330,6 +373,9 @@ struct intel_connector {
>   
>   	/* Work struct to schedule a uevent on link train failure */
>   	struct work_struct modeset_retry_work;
> +
> +	const struct intel_hdcp_shim *hdcp_shim;
> +	struct delayed_work hdcp_work;
>   };
>   
>   struct intel_digital_connector_state {
> @@ -1295,6 +1341,8 @@ void intel_ddi_set_vc_payload_alloc(const struct intel_crtc_state *crtc_state,
>   				    bool state);
>   u32 bxt_signal_levels(struct intel_dp *intel_dp);
>   uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
> +int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder);
> +int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder);
>   u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
>   
>   unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
> @@ -1746,6 +1794,11 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
>   }
>   #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
>   
> +/* intel_hdcp.c */
> +int intel_hdcp_enable(struct intel_connector *connector);
> +int intel_hdcp_disable(struct intel_connector *connector);
> +int intel_hdcp_check_link(struct intel_connector *connector);
> +void intel_hdcp_work(struct work_struct *work);
>   
>   /* intel_psr.c */
>   void intel_psr_enable(struct intel_dp *intel_dp,
> diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
> new file mode 100644
> index 000000000000..a2a575ed657e
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_hdcp.c
> @@ -0,0 +1,636 @@
> +/*
> + * Copyright (C) 2017 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_hdcp.h>
> +#include <linux/i2c.h>
> +#include <linux/random.h>
> +
> +#include "intel_drv.h"
> +#include "i915_reg.h"
> +
> +#define KEY_LOAD_TRIES	5
> +
> +static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
> +				    const struct intel_hdcp_shim *shim)
> +{
> +	unsigned long timeout = jiffies + msecs_to_jiffies_timeout(500);
> +	int ret;
> +	bool ksv_ready;
> +
> +	while (true) {
> +		ret = shim->read_ksv_ready(intel_dig_port, &ksv_ready);
> +		if (ret)
> +			return ret;
> +		if (ksv_ready)
> +			break;
> +		if (time_after(jiffies, timeout))
> +			return -ETIMEDOUT;
> +		msleep(100);
> +	}
> +	return 0;
> +}
> +
> +static void intel_hdcp_clear_keys(struct drm_i915_private *dev_priv)
> +{
> +	I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_CLEAR_KEYS_TRIGGER);
> +	I915_WRITE(SKL_HDCP_KEY_STATUS,
> +		   SKL_HDCP_KEY_LOAD_DONE | SKL_HDCP_KEY_LOAD_STATUS |
> +		   SKL_HDCP_FUSE_IN_PROGRESS | SKL_HDCP_FUSE_ERROR |
> +		   SKL_HDCP_FUSE_DONE);
> +}
> +
> +static int intel_hdcp_load_keys(struct drm_i915_private *dev_priv)
> +{
> +	unsigned long timeout;
> +	int ret;
> +	u32 val;
> +
> +	// Initiate loading the HDCP key from fuses
> +	mutex_lock(&dev_priv->pcu_lock);
> +	ret = sandybridge_pcode_write(dev_priv, SKL_PCODE_LOAD_HDCP_KEYS, 1);
> +	mutex_unlock(&dev_priv->pcu_lock);
> +	if (ret) {
> +		DRM_ERROR("Failed to initiate HDCP key load (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	// Wait for the keys to load (500us)
> +	timeout = jiffies + nsecs_to_jiffies_timeout(500 * 1000);
> +	while (true) {
> +		val = I915_READ(SKL_HDCP_KEY_STATUS);
> +		if (val & SKL_HDCP_KEY_LOAD_DONE)
> +			break;
> +		if (time_after(jiffies, timeout))
> +			return -ETIMEDOUT;
> +		usleep_range(50, 100);
> +	}
> +	if (!(val & SKL_HDCP_KEY_LOAD_STATUS))
> +		return -ENXIO;
> +
> +	// Send Aksv over to PCH display for use in authentication
> +	I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_AKSV_SEND_TRIGGER);
> +
> +	return 0;
> +}
> +
> +/* Returns updated SHA-1 index */
> +static int intel_write_sha_text(struct drm_i915_private *dev_priv, u32 sha_text)
> +{
> +	I915_WRITE(SKL_HDCP_SHA_TEXT, sha_text);
> +	if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_READY, 1)) {
> +		DRM_ERROR("Timed out waiting for SHA1 ready\n");
> +		return -ETIMEDOUT;
> +	}
> +	return 0;
> +}
> +
> +static
> +u32 intel_hdcp_get_repeater_ctl(struct intel_digital_port *intel_dig_port)
> +{
> +	enum port port = intel_dig_port->port;
> +	switch(port) {
> +	case PORT_A:
> +		return SKL_HDCP_DDIA_REP_PRESENT | SKL_HDCP_DDIA_SHA1_M0;
> +	case PORT_B:
> +		return SKL_HDCP_DDIB_REP_PRESENT | SKL_HDCP_DDIB_SHA1_M0;
> +	case PORT_C:
> +		return SKL_HDCP_DDIC_REP_PRESENT | SKL_HDCP_DDIC_SHA1_M0;
> +	case PORT_D:
> +		return SKL_HDCP_DDID_REP_PRESENT | SKL_HDCP_DDID_SHA1_M0;
> +	case PORT_E:
> +		return SKL_HDCP_DDIE_REP_PRESENT | SKL_HDCP_DDIE_SHA1_M0;
> +	default:
> +		break;
> +	}
> +	DRM_ERROR("Unknown port %d\n", port);
> +	return -EINVAL;
> +}
> +
> +/* Implements Part 2 of the HDCP authorization procedure */
> +static
> +int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
> +			       const struct intel_hdcp_shim *shim)
> +{
> +	struct drm_i915_private *dev_priv;
> +	u32 vprime, sha_text, sha_leftovers, rep_ctl;
> +	u8 bstatus[2], num_downstream, *ksv_fifo;
> +	int ret, i, j, sha_idx;
> +
> +	dev_priv = intel_dig_port->base.base.dev->dev_private;
> +
> +	ret = shim->read_bstatus(intel_dig_port, bstatus);
> +	if (ret)
> +		return ret;
> +
> +	/* If there are no downstream devices, we're all done. */
> +	num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
> +	if (num_downstream == 0) {
> +		DRM_INFO("HDCP is enabled (no downstream devices)\n");
> +		return 0;
> +	}
> +
> +	// Poll for ksv list ready (spec says max time allowed is 5s)
> +	ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim);
> +	if (ret) {
> +		DRM_ERROR("KSV list failed to become ready (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL);
> +	if (!ksv_fifo)
> +		return -ENOMEM;
> +
> +	ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo);
> +	if (ret)
> +		return ret;
> +
> +	// Process V' values from the receiver
> +	for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
> +		ret = shim->read_v_prime_part(intel_dig_port, i, &vprime);
> +		if (ret)
> +			return ret;
> +		I915_WRITE(SKL_HDCP_SHA_V_PRIME(i), vprime);
> +	}
> +
> +	/*
> +	 * We need to write the concatenation of all device KSVs, BINFO (DP) ||
> +	 * BSTATUS (HDMI), and M0 (which is added via HDCP_REP_CTL). This byte
> +	 * stream is written via the HDCP_SHA_TEXT register in 32-bit
> +	 * increments. Every 64 bytes, we need to write HDCP_REP_CTL again. This
> +	 * index will keep track of our progress through the 64 bytes as well as
> +	 * helping us work the 40-bit KSVs through our 32-bit register.
> +	 *
> +	 * NOTE: data passed via HDCP_SHA_TEXT should be big-endian
> +	 */
> +	sha_idx = 0;
> +	sha_text = 0;
> +	sha_leftovers = 0;
> +	rep_ctl = intel_hdcp_get_repeater_ctl(intel_dig_port);
> +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> +	for (i = 0; i < num_downstream; i++) {
> +		unsigned sha_empty;
> +		u8 *ksv = &ksv_fifo[i * DRM_HDCP_KSV_LEN];
> +
> +		// Fill up the empty slots in sha_text and write it out
> +		sha_empty = sizeof(sha_text) - sha_leftovers;
> +		for (j = 0; j < sha_empty; j++)
> +			sha_text |= ksv[j] << ((sizeof(sha_text) - j - 1) * 8);
> +
> +		ret = intel_write_sha_text(dev_priv, sha_text);
> +		if (ret < 0)
> +			return ret;
> +
> +		// Programming guide writes this every 64 bytes
> +		sha_idx += sizeof(sha_text);
> +		if (!(sha_idx % 64))
> +			I915_WRITE(SKL_HDCP_REP_CTL,
> +				   rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> +
> +		// Store the leftover bytes from the ksv in sha_text
> +		sha_leftovers = DRM_HDCP_KSV_LEN - sha_empty;
> +		sha_text = 0;
> +		for (j = 0; j < sha_leftovers; j++)
> +			sha_text |= ksv[sha_empty + j] <<
> +					((sizeof(sha_text) - j - 1) * 8);
> +
> +		/*
> +		 * If we still have room in sha_text for more data, continue.
> +		 * Otherwise, write it out immediately.
> +		 */
> +		if (sizeof(sha_text) > sha_leftovers)
> +			continue;
> +
> +		ret = intel_write_sha_text(dev_priv, sha_text);
> +		if (ret < 0)
> +			return ret;
> +		sha_leftovers = 0;
> +		sha_text = 0;
> +		sha_idx += sizeof(sha_text);
> +	}
> +
> +	/*
> +	 * We need to write BINFO/BSTATUS, and M0 now. Depending on how many
> +	 * bytes are leftover from the last ksv, we might be able to fit them
> +	 * all in sha_text (first 2 cases), or we might need to split them up
> +	 * into 2 writes (last 2 cases).
> +	 */
> +	if (sha_leftovers == 0) {
> +		// Write 16 bits of text, 16 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
> +		ret = intel_write_sha_text(dev_priv,
> +					   bstatus[0] << 8 | bstatus[1]);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +		// Write 32 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
> +		ret = intel_write_sha_text(dev_priv, 0);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +		// Write 16 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
> +		ret = intel_write_sha_text(dev_priv, 0);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +	} else if (sha_leftovers == 1) {
> +		// Write 24 bits of text, 8 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
> +		sha_text |= bstatus[0] << 16 | bstatus[1] << 8;
> +		// Only 24-bits of data, must be in the LSB
> +		sha_text = (sha_text & 0xffffff00) >> 8;
> +		ret = intel_write_sha_text(dev_priv, sha_text);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +		// Write 32 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
> +		ret = intel_write_sha_text(dev_priv, 0);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +		// Write 24 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
> +		ret = intel_write_sha_text(dev_priv, 0);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +	} else if (sha_leftovers == 2) {
> +		// Write 32 bits of text
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> +		sha_text |= bstatus[0] << 24 | bstatus[1] << 16;
> +		ret = intel_write_sha_text(dev_priv, sha_text);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +		// Write 64 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
> +		for (i = 0; i < 2; i++) {
> +			ret = intel_write_sha_text(dev_priv, 0);
> +			if (ret < 0)
> +				return ret;
> +			sha_idx += sizeof(sha_text);
> +		}
> +	} else if (sha_leftovers == 3) {
> +		// Write 32 bits of text
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> +		sha_text |= bstatus[0] << 24;
> +		ret = intel_write_sha_text(dev_priv, sha_text);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +		// Write 8 bits of text, 24 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
> +		ret = intel_write_sha_text(dev_priv, bstatus[1]);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +		// Write 32 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
> +		ret = intel_write_sha_text(dev_priv, 0);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +
> +		// Write 8 bits of M0
> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
> +		ret = intel_write_sha_text(dev_priv, 0);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +	} else {
> +		DRM_ERROR("Invalid number of leftovers %d\n", sha_leftovers);
> +		return -EINVAL;
> +	}
> +
> +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> +	// Fill up to 64 - 4 bytes with zeros (leave the last write for length)
> +	while ((sha_idx % 64) < (64 - sizeof(sha_text))) {
> +		ret = intel_write_sha_text(dev_priv, 0);
> +		if (ret < 0)
> +			return ret;
> +		sha_idx += sizeof(sha_text);
> +	}
> +
> +	/*
> +	 * Last write gets the length of the concatenation in bits. That is:
> +	 *  - 5 bytes per device
> +	 *  - 10 bytes for BINFO/BSTATUS(2), M0(8)
> +	 */
> +	sha_text = (num_downstream * 5 + 10) * 8;
> +	ret = intel_write_sha_text(dev_priv, sha_text);
> +	if (ret < 0)
> +		return ret;
> +
> +	// Finally, tell the HW we're done with the hash and wait for it to ACK
> +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_COMPLETE_HASH);
> +	if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_COMPLETE, 1)) {
> +		DRM_ERROR("Timed out waiting for SHA1 complete\n");
> +		return -ETIMEDOUT;
> +	}
> +	if (!(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_V_MATCH)) {
> +		DRM_ERROR("SHA-1 mismatch, HDCP failed\n");
> +		return -ENXIO;
> +	}
> +
> +	DRM_INFO("HDCP is enabled (%d downstream devices)\n", num_downstream);
> +	return 0;
> +}
> +
> +/* Implements Part 1 of the HDCP authorization procedure */
> +static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
> +			   const struct intel_hdcp_shim *shim)
> +{
> +	struct drm_i915_private *dev_priv;
> +	enum port port;
> +	unsigned long r0_prime_gen_start;
> +	int ret, i;
> +	union {
> +		u32 reg[2];
> +		u8 shim[DRM_HDCP_AN_LEN];
> +	} an;
> +	union {
> +		u32 reg[2];
> +		u8 shim[DRM_HDCP_KSV_LEN];
> +	} bksv;
> +	union {
> +		u32 reg;
> +		u8 shim[DRM_HDCP_RI_LEN];
> +	} ri;
> +	bool repeater_present;
> +
> +	dev_priv = intel_dig_port->base.base.dev->dev_private;
> +
> +	port = intel_dig_port->port;
> +
> +	// Initialize An with 2 random values and acquire it
> +	for (i = 0; i < 2; i++)
> +		I915_WRITE(SKL_PORT_HDCP_ANINIT(port), get_random_long());
> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_CAPTURE_AN);
> +
> +	// Wait for An to be acquired
> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> +		     SKL_HDCP_STATUS_AN_READY, 1)) {
> +		DRM_ERROR("Timed out waiting for An\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	an.reg[0] = I915_READ(SKL_PORT_HDCP_ANLO(port));
> +	an.reg[1] = I915_READ(SKL_PORT_HDCP_ANHI(port));
> +	ret = shim->write_an_aksv(intel_dig_port, an.shim);
> +	if (ret)
> +		return ret;
> +
> +	r0_prime_gen_start = jiffies;
> +
> +	memset(&bksv, 0, sizeof(bksv));
> +	ret = shim->read_bksv(intel_dig_port, bksv.shim);
> +	if (ret)
> +		return ret;
> +
> +	I915_WRITE(SKL_PORT_HDCP_BKSVLO(port), bksv.reg[0]);
> +	I915_WRITE(SKL_PORT_HDCP_BKSVHI(port), bksv.reg[1]);
> +
> +	ret = shim->repeater_present(intel_dig_port, &repeater_present);
> +	if (ret)
> +		return ret;
> +	if (repeater_present)
> +		I915_WRITE(SKL_HDCP_REP_CTL,
> +			   intel_hdcp_get_repeater_ctl(intel_dig_port));
> +
> +	ret = shim->toggle_signalling(intel_dig_port, true);
> +	if (ret)
> +		return ret;
> +
> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_AUTH_AND_ENC);
> +
> +	// Wait for R0 ready
> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> +		     (SKL_HDCP_STATUS_R0_READY | SKL_HDCP_STATUS_ENC), 1)) {
> +		DRM_ERROR("Timed out waiting for R0 ready\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	/*
> +	 * Wait for R0' to become available, the spec says 100ms from Aksv
> +	 * write. On DP, there's an R0_READY bit available but no such bit
> +	 * exists on HDMI. Since the upper-bound is the same, we'll just do
> +	 * the stupid thing instead of polling on one and not the other.
> +	 */
> +	wait_remaining_ms_from_jiffies(r0_prime_gen_start, 100);
> +
> +	ri.reg = 0;
> +	ret = shim->read_ri_prime(intel_dig_port, ri.shim);
> +	if (ret)
> +		return ret;
> +	I915_WRITE(SKL_PORT_HDCP_RPRIME(port), ri.reg);
> +
> +	// Wait for Ri prime match
> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> +		     (SKL_HDCP_STATUS_RI_MATCH | SKL_HDCP_STATUS_ENC), 1)) {
> +		DRM_ERROR("Timed out waiting for Ri prime match (%x)\n",
> +			  I915_READ(SKL_PORT_HDCP_STATUS(port)));
> +		return -ETIMEDOUT;
> +	}
> +
> +	// Wait for encryption confirmation
> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> +		      SKL_HDCP_STATUS_ENC, 20)) {
> +		DRM_ERROR("Timed out waiting for encryption\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	/*
> +	 * XXX: If we have MST-connected devices, we need to enable encryption
> +	 * on those as well.
> +	 */
> +
> +	return intel_hdcp_auth_downstream(intel_dig_port, shim);
> +}
> +
> +static
> +struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector)
> +{
> +	return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base);
> +}
> +
> +static int _intel_hdcp_disable(struct intel_connector *connector)
> +{
> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> +	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
> +	enum port port = intel_dig_port->port;
> +	int ret;
> +
> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) == 0, 20)) {
> +		DRM_ERROR("Failed to disable HDCP, timeout clearing status\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	intel_hdcp_clear_keys(dev_priv);
> +
> +	ret = connector->hdcp_shim->toggle_signalling(intel_dig_port, false);
> +	if (ret) {
> +		DRM_ERROR("Failed to disable HDCP signalling\n");
> +		return ret;
> +	}
> +
> +	DRM_INFO("HDCP is disabled\n");
> +	return 0;
> +}
> +
> +static int _intel_hdcp_enable(struct intel_connector *connector)
> +{
> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> +	int i, ret;
> +
> +	if (!(I915_READ(SKL_FUSE_STATUS) & SKL_FUSE_PG_DIST_STATUS(1))) {
> +		DRM_ERROR("PG1 is disabled, cannot load keys\n");
> +		return -ENXIO;
> +	}
> +
> +	for (i = 0; i < KEY_LOAD_TRIES; i++) {
> +		ret = intel_hdcp_load_keys(dev_priv);
> +		if (!ret)
> +			break;
> +		intel_hdcp_clear_keys(dev_priv);
> +	}
> +	if (ret) {
> +		DRM_ERROR("Could not load HDCP keys, (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = intel_hdcp_auth(conn_to_dig_port(connector),
> +			      connector->hdcp_shim);
> +	if (ret) {
> +		DRM_ERROR("Failed to authenticate HDCP (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +void intel_hdcp_work(struct work_struct *work)
> +{
> +	struct intel_connector *connector = container_of(to_delayed_work(work),
> +							 struct intel_connector,
> +						         hdcp_work);
> +	struct drm_device *dev = connector->base.dev;
> +	int ret;
> +
> +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> +
> +	ret = intel_hdcp_check_link(connector);
> +	if (!ret)
> +		schedule_delayed_work(&connector->hdcp_work,
> +				      DRM_HDCP_CHECK_PERIOD_MS);
> +
> +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
> +}
> +
> +int intel_hdcp_enable(struct intel_connector *connector)
> +{
> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> +	struct drm_device *dev = &dev_priv->drm;
> +	struct drm_connector_state *state = connector->base.state;
> +	int ret;
> +
> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	ret = _intel_hdcp_enable(connector);
> +	if (ret)
> +		return ret;
> +
> +	state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
> +
> +	schedule_delayed_work(&connector->hdcp_work, DRM_HDCP_CHECK_PERIOD_MS);
> +	return 0;
> +}
> +
> +int intel_hdcp_disable(struct intel_connector *connector)
> +{
> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> +	struct drm_device *dev = &dev_priv->drm;
> +
> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	cancel_delayed_work(&connector->hdcp_work);
> +
> +	return _intel_hdcp_disable(connector);
> +}
> +
> +/* Implements Part 3 of the HDCP authorization procedure */
> +int intel_hdcp_check_link(struct intel_connector *connector)
> +{
> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> +	struct drm_device *dev = &dev_priv->drm;
> +	struct drm_connector_state *state = connector->base.state;
> +	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
> +	enum port port = intel_dig_port->port;
> +	int ret;
> +
> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
> +
> +	if (state->content_protection != DRM_MODE_CONTENT_PROTECTION_ENABLED)
> +		return 0;
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	if (!(I915_READ(SKL_PORT_HDCP_STATUS(port)) & SKL_HDCP_STATUS_ENC)) {
> +		DRM_ERROR("HDCP check failed: link is not encrypted, %x\n",
> +			   I915_READ(SKL_PORT_HDCP_STATUS(port)));
> +		ret = -ENXIO;
> +		goto fail;
> +	}
> +
> +	if (connector->hdcp_shim->check_link(intel_dig_port))
> +		return 0;
> +
> +	DRM_INFO("HDCP link failed, retrying authentication\n");
> +
> +	ret = _intel_hdcp_disable(connector);
> +	if (ret) {
> +		DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
> +		goto fail;
> +	}
> +
> +	ret = _intel_hdcp_enable(connector);
> +	if (ret) {
> +		DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
> +		goto fail;
> +	}
> +
> +	return 0;
> +
> +fail:
> +	state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +	return ret;
> +}

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 5/6] drm/i915: Implement HDCP for HDMI
  2017-11-30  3:09   ` Sean Paul
@ 2017-12-01  7:31     ` Ramalingam C
  -1 siblings, 0 replies; 62+ messages in thread
From: Ramalingam C @ 2017-12-01  7:31 UTC (permalink / raw)
  To: Sean Paul, dri-devel, intel-gfx
  Cc: David Airlie, Joonas Lahtinen, linux-kernel, Rodrigo Vivi

Sean,

We might want to check the panel's HDCP capability? Agreed that majority 
of HDMI/DP panel's supports it, but there is a possibility that we might 
avoid authentication on non-hdcp panels.

As per HDCP specification, check for valid bksv will serve the purpose.


On Thursday 30 November 2017 08:39 AM, Sean Paul wrote:
> This patch adds HDCP support for HDMI connectors by implementing
> the intel_hdcp_shim.
>
> Nothing too special, just a bunch of DDC reads/writes.
>
> Signed-off-by: Sean Paul <seanpaul@chromium.org>
> ---
>   drivers/gpu/drm/i915/i915_reg.h   |   1 +
>   drivers/gpu/drm/i915/intel_ddi.c  |  50 ++++++++
>   drivers/gpu/drm/i915/intel_hdmi.c | 253 ++++++++++++++++++++++++++++++++++++++
>   3 files changed, 304 insertions(+)
>
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index 78370877fea3..061c52a5d7d7 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -8400,6 +8400,7 @@ enum skl_power_gate {
>   #define  TRANS_DDI_EDP_INPUT_A_ONOFF	(4<<12)
>   #define  TRANS_DDI_EDP_INPUT_B_ONOFF	(5<<12)
>   #define  TRANS_DDI_EDP_INPUT_C_ONOFF	(6<<12)
> +#define  TRANS_DDI_HDCP_SIGNALLING	(1<<9)
>   #define  TRANS_DDI_DP_VC_PAYLOAD_ALLOC	(1<<8)
>   #define  TRANS_DDI_HDMI_SCRAMBLER_CTS_ENABLE (1<<7)
>   #define  TRANS_DDI_HDMI_SCRAMBLER_RESET_FREQ (1<<6)
> diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
> index 0e69337f410d..0324b09ecf76 100644
> --- a/drivers/gpu/drm/i915/intel_ddi.c
> +++ b/drivers/gpu/drm/i915/intel_ddi.c
> @@ -1650,6 +1650,56 @@ void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
>   	I915_WRITE(reg, val);
>   }
>   
> +int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder)
> +{
> +	struct drm_device *dev = intel_encoder->base.dev;
> +	struct drm_i915_private *dev_priv = to_i915(dev);
> +	enum pipe pipe = 0;
> +	int ret = 0;
> +	uint32_t tmp;
> +
> +	if (!intel_display_power_get_if_enabled(dev_priv,
> +						intel_encoder->power_domain))
> +		return -ENXIO;
> +
> +	if (!intel_encoder->get_hw_state(intel_encoder, &pipe)) {
> +		ret = -EIO;
> +		goto out;
> +	}
> +
> +	tmp = I915_READ(TRANS_DDI_FUNC_CTL(pipe));
> +	tmp &= ~TRANS_DDI_HDCP_SIGNALLING;
> +	I915_WRITE(TRANS_DDI_FUNC_CTL(pipe), tmp);
> +out:
> +	intel_display_power_put(dev_priv, intel_encoder->power_domain);
> +	return ret;
> +}
> +
> +int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder)
> +{
> +	struct drm_device *dev = intel_encoder->base.dev;
> +	struct drm_i915_private *dev_priv = to_i915(dev);
> +	enum pipe pipe = 0;
> +	int ret = 0;
> +	uint32_t tmp;
> +
> +	if (!intel_display_power_get_if_enabled(dev_priv,
> +						intel_encoder->power_domain))
> +		return -ENXIO;
> +
> +	if (!intel_encoder->get_hw_state(intel_encoder, &pipe)) {
> +		ret = -EIO;
> +		goto out;
> +	}
> +
> +	tmp = I915_READ(TRANS_DDI_FUNC_CTL(pipe));
> +	tmp |= TRANS_DDI_HDCP_SIGNALLING;
> +	I915_WRITE(TRANS_DDI_FUNC_CTL(pipe), tmp);
> +out:
> +	intel_display_power_put(dev_priv, intel_encoder->power_domain);
> +	return ret;
> +}
> +
>   bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector)
>   {
>   	struct drm_device *dev = intel_connector->base.dev;
> diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
> index 59247a49a077..563168897dd9 100644
> --- a/drivers/gpu/drm/i915/intel_hdmi.c
> +++ b/drivers/gpu/drm/i915/intel_hdmi.c
> @@ -34,6 +34,7 @@
>   #include <drm/drm_atomic_helper.h>
>   #include <drm/drm_crtc.h>
>   #include <drm/drm_edid.h>
> +#include <drm/drm_hdcp.h>
>   #include <drm/drm_scdc_helper.h>
>   #include "intel_drv.h"
>   #include <drm/i915_drm.h>
> @@ -875,6 +876,252 @@ void intel_dp_dual_mode_set_tmds_output(struct intel_hdmi *hdmi, bool enable)
>   					 adapter, enable);
>   }
>   
> +static int intel_hdmi_hdcp_read(struct intel_digital_port *intel_dig_port,
> +			        unsigned int offset, void *buffer, size_t size)
> +{
> +	struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
> +	struct drm_i915_private *dev_priv =
> +		intel_dig_port->base.base.dev->dev_private;
> +	struct i2c_adapter *adapter = intel_gmbus_get_adapter(dev_priv,
> +							      hdmi->ddc_bus);
> +	int ret;
> +	u8 start = offset & 0xff;
> +	struct i2c_msg msgs[] = {
> +		{
> +			.addr = DRM_HDCP_DDC_ADDR,
> +			.flags = 0,
> +			.len = 1,
> +			.buf = &start,
> +		},
> +		{
> +			.addr = DRM_HDCP_DDC_ADDR,
> +			.flags = I2C_M_RD,
> +			.len = size,
> +			.buf = buffer
> +		}
> +	};
> +	ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
> +	if (ret == ARRAY_SIZE(msgs))
> +		return 0;
> +	return ret >= 0 ? -EIO : ret;
> +}
> +
> +static int intel_hdmi_hdcp_write(struct intel_digital_port *intel_dig_port,
> +			         unsigned int offset, void *buffer, size_t size)
> +{
> +	struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
> +	struct drm_i915_private *dev_priv =
> +		intel_dig_port->base.base.dev->dev_private;
> +	struct i2c_adapter *adapter = intel_gmbus_get_adapter(dev_priv,
> +							      hdmi->ddc_bus);
> +	int ret;
> +	u8 *write_buf;
> +	struct i2c_msg msg;
> +
> +	write_buf = kzalloc(size + 1, GFP_KERNEL);
> +	if (!write_buf)
> +		return -ENOMEM;
> +
> +	write_buf[0] = offset & 0xff;
> +	memcpy(&write_buf[1], buffer, size);
> +
> +	msg.addr = DRM_HDCP_DDC_ADDR;
> +	msg.flags = 0,
> +	msg.len = size + 1,
> +	msg.buf = write_buf;
> +
> +	ret = i2c_transfer(adapter, &msg, 1);
> +	if (ret == 1)
> +		return 0;
> +	return ret >= 0 ? -EIO : ret;
> +}
> +
> +static
> +int intel_hdmi_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port,
> +				  u8 *an)
> +{
> +	struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
> +	struct drm_i915_private *dev_priv =
> +		intel_dig_port->base.base.dev->dev_private;
> +	struct i2c_adapter *adapter = intel_gmbus_get_adapter(dev_priv,
> +							      hdmi->ddc_bus);
> +	int ret;
> +
> +	ret = intel_hdmi_hdcp_write(intel_dig_port, DRM_HDCP_DDC_AN, an,
> +				    DRM_HDCP_AN_LEN);
> +	if (ret) {
> +		DRM_ERROR("Write An over DDC failed (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = intel_gmbus_output_aksv(adapter);
> +	if (ret < 0) {
> +		DRM_ERROR("Failed to output aksv (%d)\n", ret);
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +static int intel_hdmi_hdcp_read_bksv(struct intel_digital_port *intel_dig_port,
> +				     u8 *bksv)
> +{
> +	int ret;
> +	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BKSV, bksv,
> +				   DRM_HDCP_KSV_LEN);
> +	if (ret)
> +		DRM_ERROR("Read Bksv over DDC failed (%d)\n", ret);
> +	return ret;
> +}
> +
> +static
> +int intel_hdmi_hdcp_read_bstatus(struct intel_digital_port *intel_dig_port,
> +				 u8 *bstatus)
> +{
> +	int ret;
> +	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BSTATUS,
> +				   bstatus, DRM_HDCP_BSTATUS_LEN);
> +	if (ret)
> +		DRM_ERROR("Read bstatus over DDC failed (%d)\n", ret);
> +	return ret;
> +}
> +
> +static
> +int intel_hdmi_hdcp_repeater_present(struct intel_digital_port *intel_dig_port,
> +				     bool *repeater_present)
> +{
> +	int ret;
> +	u8 val;
> +
> +	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BCAPS, &val, 1);
> +	if (ret) {
> +		DRM_ERROR("Read bcaps over DDC failed (%d)\n", ret);
> +		return ret;
> +	}
> +	*repeater_present = val & DRM_HDCP_DDC_BCAPS_REPEATER_PRESENT;
> +	return 0;
> +}
> +
> +static
> +int intel_hdmi_hdcp_read_ri_prime(struct intel_digital_port *intel_dig_port,
> +				  u8 *ri_prime)
> +{
> +	int ret;
> +	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_RI_PRIME,
> +				   ri_prime, DRM_HDCP_RI_LEN);
> +	if (ret)
> +		DRM_ERROR("Read Ri' over DDC failed (%d)\n", ret);
> +	return ret;
> +}
> +
> +static
> +int intel_hdmi_hdcp_read_ksv_ready(struct intel_digital_port *intel_dig_port,
> +				   bool *ksv_ready)
> +{
> +	int ret;
> +	u8 val;
> +
> +	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BCAPS, &val, 1);
> +	if (ret) {
> +		DRM_ERROR("Read bcaps over DDC failed (%d)\n", ret);
> +		return ret;
> +	}
> +	*ksv_ready = val & DRM_HDCP_DDC_BCAPS_KSV_FIFO_READY;
> +	return 0;
> +}
> +
> +static
> +int intel_hdmi_hdcp_read_ksv_fifo(struct intel_digital_port *intel_dig_port,
> +			          int num_downstream, u8 *ksv_fifo)
> +{
> +	int ret;
> +	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_KSV_FIFO,
> +				   ksv_fifo, num_downstream * DRM_HDCP_KSV_LEN);
> +	if (ret) {
> +		DRM_ERROR("Read ksv fifo over DDC failed (%d)\n", ret);
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +static
> +int intel_hdmi_hdcp_read_v_prime_part(struct intel_digital_port *intel_dig_port,
> +				      int i, u32 *part)
> +{
> +	int ret;
> +
> +	if (i >= DRM_HDCP_V_PRIME_NUM_PARTS)
> +		return -EINVAL;
> +
> +	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_V_PRIME(i),
> +				   part, DRM_HDCP_V_PRIME_PART_LEN);
> +	if (ret)
> +		DRM_ERROR("Read V'[%d] over DDC failed (%d)\n", i, ret);
> +	return ret;
> +}
> +
> +static
> +int intel_hdmi_hdcp_toggle_signalling(struct intel_digital_port *intel_dig_port,
> +				      bool enable)
> +{
> +	int ret;
> +	if (enable) {
> +		ret = intel_ddi_enable_hdcp_signalling(&intel_dig_port->base);
> +		if (ret) {
> +			DRM_ERROR("Enable HDCP signalling failed (%d)\n", ret);
> +			return ret;
> +		}
> +	} else {
> +		usleep_range(6, 60); /* Bspec says >= 6us */
> +		ret = intel_ddi_disable_hdcp_signalling(&intel_dig_port->base);
> +		if (ret) {
> +			DRM_ERROR("Enable HDCP signalling failed (%d)\n", ret);
> +			return ret;
> +		}
> +	}
> +	return 0;
> +}
> +
> +static
> +bool intel_hdmi_hdcp_check_link(struct intel_digital_port *intel_dig_port)
> +{
> +	struct drm_i915_private *dev_priv =
> +		intel_dig_port->base.base.dev->dev_private;
> +	enum port port = intel_dig_port->port;
> +	int ret;
> +	union {
> +		u32 reg;
> +		u8 shim[DRM_HDCP_RI_LEN];
> +	} ri;
> +
> +	ret = intel_hdmi_hdcp_read_ri_prime(intel_dig_port, ri.shim);
> +	if (ret)
> +		return false;
> +
> +	I915_WRITE(SKL_PORT_HDCP_RPRIME(port), ri.reg);
> +
> +	// Wait for Ri prime match
> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> +		     (SKL_HDCP_STATUS_RI_MATCH | SKL_HDCP_STATUS_ENC), 1)) {
> +		DRM_ERROR("Ri' mismatch detected, link check failed (%x)\n",
> +			  I915_READ(SKL_PORT_HDCP_STATUS(port)));
> +		return false;
> +	}
> +	return true;
> +}
> +
> +static const struct intel_hdcp_shim intel_hdmi_hdcp_shim = {
> +	.write_an_aksv = intel_hdmi_hdcp_write_an_aksv,
> +	.read_bksv = intel_hdmi_hdcp_read_bksv,
> +	.read_bstatus = intel_hdmi_hdcp_read_bstatus,
> +	.repeater_present = intel_hdmi_hdcp_repeater_present,
> +	.read_ri_prime = intel_hdmi_hdcp_read_ri_prime,
> +	.read_ksv_ready = intel_hdmi_hdcp_read_ksv_ready,
> +	.read_ksv_fifo = intel_hdmi_hdcp_read_ksv_fifo,
> +	.read_v_prime_part = intel_hdmi_hdcp_read_v_prime_part,
> +	.toggle_signalling = intel_hdmi_hdcp_toggle_signalling,
> +	.check_link = intel_hdmi_hdcp_check_link,
> +};
> +
>   static void intel_hdmi_prepare(struct intel_encoder *encoder,
>   			       const struct intel_crtc_state *crtc_state)
>   {
> @@ -2039,6 +2286,12 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
>   
>   	intel_hdmi_add_properties(intel_hdmi, connector);
>   
> +	if (INTEL_GEN(dev_priv) >= 9) {
> +		drm_connector_attach_content_protection_property(connector);
> +		intel_connector->hdcp_shim = &intel_hdmi_hdcp_shim;
> +		INIT_DELAYED_WORK(&intel_connector->hdcp_work, intel_hdcp_work);
> +	}
> +
>   	intel_connector_attach_encoder(intel_connector, intel_encoder);
>   	intel_hdmi->attached_connector = intel_connector;
>   

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 5/6] drm/i915: Implement HDCP for HDMI
@ 2017-12-01  7:31     ` Ramalingam C
  0 siblings, 0 replies; 62+ messages in thread
From: Ramalingam C @ 2017-12-01  7:31 UTC (permalink / raw)
  To: Sean Paul, dri-devel, intel-gfx; +Cc: David Airlie, linux-kernel, Rodrigo Vivi

Sean,

We might want to check the panel's HDCP capability? Agreed that majority 
of HDMI/DP panel's supports it, but there is a possibility that we might 
avoid authentication on non-hdcp panels.

As per HDCP specification, check for valid bksv will serve the purpose.


On Thursday 30 November 2017 08:39 AM, Sean Paul wrote:
> This patch adds HDCP support for HDMI connectors by implementing
> the intel_hdcp_shim.
>
> Nothing too special, just a bunch of DDC reads/writes.
>
> Signed-off-by: Sean Paul <seanpaul@chromium.org>
> ---
>   drivers/gpu/drm/i915/i915_reg.h   |   1 +
>   drivers/gpu/drm/i915/intel_ddi.c  |  50 ++++++++
>   drivers/gpu/drm/i915/intel_hdmi.c | 253 ++++++++++++++++++++++++++++++++++++++
>   3 files changed, 304 insertions(+)
>
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index 78370877fea3..061c52a5d7d7 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -8400,6 +8400,7 @@ enum skl_power_gate {
>   #define  TRANS_DDI_EDP_INPUT_A_ONOFF	(4<<12)
>   #define  TRANS_DDI_EDP_INPUT_B_ONOFF	(5<<12)
>   #define  TRANS_DDI_EDP_INPUT_C_ONOFF	(6<<12)
> +#define  TRANS_DDI_HDCP_SIGNALLING	(1<<9)
>   #define  TRANS_DDI_DP_VC_PAYLOAD_ALLOC	(1<<8)
>   #define  TRANS_DDI_HDMI_SCRAMBLER_CTS_ENABLE (1<<7)
>   #define  TRANS_DDI_HDMI_SCRAMBLER_RESET_FREQ (1<<6)
> diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
> index 0e69337f410d..0324b09ecf76 100644
> --- a/drivers/gpu/drm/i915/intel_ddi.c
> +++ b/drivers/gpu/drm/i915/intel_ddi.c
> @@ -1650,6 +1650,56 @@ void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
>   	I915_WRITE(reg, val);
>   }
>   
> +int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder)
> +{
> +	struct drm_device *dev = intel_encoder->base.dev;
> +	struct drm_i915_private *dev_priv = to_i915(dev);
> +	enum pipe pipe = 0;
> +	int ret = 0;
> +	uint32_t tmp;
> +
> +	if (!intel_display_power_get_if_enabled(dev_priv,
> +						intel_encoder->power_domain))
> +		return -ENXIO;
> +
> +	if (!intel_encoder->get_hw_state(intel_encoder, &pipe)) {
> +		ret = -EIO;
> +		goto out;
> +	}
> +
> +	tmp = I915_READ(TRANS_DDI_FUNC_CTL(pipe));
> +	tmp &= ~TRANS_DDI_HDCP_SIGNALLING;
> +	I915_WRITE(TRANS_DDI_FUNC_CTL(pipe), tmp);
> +out:
> +	intel_display_power_put(dev_priv, intel_encoder->power_domain);
> +	return ret;
> +}
> +
> +int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder)
> +{
> +	struct drm_device *dev = intel_encoder->base.dev;
> +	struct drm_i915_private *dev_priv = to_i915(dev);
> +	enum pipe pipe = 0;
> +	int ret = 0;
> +	uint32_t tmp;
> +
> +	if (!intel_display_power_get_if_enabled(dev_priv,
> +						intel_encoder->power_domain))
> +		return -ENXIO;
> +
> +	if (!intel_encoder->get_hw_state(intel_encoder, &pipe)) {
> +		ret = -EIO;
> +		goto out;
> +	}
> +
> +	tmp = I915_READ(TRANS_DDI_FUNC_CTL(pipe));
> +	tmp |= TRANS_DDI_HDCP_SIGNALLING;
> +	I915_WRITE(TRANS_DDI_FUNC_CTL(pipe), tmp);
> +out:
> +	intel_display_power_put(dev_priv, intel_encoder->power_domain);
> +	return ret;
> +}
> +
>   bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector)
>   {
>   	struct drm_device *dev = intel_connector->base.dev;
> diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
> index 59247a49a077..563168897dd9 100644
> --- a/drivers/gpu/drm/i915/intel_hdmi.c
> +++ b/drivers/gpu/drm/i915/intel_hdmi.c
> @@ -34,6 +34,7 @@
>   #include <drm/drm_atomic_helper.h>
>   #include <drm/drm_crtc.h>
>   #include <drm/drm_edid.h>
> +#include <drm/drm_hdcp.h>
>   #include <drm/drm_scdc_helper.h>
>   #include "intel_drv.h"
>   #include <drm/i915_drm.h>
> @@ -875,6 +876,252 @@ void intel_dp_dual_mode_set_tmds_output(struct intel_hdmi *hdmi, bool enable)
>   					 adapter, enable);
>   }
>   
> +static int intel_hdmi_hdcp_read(struct intel_digital_port *intel_dig_port,
> +			        unsigned int offset, void *buffer, size_t size)
> +{
> +	struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
> +	struct drm_i915_private *dev_priv =
> +		intel_dig_port->base.base.dev->dev_private;
> +	struct i2c_adapter *adapter = intel_gmbus_get_adapter(dev_priv,
> +							      hdmi->ddc_bus);
> +	int ret;
> +	u8 start = offset & 0xff;
> +	struct i2c_msg msgs[] = {
> +		{
> +			.addr = DRM_HDCP_DDC_ADDR,
> +			.flags = 0,
> +			.len = 1,
> +			.buf = &start,
> +		},
> +		{
> +			.addr = DRM_HDCP_DDC_ADDR,
> +			.flags = I2C_M_RD,
> +			.len = size,
> +			.buf = buffer
> +		}
> +	};
> +	ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
> +	if (ret == ARRAY_SIZE(msgs))
> +		return 0;
> +	return ret >= 0 ? -EIO : ret;
> +}
> +
> +static int intel_hdmi_hdcp_write(struct intel_digital_port *intel_dig_port,
> +			         unsigned int offset, void *buffer, size_t size)
> +{
> +	struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
> +	struct drm_i915_private *dev_priv =
> +		intel_dig_port->base.base.dev->dev_private;
> +	struct i2c_adapter *adapter = intel_gmbus_get_adapter(dev_priv,
> +							      hdmi->ddc_bus);
> +	int ret;
> +	u8 *write_buf;
> +	struct i2c_msg msg;
> +
> +	write_buf = kzalloc(size + 1, GFP_KERNEL);
> +	if (!write_buf)
> +		return -ENOMEM;
> +
> +	write_buf[0] = offset & 0xff;
> +	memcpy(&write_buf[1], buffer, size);
> +
> +	msg.addr = DRM_HDCP_DDC_ADDR;
> +	msg.flags = 0,
> +	msg.len = size + 1,
> +	msg.buf = write_buf;
> +
> +	ret = i2c_transfer(adapter, &msg, 1);
> +	if (ret == 1)
> +		return 0;
> +	return ret >= 0 ? -EIO : ret;
> +}
> +
> +static
> +int intel_hdmi_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port,
> +				  u8 *an)
> +{
> +	struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
> +	struct drm_i915_private *dev_priv =
> +		intel_dig_port->base.base.dev->dev_private;
> +	struct i2c_adapter *adapter = intel_gmbus_get_adapter(dev_priv,
> +							      hdmi->ddc_bus);
> +	int ret;
> +
> +	ret = intel_hdmi_hdcp_write(intel_dig_port, DRM_HDCP_DDC_AN, an,
> +				    DRM_HDCP_AN_LEN);
> +	if (ret) {
> +		DRM_ERROR("Write An over DDC failed (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = intel_gmbus_output_aksv(adapter);
> +	if (ret < 0) {
> +		DRM_ERROR("Failed to output aksv (%d)\n", ret);
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +static int intel_hdmi_hdcp_read_bksv(struct intel_digital_port *intel_dig_port,
> +				     u8 *bksv)
> +{
> +	int ret;
> +	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BKSV, bksv,
> +				   DRM_HDCP_KSV_LEN);
> +	if (ret)
> +		DRM_ERROR("Read Bksv over DDC failed (%d)\n", ret);
> +	return ret;
> +}
> +
> +static
> +int intel_hdmi_hdcp_read_bstatus(struct intel_digital_port *intel_dig_port,
> +				 u8 *bstatus)
> +{
> +	int ret;
> +	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BSTATUS,
> +				   bstatus, DRM_HDCP_BSTATUS_LEN);
> +	if (ret)
> +		DRM_ERROR("Read bstatus over DDC failed (%d)\n", ret);
> +	return ret;
> +}
> +
> +static
> +int intel_hdmi_hdcp_repeater_present(struct intel_digital_port *intel_dig_port,
> +				     bool *repeater_present)
> +{
> +	int ret;
> +	u8 val;
> +
> +	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BCAPS, &val, 1);
> +	if (ret) {
> +		DRM_ERROR("Read bcaps over DDC failed (%d)\n", ret);
> +		return ret;
> +	}
> +	*repeater_present = val & DRM_HDCP_DDC_BCAPS_REPEATER_PRESENT;
> +	return 0;
> +}
> +
> +static
> +int intel_hdmi_hdcp_read_ri_prime(struct intel_digital_port *intel_dig_port,
> +				  u8 *ri_prime)
> +{
> +	int ret;
> +	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_RI_PRIME,
> +				   ri_prime, DRM_HDCP_RI_LEN);
> +	if (ret)
> +		DRM_ERROR("Read Ri' over DDC failed (%d)\n", ret);
> +	return ret;
> +}
> +
> +static
> +int intel_hdmi_hdcp_read_ksv_ready(struct intel_digital_port *intel_dig_port,
> +				   bool *ksv_ready)
> +{
> +	int ret;
> +	u8 val;
> +
> +	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BCAPS, &val, 1);
> +	if (ret) {
> +		DRM_ERROR("Read bcaps over DDC failed (%d)\n", ret);
> +		return ret;
> +	}
> +	*ksv_ready = val & DRM_HDCP_DDC_BCAPS_KSV_FIFO_READY;
> +	return 0;
> +}
> +
> +static
> +int intel_hdmi_hdcp_read_ksv_fifo(struct intel_digital_port *intel_dig_port,
> +			          int num_downstream, u8 *ksv_fifo)
> +{
> +	int ret;
> +	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_KSV_FIFO,
> +				   ksv_fifo, num_downstream * DRM_HDCP_KSV_LEN);
> +	if (ret) {
> +		DRM_ERROR("Read ksv fifo over DDC failed (%d)\n", ret);
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +static
> +int intel_hdmi_hdcp_read_v_prime_part(struct intel_digital_port *intel_dig_port,
> +				      int i, u32 *part)
> +{
> +	int ret;
> +
> +	if (i >= DRM_HDCP_V_PRIME_NUM_PARTS)
> +		return -EINVAL;
> +
> +	ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_V_PRIME(i),
> +				   part, DRM_HDCP_V_PRIME_PART_LEN);
> +	if (ret)
> +		DRM_ERROR("Read V'[%d] over DDC failed (%d)\n", i, ret);
> +	return ret;
> +}
> +
> +static
> +int intel_hdmi_hdcp_toggle_signalling(struct intel_digital_port *intel_dig_port,
> +				      bool enable)
> +{
> +	int ret;
> +	if (enable) {
> +		ret = intel_ddi_enable_hdcp_signalling(&intel_dig_port->base);
> +		if (ret) {
> +			DRM_ERROR("Enable HDCP signalling failed (%d)\n", ret);
> +			return ret;
> +		}
> +	} else {
> +		usleep_range(6, 60); /* Bspec says >= 6us */
> +		ret = intel_ddi_disable_hdcp_signalling(&intel_dig_port->base);
> +		if (ret) {
> +			DRM_ERROR("Enable HDCP signalling failed (%d)\n", ret);
> +			return ret;
> +		}
> +	}
> +	return 0;
> +}
> +
> +static
> +bool intel_hdmi_hdcp_check_link(struct intel_digital_port *intel_dig_port)
> +{
> +	struct drm_i915_private *dev_priv =
> +		intel_dig_port->base.base.dev->dev_private;
> +	enum port port = intel_dig_port->port;
> +	int ret;
> +	union {
> +		u32 reg;
> +		u8 shim[DRM_HDCP_RI_LEN];
> +	} ri;
> +
> +	ret = intel_hdmi_hdcp_read_ri_prime(intel_dig_port, ri.shim);
> +	if (ret)
> +		return false;
> +
> +	I915_WRITE(SKL_PORT_HDCP_RPRIME(port), ri.reg);
> +
> +	// Wait for Ri prime match
> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> +		     (SKL_HDCP_STATUS_RI_MATCH | SKL_HDCP_STATUS_ENC), 1)) {
> +		DRM_ERROR("Ri' mismatch detected, link check failed (%x)\n",
> +			  I915_READ(SKL_PORT_HDCP_STATUS(port)));
> +		return false;
> +	}
> +	return true;
> +}
> +
> +static const struct intel_hdcp_shim intel_hdmi_hdcp_shim = {
> +	.write_an_aksv = intel_hdmi_hdcp_write_an_aksv,
> +	.read_bksv = intel_hdmi_hdcp_read_bksv,
> +	.read_bstatus = intel_hdmi_hdcp_read_bstatus,
> +	.repeater_present = intel_hdmi_hdcp_repeater_present,
> +	.read_ri_prime = intel_hdmi_hdcp_read_ri_prime,
> +	.read_ksv_ready = intel_hdmi_hdcp_read_ksv_ready,
> +	.read_ksv_fifo = intel_hdmi_hdcp_read_ksv_fifo,
> +	.read_v_prime_part = intel_hdmi_hdcp_read_v_prime_part,
> +	.toggle_signalling = intel_hdmi_hdcp_toggle_signalling,
> +	.check_link = intel_hdmi_hdcp_check_link,
> +};
> +
>   static void intel_hdmi_prepare(struct intel_encoder *encoder,
>   			       const struct intel_crtc_state *crtc_state)
>   {
> @@ -2039,6 +2286,12 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
>   
>   	intel_hdmi_add_properties(intel_hdmi, connector);
>   
> +	if (INTEL_GEN(dev_priv) >= 9) {
> +		drm_connector_attach_content_protection_property(connector);
> +		intel_connector->hdcp_shim = &intel_hdmi_hdcp_shim;
> +		INIT_DELAYED_WORK(&intel_connector->hdcp_work, intel_hdcp_work);
> +	}
> +
>   	intel_connector_attach_encoder(intel_connector, intel_encoder);
>   	intel_hdmi->attached_connector = intel_connector;
>   

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Intel-gfx] [RFC PATCH 3/6] drm/i915: Add HDCP framework + base implementation
  2017-12-01  7:23     ` Ramalingam C
@ 2017-12-01  7:36       ` Daniel Vetter
  -1 siblings, 0 replies; 62+ messages in thread
From: Daniel Vetter @ 2017-12-01  7:36 UTC (permalink / raw)
  To: Ramalingam C
  Cc: Sean Paul, dri-devel, intel-gfx, David Airlie, linux-kernel,
	Rodrigo Vivi

On Fri, Dec 01, 2017 at 12:53:31PM +0530, Ramalingam C wrote:
> Sean,
> 
> IMHO, it will good if we can have all generic hdcp1.4 authentication flow in
> drm helpers and all interested display drivers to use them.
> 
> This Design will make the extending of hdcp easy for other display drivers
> based on DRM.
> 
> We can have the required drm_hdcp_shim type of implementation at drm
> structure which will be called for platform specific operations (like
> prepare an, send aksv, program bksv/repeater/r0 and verify sha1 etc)?

I discussed this exact question with Sean Paul, and apparently the
hardware designs are too diverse to make shared code much useful. Some hw
has the entire hdcp flow in hw, some almost nothing (like i915 here), and
then there's everything in between.

Given that Sean has seen a lot more hdcp implementations than we have,
that we right now have no other implementation than i915 in upstream and
than wrong abstraction is much harder to fix than no abstraction I'm going
with Sean's approach of "no generic abstraction" here. Personally I'm not
even fully sold on the shim abstraction, but I think by&large that one is
fine.

> On Thursday 30 November 2017 08:38 AM, Sean Paul wrote:
> > This patch adds the framework required to add HDCP support to intel
> > connectors. It implements Aksv loading from fuse, and parts 1/2/3
> > of the HDCP authentication scheme.
> > 
> > Note that without shim implementations, this does not actually implement
> > HDCP. That will come in subsequent patches.
> > 
> > Signed-off-by: Sean Paul <seanpaul@chromium.org>
> > ---
> >   drivers/gpu/drm/i915/Makefile       |   1 +
> >   drivers/gpu/drm/i915/i915_reg.h     |  83 +++++
> >   drivers/gpu/drm/i915/intel_atomic.c |  26 +-
> >   drivers/gpu/drm/i915/intel_ddi.c    |  14 +
> >   drivers/gpu/drm/i915/intel_drv.h    |  53 +++
> >   drivers/gpu/drm/i915/intel_hdcp.c   | 636 ++++++++++++++++++++++++++++++++++++
> >   6 files changed, 811 insertions(+), 2 deletions(-)
> >   create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c
> > 
> > diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> > index 6c3b0481ef82..1e745508e437 100644
> > --- a/drivers/gpu/drm/i915/Makefile
> > +++ b/drivers/gpu/drm/i915/Makefile
> > @@ -87,6 +87,7 @@ i915-y += intel_audio.o \
> >   	  intel_fbc.o \
> >   	  intel_fifo_underrun.o \
> >   	  intel_frontbuffer.o \
> > +	  intel_hdcp.o \
> >   	  intel_hotplug.o \
> >   	  intel_modes.o \
> >   	  intel_overlay.o \
> > diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> > index 68a58cce6ab1..43128030171d 100644
> > --- a/drivers/gpu/drm/i915/i915_reg.h
> > +++ b/drivers/gpu/drm/i915/i915_reg.h
> > @@ -7991,6 +7991,7 @@ enum {
> >   #define     GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT	8
> >   #define     GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT	16
> >   #define     GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT	24
> > +#define   SKL_PCODE_LOAD_HDCP_KEYS		0x5
> >   #define   SKL_PCODE_CDCLK_CONTROL		0x7
> >   #define     SKL_CDCLK_PREPARE_FOR_CHANGE	0x3
> >   #define     SKL_CDCLK_READY_FOR_CHANGE		0x1
> > @@ -8285,6 +8286,88 @@ enum skl_power_gate {
> >   #define  SKL_PW_TO_PG(pw)			((pw) - SKL_DISP_PW_1 + SKL_PG1)
> >   #define  SKL_FUSE_PG_DIST_STATUS(pg)		(1 << (27 - (pg)))
> > +
> > +/* HDCP Key Registers */
> > +#define SKL_HDCP_KEY_CONF		_MMIO(0x66c00)
> > +#define	 SKL_HDCP_AKSV_SEND_TRIGGER	BIT(31)
> > +#define  SKL_HDCP_CLEAR_KEYS_TRIGGER	BIT(30)
> > +#define SKL_HDCP_KEY_STATUS		_MMIO(0x66c04)
> > +#define  SKL_HDCP_FUSE_IN_PROGRESS	BIT(7)
> > +#define  SKL_HDCP_FUSE_ERROR		BIT(6)
> > +#define  SKL_HDCP_FUSE_DONE		BIT(5)
> > +#define  SKL_HDCP_KEY_LOAD_STATUS	BIT(1)
> > +#define  SKL_HDCP_KEY_LOAD_DONE		BIT(0)
> > +#define SKL_HDCP_AKSV_LO		_MMIO(0x66c10)
> > +#define SKL_HDCP_AKSV_HI		_MMIO(0x66c14)
> > +
> > +/* HDCP Repeater Registers */
> > +#define SKL_HDCP_REP_CTL		_MMIO(0x66d00)
> > +#define  SKL_HDCP_DDIB_REP_PRESENT	BIT(30)
> > +#define  SKL_HDCP_DDIA_REP_PRESENT	BIT(29)
> > +#define  SKL_HDCP_DDIC_REP_PRESENT	BIT(28)
> > +#define  SKL_HDCP_DDID_REP_PRESENT	BIT(27)
> > +#define  SKL_HDCP_DDIF_REP_PRESENT	BIT(26)
> > +#define  SKL_HDCP_DDIE_REP_PRESENT	BIT(25)
> > +#define  SKL_HDCP_DDIB_SHA1_M0		(1 << 20)
> > +#define  SKL_HDCP_DDIA_SHA1_M0		(2 << 20)
> > +#define  SKL_HDCP_DDIC_SHA1_M0		(3 << 20)
> > +#define  SKL_HDCP_DDID_SHA1_M0		(4 << 20)
> > +#define  SKL_HDCP_DDIF_SHA1_M0		(5 << 20)
> > +#define  SKL_HDCP_DDIE_SHA1_M0		(6 << 20) // Bspec says 5?
> > +#define  SKL_HDCP_SHA1_BUSY		BIT(16)
> > +#define  SKL_HDCP_SHA1_READY		BIT(17)
> > +#define  SKL_HDCP_SHA1_COMPLETE		BIT(18)
> > +#define  SKL_HDCP_SHA1_V_MATCH		BIT(19)
> > +#define  SKL_HDCP_SHA1_TEXT_32		(1 << 1)
> > +#define  SKL_HDCP_SHA1_COMPLETE_HASH	(2 << 1)
> > +#define  SKL_HDCP_SHA1_TEXT_24		(4 << 1)
> > +#define  SKL_HDCP_SHA1_TEXT_16		(5 << 1)
> > +#define  SKL_HDCP_SHA1_TEXT_8		(6 << 1)
> > +#define  SKL_HDCP_SHA1_TEXT_0		(7 << 1)
> > +#define SKL_HDCP_SHA_V_PRIME_H0		_MMIO(0x66d04)
> > +#define SKL_HDCP_SHA_V_PRIME_H1		_MMIO(0x66d08)
> > +#define SKL_HDCP_SHA_V_PRIME_H2		_MMIO(0x66d0C)
> > +#define SKL_HDCP_SHA_V_PRIME_H3		_MMIO(0x66d10)
> > +#define SKL_HDCP_SHA_V_PRIME_H4		_MMIO(0x66d14)
> > +#define SKL_HDCP_SHA_V_PRIME(h)		_MMIO((0x66d04 + h * 4))
> > +#define SKL_HDCP_SHA_TEXT		_MMIO(0x66d18)
> > +
> > +/* HDCP Auth Registers */
> > +#define _SKL_PORTA_HDCP_AUTHENC		0x66800
> > +#define _SKL_PORTB_HDCP_AUTHENC		0x66500
> > +#define _SKL_PORTC_HDCP_AUTHENC		0x66600
> > +#define _SKL_PORTD_HDCP_AUTHENC		0x66700
> > +#define _SKL_PORTE_HDCP_AUTHENC		0x66A00
> > +#define _SKL_PORTF_HDCP_AUTHENC		0x66900
> > +#define _SKL_PORT_HDCP_AUTHENC(port, x)	_MMIO(_PICK(port, \
> > +					  _SKL_PORTA_HDCP_AUTHENC, \
> > +					  _SKL_PORTB_HDCP_AUTHENC, \
> > +					  _SKL_PORTC_HDCP_AUTHENC, \
> > +					  _SKL_PORTD_HDCP_AUTHENC, \
> > +					  _SKL_PORTE_HDCP_AUTHENC, \
> > +					  _SKL_PORTF_HDCP_AUTHENC) + x)
> > +#define SKL_PORT_HDCP_CONF(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x0)
> > +#define  SKL_HDCP_CONF_CAPTURE_AN	BIT(0)
> > +#define  SKL_HDCP_CONF_AUTH_AND_ENC	(BIT(1) | BIT(0))
> > +#define SKL_PORT_HDCP_ANINIT(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x4)
> > +#define SKL_PORT_HDCP_ANLO(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x8)
> > +#define SKL_PORT_HDCP_ANHI(port)	_SKL_PORT_HDCP_AUTHENC(port, 0xC)
> > +#define SKL_PORT_HDCP_BKSVLO(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x10)
> > +#define SKL_PORT_HDCP_BKSVHI(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x14)
> > +#define SKL_PORT_HDCP_RPRIME(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x18)
> > +#define SKL_PORT_HDCP_STATUS(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x1C)
> > +#define  SKL_HDCP_STATUS_STREAM_A_ENC	BIT(31)
> > +#define  SKL_HDCP_STATUS_STREAM_B_ENC	BIT(30)
> > +#define  SKL_HDCP_STATUS_STREAM_C_ENC	BIT(29)
> > +#define  SKL_HDCP_STATUS_STREAM_D_ENC	BIT(28)
> > +#define  SKL_HDCP_STATUS_AUTH		BIT(21)
> > +#define  SKL_HDCP_STATUS_ENC		BIT(20)
> > +#define  SKL_HDCP_STATUS_RI_MATCH	BIT(19)
> > +#define  SKL_HDCP_STATUS_R0_READY	BIT(18)
> > +#define  SKL_HDCP_STATUS_AN_READY	BIT(17)
> > +#define  SKL_HDCP_STATUS_CIPHER		BIT(16)
> > +#define  SKL_HDCP_STATUS_FRAME_CNT(x)	((x >> 8) & 0xff)
> > +
> >   /* Per-pipe DDI Function Control */
> >   #define _TRANS_DDI_FUNC_CTL_A		0x60400
> >   #define _TRANS_DDI_FUNC_CTL_B		0x61400
> > diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
> > index 36d4e635e4ce..ddf08227d9cb 100644
> > --- a/drivers/gpu/drm/i915/intel_atomic.c
> > +++ b/drivers/gpu/drm/i915/intel_atomic.c
> > @@ -109,12 +109,34 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
> >   	struct intel_digital_connector_state *old_conn_state =
> >   		to_intel_digital_connector_state(old_state);
> >   	struct drm_crtc_state *crtc_state;
> > -
> > -	if (!new_state->crtc)
> > +	uint64_t old_cp = old_conn_state->base.content_protection;
> > +	uint64_t new_cp = new_state->content_protection;
> > +
> > +	if (!new_state->crtc) {
> > +		/*
> > +		 * If the connector is being disabled with CP enabled, mark it
> > +		 * desired so it's re-enabled when the connector is brought back
> > +		 */
> > +		if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
> > +			new_state->content_protection =
> > +				DRM_MODE_CONTENT_PROTECTION_DESIRED;
> >   		return 0;
> > +	}
> >   	crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc);
> > +	if (new_cp != old_cp) {
> > +		/* Only drivers can set content protection enabled */
> > +		if (new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
> > +			new_state->content_protection =
> > +				DRM_MODE_CONTENT_PROTECTION_DESIRED;
> > +
> > +		/* Involve the encoder/connector to enable/disable CP */
> > +		if (new_cp == DRM_MODE_CONTENT_PROTECTION_OFF ||
> > +		    old_cp == DRM_MODE_CONTENT_PROTECTION_OFF)
> > +			crtc_state->mode_changed = true;
> 
> We need not perform the mode set for hdcp enable/disable.
> Authentication and encryption can be started on active port.

Was simpler to implement this way :-) We can fix this by pushing the hdcp
enable/disable code into a post-modeset operation. Ville had written the
infrastructure for that to fix a few fastboot corner cases. But that
infrastructure hasn't landed yet, so probably better to do that in a
follow-up.

I also guess that CrOS simply set this to desired every time they enable
an external screen, so it won't result in an unecessary modeset.

> > +	}
> > +
> >   	/*
> >   	 * These properties are handled by fastset, and might not end
> >   	 * up in a modeset.
> > diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
> > index 933c18fd4258..0e69337f410d 100644
> > --- a/drivers/gpu/drm/i915/intel_ddi.c
> > +++ b/drivers/gpu/drm/i915/intel_ddi.c
> > @@ -2432,10 +2432,17 @@ static void intel_enable_ddi(struct intel_encoder *encoder,
> >   			     const struct intel_crtc_state *crtc_state,
> >   			     const struct drm_connector_state *conn_state)
> >   {
> > +	struct drm_connector *connector = conn_state->connector;
> > +	struct intel_connector *intel_connector = to_intel_connector(connector);
> > +
> >   	if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
> >   		intel_enable_ddi_hdmi(encoder, crtc_state, conn_state);
> >   	else
> >   		intel_enable_ddi_dp(encoder, crtc_state, conn_state);
> > +
> > +	if (conn_state->content_protection ==
> > +			DRM_MODE_CONTENT_PROTECTION_DESIRED)
> > +		intel_hdcp_enable(intel_connector);
> >   }
> >   static void intel_disable_ddi_dp(struct intel_encoder *encoder,
> > @@ -2468,10 +2475,17 @@ static void intel_disable_ddi(struct intel_encoder *encoder,
> >   			      const struct intel_crtc_state *old_crtc_state,
> >   			      const struct drm_connector_state *old_conn_state)
> >   {
> > +	struct drm_connector *connector = old_conn_state->connector;
> > +	struct intel_connector *intel_connector = to_intel_connector(connector);
> > +
> >   	if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
> >   		intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state);
> >   	else
> >   		intel_disable_ddi_dp(encoder, old_crtc_state, old_conn_state);
> > +
> > +	if (old_conn_state->content_protection !=
> > +			DRM_MODE_CONTENT_PROTECTION_OFF)
> > +		intel_hdcp_disable(intel_connector);
> We might want to disable the hdcp before disabling the DDI. Actually we
> could trigger hdcp disable at connector state change(due to hot-unplug)
> also.

Yeah this part needs to be reworked a bit, also because the locking
doesn't work yet. I think hot-unplug is handled by the worker thread
already, it does the mandatory regular polling.
-Daniel

> 
> Thanks,
> --Ram
> >   }
> >   static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder,
> > diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> > index 47d022d48718..8924004575b8 100644
> > --- a/drivers/gpu/drm/i915/intel_drv.h
> > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > @@ -299,6 +299,49 @@ struct intel_panel {
> >   	} backlight;
> >   };
> > +struct intel_hdcp_shim {
> > +	/* Outputs the transmitter's An and Aksv values to the receiver. */
> > +	int (*write_an_aksv)(struct intel_digital_port *intel_dig_port, u8 *an);
> > +
> > +	/* Reads the receiver's key selection vector */
> > +	int (*read_bksv)(struct intel_digital_port *intel_dig_port, u8 *bksv);
> > +
> > +	/*
> > +	 * Reads BINFO from DP receivers and BSTATUS from HDMI receivers. The
> > +	 * definitions are the same in the respective specs, but the names are
> > +	 * different. Call it BSTATUS since that's the name the HDMI spec
> > +	 * uses and it was there first.
> > +	 */
> > +	int (*read_bstatus)(struct intel_digital_port *intel_dig_port,
> > +			    u8 *bstatus);
> > +
> > +	/* Determines whether a repeater is present downstream */
> > +	int (*repeater_present)(struct intel_digital_port *intel_dig_port,
> > +				bool *repeater_present);
> > +
> > +	/* Reads the receiver's Ri' value */
> > +	int (*read_ri_prime)(struct intel_digital_port *intel_dig_port, u8 *ri);
> > +
> > +	/* Determines if the receiver's KSV FIFO is ready for consumption */
> > +	int (*read_ksv_ready)(struct intel_digital_port *intel_dig_port,
> > +			      bool *ksv_ready);
> > +
> > +	/* Reads the ksv fifo for num_downstream devices */
> > +	int (*read_ksv_fifo)(struct intel_digital_port *intel_dig_port,
> > +			     int num_downstream, u8 *ksv_fifo);
> > +
> > +	/* Reads a 32-bit part of V' from the receiver */
> > +	int (*read_v_prime_part)(struct intel_digital_port *intel_dig_port,
> > +				 int i, u32 *part);
> > +
> > +	/* Enables HDCP signalling on the port */
> > +	int (*toggle_signalling)(struct intel_digital_port *intel_dig_port,
> > +				 bool enable);
> > +
> > +	/* Ensures the link is still protected */
> > +	bool (*check_link)(struct intel_digital_port *intel_dig_port);
> > +};
> > +
> >   struct intel_connector {
> >   	struct drm_connector base;
> >   	/*
> > @@ -330,6 +373,9 @@ struct intel_connector {
> >   	/* Work struct to schedule a uevent on link train failure */
> >   	struct work_struct modeset_retry_work;
> > +
> > +	const struct intel_hdcp_shim *hdcp_shim;
> > +	struct delayed_work hdcp_work;
> >   };
> >   struct intel_digital_connector_state {
> > @@ -1295,6 +1341,8 @@ void intel_ddi_set_vc_payload_alloc(const struct intel_crtc_state *crtc_state,
> >   				    bool state);
> >   u32 bxt_signal_levels(struct intel_dp *intel_dp);
> >   uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
> > +int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder);
> > +int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder);
> >   u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
> >   unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
> > @@ -1746,6 +1794,11 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
> >   }
> >   #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
> > +/* intel_hdcp.c */
> > +int intel_hdcp_enable(struct intel_connector *connector);
> > +int intel_hdcp_disable(struct intel_connector *connector);
> > +int intel_hdcp_check_link(struct intel_connector *connector);
> > +void intel_hdcp_work(struct work_struct *work);
> >   /* intel_psr.c */
> >   void intel_psr_enable(struct intel_dp *intel_dp,
> > diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
> > new file mode 100644
> > index 000000000000..a2a575ed657e
> > --- /dev/null
> > +++ b/drivers/gpu/drm/i915/intel_hdcp.c
> > @@ -0,0 +1,636 @@
> > +/*
> > + * Copyright (C) 2017 Google, Inc.
> > + *
> > + * This software is licensed under the terms of the GNU General Public
> > + * License version 2, as published by the Free Software Foundation, and
> > + * may be copied, distributed, and modified under those terms.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <drm/drmP.h>
> > +#include <drm/drm_hdcp.h>
> > +#include <linux/i2c.h>
> > +#include <linux/random.h>
> > +
> > +#include "intel_drv.h"
> > +#include "i915_reg.h"
> > +
> > +#define KEY_LOAD_TRIES	5
> > +
> > +static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
> > +				    const struct intel_hdcp_shim *shim)
> > +{
> > +	unsigned long timeout = jiffies + msecs_to_jiffies_timeout(500);
> > +	int ret;
> > +	bool ksv_ready;
> > +
> > +	while (true) {
> > +		ret = shim->read_ksv_ready(intel_dig_port, &ksv_ready);
> > +		if (ret)
> > +			return ret;
> > +		if (ksv_ready)
> > +			break;
> > +		if (time_after(jiffies, timeout))
> > +			return -ETIMEDOUT;
> > +		msleep(100);
> > +	}
> > +	return 0;
> > +}
> > +
> > +static void intel_hdcp_clear_keys(struct drm_i915_private *dev_priv)
> > +{
> > +	I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_CLEAR_KEYS_TRIGGER);
> > +	I915_WRITE(SKL_HDCP_KEY_STATUS,
> > +		   SKL_HDCP_KEY_LOAD_DONE | SKL_HDCP_KEY_LOAD_STATUS |
> > +		   SKL_HDCP_FUSE_IN_PROGRESS | SKL_HDCP_FUSE_ERROR |
> > +		   SKL_HDCP_FUSE_DONE);
> > +}
> > +
> > +static int intel_hdcp_load_keys(struct drm_i915_private *dev_priv)
> > +{
> > +	unsigned long timeout;
> > +	int ret;
> > +	u32 val;
> > +
> > +	// Initiate loading the HDCP key from fuses
> > +	mutex_lock(&dev_priv->pcu_lock);
> > +	ret = sandybridge_pcode_write(dev_priv, SKL_PCODE_LOAD_HDCP_KEYS, 1);
> > +	mutex_unlock(&dev_priv->pcu_lock);
> > +	if (ret) {
> > +		DRM_ERROR("Failed to initiate HDCP key load (%d)\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	// Wait for the keys to load (500us)
> > +	timeout = jiffies + nsecs_to_jiffies_timeout(500 * 1000);
> > +	while (true) {
> > +		val = I915_READ(SKL_HDCP_KEY_STATUS);
> > +		if (val & SKL_HDCP_KEY_LOAD_DONE)
> > +			break;
> > +		if (time_after(jiffies, timeout))
> > +			return -ETIMEDOUT;
> > +		usleep_range(50, 100);
> > +	}
> > +	if (!(val & SKL_HDCP_KEY_LOAD_STATUS))
> > +		return -ENXIO;
> > +
> > +	// Send Aksv over to PCH display for use in authentication
> > +	I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_AKSV_SEND_TRIGGER);
> > +
> > +	return 0;
> > +}
> > +
> > +/* Returns updated SHA-1 index */
> > +static int intel_write_sha_text(struct drm_i915_private *dev_priv, u32 sha_text)
> > +{
> > +	I915_WRITE(SKL_HDCP_SHA_TEXT, sha_text);
> > +	if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_READY, 1)) {
> > +		DRM_ERROR("Timed out waiting for SHA1 ready\n");
> > +		return -ETIMEDOUT;
> > +	}
> > +	return 0;
> > +}
> > +
> > +static
> > +u32 intel_hdcp_get_repeater_ctl(struct intel_digital_port *intel_dig_port)
> > +{
> > +	enum port port = intel_dig_port->port;
> > +	switch(port) {
> > +	case PORT_A:
> > +		return SKL_HDCP_DDIA_REP_PRESENT | SKL_HDCP_DDIA_SHA1_M0;
> > +	case PORT_B:
> > +		return SKL_HDCP_DDIB_REP_PRESENT | SKL_HDCP_DDIB_SHA1_M0;
> > +	case PORT_C:
> > +		return SKL_HDCP_DDIC_REP_PRESENT | SKL_HDCP_DDIC_SHA1_M0;
> > +	case PORT_D:
> > +		return SKL_HDCP_DDID_REP_PRESENT | SKL_HDCP_DDID_SHA1_M0;
> > +	case PORT_E:
> > +		return SKL_HDCP_DDIE_REP_PRESENT | SKL_HDCP_DDIE_SHA1_M0;
> > +	default:
> > +		break;
> > +	}
> > +	DRM_ERROR("Unknown port %d\n", port);
> > +	return -EINVAL;
> > +}
> > +
> > +/* Implements Part 2 of the HDCP authorization procedure */
> > +static
> > +int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
> > +			       const struct intel_hdcp_shim *shim)
> > +{
> > +	struct drm_i915_private *dev_priv;
> > +	u32 vprime, sha_text, sha_leftovers, rep_ctl;
> > +	u8 bstatus[2], num_downstream, *ksv_fifo;
> > +	int ret, i, j, sha_idx;
> > +
> > +	dev_priv = intel_dig_port->base.base.dev->dev_private;
> > +
> > +	ret = shim->read_bstatus(intel_dig_port, bstatus);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/* If there are no downstream devices, we're all done. */
> > +	num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
> > +	if (num_downstream == 0) {
> > +		DRM_INFO("HDCP is enabled (no downstream devices)\n");
> > +		return 0;
> > +	}
> > +
> > +	// Poll for ksv list ready (spec says max time allowed is 5s)
> > +	ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim);
> > +	if (ret) {
> > +		DRM_ERROR("KSV list failed to become ready (%d)\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL);
> > +	if (!ksv_fifo)
> > +		return -ENOMEM;
> > +
> > +	ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo);
> > +	if (ret)
> > +		return ret;
> > +
> > +	// Process V' values from the receiver
> > +	for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
> > +		ret = shim->read_v_prime_part(intel_dig_port, i, &vprime);
> > +		if (ret)
> > +			return ret;
> > +		I915_WRITE(SKL_HDCP_SHA_V_PRIME(i), vprime);
> > +	}
> > +
> > +	/*
> > +	 * We need to write the concatenation of all device KSVs, BINFO (DP) ||
> > +	 * BSTATUS (HDMI), and M0 (which is added via HDCP_REP_CTL). This byte
> > +	 * stream is written via the HDCP_SHA_TEXT register in 32-bit
> > +	 * increments. Every 64 bytes, we need to write HDCP_REP_CTL again. This
> > +	 * index will keep track of our progress through the 64 bytes as well as
> > +	 * helping us work the 40-bit KSVs through our 32-bit register.
> > +	 *
> > +	 * NOTE: data passed via HDCP_SHA_TEXT should be big-endian
> > +	 */
> > +	sha_idx = 0;
> > +	sha_text = 0;
> > +	sha_leftovers = 0;
> > +	rep_ctl = intel_hdcp_get_repeater_ctl(intel_dig_port);
> > +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> > +	for (i = 0; i < num_downstream; i++) {
> > +		unsigned sha_empty;
> > +		u8 *ksv = &ksv_fifo[i * DRM_HDCP_KSV_LEN];
> > +
> > +		// Fill up the empty slots in sha_text and write it out
> > +		sha_empty = sizeof(sha_text) - sha_leftovers;
> > +		for (j = 0; j < sha_empty; j++)
> > +			sha_text |= ksv[j] << ((sizeof(sha_text) - j - 1) * 8);
> > +
> > +		ret = intel_write_sha_text(dev_priv, sha_text);
> > +		if (ret < 0)
> > +			return ret;
> > +
> > +		// Programming guide writes this every 64 bytes
> > +		sha_idx += sizeof(sha_text);
> > +		if (!(sha_idx % 64))
> > +			I915_WRITE(SKL_HDCP_REP_CTL,
> > +				   rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> > +
> > +		// Store the leftover bytes from the ksv in sha_text
> > +		sha_leftovers = DRM_HDCP_KSV_LEN - sha_empty;
> > +		sha_text = 0;
> > +		for (j = 0; j < sha_leftovers; j++)
> > +			sha_text |= ksv[sha_empty + j] <<
> > +					((sizeof(sha_text) - j - 1) * 8);
> > +
> > +		/*
> > +		 * If we still have room in sha_text for more data, continue.
> > +		 * Otherwise, write it out immediately.
> > +		 */
> > +		if (sizeof(sha_text) > sha_leftovers)
> > +			continue;
> > +
> > +		ret = intel_write_sha_text(dev_priv, sha_text);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_leftovers = 0;
> > +		sha_text = 0;
> > +		sha_idx += sizeof(sha_text);
> > +	}
> > +
> > +	/*
> > +	 * We need to write BINFO/BSTATUS, and M0 now. Depending on how many
> > +	 * bytes are leftover from the last ksv, we might be able to fit them
> > +	 * all in sha_text (first 2 cases), or we might need to split them up
> > +	 * into 2 writes (last 2 cases).
> > +	 */
> > +	if (sha_leftovers == 0) {
> > +		// Write 16 bits of text, 16 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
> > +		ret = intel_write_sha_text(dev_priv,
> > +					   bstatus[0] << 8 | bstatus[1]);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +		// Write 32 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
> > +		ret = intel_write_sha_text(dev_priv, 0);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +		// Write 16 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
> > +		ret = intel_write_sha_text(dev_priv, 0);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +	} else if (sha_leftovers == 1) {
> > +		// Write 24 bits of text, 8 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
> > +		sha_text |= bstatus[0] << 16 | bstatus[1] << 8;
> > +		// Only 24-bits of data, must be in the LSB
> > +		sha_text = (sha_text & 0xffffff00) >> 8;
> > +		ret = intel_write_sha_text(dev_priv, sha_text);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +		// Write 32 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
> > +		ret = intel_write_sha_text(dev_priv, 0);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +		// Write 24 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
> > +		ret = intel_write_sha_text(dev_priv, 0);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +	} else if (sha_leftovers == 2) {
> > +		// Write 32 bits of text
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> > +		sha_text |= bstatus[0] << 24 | bstatus[1] << 16;
> > +		ret = intel_write_sha_text(dev_priv, sha_text);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +		// Write 64 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
> > +		for (i = 0; i < 2; i++) {
> > +			ret = intel_write_sha_text(dev_priv, 0);
> > +			if (ret < 0)
> > +				return ret;
> > +			sha_idx += sizeof(sha_text);
> > +		}
> > +	} else if (sha_leftovers == 3) {
> > +		// Write 32 bits of text
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> > +		sha_text |= bstatus[0] << 24;
> > +		ret = intel_write_sha_text(dev_priv, sha_text);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +		// Write 8 bits of text, 24 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
> > +		ret = intel_write_sha_text(dev_priv, bstatus[1]);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +		// Write 32 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
> > +		ret = intel_write_sha_text(dev_priv, 0);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +		// Write 8 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
> > +		ret = intel_write_sha_text(dev_priv, 0);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +	} else {
> > +		DRM_ERROR("Invalid number of leftovers %d\n", sha_leftovers);
> > +		return -EINVAL;
> > +	}
> > +
> > +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> > +	// Fill up to 64 - 4 bytes with zeros (leave the last write for length)
> > +	while ((sha_idx % 64) < (64 - sizeof(sha_text))) {
> > +		ret = intel_write_sha_text(dev_priv, 0);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +	}
> > +
> > +	/*
> > +	 * Last write gets the length of the concatenation in bits. That is:
> > +	 *  - 5 bytes per device
> > +	 *  - 10 bytes for BINFO/BSTATUS(2), M0(8)
> > +	 */
> > +	sha_text = (num_downstream * 5 + 10) * 8;
> > +	ret = intel_write_sha_text(dev_priv, sha_text);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	// Finally, tell the HW we're done with the hash and wait for it to ACK
> > +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_COMPLETE_HASH);
> > +	if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_COMPLETE, 1)) {
> > +		DRM_ERROR("Timed out waiting for SHA1 complete\n");
> > +		return -ETIMEDOUT;
> > +	}
> > +	if (!(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_V_MATCH)) {
> > +		DRM_ERROR("SHA-1 mismatch, HDCP failed\n");
> > +		return -ENXIO;
> > +	}
> > +
> > +	DRM_INFO("HDCP is enabled (%d downstream devices)\n", num_downstream);
> > +	return 0;
> > +}
> > +
> > +/* Implements Part 1 of the HDCP authorization procedure */
> > +static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
> > +			   const struct intel_hdcp_shim *shim)
> > +{
> > +	struct drm_i915_private *dev_priv;
> > +	enum port port;
> > +	unsigned long r0_prime_gen_start;
> > +	int ret, i;
> > +	union {
> > +		u32 reg[2];
> > +		u8 shim[DRM_HDCP_AN_LEN];
> > +	} an;
> > +	union {
> > +		u32 reg[2];
> > +		u8 shim[DRM_HDCP_KSV_LEN];
> > +	} bksv;
> > +	union {
> > +		u32 reg;
> > +		u8 shim[DRM_HDCP_RI_LEN];
> > +	} ri;
> > +	bool repeater_present;
> > +
> > +	dev_priv = intel_dig_port->base.base.dev->dev_private;
> > +
> > +	port = intel_dig_port->port;
> > +
> > +	// Initialize An with 2 random values and acquire it
> > +	for (i = 0; i < 2; i++)
> > +		I915_WRITE(SKL_PORT_HDCP_ANINIT(port), get_random_long());
> > +	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_CAPTURE_AN);
> > +
> > +	// Wait for An to be acquired
> > +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> > +		     SKL_HDCP_STATUS_AN_READY, 1)) {
> > +		DRM_ERROR("Timed out waiting for An\n");
> > +		return -ETIMEDOUT;
> > +	}
> > +
> > +	an.reg[0] = I915_READ(SKL_PORT_HDCP_ANLO(port));
> > +	an.reg[1] = I915_READ(SKL_PORT_HDCP_ANHI(port));
> > +	ret = shim->write_an_aksv(intel_dig_port, an.shim);
> > +	if (ret)
> > +		return ret;
> > +
> > +	r0_prime_gen_start = jiffies;
> > +
> > +	memset(&bksv, 0, sizeof(bksv));
> > +	ret = shim->read_bksv(intel_dig_port, bksv.shim);
> > +	if (ret)
> > +		return ret;
> > +
> > +	I915_WRITE(SKL_PORT_HDCP_BKSVLO(port), bksv.reg[0]);
> > +	I915_WRITE(SKL_PORT_HDCP_BKSVHI(port), bksv.reg[1]);
> > +
> > +	ret = shim->repeater_present(intel_dig_port, &repeater_present);
> > +	if (ret)
> > +		return ret;
> > +	if (repeater_present)
> > +		I915_WRITE(SKL_HDCP_REP_CTL,
> > +			   intel_hdcp_get_repeater_ctl(intel_dig_port));
> > +
> > +	ret = shim->toggle_signalling(intel_dig_port, true);
> > +	if (ret)
> > +		return ret;
> > +
> > +	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_AUTH_AND_ENC);
> > +
> > +	// Wait for R0 ready
> > +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> > +		     (SKL_HDCP_STATUS_R0_READY | SKL_HDCP_STATUS_ENC), 1)) {
> > +		DRM_ERROR("Timed out waiting for R0 ready\n");
> > +		return -ETIMEDOUT;
> > +	}
> > +
> > +	/*
> > +	 * Wait for R0' to become available, the spec says 100ms from Aksv
> > +	 * write. On DP, there's an R0_READY bit available but no such bit
> > +	 * exists on HDMI. Since the upper-bound is the same, we'll just do
> > +	 * the stupid thing instead of polling on one and not the other.
> > +	 */
> > +	wait_remaining_ms_from_jiffies(r0_prime_gen_start, 100);
> > +
> > +	ri.reg = 0;
> > +	ret = shim->read_ri_prime(intel_dig_port, ri.shim);
> > +	if (ret)
> > +		return ret;
> > +	I915_WRITE(SKL_PORT_HDCP_RPRIME(port), ri.reg);
> > +
> > +	// Wait for Ri prime match
> > +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> > +		     (SKL_HDCP_STATUS_RI_MATCH | SKL_HDCP_STATUS_ENC), 1)) {
> > +		DRM_ERROR("Timed out waiting for Ri prime match (%x)\n",
> > +			  I915_READ(SKL_PORT_HDCP_STATUS(port)));
> > +		return -ETIMEDOUT;
> > +	}
> > +
> > +	// Wait for encryption confirmation
> > +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> > +		      SKL_HDCP_STATUS_ENC, 20)) {
> > +		DRM_ERROR("Timed out waiting for encryption\n");
> > +		return -ETIMEDOUT;
> > +	}
> > +
> > +	/*
> > +	 * XXX: If we have MST-connected devices, we need to enable encryption
> > +	 * on those as well.
> > +	 */
> > +
> > +	return intel_hdcp_auth_downstream(intel_dig_port, shim);
> > +}
> > +
> > +static
> > +struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector)
> > +{
> > +	return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base);
> > +}
> > +
> > +static int _intel_hdcp_disable(struct intel_connector *connector)
> > +{
> > +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> > +	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
> > +	enum port port = intel_dig_port->port;
> > +	int ret;
> > +
> > +	I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
> > +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) == 0, 20)) {
> > +		DRM_ERROR("Failed to disable HDCP, timeout clearing status\n");
> > +		return -ETIMEDOUT;
> > +	}
> > +
> > +	intel_hdcp_clear_keys(dev_priv);
> > +
> > +	ret = connector->hdcp_shim->toggle_signalling(intel_dig_port, false);
> > +	if (ret) {
> > +		DRM_ERROR("Failed to disable HDCP signalling\n");
> > +		return ret;
> > +	}
> > +
> > +	DRM_INFO("HDCP is disabled\n");
> > +	return 0;
> > +}
> > +
> > +static int _intel_hdcp_enable(struct intel_connector *connector)
> > +{
> > +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> > +	int i, ret;
> > +
> > +	if (!(I915_READ(SKL_FUSE_STATUS) & SKL_FUSE_PG_DIST_STATUS(1))) {
> > +		DRM_ERROR("PG1 is disabled, cannot load keys\n");
> > +		return -ENXIO;
> > +	}
> > +
> > +	for (i = 0; i < KEY_LOAD_TRIES; i++) {
> > +		ret = intel_hdcp_load_keys(dev_priv);
> > +		if (!ret)
> > +			break;
> > +		intel_hdcp_clear_keys(dev_priv);
> > +	}
> > +	if (ret) {
> > +		DRM_ERROR("Could not load HDCP keys, (%d)\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = intel_hdcp_auth(conn_to_dig_port(connector),
> > +			      connector->hdcp_shim);
> > +	if (ret) {
> > +		DRM_ERROR("Failed to authenticate HDCP (%d)\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +void intel_hdcp_work(struct work_struct *work)
> > +{
> > +	struct intel_connector *connector = container_of(to_delayed_work(work),
> > +							 struct intel_connector,
> > +						         hdcp_work);
> > +	struct drm_device *dev = connector->base.dev;
> > +	int ret;
> > +
> > +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> > +
> > +	ret = intel_hdcp_check_link(connector);
> > +	if (!ret)
> > +		schedule_delayed_work(&connector->hdcp_work,
> > +				      DRM_HDCP_CHECK_PERIOD_MS);
> > +
> > +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
> > +}
> > +
> > +int intel_hdcp_enable(struct intel_connector *connector)
> > +{
> > +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> > +	struct drm_device *dev = &dev_priv->drm;
> > +	struct drm_connector_state *state = connector->base.state;
> > +	int ret;
> > +
> > +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
> > +
> > +	if (!connector->hdcp_shim)
> > +		return -ENOENT;
> > +
> > +	ret = _intel_hdcp_enable(connector);
> > +	if (ret)
> > +		return ret;
> > +
> > +	state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
> > +
> > +	schedule_delayed_work(&connector->hdcp_work, DRM_HDCP_CHECK_PERIOD_MS);
> > +	return 0;
> > +}
> > +
> > +int intel_hdcp_disable(struct intel_connector *connector)
> > +{
> > +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> > +	struct drm_device *dev = &dev_priv->drm;
> > +
> > +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
> > +
> > +	if (!connector->hdcp_shim)
> > +		return -ENOENT;
> > +
> > +	cancel_delayed_work(&connector->hdcp_work);
> > +
> > +	return _intel_hdcp_disable(connector);
> > +}
> > +
> > +/* Implements Part 3 of the HDCP authorization procedure */
> > +int intel_hdcp_check_link(struct intel_connector *connector)
> > +{
> > +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> > +	struct drm_device *dev = &dev_priv->drm;
> > +	struct drm_connector_state *state = connector->base.state;
> > +	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
> > +	enum port port = intel_dig_port->port;
> > +	int ret;
> > +
> > +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
> > +
> > +	if (state->content_protection != DRM_MODE_CONTENT_PROTECTION_ENABLED)
> > +		return 0;
> > +
> > +	if (!connector->hdcp_shim)
> > +		return -ENOENT;
> > +
> > +	if (!(I915_READ(SKL_PORT_HDCP_STATUS(port)) & SKL_HDCP_STATUS_ENC)) {
> > +		DRM_ERROR("HDCP check failed: link is not encrypted, %x\n",
> > +			   I915_READ(SKL_PORT_HDCP_STATUS(port)));
> > +		ret = -ENXIO;
> > +		goto fail;
> > +	}
> > +
> > +	if (connector->hdcp_shim->check_link(intel_dig_port))
> > +		return 0;
> > +
> > +	DRM_INFO("HDCP link failed, retrying authentication\n");
> > +
> > +	ret = _intel_hdcp_disable(connector);
> > +	if (ret) {
> > +		DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
> > +		goto fail;
> > +	}
> > +
> > +	ret = _intel_hdcp_enable(connector);
> > +	if (ret) {
> > +		DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
> > +		goto fail;
> > +	}
> > +
> > +	return 0;
> > +
> > +fail:
> > +	state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
> > +	return ret;
> > +}
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 3/6] drm/i915: Add HDCP framework + base implementation
@ 2017-12-01  7:36       ` Daniel Vetter
  0 siblings, 0 replies; 62+ messages in thread
From: Daniel Vetter @ 2017-12-01  7:36 UTC (permalink / raw)
  To: Ramalingam C
  Cc: David Airlie, intel-gfx, linux-kernel, dri-devel, Rodrigo Vivi

On Fri, Dec 01, 2017 at 12:53:31PM +0530, Ramalingam C wrote:
> Sean,
> 
> IMHO, it will good if we can have all generic hdcp1.4 authentication flow in
> drm helpers and all interested display drivers to use them.
> 
> This Design will make the extending of hdcp easy for other display drivers
> based on DRM.
> 
> We can have the required drm_hdcp_shim type of implementation at drm
> structure which will be called for platform specific operations (like
> prepare an, send aksv, program bksv/repeater/r0 and verify sha1 etc)?

I discussed this exact question with Sean Paul, and apparently the
hardware designs are too diverse to make shared code much useful. Some hw
has the entire hdcp flow in hw, some almost nothing (like i915 here), and
then there's everything in between.

Given that Sean has seen a lot more hdcp implementations than we have,
that we right now have no other implementation than i915 in upstream and
than wrong abstraction is much harder to fix than no abstraction I'm going
with Sean's approach of "no generic abstraction" here. Personally I'm not
even fully sold on the shim abstraction, but I think by&large that one is
fine.

> On Thursday 30 November 2017 08:38 AM, Sean Paul wrote:
> > This patch adds the framework required to add HDCP support to intel
> > connectors. It implements Aksv loading from fuse, and parts 1/2/3
> > of the HDCP authentication scheme.
> > 
> > Note that without shim implementations, this does not actually implement
> > HDCP. That will come in subsequent patches.
> > 
> > Signed-off-by: Sean Paul <seanpaul@chromium.org>
> > ---
> >   drivers/gpu/drm/i915/Makefile       |   1 +
> >   drivers/gpu/drm/i915/i915_reg.h     |  83 +++++
> >   drivers/gpu/drm/i915/intel_atomic.c |  26 +-
> >   drivers/gpu/drm/i915/intel_ddi.c    |  14 +
> >   drivers/gpu/drm/i915/intel_drv.h    |  53 +++
> >   drivers/gpu/drm/i915/intel_hdcp.c   | 636 ++++++++++++++++++++++++++++++++++++
> >   6 files changed, 811 insertions(+), 2 deletions(-)
> >   create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c
> > 
> > diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> > index 6c3b0481ef82..1e745508e437 100644
> > --- a/drivers/gpu/drm/i915/Makefile
> > +++ b/drivers/gpu/drm/i915/Makefile
> > @@ -87,6 +87,7 @@ i915-y += intel_audio.o \
> >   	  intel_fbc.o \
> >   	  intel_fifo_underrun.o \
> >   	  intel_frontbuffer.o \
> > +	  intel_hdcp.o \
> >   	  intel_hotplug.o \
> >   	  intel_modes.o \
> >   	  intel_overlay.o \
> > diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> > index 68a58cce6ab1..43128030171d 100644
> > --- a/drivers/gpu/drm/i915/i915_reg.h
> > +++ b/drivers/gpu/drm/i915/i915_reg.h
> > @@ -7991,6 +7991,7 @@ enum {
> >   #define     GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT	8
> >   #define     GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT	16
> >   #define     GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT	24
> > +#define   SKL_PCODE_LOAD_HDCP_KEYS		0x5
> >   #define   SKL_PCODE_CDCLK_CONTROL		0x7
> >   #define     SKL_CDCLK_PREPARE_FOR_CHANGE	0x3
> >   #define     SKL_CDCLK_READY_FOR_CHANGE		0x1
> > @@ -8285,6 +8286,88 @@ enum skl_power_gate {
> >   #define  SKL_PW_TO_PG(pw)			((pw) - SKL_DISP_PW_1 + SKL_PG1)
> >   #define  SKL_FUSE_PG_DIST_STATUS(pg)		(1 << (27 - (pg)))
> > +
> > +/* HDCP Key Registers */
> > +#define SKL_HDCP_KEY_CONF		_MMIO(0x66c00)
> > +#define	 SKL_HDCP_AKSV_SEND_TRIGGER	BIT(31)
> > +#define  SKL_HDCP_CLEAR_KEYS_TRIGGER	BIT(30)
> > +#define SKL_HDCP_KEY_STATUS		_MMIO(0x66c04)
> > +#define  SKL_HDCP_FUSE_IN_PROGRESS	BIT(7)
> > +#define  SKL_HDCP_FUSE_ERROR		BIT(6)
> > +#define  SKL_HDCP_FUSE_DONE		BIT(5)
> > +#define  SKL_HDCP_KEY_LOAD_STATUS	BIT(1)
> > +#define  SKL_HDCP_KEY_LOAD_DONE		BIT(0)
> > +#define SKL_HDCP_AKSV_LO		_MMIO(0x66c10)
> > +#define SKL_HDCP_AKSV_HI		_MMIO(0x66c14)
> > +
> > +/* HDCP Repeater Registers */
> > +#define SKL_HDCP_REP_CTL		_MMIO(0x66d00)
> > +#define  SKL_HDCP_DDIB_REP_PRESENT	BIT(30)
> > +#define  SKL_HDCP_DDIA_REP_PRESENT	BIT(29)
> > +#define  SKL_HDCP_DDIC_REP_PRESENT	BIT(28)
> > +#define  SKL_HDCP_DDID_REP_PRESENT	BIT(27)
> > +#define  SKL_HDCP_DDIF_REP_PRESENT	BIT(26)
> > +#define  SKL_HDCP_DDIE_REP_PRESENT	BIT(25)
> > +#define  SKL_HDCP_DDIB_SHA1_M0		(1 << 20)
> > +#define  SKL_HDCP_DDIA_SHA1_M0		(2 << 20)
> > +#define  SKL_HDCP_DDIC_SHA1_M0		(3 << 20)
> > +#define  SKL_HDCP_DDID_SHA1_M0		(4 << 20)
> > +#define  SKL_HDCP_DDIF_SHA1_M0		(5 << 20)
> > +#define  SKL_HDCP_DDIE_SHA1_M0		(6 << 20) // Bspec says 5?
> > +#define  SKL_HDCP_SHA1_BUSY		BIT(16)
> > +#define  SKL_HDCP_SHA1_READY		BIT(17)
> > +#define  SKL_HDCP_SHA1_COMPLETE		BIT(18)
> > +#define  SKL_HDCP_SHA1_V_MATCH		BIT(19)
> > +#define  SKL_HDCP_SHA1_TEXT_32		(1 << 1)
> > +#define  SKL_HDCP_SHA1_COMPLETE_HASH	(2 << 1)
> > +#define  SKL_HDCP_SHA1_TEXT_24		(4 << 1)
> > +#define  SKL_HDCP_SHA1_TEXT_16		(5 << 1)
> > +#define  SKL_HDCP_SHA1_TEXT_8		(6 << 1)
> > +#define  SKL_HDCP_SHA1_TEXT_0		(7 << 1)
> > +#define SKL_HDCP_SHA_V_PRIME_H0		_MMIO(0x66d04)
> > +#define SKL_HDCP_SHA_V_PRIME_H1		_MMIO(0x66d08)
> > +#define SKL_HDCP_SHA_V_PRIME_H2		_MMIO(0x66d0C)
> > +#define SKL_HDCP_SHA_V_PRIME_H3		_MMIO(0x66d10)
> > +#define SKL_HDCP_SHA_V_PRIME_H4		_MMIO(0x66d14)
> > +#define SKL_HDCP_SHA_V_PRIME(h)		_MMIO((0x66d04 + h * 4))
> > +#define SKL_HDCP_SHA_TEXT		_MMIO(0x66d18)
> > +
> > +/* HDCP Auth Registers */
> > +#define _SKL_PORTA_HDCP_AUTHENC		0x66800
> > +#define _SKL_PORTB_HDCP_AUTHENC		0x66500
> > +#define _SKL_PORTC_HDCP_AUTHENC		0x66600
> > +#define _SKL_PORTD_HDCP_AUTHENC		0x66700
> > +#define _SKL_PORTE_HDCP_AUTHENC		0x66A00
> > +#define _SKL_PORTF_HDCP_AUTHENC		0x66900
> > +#define _SKL_PORT_HDCP_AUTHENC(port, x)	_MMIO(_PICK(port, \
> > +					  _SKL_PORTA_HDCP_AUTHENC, \
> > +					  _SKL_PORTB_HDCP_AUTHENC, \
> > +					  _SKL_PORTC_HDCP_AUTHENC, \
> > +					  _SKL_PORTD_HDCP_AUTHENC, \
> > +					  _SKL_PORTE_HDCP_AUTHENC, \
> > +					  _SKL_PORTF_HDCP_AUTHENC) + x)
> > +#define SKL_PORT_HDCP_CONF(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x0)
> > +#define  SKL_HDCP_CONF_CAPTURE_AN	BIT(0)
> > +#define  SKL_HDCP_CONF_AUTH_AND_ENC	(BIT(1) | BIT(0))
> > +#define SKL_PORT_HDCP_ANINIT(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x4)
> > +#define SKL_PORT_HDCP_ANLO(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x8)
> > +#define SKL_PORT_HDCP_ANHI(port)	_SKL_PORT_HDCP_AUTHENC(port, 0xC)
> > +#define SKL_PORT_HDCP_BKSVLO(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x10)
> > +#define SKL_PORT_HDCP_BKSVHI(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x14)
> > +#define SKL_PORT_HDCP_RPRIME(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x18)
> > +#define SKL_PORT_HDCP_STATUS(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x1C)
> > +#define  SKL_HDCP_STATUS_STREAM_A_ENC	BIT(31)
> > +#define  SKL_HDCP_STATUS_STREAM_B_ENC	BIT(30)
> > +#define  SKL_HDCP_STATUS_STREAM_C_ENC	BIT(29)
> > +#define  SKL_HDCP_STATUS_STREAM_D_ENC	BIT(28)
> > +#define  SKL_HDCP_STATUS_AUTH		BIT(21)
> > +#define  SKL_HDCP_STATUS_ENC		BIT(20)
> > +#define  SKL_HDCP_STATUS_RI_MATCH	BIT(19)
> > +#define  SKL_HDCP_STATUS_R0_READY	BIT(18)
> > +#define  SKL_HDCP_STATUS_AN_READY	BIT(17)
> > +#define  SKL_HDCP_STATUS_CIPHER		BIT(16)
> > +#define  SKL_HDCP_STATUS_FRAME_CNT(x)	((x >> 8) & 0xff)
> > +
> >   /* Per-pipe DDI Function Control */
> >   #define _TRANS_DDI_FUNC_CTL_A		0x60400
> >   #define _TRANS_DDI_FUNC_CTL_B		0x61400
> > diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
> > index 36d4e635e4ce..ddf08227d9cb 100644
> > --- a/drivers/gpu/drm/i915/intel_atomic.c
> > +++ b/drivers/gpu/drm/i915/intel_atomic.c
> > @@ -109,12 +109,34 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
> >   	struct intel_digital_connector_state *old_conn_state =
> >   		to_intel_digital_connector_state(old_state);
> >   	struct drm_crtc_state *crtc_state;
> > -
> > -	if (!new_state->crtc)
> > +	uint64_t old_cp = old_conn_state->base.content_protection;
> > +	uint64_t new_cp = new_state->content_protection;
> > +
> > +	if (!new_state->crtc) {
> > +		/*
> > +		 * If the connector is being disabled with CP enabled, mark it
> > +		 * desired so it's re-enabled when the connector is brought back
> > +		 */
> > +		if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
> > +			new_state->content_protection =
> > +				DRM_MODE_CONTENT_PROTECTION_DESIRED;
> >   		return 0;
> > +	}
> >   	crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc);
> > +	if (new_cp != old_cp) {
> > +		/* Only drivers can set content protection enabled */
> > +		if (new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
> > +			new_state->content_protection =
> > +				DRM_MODE_CONTENT_PROTECTION_DESIRED;
> > +
> > +		/* Involve the encoder/connector to enable/disable CP */
> > +		if (new_cp == DRM_MODE_CONTENT_PROTECTION_OFF ||
> > +		    old_cp == DRM_MODE_CONTENT_PROTECTION_OFF)
> > +			crtc_state->mode_changed = true;
> 
> We need not perform the mode set for hdcp enable/disable.
> Authentication and encryption can be started on active port.

Was simpler to implement this way :-) We can fix this by pushing the hdcp
enable/disable code into a post-modeset operation. Ville had written the
infrastructure for that to fix a few fastboot corner cases. But that
infrastructure hasn't landed yet, so probably better to do that in a
follow-up.

I also guess that CrOS simply set this to desired every time they enable
an external screen, so it won't result in an unecessary modeset.

> > +	}
> > +
> >   	/*
> >   	 * These properties are handled by fastset, and might not end
> >   	 * up in a modeset.
> > diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
> > index 933c18fd4258..0e69337f410d 100644
> > --- a/drivers/gpu/drm/i915/intel_ddi.c
> > +++ b/drivers/gpu/drm/i915/intel_ddi.c
> > @@ -2432,10 +2432,17 @@ static void intel_enable_ddi(struct intel_encoder *encoder,
> >   			     const struct intel_crtc_state *crtc_state,
> >   			     const struct drm_connector_state *conn_state)
> >   {
> > +	struct drm_connector *connector = conn_state->connector;
> > +	struct intel_connector *intel_connector = to_intel_connector(connector);
> > +
> >   	if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
> >   		intel_enable_ddi_hdmi(encoder, crtc_state, conn_state);
> >   	else
> >   		intel_enable_ddi_dp(encoder, crtc_state, conn_state);
> > +
> > +	if (conn_state->content_protection ==
> > +			DRM_MODE_CONTENT_PROTECTION_DESIRED)
> > +		intel_hdcp_enable(intel_connector);
> >   }
> >   static void intel_disable_ddi_dp(struct intel_encoder *encoder,
> > @@ -2468,10 +2475,17 @@ static void intel_disable_ddi(struct intel_encoder *encoder,
> >   			      const struct intel_crtc_state *old_crtc_state,
> >   			      const struct drm_connector_state *old_conn_state)
> >   {
> > +	struct drm_connector *connector = old_conn_state->connector;
> > +	struct intel_connector *intel_connector = to_intel_connector(connector);
> > +
> >   	if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
> >   		intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state);
> >   	else
> >   		intel_disable_ddi_dp(encoder, old_crtc_state, old_conn_state);
> > +
> > +	if (old_conn_state->content_protection !=
> > +			DRM_MODE_CONTENT_PROTECTION_OFF)
> > +		intel_hdcp_disable(intel_connector);
> We might want to disable the hdcp before disabling the DDI. Actually we
> could trigger hdcp disable at connector state change(due to hot-unplug)
> also.

Yeah this part needs to be reworked a bit, also because the locking
doesn't work yet. I think hot-unplug is handled by the worker thread
already, it does the mandatory regular polling.
-Daniel

> 
> Thanks,
> --Ram
> >   }
> >   static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder,
> > diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> > index 47d022d48718..8924004575b8 100644
> > --- a/drivers/gpu/drm/i915/intel_drv.h
> > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > @@ -299,6 +299,49 @@ struct intel_panel {
> >   	} backlight;
> >   };
> > +struct intel_hdcp_shim {
> > +	/* Outputs the transmitter's An and Aksv values to the receiver. */
> > +	int (*write_an_aksv)(struct intel_digital_port *intel_dig_port, u8 *an);
> > +
> > +	/* Reads the receiver's key selection vector */
> > +	int (*read_bksv)(struct intel_digital_port *intel_dig_port, u8 *bksv);
> > +
> > +	/*
> > +	 * Reads BINFO from DP receivers and BSTATUS from HDMI receivers. The
> > +	 * definitions are the same in the respective specs, but the names are
> > +	 * different. Call it BSTATUS since that's the name the HDMI spec
> > +	 * uses and it was there first.
> > +	 */
> > +	int (*read_bstatus)(struct intel_digital_port *intel_dig_port,
> > +			    u8 *bstatus);
> > +
> > +	/* Determines whether a repeater is present downstream */
> > +	int (*repeater_present)(struct intel_digital_port *intel_dig_port,
> > +				bool *repeater_present);
> > +
> > +	/* Reads the receiver's Ri' value */
> > +	int (*read_ri_prime)(struct intel_digital_port *intel_dig_port, u8 *ri);
> > +
> > +	/* Determines if the receiver's KSV FIFO is ready for consumption */
> > +	int (*read_ksv_ready)(struct intel_digital_port *intel_dig_port,
> > +			      bool *ksv_ready);
> > +
> > +	/* Reads the ksv fifo for num_downstream devices */
> > +	int (*read_ksv_fifo)(struct intel_digital_port *intel_dig_port,
> > +			     int num_downstream, u8 *ksv_fifo);
> > +
> > +	/* Reads a 32-bit part of V' from the receiver */
> > +	int (*read_v_prime_part)(struct intel_digital_port *intel_dig_port,
> > +				 int i, u32 *part);
> > +
> > +	/* Enables HDCP signalling on the port */
> > +	int (*toggle_signalling)(struct intel_digital_port *intel_dig_port,
> > +				 bool enable);
> > +
> > +	/* Ensures the link is still protected */
> > +	bool (*check_link)(struct intel_digital_port *intel_dig_port);
> > +};
> > +
> >   struct intel_connector {
> >   	struct drm_connector base;
> >   	/*
> > @@ -330,6 +373,9 @@ struct intel_connector {
> >   	/* Work struct to schedule a uevent on link train failure */
> >   	struct work_struct modeset_retry_work;
> > +
> > +	const struct intel_hdcp_shim *hdcp_shim;
> > +	struct delayed_work hdcp_work;
> >   };
> >   struct intel_digital_connector_state {
> > @@ -1295,6 +1341,8 @@ void intel_ddi_set_vc_payload_alloc(const struct intel_crtc_state *crtc_state,
> >   				    bool state);
> >   u32 bxt_signal_levels(struct intel_dp *intel_dp);
> >   uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
> > +int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder);
> > +int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder);
> >   u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
> >   unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
> > @@ -1746,6 +1794,11 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
> >   }
> >   #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
> > +/* intel_hdcp.c */
> > +int intel_hdcp_enable(struct intel_connector *connector);
> > +int intel_hdcp_disable(struct intel_connector *connector);
> > +int intel_hdcp_check_link(struct intel_connector *connector);
> > +void intel_hdcp_work(struct work_struct *work);
> >   /* intel_psr.c */
> >   void intel_psr_enable(struct intel_dp *intel_dp,
> > diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
> > new file mode 100644
> > index 000000000000..a2a575ed657e
> > --- /dev/null
> > +++ b/drivers/gpu/drm/i915/intel_hdcp.c
> > @@ -0,0 +1,636 @@
> > +/*
> > + * Copyright (C) 2017 Google, Inc.
> > + *
> > + * This software is licensed under the terms of the GNU General Public
> > + * License version 2, as published by the Free Software Foundation, and
> > + * may be copied, distributed, and modified under those terms.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <drm/drmP.h>
> > +#include <drm/drm_hdcp.h>
> > +#include <linux/i2c.h>
> > +#include <linux/random.h>
> > +
> > +#include "intel_drv.h"
> > +#include "i915_reg.h"
> > +
> > +#define KEY_LOAD_TRIES	5
> > +
> > +static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
> > +				    const struct intel_hdcp_shim *shim)
> > +{
> > +	unsigned long timeout = jiffies + msecs_to_jiffies_timeout(500);
> > +	int ret;
> > +	bool ksv_ready;
> > +
> > +	while (true) {
> > +		ret = shim->read_ksv_ready(intel_dig_port, &ksv_ready);
> > +		if (ret)
> > +			return ret;
> > +		if (ksv_ready)
> > +			break;
> > +		if (time_after(jiffies, timeout))
> > +			return -ETIMEDOUT;
> > +		msleep(100);
> > +	}
> > +	return 0;
> > +}
> > +
> > +static void intel_hdcp_clear_keys(struct drm_i915_private *dev_priv)
> > +{
> > +	I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_CLEAR_KEYS_TRIGGER);
> > +	I915_WRITE(SKL_HDCP_KEY_STATUS,
> > +		   SKL_HDCP_KEY_LOAD_DONE | SKL_HDCP_KEY_LOAD_STATUS |
> > +		   SKL_HDCP_FUSE_IN_PROGRESS | SKL_HDCP_FUSE_ERROR |
> > +		   SKL_HDCP_FUSE_DONE);
> > +}
> > +
> > +static int intel_hdcp_load_keys(struct drm_i915_private *dev_priv)
> > +{
> > +	unsigned long timeout;
> > +	int ret;
> > +	u32 val;
> > +
> > +	// Initiate loading the HDCP key from fuses
> > +	mutex_lock(&dev_priv->pcu_lock);
> > +	ret = sandybridge_pcode_write(dev_priv, SKL_PCODE_LOAD_HDCP_KEYS, 1);
> > +	mutex_unlock(&dev_priv->pcu_lock);
> > +	if (ret) {
> > +		DRM_ERROR("Failed to initiate HDCP key load (%d)\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	// Wait for the keys to load (500us)
> > +	timeout = jiffies + nsecs_to_jiffies_timeout(500 * 1000);
> > +	while (true) {
> > +		val = I915_READ(SKL_HDCP_KEY_STATUS);
> > +		if (val & SKL_HDCP_KEY_LOAD_DONE)
> > +			break;
> > +		if (time_after(jiffies, timeout))
> > +			return -ETIMEDOUT;
> > +		usleep_range(50, 100);
> > +	}
> > +	if (!(val & SKL_HDCP_KEY_LOAD_STATUS))
> > +		return -ENXIO;
> > +
> > +	// Send Aksv over to PCH display for use in authentication
> > +	I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_AKSV_SEND_TRIGGER);
> > +
> > +	return 0;
> > +}
> > +
> > +/* Returns updated SHA-1 index */
> > +static int intel_write_sha_text(struct drm_i915_private *dev_priv, u32 sha_text)
> > +{
> > +	I915_WRITE(SKL_HDCP_SHA_TEXT, sha_text);
> > +	if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_READY, 1)) {
> > +		DRM_ERROR("Timed out waiting for SHA1 ready\n");
> > +		return -ETIMEDOUT;
> > +	}
> > +	return 0;
> > +}
> > +
> > +static
> > +u32 intel_hdcp_get_repeater_ctl(struct intel_digital_port *intel_dig_port)
> > +{
> > +	enum port port = intel_dig_port->port;
> > +	switch(port) {
> > +	case PORT_A:
> > +		return SKL_HDCP_DDIA_REP_PRESENT | SKL_HDCP_DDIA_SHA1_M0;
> > +	case PORT_B:
> > +		return SKL_HDCP_DDIB_REP_PRESENT | SKL_HDCP_DDIB_SHA1_M0;
> > +	case PORT_C:
> > +		return SKL_HDCP_DDIC_REP_PRESENT | SKL_HDCP_DDIC_SHA1_M0;
> > +	case PORT_D:
> > +		return SKL_HDCP_DDID_REP_PRESENT | SKL_HDCP_DDID_SHA1_M0;
> > +	case PORT_E:
> > +		return SKL_HDCP_DDIE_REP_PRESENT | SKL_HDCP_DDIE_SHA1_M0;
> > +	default:
> > +		break;
> > +	}
> > +	DRM_ERROR("Unknown port %d\n", port);
> > +	return -EINVAL;
> > +}
> > +
> > +/* Implements Part 2 of the HDCP authorization procedure */
> > +static
> > +int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
> > +			       const struct intel_hdcp_shim *shim)
> > +{
> > +	struct drm_i915_private *dev_priv;
> > +	u32 vprime, sha_text, sha_leftovers, rep_ctl;
> > +	u8 bstatus[2], num_downstream, *ksv_fifo;
> > +	int ret, i, j, sha_idx;
> > +
> > +	dev_priv = intel_dig_port->base.base.dev->dev_private;
> > +
> > +	ret = shim->read_bstatus(intel_dig_port, bstatus);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/* If there are no downstream devices, we're all done. */
> > +	num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
> > +	if (num_downstream == 0) {
> > +		DRM_INFO("HDCP is enabled (no downstream devices)\n");
> > +		return 0;
> > +	}
> > +
> > +	// Poll for ksv list ready (spec says max time allowed is 5s)
> > +	ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim);
> > +	if (ret) {
> > +		DRM_ERROR("KSV list failed to become ready (%d)\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL);
> > +	if (!ksv_fifo)
> > +		return -ENOMEM;
> > +
> > +	ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo);
> > +	if (ret)
> > +		return ret;
> > +
> > +	// Process V' values from the receiver
> > +	for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
> > +		ret = shim->read_v_prime_part(intel_dig_port, i, &vprime);
> > +		if (ret)
> > +			return ret;
> > +		I915_WRITE(SKL_HDCP_SHA_V_PRIME(i), vprime);
> > +	}
> > +
> > +	/*
> > +	 * We need to write the concatenation of all device KSVs, BINFO (DP) ||
> > +	 * BSTATUS (HDMI), and M0 (which is added via HDCP_REP_CTL). This byte
> > +	 * stream is written via the HDCP_SHA_TEXT register in 32-bit
> > +	 * increments. Every 64 bytes, we need to write HDCP_REP_CTL again. This
> > +	 * index will keep track of our progress through the 64 bytes as well as
> > +	 * helping us work the 40-bit KSVs through our 32-bit register.
> > +	 *
> > +	 * NOTE: data passed via HDCP_SHA_TEXT should be big-endian
> > +	 */
> > +	sha_idx = 0;
> > +	sha_text = 0;
> > +	sha_leftovers = 0;
> > +	rep_ctl = intel_hdcp_get_repeater_ctl(intel_dig_port);
> > +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> > +	for (i = 0; i < num_downstream; i++) {
> > +		unsigned sha_empty;
> > +		u8 *ksv = &ksv_fifo[i * DRM_HDCP_KSV_LEN];
> > +
> > +		// Fill up the empty slots in sha_text and write it out
> > +		sha_empty = sizeof(sha_text) - sha_leftovers;
> > +		for (j = 0; j < sha_empty; j++)
> > +			sha_text |= ksv[j] << ((sizeof(sha_text) - j - 1) * 8);
> > +
> > +		ret = intel_write_sha_text(dev_priv, sha_text);
> > +		if (ret < 0)
> > +			return ret;
> > +
> > +		// Programming guide writes this every 64 bytes
> > +		sha_idx += sizeof(sha_text);
> > +		if (!(sha_idx % 64))
> > +			I915_WRITE(SKL_HDCP_REP_CTL,
> > +				   rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> > +
> > +		// Store the leftover bytes from the ksv in sha_text
> > +		sha_leftovers = DRM_HDCP_KSV_LEN - sha_empty;
> > +		sha_text = 0;
> > +		for (j = 0; j < sha_leftovers; j++)
> > +			sha_text |= ksv[sha_empty + j] <<
> > +					((sizeof(sha_text) - j - 1) * 8);
> > +
> > +		/*
> > +		 * If we still have room in sha_text for more data, continue.
> > +		 * Otherwise, write it out immediately.
> > +		 */
> > +		if (sizeof(sha_text) > sha_leftovers)
> > +			continue;
> > +
> > +		ret = intel_write_sha_text(dev_priv, sha_text);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_leftovers = 0;
> > +		sha_text = 0;
> > +		sha_idx += sizeof(sha_text);
> > +	}
> > +
> > +	/*
> > +	 * We need to write BINFO/BSTATUS, and M0 now. Depending on how many
> > +	 * bytes are leftover from the last ksv, we might be able to fit them
> > +	 * all in sha_text (first 2 cases), or we might need to split them up
> > +	 * into 2 writes (last 2 cases).
> > +	 */
> > +	if (sha_leftovers == 0) {
> > +		// Write 16 bits of text, 16 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
> > +		ret = intel_write_sha_text(dev_priv,
> > +					   bstatus[0] << 8 | bstatus[1]);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +		// Write 32 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
> > +		ret = intel_write_sha_text(dev_priv, 0);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +		// Write 16 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
> > +		ret = intel_write_sha_text(dev_priv, 0);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +	} else if (sha_leftovers == 1) {
> > +		// Write 24 bits of text, 8 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
> > +		sha_text |= bstatus[0] << 16 | bstatus[1] << 8;
> > +		// Only 24-bits of data, must be in the LSB
> > +		sha_text = (sha_text & 0xffffff00) >> 8;
> > +		ret = intel_write_sha_text(dev_priv, sha_text);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +		// Write 32 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
> > +		ret = intel_write_sha_text(dev_priv, 0);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +		// Write 24 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
> > +		ret = intel_write_sha_text(dev_priv, 0);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +	} else if (sha_leftovers == 2) {
> > +		// Write 32 bits of text
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> > +		sha_text |= bstatus[0] << 24 | bstatus[1] << 16;
> > +		ret = intel_write_sha_text(dev_priv, sha_text);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +		// Write 64 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
> > +		for (i = 0; i < 2; i++) {
> > +			ret = intel_write_sha_text(dev_priv, 0);
> > +			if (ret < 0)
> > +				return ret;
> > +			sha_idx += sizeof(sha_text);
> > +		}
> > +	} else if (sha_leftovers == 3) {
> > +		// Write 32 bits of text
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> > +		sha_text |= bstatus[0] << 24;
> > +		ret = intel_write_sha_text(dev_priv, sha_text);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +		// Write 8 bits of text, 24 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
> > +		ret = intel_write_sha_text(dev_priv, bstatus[1]);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +		// Write 32 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
> > +		ret = intel_write_sha_text(dev_priv, 0);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +
> > +		// Write 8 bits of M0
> > +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
> > +		ret = intel_write_sha_text(dev_priv, 0);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +	} else {
> > +		DRM_ERROR("Invalid number of leftovers %d\n", sha_leftovers);
> > +		return -EINVAL;
> > +	}
> > +
> > +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> > +	// Fill up to 64 - 4 bytes with zeros (leave the last write for length)
> > +	while ((sha_idx % 64) < (64 - sizeof(sha_text))) {
> > +		ret = intel_write_sha_text(dev_priv, 0);
> > +		if (ret < 0)
> > +			return ret;
> > +		sha_idx += sizeof(sha_text);
> > +	}
> > +
> > +	/*
> > +	 * Last write gets the length of the concatenation in bits. That is:
> > +	 *  - 5 bytes per device
> > +	 *  - 10 bytes for BINFO/BSTATUS(2), M0(8)
> > +	 */
> > +	sha_text = (num_downstream * 5 + 10) * 8;
> > +	ret = intel_write_sha_text(dev_priv, sha_text);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	// Finally, tell the HW we're done with the hash and wait for it to ACK
> > +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_COMPLETE_HASH);
> > +	if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_COMPLETE, 1)) {
> > +		DRM_ERROR("Timed out waiting for SHA1 complete\n");
> > +		return -ETIMEDOUT;
> > +	}
> > +	if (!(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_V_MATCH)) {
> > +		DRM_ERROR("SHA-1 mismatch, HDCP failed\n");
> > +		return -ENXIO;
> > +	}
> > +
> > +	DRM_INFO("HDCP is enabled (%d downstream devices)\n", num_downstream);
> > +	return 0;
> > +}
> > +
> > +/* Implements Part 1 of the HDCP authorization procedure */
> > +static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
> > +			   const struct intel_hdcp_shim *shim)
> > +{
> > +	struct drm_i915_private *dev_priv;
> > +	enum port port;
> > +	unsigned long r0_prime_gen_start;
> > +	int ret, i;
> > +	union {
> > +		u32 reg[2];
> > +		u8 shim[DRM_HDCP_AN_LEN];
> > +	} an;
> > +	union {
> > +		u32 reg[2];
> > +		u8 shim[DRM_HDCP_KSV_LEN];
> > +	} bksv;
> > +	union {
> > +		u32 reg;
> > +		u8 shim[DRM_HDCP_RI_LEN];
> > +	} ri;
> > +	bool repeater_present;
> > +
> > +	dev_priv = intel_dig_port->base.base.dev->dev_private;
> > +
> > +	port = intel_dig_port->port;
> > +
> > +	// Initialize An with 2 random values and acquire it
> > +	for (i = 0; i < 2; i++)
> > +		I915_WRITE(SKL_PORT_HDCP_ANINIT(port), get_random_long());
> > +	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_CAPTURE_AN);
> > +
> > +	// Wait for An to be acquired
> > +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> > +		     SKL_HDCP_STATUS_AN_READY, 1)) {
> > +		DRM_ERROR("Timed out waiting for An\n");
> > +		return -ETIMEDOUT;
> > +	}
> > +
> > +	an.reg[0] = I915_READ(SKL_PORT_HDCP_ANLO(port));
> > +	an.reg[1] = I915_READ(SKL_PORT_HDCP_ANHI(port));
> > +	ret = shim->write_an_aksv(intel_dig_port, an.shim);
> > +	if (ret)
> > +		return ret;
> > +
> > +	r0_prime_gen_start = jiffies;
> > +
> > +	memset(&bksv, 0, sizeof(bksv));
> > +	ret = shim->read_bksv(intel_dig_port, bksv.shim);
> > +	if (ret)
> > +		return ret;
> > +
> > +	I915_WRITE(SKL_PORT_HDCP_BKSVLO(port), bksv.reg[0]);
> > +	I915_WRITE(SKL_PORT_HDCP_BKSVHI(port), bksv.reg[1]);
> > +
> > +	ret = shim->repeater_present(intel_dig_port, &repeater_present);
> > +	if (ret)
> > +		return ret;
> > +	if (repeater_present)
> > +		I915_WRITE(SKL_HDCP_REP_CTL,
> > +			   intel_hdcp_get_repeater_ctl(intel_dig_port));
> > +
> > +	ret = shim->toggle_signalling(intel_dig_port, true);
> > +	if (ret)
> > +		return ret;
> > +
> > +	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_AUTH_AND_ENC);
> > +
> > +	// Wait for R0 ready
> > +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> > +		     (SKL_HDCP_STATUS_R0_READY | SKL_HDCP_STATUS_ENC), 1)) {
> > +		DRM_ERROR("Timed out waiting for R0 ready\n");
> > +		return -ETIMEDOUT;
> > +	}
> > +
> > +	/*
> > +	 * Wait for R0' to become available, the spec says 100ms from Aksv
> > +	 * write. On DP, there's an R0_READY bit available but no such bit
> > +	 * exists on HDMI. Since the upper-bound is the same, we'll just do
> > +	 * the stupid thing instead of polling on one and not the other.
> > +	 */
> > +	wait_remaining_ms_from_jiffies(r0_prime_gen_start, 100);
> > +
> > +	ri.reg = 0;
> > +	ret = shim->read_ri_prime(intel_dig_port, ri.shim);
> > +	if (ret)
> > +		return ret;
> > +	I915_WRITE(SKL_PORT_HDCP_RPRIME(port), ri.reg);
> > +
> > +	// Wait for Ri prime match
> > +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> > +		     (SKL_HDCP_STATUS_RI_MATCH | SKL_HDCP_STATUS_ENC), 1)) {
> > +		DRM_ERROR("Timed out waiting for Ri prime match (%x)\n",
> > +			  I915_READ(SKL_PORT_HDCP_STATUS(port)));
> > +		return -ETIMEDOUT;
> > +	}
> > +
> > +	// Wait for encryption confirmation
> > +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> > +		      SKL_HDCP_STATUS_ENC, 20)) {
> > +		DRM_ERROR("Timed out waiting for encryption\n");
> > +		return -ETIMEDOUT;
> > +	}
> > +
> > +	/*
> > +	 * XXX: If we have MST-connected devices, we need to enable encryption
> > +	 * on those as well.
> > +	 */
> > +
> > +	return intel_hdcp_auth_downstream(intel_dig_port, shim);
> > +}
> > +
> > +static
> > +struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector)
> > +{
> > +	return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base);
> > +}
> > +
> > +static int _intel_hdcp_disable(struct intel_connector *connector)
> > +{
> > +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> > +	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
> > +	enum port port = intel_dig_port->port;
> > +	int ret;
> > +
> > +	I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
> > +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) == 0, 20)) {
> > +		DRM_ERROR("Failed to disable HDCP, timeout clearing status\n");
> > +		return -ETIMEDOUT;
> > +	}
> > +
> > +	intel_hdcp_clear_keys(dev_priv);
> > +
> > +	ret = connector->hdcp_shim->toggle_signalling(intel_dig_port, false);
> > +	if (ret) {
> > +		DRM_ERROR("Failed to disable HDCP signalling\n");
> > +		return ret;
> > +	}
> > +
> > +	DRM_INFO("HDCP is disabled\n");
> > +	return 0;
> > +}
> > +
> > +static int _intel_hdcp_enable(struct intel_connector *connector)
> > +{
> > +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> > +	int i, ret;
> > +
> > +	if (!(I915_READ(SKL_FUSE_STATUS) & SKL_FUSE_PG_DIST_STATUS(1))) {
> > +		DRM_ERROR("PG1 is disabled, cannot load keys\n");
> > +		return -ENXIO;
> > +	}
> > +
> > +	for (i = 0; i < KEY_LOAD_TRIES; i++) {
> > +		ret = intel_hdcp_load_keys(dev_priv);
> > +		if (!ret)
> > +			break;
> > +		intel_hdcp_clear_keys(dev_priv);
> > +	}
> > +	if (ret) {
> > +		DRM_ERROR("Could not load HDCP keys, (%d)\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = intel_hdcp_auth(conn_to_dig_port(connector),
> > +			      connector->hdcp_shim);
> > +	if (ret) {
> > +		DRM_ERROR("Failed to authenticate HDCP (%d)\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +void intel_hdcp_work(struct work_struct *work)
> > +{
> > +	struct intel_connector *connector = container_of(to_delayed_work(work),
> > +							 struct intel_connector,
> > +						         hdcp_work);
> > +	struct drm_device *dev = connector->base.dev;
> > +	int ret;
> > +
> > +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> > +
> > +	ret = intel_hdcp_check_link(connector);
> > +	if (!ret)
> > +		schedule_delayed_work(&connector->hdcp_work,
> > +				      DRM_HDCP_CHECK_PERIOD_MS);
> > +
> > +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
> > +}
> > +
> > +int intel_hdcp_enable(struct intel_connector *connector)
> > +{
> > +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> > +	struct drm_device *dev = &dev_priv->drm;
> > +	struct drm_connector_state *state = connector->base.state;
> > +	int ret;
> > +
> > +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
> > +
> > +	if (!connector->hdcp_shim)
> > +		return -ENOENT;
> > +
> > +	ret = _intel_hdcp_enable(connector);
> > +	if (ret)
> > +		return ret;
> > +
> > +	state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
> > +
> > +	schedule_delayed_work(&connector->hdcp_work, DRM_HDCP_CHECK_PERIOD_MS);
> > +	return 0;
> > +}
> > +
> > +int intel_hdcp_disable(struct intel_connector *connector)
> > +{
> > +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> > +	struct drm_device *dev = &dev_priv->drm;
> > +
> > +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
> > +
> > +	if (!connector->hdcp_shim)
> > +		return -ENOENT;
> > +
> > +	cancel_delayed_work(&connector->hdcp_work);
> > +
> > +	return _intel_hdcp_disable(connector);
> > +}
> > +
> > +/* Implements Part 3 of the HDCP authorization procedure */
> > +int intel_hdcp_check_link(struct intel_connector *connector)
> > +{
> > +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
> > +	struct drm_device *dev = &dev_priv->drm;
> > +	struct drm_connector_state *state = connector->base.state;
> > +	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
> > +	enum port port = intel_dig_port->port;
> > +	int ret;
> > +
> > +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
> > +
> > +	if (state->content_protection != DRM_MODE_CONTENT_PROTECTION_ENABLED)
> > +		return 0;
> > +
> > +	if (!connector->hdcp_shim)
> > +		return -ENOENT;
> > +
> > +	if (!(I915_READ(SKL_PORT_HDCP_STATUS(port)) & SKL_HDCP_STATUS_ENC)) {
> > +		DRM_ERROR("HDCP check failed: link is not encrypted, %x\n",
> > +			   I915_READ(SKL_PORT_HDCP_STATUS(port)));
> > +		ret = -ENXIO;
> > +		goto fail;
> > +	}
> > +
> > +	if (connector->hdcp_shim->check_link(intel_dig_port))
> > +		return 0;
> > +
> > +	DRM_INFO("HDCP link failed, retrying authentication\n");
> > +
> > +	ret = _intel_hdcp_disable(connector);
> > +	if (ret) {
> > +		DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
> > +		goto fail;
> > +	}
> > +
> > +	ret = _intel_hdcp_enable(connector);
> > +	if (ret) {
> > +		DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
> > +		goto fail;
> > +	}
> > +
> > +	return 0;
> > +
> > +fail:
> > +	state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
> > +	return ret;
> > +}
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Intel-gfx] [RFC PATCH 3/6] drm/i915: Add HDCP framework + base implementation
  2017-12-01  7:36       ` Daniel Vetter
@ 2017-12-01  8:36         ` Ramalingam C
  -1 siblings, 0 replies; 62+ messages in thread
From: Ramalingam C @ 2017-12-01  8:36 UTC (permalink / raw)
  To: Sean Paul, dri-devel, intel-gfx, David Airlie, linux-kernel,
	Rodrigo Vivi



On Friday 01 December 2017 01:06 PM, Daniel Vetter wrote:
> On Fri, Dec 01, 2017 at 12:53:31PM +0530, Ramalingam C wrote:
>> Sean,
>>
>> IMHO, it will good if we can have all generic hdcp1.4 authentication flow in
>> drm helpers and all interested display drivers to use them.
>>
>> This Design will make the extending of hdcp easy for other display drivers
>> based on DRM.
>>
>> We can have the required drm_hdcp_shim type of implementation at drm
>> structure which will be called for platform specific operations (like
>> prepare an, send aksv, program bksv/repeater/r0 and verify sha1 etc)?
> I discussed this exact question with Sean Paul, and apparently the
> hardware designs are too diverse to make shared code much useful. Some hw
> has the entire hdcp flow in hw, some almost nothing (like i915 here), and
> then there's everything in between.
Just trying to understand the other extreme of HW (full)support for HDCP 
here.

When you say everything about HDCP is implemented in HW, do you mean 
that whole protocol comm on HDCP link also driven by HW?

--Ram
>
> Given that Sean has seen a lot more hdcp implementations than we have,
> that we right now have no other implementation than i915 in upstream and
> than wrong abstraction is much harder to fix than no abstraction I'm going
> with Sean's approach of "no generic abstraction" here. Personally I'm not
> even fully sold on the shim abstraction, but I think by&large that one is
> fine.
>
>> On Thursday 30 November 2017 08:38 AM, Sean Paul wrote:
>>> This patch adds the framework required to add HDCP support to intel
>>> connectors. It implements Aksv loading from fuse, and parts 1/2/3
>>> of the HDCP authentication scheme.
>>>
>>> Note that without shim implementations, this does not actually implement
>>> HDCP. That will come in subsequent patches.
>>>
>>> Signed-off-by: Sean Paul <seanpaul@chromium.org>
>>> ---
>>>    drivers/gpu/drm/i915/Makefile       |   1 +
>>>    drivers/gpu/drm/i915/i915_reg.h     |  83 +++++
>>>    drivers/gpu/drm/i915/intel_atomic.c |  26 +-
>>>    drivers/gpu/drm/i915/intel_ddi.c    |  14 +
>>>    drivers/gpu/drm/i915/intel_drv.h    |  53 +++
>>>    drivers/gpu/drm/i915/intel_hdcp.c   | 636 ++++++++++++++++++++++++++++++++++++
>>>    6 files changed, 811 insertions(+), 2 deletions(-)
>>>    create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c
>>>
>>> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
>>> index 6c3b0481ef82..1e745508e437 100644
>>> --- a/drivers/gpu/drm/i915/Makefile
>>> +++ b/drivers/gpu/drm/i915/Makefile
>>> @@ -87,6 +87,7 @@ i915-y += intel_audio.o \
>>>    	  intel_fbc.o \
>>>    	  intel_fifo_underrun.o \
>>>    	  intel_frontbuffer.o \
>>> +	  intel_hdcp.o \
>>>    	  intel_hotplug.o \
>>>    	  intel_modes.o \
>>>    	  intel_overlay.o \
>>> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
>>> index 68a58cce6ab1..43128030171d 100644
>>> --- a/drivers/gpu/drm/i915/i915_reg.h
>>> +++ b/drivers/gpu/drm/i915/i915_reg.h
>>> @@ -7991,6 +7991,7 @@ enum {
>>>    #define     GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT	8
>>>    #define     GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT	16
>>>    #define     GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT	24
>>> +#define   SKL_PCODE_LOAD_HDCP_KEYS		0x5
>>>    #define   SKL_PCODE_CDCLK_CONTROL		0x7
>>>    #define     SKL_CDCLK_PREPARE_FOR_CHANGE	0x3
>>>    #define     SKL_CDCLK_READY_FOR_CHANGE		0x1
>>> @@ -8285,6 +8286,88 @@ enum skl_power_gate {
>>>    #define  SKL_PW_TO_PG(pw)			((pw) - SKL_DISP_PW_1 + SKL_PG1)
>>>    #define  SKL_FUSE_PG_DIST_STATUS(pg)		(1 << (27 - (pg)))
>>> +
>>> +/* HDCP Key Registers */
>>> +#define SKL_HDCP_KEY_CONF		_MMIO(0x66c00)
>>> +#define	 SKL_HDCP_AKSV_SEND_TRIGGER	BIT(31)
>>> +#define  SKL_HDCP_CLEAR_KEYS_TRIGGER	BIT(30)
>>> +#define SKL_HDCP_KEY_STATUS		_MMIO(0x66c04)
>>> +#define  SKL_HDCP_FUSE_IN_PROGRESS	BIT(7)
>>> +#define  SKL_HDCP_FUSE_ERROR		BIT(6)
>>> +#define  SKL_HDCP_FUSE_DONE		BIT(5)
>>> +#define  SKL_HDCP_KEY_LOAD_STATUS	BIT(1)
>>> +#define  SKL_HDCP_KEY_LOAD_DONE		BIT(0)
>>> +#define SKL_HDCP_AKSV_LO		_MMIO(0x66c10)
>>> +#define SKL_HDCP_AKSV_HI		_MMIO(0x66c14)
>>> +
>>> +/* HDCP Repeater Registers */
>>> +#define SKL_HDCP_REP_CTL		_MMIO(0x66d00)
>>> +#define  SKL_HDCP_DDIB_REP_PRESENT	BIT(30)
>>> +#define  SKL_HDCP_DDIA_REP_PRESENT	BIT(29)
>>> +#define  SKL_HDCP_DDIC_REP_PRESENT	BIT(28)
>>> +#define  SKL_HDCP_DDID_REP_PRESENT	BIT(27)
>>> +#define  SKL_HDCP_DDIF_REP_PRESENT	BIT(26)
>>> +#define  SKL_HDCP_DDIE_REP_PRESENT	BIT(25)
>>> +#define  SKL_HDCP_DDIB_SHA1_M0		(1 << 20)
>>> +#define  SKL_HDCP_DDIA_SHA1_M0		(2 << 20)
>>> +#define  SKL_HDCP_DDIC_SHA1_M0		(3 << 20)
>>> +#define  SKL_HDCP_DDID_SHA1_M0		(4 << 20)
>>> +#define  SKL_HDCP_DDIF_SHA1_M0		(5 << 20)
>>> +#define  SKL_HDCP_DDIE_SHA1_M0		(6 << 20) // Bspec says 5?
>>> +#define  SKL_HDCP_SHA1_BUSY		BIT(16)
>>> +#define  SKL_HDCP_SHA1_READY		BIT(17)
>>> +#define  SKL_HDCP_SHA1_COMPLETE		BIT(18)
>>> +#define  SKL_HDCP_SHA1_V_MATCH		BIT(19)
>>> +#define  SKL_HDCP_SHA1_TEXT_32		(1 << 1)
>>> +#define  SKL_HDCP_SHA1_COMPLETE_HASH	(2 << 1)
>>> +#define  SKL_HDCP_SHA1_TEXT_24		(4 << 1)
>>> +#define  SKL_HDCP_SHA1_TEXT_16		(5 << 1)
>>> +#define  SKL_HDCP_SHA1_TEXT_8		(6 << 1)
>>> +#define  SKL_HDCP_SHA1_TEXT_0		(7 << 1)
>>> +#define SKL_HDCP_SHA_V_PRIME_H0		_MMIO(0x66d04)
>>> +#define SKL_HDCP_SHA_V_PRIME_H1		_MMIO(0x66d08)
>>> +#define SKL_HDCP_SHA_V_PRIME_H2		_MMIO(0x66d0C)
>>> +#define SKL_HDCP_SHA_V_PRIME_H3		_MMIO(0x66d10)
>>> +#define SKL_HDCP_SHA_V_PRIME_H4		_MMIO(0x66d14)
>>> +#define SKL_HDCP_SHA_V_PRIME(h)		_MMIO((0x66d04 + h * 4))
>>> +#define SKL_HDCP_SHA_TEXT		_MMIO(0x66d18)
>>> +
>>> +/* HDCP Auth Registers */
>>> +#define _SKL_PORTA_HDCP_AUTHENC		0x66800
>>> +#define _SKL_PORTB_HDCP_AUTHENC		0x66500
>>> +#define _SKL_PORTC_HDCP_AUTHENC		0x66600
>>> +#define _SKL_PORTD_HDCP_AUTHENC		0x66700
>>> +#define _SKL_PORTE_HDCP_AUTHENC		0x66A00
>>> +#define _SKL_PORTF_HDCP_AUTHENC		0x66900
>>> +#define _SKL_PORT_HDCP_AUTHENC(port, x)	_MMIO(_PICK(port, \
>>> +					  _SKL_PORTA_HDCP_AUTHENC, \
>>> +					  _SKL_PORTB_HDCP_AUTHENC, \
>>> +					  _SKL_PORTC_HDCP_AUTHENC, \
>>> +					  _SKL_PORTD_HDCP_AUTHENC, \
>>> +					  _SKL_PORTE_HDCP_AUTHENC, \
>>> +					  _SKL_PORTF_HDCP_AUTHENC) + x)
>>> +#define SKL_PORT_HDCP_CONF(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x0)
>>> +#define  SKL_HDCP_CONF_CAPTURE_AN	BIT(0)
>>> +#define  SKL_HDCP_CONF_AUTH_AND_ENC	(BIT(1) | BIT(0))
>>> +#define SKL_PORT_HDCP_ANINIT(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x4)
>>> +#define SKL_PORT_HDCP_ANLO(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x8)
>>> +#define SKL_PORT_HDCP_ANHI(port)	_SKL_PORT_HDCP_AUTHENC(port, 0xC)
>>> +#define SKL_PORT_HDCP_BKSVLO(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x10)
>>> +#define SKL_PORT_HDCP_BKSVHI(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x14)
>>> +#define SKL_PORT_HDCP_RPRIME(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x18)
>>> +#define SKL_PORT_HDCP_STATUS(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x1C)
>>> +#define  SKL_HDCP_STATUS_STREAM_A_ENC	BIT(31)
>>> +#define  SKL_HDCP_STATUS_STREAM_B_ENC	BIT(30)
>>> +#define  SKL_HDCP_STATUS_STREAM_C_ENC	BIT(29)
>>> +#define  SKL_HDCP_STATUS_STREAM_D_ENC	BIT(28)
>>> +#define  SKL_HDCP_STATUS_AUTH		BIT(21)
>>> +#define  SKL_HDCP_STATUS_ENC		BIT(20)
>>> +#define  SKL_HDCP_STATUS_RI_MATCH	BIT(19)
>>> +#define  SKL_HDCP_STATUS_R0_READY	BIT(18)
>>> +#define  SKL_HDCP_STATUS_AN_READY	BIT(17)
>>> +#define  SKL_HDCP_STATUS_CIPHER		BIT(16)
>>> +#define  SKL_HDCP_STATUS_FRAME_CNT(x)	((x >> 8) & 0xff)
>>> +
>>>    /* Per-pipe DDI Function Control */
>>>    #define _TRANS_DDI_FUNC_CTL_A		0x60400
>>>    #define _TRANS_DDI_FUNC_CTL_B		0x61400
>>> diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
>>> index 36d4e635e4ce..ddf08227d9cb 100644
>>> --- a/drivers/gpu/drm/i915/intel_atomic.c
>>> +++ b/drivers/gpu/drm/i915/intel_atomic.c
>>> @@ -109,12 +109,34 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
>>>    	struct intel_digital_connector_state *old_conn_state =
>>>    		to_intel_digital_connector_state(old_state);
>>>    	struct drm_crtc_state *crtc_state;
>>> -
>>> -	if (!new_state->crtc)
>>> +	uint64_t old_cp = old_conn_state->base.content_protection;
>>> +	uint64_t new_cp = new_state->content_protection;
>>> +
>>> +	if (!new_state->crtc) {
>>> +		/*
>>> +		 * If the connector is being disabled with CP enabled, mark it
>>> +		 * desired so it's re-enabled when the connector is brought back
>>> +		 */
>>> +		if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
>>> +			new_state->content_protection =
>>> +				DRM_MODE_CONTENT_PROTECTION_DESIRED;
>>>    		return 0;
>>> +	}
>>>    	crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc);
>>> +	if (new_cp != old_cp) {
>>> +		/* Only drivers can set content protection enabled */
>>> +		if (new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
>>> +			new_state->content_protection =
>>> +				DRM_MODE_CONTENT_PROTECTION_DESIRED;
>>> +
>>> +		/* Involve the encoder/connector to enable/disable CP */
>>> +		if (new_cp == DRM_MODE_CONTENT_PROTECTION_OFF ||
>>> +		    old_cp == DRM_MODE_CONTENT_PROTECTION_OFF)
>>> +			crtc_state->mode_changed = true;
>> We need not perform the mode set for hdcp enable/disable.
>> Authentication and encryption can be started on active port.
> Was simpler to implement this way :-) We can fix this by pushing the hdcp
> enable/disable code into a post-modeset operation. Ville had written the
> infrastructure for that to fix a few fastboot corner cases. But that
> infrastructure hasn't landed yet, so probably better to do that in a
> follow-up.
>
> I also guess that CrOS simply set this to desired every time they enable
> an external screen, so it won't result in an unecessary modeset.
>
>>> +	}
>>> +
>>>    	/*
>>>    	 * These properties are handled by fastset, and might not end
>>>    	 * up in a modeset.
>>> diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
>>> index 933c18fd4258..0e69337f410d 100644
>>> --- a/drivers/gpu/drm/i915/intel_ddi.c
>>> +++ b/drivers/gpu/drm/i915/intel_ddi.c
>>> @@ -2432,10 +2432,17 @@ static void intel_enable_ddi(struct intel_encoder *encoder,
>>>    			     const struct intel_crtc_state *crtc_state,
>>>    			     const struct drm_connector_state *conn_state)
>>>    {
>>> +	struct drm_connector *connector = conn_state->connector;
>>> +	struct intel_connector *intel_connector = to_intel_connector(connector);
>>> +
>>>    	if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
>>>    		intel_enable_ddi_hdmi(encoder, crtc_state, conn_state);
>>>    	else
>>>    		intel_enable_ddi_dp(encoder, crtc_state, conn_state);
>>> +
>>> +	if (conn_state->content_protection ==
>>> +			DRM_MODE_CONTENT_PROTECTION_DESIRED)
>>> +		intel_hdcp_enable(intel_connector);
>>>    }
>>>    static void intel_disable_ddi_dp(struct intel_encoder *encoder,
>>> @@ -2468,10 +2475,17 @@ static void intel_disable_ddi(struct intel_encoder *encoder,
>>>    			      const struct intel_crtc_state *old_crtc_state,
>>>    			      const struct drm_connector_state *old_conn_state)
>>>    {
>>> +	struct drm_connector *connector = old_conn_state->connector;
>>> +	struct intel_connector *intel_connector = to_intel_connector(connector);
>>> +
>>>    	if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
>>>    		intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state);
>>>    	else
>>>    		intel_disable_ddi_dp(encoder, old_crtc_state, old_conn_state);
>>> +
>>> +	if (old_conn_state->content_protection !=
>>> +			DRM_MODE_CONTENT_PROTECTION_OFF)
>>> +		intel_hdcp_disable(intel_connector);
>> We might want to disable the hdcp before disabling the DDI. Actually we
>> could trigger hdcp disable at connector state change(due to hot-unplug)
>> also.
> Yeah this part needs to be reworked a bit, also because the locking
> doesn't work yet. I think hot-unplug is handled by the worker thread
> already, it does the mandatory regular polling.
> -Daniel
>
>> Thanks,
>> --Ram
>>>    }
>>>    static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder,
>>> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
>>> index 47d022d48718..8924004575b8 100644
>>> --- a/drivers/gpu/drm/i915/intel_drv.h
>>> +++ b/drivers/gpu/drm/i915/intel_drv.h
>>> @@ -299,6 +299,49 @@ struct intel_panel {
>>>    	} backlight;
>>>    };
>>> +struct intel_hdcp_shim {
>>> +	/* Outputs the transmitter's An and Aksv values to the receiver. */
>>> +	int (*write_an_aksv)(struct intel_digital_port *intel_dig_port, u8 *an);
>>> +
>>> +	/* Reads the receiver's key selection vector */
>>> +	int (*read_bksv)(struct intel_digital_port *intel_dig_port, u8 *bksv);
>>> +
>>> +	/*
>>> +	 * Reads BINFO from DP receivers and BSTATUS from HDMI receivers. The
>>> +	 * definitions are the same in the respective specs, but the names are
>>> +	 * different. Call it BSTATUS since that's the name the HDMI spec
>>> +	 * uses and it was there first.
>>> +	 */
>>> +	int (*read_bstatus)(struct intel_digital_port *intel_dig_port,
>>> +			    u8 *bstatus);
>>> +
>>> +	/* Determines whether a repeater is present downstream */
>>> +	int (*repeater_present)(struct intel_digital_port *intel_dig_port,
>>> +				bool *repeater_present);
>>> +
>>> +	/* Reads the receiver's Ri' value */
>>> +	int (*read_ri_prime)(struct intel_digital_port *intel_dig_port, u8 *ri);
>>> +
>>> +	/* Determines if the receiver's KSV FIFO is ready for consumption */
>>> +	int (*read_ksv_ready)(struct intel_digital_port *intel_dig_port,
>>> +			      bool *ksv_ready);
>>> +
>>> +	/* Reads the ksv fifo for num_downstream devices */
>>> +	int (*read_ksv_fifo)(struct intel_digital_port *intel_dig_port,
>>> +			     int num_downstream, u8 *ksv_fifo);
>>> +
>>> +	/* Reads a 32-bit part of V' from the receiver */
>>> +	int (*read_v_prime_part)(struct intel_digital_port *intel_dig_port,
>>> +				 int i, u32 *part);
>>> +
>>> +	/* Enables HDCP signalling on the port */
>>> +	int (*toggle_signalling)(struct intel_digital_port *intel_dig_port,
>>> +				 bool enable);
>>> +
>>> +	/* Ensures the link is still protected */
>>> +	bool (*check_link)(struct intel_digital_port *intel_dig_port);
>>> +};
>>> +
>>>    struct intel_connector {
>>>    	struct drm_connector base;
>>>    	/*
>>> @@ -330,6 +373,9 @@ struct intel_connector {
>>>    	/* Work struct to schedule a uevent on link train failure */
>>>    	struct work_struct modeset_retry_work;
>>> +
>>> +	const struct intel_hdcp_shim *hdcp_shim;
>>> +	struct delayed_work hdcp_work;
>>>    };
>>>    struct intel_digital_connector_state {
>>> @@ -1295,6 +1341,8 @@ void intel_ddi_set_vc_payload_alloc(const struct intel_crtc_state *crtc_state,
>>>    				    bool state);
>>>    u32 bxt_signal_levels(struct intel_dp *intel_dp);
>>>    uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
>>> +int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder);
>>> +int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder);
>>>    u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
>>>    unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
>>> @@ -1746,6 +1794,11 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
>>>    }
>>>    #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
>>> +/* intel_hdcp.c */
>>> +int intel_hdcp_enable(struct intel_connector *connector);
>>> +int intel_hdcp_disable(struct intel_connector *connector);
>>> +int intel_hdcp_check_link(struct intel_connector *connector);
>>> +void intel_hdcp_work(struct work_struct *work);
>>>    /* intel_psr.c */
>>>    void intel_psr_enable(struct intel_dp *intel_dp,
>>> diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
>>> new file mode 100644
>>> index 000000000000..a2a575ed657e
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/i915/intel_hdcp.c
>>> @@ -0,0 +1,636 @@
>>> +/*
>>> + * Copyright (C) 2017 Google, Inc.
>>> + *
>>> + * This software is licensed under the terms of the GNU General Public
>>> + * License version 2, as published by the Free Software Foundation, and
>>> + * may be copied, distributed, and modified under those terms.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + */
>>> +
>>> +#include <drm/drmP.h>
>>> +#include <drm/drm_hdcp.h>
>>> +#include <linux/i2c.h>
>>> +#include <linux/random.h>
>>> +
>>> +#include "intel_drv.h"
>>> +#include "i915_reg.h"
>>> +
>>> +#define KEY_LOAD_TRIES	5
>>> +
>>> +static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
>>> +				    const struct intel_hdcp_shim *shim)
>>> +{
>>> +	unsigned long timeout = jiffies + msecs_to_jiffies_timeout(500);
>>> +	int ret;
>>> +	bool ksv_ready;
>>> +
>>> +	while (true) {
>>> +		ret = shim->read_ksv_ready(intel_dig_port, &ksv_ready);
>>> +		if (ret)
>>> +			return ret;
>>> +		if (ksv_ready)
>>> +			break;
>>> +		if (time_after(jiffies, timeout))
>>> +			return -ETIMEDOUT;
>>> +		msleep(100);
>>> +	}
>>> +	return 0;
>>> +}
>>> +
>>> +static void intel_hdcp_clear_keys(struct drm_i915_private *dev_priv)
>>> +{
>>> +	I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_CLEAR_KEYS_TRIGGER);
>>> +	I915_WRITE(SKL_HDCP_KEY_STATUS,
>>> +		   SKL_HDCP_KEY_LOAD_DONE | SKL_HDCP_KEY_LOAD_STATUS |
>>> +		   SKL_HDCP_FUSE_IN_PROGRESS | SKL_HDCP_FUSE_ERROR |
>>> +		   SKL_HDCP_FUSE_DONE);
>>> +}
>>> +
>>> +static int intel_hdcp_load_keys(struct drm_i915_private *dev_priv)
>>> +{
>>> +	unsigned long timeout;
>>> +	int ret;
>>> +	u32 val;
>>> +
>>> +	// Initiate loading the HDCP key from fuses
>>> +	mutex_lock(&dev_priv->pcu_lock);
>>> +	ret = sandybridge_pcode_write(dev_priv, SKL_PCODE_LOAD_HDCP_KEYS, 1);
>>> +	mutex_unlock(&dev_priv->pcu_lock);
>>> +	if (ret) {
>>> +		DRM_ERROR("Failed to initiate HDCP key load (%d)\n", ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	// Wait for the keys to load (500us)
>>> +	timeout = jiffies + nsecs_to_jiffies_timeout(500 * 1000);
>>> +	while (true) {
>>> +		val = I915_READ(SKL_HDCP_KEY_STATUS);
>>> +		if (val & SKL_HDCP_KEY_LOAD_DONE)
>>> +			break;
>>> +		if (time_after(jiffies, timeout))
>>> +			return -ETIMEDOUT;
>>> +		usleep_range(50, 100);
>>> +	}
>>> +	if (!(val & SKL_HDCP_KEY_LOAD_STATUS))
>>> +		return -ENXIO;
>>> +
>>> +	// Send Aksv over to PCH display for use in authentication
>>> +	I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_AKSV_SEND_TRIGGER);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/* Returns updated SHA-1 index */
>>> +static int intel_write_sha_text(struct drm_i915_private *dev_priv, u32 sha_text)
>>> +{
>>> +	I915_WRITE(SKL_HDCP_SHA_TEXT, sha_text);
>>> +	if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_READY, 1)) {
>>> +		DRM_ERROR("Timed out waiting for SHA1 ready\n");
>>> +		return -ETIMEDOUT;
>>> +	}
>>> +	return 0;
>>> +}
>>> +
>>> +static
>>> +u32 intel_hdcp_get_repeater_ctl(struct intel_digital_port *intel_dig_port)
>>> +{
>>> +	enum port port = intel_dig_port->port;
>>> +	switch(port) {
>>> +	case PORT_A:
>>> +		return SKL_HDCP_DDIA_REP_PRESENT | SKL_HDCP_DDIA_SHA1_M0;
>>> +	case PORT_B:
>>> +		return SKL_HDCP_DDIB_REP_PRESENT | SKL_HDCP_DDIB_SHA1_M0;
>>> +	case PORT_C:
>>> +		return SKL_HDCP_DDIC_REP_PRESENT | SKL_HDCP_DDIC_SHA1_M0;
>>> +	case PORT_D:
>>> +		return SKL_HDCP_DDID_REP_PRESENT | SKL_HDCP_DDID_SHA1_M0;
>>> +	case PORT_E:
>>> +		return SKL_HDCP_DDIE_REP_PRESENT | SKL_HDCP_DDIE_SHA1_M0;
>>> +	default:
>>> +		break;
>>> +	}
>>> +	DRM_ERROR("Unknown port %d\n", port);
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +/* Implements Part 2 of the HDCP authorization procedure */
>>> +static
>>> +int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
>>> +			       const struct intel_hdcp_shim *shim)
>>> +{
>>> +	struct drm_i915_private *dev_priv;
>>> +	u32 vprime, sha_text, sha_leftovers, rep_ctl;
>>> +	u8 bstatus[2], num_downstream, *ksv_fifo;
>>> +	int ret, i, j, sha_idx;
>>> +
>>> +	dev_priv = intel_dig_port->base.base.dev->dev_private;
>>> +
>>> +	ret = shim->read_bstatus(intel_dig_port, bstatus);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	/* If there are no downstream devices, we're all done. */
>>> +	num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
>>> +	if (num_downstream == 0) {
>>> +		DRM_INFO("HDCP is enabled (no downstream devices)\n");
>>> +		return 0;
>>> +	}
>>> +
>>> +	// Poll for ksv list ready (spec says max time allowed is 5s)
>>> +	ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim);
>>> +	if (ret) {
>>> +		DRM_ERROR("KSV list failed to become ready (%d)\n", ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL);
>>> +	if (!ksv_fifo)
>>> +		return -ENOMEM;
>>> +
>>> +	ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	// Process V' values from the receiver
>>> +	for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
>>> +		ret = shim->read_v_prime_part(intel_dig_port, i, &vprime);
>>> +		if (ret)
>>> +			return ret;
>>> +		I915_WRITE(SKL_HDCP_SHA_V_PRIME(i), vprime);
>>> +	}
>>> +
>>> +	/*
>>> +	 * We need to write the concatenation of all device KSVs, BINFO (DP) ||
>>> +	 * BSTATUS (HDMI), and M0 (which is added via HDCP_REP_CTL). This byte
>>> +	 * stream is written via the HDCP_SHA_TEXT register in 32-bit
>>> +	 * increments. Every 64 bytes, we need to write HDCP_REP_CTL again. This
>>> +	 * index will keep track of our progress through the 64 bytes as well as
>>> +	 * helping us work the 40-bit KSVs through our 32-bit register.
>>> +	 *
>>> +	 * NOTE: data passed via HDCP_SHA_TEXT should be big-endian
>>> +	 */
>>> +	sha_idx = 0;
>>> +	sha_text = 0;
>>> +	sha_leftovers = 0;
>>> +	rep_ctl = intel_hdcp_get_repeater_ctl(intel_dig_port);
>>> +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>> +	for (i = 0; i < num_downstream; i++) {
>>> +		unsigned sha_empty;
>>> +		u8 *ksv = &ksv_fifo[i * DRM_HDCP_KSV_LEN];
>>> +
>>> +		// Fill up the empty slots in sha_text and write it out
>>> +		sha_empty = sizeof(sha_text) - sha_leftovers;
>>> +		for (j = 0; j < sha_empty; j++)
>>> +			sha_text |= ksv[j] << ((sizeof(sha_text) - j - 1) * 8);
>>> +
>>> +		ret = intel_write_sha_text(dev_priv, sha_text);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +
>>> +		// Programming guide writes this every 64 bytes
>>> +		sha_idx += sizeof(sha_text);
>>> +		if (!(sha_idx % 64))
>>> +			I915_WRITE(SKL_HDCP_REP_CTL,
>>> +				   rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>> +
>>> +		// Store the leftover bytes from the ksv in sha_text
>>> +		sha_leftovers = DRM_HDCP_KSV_LEN - sha_empty;
>>> +		sha_text = 0;
>>> +		for (j = 0; j < sha_leftovers; j++)
>>> +			sha_text |= ksv[sha_empty + j] <<
>>> +					((sizeof(sha_text) - j - 1) * 8);
>>> +
>>> +		/*
>>> +		 * If we still have room in sha_text for more data, continue.
>>> +		 * Otherwise, write it out immediately.
>>> +		 */
>>> +		if (sizeof(sha_text) > sha_leftovers)
>>> +			continue;
>>> +
>>> +		ret = intel_write_sha_text(dev_priv, sha_text);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_leftovers = 0;
>>> +		sha_text = 0;
>>> +		sha_idx += sizeof(sha_text);
>>> +	}
>>> +
>>> +	/*
>>> +	 * We need to write BINFO/BSTATUS, and M0 now. Depending on how many
>>> +	 * bytes are leftover from the last ksv, we might be able to fit them
>>> +	 * all in sha_text (first 2 cases), or we might need to split them up
>>> +	 * into 2 writes (last 2 cases).
>>> +	 */
>>> +	if (sha_leftovers == 0) {
>>> +		// Write 16 bits of text, 16 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
>>> +		ret = intel_write_sha_text(dev_priv,
>>> +					   bstatus[0] << 8 | bstatus[1]);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +		// Write 32 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>>> +		ret = intel_write_sha_text(dev_priv, 0);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +		// Write 16 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
>>> +		ret = intel_write_sha_text(dev_priv, 0);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +	} else if (sha_leftovers == 1) {
>>> +		// Write 24 bits of text, 8 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
>>> +		sha_text |= bstatus[0] << 16 | bstatus[1] << 8;
>>> +		// Only 24-bits of data, must be in the LSB
>>> +		sha_text = (sha_text & 0xffffff00) >> 8;
>>> +		ret = intel_write_sha_text(dev_priv, sha_text);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +		// Write 32 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>>> +		ret = intel_write_sha_text(dev_priv, 0);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +		// Write 24 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
>>> +		ret = intel_write_sha_text(dev_priv, 0);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +	} else if (sha_leftovers == 2) {
>>> +		// Write 32 bits of text
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>> +		sha_text |= bstatus[0] << 24 | bstatus[1] << 16;
>>> +		ret = intel_write_sha_text(dev_priv, sha_text);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +		// Write 64 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>>> +		for (i = 0; i < 2; i++) {
>>> +			ret = intel_write_sha_text(dev_priv, 0);
>>> +			if (ret < 0)
>>> +				return ret;
>>> +			sha_idx += sizeof(sha_text);
>>> +		}
>>> +	} else if (sha_leftovers == 3) {
>>> +		// Write 32 bits of text
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>> +		sha_text |= bstatus[0] << 24;
>>> +		ret = intel_write_sha_text(dev_priv, sha_text);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +		// Write 8 bits of text, 24 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
>>> +		ret = intel_write_sha_text(dev_priv, bstatus[1]);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +		// Write 32 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>>> +		ret = intel_write_sha_text(dev_priv, 0);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +		// Write 8 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
>>> +		ret = intel_write_sha_text(dev_priv, 0);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +	} else {
>>> +		DRM_ERROR("Invalid number of leftovers %d\n", sha_leftovers);
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>> +	// Fill up to 64 - 4 bytes with zeros (leave the last write for length)
>>> +	while ((sha_idx % 64) < (64 - sizeof(sha_text))) {
>>> +		ret = intel_write_sha_text(dev_priv, 0);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +	}
>>> +
>>> +	/*
>>> +	 * Last write gets the length of the concatenation in bits. That is:
>>> +	 *  - 5 bytes per device
>>> +	 *  - 10 bytes for BINFO/BSTATUS(2), M0(8)
>>> +	 */
>>> +	sha_text = (num_downstream * 5 + 10) * 8;
>>> +	ret = intel_write_sha_text(dev_priv, sha_text);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	// Finally, tell the HW we're done with the hash and wait for it to ACK
>>> +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_COMPLETE_HASH);
>>> +	if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_COMPLETE, 1)) {
>>> +		DRM_ERROR("Timed out waiting for SHA1 complete\n");
>>> +		return -ETIMEDOUT;
>>> +	}
>>> +	if (!(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_V_MATCH)) {
>>> +		DRM_ERROR("SHA-1 mismatch, HDCP failed\n");
>>> +		return -ENXIO;
>>> +	}
>>> +
>>> +	DRM_INFO("HDCP is enabled (%d downstream devices)\n", num_downstream);
>>> +	return 0;
>>> +}
>>> +
>>> +/* Implements Part 1 of the HDCP authorization procedure */
>>> +static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
>>> +			   const struct intel_hdcp_shim *shim)
>>> +{
>>> +	struct drm_i915_private *dev_priv;
>>> +	enum port port;
>>> +	unsigned long r0_prime_gen_start;
>>> +	int ret, i;
>>> +	union {
>>> +		u32 reg[2];
>>> +		u8 shim[DRM_HDCP_AN_LEN];
>>> +	} an;
>>> +	union {
>>> +		u32 reg[2];
>>> +		u8 shim[DRM_HDCP_KSV_LEN];
>>> +	} bksv;
>>> +	union {
>>> +		u32 reg;
>>> +		u8 shim[DRM_HDCP_RI_LEN];
>>> +	} ri;
>>> +	bool repeater_present;
>>> +
>>> +	dev_priv = intel_dig_port->base.base.dev->dev_private;
>>> +
>>> +	port = intel_dig_port->port;
>>> +
>>> +	// Initialize An with 2 random values and acquire it
>>> +	for (i = 0; i < 2; i++)
>>> +		I915_WRITE(SKL_PORT_HDCP_ANINIT(port), get_random_long());
>>> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_CAPTURE_AN);
>>> +
>>> +	// Wait for An to be acquired
>>> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>>> +		     SKL_HDCP_STATUS_AN_READY, 1)) {
>>> +		DRM_ERROR("Timed out waiting for An\n");
>>> +		return -ETIMEDOUT;
>>> +	}
>>> +
>>> +	an.reg[0] = I915_READ(SKL_PORT_HDCP_ANLO(port));
>>> +	an.reg[1] = I915_READ(SKL_PORT_HDCP_ANHI(port));
>>> +	ret = shim->write_an_aksv(intel_dig_port, an.shim);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	r0_prime_gen_start = jiffies;
>>> +
>>> +	memset(&bksv, 0, sizeof(bksv));
>>> +	ret = shim->read_bksv(intel_dig_port, bksv.shim);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	I915_WRITE(SKL_PORT_HDCP_BKSVLO(port), bksv.reg[0]);
>>> +	I915_WRITE(SKL_PORT_HDCP_BKSVHI(port), bksv.reg[1]);
>>> +
>>> +	ret = shim->repeater_present(intel_dig_port, &repeater_present);
>>> +	if (ret)
>>> +		return ret;
>>> +	if (repeater_present)
>>> +		I915_WRITE(SKL_HDCP_REP_CTL,
>>> +			   intel_hdcp_get_repeater_ctl(intel_dig_port));
>>> +
>>> +	ret = shim->toggle_signalling(intel_dig_port, true);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_AUTH_AND_ENC);
>>> +
>>> +	// Wait for R0 ready
>>> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>>> +		     (SKL_HDCP_STATUS_R0_READY | SKL_HDCP_STATUS_ENC), 1)) {
>>> +		DRM_ERROR("Timed out waiting for R0 ready\n");
>>> +		return -ETIMEDOUT;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Wait for R0' to become available, the spec says 100ms from Aksv
>>> +	 * write. On DP, there's an R0_READY bit available but no such bit
>>> +	 * exists on HDMI. Since the upper-bound is the same, we'll just do
>>> +	 * the stupid thing instead of polling on one and not the other.
>>> +	 */
>>> +	wait_remaining_ms_from_jiffies(r0_prime_gen_start, 100);
>>> +
>>> +	ri.reg = 0;
>>> +	ret = shim->read_ri_prime(intel_dig_port, ri.shim);
>>> +	if (ret)
>>> +		return ret;
>>> +	I915_WRITE(SKL_PORT_HDCP_RPRIME(port), ri.reg);
>>> +
>>> +	// Wait for Ri prime match
>>> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>>> +		     (SKL_HDCP_STATUS_RI_MATCH | SKL_HDCP_STATUS_ENC), 1)) {
>>> +		DRM_ERROR("Timed out waiting for Ri prime match (%x)\n",
>>> +			  I915_READ(SKL_PORT_HDCP_STATUS(port)));
>>> +		return -ETIMEDOUT;
>>> +	}
>>> +
>>> +	// Wait for encryption confirmation
>>> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>>> +		      SKL_HDCP_STATUS_ENC, 20)) {
>>> +		DRM_ERROR("Timed out waiting for encryption\n");
>>> +		return -ETIMEDOUT;
>>> +	}
>>> +
>>> +	/*
>>> +	 * XXX: If we have MST-connected devices, we need to enable encryption
>>> +	 * on those as well.
>>> +	 */
>>> +
>>> +	return intel_hdcp_auth_downstream(intel_dig_port, shim);
>>> +}
>>> +
>>> +static
>>> +struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector)
>>> +{
>>> +	return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base);
>>> +}
>>> +
>>> +static int _intel_hdcp_disable(struct intel_connector *connector)
>>> +{
>>> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>> +	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
>>> +	enum port port = intel_dig_port->port;
>>> +	int ret;
>>> +
>>> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
>>> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) == 0, 20)) {
>>> +		DRM_ERROR("Failed to disable HDCP, timeout clearing status\n");
>>> +		return -ETIMEDOUT;
>>> +	}
>>> +
>>> +	intel_hdcp_clear_keys(dev_priv);
>>> +
>>> +	ret = connector->hdcp_shim->toggle_signalling(intel_dig_port, false);
>>> +	if (ret) {
>>> +		DRM_ERROR("Failed to disable HDCP signalling\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	DRM_INFO("HDCP is disabled\n");
>>> +	return 0;
>>> +}
>>> +
>>> +static int _intel_hdcp_enable(struct intel_connector *connector)
>>> +{
>>> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>> +	int i, ret;
>>> +
>>> +	if (!(I915_READ(SKL_FUSE_STATUS) & SKL_FUSE_PG_DIST_STATUS(1))) {
>>> +		DRM_ERROR("PG1 is disabled, cannot load keys\n");
>>> +		return -ENXIO;
>>> +	}
>>> +
>>> +	for (i = 0; i < KEY_LOAD_TRIES; i++) {
>>> +		ret = intel_hdcp_load_keys(dev_priv);
>>> +		if (!ret)
>>> +			break;
>>> +		intel_hdcp_clear_keys(dev_priv);
>>> +	}
>>> +	if (ret) {
>>> +		DRM_ERROR("Could not load HDCP keys, (%d)\n", ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = intel_hdcp_auth(conn_to_dig_port(connector),
>>> +			      connector->hdcp_shim);
>>> +	if (ret) {
>>> +		DRM_ERROR("Failed to authenticate HDCP (%d)\n", ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +void intel_hdcp_work(struct work_struct *work)
>>> +{
>>> +	struct intel_connector *connector = container_of(to_delayed_work(work),
>>> +							 struct intel_connector,
>>> +						         hdcp_work);
>>> +	struct drm_device *dev = connector->base.dev;
>>> +	int ret;
>>> +
>>> +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>>> +
>>> +	ret = intel_hdcp_check_link(connector);
>>> +	if (!ret)
>>> +		schedule_delayed_work(&connector->hdcp_work,
>>> +				      DRM_HDCP_CHECK_PERIOD_MS);
>>> +
>>> +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
>>> +}
>>> +
>>> +int intel_hdcp_enable(struct intel_connector *connector)
>>> +{
>>> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>> +	struct drm_device *dev = &dev_priv->drm;
>>> +	struct drm_connector_state *state = connector->base.state;
>>> +	int ret;
>>> +
>>> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>>> +
>>> +	if (!connector->hdcp_shim)
>>> +		return -ENOENT;
>>> +
>>> +	ret = _intel_hdcp_enable(connector);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
>>> +
>>> +	schedule_delayed_work(&connector->hdcp_work, DRM_HDCP_CHECK_PERIOD_MS);
>>> +	return 0;
>>> +}
>>> +
>>> +int intel_hdcp_disable(struct intel_connector *connector)
>>> +{
>>> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>> +	struct drm_device *dev = &dev_priv->drm;
>>> +
>>> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>>> +
>>> +	if (!connector->hdcp_shim)
>>> +		return -ENOENT;
>>> +
>>> +	cancel_delayed_work(&connector->hdcp_work);
>>> +
>>> +	return _intel_hdcp_disable(connector);
>>> +}
>>> +
>>> +/* Implements Part 3 of the HDCP authorization procedure */
>>> +int intel_hdcp_check_link(struct intel_connector *connector)
>>> +{
>>> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>> +	struct drm_device *dev = &dev_priv->drm;
>>> +	struct drm_connector_state *state = connector->base.state;
>>> +	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
>>> +	enum port port = intel_dig_port->port;
>>> +	int ret;
>>> +
>>> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>>> +
>>> +	if (state->content_protection != DRM_MODE_CONTENT_PROTECTION_ENABLED)
>>> +		return 0;
>>> +
>>> +	if (!connector->hdcp_shim)
>>> +		return -ENOENT;
>>> +
>>> +	if (!(I915_READ(SKL_PORT_HDCP_STATUS(port)) & SKL_HDCP_STATUS_ENC)) {
>>> +		DRM_ERROR("HDCP check failed: link is not encrypted, %x\n",
>>> +			   I915_READ(SKL_PORT_HDCP_STATUS(port)));
>>> +		ret = -ENXIO;
>>> +		goto fail;
>>> +	}
>>> +
>>> +	if (connector->hdcp_shim->check_link(intel_dig_port))
>>> +		return 0;
>>> +
>>> +	DRM_INFO("HDCP link failed, retrying authentication\n");
>>> +
>>> +	ret = _intel_hdcp_disable(connector);
>>> +	if (ret) {
>>> +		DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
>>> +		goto fail;
>>> +	}
>>> +
>>> +	ret = _intel_hdcp_enable(connector);
>>> +	if (ret) {
>>> +		DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
>>> +		goto fail;
>>> +	}
>>> +
>>> +	return 0;
>>> +
>>> +fail:
>>> +	state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
>>> +	return ret;
>>> +}
>> _______________________________________________
>> Intel-gfx mailing list
>> Intel-gfx@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Intel-gfx] [RFC PATCH 3/6] drm/i915: Add HDCP framework + base implementation
@ 2017-12-01  8:36         ` Ramalingam C
  0 siblings, 0 replies; 62+ messages in thread
From: Ramalingam C @ 2017-12-01  8:36 UTC (permalink / raw)
  To: Sean Paul, dri-devel, intel-gfx, David Airlie, linux-kernel,
	Rodrigo Vivi



On Friday 01 December 2017 01:06 PM, Daniel Vetter wrote:
> On Fri, Dec 01, 2017 at 12:53:31PM +0530, Ramalingam C wrote:
>> Sean,
>>
>> IMHO, it will good if we can have all generic hdcp1.4 authentication flow in
>> drm helpers and all interested display drivers to use them.
>>
>> This Design will make the extending of hdcp easy for other display drivers
>> based on DRM.
>>
>> We can have the required drm_hdcp_shim type of implementation at drm
>> structure which will be called for platform specific operations (like
>> prepare an, send aksv, program bksv/repeater/r0 and verify sha1 etc)?
> I discussed this exact question with Sean Paul, and apparently the
> hardware designs are too diverse to make shared code much useful. Some hw
> has the entire hdcp flow in hw, some almost nothing (like i915 here), and
> then there's everything in between.
Just trying to understand the other extreme of HW (full)support for HDCP 
here.

When you say everything about HDCP is implemented in HW, do you mean 
that whole protocol comm on HDCP link also driven by HW?

--Ram
>
> Given that Sean has seen a lot more hdcp implementations than we have,
> that we right now have no other implementation than i915 in upstream and
> than wrong abstraction is much harder to fix than no abstraction I'm going
> with Sean's approach of "no generic abstraction" here. Personally I'm not
> even fully sold on the shim abstraction, but I think by&large that one is
> fine.
>
>> On Thursday 30 November 2017 08:38 AM, Sean Paul wrote:
>>> This patch adds the framework required to add HDCP support to intel
>>> connectors. It implements Aksv loading from fuse, and parts 1/2/3
>>> of the HDCP authentication scheme.
>>>
>>> Note that without shim implementations, this does not actually implement
>>> HDCP. That will come in subsequent patches.
>>>
>>> Signed-off-by: Sean Paul <seanpaul@chromium.org>
>>> ---
>>>    drivers/gpu/drm/i915/Makefile       |   1 +
>>>    drivers/gpu/drm/i915/i915_reg.h     |  83 +++++
>>>    drivers/gpu/drm/i915/intel_atomic.c |  26 +-
>>>    drivers/gpu/drm/i915/intel_ddi.c    |  14 +
>>>    drivers/gpu/drm/i915/intel_drv.h    |  53 +++
>>>    drivers/gpu/drm/i915/intel_hdcp.c   | 636 ++++++++++++++++++++++++++++++++++++
>>>    6 files changed, 811 insertions(+), 2 deletions(-)
>>>    create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c
>>>
>>> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
>>> index 6c3b0481ef82..1e745508e437 100644
>>> --- a/drivers/gpu/drm/i915/Makefile
>>> +++ b/drivers/gpu/drm/i915/Makefile
>>> @@ -87,6 +87,7 @@ i915-y += intel_audio.o \
>>>    	  intel_fbc.o \
>>>    	  intel_fifo_underrun.o \
>>>    	  intel_frontbuffer.o \
>>> +	  intel_hdcp.o \
>>>    	  intel_hotplug.o \
>>>    	  intel_modes.o \
>>>    	  intel_overlay.o \
>>> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
>>> index 68a58cce6ab1..43128030171d 100644
>>> --- a/drivers/gpu/drm/i915/i915_reg.h
>>> +++ b/drivers/gpu/drm/i915/i915_reg.h
>>> @@ -7991,6 +7991,7 @@ enum {
>>>    #define     GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT	8
>>>    #define     GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT	16
>>>    #define     GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT	24
>>> +#define   SKL_PCODE_LOAD_HDCP_KEYS		0x5
>>>    #define   SKL_PCODE_CDCLK_CONTROL		0x7
>>>    #define     SKL_CDCLK_PREPARE_FOR_CHANGE	0x3
>>>    #define     SKL_CDCLK_READY_FOR_CHANGE		0x1
>>> @@ -8285,6 +8286,88 @@ enum skl_power_gate {
>>>    #define  SKL_PW_TO_PG(pw)			((pw) - SKL_DISP_PW_1 + SKL_PG1)
>>>    #define  SKL_FUSE_PG_DIST_STATUS(pg)		(1 << (27 - (pg)))
>>> +
>>> +/* HDCP Key Registers */
>>> +#define SKL_HDCP_KEY_CONF		_MMIO(0x66c00)
>>> +#define	 SKL_HDCP_AKSV_SEND_TRIGGER	BIT(31)
>>> +#define  SKL_HDCP_CLEAR_KEYS_TRIGGER	BIT(30)
>>> +#define SKL_HDCP_KEY_STATUS		_MMIO(0x66c04)
>>> +#define  SKL_HDCP_FUSE_IN_PROGRESS	BIT(7)
>>> +#define  SKL_HDCP_FUSE_ERROR		BIT(6)
>>> +#define  SKL_HDCP_FUSE_DONE		BIT(5)
>>> +#define  SKL_HDCP_KEY_LOAD_STATUS	BIT(1)
>>> +#define  SKL_HDCP_KEY_LOAD_DONE		BIT(0)
>>> +#define SKL_HDCP_AKSV_LO		_MMIO(0x66c10)
>>> +#define SKL_HDCP_AKSV_HI		_MMIO(0x66c14)
>>> +
>>> +/* HDCP Repeater Registers */
>>> +#define SKL_HDCP_REP_CTL		_MMIO(0x66d00)
>>> +#define  SKL_HDCP_DDIB_REP_PRESENT	BIT(30)
>>> +#define  SKL_HDCP_DDIA_REP_PRESENT	BIT(29)
>>> +#define  SKL_HDCP_DDIC_REP_PRESENT	BIT(28)
>>> +#define  SKL_HDCP_DDID_REP_PRESENT	BIT(27)
>>> +#define  SKL_HDCP_DDIF_REP_PRESENT	BIT(26)
>>> +#define  SKL_HDCP_DDIE_REP_PRESENT	BIT(25)
>>> +#define  SKL_HDCP_DDIB_SHA1_M0		(1 << 20)
>>> +#define  SKL_HDCP_DDIA_SHA1_M0		(2 << 20)
>>> +#define  SKL_HDCP_DDIC_SHA1_M0		(3 << 20)
>>> +#define  SKL_HDCP_DDID_SHA1_M0		(4 << 20)
>>> +#define  SKL_HDCP_DDIF_SHA1_M0		(5 << 20)
>>> +#define  SKL_HDCP_DDIE_SHA1_M0		(6 << 20) // Bspec says 5?
>>> +#define  SKL_HDCP_SHA1_BUSY		BIT(16)
>>> +#define  SKL_HDCP_SHA1_READY		BIT(17)
>>> +#define  SKL_HDCP_SHA1_COMPLETE		BIT(18)
>>> +#define  SKL_HDCP_SHA1_V_MATCH		BIT(19)
>>> +#define  SKL_HDCP_SHA1_TEXT_32		(1 << 1)
>>> +#define  SKL_HDCP_SHA1_COMPLETE_HASH	(2 << 1)
>>> +#define  SKL_HDCP_SHA1_TEXT_24		(4 << 1)
>>> +#define  SKL_HDCP_SHA1_TEXT_16		(5 << 1)
>>> +#define  SKL_HDCP_SHA1_TEXT_8		(6 << 1)
>>> +#define  SKL_HDCP_SHA1_TEXT_0		(7 << 1)
>>> +#define SKL_HDCP_SHA_V_PRIME_H0		_MMIO(0x66d04)
>>> +#define SKL_HDCP_SHA_V_PRIME_H1		_MMIO(0x66d08)
>>> +#define SKL_HDCP_SHA_V_PRIME_H2		_MMIO(0x66d0C)
>>> +#define SKL_HDCP_SHA_V_PRIME_H3		_MMIO(0x66d10)
>>> +#define SKL_HDCP_SHA_V_PRIME_H4		_MMIO(0x66d14)
>>> +#define SKL_HDCP_SHA_V_PRIME(h)		_MMIO((0x66d04 + h * 4))
>>> +#define SKL_HDCP_SHA_TEXT		_MMIO(0x66d18)
>>> +
>>> +/* HDCP Auth Registers */
>>> +#define _SKL_PORTA_HDCP_AUTHENC		0x66800
>>> +#define _SKL_PORTB_HDCP_AUTHENC		0x66500
>>> +#define _SKL_PORTC_HDCP_AUTHENC		0x66600
>>> +#define _SKL_PORTD_HDCP_AUTHENC		0x66700
>>> +#define _SKL_PORTE_HDCP_AUTHENC		0x66A00
>>> +#define _SKL_PORTF_HDCP_AUTHENC		0x66900
>>> +#define _SKL_PORT_HDCP_AUTHENC(port, x)	_MMIO(_PICK(port, \
>>> +					  _SKL_PORTA_HDCP_AUTHENC, \
>>> +					  _SKL_PORTB_HDCP_AUTHENC, \
>>> +					  _SKL_PORTC_HDCP_AUTHENC, \
>>> +					  _SKL_PORTD_HDCP_AUTHENC, \
>>> +					  _SKL_PORTE_HDCP_AUTHENC, \
>>> +					  _SKL_PORTF_HDCP_AUTHENC) + x)
>>> +#define SKL_PORT_HDCP_CONF(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x0)
>>> +#define  SKL_HDCP_CONF_CAPTURE_AN	BIT(0)
>>> +#define  SKL_HDCP_CONF_AUTH_AND_ENC	(BIT(1) | BIT(0))
>>> +#define SKL_PORT_HDCP_ANINIT(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x4)
>>> +#define SKL_PORT_HDCP_ANLO(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x8)
>>> +#define SKL_PORT_HDCP_ANHI(port)	_SKL_PORT_HDCP_AUTHENC(port, 0xC)
>>> +#define SKL_PORT_HDCP_BKSVLO(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x10)
>>> +#define SKL_PORT_HDCP_BKSVHI(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x14)
>>> +#define SKL_PORT_HDCP_RPRIME(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x18)
>>> +#define SKL_PORT_HDCP_STATUS(port)	_SKL_PORT_HDCP_AUTHENC(port, 0x1C)
>>> +#define  SKL_HDCP_STATUS_STREAM_A_ENC	BIT(31)
>>> +#define  SKL_HDCP_STATUS_STREAM_B_ENC	BIT(30)
>>> +#define  SKL_HDCP_STATUS_STREAM_C_ENC	BIT(29)
>>> +#define  SKL_HDCP_STATUS_STREAM_D_ENC	BIT(28)
>>> +#define  SKL_HDCP_STATUS_AUTH		BIT(21)
>>> +#define  SKL_HDCP_STATUS_ENC		BIT(20)
>>> +#define  SKL_HDCP_STATUS_RI_MATCH	BIT(19)
>>> +#define  SKL_HDCP_STATUS_R0_READY	BIT(18)
>>> +#define  SKL_HDCP_STATUS_AN_READY	BIT(17)
>>> +#define  SKL_HDCP_STATUS_CIPHER		BIT(16)
>>> +#define  SKL_HDCP_STATUS_FRAME_CNT(x)	((x >> 8) & 0xff)
>>> +
>>>    /* Per-pipe DDI Function Control */
>>>    #define _TRANS_DDI_FUNC_CTL_A		0x60400
>>>    #define _TRANS_DDI_FUNC_CTL_B		0x61400
>>> diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
>>> index 36d4e635e4ce..ddf08227d9cb 100644
>>> --- a/drivers/gpu/drm/i915/intel_atomic.c
>>> +++ b/drivers/gpu/drm/i915/intel_atomic.c
>>> @@ -109,12 +109,34 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
>>>    	struct intel_digital_connector_state *old_conn_state =
>>>    		to_intel_digital_connector_state(old_state);
>>>    	struct drm_crtc_state *crtc_state;
>>> -
>>> -	if (!new_state->crtc)
>>> +	uint64_t old_cp = old_conn_state->base.content_protection;
>>> +	uint64_t new_cp = new_state->content_protection;
>>> +
>>> +	if (!new_state->crtc) {
>>> +		/*
>>> +		 * If the connector is being disabled with CP enabled, mark it
>>> +		 * desired so it's re-enabled when the connector is brought back
>>> +		 */
>>> +		if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
>>> +			new_state->content_protection =
>>> +				DRM_MODE_CONTENT_PROTECTION_DESIRED;
>>>    		return 0;
>>> +	}
>>>    	crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc);
>>> +	if (new_cp != old_cp) {
>>> +		/* Only drivers can set content protection enabled */
>>> +		if (new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
>>> +			new_state->content_protection =
>>> +				DRM_MODE_CONTENT_PROTECTION_DESIRED;
>>> +
>>> +		/* Involve the encoder/connector to enable/disable CP */
>>> +		if (new_cp == DRM_MODE_CONTENT_PROTECTION_OFF ||
>>> +		    old_cp == DRM_MODE_CONTENT_PROTECTION_OFF)
>>> +			crtc_state->mode_changed = true;
>> We need not perform the mode set for hdcp enable/disable.
>> Authentication and encryption can be started on active port.
> Was simpler to implement this way :-) We can fix this by pushing the hdcp
> enable/disable code into a post-modeset operation. Ville had written the
> infrastructure for that to fix a few fastboot corner cases. But that
> infrastructure hasn't landed yet, so probably better to do that in a
> follow-up.
>
> I also guess that CrOS simply set this to desired every time they enable
> an external screen, so it won't result in an unecessary modeset.
>
>>> +	}
>>> +
>>>    	/*
>>>    	 * These properties are handled by fastset, and might not end
>>>    	 * up in a modeset.
>>> diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
>>> index 933c18fd4258..0e69337f410d 100644
>>> --- a/drivers/gpu/drm/i915/intel_ddi.c
>>> +++ b/drivers/gpu/drm/i915/intel_ddi.c
>>> @@ -2432,10 +2432,17 @@ static void intel_enable_ddi(struct intel_encoder *encoder,
>>>    			     const struct intel_crtc_state *crtc_state,
>>>    			     const struct drm_connector_state *conn_state)
>>>    {
>>> +	struct drm_connector *connector = conn_state->connector;
>>> +	struct intel_connector *intel_connector = to_intel_connector(connector);
>>> +
>>>    	if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
>>>    		intel_enable_ddi_hdmi(encoder, crtc_state, conn_state);
>>>    	else
>>>    		intel_enable_ddi_dp(encoder, crtc_state, conn_state);
>>> +
>>> +	if (conn_state->content_protection ==
>>> +			DRM_MODE_CONTENT_PROTECTION_DESIRED)
>>> +		intel_hdcp_enable(intel_connector);
>>>    }
>>>    static void intel_disable_ddi_dp(struct intel_encoder *encoder,
>>> @@ -2468,10 +2475,17 @@ static void intel_disable_ddi(struct intel_encoder *encoder,
>>>    			      const struct intel_crtc_state *old_crtc_state,
>>>    			      const struct drm_connector_state *old_conn_state)
>>>    {
>>> +	struct drm_connector *connector = old_conn_state->connector;
>>> +	struct intel_connector *intel_connector = to_intel_connector(connector);
>>> +
>>>    	if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
>>>    		intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state);
>>>    	else
>>>    		intel_disable_ddi_dp(encoder, old_crtc_state, old_conn_state);
>>> +
>>> +	if (old_conn_state->content_protection !=
>>> +			DRM_MODE_CONTENT_PROTECTION_OFF)
>>> +		intel_hdcp_disable(intel_connector);
>> We might want to disable the hdcp before disabling the DDI. Actually we
>> could trigger hdcp disable at connector state change(due to hot-unplug)
>> also.
> Yeah this part needs to be reworked a bit, also because the locking
> doesn't work yet. I think hot-unplug is handled by the worker thread
> already, it does the mandatory regular polling.
> -Daniel
>
>> Thanks,
>> --Ram
>>>    }
>>>    static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder,
>>> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
>>> index 47d022d48718..8924004575b8 100644
>>> --- a/drivers/gpu/drm/i915/intel_drv.h
>>> +++ b/drivers/gpu/drm/i915/intel_drv.h
>>> @@ -299,6 +299,49 @@ struct intel_panel {
>>>    	} backlight;
>>>    };
>>> +struct intel_hdcp_shim {
>>> +	/* Outputs the transmitter's An and Aksv values to the receiver. */
>>> +	int (*write_an_aksv)(struct intel_digital_port *intel_dig_port, u8 *an);
>>> +
>>> +	/* Reads the receiver's key selection vector */
>>> +	int (*read_bksv)(struct intel_digital_port *intel_dig_port, u8 *bksv);
>>> +
>>> +	/*
>>> +	 * Reads BINFO from DP receivers and BSTATUS from HDMI receivers. The
>>> +	 * definitions are the same in the respective specs, but the names are
>>> +	 * different. Call it BSTATUS since that's the name the HDMI spec
>>> +	 * uses and it was there first.
>>> +	 */
>>> +	int (*read_bstatus)(struct intel_digital_port *intel_dig_port,
>>> +			    u8 *bstatus);
>>> +
>>> +	/* Determines whether a repeater is present downstream */
>>> +	int (*repeater_present)(struct intel_digital_port *intel_dig_port,
>>> +				bool *repeater_present);
>>> +
>>> +	/* Reads the receiver's Ri' value */
>>> +	int (*read_ri_prime)(struct intel_digital_port *intel_dig_port, u8 *ri);
>>> +
>>> +	/* Determines if the receiver's KSV FIFO is ready for consumption */
>>> +	int (*read_ksv_ready)(struct intel_digital_port *intel_dig_port,
>>> +			      bool *ksv_ready);
>>> +
>>> +	/* Reads the ksv fifo for num_downstream devices */
>>> +	int (*read_ksv_fifo)(struct intel_digital_port *intel_dig_port,
>>> +			     int num_downstream, u8 *ksv_fifo);
>>> +
>>> +	/* Reads a 32-bit part of V' from the receiver */
>>> +	int (*read_v_prime_part)(struct intel_digital_port *intel_dig_port,
>>> +				 int i, u32 *part);
>>> +
>>> +	/* Enables HDCP signalling on the port */
>>> +	int (*toggle_signalling)(struct intel_digital_port *intel_dig_port,
>>> +				 bool enable);
>>> +
>>> +	/* Ensures the link is still protected */
>>> +	bool (*check_link)(struct intel_digital_port *intel_dig_port);
>>> +};
>>> +
>>>    struct intel_connector {
>>>    	struct drm_connector base;
>>>    	/*
>>> @@ -330,6 +373,9 @@ struct intel_connector {
>>>    	/* Work struct to schedule a uevent on link train failure */
>>>    	struct work_struct modeset_retry_work;
>>> +
>>> +	const struct intel_hdcp_shim *hdcp_shim;
>>> +	struct delayed_work hdcp_work;
>>>    };
>>>    struct intel_digital_connector_state {
>>> @@ -1295,6 +1341,8 @@ void intel_ddi_set_vc_payload_alloc(const struct intel_crtc_state *crtc_state,
>>>    				    bool state);
>>>    u32 bxt_signal_levels(struct intel_dp *intel_dp);
>>>    uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
>>> +int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder);
>>> +int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder);
>>>    u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
>>>    unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
>>> @@ -1746,6 +1794,11 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
>>>    }
>>>    #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
>>> +/* intel_hdcp.c */
>>> +int intel_hdcp_enable(struct intel_connector *connector);
>>> +int intel_hdcp_disable(struct intel_connector *connector);
>>> +int intel_hdcp_check_link(struct intel_connector *connector);
>>> +void intel_hdcp_work(struct work_struct *work);
>>>    /* intel_psr.c */
>>>    void intel_psr_enable(struct intel_dp *intel_dp,
>>> diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
>>> new file mode 100644
>>> index 000000000000..a2a575ed657e
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/i915/intel_hdcp.c
>>> @@ -0,0 +1,636 @@
>>> +/*
>>> + * Copyright (C) 2017 Google, Inc.
>>> + *
>>> + * This software is licensed under the terms of the GNU General Public
>>> + * License version 2, as published by the Free Software Foundation, and
>>> + * may be copied, distributed, and modified under those terms.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + */
>>> +
>>> +#include <drm/drmP.h>
>>> +#include <drm/drm_hdcp.h>
>>> +#include <linux/i2c.h>
>>> +#include <linux/random.h>
>>> +
>>> +#include "intel_drv.h"
>>> +#include "i915_reg.h"
>>> +
>>> +#define KEY_LOAD_TRIES	5
>>> +
>>> +static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
>>> +				    const struct intel_hdcp_shim *shim)
>>> +{
>>> +	unsigned long timeout = jiffies + msecs_to_jiffies_timeout(500);
>>> +	int ret;
>>> +	bool ksv_ready;
>>> +
>>> +	while (true) {
>>> +		ret = shim->read_ksv_ready(intel_dig_port, &ksv_ready);
>>> +		if (ret)
>>> +			return ret;
>>> +		if (ksv_ready)
>>> +			break;
>>> +		if (time_after(jiffies, timeout))
>>> +			return -ETIMEDOUT;
>>> +		msleep(100);
>>> +	}
>>> +	return 0;
>>> +}
>>> +
>>> +static void intel_hdcp_clear_keys(struct drm_i915_private *dev_priv)
>>> +{
>>> +	I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_CLEAR_KEYS_TRIGGER);
>>> +	I915_WRITE(SKL_HDCP_KEY_STATUS,
>>> +		   SKL_HDCP_KEY_LOAD_DONE | SKL_HDCP_KEY_LOAD_STATUS |
>>> +		   SKL_HDCP_FUSE_IN_PROGRESS | SKL_HDCP_FUSE_ERROR |
>>> +		   SKL_HDCP_FUSE_DONE);
>>> +}
>>> +
>>> +static int intel_hdcp_load_keys(struct drm_i915_private *dev_priv)
>>> +{
>>> +	unsigned long timeout;
>>> +	int ret;
>>> +	u32 val;
>>> +
>>> +	// Initiate loading the HDCP key from fuses
>>> +	mutex_lock(&dev_priv->pcu_lock);
>>> +	ret = sandybridge_pcode_write(dev_priv, SKL_PCODE_LOAD_HDCP_KEYS, 1);
>>> +	mutex_unlock(&dev_priv->pcu_lock);
>>> +	if (ret) {
>>> +		DRM_ERROR("Failed to initiate HDCP key load (%d)\n", ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	// Wait for the keys to load (500us)
>>> +	timeout = jiffies + nsecs_to_jiffies_timeout(500 * 1000);
>>> +	while (true) {
>>> +		val = I915_READ(SKL_HDCP_KEY_STATUS);
>>> +		if (val & SKL_HDCP_KEY_LOAD_DONE)
>>> +			break;
>>> +		if (time_after(jiffies, timeout))
>>> +			return -ETIMEDOUT;
>>> +		usleep_range(50, 100);
>>> +	}
>>> +	if (!(val & SKL_HDCP_KEY_LOAD_STATUS))
>>> +		return -ENXIO;
>>> +
>>> +	// Send Aksv over to PCH display for use in authentication
>>> +	I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_AKSV_SEND_TRIGGER);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/* Returns updated SHA-1 index */
>>> +static int intel_write_sha_text(struct drm_i915_private *dev_priv, u32 sha_text)
>>> +{
>>> +	I915_WRITE(SKL_HDCP_SHA_TEXT, sha_text);
>>> +	if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_READY, 1)) {
>>> +		DRM_ERROR("Timed out waiting for SHA1 ready\n");
>>> +		return -ETIMEDOUT;
>>> +	}
>>> +	return 0;
>>> +}
>>> +
>>> +static
>>> +u32 intel_hdcp_get_repeater_ctl(struct intel_digital_port *intel_dig_port)
>>> +{
>>> +	enum port port = intel_dig_port->port;
>>> +	switch(port) {
>>> +	case PORT_A:
>>> +		return SKL_HDCP_DDIA_REP_PRESENT | SKL_HDCP_DDIA_SHA1_M0;
>>> +	case PORT_B:
>>> +		return SKL_HDCP_DDIB_REP_PRESENT | SKL_HDCP_DDIB_SHA1_M0;
>>> +	case PORT_C:
>>> +		return SKL_HDCP_DDIC_REP_PRESENT | SKL_HDCP_DDIC_SHA1_M0;
>>> +	case PORT_D:
>>> +		return SKL_HDCP_DDID_REP_PRESENT | SKL_HDCP_DDID_SHA1_M0;
>>> +	case PORT_E:
>>> +		return SKL_HDCP_DDIE_REP_PRESENT | SKL_HDCP_DDIE_SHA1_M0;
>>> +	default:
>>> +		break;
>>> +	}
>>> +	DRM_ERROR("Unknown port %d\n", port);
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +/* Implements Part 2 of the HDCP authorization procedure */
>>> +static
>>> +int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
>>> +			       const struct intel_hdcp_shim *shim)
>>> +{
>>> +	struct drm_i915_private *dev_priv;
>>> +	u32 vprime, sha_text, sha_leftovers, rep_ctl;
>>> +	u8 bstatus[2], num_downstream, *ksv_fifo;
>>> +	int ret, i, j, sha_idx;
>>> +
>>> +	dev_priv = intel_dig_port->base.base.dev->dev_private;
>>> +
>>> +	ret = shim->read_bstatus(intel_dig_port, bstatus);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	/* If there are no downstream devices, we're all done. */
>>> +	num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
>>> +	if (num_downstream == 0) {
>>> +		DRM_INFO("HDCP is enabled (no downstream devices)\n");
>>> +		return 0;
>>> +	}
>>> +
>>> +	// Poll for ksv list ready (spec says max time allowed is 5s)
>>> +	ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim);
>>> +	if (ret) {
>>> +		DRM_ERROR("KSV list failed to become ready (%d)\n", ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL);
>>> +	if (!ksv_fifo)
>>> +		return -ENOMEM;
>>> +
>>> +	ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	// Process V' values from the receiver
>>> +	for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
>>> +		ret = shim->read_v_prime_part(intel_dig_port, i, &vprime);
>>> +		if (ret)
>>> +			return ret;
>>> +		I915_WRITE(SKL_HDCP_SHA_V_PRIME(i), vprime);
>>> +	}
>>> +
>>> +	/*
>>> +	 * We need to write the concatenation of all device KSVs, BINFO (DP) ||
>>> +	 * BSTATUS (HDMI), and M0 (which is added via HDCP_REP_CTL). This byte
>>> +	 * stream is written via the HDCP_SHA_TEXT register in 32-bit
>>> +	 * increments. Every 64 bytes, we need to write HDCP_REP_CTL again. This
>>> +	 * index will keep track of our progress through the 64 bytes as well as
>>> +	 * helping us work the 40-bit KSVs through our 32-bit register.
>>> +	 *
>>> +	 * NOTE: data passed via HDCP_SHA_TEXT should be big-endian
>>> +	 */
>>> +	sha_idx = 0;
>>> +	sha_text = 0;
>>> +	sha_leftovers = 0;
>>> +	rep_ctl = intel_hdcp_get_repeater_ctl(intel_dig_port);
>>> +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>> +	for (i = 0; i < num_downstream; i++) {
>>> +		unsigned sha_empty;
>>> +		u8 *ksv = &ksv_fifo[i * DRM_HDCP_KSV_LEN];
>>> +
>>> +		// Fill up the empty slots in sha_text and write it out
>>> +		sha_empty = sizeof(sha_text) - sha_leftovers;
>>> +		for (j = 0; j < sha_empty; j++)
>>> +			sha_text |= ksv[j] << ((sizeof(sha_text) - j - 1) * 8);
>>> +
>>> +		ret = intel_write_sha_text(dev_priv, sha_text);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +
>>> +		// Programming guide writes this every 64 bytes
>>> +		sha_idx += sizeof(sha_text);
>>> +		if (!(sha_idx % 64))
>>> +			I915_WRITE(SKL_HDCP_REP_CTL,
>>> +				   rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>> +
>>> +		// Store the leftover bytes from the ksv in sha_text
>>> +		sha_leftovers = DRM_HDCP_KSV_LEN - sha_empty;
>>> +		sha_text = 0;
>>> +		for (j = 0; j < sha_leftovers; j++)
>>> +			sha_text |= ksv[sha_empty + j] <<
>>> +					((sizeof(sha_text) - j - 1) * 8);
>>> +
>>> +		/*
>>> +		 * If we still have room in sha_text for more data, continue.
>>> +		 * Otherwise, write it out immediately.
>>> +		 */
>>> +		if (sizeof(sha_text) > sha_leftovers)
>>> +			continue;
>>> +
>>> +		ret = intel_write_sha_text(dev_priv, sha_text);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_leftovers = 0;
>>> +		sha_text = 0;
>>> +		sha_idx += sizeof(sha_text);
>>> +	}
>>> +
>>> +	/*
>>> +	 * We need to write BINFO/BSTATUS, and M0 now. Depending on how many
>>> +	 * bytes are leftover from the last ksv, we might be able to fit them
>>> +	 * all in sha_text (first 2 cases), or we might need to split them up
>>> +	 * into 2 writes (last 2 cases).
>>> +	 */
>>> +	if (sha_leftovers == 0) {
>>> +		// Write 16 bits of text, 16 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
>>> +		ret = intel_write_sha_text(dev_priv,
>>> +					   bstatus[0] << 8 | bstatus[1]);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +		// Write 32 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>>> +		ret = intel_write_sha_text(dev_priv, 0);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +		// Write 16 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
>>> +		ret = intel_write_sha_text(dev_priv, 0);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +	} else if (sha_leftovers == 1) {
>>> +		// Write 24 bits of text, 8 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
>>> +		sha_text |= bstatus[0] << 16 | bstatus[1] << 8;
>>> +		// Only 24-bits of data, must be in the LSB
>>> +		sha_text = (sha_text & 0xffffff00) >> 8;
>>> +		ret = intel_write_sha_text(dev_priv, sha_text);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +		// Write 32 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>>> +		ret = intel_write_sha_text(dev_priv, 0);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +		// Write 24 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
>>> +		ret = intel_write_sha_text(dev_priv, 0);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +	} else if (sha_leftovers == 2) {
>>> +		// Write 32 bits of text
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>> +		sha_text |= bstatus[0] << 24 | bstatus[1] << 16;
>>> +		ret = intel_write_sha_text(dev_priv, sha_text);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +		// Write 64 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>>> +		for (i = 0; i < 2; i++) {
>>> +			ret = intel_write_sha_text(dev_priv, 0);
>>> +			if (ret < 0)
>>> +				return ret;
>>> +			sha_idx += sizeof(sha_text);
>>> +		}
>>> +	} else if (sha_leftovers == 3) {
>>> +		// Write 32 bits of text
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>> +		sha_text |= bstatus[0] << 24;
>>> +		ret = intel_write_sha_text(dev_priv, sha_text);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +		// Write 8 bits of text, 24 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
>>> +		ret = intel_write_sha_text(dev_priv, bstatus[1]);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +		// Write 32 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>>> +		ret = intel_write_sha_text(dev_priv, 0);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +
>>> +		// Write 8 bits of M0
>>> +		I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
>>> +		ret = intel_write_sha_text(dev_priv, 0);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +	} else {
>>> +		DRM_ERROR("Invalid number of leftovers %d\n", sha_leftovers);
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>> +	// Fill up to 64 - 4 bytes with zeros (leave the last write for length)
>>> +	while ((sha_idx % 64) < (64 - sizeof(sha_text))) {
>>> +		ret = intel_write_sha_text(dev_priv, 0);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +		sha_idx += sizeof(sha_text);
>>> +	}
>>> +
>>> +	/*
>>> +	 * Last write gets the length of the concatenation in bits. That is:
>>> +	 *  - 5 bytes per device
>>> +	 *  - 10 bytes for BINFO/BSTATUS(2), M0(8)
>>> +	 */
>>> +	sha_text = (num_downstream * 5 + 10) * 8;
>>> +	ret = intel_write_sha_text(dev_priv, sha_text);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	// Finally, tell the HW we're done with the hash and wait for it to ACK
>>> +	I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_COMPLETE_HASH);
>>> +	if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_COMPLETE, 1)) {
>>> +		DRM_ERROR("Timed out waiting for SHA1 complete\n");
>>> +		return -ETIMEDOUT;
>>> +	}
>>> +	if (!(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_V_MATCH)) {
>>> +		DRM_ERROR("SHA-1 mismatch, HDCP failed\n");
>>> +		return -ENXIO;
>>> +	}
>>> +
>>> +	DRM_INFO("HDCP is enabled (%d downstream devices)\n", num_downstream);
>>> +	return 0;
>>> +}
>>> +
>>> +/* Implements Part 1 of the HDCP authorization procedure */
>>> +static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
>>> +			   const struct intel_hdcp_shim *shim)
>>> +{
>>> +	struct drm_i915_private *dev_priv;
>>> +	enum port port;
>>> +	unsigned long r0_prime_gen_start;
>>> +	int ret, i;
>>> +	union {
>>> +		u32 reg[2];
>>> +		u8 shim[DRM_HDCP_AN_LEN];
>>> +	} an;
>>> +	union {
>>> +		u32 reg[2];
>>> +		u8 shim[DRM_HDCP_KSV_LEN];
>>> +	} bksv;
>>> +	union {
>>> +		u32 reg;
>>> +		u8 shim[DRM_HDCP_RI_LEN];
>>> +	} ri;
>>> +	bool repeater_present;
>>> +
>>> +	dev_priv = intel_dig_port->base.base.dev->dev_private;
>>> +
>>> +	port = intel_dig_port->port;
>>> +
>>> +	// Initialize An with 2 random values and acquire it
>>> +	for (i = 0; i < 2; i++)
>>> +		I915_WRITE(SKL_PORT_HDCP_ANINIT(port), get_random_long());
>>> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_CAPTURE_AN);
>>> +
>>> +	// Wait for An to be acquired
>>> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>>> +		     SKL_HDCP_STATUS_AN_READY, 1)) {
>>> +		DRM_ERROR("Timed out waiting for An\n");
>>> +		return -ETIMEDOUT;
>>> +	}
>>> +
>>> +	an.reg[0] = I915_READ(SKL_PORT_HDCP_ANLO(port));
>>> +	an.reg[1] = I915_READ(SKL_PORT_HDCP_ANHI(port));
>>> +	ret = shim->write_an_aksv(intel_dig_port, an.shim);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	r0_prime_gen_start = jiffies;
>>> +
>>> +	memset(&bksv, 0, sizeof(bksv));
>>> +	ret = shim->read_bksv(intel_dig_port, bksv.shim);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	I915_WRITE(SKL_PORT_HDCP_BKSVLO(port), bksv.reg[0]);
>>> +	I915_WRITE(SKL_PORT_HDCP_BKSVHI(port), bksv.reg[1]);
>>> +
>>> +	ret = shim->repeater_present(intel_dig_port, &repeater_present);
>>> +	if (ret)
>>> +		return ret;
>>> +	if (repeater_present)
>>> +		I915_WRITE(SKL_HDCP_REP_CTL,
>>> +			   intel_hdcp_get_repeater_ctl(intel_dig_port));
>>> +
>>> +	ret = shim->toggle_signalling(intel_dig_port, true);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_AUTH_AND_ENC);
>>> +
>>> +	// Wait for R0 ready
>>> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>>> +		     (SKL_HDCP_STATUS_R0_READY | SKL_HDCP_STATUS_ENC), 1)) {
>>> +		DRM_ERROR("Timed out waiting for R0 ready\n");
>>> +		return -ETIMEDOUT;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Wait for R0' to become available, the spec says 100ms from Aksv
>>> +	 * write. On DP, there's an R0_READY bit available but no such bit
>>> +	 * exists on HDMI. Since the upper-bound is the same, we'll just do
>>> +	 * the stupid thing instead of polling on one and not the other.
>>> +	 */
>>> +	wait_remaining_ms_from_jiffies(r0_prime_gen_start, 100);
>>> +
>>> +	ri.reg = 0;
>>> +	ret = shim->read_ri_prime(intel_dig_port, ri.shim);
>>> +	if (ret)
>>> +		return ret;
>>> +	I915_WRITE(SKL_PORT_HDCP_RPRIME(port), ri.reg);
>>> +
>>> +	// Wait for Ri prime match
>>> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>>> +		     (SKL_HDCP_STATUS_RI_MATCH | SKL_HDCP_STATUS_ENC), 1)) {
>>> +		DRM_ERROR("Timed out waiting for Ri prime match (%x)\n",
>>> +			  I915_READ(SKL_PORT_HDCP_STATUS(port)));
>>> +		return -ETIMEDOUT;
>>> +	}
>>> +
>>> +	// Wait for encryption confirmation
>>> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>>> +		      SKL_HDCP_STATUS_ENC, 20)) {
>>> +		DRM_ERROR("Timed out waiting for encryption\n");
>>> +		return -ETIMEDOUT;
>>> +	}
>>> +
>>> +	/*
>>> +	 * XXX: If we have MST-connected devices, we need to enable encryption
>>> +	 * on those as well.
>>> +	 */
>>> +
>>> +	return intel_hdcp_auth_downstream(intel_dig_port, shim);
>>> +}
>>> +
>>> +static
>>> +struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector)
>>> +{
>>> +	return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base);
>>> +}
>>> +
>>> +static int _intel_hdcp_disable(struct intel_connector *connector)
>>> +{
>>> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>> +	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
>>> +	enum port port = intel_dig_port->port;
>>> +	int ret;
>>> +
>>> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
>>> +	if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) == 0, 20)) {
>>> +		DRM_ERROR("Failed to disable HDCP, timeout clearing status\n");
>>> +		return -ETIMEDOUT;
>>> +	}
>>> +
>>> +	intel_hdcp_clear_keys(dev_priv);
>>> +
>>> +	ret = connector->hdcp_shim->toggle_signalling(intel_dig_port, false);
>>> +	if (ret) {
>>> +		DRM_ERROR("Failed to disable HDCP signalling\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	DRM_INFO("HDCP is disabled\n");
>>> +	return 0;
>>> +}
>>> +
>>> +static int _intel_hdcp_enable(struct intel_connector *connector)
>>> +{
>>> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>> +	int i, ret;
>>> +
>>> +	if (!(I915_READ(SKL_FUSE_STATUS) & SKL_FUSE_PG_DIST_STATUS(1))) {
>>> +		DRM_ERROR("PG1 is disabled, cannot load keys\n");
>>> +		return -ENXIO;
>>> +	}
>>> +
>>> +	for (i = 0; i < KEY_LOAD_TRIES; i++) {
>>> +		ret = intel_hdcp_load_keys(dev_priv);
>>> +		if (!ret)
>>> +			break;
>>> +		intel_hdcp_clear_keys(dev_priv);
>>> +	}
>>> +	if (ret) {
>>> +		DRM_ERROR("Could not load HDCP keys, (%d)\n", ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = intel_hdcp_auth(conn_to_dig_port(connector),
>>> +			      connector->hdcp_shim);
>>> +	if (ret) {
>>> +		DRM_ERROR("Failed to authenticate HDCP (%d)\n", ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +void intel_hdcp_work(struct work_struct *work)
>>> +{
>>> +	struct intel_connector *connector = container_of(to_delayed_work(work),
>>> +							 struct intel_connector,
>>> +						         hdcp_work);
>>> +	struct drm_device *dev = connector->base.dev;
>>> +	int ret;
>>> +
>>> +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>>> +
>>> +	ret = intel_hdcp_check_link(connector);
>>> +	if (!ret)
>>> +		schedule_delayed_work(&connector->hdcp_work,
>>> +				      DRM_HDCP_CHECK_PERIOD_MS);
>>> +
>>> +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
>>> +}
>>> +
>>> +int intel_hdcp_enable(struct intel_connector *connector)
>>> +{
>>> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>> +	struct drm_device *dev = &dev_priv->drm;
>>> +	struct drm_connector_state *state = connector->base.state;
>>> +	int ret;
>>> +
>>> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>>> +
>>> +	if (!connector->hdcp_shim)
>>> +		return -ENOENT;
>>> +
>>> +	ret = _intel_hdcp_enable(connector);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
>>> +
>>> +	schedule_delayed_work(&connector->hdcp_work, DRM_HDCP_CHECK_PERIOD_MS);
>>> +	return 0;
>>> +}
>>> +
>>> +int intel_hdcp_disable(struct intel_connector *connector)
>>> +{
>>> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>> +	struct drm_device *dev = &dev_priv->drm;
>>> +
>>> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>>> +
>>> +	if (!connector->hdcp_shim)
>>> +		return -ENOENT;
>>> +
>>> +	cancel_delayed_work(&connector->hdcp_work);
>>> +
>>> +	return _intel_hdcp_disable(connector);
>>> +}
>>> +
>>> +/* Implements Part 3 of the HDCP authorization procedure */
>>> +int intel_hdcp_check_link(struct intel_connector *connector)
>>> +{
>>> +	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>> +	struct drm_device *dev = &dev_priv->drm;
>>> +	struct drm_connector_state *state = connector->base.state;
>>> +	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
>>> +	enum port port = intel_dig_port->port;
>>> +	int ret;
>>> +
>>> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>>> +
>>> +	if (state->content_protection != DRM_MODE_CONTENT_PROTECTION_ENABLED)
>>> +		return 0;
>>> +
>>> +	if (!connector->hdcp_shim)
>>> +		return -ENOENT;
>>> +
>>> +	if (!(I915_READ(SKL_PORT_HDCP_STATUS(port)) & SKL_HDCP_STATUS_ENC)) {
>>> +		DRM_ERROR("HDCP check failed: link is not encrypted, %x\n",
>>> +			   I915_READ(SKL_PORT_HDCP_STATUS(port)));
>>> +		ret = -ENXIO;
>>> +		goto fail;
>>> +	}
>>> +
>>> +	if (connector->hdcp_shim->check_link(intel_dig_port))
>>> +		return 0;
>>> +
>>> +	DRM_INFO("HDCP link failed, retrying authentication\n");
>>> +
>>> +	ret = _intel_hdcp_disable(connector);
>>> +	if (ret) {
>>> +		DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
>>> +		goto fail;
>>> +	}
>>> +
>>> +	ret = _intel_hdcp_enable(connector);
>>> +	if (ret) {
>>> +		DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
>>> +		goto fail;
>>> +	}
>>> +
>>> +	return 0;
>>> +
>>> +fail:
>>> +	state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
>>> +	return ret;
>>> +}
>> _______________________________________________
>> Intel-gfx mailing list
>> Intel-gfx@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Intel-gfx] [RFC PATCH 3/6] drm/i915: Add HDCP framework + base implementation
  2017-12-01  7:36       ` Daniel Vetter
  (?)
  (?)
@ 2017-12-01 14:12       ` Sean Paul
  -1 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-12-01 14:12 UTC (permalink / raw)
  To: Ramalingam C, Sean Paul, dri-devel, Intel Graphics Development,
	David Airlie, Linux Kernel Mailing List, Rodrigo Vivi


[-- Attachment #1.1: Type: text/plain, Size: 44157 bytes --]

On Fri, Dec 1, 2017 at 2:36 AM, Daniel Vetter <daniel@ffwll.ch> wrote:

> On Fri, Dec 01, 2017 at 12:53:31PM +0530, Ramalingam C wrote:
> > Sean,
> >
> > IMHO, it will good if we can have all generic hdcp1.4 authentication
> flow in
> > drm helpers and all interested display drivers to use them.
> >
> > This Design will make the extending of hdcp easy for other display
> drivers
> > based on DRM.
> >
> > We can have the required drm_hdcp_shim type of implementation at drm
> > structure which will be called for platform specific operations (like
> > prepare an, send aksv, program bksv/repeater/r0 and verify sha1 etc)?
>
> I discussed this exact question with Sean Paul, and apparently the
> hardware designs are too diverse to make shared code much useful. Some hw
> has the entire hdcp flow in hw, some almost nothing (like i915 here), and
> then there's everything in between.
>
> Given that Sean has seen a lot more hdcp implementations than we have,
> that we right now have no other implementation than i915 in upstream and
> than wrong abstraction is much harder to fix than no abstraction I'm going
> with Sean's approach of "no generic abstraction" here. Personally I'm not
> even fully sold on the shim abstraction, but I think by&large that one is
> fine.
>

I think there's some sharing potential between exynos and i915, but the
rockchip stuff is completely different. Even exynos differs in that each
step of the authentication process is interrupt driven (iirc). I just don't
see a pattern worth abstracting atm. We might be able to share the
enable/disable/check song & dance, but let's not worry about abstraction
until we have 2 implementations.



> > On Thursday 30 November 2017 08:38 AM, Sean Paul wrote:
> > > This patch adds the framework required to add HDCP support to intel
> > > connectors. It implements Aksv loading from fuse, and parts 1/2/3
> > > of the HDCP authentication scheme.
> > >
> > > Note that without shim implementations, this does not actually
> implement
> > > HDCP. That will come in subsequent patches.
> > >
> > > Signed-off-by: Sean Paul <seanpaul@chromium.org>
> > > ---
> > >   drivers/gpu/drm/i915/Makefile       |   1 +
> > >   drivers/gpu/drm/i915/i915_reg.h     |  83 +++++
> > >   drivers/gpu/drm/i915/intel_atomic.c |  26 +-
> > >   drivers/gpu/drm/i915/intel_ddi.c    |  14 +
> > >   drivers/gpu/drm/i915/intel_drv.h    |  53 +++
> > >   drivers/gpu/drm/i915/intel_hdcp.c   | 636
> ++++++++++++++++++++++++++++++++++++
> > >   6 files changed, 811 insertions(+), 2 deletions(-)
> > >   create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c
> > >
> > > diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/
> Makefile
> > > index 6c3b0481ef82..1e745508e437 100644
> > > --- a/drivers/gpu/drm/i915/Makefile
> > > +++ b/drivers/gpu/drm/i915/Makefile
> > > @@ -87,6 +87,7 @@ i915-y += intel_audio.o \
> > >       intel_fbc.o \
> > >       intel_fifo_underrun.o \
> > >       intel_frontbuffer.o \
> > > +     intel_hdcp.o \
> > >       intel_hotplug.o \
> > >       intel_modes.o \
> > >       intel_overlay.o \
> > > diff --git a/drivers/gpu/drm/i915/i915_reg.h
> b/drivers/gpu/drm/i915/i915_reg.h
> > > index 68a58cce6ab1..43128030171d 100644
> > > --- a/drivers/gpu/drm/i915/i915_reg.h
> > > +++ b/drivers/gpu/drm/i915/i915_reg.h
> > > @@ -7991,6 +7991,7 @@ enum {
> > >   #define     GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT      8
> > >   #define     GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT      16
> > >   #define     GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT      24
> > > +#define   SKL_PCODE_LOAD_HDCP_KEYS         0x5
> > >   #define   SKL_PCODE_CDCLK_CONTROL         0x7
> > >   #define     SKL_CDCLK_PREPARE_FOR_CHANGE  0x3
> > >   #define     SKL_CDCLK_READY_FOR_CHANGE            0x1
> > > @@ -8285,6 +8286,88 @@ enum skl_power_gate {
> > >   #define  SKL_PW_TO_PG(pw)                 ((pw) - SKL_DISP_PW_1 +
> SKL_PG1)
> > >   #define  SKL_FUSE_PG_DIST_STATUS(pg)              (1 << (27 - (pg)))
> > > +
> > > +/* HDCP Key Registers */
> > > +#define SKL_HDCP_KEY_CONF          _MMIO(0x66c00)
> > > +#define     SKL_HDCP_AKSV_SEND_TRIGGER     BIT(31)
> > > +#define  SKL_HDCP_CLEAR_KEYS_TRIGGER       BIT(30)
> > > +#define SKL_HDCP_KEY_STATUS                _MMIO(0x66c04)
> > > +#define  SKL_HDCP_FUSE_IN_PROGRESS BIT(7)
> > > +#define  SKL_HDCP_FUSE_ERROR               BIT(6)
> > > +#define  SKL_HDCP_FUSE_DONE                BIT(5)
> > > +#define  SKL_HDCP_KEY_LOAD_STATUS  BIT(1)
> > > +#define  SKL_HDCP_KEY_LOAD_DONE            BIT(0)
> > > +#define SKL_HDCP_AKSV_LO           _MMIO(0x66c10)
> > > +#define SKL_HDCP_AKSV_HI           _MMIO(0x66c14)
> > > +
> > > +/* HDCP Repeater Registers */
> > > +#define SKL_HDCP_REP_CTL           _MMIO(0x66d00)
> > > +#define  SKL_HDCP_DDIB_REP_PRESENT BIT(30)
> > > +#define  SKL_HDCP_DDIA_REP_PRESENT BIT(29)
> > > +#define  SKL_HDCP_DDIC_REP_PRESENT BIT(28)
> > > +#define  SKL_HDCP_DDID_REP_PRESENT BIT(27)
> > > +#define  SKL_HDCP_DDIF_REP_PRESENT BIT(26)
> > > +#define  SKL_HDCP_DDIE_REP_PRESENT BIT(25)
> > > +#define  SKL_HDCP_DDIB_SHA1_M0             (1 << 20)
> > > +#define  SKL_HDCP_DDIA_SHA1_M0             (2 << 20)
> > > +#define  SKL_HDCP_DDIC_SHA1_M0             (3 << 20)
> > > +#define  SKL_HDCP_DDID_SHA1_M0             (4 << 20)
> > > +#define  SKL_HDCP_DDIF_SHA1_M0             (5 << 20)
> > > +#define  SKL_HDCP_DDIE_SHA1_M0             (6 << 20) // Bspec says 5?
> > > +#define  SKL_HDCP_SHA1_BUSY                BIT(16)
> > > +#define  SKL_HDCP_SHA1_READY               BIT(17)
> > > +#define  SKL_HDCP_SHA1_COMPLETE            BIT(18)
> > > +#define  SKL_HDCP_SHA1_V_MATCH             BIT(19)
> > > +#define  SKL_HDCP_SHA1_TEXT_32             (1 << 1)
> > > +#define  SKL_HDCP_SHA1_COMPLETE_HASH       (2 << 1)
> > > +#define  SKL_HDCP_SHA1_TEXT_24             (4 << 1)
> > > +#define  SKL_HDCP_SHA1_TEXT_16             (5 << 1)
> > > +#define  SKL_HDCP_SHA1_TEXT_8              (6 << 1)
> > > +#define  SKL_HDCP_SHA1_TEXT_0              (7 << 1)
> > > +#define SKL_HDCP_SHA_V_PRIME_H0            _MMIO(0x66d04)
> > > +#define SKL_HDCP_SHA_V_PRIME_H1            _MMIO(0x66d08)
> > > +#define SKL_HDCP_SHA_V_PRIME_H2            _MMIO(0x66d0C)
> > > +#define SKL_HDCP_SHA_V_PRIME_H3            _MMIO(0x66d10)
> > > +#define SKL_HDCP_SHA_V_PRIME_H4            _MMIO(0x66d14)
> > > +#define SKL_HDCP_SHA_V_PRIME(h)            _MMIO((0x66d04 + h * 4))
> > > +#define SKL_HDCP_SHA_TEXT          _MMIO(0x66d18)
> > > +
> > > +/* HDCP Auth Registers */
> > > +#define _SKL_PORTA_HDCP_AUTHENC            0x66800
> > > +#define _SKL_PORTB_HDCP_AUTHENC            0x66500
> > > +#define _SKL_PORTC_HDCP_AUTHENC            0x66600
> > > +#define _SKL_PORTD_HDCP_AUTHENC            0x66700
> > > +#define _SKL_PORTE_HDCP_AUTHENC            0x66A00
> > > +#define _SKL_PORTF_HDCP_AUTHENC            0x66900
> > > +#define _SKL_PORT_HDCP_AUTHENC(port, x)    _MMIO(_PICK(port, \
> > > +                                     _SKL_PORTA_HDCP_AUTHENC, \
> > > +                                     _SKL_PORTB_HDCP_AUTHENC, \
> > > +                                     _SKL_PORTC_HDCP_AUTHENC, \
> > > +                                     _SKL_PORTD_HDCP_AUTHENC, \
> > > +                                     _SKL_PORTE_HDCP_AUTHENC, \
> > > +                                     _SKL_PORTF_HDCP_AUTHENC) + x)
> > > +#define SKL_PORT_HDCP_CONF(port)   _SKL_PORT_HDCP_AUTHENC(port, 0x0)
> > > +#define  SKL_HDCP_CONF_CAPTURE_AN  BIT(0)
> > > +#define  SKL_HDCP_CONF_AUTH_AND_ENC        (BIT(1) | BIT(0))
> > > +#define SKL_PORT_HDCP_ANINIT(port) _SKL_PORT_HDCP_AUTHENC(port, 0x4)
> > > +#define SKL_PORT_HDCP_ANLO(port)   _SKL_PORT_HDCP_AUTHENC(port, 0x8)
> > > +#define SKL_PORT_HDCP_ANHI(port)   _SKL_PORT_HDCP_AUTHENC(port, 0xC)
> > > +#define SKL_PORT_HDCP_BKSVLO(port) _SKL_PORT_HDCP_AUTHENC(port, 0x10)
> > > +#define SKL_PORT_HDCP_BKSVHI(port) _SKL_PORT_HDCP_AUTHENC(port, 0x14)
> > > +#define SKL_PORT_HDCP_RPRIME(port) _SKL_PORT_HDCP_AUTHENC(port, 0x18)
> > > +#define SKL_PORT_HDCP_STATUS(port) _SKL_PORT_HDCP_AUTHENC(port, 0x1C)
> > > +#define  SKL_HDCP_STATUS_STREAM_A_ENC      BIT(31)
> > > +#define  SKL_HDCP_STATUS_STREAM_B_ENC      BIT(30)
> > > +#define  SKL_HDCP_STATUS_STREAM_C_ENC      BIT(29)
> > > +#define  SKL_HDCP_STATUS_STREAM_D_ENC      BIT(28)
> > > +#define  SKL_HDCP_STATUS_AUTH              BIT(21)
> > > +#define  SKL_HDCP_STATUS_ENC               BIT(20)
> > > +#define  SKL_HDCP_STATUS_RI_MATCH  BIT(19)
> > > +#define  SKL_HDCP_STATUS_R0_READY  BIT(18)
> > > +#define  SKL_HDCP_STATUS_AN_READY  BIT(17)
> > > +#define  SKL_HDCP_STATUS_CIPHER            BIT(16)
> > > +#define  SKL_HDCP_STATUS_FRAME_CNT(x)      ((x >> 8) & 0xff)
> > > +
> > >   /* Per-pipe DDI Function Control */
> > >   #define _TRANS_DDI_FUNC_CTL_A             0x60400
> > >   #define _TRANS_DDI_FUNC_CTL_B             0x61400
> > > diff --git a/drivers/gpu/drm/i915/intel_atomic.c
> b/drivers/gpu/drm/i915/intel_atomic.c
> > > index 36d4e635e4ce..ddf08227d9cb 100644
> > > --- a/drivers/gpu/drm/i915/intel_atomic.c
> > > +++ b/drivers/gpu/drm/i915/intel_atomic.c
> > > @@ -109,12 +109,34 @@ int intel_digital_connector_atomic_check(struct
> drm_connector *conn,
> > >     struct intel_digital_connector_state *old_conn_state =
> > >             to_intel_digital_connector_state(old_state);
> > >     struct drm_crtc_state *crtc_state;
> > > -
> > > -   if (!new_state->crtc)
> > > +   uint64_t old_cp = old_conn_state->base.content_protection;
> > > +   uint64_t new_cp = new_state->content_protection;
> > > +
> > > +   if (!new_state->crtc) {
> > > +           /*
> > > +            * If the connector is being disabled with CP enabled,
> mark it
> > > +            * desired so it's re-enabled when the connector is
> brought back
> > > +            */
> > > +           if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
> > > +                   new_state->content_protection =
> > > +                           DRM_MODE_CONTENT_PROTECTION_DESIRED;
> > >             return 0;
> > > +   }
> > >     crtc_state = drm_atomic_get_new_crtc_state(new_state->state,
> new_state->crtc);
> > > +   if (new_cp != old_cp) {
> > > +           /* Only drivers can set content protection enabled */
> > > +           if (new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
> > > +                   new_state->content_protection =
> > > +                           DRM_MODE_CONTENT_PROTECTION_DESIRED;
> > > +
> > > +           /* Involve the encoder/connector to enable/disable CP */
> > > +           if (new_cp == DRM_MODE_CONTENT_PROTECTION_OFF ||
> > > +               old_cp == DRM_MODE_CONTENT_PROTECTION_OFF)
> > > +                   crtc_state->mode_changed = true;
> >
> > We need not perform the mode set for hdcp enable/disable.
> > Authentication and encryption can be started on active port.
>
> Was simpler to implement this way :-) We can fix this by pushing the hdcp
> enable/disable code into a post-modeset operation. Ville had written the
> infrastructure for that to fix a few fastboot corner cases. But that
> infrastructure hasn't landed yet, so probably better to do that in a
> follow-up.
>
> I also guess that CrOS simply set this to desired every time they enable
> an external screen, so it won't result in an unecessary modeset.
>

Actually, we enable only as needed, if there is no HD licensed content on
the screen, the link is unencrypted. The UX of doing it through
enable/disable is meh, but things are a lot easier to reason about.


>
> > > +   }
> > > +
> > >     /*
> > >      * These properties are handled by fastset, and might not end
> > >      * up in a modeset.
> > > diff --git a/drivers/gpu/drm/i915/intel_ddi.c
> b/drivers/gpu/drm/i915/intel_ddi.c
> > > index 933c18fd4258..0e69337f410d 100644
> > > --- a/drivers/gpu/drm/i915/intel_ddi.c
> > > +++ b/drivers/gpu/drm/i915/intel_ddi.c
> > > @@ -2432,10 +2432,17 @@ static void intel_enable_ddi(struct
> intel_encoder *encoder,
> > >                          const struct intel_crtc_state *crtc_state,
> > >                          const struct drm_connector_state *conn_state)
> > >   {
> > > +   struct drm_connector *connector = conn_state->connector;
> > > +   struct intel_connector *intel_connector =
> to_intel_connector(connector);
> > > +
> > >     if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
> > >             intel_enable_ddi_hdmi(encoder, crtc_state, conn_state);
> > >     else
> > >             intel_enable_ddi_dp(encoder, crtc_state, conn_state);
> > > +
> > > +   if (conn_state->content_protection ==
> > > +                   DRM_MODE_CONTENT_PROTECTION_DESIRED)
> > > +           intel_hdcp_enable(intel_connector);
> > >   }
> > >   static void intel_disable_ddi_dp(struct intel_encoder *encoder,
> > > @@ -2468,10 +2475,17 @@ static void intel_disable_ddi(struct
> intel_encoder *encoder,
> > >                           const struct intel_crtc_state
> *old_crtc_state,
> > >                           const struct drm_connector_state
> *old_conn_state)
> > >   {
> > > +   struct drm_connector *connector = old_conn_state->connector;
> > > +   struct intel_connector *intel_connector =
> to_intel_connector(connector);
> > > +
> > >     if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
> > >             intel_disable_ddi_hdmi(encoder, old_crtc_state,
> old_conn_state);
> > >     else
> > >             intel_disable_ddi_dp(encoder, old_crtc_state,
> old_conn_state);
> > > +
> > > +   if (old_conn_state->content_protection !=
> > > +                   DRM_MODE_CONTENT_PROTECTION_OFF)
> > > +           intel_hdcp_disable(intel_connector);
> > We might want to disable the hdcp before disabling the DDI. Actually we
> > could trigger hdcp disable at connector state change(due to hot-unplug)
> > also.
>
> Yeah this part needs to be reworked a bit, also because the locking
> doesn't work yet. I think hot-unplug is handled by the worker thread
> already, it does the mandatory regular polling.
>

Yeah, this should go above the disable. intel_hdcp_disable() only does
transmitter-side operations, but it's certainly better form to wind/unwind
in enable/disable.

Sean



> -Daniel
>
> >
> > Thanks,
> > --Ram
> > >   }
> > >   static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder,
> > > diff --git a/drivers/gpu/drm/i915/intel_drv.h
> b/drivers/gpu/drm/i915/intel_drv.h
> > > index 47d022d48718..8924004575b8 100644
> > > --- a/drivers/gpu/drm/i915/intel_drv.h
> > > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > > @@ -299,6 +299,49 @@ struct intel_panel {
> > >     } backlight;
> > >   };
> > > +struct intel_hdcp_shim {
> > > +   /* Outputs the transmitter's An and Aksv values to the receiver. */
> > > +   int (*write_an_aksv)(struct intel_digital_port *intel_dig_port, u8
> *an);
> > > +
> > > +   /* Reads the receiver's key selection vector */
> > > +   int (*read_bksv)(struct intel_digital_port *intel_dig_port, u8
> *bksv);
> > > +
> > > +   /*
> > > +    * Reads BINFO from DP receivers and BSTATUS from HDMI receivers.
> The
> > > +    * definitions are the same in the respective specs, but the names
> are
> > > +    * different. Call it BSTATUS since that's the name the HDMI spec
> > > +    * uses and it was there first.
> > > +    */
> > > +   int (*read_bstatus)(struct intel_digital_port *intel_dig_port,
> > > +                       u8 *bstatus);
> > > +
> > > +   /* Determines whether a repeater is present downstream */
> > > +   int (*repeater_present)(struct intel_digital_port *intel_dig_port,
> > > +                           bool *repeater_present);
> > > +
> > > +   /* Reads the receiver's Ri' value */
> > > +   int (*read_ri_prime)(struct intel_digital_port *intel_dig_port, u8
> *ri);
> > > +
> > > +   /* Determines if the receiver's KSV FIFO is ready for consumption
> */
> > > +   int (*read_ksv_ready)(struct intel_digital_port *intel_dig_port,
> > > +                         bool *ksv_ready);
> > > +
> > > +   /* Reads the ksv fifo for num_downstream devices */
> > > +   int (*read_ksv_fifo)(struct intel_digital_port *intel_dig_port,
> > > +                        int num_downstream, u8 *ksv_fifo);
> > > +
> > > +   /* Reads a 32-bit part of V' from the receiver */
> > > +   int (*read_v_prime_part)(struct intel_digital_port *intel_dig_port,
> > > +                            int i, u32 *part);
> > > +
> > > +   /* Enables HDCP signalling on the port */
> > > +   int (*toggle_signalling)(struct intel_digital_port *intel_dig_port,
> > > +                            bool enable);
> > > +
> > > +   /* Ensures the link is still protected */
> > > +   bool (*check_link)(struct intel_digital_port *intel_dig_port);
> > > +};
> > > +
> > >   struct intel_connector {
> > >     struct drm_connector base;
> > >     /*
> > > @@ -330,6 +373,9 @@ struct intel_connector {
> > >     /* Work struct to schedule a uevent on link train failure */
> > >     struct work_struct modeset_retry_work;
> > > +
> > > +   const struct intel_hdcp_shim *hdcp_shim;
> > > +   struct delayed_work hdcp_work;
> > >   };
> > >   struct intel_digital_connector_state {
> > > @@ -1295,6 +1341,8 @@ void intel_ddi_set_vc_payload_alloc(const
> struct intel_crtc_state *crtc_state,
> > >                                 bool state);
> > >   u32 bxt_signal_levels(struct intel_dp *intel_dp);
> > >   uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
> > > +int intel_ddi_enable_hdcp_signalling(struct intel_encoder
> *intel_encoder);
> > > +int intel_ddi_disable_hdcp_signalling(struct intel_encoder
> *intel_encoder);
> > >   u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
> > >   unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
> > > @@ -1746,6 +1794,11 @@ static inline void intel_backlight_device_unregister(struct
> intel_connector *con
> > >   }
> > >   #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
> > > +/* intel_hdcp.c */
> > > +int intel_hdcp_enable(struct intel_connector *connector);
> > > +int intel_hdcp_disable(struct intel_connector *connector);
> > > +int intel_hdcp_check_link(struct intel_connector *connector);
> > > +void intel_hdcp_work(struct work_struct *work);
> > >   /* intel_psr.c */
> > >   void intel_psr_enable(struct intel_dp *intel_dp,
> > > diff --git a/drivers/gpu/drm/i915/intel_hdcp.c
> b/drivers/gpu/drm/i915/intel_hdcp.c
> > > new file mode 100644
> > > index 000000000000..a2a575ed657e
> > > --- /dev/null
> > > +++ b/drivers/gpu/drm/i915/intel_hdcp.c
> > > @@ -0,0 +1,636 @@
> > > +/*
> > > + * Copyright (C) 2017 Google, Inc.
> > > + *
> > > + * This software is licensed under the terms of the GNU General Public
> > > + * License version 2, as published by the Free Software Foundation,
> and
> > > + * may be copied, distributed, and modified under those terms.
> > > + *
> > > + * This program is distributed in the hope that it will be useful,
> > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > > + * GNU General Public License for more details.
> > > + */
> > > +
> > > +#include <drm/drmP.h>
> > > +#include <drm/drm_hdcp.h>
> > > +#include <linux/i2c.h>
> > > +#include <linux/random.h>
> > > +
> > > +#include "intel_drv.h"
> > > +#include "i915_reg.h"
> > > +
> > > +#define KEY_LOAD_TRIES     5
> > > +
> > > +static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port
> *intel_dig_port,
> > > +                               const struct intel_hdcp_shim *shim)
> > > +{
> > > +   unsigned long timeout = jiffies + msecs_to_jiffies_timeout(500);
> > > +   int ret;
> > > +   bool ksv_ready;
> > > +
> > > +   while (true) {
> > > +           ret = shim->read_ksv_ready(intel_dig_port, &ksv_ready);
> > > +           if (ret)
> > > +                   return ret;
> > > +           if (ksv_ready)
> > > +                   break;
> > > +           if (time_after(jiffies, timeout))
> > > +                   return -ETIMEDOUT;
> > > +           msleep(100);
> > > +   }
> > > +   return 0;
> > > +}
> > > +
> > > +static void intel_hdcp_clear_keys(struct drm_i915_private *dev_priv)
> > > +{
> > > +   I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_CLEAR_KEYS_TRIGGER);
> > > +   I915_WRITE(SKL_HDCP_KEY_STATUS,
> > > +              SKL_HDCP_KEY_LOAD_DONE | SKL_HDCP_KEY_LOAD_STATUS |
> > > +              SKL_HDCP_FUSE_IN_PROGRESS | SKL_HDCP_FUSE_ERROR |
> > > +              SKL_HDCP_FUSE_DONE);
> > > +}
> > > +
> > > +static int intel_hdcp_load_keys(struct drm_i915_private *dev_priv)
> > > +{
> > > +   unsigned long timeout;
> > > +   int ret;
> > > +   u32 val;
> > > +
> > > +   // Initiate loading the HDCP key from fuses
> > > +   mutex_lock(&dev_priv->pcu_lock);
> > > +   ret = sandybridge_pcode_write(dev_priv, SKL_PCODE_LOAD_HDCP_KEYS,
> 1);
> > > +   mutex_unlock(&dev_priv->pcu_lock);
> > > +   if (ret) {
> > > +           DRM_ERROR("Failed to initiate HDCP key load (%d)\n", ret);
> > > +           return ret;
> > > +   }
> > > +
> > > +   // Wait for the keys to load (500us)
> > > +   timeout = jiffies + nsecs_to_jiffies_timeout(500 * 1000);
> > > +   while (true) {
> > > +           val = I915_READ(SKL_HDCP_KEY_STATUS);
> > > +           if (val & SKL_HDCP_KEY_LOAD_DONE)
> > > +                   break;
> > > +           if (time_after(jiffies, timeout))
> > > +                   return -ETIMEDOUT;
> > > +           usleep_range(50, 100);
> > > +   }
> > > +   if (!(val & SKL_HDCP_KEY_LOAD_STATUS))
> > > +           return -ENXIO;
> > > +
> > > +   // Send Aksv over to PCH display for use in authentication
> > > +   I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_AKSV_SEND_TRIGGER);
> > > +
> > > +   return 0;
> > > +}
> > > +
> > > +/* Returns updated SHA-1 index */
> > > +static int intel_write_sha_text(struct drm_i915_private *dev_priv,
> u32 sha_text)
> > > +{
> > > +   I915_WRITE(SKL_HDCP_SHA_TEXT, sha_text);
> > > +   if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_READY,
> 1)) {
> > > +           DRM_ERROR("Timed out waiting for SHA1 ready\n");
> > > +           return -ETIMEDOUT;
> > > +   }
> > > +   return 0;
> > > +}
> > > +
> > > +static
> > > +u32 intel_hdcp_get_repeater_ctl(struct intel_digital_port
> *intel_dig_port)
> > > +{
> > > +   enum port port = intel_dig_port->port;
> > > +   switch(port) {
> > > +   case PORT_A:
> > > +           return SKL_HDCP_DDIA_REP_PRESENT | SKL_HDCP_DDIA_SHA1_M0;
> > > +   case PORT_B:
> > > +           return SKL_HDCP_DDIB_REP_PRESENT | SKL_HDCP_DDIB_SHA1_M0;
> > > +   case PORT_C:
> > > +           return SKL_HDCP_DDIC_REP_PRESENT | SKL_HDCP_DDIC_SHA1_M0;
> > > +   case PORT_D:
> > > +           return SKL_HDCP_DDID_REP_PRESENT | SKL_HDCP_DDID_SHA1_M0;
> > > +   case PORT_E:
> > > +           return SKL_HDCP_DDIE_REP_PRESENT | SKL_HDCP_DDIE_SHA1_M0;
> > > +   default:
> > > +           break;
> > > +   }
> > > +   DRM_ERROR("Unknown port %d\n", port);
> > > +   return -EINVAL;
> > > +}
> > > +
> > > +/* Implements Part 2 of the HDCP authorization procedure */
> > > +static
> > > +int intel_hdcp_auth_downstream(struct intel_digital_port
> *intel_dig_port,
> > > +                          const struct intel_hdcp_shim *shim)
> > > +{
> > > +   struct drm_i915_private *dev_priv;
> > > +   u32 vprime, sha_text, sha_leftovers, rep_ctl;
> > > +   u8 bstatus[2], num_downstream, *ksv_fifo;
> > > +   int ret, i, j, sha_idx;
> > > +
> > > +   dev_priv = intel_dig_port->base.base.dev->dev_private;
> > > +
> > > +   ret = shim->read_bstatus(intel_dig_port, bstatus);
> > > +   if (ret)
> > > +           return ret;
> > > +
> > > +   /* If there are no downstream devices, we're all done. */
> > > +   num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
> > > +   if (num_downstream == 0) {
> > > +           DRM_INFO("HDCP is enabled (no downstream devices)\n");
> > > +           return 0;
> > > +   }
> > > +
> > > +   // Poll for ksv list ready (spec says max time allowed is 5s)
> > > +   ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim);
> > > +   if (ret) {
> > > +           DRM_ERROR("KSV list failed to become ready (%d)\n", ret);
> > > +           return ret;
> > > +   }
> > > +
> > > +   ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL);
> > > +   if (!ksv_fifo)
> > > +           return -ENOMEM;
> > > +
> > > +   ret = shim->read_ksv_fifo(intel_dig_port, num_downstream,
> ksv_fifo);
> > > +   if (ret)
> > > +           return ret;
> > > +
> > > +   // Process V' values from the receiver
> > > +   for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
> > > +           ret = shim->read_v_prime_part(intel_dig_port, i, &vprime);
> > > +           if (ret)
> > > +                   return ret;
> > > +           I915_WRITE(SKL_HDCP_SHA_V_PRIME(i), vprime);
> > > +   }
> > > +
> > > +   /*
> > > +    * We need to write the concatenation of all device KSVs, BINFO
> (DP) ||
> > > +    * BSTATUS (HDMI), and M0 (which is added via HDCP_REP_CTL). This
> byte
> > > +    * stream is written via the HDCP_SHA_TEXT register in 32-bit
> > > +    * increments. Every 64 bytes, we need to write HDCP_REP_CTL
> again. This
> > > +    * index will keep track of our progress through the 64 bytes as
> well as
> > > +    * helping us work the 40-bit KSVs through our 32-bit register.
> > > +    *
> > > +    * NOTE: data passed via HDCP_SHA_TEXT should be big-endian
> > > +    */
> > > +   sha_idx = 0;
> > > +   sha_text = 0;
> > > +   sha_leftovers = 0;
> > > +   rep_ctl = intel_hdcp_get_repeater_ctl(intel_dig_port);
> > > +   I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> > > +   for (i = 0; i < num_downstream; i++) {
> > > +           unsigned sha_empty;
> > > +           u8 *ksv = &ksv_fifo[i * DRM_HDCP_KSV_LEN];
> > > +
> > > +           // Fill up the empty slots in sha_text and write it out
> > > +           sha_empty = sizeof(sha_text) - sha_leftovers;
> > > +           for (j = 0; j < sha_empty; j++)
> > > +                   sha_text |= ksv[j] << ((sizeof(sha_text) - j - 1)
> * 8);
> > > +
> > > +           ret = intel_write_sha_text(dev_priv, sha_text);
> > > +           if (ret < 0)
> > > +                   return ret;
> > > +
> > > +           // Programming guide writes this every 64 bytes
> > > +           sha_idx += sizeof(sha_text);
> > > +           if (!(sha_idx % 64))
> > > +                   I915_WRITE(SKL_HDCP_REP_CTL,
> > > +                              rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> > > +
> > > +           // Store the leftover bytes from the ksv in sha_text
> > > +           sha_leftovers = DRM_HDCP_KSV_LEN - sha_empty;
> > > +           sha_text = 0;
> > > +           for (j = 0; j < sha_leftovers; j++)
> > > +                   sha_text |= ksv[sha_empty + j] <<
> > > +                                   ((sizeof(sha_text) - j - 1) * 8);
> > > +
> > > +           /*
> > > +            * If we still have room in sha_text for more data,
> continue.
> > > +            * Otherwise, write it out immediately.
> > > +            */
> > > +           if (sizeof(sha_text) > sha_leftovers)
> > > +                   continue;
> > > +
> > > +           ret = intel_write_sha_text(dev_priv, sha_text);
> > > +           if (ret < 0)
> > > +                   return ret;
> > > +           sha_leftovers = 0;
> > > +           sha_text = 0;
> > > +           sha_idx += sizeof(sha_text);
> > > +   }
> > > +
> > > +   /*
> > > +    * We need to write BINFO/BSTATUS, and M0 now. Depending on how
> many
> > > +    * bytes are leftover from the last ksv, we might be able to fit
> them
> > > +    * all in sha_text (first 2 cases), or we might need to split them
> up
> > > +    * into 2 writes (last 2 cases).
> > > +    */
> > > +   if (sha_leftovers == 0) {
> > > +           // Write 16 bits of text, 16 bits of M0
> > > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl |
> SKL_HDCP_SHA1_TEXT_16);
> > > +           ret = intel_write_sha_text(dev_priv,
> > > +                                      bstatus[0] << 8 | bstatus[1]);
> > > +           if (ret < 0)
> > > +                   return ret;
> > > +           sha_idx += sizeof(sha_text);
> > > +
> > > +           // Write 32 bits of M0
> > > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl |
> SKL_HDCP_SHA1_TEXT_0);
> > > +           ret = intel_write_sha_text(dev_priv, 0);
> > > +           if (ret < 0)
> > > +                   return ret;
> > > +           sha_idx += sizeof(sha_text);
> > > +
> > > +           // Write 16 bits of M0
> > > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl |
> SKL_HDCP_SHA1_TEXT_16);
> > > +           ret = intel_write_sha_text(dev_priv, 0);
> > > +           if (ret < 0)
> > > +                   return ret;
> > > +           sha_idx += sizeof(sha_text);
> > > +
> > > +   } else if (sha_leftovers == 1) {
> > > +           // Write 24 bits of text, 8 bits of M0
> > > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl |
> SKL_HDCP_SHA1_TEXT_24);
> > > +           sha_text |= bstatus[0] << 16 | bstatus[1] << 8;
> > > +           // Only 24-bits of data, must be in the LSB
> > > +           sha_text = (sha_text & 0xffffff00) >> 8;
> > > +           ret = intel_write_sha_text(dev_priv, sha_text);
> > > +           if (ret < 0)
> > > +                   return ret;
> > > +           sha_idx += sizeof(sha_text);
> > > +
> > > +           // Write 32 bits of M0
> > > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl |
> SKL_HDCP_SHA1_TEXT_0);
> > > +           ret = intel_write_sha_text(dev_priv, 0);
> > > +           if (ret < 0)
> > > +                   return ret;
> > > +           sha_idx += sizeof(sha_text);
> > > +
> > > +           // Write 24 bits of M0
> > > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl |
> SKL_HDCP_SHA1_TEXT_8);
> > > +           ret = intel_write_sha_text(dev_priv, 0);
> > > +           if (ret < 0)
> > > +                   return ret;
> > > +           sha_idx += sizeof(sha_text);
> > > +
> > > +   } else if (sha_leftovers == 2) {
> > > +           // Write 32 bits of text
> > > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl |
> SKL_HDCP_SHA1_TEXT_32);
> > > +           sha_text |= bstatus[0] << 24 | bstatus[1] << 16;
> > > +           ret = intel_write_sha_text(dev_priv, sha_text);
> > > +           if (ret < 0)
> > > +                   return ret;
> > > +           sha_idx += sizeof(sha_text);
> > > +
> > > +           // Write 64 bits of M0
> > > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl |
> SKL_HDCP_SHA1_TEXT_0);
> > > +           for (i = 0; i < 2; i++) {
> > > +                   ret = intel_write_sha_text(dev_priv, 0);
> > > +                   if (ret < 0)
> > > +                           return ret;
> > > +                   sha_idx += sizeof(sha_text);
> > > +           }
> > > +   } else if (sha_leftovers == 3) {
> > > +           // Write 32 bits of text
> > > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl |
> SKL_HDCP_SHA1_TEXT_32);
> > > +           sha_text |= bstatus[0] << 24;
> > > +           ret = intel_write_sha_text(dev_priv, sha_text);
> > > +           if (ret < 0)
> > > +                   return ret;
> > > +           sha_idx += sizeof(sha_text);
> > > +
> > > +           // Write 8 bits of text, 24 bits of M0
> > > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl |
> SKL_HDCP_SHA1_TEXT_8);
> > > +           ret = intel_write_sha_text(dev_priv, bstatus[1]);
> > > +           if (ret < 0)
> > > +                   return ret;
> > > +           sha_idx += sizeof(sha_text);
> > > +
> > > +           // Write 32 bits of M0
> > > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl |
> SKL_HDCP_SHA1_TEXT_0);
> > > +           ret = intel_write_sha_text(dev_priv, 0);
> > > +           if (ret < 0)
> > > +                   return ret;
> > > +           sha_idx += sizeof(sha_text);
> > > +
> > > +           // Write 8 bits of M0
> > > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl |
> SKL_HDCP_SHA1_TEXT_24);
> > > +           ret = intel_write_sha_text(dev_priv, 0);
> > > +           if (ret < 0)
> > > +                   return ret;
> > > +           sha_idx += sizeof(sha_text);
> > > +   } else {
> > > +           DRM_ERROR("Invalid number of leftovers %d\n",
> sha_leftovers);
> > > +           return -EINVAL;
> > > +   }
> > > +
> > > +   I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
> > > +   // Fill up to 64 - 4 bytes with zeros (leave the last write for
> length)
> > > +   while ((sha_idx % 64) < (64 - sizeof(sha_text))) {
> > > +           ret = intel_write_sha_text(dev_priv, 0);
> > > +           if (ret < 0)
> > > +                   return ret;
> > > +           sha_idx += sizeof(sha_text);
> > > +   }
> > > +
> > > +   /*
> > > +    * Last write gets the length of the concatenation in bits. That
> is:
> > > +    *  - 5 bytes per device
> > > +    *  - 10 bytes for BINFO/BSTATUS(2), M0(8)
> > > +    */
> > > +   sha_text = (num_downstream * 5 + 10) * 8;
> > > +   ret = intel_write_sha_text(dev_priv, sha_text);
> > > +   if (ret < 0)
> > > +           return ret;
> > > +
> > > +   // Finally, tell the HW we're done with the hash and wait for it
> to ACK
> > > +   I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl |
> SKL_HDCP_SHA1_COMPLETE_HASH);
> > > +   if (wait_for(I915_READ(SKL_HDCP_REP_CTL) &
> SKL_HDCP_SHA1_COMPLETE, 1)) {
> > > +           DRM_ERROR("Timed out waiting for SHA1 complete\n");
> > > +           return -ETIMEDOUT;
> > > +   }
> > > +   if (!(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_V_MATCH)) {
> > > +           DRM_ERROR("SHA-1 mismatch, HDCP failed\n");
> > > +           return -ENXIO;
> > > +   }
> > > +
> > > +   DRM_INFO("HDCP is enabled (%d downstream devices)\n",
> num_downstream);
> > > +   return 0;
> > > +}
> > > +
> > > +/* Implements Part 1 of the HDCP authorization procedure */
> > > +static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
> > > +                      const struct intel_hdcp_shim *shim)
> > > +{
> > > +   struct drm_i915_private *dev_priv;
> > > +   enum port port;
> > > +   unsigned long r0_prime_gen_start;
> > > +   int ret, i;
> > > +   union {
> > > +           u32 reg[2];
> > > +           u8 shim[DRM_HDCP_AN_LEN];
> > > +   } an;
> > > +   union {
> > > +           u32 reg[2];
> > > +           u8 shim[DRM_HDCP_KSV_LEN];
> > > +   } bksv;
> > > +   union {
> > > +           u32 reg;
> > > +           u8 shim[DRM_HDCP_RI_LEN];
> > > +   } ri;
> > > +   bool repeater_present;
> > > +
> > > +   dev_priv = intel_dig_port->base.base.dev->dev_private;
> > > +
> > > +   port = intel_dig_port->port;
> > > +
> > > +   // Initialize An with 2 random values and acquire it
> > > +   for (i = 0; i < 2; i++)
> > > +           I915_WRITE(SKL_PORT_HDCP_ANINIT(port), get_random_long());
> > > +   I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_CAPTURE_AN);
> > > +
> > > +   // Wait for An to be acquired
> > > +   if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> > > +                SKL_HDCP_STATUS_AN_READY, 1)) {
> > > +           DRM_ERROR("Timed out waiting for An\n");
> > > +           return -ETIMEDOUT;
> > > +   }
> > > +
> > > +   an.reg[0] = I915_READ(SKL_PORT_HDCP_ANLO(port));
> > > +   an.reg[1] = I915_READ(SKL_PORT_HDCP_ANHI(port));
> > > +   ret = shim->write_an_aksv(intel_dig_port, an.shim);
> > > +   if (ret)
> > > +           return ret;
> > > +
> > > +   r0_prime_gen_start = jiffies;
> > > +
> > > +   memset(&bksv, 0, sizeof(bksv));
> > > +   ret = shim->read_bksv(intel_dig_port, bksv.shim);
> > > +   if (ret)
> > > +           return ret;
> > > +
> > > +   I915_WRITE(SKL_PORT_HDCP_BKSVLO(port), bksv.reg[0]);
> > > +   I915_WRITE(SKL_PORT_HDCP_BKSVHI(port), bksv.reg[1]);
> > > +
> > > +   ret = shim->repeater_present(intel_dig_port, &repeater_present);
> > > +   if (ret)
> > > +           return ret;
> > > +   if (repeater_present)
> > > +           I915_WRITE(SKL_HDCP_REP_CTL,
> > > +                      intel_hdcp_get_repeater_ctl(intel_dig_port));
> > > +
> > > +   ret = shim->toggle_signalling(intel_dig_port, true);
> > > +   if (ret)
> > > +           return ret;
> > > +
> > > +   I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_AUTH_AND_ENC);
> > > +
> > > +   // Wait for R0 ready
> > > +   if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> > > +                (SKL_HDCP_STATUS_R0_READY | SKL_HDCP_STATUS_ENC), 1))
> {
> > > +           DRM_ERROR("Timed out waiting for R0 ready\n");
> > > +           return -ETIMEDOUT;
> > > +   }
> > > +
> > > +   /*
> > > +    * Wait for R0' to become available, the spec says 100ms from Aksv
> > > +    * write. On DP, there's an R0_READY bit available but no such bit
> > > +    * exists on HDMI. Since the upper-bound is the same, we'll just do
> > > +    * the stupid thing instead of polling on one and not the other.
> > > +    */
> > > +   wait_remaining_ms_from_jiffies(r0_prime_gen_start, 100);
> > > +
> > > +   ri.reg = 0;
> > > +   ret = shim->read_ri_prime(intel_dig_port, ri.shim);
> > > +   if (ret)
> > > +           return ret;
> > > +   I915_WRITE(SKL_PORT_HDCP_RPRIME(port), ri.reg);
> > > +
> > > +   // Wait for Ri prime match
> > > +   if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> > > +                (SKL_HDCP_STATUS_RI_MATCH | SKL_HDCP_STATUS_ENC), 1))
> {
> > > +           DRM_ERROR("Timed out waiting for Ri prime match (%x)\n",
> > > +                     I915_READ(SKL_PORT_HDCP_STATUS(port)));
> > > +           return -ETIMEDOUT;
> > > +   }
> > > +
> > > +   // Wait for encryption confirmation
> > > +   if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> > > +                 SKL_HDCP_STATUS_ENC, 20)) {
> > > +           DRM_ERROR("Timed out waiting for encryption\n");
> > > +           return -ETIMEDOUT;
> > > +   }
> > > +
> > > +   /*
> > > +    * XXX: If we have MST-connected devices, we need to enable
> encryption
> > > +    * on those as well.
> > > +    */
> > > +
> > > +   return intel_hdcp_auth_downstream(intel_dig_port, shim);
> > > +}
> > > +
> > > +static
> > > +struct intel_digital_port *conn_to_dig_port(struct intel_connector
> *connector)
> > > +{
> > > +   return enc_to_dig_port(&intel_attached_encoder(&connector->
> base)->base);
> > > +}
> > > +
> > > +static int _intel_hdcp_disable(struct intel_connector *connector)
> > > +{
> > > +   struct drm_i915_private *dev_priv = connector->base.dev->dev_
> private;
> > > +   struct intel_digital_port *intel_dig_port =
> conn_to_dig_port(connector);
> > > +   enum port port = intel_dig_port->port;
> > > +   int ret;
> > > +
> > > +   I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
> > > +   if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) == 0, 20)) {
> > > +           DRM_ERROR("Failed to disable HDCP, timeout clearing
> status\n");
> > > +           return -ETIMEDOUT;
> > > +   }
> > > +
> > > +   intel_hdcp_clear_keys(dev_priv);
> > > +
> > > +   ret = connector->hdcp_shim->toggle_signalling(intel_dig_port,
> false);
> > > +   if (ret) {
> > > +           DRM_ERROR("Failed to disable HDCP signalling\n");
> > > +           return ret;
> > > +   }
> > > +
> > > +   DRM_INFO("HDCP is disabled\n");
> > > +   return 0;
> > > +}
> > > +
> > > +static int _intel_hdcp_enable(struct intel_connector *connector)
> > > +{
> > > +   struct drm_i915_private *dev_priv = connector->base.dev->dev_
> private;
> > > +   int i, ret;
> > > +
> > > +   if (!(I915_READ(SKL_FUSE_STATUS) & SKL_FUSE_PG_DIST_STATUS(1))) {
> > > +           DRM_ERROR("PG1 is disabled, cannot load keys\n");
> > > +           return -ENXIO;
> > > +   }
> > > +
> > > +   for (i = 0; i < KEY_LOAD_TRIES; i++) {
> > > +           ret = intel_hdcp_load_keys(dev_priv);
> > > +           if (!ret)
> > > +                   break;
> > > +           intel_hdcp_clear_keys(dev_priv);
> > > +   }
> > > +   if (ret) {
> > > +           DRM_ERROR("Could not load HDCP keys, (%d)\n", ret);
> > > +           return ret;
> > > +   }
> > > +
> > > +   ret = intel_hdcp_auth(conn_to_dig_port(connector),
> > > +                         connector->hdcp_shim);
> > > +   if (ret) {
> > > +           DRM_ERROR("Failed to authenticate HDCP (%d)\n", ret);
> > > +           return ret;
> > > +   }
> > > +
> > > +   return 0;
> > > +}
> > > +
> > > +void intel_hdcp_work(struct work_struct *work)
> > > +{
> > > +   struct intel_connector *connector = container_of(to_delayed_work(
> work),
> > > +                                                    struct
> intel_connector,
> > > +                                                    hdcp_work);
> > > +   struct drm_device *dev = connector->base.dev;
> > > +   int ret;
> > > +
> > > +   drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> > > +
> > > +   ret = intel_hdcp_check_link(connector);
> > > +   if (!ret)
> > > +           schedule_delayed_work(&connector->hdcp_work,
> > > +                                 DRM_HDCP_CHECK_PERIOD_MS);
> > > +
> > > +   drm_modeset_unlock(&dev->mode_config.connection_mutex);
> > > +}
> > > +
> > > +int intel_hdcp_enable(struct intel_connector *connector)
> > > +{
> > > +   struct drm_i915_private *dev_priv = connector->base.dev->dev_
> private;
> > > +   struct drm_device *dev = &dev_priv->drm;
> > > +   struct drm_connector_state *state = connector->base.state;
> > > +   int ret;
> > > +
> > > +   WARN_ON(!drm_modeset_is_locked(&dev->mode_config.
> connection_mutex));
> > > +
> > > +   if (!connector->hdcp_shim)
> > > +           return -ENOENT;
> > > +
> > > +   ret = _intel_hdcp_enable(connector);
> > > +   if (ret)
> > > +           return ret;
> > > +
> > > +   state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
> > > +
> > > +   schedule_delayed_work(&connector->hdcp_work,
> DRM_HDCP_CHECK_PERIOD_MS);
> > > +   return 0;
> > > +}
> > > +
> > > +int intel_hdcp_disable(struct intel_connector *connector)
> > > +{
> > > +   struct drm_i915_private *dev_priv = connector->base.dev->dev_
> private;
> > > +   struct drm_device *dev = &dev_priv->drm;
> > > +
> > > +   WARN_ON(!drm_modeset_is_locked(&dev->mode_config.
> connection_mutex));
> > > +
> > > +   if (!connector->hdcp_shim)
> > > +           return -ENOENT;
> > > +
> > > +   cancel_delayed_work(&connector->hdcp_work);
> > > +
> > > +   return _intel_hdcp_disable(connector);
> > > +}
> > > +
> > > +/* Implements Part 3 of the HDCP authorization procedure */
> > > +int intel_hdcp_check_link(struct intel_connector *connector)
> > > +{
> > > +   struct drm_i915_private *dev_priv = connector->base.dev->dev_
> private;
> > > +   struct drm_device *dev = &dev_priv->drm;
> > > +   struct drm_connector_state *state = connector->base.state;
> > > +   struct intel_digital_port *intel_dig_port =
> conn_to_dig_port(connector);
> > > +   enum port port = intel_dig_port->port;
> > > +   int ret;
> > > +
> > > +   WARN_ON(!drm_modeset_is_locked(&dev->mode_config.
> connection_mutex));
> > > +
> > > +   if (state->content_protection != DRM_MODE_CONTENT_PROTECTION_
> ENABLED)
> > > +           return 0;
> > > +
> > > +   if (!connector->hdcp_shim)
> > > +           return -ENOENT;
> > > +
> > > +   if (!(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
> SKL_HDCP_STATUS_ENC)) {
> > > +           DRM_ERROR("HDCP check failed: link is not encrypted, %x\n",
> > > +                      I915_READ(SKL_PORT_HDCP_STATUS(port)));
> > > +           ret = -ENXIO;
> > > +           goto fail;
> > > +   }
> > > +
> > > +   if (connector->hdcp_shim->check_link(intel_dig_port))
> > > +           return 0;
> > > +
> > > +   DRM_INFO("HDCP link failed, retrying authentication\n");
> > > +
> > > +   ret = _intel_hdcp_disable(connector);
> > > +   if (ret) {
> > > +           DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
> > > +           goto fail;
> > > +   }
> > > +
> > > +   ret = _intel_hdcp_enable(connector);
> > > +   if (ret) {
> > > +           DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
> > > +           goto fail;
> > > +   }
> > > +
> > > +   return 0;
> > > +
> > > +fail:
> > > +   state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
> > > +   return ret;
> > > +}
> >
> > _______________________________________________
> > Intel-gfx mailing list
> > Intel-gfx@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/intel-gfx
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
>

[-- Attachment #1.2: Type: text/html, Size: 58688 bytes --]

[-- Attachment #2: Type: text/plain, Size: 160 bytes --]

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

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Intel-gfx] [RFC PATCH 3/6] drm/i915: Add HDCP framework + base implementation
  2017-12-01  8:36         ` Ramalingam C
@ 2017-12-01 14:13           ` Sean Paul
  -1 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-12-01 14:13 UTC (permalink / raw)
  To: Ramalingam C
  Cc: dri-devel, Intel Graphics Development, David Airlie,
	Linux Kernel Mailing List, Rodrigo Vivi

On Fri, Dec 1, 2017 at 3:36 AM, Ramalingam C <ramalingam.c@intel.com> wrote:
>
>
>
> On Friday 01 December 2017 01:06 PM, Daniel Vetter wrote:
>>
>> On Fri, Dec 01, 2017 at 12:53:31PM +0530, Ramalingam C wrote:
>>>
>>> Sean,
>>>
>>> IMHO, it will good if we can have all generic hdcp1.4 authentication flow in
>>> drm helpers and all interested display drivers to use them.
>>>
>>> This Design will make the extending of hdcp easy for other display drivers
>>> based on DRM.
>>>
>>> We can have the required drm_hdcp_shim type of implementation at drm
>>> structure which will be called for platform specific operations (like
>>> prepare an, send aksv, program bksv/repeater/r0 and verify sha1 etc)?
>>
>> I discussed this exact question with Sean Paul, and apparently the
>> hardware designs are too diverse to make shared code much useful. Some hw
>> has the entire hdcp flow in hw, some almost nothing (like i915 here), and
>> then there's everything in between.
>
> Just trying to understand the other extreme of HW (full)support for HDCP here.
>
> When you say everything about HDCP is implemented in HW, do you mean that whole protocol comm on HDCP link also driven by HW?
>

Yep. Check out the rockchip implementation in our 4.4 tree.

Sean


>
> --Ram
>
>>
>> Given that Sean has seen a lot more hdcp implementations than we have,
>> that we right now have no other implementation than i915 in upstream and
>> than wrong abstraction is much harder to fix than no abstraction I'm going
>> with Sean's approach of "no generic abstraction" here. Personally I'm not
>> even fully sold on the shim abstraction, but I think by&large that one is
>> fine.
>>
>>> On Thursday 30 November 2017 08:38 AM, Sean Paul wrote:
>>>>
>>>> This patch adds the framework required to add HDCP support to intel
>>>> connectors. It implements Aksv loading from fuse, and parts 1/2/3
>>>> of the HDCP authentication scheme.
>>>>
>>>> Note that without shim implementations, this does not actually implement
>>>> HDCP. That will come in subsequent patches.
>>>>
>>>> Signed-off-by: Sean Paul <seanpaul@chromium.org>
>>>> ---
>>>>    drivers/gpu/drm/i915/Makefile       |   1 +
>>>>    drivers/gpu/drm/i915/i915_reg.h     |  83 +++++
>>>>    drivers/gpu/drm/i915/intel_atomic.c |  26 +-
>>>>    drivers/gpu/drm/i915/intel_ddi.c    |  14 +
>>>>    drivers/gpu/drm/i915/intel_drv.h    |  53 +++
>>>>    drivers/gpu/drm/i915/intel_hdcp.c   | 636 ++++++++++++++++++++++++++++++++++++
>>>>    6 files changed, 811 insertions(+), 2 deletions(-)
>>>>    create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c
>>>>
>>>> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
>>>> index 6c3b0481ef82..1e745508e437 100644
>>>> --- a/drivers/gpu/drm/i915/Makefile
>>>> +++ b/drivers/gpu/drm/i915/Makefile
>>>> @@ -87,6 +87,7 @@ i915-y += intel_audio.o \
>>>>           intel_fbc.o \
>>>>           intel_fifo_underrun.o \
>>>>           intel_frontbuffer.o \
>>>> +         intel_hdcp.o \
>>>>           intel_hotplug.o \
>>>>           intel_modes.o \
>>>>           intel_overlay.o \
>>>> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
>>>> index 68a58cce6ab1..43128030171d 100644
>>>> --- a/drivers/gpu/drm/i915/i915_reg.h
>>>> +++ b/drivers/gpu/drm/i915/i915_reg.h
>>>> @@ -7991,6 +7991,7 @@ enum {
>>>>    #define     GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT 8
>>>>    #define     GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT 16
>>>>    #define     GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT 24
>>>> +#define   SKL_PCODE_LOAD_HDCP_KEYS             0x5
>>>>    #define   SKL_PCODE_CDCLK_CONTROL            0x7
>>>>    #define     SKL_CDCLK_PREPARE_FOR_CHANGE     0x3
>>>>    #define     SKL_CDCLK_READY_FOR_CHANGE               0x1
>>>> @@ -8285,6 +8286,88 @@ enum skl_power_gate {
>>>>    #define  SKL_PW_TO_PG(pw)                    ((pw) - SKL_DISP_PW_1 + SKL_PG1)
>>>>    #define  SKL_FUSE_PG_DIST_STATUS(pg)         (1 << (27 - (pg)))
>>>> +
>>>> +/* HDCP Key Registers */
>>>> +#define SKL_HDCP_KEY_CONF              _MMIO(0x66c00)
>>>> +#define         SKL_HDCP_AKSV_SEND_TRIGGER     BIT(31)
>>>> +#define  SKL_HDCP_CLEAR_KEYS_TRIGGER   BIT(30)
>>>> +#define SKL_HDCP_KEY_STATUS            _MMIO(0x66c04)
>>>> +#define  SKL_HDCP_FUSE_IN_PROGRESS     BIT(7)
>>>> +#define  SKL_HDCP_FUSE_ERROR           BIT(6)
>>>> +#define  SKL_HDCP_FUSE_DONE            BIT(5)
>>>> +#define  SKL_HDCP_KEY_LOAD_STATUS      BIT(1)
>>>> +#define  SKL_HDCP_KEY_LOAD_DONE                BIT(0)
>>>> +#define SKL_HDCP_AKSV_LO               _MMIO(0x66c10)
>>>> +#define SKL_HDCP_AKSV_HI               _MMIO(0x66c14)
>>>> +
>>>> +/* HDCP Repeater Registers */
>>>> +#define SKL_HDCP_REP_CTL               _MMIO(0x66d00)
>>>> +#define  SKL_HDCP_DDIB_REP_PRESENT     BIT(30)
>>>> +#define  SKL_HDCP_DDIA_REP_PRESENT     BIT(29)
>>>> +#define  SKL_HDCP_DDIC_REP_PRESENT     BIT(28)
>>>> +#define  SKL_HDCP_DDID_REP_PRESENT     BIT(27)
>>>> +#define  SKL_HDCP_DDIF_REP_PRESENT     BIT(26)
>>>> +#define  SKL_HDCP_DDIE_REP_PRESENT     BIT(25)
>>>> +#define  SKL_HDCP_DDIB_SHA1_M0         (1 << 20)
>>>> +#define  SKL_HDCP_DDIA_SHA1_M0         (2 << 20)
>>>> +#define  SKL_HDCP_DDIC_SHA1_M0         (3 << 20)
>>>> +#define  SKL_HDCP_DDID_SHA1_M0         (4 << 20)
>>>> +#define  SKL_HDCP_DDIF_SHA1_M0         (5 << 20)
>>>> +#define  SKL_HDCP_DDIE_SHA1_M0         (6 << 20) // Bspec says 5?
>>>> +#define  SKL_HDCP_SHA1_BUSY            BIT(16)
>>>> +#define  SKL_HDCP_SHA1_READY           BIT(17)
>>>> +#define  SKL_HDCP_SHA1_COMPLETE                BIT(18)
>>>> +#define  SKL_HDCP_SHA1_V_MATCH         BIT(19)
>>>> +#define  SKL_HDCP_SHA1_TEXT_32         (1 << 1)
>>>> +#define  SKL_HDCP_SHA1_COMPLETE_HASH   (2 << 1)
>>>> +#define  SKL_HDCP_SHA1_TEXT_24         (4 << 1)
>>>> +#define  SKL_HDCP_SHA1_TEXT_16         (5 << 1)
>>>> +#define  SKL_HDCP_SHA1_TEXT_8          (6 << 1)
>>>> +#define  SKL_HDCP_SHA1_TEXT_0          (7 << 1)
>>>> +#define SKL_HDCP_SHA_V_PRIME_H0                _MMIO(0x66d04)
>>>> +#define SKL_HDCP_SHA_V_PRIME_H1                _MMIO(0x66d08)
>>>> +#define SKL_HDCP_SHA_V_PRIME_H2                _MMIO(0x66d0C)
>>>> +#define SKL_HDCP_SHA_V_PRIME_H3                _MMIO(0x66d10)
>>>> +#define SKL_HDCP_SHA_V_PRIME_H4                _MMIO(0x66d14)
>>>> +#define SKL_HDCP_SHA_V_PRIME(h)                _MMIO((0x66d04 + h * 4))
>>>> +#define SKL_HDCP_SHA_TEXT              _MMIO(0x66d18)
>>>> +
>>>> +/* HDCP Auth Registers */
>>>> +#define _SKL_PORTA_HDCP_AUTHENC                0x66800
>>>> +#define _SKL_PORTB_HDCP_AUTHENC                0x66500
>>>> +#define _SKL_PORTC_HDCP_AUTHENC                0x66600
>>>> +#define _SKL_PORTD_HDCP_AUTHENC                0x66700
>>>> +#define _SKL_PORTE_HDCP_AUTHENC                0x66A00
>>>> +#define _SKL_PORTF_HDCP_AUTHENC                0x66900
>>>> +#define _SKL_PORT_HDCP_AUTHENC(port, x)        _MMIO(_PICK(port, \
>>>> +                                         _SKL_PORTA_HDCP_AUTHENC, \
>>>> +                                         _SKL_PORTB_HDCP_AUTHENC, \
>>>> +                                         _SKL_PORTC_HDCP_AUTHENC, \
>>>> +                                         _SKL_PORTD_HDCP_AUTHENC, \
>>>> +                                         _SKL_PORTE_HDCP_AUTHENC, \
>>>> +                                         _SKL_PORTF_HDCP_AUTHENC) + x)
>>>> +#define SKL_PORT_HDCP_CONF(port)       _SKL_PORT_HDCP_AUTHENC(port, 0x0)
>>>> +#define  SKL_HDCP_CONF_CAPTURE_AN      BIT(0)
>>>> +#define  SKL_HDCP_CONF_AUTH_AND_ENC    (BIT(1) | BIT(0))
>>>> +#define SKL_PORT_HDCP_ANINIT(port)     _SKL_PORT_HDCP_AUTHENC(port, 0x4)
>>>> +#define SKL_PORT_HDCP_ANLO(port)       _SKL_PORT_HDCP_AUTHENC(port, 0x8)
>>>> +#define SKL_PORT_HDCP_ANHI(port)       _SKL_PORT_HDCP_AUTHENC(port, 0xC)
>>>> +#define SKL_PORT_HDCP_BKSVLO(port)     _SKL_PORT_HDCP_AUTHENC(port, 0x10)
>>>> +#define SKL_PORT_HDCP_BKSVHI(port)     _SKL_PORT_HDCP_AUTHENC(port, 0x14)
>>>> +#define SKL_PORT_HDCP_RPRIME(port)     _SKL_PORT_HDCP_AUTHENC(port, 0x18)
>>>> +#define SKL_PORT_HDCP_STATUS(port)     _SKL_PORT_HDCP_AUTHENC(port, 0x1C)
>>>> +#define  SKL_HDCP_STATUS_STREAM_A_ENC  BIT(31)
>>>> +#define  SKL_HDCP_STATUS_STREAM_B_ENC  BIT(30)
>>>> +#define  SKL_HDCP_STATUS_STREAM_C_ENC  BIT(29)
>>>> +#define  SKL_HDCP_STATUS_STREAM_D_ENC  BIT(28)
>>>> +#define  SKL_HDCP_STATUS_AUTH          BIT(21)
>>>> +#define  SKL_HDCP_STATUS_ENC           BIT(20)
>>>> +#define  SKL_HDCP_STATUS_RI_MATCH      BIT(19)
>>>> +#define  SKL_HDCP_STATUS_R0_READY      BIT(18)
>>>> +#define  SKL_HDCP_STATUS_AN_READY      BIT(17)
>>>> +#define  SKL_HDCP_STATUS_CIPHER                BIT(16)
>>>> +#define  SKL_HDCP_STATUS_FRAME_CNT(x)  ((x >> 8) & 0xff)
>>>> +
>>>>    /* Per-pipe DDI Function Control */
>>>>    #define _TRANS_DDI_FUNC_CTL_A                0x60400
>>>>    #define _TRANS_DDI_FUNC_CTL_B                0x61400
>>>> diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
>>>> index 36d4e635e4ce..ddf08227d9cb 100644
>>>> --- a/drivers/gpu/drm/i915/intel_atomic.c
>>>> +++ b/drivers/gpu/drm/i915/intel_atomic.c
>>>> @@ -109,12 +109,34 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
>>>>         struct intel_digital_connector_state *old_conn_state =
>>>>                 to_intel_digital_connector_state(old_state);
>>>>         struct drm_crtc_state *crtc_state;
>>>> -
>>>> -       if (!new_state->crtc)
>>>> +       uint64_t old_cp = old_conn_state->base.content_protection;
>>>> +       uint64_t new_cp = new_state->content_protection;
>>>> +
>>>> +       if (!new_state->crtc) {
>>>> +               /*
>>>> +                * If the connector is being disabled with CP enabled, mark it
>>>> +                * desired so it's re-enabled when the connector is brought back
>>>> +                */
>>>> +               if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
>>>> +                       new_state->content_protection =
>>>> +                               DRM_MODE_CONTENT_PROTECTION_DESIRED;
>>>>                 return 0;
>>>> +       }
>>>>         crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc);
>>>> +       if (new_cp != old_cp) {
>>>> +               /* Only drivers can set content protection enabled */
>>>> +               if (new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
>>>> +                       new_state->content_protection =
>>>> +                               DRM_MODE_CONTENT_PROTECTION_DESIRED;
>>>> +
>>>> +               /* Involve the encoder/connector to enable/disable CP */
>>>> +               if (new_cp == DRM_MODE_CONTENT_PROTECTION_OFF ||
>>>> +                   old_cp == DRM_MODE_CONTENT_PROTECTION_OFF)
>>>> +                       crtc_state->mode_changed = true;
>>>
>>> We need not perform the mode set for hdcp enable/disable.
>>> Authentication and encryption can be started on active port.
>>
>> Was simpler to implement this way :-) We can fix this by pushing the hdcp
>> enable/disable code into a post-modeset operation. Ville had written the
>> infrastructure for that to fix a few fastboot corner cases. But that
>> infrastructure hasn't landed yet, so probably better to do that in a
>> follow-up.
>>
>> I also guess that CrOS simply set this to desired every time they enable
>> an external screen, so it won't result in an unecessary modeset.
>>
>>>> +       }
>>>> +
>>>>         /*
>>>>          * These properties are handled by fastset, and might not end
>>>>          * up in a modeset.
>>>> diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
>>>> index 933c18fd4258..0e69337f410d 100644
>>>> --- a/drivers/gpu/drm/i915/intel_ddi.c
>>>> +++ b/drivers/gpu/drm/i915/intel_ddi.c
>>>> @@ -2432,10 +2432,17 @@ static void intel_enable_ddi(struct intel_encoder *encoder,
>>>>                              const struct intel_crtc_state *crtc_state,
>>>>                              const struct drm_connector_state *conn_state)
>>>>    {
>>>> +       struct drm_connector *connector = conn_state->connector;
>>>> +       struct intel_connector *intel_connector = to_intel_connector(connector);
>>>> +
>>>>         if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
>>>>                 intel_enable_ddi_hdmi(encoder, crtc_state, conn_state);
>>>>         else
>>>>                 intel_enable_ddi_dp(encoder, crtc_state, conn_state);
>>>> +
>>>> +       if (conn_state->content_protection ==
>>>> +                       DRM_MODE_CONTENT_PROTECTION_DESIRED)
>>>> +               intel_hdcp_enable(intel_connector);
>>>>    }
>>>>    static void intel_disable_ddi_dp(struct intel_encoder *encoder,
>>>> @@ -2468,10 +2475,17 @@ static void intel_disable_ddi(struct intel_encoder *encoder,
>>>>                               const struct intel_crtc_state *old_crtc_state,
>>>>                               const struct drm_connector_state *old_conn_state)
>>>>    {
>>>> +       struct drm_connector *connector = old_conn_state->connector;
>>>> +       struct intel_connector *intel_connector = to_intel_connector(connector);
>>>> +
>>>>         if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
>>>>                 intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state);
>>>>         else
>>>>                 intel_disable_ddi_dp(encoder, old_crtc_state, old_conn_state);
>>>> +
>>>> +       if (old_conn_state->content_protection !=
>>>> +                       DRM_MODE_CONTENT_PROTECTION_OFF)
>>>> +               intel_hdcp_disable(intel_connector);
>>>
>>> We might want to disable the hdcp before disabling the DDI. Actually we
>>> could trigger hdcp disable at connector state change(due to hot-unplug)
>>> also.
>>
>> Yeah this part needs to be reworked a bit, also because the locking
>> doesn't work yet. I think hot-unplug is handled by the worker thread
>> already, it does the mandatory regular polling.
>> -Daniel
>>
>>> Thanks,
>>> --Ram
>>>>
>>>>    }
>>>>    static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder,
>>>> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
>>>> index 47d022d48718..8924004575b8 100644
>>>> --- a/drivers/gpu/drm/i915/intel_drv.h
>>>> +++ b/drivers/gpu/drm/i915/intel_drv.h
>>>> @@ -299,6 +299,49 @@ struct intel_panel {
>>>>         } backlight;
>>>>    };
>>>> +struct intel_hdcp_shim {
>>>> +       /* Outputs the transmitter's An and Aksv values to the receiver. */
>>>> +       int (*write_an_aksv)(struct intel_digital_port *intel_dig_port, u8 *an);
>>>> +
>>>> +       /* Reads the receiver's key selection vector */
>>>> +       int (*read_bksv)(struct intel_digital_port *intel_dig_port, u8 *bksv);
>>>> +
>>>> +       /*
>>>> +        * Reads BINFO from DP receivers and BSTATUS from HDMI receivers. The
>>>> +        * definitions are the same in the respective specs, but the names are
>>>> +        * different. Call it BSTATUS since that's the name the HDMI spec
>>>> +        * uses and it was there first.
>>>> +        */
>>>> +       int (*read_bstatus)(struct intel_digital_port *intel_dig_port,
>>>> +                           u8 *bstatus);
>>>> +
>>>> +       /* Determines whether a repeater is present downstream */
>>>> +       int (*repeater_present)(struct intel_digital_port *intel_dig_port,
>>>> +                               bool *repeater_present);
>>>> +
>>>> +       /* Reads the receiver's Ri' value */
>>>> +       int (*read_ri_prime)(struct intel_digital_port *intel_dig_port, u8 *ri);
>>>> +
>>>> +       /* Determines if the receiver's KSV FIFO is ready for consumption */
>>>> +       int (*read_ksv_ready)(struct intel_digital_port *intel_dig_port,
>>>> +                             bool *ksv_ready);
>>>> +
>>>> +       /* Reads the ksv fifo for num_downstream devices */
>>>> +       int (*read_ksv_fifo)(struct intel_digital_port *intel_dig_port,
>>>> +                            int num_downstream, u8 *ksv_fifo);
>>>> +
>>>> +       /* Reads a 32-bit part of V' from the receiver */
>>>> +       int (*read_v_prime_part)(struct intel_digital_port *intel_dig_port,
>>>> +                                int i, u32 *part);
>>>> +
>>>> +       /* Enables HDCP signalling on the port */
>>>> +       int (*toggle_signalling)(struct intel_digital_port *intel_dig_port,
>>>> +                                bool enable);
>>>> +
>>>> +       /* Ensures the link is still protected */
>>>> +       bool (*check_link)(struct intel_digital_port *intel_dig_port);
>>>> +};
>>>> +
>>>>    struct intel_connector {
>>>>         struct drm_connector base;
>>>>         /*
>>>> @@ -330,6 +373,9 @@ struct intel_connector {
>>>>         /* Work struct to schedule a uevent on link train failure */
>>>>         struct work_struct modeset_retry_work;
>>>> +
>>>> +       const struct intel_hdcp_shim *hdcp_shim;
>>>> +       struct delayed_work hdcp_work;
>>>>    };
>>>>    struct intel_digital_connector_state {
>>>> @@ -1295,6 +1341,8 @@ void intel_ddi_set_vc_payload_alloc(const struct intel_crtc_state *crtc_state,
>>>>                                     bool state);
>>>>    u32 bxt_signal_levels(struct intel_dp *intel_dp);
>>>>    uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
>>>> +int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder);
>>>> +int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder);
>>>>    u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
>>>>    unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
>>>> @@ -1746,6 +1794,11 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
>>>>    }
>>>>    #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
>>>> +/* intel_hdcp.c */
>>>> +int intel_hdcp_enable(struct intel_connector *connector);
>>>> +int intel_hdcp_disable(struct intel_connector *connector);
>>>> +int intel_hdcp_check_link(struct intel_connector *connector);
>>>> +void intel_hdcp_work(struct work_struct *work);
>>>>    /* intel_psr.c */
>>>>    void intel_psr_enable(struct intel_dp *intel_dp,
>>>> diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
>>>> new file mode 100644
>>>> index 000000000000..a2a575ed657e
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/i915/intel_hdcp.c
>>>> @@ -0,0 +1,636 @@
>>>> +/*
>>>> + * Copyright (C) 2017 Google, Inc.
>>>> + *
>>>> + * This software is licensed under the terms of the GNU General Public
>>>> + * License version 2, as published by the Free Software Foundation, and
>>>> + * may be copied, distributed, and modified under those terms.
>>>> + *
>>>> + * This program is distributed in the hope that it will be useful,
>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>>> + * GNU General Public License for more details.
>>>> + */
>>>> +
>>>> +#include <drm/drmP.h>
>>>> +#include <drm/drm_hdcp.h>
>>>> +#include <linux/i2c.h>
>>>> +#include <linux/random.h>
>>>> +
>>>> +#include "intel_drv.h"
>>>> +#include "i915_reg.h"
>>>> +
>>>> +#define KEY_LOAD_TRIES 5
>>>> +
>>>> +static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
>>>> +                                   const struct intel_hdcp_shim *shim)
>>>> +{
>>>> +       unsigned long timeout = jiffies + msecs_to_jiffies_timeout(500);
>>>> +       int ret;
>>>> +       bool ksv_ready;
>>>> +
>>>> +       while (true) {
>>>> +               ret = shim->read_ksv_ready(intel_dig_port, &ksv_ready);
>>>> +               if (ret)
>>>> +                       return ret;
>>>> +               if (ksv_ready)
>>>> +                       break;
>>>> +               if (time_after(jiffies, timeout))
>>>> +                       return -ETIMEDOUT;
>>>> +               msleep(100);
>>>> +       }
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static void intel_hdcp_clear_keys(struct drm_i915_private *dev_priv)
>>>> +{
>>>> +       I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_CLEAR_KEYS_TRIGGER);
>>>> +       I915_WRITE(SKL_HDCP_KEY_STATUS,
>>>> +                  SKL_HDCP_KEY_LOAD_DONE | SKL_HDCP_KEY_LOAD_STATUS |
>>>> +                  SKL_HDCP_FUSE_IN_PROGRESS | SKL_HDCP_FUSE_ERROR |
>>>> +                  SKL_HDCP_FUSE_DONE);
>>>> +}
>>>> +
>>>> +static int intel_hdcp_load_keys(struct drm_i915_private *dev_priv)
>>>> +{
>>>> +       unsigned long timeout;
>>>> +       int ret;
>>>> +       u32 val;
>>>> +
>>>> +       // Initiate loading the HDCP key from fuses
>>>> +       mutex_lock(&dev_priv->pcu_lock);
>>>> +       ret = sandybridge_pcode_write(dev_priv, SKL_PCODE_LOAD_HDCP_KEYS, 1);
>>>> +       mutex_unlock(&dev_priv->pcu_lock);
>>>> +       if (ret) {
>>>> +               DRM_ERROR("Failed to initiate HDCP key load (%d)\n", ret);
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       // Wait for the keys to load (500us)
>>>> +       timeout = jiffies + nsecs_to_jiffies_timeout(500 * 1000);
>>>> +       while (true) {
>>>> +               val = I915_READ(SKL_HDCP_KEY_STATUS);
>>>> +               if (val & SKL_HDCP_KEY_LOAD_DONE)
>>>> +                       break;
>>>> +               if (time_after(jiffies, timeout))
>>>> +                       return -ETIMEDOUT;
>>>> +               usleep_range(50, 100);
>>>> +       }
>>>> +       if (!(val & SKL_HDCP_KEY_LOAD_STATUS))
>>>> +               return -ENXIO;
>>>> +
>>>> +       // Send Aksv over to PCH display for use in authentication
>>>> +       I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_AKSV_SEND_TRIGGER);
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +/* Returns updated SHA-1 index */
>>>> +static int intel_write_sha_text(struct drm_i915_private *dev_priv, u32 sha_text)
>>>> +{
>>>> +       I915_WRITE(SKL_HDCP_SHA_TEXT, sha_text);
>>>> +       if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_READY, 1)) {
>>>> +               DRM_ERROR("Timed out waiting for SHA1 ready\n");
>>>> +               return -ETIMEDOUT;
>>>> +       }
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static
>>>> +u32 intel_hdcp_get_repeater_ctl(struct intel_digital_port *intel_dig_port)
>>>> +{
>>>> +       enum port port = intel_dig_port->port;
>>>> +       switch(port) {
>>>> +       case PORT_A:
>>>> +               return SKL_HDCP_DDIA_REP_PRESENT | SKL_HDCP_DDIA_SHA1_M0;
>>>> +       case PORT_B:
>>>> +               return SKL_HDCP_DDIB_REP_PRESENT | SKL_HDCP_DDIB_SHA1_M0;
>>>> +       case PORT_C:
>>>> +               return SKL_HDCP_DDIC_REP_PRESENT | SKL_HDCP_DDIC_SHA1_M0;
>>>> +       case PORT_D:
>>>> +               return SKL_HDCP_DDID_REP_PRESENT | SKL_HDCP_DDID_SHA1_M0;
>>>> +       case PORT_E:
>>>> +               return SKL_HDCP_DDIE_REP_PRESENT | SKL_HDCP_DDIE_SHA1_M0;
>>>> +       default:
>>>> +               break;
>>>> +       }
>>>> +       DRM_ERROR("Unknown port %d\n", port);
>>>> +       return -EINVAL;
>>>> +}
>>>> +
>>>> +/* Implements Part 2 of the HDCP authorization procedure */
>>>> +static
>>>> +int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
>>>> +                              const struct intel_hdcp_shim *shim)
>>>> +{
>>>> +       struct drm_i915_private *dev_priv;
>>>> +       u32 vprime, sha_text, sha_leftovers, rep_ctl;
>>>> +       u8 bstatus[2], num_downstream, *ksv_fifo;
>>>> +       int ret, i, j, sha_idx;
>>>> +
>>>> +       dev_priv = intel_dig_port->base.base.dev->dev_private;
>>>> +
>>>> +       ret = shim->read_bstatus(intel_dig_port, bstatus);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       /* If there are no downstream devices, we're all done. */
>>>> +       num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
>>>> +       if (num_downstream == 0) {
>>>> +               DRM_INFO("HDCP is enabled (no downstream devices)\n");
>>>> +               return 0;
>>>> +       }
>>>> +
>>>> +       // Poll for ksv list ready (spec says max time allowed is 5s)
>>>> +       ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim);
>>>> +       if (ret) {
>>>> +               DRM_ERROR("KSV list failed to become ready (%d)\n", ret);
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL);
>>>> +       if (!ksv_fifo)
>>>> +               return -ENOMEM;
>>>> +
>>>> +       ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       // Process V' values from the receiver
>>>> +       for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
>>>> +               ret = shim->read_v_prime_part(intel_dig_port, i, &vprime);
>>>> +               if (ret)
>>>> +                       return ret;
>>>> +               I915_WRITE(SKL_HDCP_SHA_V_PRIME(i), vprime);
>>>> +       }
>>>> +
>>>> +       /*
>>>> +        * We need to write the concatenation of all device KSVs, BINFO (DP) ||
>>>> +        * BSTATUS (HDMI), and M0 (which is added via HDCP_REP_CTL). This byte
>>>> +        * stream is written via the HDCP_SHA_TEXT register in 32-bit
>>>> +        * increments. Every 64 bytes, we need to write HDCP_REP_CTL again. This
>>>> +        * index will keep track of our progress through the 64 bytes as well as
>>>> +        * helping us work the 40-bit KSVs through our 32-bit register.
>>>> +        *
>>>> +        * NOTE: data passed via HDCP_SHA_TEXT should be big-endian
>>>> +        */
>>>> +       sha_idx = 0;
>>>> +       sha_text = 0;
>>>> +       sha_leftovers = 0;
>>>> +       rep_ctl = intel_hdcp_get_repeater_ctl(intel_dig_port);
>>>> +       I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>>> +       for (i = 0; i < num_downstream; i++) {
>>>> +               unsigned sha_empty;
>>>> +               u8 *ksv = &ksv_fifo[i * DRM_HDCP_KSV_LEN];
>>>> +
>>>> +               // Fill up the empty slots in sha_text and write it out
>>>> +               sha_empty = sizeof(sha_text) - sha_leftovers;
>>>> +               for (j = 0; j < sha_empty; j++)
>>>> +                       sha_text |= ksv[j] << ((sizeof(sha_text) - j - 1) * 8);
>>>> +
>>>> +               ret = intel_write_sha_text(dev_priv, sha_text);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +
>>>> +               // Programming guide writes this every 64 bytes
>>>> +               sha_idx += sizeof(sha_text);
>>>> +               if (!(sha_idx % 64))
>>>> +                       I915_WRITE(SKL_HDCP_REP_CTL,
>>>> +                                  rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>>> +
>>>> +               // Store the leftover bytes from the ksv in sha_text
>>>> +               sha_leftovers = DRM_HDCP_KSV_LEN - sha_empty;
>>>> +               sha_text = 0;
>>>> +               for (j = 0; j < sha_leftovers; j++)
>>>> +                       sha_text |= ksv[sha_empty + j] <<
>>>> +                                       ((sizeof(sha_text) - j - 1) * 8);
>>>> +
>>>> +               /*
>>>> +                * If we still have room in sha_text for more data, continue.
>>>> +                * Otherwise, write it out immediately.
>>>> +                */
>>>> +               if (sizeof(sha_text) > sha_leftovers)
>>>> +                       continue;
>>>> +
>>>> +               ret = intel_write_sha_text(dev_priv, sha_text);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_leftovers = 0;
>>>> +               sha_text = 0;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +       }
>>>> +
>>>> +       /*
>>>> +        * We need to write BINFO/BSTATUS, and M0 now. Depending on how many
>>>> +        * bytes are leftover from the last ksv, we might be able to fit them
>>>> +        * all in sha_text (first 2 cases), or we might need to split them up
>>>> +        * into 2 writes (last 2 cases).
>>>> +        */
>>>> +       if (sha_leftovers == 0) {
>>>> +               // Write 16 bits of text, 16 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
>>>> +               ret = intel_write_sha_text(dev_priv,
>>>> +                                          bstatus[0] << 8 | bstatus[1]);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +               // Write 32 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>>>> +               ret = intel_write_sha_text(dev_priv, 0);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +               // Write 16 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
>>>> +               ret = intel_write_sha_text(dev_priv, 0);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +       } else if (sha_leftovers == 1) {
>>>> +               // Write 24 bits of text, 8 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
>>>> +               sha_text |= bstatus[0] << 16 | bstatus[1] << 8;
>>>> +               // Only 24-bits of data, must be in the LSB
>>>> +               sha_text = (sha_text & 0xffffff00) >> 8;
>>>> +               ret = intel_write_sha_text(dev_priv, sha_text);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +               // Write 32 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>>>> +               ret = intel_write_sha_text(dev_priv, 0);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +               // Write 24 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
>>>> +               ret = intel_write_sha_text(dev_priv, 0);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +       } else if (sha_leftovers == 2) {
>>>> +               // Write 32 bits of text
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>>> +               sha_text |= bstatus[0] << 24 | bstatus[1] << 16;
>>>> +               ret = intel_write_sha_text(dev_priv, sha_text);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +               // Write 64 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>>>> +               for (i = 0; i < 2; i++) {
>>>> +                       ret = intel_write_sha_text(dev_priv, 0);
>>>> +                       if (ret < 0)
>>>> +                               return ret;
>>>> +                       sha_idx += sizeof(sha_text);
>>>> +               }
>>>> +       } else if (sha_leftovers == 3) {
>>>> +               // Write 32 bits of text
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>>> +               sha_text |= bstatus[0] << 24;
>>>> +               ret = intel_write_sha_text(dev_priv, sha_text);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +               // Write 8 bits of text, 24 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
>>>> +               ret = intel_write_sha_text(dev_priv, bstatus[1]);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +               // Write 32 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>>>> +               ret = intel_write_sha_text(dev_priv, 0);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +               // Write 8 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
>>>> +               ret = intel_write_sha_text(dev_priv, 0);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +       } else {
>>>> +               DRM_ERROR("Invalid number of leftovers %d\n", sha_leftovers);
>>>> +               return -EINVAL;
>>>> +       }
>>>> +
>>>> +       I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>>> +       // Fill up to 64 - 4 bytes with zeros (leave the last write for length)
>>>> +       while ((sha_idx % 64) < (64 - sizeof(sha_text))) {
>>>> +               ret = intel_write_sha_text(dev_priv, 0);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +       }
>>>> +
>>>> +       /*
>>>> +        * Last write gets the length of the concatenation in bits. That is:
>>>> +        *  - 5 bytes per device
>>>> +        *  - 10 bytes for BINFO/BSTATUS(2), M0(8)
>>>> +        */
>>>> +       sha_text = (num_downstream * 5 + 10) * 8;
>>>> +       ret = intel_write_sha_text(dev_priv, sha_text);
>>>> +       if (ret < 0)
>>>> +               return ret;
>>>> +
>>>> +       // Finally, tell the HW we're done with the hash and wait for it to ACK
>>>> +       I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_COMPLETE_HASH);
>>>> +       if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_COMPLETE, 1)) {
>>>> +               DRM_ERROR("Timed out waiting for SHA1 complete\n");
>>>> +               return -ETIMEDOUT;
>>>> +       }
>>>> +       if (!(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_V_MATCH)) {
>>>> +               DRM_ERROR("SHA-1 mismatch, HDCP failed\n");
>>>> +               return -ENXIO;
>>>> +       }
>>>> +
>>>> +       DRM_INFO("HDCP is enabled (%d downstream devices)\n", num_downstream);
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +/* Implements Part 1 of the HDCP authorization procedure */
>>>> +static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
>>>> +                          const struct intel_hdcp_shim *shim)
>>>> +{
>>>> +       struct drm_i915_private *dev_priv;
>>>> +       enum port port;
>>>> +       unsigned long r0_prime_gen_start;
>>>> +       int ret, i;
>>>> +       union {
>>>> +               u32 reg[2];
>>>> +               u8 shim[DRM_HDCP_AN_LEN];
>>>> +       } an;
>>>> +       union {
>>>> +               u32 reg[2];
>>>> +               u8 shim[DRM_HDCP_KSV_LEN];
>>>> +       } bksv;
>>>> +       union {
>>>> +               u32 reg;
>>>> +               u8 shim[DRM_HDCP_RI_LEN];
>>>> +       } ri;
>>>> +       bool repeater_present;
>>>> +
>>>> +       dev_priv = intel_dig_port->base.base.dev->dev_private;
>>>> +
>>>> +       port = intel_dig_port->port;
>>>> +
>>>> +       // Initialize An with 2 random values and acquire it
>>>> +       for (i = 0; i < 2; i++)
>>>> +               I915_WRITE(SKL_PORT_HDCP_ANINIT(port), get_random_long());
>>>> +       I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_CAPTURE_AN);
>>>> +
>>>> +       // Wait for An to be acquired
>>>> +       if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>>>> +                    SKL_HDCP_STATUS_AN_READY, 1)) {
>>>> +               DRM_ERROR("Timed out waiting for An\n");
>>>> +               return -ETIMEDOUT;
>>>> +       }
>>>> +
>>>> +       an.reg[0] = I915_READ(SKL_PORT_HDCP_ANLO(port));
>>>> +       an.reg[1] = I915_READ(SKL_PORT_HDCP_ANHI(port));
>>>> +       ret = shim->write_an_aksv(intel_dig_port, an.shim);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       r0_prime_gen_start = jiffies;
>>>> +
>>>> +       memset(&bksv, 0, sizeof(bksv));
>>>> +       ret = shim->read_bksv(intel_dig_port, bksv.shim);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       I915_WRITE(SKL_PORT_HDCP_BKSVLO(port), bksv.reg[0]);
>>>> +       I915_WRITE(SKL_PORT_HDCP_BKSVHI(port), bksv.reg[1]);
>>>> +
>>>> +       ret = shim->repeater_present(intel_dig_port, &repeater_present);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +       if (repeater_present)
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL,
>>>> +                          intel_hdcp_get_repeater_ctl(intel_dig_port));
>>>> +
>>>> +       ret = shim->toggle_signalling(intel_dig_port, true);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_AUTH_AND_ENC);
>>>> +
>>>> +       // Wait for R0 ready
>>>> +       if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>>>> +                    (SKL_HDCP_STATUS_R0_READY | SKL_HDCP_STATUS_ENC), 1)) {
>>>> +               DRM_ERROR("Timed out waiting for R0 ready\n");
>>>> +               return -ETIMEDOUT;
>>>> +       }
>>>> +
>>>> +       /*
>>>> +        * Wait for R0' to become available, the spec says 100ms from Aksv
>>>> +        * write. On DP, there's an R0_READY bit available but no such bit
>>>> +        * exists on HDMI. Since the upper-bound is the same, we'll just do
>>>> +        * the stupid thing instead of polling on one and not the other.
>>>> +        */
>>>> +       wait_remaining_ms_from_jiffies(r0_prime_gen_start, 100);
>>>> +
>>>> +       ri.reg = 0;
>>>> +       ret = shim->read_ri_prime(intel_dig_port, ri.shim);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +       I915_WRITE(SKL_PORT_HDCP_RPRIME(port), ri.reg);
>>>> +
>>>> +       // Wait for Ri prime match
>>>> +       if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>>>> +                    (SKL_HDCP_STATUS_RI_MATCH | SKL_HDCP_STATUS_ENC), 1)) {
>>>> +               DRM_ERROR("Timed out waiting for Ri prime match (%x)\n",
>>>> +                         I915_READ(SKL_PORT_HDCP_STATUS(port)));
>>>> +               return -ETIMEDOUT;
>>>> +       }
>>>> +
>>>> +       // Wait for encryption confirmation
>>>> +       if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>>>> +                     SKL_HDCP_STATUS_ENC, 20)) {
>>>> +               DRM_ERROR("Timed out waiting for encryption\n");
>>>> +               return -ETIMEDOUT;
>>>> +       }
>>>> +
>>>> +       /*
>>>> +        * XXX: If we have MST-connected devices, we need to enable encryption
>>>> +        * on those as well.
>>>> +        */
>>>> +
>>>> +       return intel_hdcp_auth_downstream(intel_dig_port, shim);
>>>> +}
>>>> +
>>>> +static
>>>> +struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector)
>>>> +{
>>>> +       return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base);
>>>> +}
>>>> +
>>>> +static int _intel_hdcp_disable(struct intel_connector *connector)
>>>> +{
>>>> +       struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>>> +       struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
>>>> +       enum port port = intel_dig_port->port;
>>>> +       int ret;
>>>> +
>>>> +       I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
>>>> +       if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) == 0, 20)) {
>>>> +               DRM_ERROR("Failed to disable HDCP, timeout clearing status\n");
>>>> +               return -ETIMEDOUT;
>>>> +       }
>>>> +
>>>> +       intel_hdcp_clear_keys(dev_priv);
>>>> +
>>>> +       ret = connector->hdcp_shim->toggle_signalling(intel_dig_port, false);
>>>> +       if (ret) {
>>>> +               DRM_ERROR("Failed to disable HDCP signalling\n");
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       DRM_INFO("HDCP is disabled\n");
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static int _intel_hdcp_enable(struct intel_connector *connector)
>>>> +{
>>>> +       struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>>> +       int i, ret;
>>>> +
>>>> +       if (!(I915_READ(SKL_FUSE_STATUS) & SKL_FUSE_PG_DIST_STATUS(1))) {
>>>> +               DRM_ERROR("PG1 is disabled, cannot load keys\n");
>>>> +               return -ENXIO;
>>>> +       }
>>>> +
>>>> +       for (i = 0; i < KEY_LOAD_TRIES; i++) {
>>>> +               ret = intel_hdcp_load_keys(dev_priv);
>>>> +               if (!ret)
>>>> +                       break;
>>>> +               intel_hdcp_clear_keys(dev_priv);
>>>> +       }
>>>> +       if (ret) {
>>>> +               DRM_ERROR("Could not load HDCP keys, (%d)\n", ret);
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       ret = intel_hdcp_auth(conn_to_dig_port(connector),
>>>> +                             connector->hdcp_shim);
>>>> +       if (ret) {
>>>> +               DRM_ERROR("Failed to authenticate HDCP (%d)\n", ret);
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +void intel_hdcp_work(struct work_struct *work)
>>>> +{
>>>> +       struct intel_connector *connector = container_of(to_delayed_work(work),
>>>> +                                                        struct intel_connector,
>>>> +                                                        hdcp_work);
>>>> +       struct drm_device *dev = connector->base.dev;
>>>> +       int ret;
>>>> +
>>>> +       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>>>> +
>>>> +       ret = intel_hdcp_check_link(connector);
>>>> +       if (!ret)
>>>> +               schedule_delayed_work(&connector->hdcp_work,
>>>> +                                     DRM_HDCP_CHECK_PERIOD_MS);
>>>> +
>>>> +       drm_modeset_unlock(&dev->mode_config.connection_mutex);
>>>> +}
>>>> +
>>>> +int intel_hdcp_enable(struct intel_connector *connector)
>>>> +{
>>>> +       struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>>> +       struct drm_device *dev = &dev_priv->drm;
>>>> +       struct drm_connector_state *state = connector->base.state;
>>>> +       int ret;
>>>> +
>>>> +       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>>>> +
>>>> +       if (!connector->hdcp_shim)
>>>> +               return -ENOENT;
>>>> +
>>>> +       ret = _intel_hdcp_enable(connector);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
>>>> +
>>>> +       schedule_delayed_work(&connector->hdcp_work, DRM_HDCP_CHECK_PERIOD_MS);
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +int intel_hdcp_disable(struct intel_connector *connector)
>>>> +{
>>>> +       struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>>> +       struct drm_device *dev = &dev_priv->drm;
>>>> +
>>>> +       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>>>> +
>>>> +       if (!connector->hdcp_shim)
>>>> +               return -ENOENT;
>>>> +
>>>> +       cancel_delayed_work(&connector->hdcp_work);
>>>> +
>>>> +       return _intel_hdcp_disable(connector);
>>>> +}
>>>> +
>>>> +/* Implements Part 3 of the HDCP authorization procedure */
>>>> +int intel_hdcp_check_link(struct intel_connector *connector)
>>>> +{
>>>> +       struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>>> +       struct drm_device *dev = &dev_priv->drm;
>>>> +       struct drm_connector_state *state = connector->base.state;
>>>> +       struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
>>>> +       enum port port = intel_dig_port->port;
>>>> +       int ret;
>>>> +
>>>> +       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>>>> +
>>>> +       if (state->content_protection != DRM_MODE_CONTENT_PROTECTION_ENABLED)
>>>> +               return 0;
>>>> +
>>>> +       if (!connector->hdcp_shim)
>>>> +               return -ENOENT;
>>>> +
>>>> +       if (!(I915_READ(SKL_PORT_HDCP_STATUS(port)) & SKL_HDCP_STATUS_ENC)) {
>>>> +               DRM_ERROR("HDCP check failed: link is not encrypted, %x\n",
>>>> +                          I915_READ(SKL_PORT_HDCP_STATUS(port)));
>>>> +               ret = -ENXIO;
>>>> +               goto fail;
>>>> +       }
>>>> +
>>>> +       if (connector->hdcp_shim->check_link(intel_dig_port))
>>>> +               return 0;
>>>> +
>>>> +       DRM_INFO("HDCP link failed, retrying authentication\n");
>>>> +
>>>> +       ret = _intel_hdcp_disable(connector);
>>>> +       if (ret) {
>>>> +               DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
>>>> +               goto fail;
>>>> +       }
>>>> +
>>>> +       ret = _intel_hdcp_enable(connector);
>>>> +       if (ret) {
>>>> +               DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
>>>> +               goto fail;
>>>> +       }
>>>> +
>>>> +       return 0;
>>>> +
>>>> +fail:
>>>> +       state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
>>>> +       return ret;
>>>> +}
>>>
>>> _______________________________________________
>>> Intel-gfx mailing list
>>> Intel-gfx@lists.freedesktop.org
>>> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
>
>

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 3/6] drm/i915: Add HDCP framework + base implementation
@ 2017-12-01 14:13           ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-12-01 14:13 UTC (permalink / raw)
  To: Ramalingam C
  Cc: David Airlie, Intel Graphics Development,
	Linux Kernel Mailing List, dri-devel, Rodrigo Vivi

On Fri, Dec 1, 2017 at 3:36 AM, Ramalingam C <ramalingam.c@intel.com> wrote:
>
>
>
> On Friday 01 December 2017 01:06 PM, Daniel Vetter wrote:
>>
>> On Fri, Dec 01, 2017 at 12:53:31PM +0530, Ramalingam C wrote:
>>>
>>> Sean,
>>>
>>> IMHO, it will good if we can have all generic hdcp1.4 authentication flow in
>>> drm helpers and all interested display drivers to use them.
>>>
>>> This Design will make the extending of hdcp easy for other display drivers
>>> based on DRM.
>>>
>>> We can have the required drm_hdcp_shim type of implementation at drm
>>> structure which will be called for platform specific operations (like
>>> prepare an, send aksv, program bksv/repeater/r0 and verify sha1 etc)?
>>
>> I discussed this exact question with Sean Paul, and apparently the
>> hardware designs are too diverse to make shared code much useful. Some hw
>> has the entire hdcp flow in hw, some almost nothing (like i915 here), and
>> then there's everything in between.
>
> Just trying to understand the other extreme of HW (full)support for HDCP here.
>
> When you say everything about HDCP is implemented in HW, do you mean that whole protocol comm on HDCP link also driven by HW?
>

Yep. Check out the rockchip implementation in our 4.4 tree.

Sean


>
> --Ram
>
>>
>> Given that Sean has seen a lot more hdcp implementations than we have,
>> that we right now have no other implementation than i915 in upstream and
>> than wrong abstraction is much harder to fix than no abstraction I'm going
>> with Sean's approach of "no generic abstraction" here. Personally I'm not
>> even fully sold on the shim abstraction, but I think by&large that one is
>> fine.
>>
>>> On Thursday 30 November 2017 08:38 AM, Sean Paul wrote:
>>>>
>>>> This patch adds the framework required to add HDCP support to intel
>>>> connectors. It implements Aksv loading from fuse, and parts 1/2/3
>>>> of the HDCP authentication scheme.
>>>>
>>>> Note that without shim implementations, this does not actually implement
>>>> HDCP. That will come in subsequent patches.
>>>>
>>>> Signed-off-by: Sean Paul <seanpaul@chromium.org>
>>>> ---
>>>>    drivers/gpu/drm/i915/Makefile       |   1 +
>>>>    drivers/gpu/drm/i915/i915_reg.h     |  83 +++++
>>>>    drivers/gpu/drm/i915/intel_atomic.c |  26 +-
>>>>    drivers/gpu/drm/i915/intel_ddi.c    |  14 +
>>>>    drivers/gpu/drm/i915/intel_drv.h    |  53 +++
>>>>    drivers/gpu/drm/i915/intel_hdcp.c   | 636 ++++++++++++++++++++++++++++++++++++
>>>>    6 files changed, 811 insertions(+), 2 deletions(-)
>>>>    create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c
>>>>
>>>> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
>>>> index 6c3b0481ef82..1e745508e437 100644
>>>> --- a/drivers/gpu/drm/i915/Makefile
>>>> +++ b/drivers/gpu/drm/i915/Makefile
>>>> @@ -87,6 +87,7 @@ i915-y += intel_audio.o \
>>>>           intel_fbc.o \
>>>>           intel_fifo_underrun.o \
>>>>           intel_frontbuffer.o \
>>>> +         intel_hdcp.o \
>>>>           intel_hotplug.o \
>>>>           intel_modes.o \
>>>>           intel_overlay.o \
>>>> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
>>>> index 68a58cce6ab1..43128030171d 100644
>>>> --- a/drivers/gpu/drm/i915/i915_reg.h
>>>> +++ b/drivers/gpu/drm/i915/i915_reg.h
>>>> @@ -7991,6 +7991,7 @@ enum {
>>>>    #define     GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT 8
>>>>    #define     GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT 16
>>>>    #define     GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT 24
>>>> +#define   SKL_PCODE_LOAD_HDCP_KEYS             0x5
>>>>    #define   SKL_PCODE_CDCLK_CONTROL            0x7
>>>>    #define     SKL_CDCLK_PREPARE_FOR_CHANGE     0x3
>>>>    #define     SKL_CDCLK_READY_FOR_CHANGE               0x1
>>>> @@ -8285,6 +8286,88 @@ enum skl_power_gate {
>>>>    #define  SKL_PW_TO_PG(pw)                    ((pw) - SKL_DISP_PW_1 + SKL_PG1)
>>>>    #define  SKL_FUSE_PG_DIST_STATUS(pg)         (1 << (27 - (pg)))
>>>> +
>>>> +/* HDCP Key Registers */
>>>> +#define SKL_HDCP_KEY_CONF              _MMIO(0x66c00)
>>>> +#define         SKL_HDCP_AKSV_SEND_TRIGGER     BIT(31)
>>>> +#define  SKL_HDCP_CLEAR_KEYS_TRIGGER   BIT(30)
>>>> +#define SKL_HDCP_KEY_STATUS            _MMIO(0x66c04)
>>>> +#define  SKL_HDCP_FUSE_IN_PROGRESS     BIT(7)
>>>> +#define  SKL_HDCP_FUSE_ERROR           BIT(6)
>>>> +#define  SKL_HDCP_FUSE_DONE            BIT(5)
>>>> +#define  SKL_HDCP_KEY_LOAD_STATUS      BIT(1)
>>>> +#define  SKL_HDCP_KEY_LOAD_DONE                BIT(0)
>>>> +#define SKL_HDCP_AKSV_LO               _MMIO(0x66c10)
>>>> +#define SKL_HDCP_AKSV_HI               _MMIO(0x66c14)
>>>> +
>>>> +/* HDCP Repeater Registers */
>>>> +#define SKL_HDCP_REP_CTL               _MMIO(0x66d00)
>>>> +#define  SKL_HDCP_DDIB_REP_PRESENT     BIT(30)
>>>> +#define  SKL_HDCP_DDIA_REP_PRESENT     BIT(29)
>>>> +#define  SKL_HDCP_DDIC_REP_PRESENT     BIT(28)
>>>> +#define  SKL_HDCP_DDID_REP_PRESENT     BIT(27)
>>>> +#define  SKL_HDCP_DDIF_REP_PRESENT     BIT(26)
>>>> +#define  SKL_HDCP_DDIE_REP_PRESENT     BIT(25)
>>>> +#define  SKL_HDCP_DDIB_SHA1_M0         (1 << 20)
>>>> +#define  SKL_HDCP_DDIA_SHA1_M0         (2 << 20)
>>>> +#define  SKL_HDCP_DDIC_SHA1_M0         (3 << 20)
>>>> +#define  SKL_HDCP_DDID_SHA1_M0         (4 << 20)
>>>> +#define  SKL_HDCP_DDIF_SHA1_M0         (5 << 20)
>>>> +#define  SKL_HDCP_DDIE_SHA1_M0         (6 << 20) // Bspec says 5?
>>>> +#define  SKL_HDCP_SHA1_BUSY            BIT(16)
>>>> +#define  SKL_HDCP_SHA1_READY           BIT(17)
>>>> +#define  SKL_HDCP_SHA1_COMPLETE                BIT(18)
>>>> +#define  SKL_HDCP_SHA1_V_MATCH         BIT(19)
>>>> +#define  SKL_HDCP_SHA1_TEXT_32         (1 << 1)
>>>> +#define  SKL_HDCP_SHA1_COMPLETE_HASH   (2 << 1)
>>>> +#define  SKL_HDCP_SHA1_TEXT_24         (4 << 1)
>>>> +#define  SKL_HDCP_SHA1_TEXT_16         (5 << 1)
>>>> +#define  SKL_HDCP_SHA1_TEXT_8          (6 << 1)
>>>> +#define  SKL_HDCP_SHA1_TEXT_0          (7 << 1)
>>>> +#define SKL_HDCP_SHA_V_PRIME_H0                _MMIO(0x66d04)
>>>> +#define SKL_HDCP_SHA_V_PRIME_H1                _MMIO(0x66d08)
>>>> +#define SKL_HDCP_SHA_V_PRIME_H2                _MMIO(0x66d0C)
>>>> +#define SKL_HDCP_SHA_V_PRIME_H3                _MMIO(0x66d10)
>>>> +#define SKL_HDCP_SHA_V_PRIME_H4                _MMIO(0x66d14)
>>>> +#define SKL_HDCP_SHA_V_PRIME(h)                _MMIO((0x66d04 + h * 4))
>>>> +#define SKL_HDCP_SHA_TEXT              _MMIO(0x66d18)
>>>> +
>>>> +/* HDCP Auth Registers */
>>>> +#define _SKL_PORTA_HDCP_AUTHENC                0x66800
>>>> +#define _SKL_PORTB_HDCP_AUTHENC                0x66500
>>>> +#define _SKL_PORTC_HDCP_AUTHENC                0x66600
>>>> +#define _SKL_PORTD_HDCP_AUTHENC                0x66700
>>>> +#define _SKL_PORTE_HDCP_AUTHENC                0x66A00
>>>> +#define _SKL_PORTF_HDCP_AUTHENC                0x66900
>>>> +#define _SKL_PORT_HDCP_AUTHENC(port, x)        _MMIO(_PICK(port, \
>>>> +                                         _SKL_PORTA_HDCP_AUTHENC, \
>>>> +                                         _SKL_PORTB_HDCP_AUTHENC, \
>>>> +                                         _SKL_PORTC_HDCP_AUTHENC, \
>>>> +                                         _SKL_PORTD_HDCP_AUTHENC, \
>>>> +                                         _SKL_PORTE_HDCP_AUTHENC, \
>>>> +                                         _SKL_PORTF_HDCP_AUTHENC) + x)
>>>> +#define SKL_PORT_HDCP_CONF(port)       _SKL_PORT_HDCP_AUTHENC(port, 0x0)
>>>> +#define  SKL_HDCP_CONF_CAPTURE_AN      BIT(0)
>>>> +#define  SKL_HDCP_CONF_AUTH_AND_ENC    (BIT(1) | BIT(0))
>>>> +#define SKL_PORT_HDCP_ANINIT(port)     _SKL_PORT_HDCP_AUTHENC(port, 0x4)
>>>> +#define SKL_PORT_HDCP_ANLO(port)       _SKL_PORT_HDCP_AUTHENC(port, 0x8)
>>>> +#define SKL_PORT_HDCP_ANHI(port)       _SKL_PORT_HDCP_AUTHENC(port, 0xC)
>>>> +#define SKL_PORT_HDCP_BKSVLO(port)     _SKL_PORT_HDCP_AUTHENC(port, 0x10)
>>>> +#define SKL_PORT_HDCP_BKSVHI(port)     _SKL_PORT_HDCP_AUTHENC(port, 0x14)
>>>> +#define SKL_PORT_HDCP_RPRIME(port)     _SKL_PORT_HDCP_AUTHENC(port, 0x18)
>>>> +#define SKL_PORT_HDCP_STATUS(port)     _SKL_PORT_HDCP_AUTHENC(port, 0x1C)
>>>> +#define  SKL_HDCP_STATUS_STREAM_A_ENC  BIT(31)
>>>> +#define  SKL_HDCP_STATUS_STREAM_B_ENC  BIT(30)
>>>> +#define  SKL_HDCP_STATUS_STREAM_C_ENC  BIT(29)
>>>> +#define  SKL_HDCP_STATUS_STREAM_D_ENC  BIT(28)
>>>> +#define  SKL_HDCP_STATUS_AUTH          BIT(21)
>>>> +#define  SKL_HDCP_STATUS_ENC           BIT(20)
>>>> +#define  SKL_HDCP_STATUS_RI_MATCH      BIT(19)
>>>> +#define  SKL_HDCP_STATUS_R0_READY      BIT(18)
>>>> +#define  SKL_HDCP_STATUS_AN_READY      BIT(17)
>>>> +#define  SKL_HDCP_STATUS_CIPHER                BIT(16)
>>>> +#define  SKL_HDCP_STATUS_FRAME_CNT(x)  ((x >> 8) & 0xff)
>>>> +
>>>>    /* Per-pipe DDI Function Control */
>>>>    #define _TRANS_DDI_FUNC_CTL_A                0x60400
>>>>    #define _TRANS_DDI_FUNC_CTL_B                0x61400
>>>> diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
>>>> index 36d4e635e4ce..ddf08227d9cb 100644
>>>> --- a/drivers/gpu/drm/i915/intel_atomic.c
>>>> +++ b/drivers/gpu/drm/i915/intel_atomic.c
>>>> @@ -109,12 +109,34 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
>>>>         struct intel_digital_connector_state *old_conn_state =
>>>>                 to_intel_digital_connector_state(old_state);
>>>>         struct drm_crtc_state *crtc_state;
>>>> -
>>>> -       if (!new_state->crtc)
>>>> +       uint64_t old_cp = old_conn_state->base.content_protection;
>>>> +       uint64_t new_cp = new_state->content_protection;
>>>> +
>>>> +       if (!new_state->crtc) {
>>>> +               /*
>>>> +                * If the connector is being disabled with CP enabled, mark it
>>>> +                * desired so it's re-enabled when the connector is brought back
>>>> +                */
>>>> +               if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
>>>> +                       new_state->content_protection =
>>>> +                               DRM_MODE_CONTENT_PROTECTION_DESIRED;
>>>>                 return 0;
>>>> +       }
>>>>         crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc);
>>>> +       if (new_cp != old_cp) {
>>>> +               /* Only drivers can set content protection enabled */
>>>> +               if (new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
>>>> +                       new_state->content_protection =
>>>> +                               DRM_MODE_CONTENT_PROTECTION_DESIRED;
>>>> +
>>>> +               /* Involve the encoder/connector to enable/disable CP */
>>>> +               if (new_cp == DRM_MODE_CONTENT_PROTECTION_OFF ||
>>>> +                   old_cp == DRM_MODE_CONTENT_PROTECTION_OFF)
>>>> +                       crtc_state->mode_changed = true;
>>>
>>> We need not perform the mode set for hdcp enable/disable.
>>> Authentication and encryption can be started on active port.
>>
>> Was simpler to implement this way :-) We can fix this by pushing the hdcp
>> enable/disable code into a post-modeset operation. Ville had written the
>> infrastructure for that to fix a few fastboot corner cases. But that
>> infrastructure hasn't landed yet, so probably better to do that in a
>> follow-up.
>>
>> I also guess that CrOS simply set this to desired every time they enable
>> an external screen, so it won't result in an unecessary modeset.
>>
>>>> +       }
>>>> +
>>>>         /*
>>>>          * These properties are handled by fastset, and might not end
>>>>          * up in a modeset.
>>>> diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
>>>> index 933c18fd4258..0e69337f410d 100644
>>>> --- a/drivers/gpu/drm/i915/intel_ddi.c
>>>> +++ b/drivers/gpu/drm/i915/intel_ddi.c
>>>> @@ -2432,10 +2432,17 @@ static void intel_enable_ddi(struct intel_encoder *encoder,
>>>>                              const struct intel_crtc_state *crtc_state,
>>>>                              const struct drm_connector_state *conn_state)
>>>>    {
>>>> +       struct drm_connector *connector = conn_state->connector;
>>>> +       struct intel_connector *intel_connector = to_intel_connector(connector);
>>>> +
>>>>         if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
>>>>                 intel_enable_ddi_hdmi(encoder, crtc_state, conn_state);
>>>>         else
>>>>                 intel_enable_ddi_dp(encoder, crtc_state, conn_state);
>>>> +
>>>> +       if (conn_state->content_protection ==
>>>> +                       DRM_MODE_CONTENT_PROTECTION_DESIRED)
>>>> +               intel_hdcp_enable(intel_connector);
>>>>    }
>>>>    static void intel_disable_ddi_dp(struct intel_encoder *encoder,
>>>> @@ -2468,10 +2475,17 @@ static void intel_disable_ddi(struct intel_encoder *encoder,
>>>>                               const struct intel_crtc_state *old_crtc_state,
>>>>                               const struct drm_connector_state *old_conn_state)
>>>>    {
>>>> +       struct drm_connector *connector = old_conn_state->connector;
>>>> +       struct intel_connector *intel_connector = to_intel_connector(connector);
>>>> +
>>>>         if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
>>>>                 intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state);
>>>>         else
>>>>                 intel_disable_ddi_dp(encoder, old_crtc_state, old_conn_state);
>>>> +
>>>> +       if (old_conn_state->content_protection !=
>>>> +                       DRM_MODE_CONTENT_PROTECTION_OFF)
>>>> +               intel_hdcp_disable(intel_connector);
>>>
>>> We might want to disable the hdcp before disabling the DDI. Actually we
>>> could trigger hdcp disable at connector state change(due to hot-unplug)
>>> also.
>>
>> Yeah this part needs to be reworked a bit, also because the locking
>> doesn't work yet. I think hot-unplug is handled by the worker thread
>> already, it does the mandatory regular polling.
>> -Daniel
>>
>>> Thanks,
>>> --Ram
>>>>
>>>>    }
>>>>    static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder,
>>>> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
>>>> index 47d022d48718..8924004575b8 100644
>>>> --- a/drivers/gpu/drm/i915/intel_drv.h
>>>> +++ b/drivers/gpu/drm/i915/intel_drv.h
>>>> @@ -299,6 +299,49 @@ struct intel_panel {
>>>>         } backlight;
>>>>    };
>>>> +struct intel_hdcp_shim {
>>>> +       /* Outputs the transmitter's An and Aksv values to the receiver. */
>>>> +       int (*write_an_aksv)(struct intel_digital_port *intel_dig_port, u8 *an);
>>>> +
>>>> +       /* Reads the receiver's key selection vector */
>>>> +       int (*read_bksv)(struct intel_digital_port *intel_dig_port, u8 *bksv);
>>>> +
>>>> +       /*
>>>> +        * Reads BINFO from DP receivers and BSTATUS from HDMI receivers. The
>>>> +        * definitions are the same in the respective specs, but the names are
>>>> +        * different. Call it BSTATUS since that's the name the HDMI spec
>>>> +        * uses and it was there first.
>>>> +        */
>>>> +       int (*read_bstatus)(struct intel_digital_port *intel_dig_port,
>>>> +                           u8 *bstatus);
>>>> +
>>>> +       /* Determines whether a repeater is present downstream */
>>>> +       int (*repeater_present)(struct intel_digital_port *intel_dig_port,
>>>> +                               bool *repeater_present);
>>>> +
>>>> +       /* Reads the receiver's Ri' value */
>>>> +       int (*read_ri_prime)(struct intel_digital_port *intel_dig_port, u8 *ri);
>>>> +
>>>> +       /* Determines if the receiver's KSV FIFO is ready for consumption */
>>>> +       int (*read_ksv_ready)(struct intel_digital_port *intel_dig_port,
>>>> +                             bool *ksv_ready);
>>>> +
>>>> +       /* Reads the ksv fifo for num_downstream devices */
>>>> +       int (*read_ksv_fifo)(struct intel_digital_port *intel_dig_port,
>>>> +                            int num_downstream, u8 *ksv_fifo);
>>>> +
>>>> +       /* Reads a 32-bit part of V' from the receiver */
>>>> +       int (*read_v_prime_part)(struct intel_digital_port *intel_dig_port,
>>>> +                                int i, u32 *part);
>>>> +
>>>> +       /* Enables HDCP signalling on the port */
>>>> +       int (*toggle_signalling)(struct intel_digital_port *intel_dig_port,
>>>> +                                bool enable);
>>>> +
>>>> +       /* Ensures the link is still protected */
>>>> +       bool (*check_link)(struct intel_digital_port *intel_dig_port);
>>>> +};
>>>> +
>>>>    struct intel_connector {
>>>>         struct drm_connector base;
>>>>         /*
>>>> @@ -330,6 +373,9 @@ struct intel_connector {
>>>>         /* Work struct to schedule a uevent on link train failure */
>>>>         struct work_struct modeset_retry_work;
>>>> +
>>>> +       const struct intel_hdcp_shim *hdcp_shim;
>>>> +       struct delayed_work hdcp_work;
>>>>    };
>>>>    struct intel_digital_connector_state {
>>>> @@ -1295,6 +1341,8 @@ void intel_ddi_set_vc_payload_alloc(const struct intel_crtc_state *crtc_state,
>>>>                                     bool state);
>>>>    u32 bxt_signal_levels(struct intel_dp *intel_dp);
>>>>    uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
>>>> +int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder);
>>>> +int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder);
>>>>    u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
>>>>    unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
>>>> @@ -1746,6 +1794,11 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
>>>>    }
>>>>    #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
>>>> +/* intel_hdcp.c */
>>>> +int intel_hdcp_enable(struct intel_connector *connector);
>>>> +int intel_hdcp_disable(struct intel_connector *connector);
>>>> +int intel_hdcp_check_link(struct intel_connector *connector);
>>>> +void intel_hdcp_work(struct work_struct *work);
>>>>    /* intel_psr.c */
>>>>    void intel_psr_enable(struct intel_dp *intel_dp,
>>>> diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
>>>> new file mode 100644
>>>> index 000000000000..a2a575ed657e
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/i915/intel_hdcp.c
>>>> @@ -0,0 +1,636 @@
>>>> +/*
>>>> + * Copyright (C) 2017 Google, Inc.
>>>> + *
>>>> + * This software is licensed under the terms of the GNU General Public
>>>> + * License version 2, as published by the Free Software Foundation, and
>>>> + * may be copied, distributed, and modified under those terms.
>>>> + *
>>>> + * This program is distributed in the hope that it will be useful,
>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>>> + * GNU General Public License for more details.
>>>> + */
>>>> +
>>>> +#include <drm/drmP.h>
>>>> +#include <drm/drm_hdcp.h>
>>>> +#include <linux/i2c.h>
>>>> +#include <linux/random.h>
>>>> +
>>>> +#include "intel_drv.h"
>>>> +#include "i915_reg.h"
>>>> +
>>>> +#define KEY_LOAD_TRIES 5
>>>> +
>>>> +static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
>>>> +                                   const struct intel_hdcp_shim *shim)
>>>> +{
>>>> +       unsigned long timeout = jiffies + msecs_to_jiffies_timeout(500);
>>>> +       int ret;
>>>> +       bool ksv_ready;
>>>> +
>>>> +       while (true) {
>>>> +               ret = shim->read_ksv_ready(intel_dig_port, &ksv_ready);
>>>> +               if (ret)
>>>> +                       return ret;
>>>> +               if (ksv_ready)
>>>> +                       break;
>>>> +               if (time_after(jiffies, timeout))
>>>> +                       return -ETIMEDOUT;
>>>> +               msleep(100);
>>>> +       }
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static void intel_hdcp_clear_keys(struct drm_i915_private *dev_priv)
>>>> +{
>>>> +       I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_CLEAR_KEYS_TRIGGER);
>>>> +       I915_WRITE(SKL_HDCP_KEY_STATUS,
>>>> +                  SKL_HDCP_KEY_LOAD_DONE | SKL_HDCP_KEY_LOAD_STATUS |
>>>> +                  SKL_HDCP_FUSE_IN_PROGRESS | SKL_HDCP_FUSE_ERROR |
>>>> +                  SKL_HDCP_FUSE_DONE);
>>>> +}
>>>> +
>>>> +static int intel_hdcp_load_keys(struct drm_i915_private *dev_priv)
>>>> +{
>>>> +       unsigned long timeout;
>>>> +       int ret;
>>>> +       u32 val;
>>>> +
>>>> +       // Initiate loading the HDCP key from fuses
>>>> +       mutex_lock(&dev_priv->pcu_lock);
>>>> +       ret = sandybridge_pcode_write(dev_priv, SKL_PCODE_LOAD_HDCP_KEYS, 1);
>>>> +       mutex_unlock(&dev_priv->pcu_lock);
>>>> +       if (ret) {
>>>> +               DRM_ERROR("Failed to initiate HDCP key load (%d)\n", ret);
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       // Wait for the keys to load (500us)
>>>> +       timeout = jiffies + nsecs_to_jiffies_timeout(500 * 1000);
>>>> +       while (true) {
>>>> +               val = I915_READ(SKL_HDCP_KEY_STATUS);
>>>> +               if (val & SKL_HDCP_KEY_LOAD_DONE)
>>>> +                       break;
>>>> +               if (time_after(jiffies, timeout))
>>>> +                       return -ETIMEDOUT;
>>>> +               usleep_range(50, 100);
>>>> +       }
>>>> +       if (!(val & SKL_HDCP_KEY_LOAD_STATUS))
>>>> +               return -ENXIO;
>>>> +
>>>> +       // Send Aksv over to PCH display for use in authentication
>>>> +       I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_AKSV_SEND_TRIGGER);
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +/* Returns updated SHA-1 index */
>>>> +static int intel_write_sha_text(struct drm_i915_private *dev_priv, u32 sha_text)
>>>> +{
>>>> +       I915_WRITE(SKL_HDCP_SHA_TEXT, sha_text);
>>>> +       if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_READY, 1)) {
>>>> +               DRM_ERROR("Timed out waiting for SHA1 ready\n");
>>>> +               return -ETIMEDOUT;
>>>> +       }
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static
>>>> +u32 intel_hdcp_get_repeater_ctl(struct intel_digital_port *intel_dig_port)
>>>> +{
>>>> +       enum port port = intel_dig_port->port;
>>>> +       switch(port) {
>>>> +       case PORT_A:
>>>> +               return SKL_HDCP_DDIA_REP_PRESENT | SKL_HDCP_DDIA_SHA1_M0;
>>>> +       case PORT_B:
>>>> +               return SKL_HDCP_DDIB_REP_PRESENT | SKL_HDCP_DDIB_SHA1_M0;
>>>> +       case PORT_C:
>>>> +               return SKL_HDCP_DDIC_REP_PRESENT | SKL_HDCP_DDIC_SHA1_M0;
>>>> +       case PORT_D:
>>>> +               return SKL_HDCP_DDID_REP_PRESENT | SKL_HDCP_DDID_SHA1_M0;
>>>> +       case PORT_E:
>>>> +               return SKL_HDCP_DDIE_REP_PRESENT | SKL_HDCP_DDIE_SHA1_M0;
>>>> +       default:
>>>> +               break;
>>>> +       }
>>>> +       DRM_ERROR("Unknown port %d\n", port);
>>>> +       return -EINVAL;
>>>> +}
>>>> +
>>>> +/* Implements Part 2 of the HDCP authorization procedure */
>>>> +static
>>>> +int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
>>>> +                              const struct intel_hdcp_shim *shim)
>>>> +{
>>>> +       struct drm_i915_private *dev_priv;
>>>> +       u32 vprime, sha_text, sha_leftovers, rep_ctl;
>>>> +       u8 bstatus[2], num_downstream, *ksv_fifo;
>>>> +       int ret, i, j, sha_idx;
>>>> +
>>>> +       dev_priv = intel_dig_port->base.base.dev->dev_private;
>>>> +
>>>> +       ret = shim->read_bstatus(intel_dig_port, bstatus);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       /* If there are no downstream devices, we're all done. */
>>>> +       num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
>>>> +       if (num_downstream == 0) {
>>>> +               DRM_INFO("HDCP is enabled (no downstream devices)\n");
>>>> +               return 0;
>>>> +       }
>>>> +
>>>> +       // Poll for ksv list ready (spec says max time allowed is 5s)
>>>> +       ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim);
>>>> +       if (ret) {
>>>> +               DRM_ERROR("KSV list failed to become ready (%d)\n", ret);
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL);
>>>> +       if (!ksv_fifo)
>>>> +               return -ENOMEM;
>>>> +
>>>> +       ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       // Process V' values from the receiver
>>>> +       for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
>>>> +               ret = shim->read_v_prime_part(intel_dig_port, i, &vprime);
>>>> +               if (ret)
>>>> +                       return ret;
>>>> +               I915_WRITE(SKL_HDCP_SHA_V_PRIME(i), vprime);
>>>> +       }
>>>> +
>>>> +       /*
>>>> +        * We need to write the concatenation of all device KSVs, BINFO (DP) ||
>>>> +        * BSTATUS (HDMI), and M0 (which is added via HDCP_REP_CTL). This byte
>>>> +        * stream is written via the HDCP_SHA_TEXT register in 32-bit
>>>> +        * increments. Every 64 bytes, we need to write HDCP_REP_CTL again. This
>>>> +        * index will keep track of our progress through the 64 bytes as well as
>>>> +        * helping us work the 40-bit KSVs through our 32-bit register.
>>>> +        *
>>>> +        * NOTE: data passed via HDCP_SHA_TEXT should be big-endian
>>>> +        */
>>>> +       sha_idx = 0;
>>>> +       sha_text = 0;
>>>> +       sha_leftovers = 0;
>>>> +       rep_ctl = intel_hdcp_get_repeater_ctl(intel_dig_port);
>>>> +       I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>>> +       for (i = 0; i < num_downstream; i++) {
>>>> +               unsigned sha_empty;
>>>> +               u8 *ksv = &ksv_fifo[i * DRM_HDCP_KSV_LEN];
>>>> +
>>>> +               // Fill up the empty slots in sha_text and write it out
>>>> +               sha_empty = sizeof(sha_text) - sha_leftovers;
>>>> +               for (j = 0; j < sha_empty; j++)
>>>> +                       sha_text |= ksv[j] << ((sizeof(sha_text) - j - 1) * 8);
>>>> +
>>>> +               ret = intel_write_sha_text(dev_priv, sha_text);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +
>>>> +               // Programming guide writes this every 64 bytes
>>>> +               sha_idx += sizeof(sha_text);
>>>> +               if (!(sha_idx % 64))
>>>> +                       I915_WRITE(SKL_HDCP_REP_CTL,
>>>> +                                  rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>>> +
>>>> +               // Store the leftover bytes from the ksv in sha_text
>>>> +               sha_leftovers = DRM_HDCP_KSV_LEN - sha_empty;
>>>> +               sha_text = 0;
>>>> +               for (j = 0; j < sha_leftovers; j++)
>>>> +                       sha_text |= ksv[sha_empty + j] <<
>>>> +                                       ((sizeof(sha_text) - j - 1) * 8);
>>>> +
>>>> +               /*
>>>> +                * If we still have room in sha_text for more data, continue.
>>>> +                * Otherwise, write it out immediately.
>>>> +                */
>>>> +               if (sizeof(sha_text) > sha_leftovers)
>>>> +                       continue;
>>>> +
>>>> +               ret = intel_write_sha_text(dev_priv, sha_text);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_leftovers = 0;
>>>> +               sha_text = 0;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +       }
>>>> +
>>>> +       /*
>>>> +        * We need to write BINFO/BSTATUS, and M0 now. Depending on how many
>>>> +        * bytes are leftover from the last ksv, we might be able to fit them
>>>> +        * all in sha_text (first 2 cases), or we might need to split them up
>>>> +        * into 2 writes (last 2 cases).
>>>> +        */
>>>> +       if (sha_leftovers == 0) {
>>>> +               // Write 16 bits of text, 16 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
>>>> +               ret = intel_write_sha_text(dev_priv,
>>>> +                                          bstatus[0] << 8 | bstatus[1]);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +               // Write 32 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>>>> +               ret = intel_write_sha_text(dev_priv, 0);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +               // Write 16 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
>>>> +               ret = intel_write_sha_text(dev_priv, 0);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +       } else if (sha_leftovers == 1) {
>>>> +               // Write 24 bits of text, 8 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
>>>> +               sha_text |= bstatus[0] << 16 | bstatus[1] << 8;
>>>> +               // Only 24-bits of data, must be in the LSB
>>>> +               sha_text = (sha_text & 0xffffff00) >> 8;
>>>> +               ret = intel_write_sha_text(dev_priv, sha_text);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +               // Write 32 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>>>> +               ret = intel_write_sha_text(dev_priv, 0);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +               // Write 24 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
>>>> +               ret = intel_write_sha_text(dev_priv, 0);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +       } else if (sha_leftovers == 2) {
>>>> +               // Write 32 bits of text
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>>> +               sha_text |= bstatus[0] << 24 | bstatus[1] << 16;
>>>> +               ret = intel_write_sha_text(dev_priv, sha_text);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +               // Write 64 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>>>> +               for (i = 0; i < 2; i++) {
>>>> +                       ret = intel_write_sha_text(dev_priv, 0);
>>>> +                       if (ret < 0)
>>>> +                               return ret;
>>>> +                       sha_idx += sizeof(sha_text);
>>>> +               }
>>>> +       } else if (sha_leftovers == 3) {
>>>> +               // Write 32 bits of text
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>>> +               sha_text |= bstatus[0] << 24;
>>>> +               ret = intel_write_sha_text(dev_priv, sha_text);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +               // Write 8 bits of text, 24 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
>>>> +               ret = intel_write_sha_text(dev_priv, bstatus[1]);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +               // Write 32 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>>>> +               ret = intel_write_sha_text(dev_priv, 0);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +
>>>> +               // Write 8 bits of M0
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
>>>> +               ret = intel_write_sha_text(dev_priv, 0);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +       } else {
>>>> +               DRM_ERROR("Invalid number of leftovers %d\n", sha_leftovers);
>>>> +               return -EINVAL;
>>>> +       }
>>>> +
>>>> +       I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>>>> +       // Fill up to 64 - 4 bytes with zeros (leave the last write for length)
>>>> +       while ((sha_idx % 64) < (64 - sizeof(sha_text))) {
>>>> +               ret = intel_write_sha_text(dev_priv, 0);
>>>> +               if (ret < 0)
>>>> +                       return ret;
>>>> +               sha_idx += sizeof(sha_text);
>>>> +       }
>>>> +
>>>> +       /*
>>>> +        * Last write gets the length of the concatenation in bits. That is:
>>>> +        *  - 5 bytes per device
>>>> +        *  - 10 bytes for BINFO/BSTATUS(2), M0(8)
>>>> +        */
>>>> +       sha_text = (num_downstream * 5 + 10) * 8;
>>>> +       ret = intel_write_sha_text(dev_priv, sha_text);
>>>> +       if (ret < 0)
>>>> +               return ret;
>>>> +
>>>> +       // Finally, tell the HW we're done with the hash and wait for it to ACK
>>>> +       I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_COMPLETE_HASH);
>>>> +       if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_COMPLETE, 1)) {
>>>> +               DRM_ERROR("Timed out waiting for SHA1 complete\n");
>>>> +               return -ETIMEDOUT;
>>>> +       }
>>>> +       if (!(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_V_MATCH)) {
>>>> +               DRM_ERROR("SHA-1 mismatch, HDCP failed\n");
>>>> +               return -ENXIO;
>>>> +       }
>>>> +
>>>> +       DRM_INFO("HDCP is enabled (%d downstream devices)\n", num_downstream);
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +/* Implements Part 1 of the HDCP authorization procedure */
>>>> +static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
>>>> +                          const struct intel_hdcp_shim *shim)
>>>> +{
>>>> +       struct drm_i915_private *dev_priv;
>>>> +       enum port port;
>>>> +       unsigned long r0_prime_gen_start;
>>>> +       int ret, i;
>>>> +       union {
>>>> +               u32 reg[2];
>>>> +               u8 shim[DRM_HDCP_AN_LEN];
>>>> +       } an;
>>>> +       union {
>>>> +               u32 reg[2];
>>>> +               u8 shim[DRM_HDCP_KSV_LEN];
>>>> +       } bksv;
>>>> +       union {
>>>> +               u32 reg;
>>>> +               u8 shim[DRM_HDCP_RI_LEN];
>>>> +       } ri;
>>>> +       bool repeater_present;
>>>> +
>>>> +       dev_priv = intel_dig_port->base.base.dev->dev_private;
>>>> +
>>>> +       port = intel_dig_port->port;
>>>> +
>>>> +       // Initialize An with 2 random values and acquire it
>>>> +       for (i = 0; i < 2; i++)
>>>> +               I915_WRITE(SKL_PORT_HDCP_ANINIT(port), get_random_long());
>>>> +       I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_CAPTURE_AN);
>>>> +
>>>> +       // Wait for An to be acquired
>>>> +       if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>>>> +                    SKL_HDCP_STATUS_AN_READY, 1)) {
>>>> +               DRM_ERROR("Timed out waiting for An\n");
>>>> +               return -ETIMEDOUT;
>>>> +       }
>>>> +
>>>> +       an.reg[0] = I915_READ(SKL_PORT_HDCP_ANLO(port));
>>>> +       an.reg[1] = I915_READ(SKL_PORT_HDCP_ANHI(port));
>>>> +       ret = shim->write_an_aksv(intel_dig_port, an.shim);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       r0_prime_gen_start = jiffies;
>>>> +
>>>> +       memset(&bksv, 0, sizeof(bksv));
>>>> +       ret = shim->read_bksv(intel_dig_port, bksv.shim);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       I915_WRITE(SKL_PORT_HDCP_BKSVLO(port), bksv.reg[0]);
>>>> +       I915_WRITE(SKL_PORT_HDCP_BKSVHI(port), bksv.reg[1]);
>>>> +
>>>> +       ret = shim->repeater_present(intel_dig_port, &repeater_present);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +       if (repeater_present)
>>>> +               I915_WRITE(SKL_HDCP_REP_CTL,
>>>> +                          intel_hdcp_get_repeater_ctl(intel_dig_port));
>>>> +
>>>> +       ret = shim->toggle_signalling(intel_dig_port, true);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_AUTH_AND_ENC);
>>>> +
>>>> +       // Wait for R0 ready
>>>> +       if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>>>> +                    (SKL_HDCP_STATUS_R0_READY | SKL_HDCP_STATUS_ENC), 1)) {
>>>> +               DRM_ERROR("Timed out waiting for R0 ready\n");
>>>> +               return -ETIMEDOUT;
>>>> +       }
>>>> +
>>>> +       /*
>>>> +        * Wait for R0' to become available, the spec says 100ms from Aksv
>>>> +        * write. On DP, there's an R0_READY bit available but no such bit
>>>> +        * exists on HDMI. Since the upper-bound is the same, we'll just do
>>>> +        * the stupid thing instead of polling on one and not the other.
>>>> +        */
>>>> +       wait_remaining_ms_from_jiffies(r0_prime_gen_start, 100);
>>>> +
>>>> +       ri.reg = 0;
>>>> +       ret = shim->read_ri_prime(intel_dig_port, ri.shim);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +       I915_WRITE(SKL_PORT_HDCP_RPRIME(port), ri.reg);
>>>> +
>>>> +       // Wait for Ri prime match
>>>> +       if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>>>> +                    (SKL_HDCP_STATUS_RI_MATCH | SKL_HDCP_STATUS_ENC), 1)) {
>>>> +               DRM_ERROR("Timed out waiting for Ri prime match (%x)\n",
>>>> +                         I915_READ(SKL_PORT_HDCP_STATUS(port)));
>>>> +               return -ETIMEDOUT;
>>>> +       }
>>>> +
>>>> +       // Wait for encryption confirmation
>>>> +       if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>>>> +                     SKL_HDCP_STATUS_ENC, 20)) {
>>>> +               DRM_ERROR("Timed out waiting for encryption\n");
>>>> +               return -ETIMEDOUT;
>>>> +       }
>>>> +
>>>> +       /*
>>>> +        * XXX: If we have MST-connected devices, we need to enable encryption
>>>> +        * on those as well.
>>>> +        */
>>>> +
>>>> +       return intel_hdcp_auth_downstream(intel_dig_port, shim);
>>>> +}
>>>> +
>>>> +static
>>>> +struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector)
>>>> +{
>>>> +       return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base);
>>>> +}
>>>> +
>>>> +static int _intel_hdcp_disable(struct intel_connector *connector)
>>>> +{
>>>> +       struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>>> +       struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
>>>> +       enum port port = intel_dig_port->port;
>>>> +       int ret;
>>>> +
>>>> +       I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
>>>> +       if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) == 0, 20)) {
>>>> +               DRM_ERROR("Failed to disable HDCP, timeout clearing status\n");
>>>> +               return -ETIMEDOUT;
>>>> +       }
>>>> +
>>>> +       intel_hdcp_clear_keys(dev_priv);
>>>> +
>>>> +       ret = connector->hdcp_shim->toggle_signalling(intel_dig_port, false);
>>>> +       if (ret) {
>>>> +               DRM_ERROR("Failed to disable HDCP signalling\n");
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       DRM_INFO("HDCP is disabled\n");
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static int _intel_hdcp_enable(struct intel_connector *connector)
>>>> +{
>>>> +       struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>>> +       int i, ret;
>>>> +
>>>> +       if (!(I915_READ(SKL_FUSE_STATUS) & SKL_FUSE_PG_DIST_STATUS(1))) {
>>>> +               DRM_ERROR("PG1 is disabled, cannot load keys\n");
>>>> +               return -ENXIO;
>>>> +       }
>>>> +
>>>> +       for (i = 0; i < KEY_LOAD_TRIES; i++) {
>>>> +               ret = intel_hdcp_load_keys(dev_priv);
>>>> +               if (!ret)
>>>> +                       break;
>>>> +               intel_hdcp_clear_keys(dev_priv);
>>>> +       }
>>>> +       if (ret) {
>>>> +               DRM_ERROR("Could not load HDCP keys, (%d)\n", ret);
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       ret = intel_hdcp_auth(conn_to_dig_port(connector),
>>>> +                             connector->hdcp_shim);
>>>> +       if (ret) {
>>>> +               DRM_ERROR("Failed to authenticate HDCP (%d)\n", ret);
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +void intel_hdcp_work(struct work_struct *work)
>>>> +{
>>>> +       struct intel_connector *connector = container_of(to_delayed_work(work),
>>>> +                                                        struct intel_connector,
>>>> +                                                        hdcp_work);
>>>> +       struct drm_device *dev = connector->base.dev;
>>>> +       int ret;
>>>> +
>>>> +       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>>>> +
>>>> +       ret = intel_hdcp_check_link(connector);
>>>> +       if (!ret)
>>>> +               schedule_delayed_work(&connector->hdcp_work,
>>>> +                                     DRM_HDCP_CHECK_PERIOD_MS);
>>>> +
>>>> +       drm_modeset_unlock(&dev->mode_config.connection_mutex);
>>>> +}
>>>> +
>>>> +int intel_hdcp_enable(struct intel_connector *connector)
>>>> +{
>>>> +       struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>>> +       struct drm_device *dev = &dev_priv->drm;
>>>> +       struct drm_connector_state *state = connector->base.state;
>>>> +       int ret;
>>>> +
>>>> +       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>>>> +
>>>> +       if (!connector->hdcp_shim)
>>>> +               return -ENOENT;
>>>> +
>>>> +       ret = _intel_hdcp_enable(connector);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
>>>> +
>>>> +       schedule_delayed_work(&connector->hdcp_work, DRM_HDCP_CHECK_PERIOD_MS);
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +int intel_hdcp_disable(struct intel_connector *connector)
>>>> +{
>>>> +       struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>>> +       struct drm_device *dev = &dev_priv->drm;
>>>> +
>>>> +       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>>>> +
>>>> +       if (!connector->hdcp_shim)
>>>> +               return -ENOENT;
>>>> +
>>>> +       cancel_delayed_work(&connector->hdcp_work);
>>>> +
>>>> +       return _intel_hdcp_disable(connector);
>>>> +}
>>>> +
>>>> +/* Implements Part 3 of the HDCP authorization procedure */
>>>> +int intel_hdcp_check_link(struct intel_connector *connector)
>>>> +{
>>>> +       struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>>>> +       struct drm_device *dev = &dev_priv->drm;
>>>> +       struct drm_connector_state *state = connector->base.state;
>>>> +       struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
>>>> +       enum port port = intel_dig_port->port;
>>>> +       int ret;
>>>> +
>>>> +       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>>>> +
>>>> +       if (state->content_protection != DRM_MODE_CONTENT_PROTECTION_ENABLED)
>>>> +               return 0;
>>>> +
>>>> +       if (!connector->hdcp_shim)
>>>> +               return -ENOENT;
>>>> +
>>>> +       if (!(I915_READ(SKL_PORT_HDCP_STATUS(port)) & SKL_HDCP_STATUS_ENC)) {
>>>> +               DRM_ERROR("HDCP check failed: link is not encrypted, %x\n",
>>>> +                          I915_READ(SKL_PORT_HDCP_STATUS(port)));
>>>> +               ret = -ENXIO;
>>>> +               goto fail;
>>>> +       }
>>>> +
>>>> +       if (connector->hdcp_shim->check_link(intel_dig_port))
>>>> +               return 0;
>>>> +
>>>> +       DRM_INFO("HDCP link failed, retrying authentication\n");
>>>> +
>>>> +       ret = _intel_hdcp_disable(connector);
>>>> +       if (ret) {
>>>> +               DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
>>>> +               goto fail;
>>>> +       }
>>>> +
>>>> +       ret = _intel_hdcp_enable(connector);
>>>> +       if (ret) {
>>>> +               DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
>>>> +               goto fail;
>>>> +       }
>>>> +
>>>> +       return 0;
>>>> +
>>>> +fail:
>>>> +       state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
>>>> +       return ret;
>>>> +}
>>>
>>> _______________________________________________
>>> Intel-gfx mailing list
>>> Intel-gfx@lists.freedesktop.org
>>> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
>
>
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Intel-gfx] [RFC PATCH 3/6] drm/i915: Add HDCP framework + base implementation
  2017-12-01  7:36       ` Daniel Vetter
@ 2017-12-01 14:16         ` Sean Paul
  -1 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-12-01 14:16 UTC (permalink / raw)
  To: Ramalingam C, Sean Paul, dri-devel, Intel Graphics Development,
	David Airlie, Linux Kernel Mailing List, Rodrigo Vivi

On Fri, Dec 1, 2017 at 2:36 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Fri, Dec 01, 2017 at 12:53:31PM +0530, Ramalingam C wrote:
>> Sean,
>>
>> IMHO, it will good if we can have all generic hdcp1.4 authentication flow in
>> drm helpers and all interested display drivers to use them.
>>
>> This Design will make the extending of hdcp easy for other display drivers
>> based on DRM.
>>
>> We can have the required drm_hdcp_shim type of implementation at drm
>> structure which will be called for platform specific operations (like
>> prepare an, send aksv, program bksv/repeater/r0 and verify sha1 etc)?
>
> I discussed this exact question with Sean Paul, and apparently the
> hardware designs are too diverse to make shared code much useful. Some hw
> has the entire hdcp flow in hw, some almost nothing (like i915 here), and
> then there's everything in between.
>
> Given that Sean has seen a lot more hdcp implementations than we have,
> that we right now have no other implementation than i915 in upstream and
> than wrong abstraction is much harder to fix than no abstraction I'm going
> with Sean's approach of "no generic abstraction" here. Personally I'm not
> even fully sold on the shim abstraction, but I think by&large that one is
> fine.
>

[html fail on the first response, resending in plain text]

I think there's some sharing potential between exynos and i915, but
the rockchip stuff is completely different. Even exynos differs in
that each step of the authentication process is interrupt driven
(iirc). I just don't see a pattern worth abstracting atm. We might be
able to share the enable/disable/check song & dance, but let's not
worry about abstraction until we have 2 implementations.


>> On Thursday 30 November 2017 08:38 AM, Sean Paul wrote:
>> > This patch adds the framework required to add HDCP support to intel
>> > connectors. It implements Aksv loading from fuse, and parts 1/2/3
>> > of the HDCP authentication scheme.
>> >
>> > Note that without shim implementations, this does not actually implement
>> > HDCP. That will come in subsequent patches.
>> >
>> > Signed-off-by: Sean Paul <seanpaul@chromium.org>
>> > ---
>> >   drivers/gpu/drm/i915/Makefile       |   1 +
>> >   drivers/gpu/drm/i915/i915_reg.h     |  83 +++++
>> >   drivers/gpu/drm/i915/intel_atomic.c |  26 +-
>> >   drivers/gpu/drm/i915/intel_ddi.c    |  14 +
>> >   drivers/gpu/drm/i915/intel_drv.h    |  53 +++
>> >   drivers/gpu/drm/i915/intel_hdcp.c   | 636 ++++++++++++++++++++++++++++++++++++
>> >   6 files changed, 811 insertions(+), 2 deletions(-)
>> >   create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c
>> >
>> > diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
>> > index 6c3b0481ef82..1e745508e437 100644
>> > --- a/drivers/gpu/drm/i915/Makefile
>> > +++ b/drivers/gpu/drm/i915/Makefile
>> > @@ -87,6 +87,7 @@ i915-y += intel_audio.o \
>> >       intel_fbc.o \
>> >       intel_fifo_underrun.o \
>> >       intel_frontbuffer.o \
>> > +     intel_hdcp.o \
>> >       intel_hotplug.o \
>> >       intel_modes.o \
>> >       intel_overlay.o \
>> > diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
>> > index 68a58cce6ab1..43128030171d 100644
>> > --- a/drivers/gpu/drm/i915/i915_reg.h
>> > +++ b/drivers/gpu/drm/i915/i915_reg.h
>> > @@ -7991,6 +7991,7 @@ enum {
>> >   #define     GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT      8
>> >   #define     GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT      16
>> >   #define     GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT      24
>> > +#define   SKL_PCODE_LOAD_HDCP_KEYS         0x5
>> >   #define   SKL_PCODE_CDCLK_CONTROL         0x7
>> >   #define     SKL_CDCLK_PREPARE_FOR_CHANGE  0x3
>> >   #define     SKL_CDCLK_READY_FOR_CHANGE            0x1
>> > @@ -8285,6 +8286,88 @@ enum skl_power_gate {
>> >   #define  SKL_PW_TO_PG(pw)                 ((pw) - SKL_DISP_PW_1 + SKL_PG1)
>> >   #define  SKL_FUSE_PG_DIST_STATUS(pg)              (1 << (27 - (pg)))
>> > +
>> > +/* HDCP Key Registers */
>> > +#define SKL_HDCP_KEY_CONF          _MMIO(0x66c00)
>> > +#define     SKL_HDCP_AKSV_SEND_TRIGGER     BIT(31)
>> > +#define  SKL_HDCP_CLEAR_KEYS_TRIGGER       BIT(30)
>> > +#define SKL_HDCP_KEY_STATUS                _MMIO(0x66c04)
>> > +#define  SKL_HDCP_FUSE_IN_PROGRESS BIT(7)
>> > +#define  SKL_HDCP_FUSE_ERROR               BIT(6)
>> > +#define  SKL_HDCP_FUSE_DONE                BIT(5)
>> > +#define  SKL_HDCP_KEY_LOAD_STATUS  BIT(1)
>> > +#define  SKL_HDCP_KEY_LOAD_DONE            BIT(0)
>> > +#define SKL_HDCP_AKSV_LO           _MMIO(0x66c10)
>> > +#define SKL_HDCP_AKSV_HI           _MMIO(0x66c14)
>> > +
>> > +/* HDCP Repeater Registers */
>> > +#define SKL_HDCP_REP_CTL           _MMIO(0x66d00)
>> > +#define  SKL_HDCP_DDIB_REP_PRESENT BIT(30)
>> > +#define  SKL_HDCP_DDIA_REP_PRESENT BIT(29)
>> > +#define  SKL_HDCP_DDIC_REP_PRESENT BIT(28)
>> > +#define  SKL_HDCP_DDID_REP_PRESENT BIT(27)
>> > +#define  SKL_HDCP_DDIF_REP_PRESENT BIT(26)
>> > +#define  SKL_HDCP_DDIE_REP_PRESENT BIT(25)
>> > +#define  SKL_HDCP_DDIB_SHA1_M0             (1 << 20)
>> > +#define  SKL_HDCP_DDIA_SHA1_M0             (2 << 20)
>> > +#define  SKL_HDCP_DDIC_SHA1_M0             (3 << 20)
>> > +#define  SKL_HDCP_DDID_SHA1_M0             (4 << 20)
>> > +#define  SKL_HDCP_DDIF_SHA1_M0             (5 << 20)
>> > +#define  SKL_HDCP_DDIE_SHA1_M0             (6 << 20) // Bspec says 5?
>> > +#define  SKL_HDCP_SHA1_BUSY                BIT(16)
>> > +#define  SKL_HDCP_SHA1_READY               BIT(17)
>> > +#define  SKL_HDCP_SHA1_COMPLETE            BIT(18)
>> > +#define  SKL_HDCP_SHA1_V_MATCH             BIT(19)
>> > +#define  SKL_HDCP_SHA1_TEXT_32             (1 << 1)
>> > +#define  SKL_HDCP_SHA1_COMPLETE_HASH       (2 << 1)
>> > +#define  SKL_HDCP_SHA1_TEXT_24             (4 << 1)
>> > +#define  SKL_HDCP_SHA1_TEXT_16             (5 << 1)
>> > +#define  SKL_HDCP_SHA1_TEXT_8              (6 << 1)
>> > +#define  SKL_HDCP_SHA1_TEXT_0              (7 << 1)
>> > +#define SKL_HDCP_SHA_V_PRIME_H0            _MMIO(0x66d04)
>> > +#define SKL_HDCP_SHA_V_PRIME_H1            _MMIO(0x66d08)
>> > +#define SKL_HDCP_SHA_V_PRIME_H2            _MMIO(0x66d0C)
>> > +#define SKL_HDCP_SHA_V_PRIME_H3            _MMIO(0x66d10)
>> > +#define SKL_HDCP_SHA_V_PRIME_H4            _MMIO(0x66d14)
>> > +#define SKL_HDCP_SHA_V_PRIME(h)            _MMIO((0x66d04 + h * 4))
>> > +#define SKL_HDCP_SHA_TEXT          _MMIO(0x66d18)
>> > +
>> > +/* HDCP Auth Registers */
>> > +#define _SKL_PORTA_HDCP_AUTHENC            0x66800
>> > +#define _SKL_PORTB_HDCP_AUTHENC            0x66500
>> > +#define _SKL_PORTC_HDCP_AUTHENC            0x66600
>> > +#define _SKL_PORTD_HDCP_AUTHENC            0x66700
>> > +#define _SKL_PORTE_HDCP_AUTHENC            0x66A00
>> > +#define _SKL_PORTF_HDCP_AUTHENC            0x66900
>> > +#define _SKL_PORT_HDCP_AUTHENC(port, x)    _MMIO(_PICK(port, \
>> > +                                     _SKL_PORTA_HDCP_AUTHENC, \
>> > +                                     _SKL_PORTB_HDCP_AUTHENC, \
>> > +                                     _SKL_PORTC_HDCP_AUTHENC, \
>> > +                                     _SKL_PORTD_HDCP_AUTHENC, \
>> > +                                     _SKL_PORTE_HDCP_AUTHENC, \
>> > +                                     _SKL_PORTF_HDCP_AUTHENC) + x)
>> > +#define SKL_PORT_HDCP_CONF(port)   _SKL_PORT_HDCP_AUTHENC(port, 0x0)
>> > +#define  SKL_HDCP_CONF_CAPTURE_AN  BIT(0)
>> > +#define  SKL_HDCP_CONF_AUTH_AND_ENC        (BIT(1) | BIT(0))
>> > +#define SKL_PORT_HDCP_ANINIT(port) _SKL_PORT_HDCP_AUTHENC(port, 0x4)
>> > +#define SKL_PORT_HDCP_ANLO(port)   _SKL_PORT_HDCP_AUTHENC(port, 0x8)
>> > +#define SKL_PORT_HDCP_ANHI(port)   _SKL_PORT_HDCP_AUTHENC(port, 0xC)
>> > +#define SKL_PORT_HDCP_BKSVLO(port) _SKL_PORT_HDCP_AUTHENC(port, 0x10)
>> > +#define SKL_PORT_HDCP_BKSVHI(port) _SKL_PORT_HDCP_AUTHENC(port, 0x14)
>> > +#define SKL_PORT_HDCP_RPRIME(port) _SKL_PORT_HDCP_AUTHENC(port, 0x18)
>> > +#define SKL_PORT_HDCP_STATUS(port) _SKL_PORT_HDCP_AUTHENC(port, 0x1C)
>> > +#define  SKL_HDCP_STATUS_STREAM_A_ENC      BIT(31)
>> > +#define  SKL_HDCP_STATUS_STREAM_B_ENC      BIT(30)
>> > +#define  SKL_HDCP_STATUS_STREAM_C_ENC      BIT(29)
>> > +#define  SKL_HDCP_STATUS_STREAM_D_ENC      BIT(28)
>> > +#define  SKL_HDCP_STATUS_AUTH              BIT(21)
>> > +#define  SKL_HDCP_STATUS_ENC               BIT(20)
>> > +#define  SKL_HDCP_STATUS_RI_MATCH  BIT(19)
>> > +#define  SKL_HDCP_STATUS_R0_READY  BIT(18)
>> > +#define  SKL_HDCP_STATUS_AN_READY  BIT(17)
>> > +#define  SKL_HDCP_STATUS_CIPHER            BIT(16)
>> > +#define  SKL_HDCP_STATUS_FRAME_CNT(x)      ((x >> 8) & 0xff)
>> > +
>> >   /* Per-pipe DDI Function Control */
>> >   #define _TRANS_DDI_FUNC_CTL_A             0x60400
>> >   #define _TRANS_DDI_FUNC_CTL_B             0x61400
>> > diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
>> > index 36d4e635e4ce..ddf08227d9cb 100644
>> > --- a/drivers/gpu/drm/i915/intel_atomic.c
>> > +++ b/drivers/gpu/drm/i915/intel_atomic.c
>> > @@ -109,12 +109,34 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
>> >     struct intel_digital_connector_state *old_conn_state =
>> >             to_intel_digital_connector_state(old_state);
>> >     struct drm_crtc_state *crtc_state;
>> > -
>> > -   if (!new_state->crtc)
>> > +   uint64_t old_cp = old_conn_state->base.content_protection;
>> > +   uint64_t new_cp = new_state->content_protection;
>> > +
>> > +   if (!new_state->crtc) {
>> > +           /*
>> > +            * If the connector is being disabled with CP enabled, mark it
>> > +            * desired so it's re-enabled when the connector is brought back
>> > +            */
>> > +           if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
>> > +                   new_state->content_protection =
>> > +                           DRM_MODE_CONTENT_PROTECTION_DESIRED;
>> >             return 0;
>> > +   }
>> >     crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc);
>> > +   if (new_cp != old_cp) {
>> > +           /* Only drivers can set content protection enabled */
>> > +           if (new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
>> > +                   new_state->content_protection =
>> > +                           DRM_MODE_CONTENT_PROTECTION_DESIRED;
>> > +
>> > +           /* Involve the encoder/connector to enable/disable CP */
>> > +           if (new_cp == DRM_MODE_CONTENT_PROTECTION_OFF ||
>> > +               old_cp == DRM_MODE_CONTENT_PROTECTION_OFF)
>> > +                   crtc_state->mode_changed = true;
>>
>> We need not perform the mode set for hdcp enable/disable.
>> Authentication and encryption can be started on active port.
>
> Was simpler to implement this way :-) We can fix this by pushing the hdcp
> enable/disable code into a post-modeset operation. Ville had written the
> infrastructure for that to fix a few fastboot corner cases. But that
> infrastructure hasn't landed yet, so probably better to do that in a
> follow-up.
>
> I also guess that CrOS simply set this to desired every time they enable
> an external screen, so it won't result in an unecessary modeset.
>

Actually, we enable only as needed, if there is no HD licensed content
on the screen, the link is unencrypted. The UX of doing it through
enable/disable is meh, but things are a lot easier to reason about.

>> > +   }
>> > +
>> >     /*
>> >      * These properties are handled by fastset, and might not end
>> >      * up in a modeset.
>> > diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
>> > index 933c18fd4258..0e69337f410d 100644
>> > --- a/drivers/gpu/drm/i915/intel_ddi.c
>> > +++ b/drivers/gpu/drm/i915/intel_ddi.c
>> > @@ -2432,10 +2432,17 @@ static void intel_enable_ddi(struct intel_encoder *encoder,
>> >                          const struct intel_crtc_state *crtc_state,
>> >                          const struct drm_connector_state *conn_state)
>> >   {
>> > +   struct drm_connector *connector = conn_state->connector;
>> > +   struct intel_connector *intel_connector = to_intel_connector(connector);
>> > +
>> >     if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
>> >             intel_enable_ddi_hdmi(encoder, crtc_state, conn_state);
>> >     else
>> >             intel_enable_ddi_dp(encoder, crtc_state, conn_state);
>> > +
>> > +   if (conn_state->content_protection ==
>> > +                   DRM_MODE_CONTENT_PROTECTION_DESIRED)
>> > +           intel_hdcp_enable(intel_connector);
>> >   }
>> >   static void intel_disable_ddi_dp(struct intel_encoder *encoder,
>> > @@ -2468,10 +2475,17 @@ static void intel_disable_ddi(struct intel_encoder *encoder,
>> >                           const struct intel_crtc_state *old_crtc_state,
>> >                           const struct drm_connector_state *old_conn_state)
>> >   {
>> > +   struct drm_connector *connector = old_conn_state->connector;
>> > +   struct intel_connector *intel_connector = to_intel_connector(connector);
>> > +
>> >     if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
>> >             intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state);
>> >     else
>> >             intel_disable_ddi_dp(encoder, old_crtc_state, old_conn_state);
>> > +
>> > +   if (old_conn_state->content_protection !=
>> > +                   DRM_MODE_CONTENT_PROTECTION_OFF)
>> > +           intel_hdcp_disable(intel_connector);
>> We might want to disable the hdcp before disabling the DDI. Actually we
>> could trigger hdcp disable at connector state change(due to hot-unplug)
>> also.
>
> Yeah this part needs to be reworked a bit, also because the locking
> doesn't work yet. I think hot-unplug is handled by the worker thread
> already, it does the mandatory regular polling.

Yeah, this should go above the disable. intel_hdcp_disable() only does
transmitter-side operations, but it's certainly better form to
wind/unwind in enable/disable.

Sean

> -Daniel
>
>>
>> Thanks,
>> --Ram
>> >   }
>> >   static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder,
>> > diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
>> > index 47d022d48718..8924004575b8 100644
>> > --- a/drivers/gpu/drm/i915/intel_drv.h
>> > +++ b/drivers/gpu/drm/i915/intel_drv.h
>> > @@ -299,6 +299,49 @@ struct intel_panel {
>> >     } backlight;
>> >   };
>> > +struct intel_hdcp_shim {
>> > +   /* Outputs the transmitter's An and Aksv values to the receiver. */
>> > +   int (*write_an_aksv)(struct intel_digital_port *intel_dig_port, u8 *an);
>> > +
>> > +   /* Reads the receiver's key selection vector */
>> > +   int (*read_bksv)(struct intel_digital_port *intel_dig_port, u8 *bksv);
>> > +
>> > +   /*
>> > +    * Reads BINFO from DP receivers and BSTATUS from HDMI receivers. The
>> > +    * definitions are the same in the respective specs, but the names are
>> > +    * different. Call it BSTATUS since that's the name the HDMI spec
>> > +    * uses and it was there first.
>> > +    */
>> > +   int (*read_bstatus)(struct intel_digital_port *intel_dig_port,
>> > +                       u8 *bstatus);
>> > +
>> > +   /* Determines whether a repeater is present downstream */
>> > +   int (*repeater_present)(struct intel_digital_port *intel_dig_port,
>> > +                           bool *repeater_present);
>> > +
>> > +   /* Reads the receiver's Ri' value */
>> > +   int (*read_ri_prime)(struct intel_digital_port *intel_dig_port, u8 *ri);
>> > +
>> > +   /* Determines if the receiver's KSV FIFO is ready for consumption */
>> > +   int (*read_ksv_ready)(struct intel_digital_port *intel_dig_port,
>> > +                         bool *ksv_ready);
>> > +
>> > +   /* Reads the ksv fifo for num_downstream devices */
>> > +   int (*read_ksv_fifo)(struct intel_digital_port *intel_dig_port,
>> > +                        int num_downstream, u8 *ksv_fifo);
>> > +
>> > +   /* Reads a 32-bit part of V' from the receiver */
>> > +   int (*read_v_prime_part)(struct intel_digital_port *intel_dig_port,
>> > +                            int i, u32 *part);
>> > +
>> > +   /* Enables HDCP signalling on the port */
>> > +   int (*toggle_signalling)(struct intel_digital_port *intel_dig_port,
>> > +                            bool enable);
>> > +
>> > +   /* Ensures the link is still protected */
>> > +   bool (*check_link)(struct intel_digital_port *intel_dig_port);
>> > +};
>> > +
>> >   struct intel_connector {
>> >     struct drm_connector base;
>> >     /*
>> > @@ -330,6 +373,9 @@ struct intel_connector {
>> >     /* Work struct to schedule a uevent on link train failure */
>> >     struct work_struct modeset_retry_work;
>> > +
>> > +   const struct intel_hdcp_shim *hdcp_shim;
>> > +   struct delayed_work hdcp_work;
>> >   };
>> >   struct intel_digital_connector_state {
>> > @@ -1295,6 +1341,8 @@ void intel_ddi_set_vc_payload_alloc(const struct intel_crtc_state *crtc_state,
>> >                                 bool state);
>> >   u32 bxt_signal_levels(struct intel_dp *intel_dp);
>> >   uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
>> > +int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder);
>> > +int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder);
>> >   u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
>> >   unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
>> > @@ -1746,6 +1794,11 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
>> >   }
>> >   #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
>> > +/* intel_hdcp.c */
>> > +int intel_hdcp_enable(struct intel_connector *connector);
>> > +int intel_hdcp_disable(struct intel_connector *connector);
>> > +int intel_hdcp_check_link(struct intel_connector *connector);
>> > +void intel_hdcp_work(struct work_struct *work);
>> >   /* intel_psr.c */
>> >   void intel_psr_enable(struct intel_dp *intel_dp,
>> > diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
>> > new file mode 100644
>> > index 000000000000..a2a575ed657e
>> > --- /dev/null
>> > +++ b/drivers/gpu/drm/i915/intel_hdcp.c
>> > @@ -0,0 +1,636 @@
>> > +/*
>> > + * Copyright (C) 2017 Google, Inc.
>> > + *
>> > + * This software is licensed under the terms of the GNU General Public
>> > + * License version 2, as published by the Free Software Foundation, and
>> > + * may be copied, distributed, and modified under those terms.
>> > + *
>> > + * This program is distributed in the hope that it will be useful,
>> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> > + * GNU General Public License for more details.
>> > + */
>> > +
>> > +#include <drm/drmP.h>
>> > +#include <drm/drm_hdcp.h>
>> > +#include <linux/i2c.h>
>> > +#include <linux/random.h>
>> > +
>> > +#include "intel_drv.h"
>> > +#include "i915_reg.h"
>> > +
>> > +#define KEY_LOAD_TRIES     5
>> > +
>> > +static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
>> > +                               const struct intel_hdcp_shim *shim)
>> > +{
>> > +   unsigned long timeout = jiffies + msecs_to_jiffies_timeout(500);
>> > +   int ret;
>> > +   bool ksv_ready;
>> > +
>> > +   while (true) {
>> > +           ret = shim->read_ksv_ready(intel_dig_port, &ksv_ready);
>> > +           if (ret)
>> > +                   return ret;
>> > +           if (ksv_ready)
>> > +                   break;
>> > +           if (time_after(jiffies, timeout))
>> > +                   return -ETIMEDOUT;
>> > +           msleep(100);
>> > +   }
>> > +   return 0;
>> > +}
>> > +
>> > +static void intel_hdcp_clear_keys(struct drm_i915_private *dev_priv)
>> > +{
>> > +   I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_CLEAR_KEYS_TRIGGER);
>> > +   I915_WRITE(SKL_HDCP_KEY_STATUS,
>> > +              SKL_HDCP_KEY_LOAD_DONE | SKL_HDCP_KEY_LOAD_STATUS |
>> > +              SKL_HDCP_FUSE_IN_PROGRESS | SKL_HDCP_FUSE_ERROR |
>> > +              SKL_HDCP_FUSE_DONE);
>> > +}
>> > +
>> > +static int intel_hdcp_load_keys(struct drm_i915_private *dev_priv)
>> > +{
>> > +   unsigned long timeout;
>> > +   int ret;
>> > +   u32 val;
>> > +
>> > +   // Initiate loading the HDCP key from fuses
>> > +   mutex_lock(&dev_priv->pcu_lock);
>> > +   ret = sandybridge_pcode_write(dev_priv, SKL_PCODE_LOAD_HDCP_KEYS, 1);
>> > +   mutex_unlock(&dev_priv->pcu_lock);
>> > +   if (ret) {
>> > +           DRM_ERROR("Failed to initiate HDCP key load (%d)\n", ret);
>> > +           return ret;
>> > +   }
>> > +
>> > +   // Wait for the keys to load (500us)
>> > +   timeout = jiffies + nsecs_to_jiffies_timeout(500 * 1000);
>> > +   while (true) {
>> > +           val = I915_READ(SKL_HDCP_KEY_STATUS);
>> > +           if (val & SKL_HDCP_KEY_LOAD_DONE)
>> > +                   break;
>> > +           if (time_after(jiffies, timeout))
>> > +                   return -ETIMEDOUT;
>> > +           usleep_range(50, 100);
>> > +   }
>> > +   if (!(val & SKL_HDCP_KEY_LOAD_STATUS))
>> > +           return -ENXIO;
>> > +
>> > +   // Send Aksv over to PCH display for use in authentication
>> > +   I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_AKSV_SEND_TRIGGER);
>> > +
>> > +   return 0;
>> > +}
>> > +
>> > +/* Returns updated SHA-1 index */
>> > +static int intel_write_sha_text(struct drm_i915_private *dev_priv, u32 sha_text)
>> > +{
>> > +   I915_WRITE(SKL_HDCP_SHA_TEXT, sha_text);
>> > +   if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_READY, 1)) {
>> > +           DRM_ERROR("Timed out waiting for SHA1 ready\n");
>> > +           return -ETIMEDOUT;
>> > +   }
>> > +   return 0;
>> > +}
>> > +
>> > +static
>> > +u32 intel_hdcp_get_repeater_ctl(struct intel_digital_port *intel_dig_port)
>> > +{
>> > +   enum port port = intel_dig_port->port;
>> > +   switch(port) {
>> > +   case PORT_A:
>> > +           return SKL_HDCP_DDIA_REP_PRESENT | SKL_HDCP_DDIA_SHA1_M0;
>> > +   case PORT_B:
>> > +           return SKL_HDCP_DDIB_REP_PRESENT | SKL_HDCP_DDIB_SHA1_M0;
>> > +   case PORT_C:
>> > +           return SKL_HDCP_DDIC_REP_PRESENT | SKL_HDCP_DDIC_SHA1_M0;
>> > +   case PORT_D:
>> > +           return SKL_HDCP_DDID_REP_PRESENT | SKL_HDCP_DDID_SHA1_M0;
>> > +   case PORT_E:
>> > +           return SKL_HDCP_DDIE_REP_PRESENT | SKL_HDCP_DDIE_SHA1_M0;
>> > +   default:
>> > +           break;
>> > +   }
>> > +   DRM_ERROR("Unknown port %d\n", port);
>> > +   return -EINVAL;
>> > +}
>> > +
>> > +/* Implements Part 2 of the HDCP authorization procedure */
>> > +static
>> > +int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
>> > +                          const struct intel_hdcp_shim *shim)
>> > +{
>> > +   struct drm_i915_private *dev_priv;
>> > +   u32 vprime, sha_text, sha_leftovers, rep_ctl;
>> > +   u8 bstatus[2], num_downstream, *ksv_fifo;
>> > +   int ret, i, j, sha_idx;
>> > +
>> > +   dev_priv = intel_dig_port->base.base.dev->dev_private;
>> > +
>> > +   ret = shim->read_bstatus(intel_dig_port, bstatus);
>> > +   if (ret)
>> > +           return ret;
>> > +
>> > +   /* If there are no downstream devices, we're all done. */
>> > +   num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
>> > +   if (num_downstream == 0) {
>> > +           DRM_INFO("HDCP is enabled (no downstream devices)\n");
>> > +           return 0;
>> > +   }
>> > +
>> > +   // Poll for ksv list ready (spec says max time allowed is 5s)
>> > +   ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim);
>> > +   if (ret) {
>> > +           DRM_ERROR("KSV list failed to become ready (%d)\n", ret);
>> > +           return ret;
>> > +   }
>> > +
>> > +   ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL);
>> > +   if (!ksv_fifo)
>> > +           return -ENOMEM;
>> > +
>> > +   ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo);
>> > +   if (ret)
>> > +           return ret;
>> > +
>> > +   // Process V' values from the receiver
>> > +   for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
>> > +           ret = shim->read_v_prime_part(intel_dig_port, i, &vprime);
>> > +           if (ret)
>> > +                   return ret;
>> > +           I915_WRITE(SKL_HDCP_SHA_V_PRIME(i), vprime);
>> > +   }
>> > +
>> > +   /*
>> > +    * We need to write the concatenation of all device KSVs, BINFO (DP) ||
>> > +    * BSTATUS (HDMI), and M0 (which is added via HDCP_REP_CTL). This byte
>> > +    * stream is written via the HDCP_SHA_TEXT register in 32-bit
>> > +    * increments. Every 64 bytes, we need to write HDCP_REP_CTL again. This
>> > +    * index will keep track of our progress through the 64 bytes as well as
>> > +    * helping us work the 40-bit KSVs through our 32-bit register.
>> > +    *
>> > +    * NOTE: data passed via HDCP_SHA_TEXT should be big-endian
>> > +    */
>> > +   sha_idx = 0;
>> > +   sha_text = 0;
>> > +   sha_leftovers = 0;
>> > +   rep_ctl = intel_hdcp_get_repeater_ctl(intel_dig_port);
>> > +   I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>> > +   for (i = 0; i < num_downstream; i++) {
>> > +           unsigned sha_empty;
>> > +           u8 *ksv = &ksv_fifo[i * DRM_HDCP_KSV_LEN];
>> > +
>> > +           // Fill up the empty slots in sha_text and write it out
>> > +           sha_empty = sizeof(sha_text) - sha_leftovers;
>> > +           for (j = 0; j < sha_empty; j++)
>> > +                   sha_text |= ksv[j] << ((sizeof(sha_text) - j - 1) * 8);
>> > +
>> > +           ret = intel_write_sha_text(dev_priv, sha_text);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +
>> > +           // Programming guide writes this every 64 bytes
>> > +           sha_idx += sizeof(sha_text);
>> > +           if (!(sha_idx % 64))
>> > +                   I915_WRITE(SKL_HDCP_REP_CTL,
>> > +                              rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>> > +
>> > +           // Store the leftover bytes from the ksv in sha_text
>> > +           sha_leftovers = DRM_HDCP_KSV_LEN - sha_empty;
>> > +           sha_text = 0;
>> > +           for (j = 0; j < sha_leftovers; j++)
>> > +                   sha_text |= ksv[sha_empty + j] <<
>> > +                                   ((sizeof(sha_text) - j - 1) * 8);
>> > +
>> > +           /*
>> > +            * If we still have room in sha_text for more data, continue.
>> > +            * Otherwise, write it out immediately.
>> > +            */
>> > +           if (sizeof(sha_text) > sha_leftovers)
>> > +                   continue;
>> > +
>> > +           ret = intel_write_sha_text(dev_priv, sha_text);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_leftovers = 0;
>> > +           sha_text = 0;
>> > +           sha_idx += sizeof(sha_text);
>> > +   }
>> > +
>> > +   /*
>> > +    * We need to write BINFO/BSTATUS, and M0 now. Depending on how many
>> > +    * bytes are leftover from the last ksv, we might be able to fit them
>> > +    * all in sha_text (first 2 cases), or we might need to split them up
>> > +    * into 2 writes (last 2 cases).
>> > +    */
>> > +   if (sha_leftovers == 0) {
>> > +           // Write 16 bits of text, 16 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
>> > +           ret = intel_write_sha_text(dev_priv,
>> > +                                      bstatus[0] << 8 | bstatus[1]);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +           // Write 32 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>> > +           ret = intel_write_sha_text(dev_priv, 0);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +           // Write 16 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
>> > +           ret = intel_write_sha_text(dev_priv, 0);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +   } else if (sha_leftovers == 1) {
>> > +           // Write 24 bits of text, 8 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
>> > +           sha_text |= bstatus[0] << 16 | bstatus[1] << 8;
>> > +           // Only 24-bits of data, must be in the LSB
>> > +           sha_text = (sha_text & 0xffffff00) >> 8;
>> > +           ret = intel_write_sha_text(dev_priv, sha_text);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +           // Write 32 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>> > +           ret = intel_write_sha_text(dev_priv, 0);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +           // Write 24 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
>> > +           ret = intel_write_sha_text(dev_priv, 0);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +   } else if (sha_leftovers == 2) {
>> > +           // Write 32 bits of text
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>> > +           sha_text |= bstatus[0] << 24 | bstatus[1] << 16;
>> > +           ret = intel_write_sha_text(dev_priv, sha_text);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +           // Write 64 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>> > +           for (i = 0; i < 2; i++) {
>> > +                   ret = intel_write_sha_text(dev_priv, 0);
>> > +                   if (ret < 0)
>> > +                           return ret;
>> > +                   sha_idx += sizeof(sha_text);
>> > +           }
>> > +   } else if (sha_leftovers == 3) {
>> > +           // Write 32 bits of text
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>> > +           sha_text |= bstatus[0] << 24;
>> > +           ret = intel_write_sha_text(dev_priv, sha_text);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +           // Write 8 bits of text, 24 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
>> > +           ret = intel_write_sha_text(dev_priv, bstatus[1]);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +           // Write 32 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>> > +           ret = intel_write_sha_text(dev_priv, 0);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +           // Write 8 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
>> > +           ret = intel_write_sha_text(dev_priv, 0);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +   } else {
>> > +           DRM_ERROR("Invalid number of leftovers %d\n", sha_leftovers);
>> > +           return -EINVAL;
>> > +   }
>> > +
>> > +   I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>> > +   // Fill up to 64 - 4 bytes with zeros (leave the last write for length)
>> > +   while ((sha_idx % 64) < (64 - sizeof(sha_text))) {
>> > +           ret = intel_write_sha_text(dev_priv, 0);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +   }
>> > +
>> > +   /*
>> > +    * Last write gets the length of the concatenation in bits. That is:
>> > +    *  - 5 bytes per device
>> > +    *  - 10 bytes for BINFO/BSTATUS(2), M0(8)
>> > +    */
>> > +   sha_text = (num_downstream * 5 + 10) * 8;
>> > +   ret = intel_write_sha_text(dev_priv, sha_text);
>> > +   if (ret < 0)
>> > +           return ret;
>> > +
>> > +   // Finally, tell the HW we're done with the hash and wait for it to ACK
>> > +   I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_COMPLETE_HASH);
>> > +   if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_COMPLETE, 1)) {
>> > +           DRM_ERROR("Timed out waiting for SHA1 complete\n");
>> > +           return -ETIMEDOUT;
>> > +   }
>> > +   if (!(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_V_MATCH)) {
>> > +           DRM_ERROR("SHA-1 mismatch, HDCP failed\n");
>> > +           return -ENXIO;
>> > +   }
>> > +
>> > +   DRM_INFO("HDCP is enabled (%d downstream devices)\n", num_downstream);
>> > +   return 0;
>> > +}
>> > +
>> > +/* Implements Part 1 of the HDCP authorization procedure */
>> > +static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
>> > +                      const struct intel_hdcp_shim *shim)
>> > +{
>> > +   struct drm_i915_private *dev_priv;
>> > +   enum port port;
>> > +   unsigned long r0_prime_gen_start;
>> > +   int ret, i;
>> > +   union {
>> > +           u32 reg[2];
>> > +           u8 shim[DRM_HDCP_AN_LEN];
>> > +   } an;
>> > +   union {
>> > +           u32 reg[2];
>> > +           u8 shim[DRM_HDCP_KSV_LEN];
>> > +   } bksv;
>> > +   union {
>> > +           u32 reg;
>> > +           u8 shim[DRM_HDCP_RI_LEN];
>> > +   } ri;
>> > +   bool repeater_present;
>> > +
>> > +   dev_priv = intel_dig_port->base.base.dev->dev_private;
>> > +
>> > +   port = intel_dig_port->port;
>> > +
>> > +   // Initialize An with 2 random values and acquire it
>> > +   for (i = 0; i < 2; i++)
>> > +           I915_WRITE(SKL_PORT_HDCP_ANINIT(port), get_random_long());
>> > +   I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_CAPTURE_AN);
>> > +
>> > +   // Wait for An to be acquired
>> > +   if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>> > +                SKL_HDCP_STATUS_AN_READY, 1)) {
>> > +           DRM_ERROR("Timed out waiting for An\n");
>> > +           return -ETIMEDOUT;
>> > +   }
>> > +
>> > +   an.reg[0] = I915_READ(SKL_PORT_HDCP_ANLO(port));
>> > +   an.reg[1] = I915_READ(SKL_PORT_HDCP_ANHI(port));
>> > +   ret = shim->write_an_aksv(intel_dig_port, an.shim);
>> > +   if (ret)
>> > +           return ret;
>> > +
>> > +   r0_prime_gen_start = jiffies;
>> > +
>> > +   memset(&bksv, 0, sizeof(bksv));
>> > +   ret = shim->read_bksv(intel_dig_port, bksv.shim);
>> > +   if (ret)
>> > +           return ret;
>> > +
>> > +   I915_WRITE(SKL_PORT_HDCP_BKSVLO(port), bksv.reg[0]);
>> > +   I915_WRITE(SKL_PORT_HDCP_BKSVHI(port), bksv.reg[1]);
>> > +
>> > +   ret = shim->repeater_present(intel_dig_port, &repeater_present);
>> > +   if (ret)
>> > +           return ret;
>> > +   if (repeater_present)
>> > +           I915_WRITE(SKL_HDCP_REP_CTL,
>> > +                      intel_hdcp_get_repeater_ctl(intel_dig_port));
>> > +
>> > +   ret = shim->toggle_signalling(intel_dig_port, true);
>> > +   if (ret)
>> > +           return ret;
>> > +
>> > +   I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_AUTH_AND_ENC);
>> > +
>> > +   // Wait for R0 ready
>> > +   if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>> > +                (SKL_HDCP_STATUS_R0_READY | SKL_HDCP_STATUS_ENC), 1)) {
>> > +           DRM_ERROR("Timed out waiting for R0 ready\n");
>> > +           return -ETIMEDOUT;
>> > +   }
>> > +
>> > +   /*
>> > +    * Wait for R0' to become available, the spec says 100ms from Aksv
>> > +    * write. On DP, there's an R0_READY bit available but no such bit
>> > +    * exists on HDMI. Since the upper-bound is the same, we'll just do
>> > +    * the stupid thing instead of polling on one and not the other.
>> > +    */
>> > +   wait_remaining_ms_from_jiffies(r0_prime_gen_start, 100);
>> > +
>> > +   ri.reg = 0;
>> > +   ret = shim->read_ri_prime(intel_dig_port, ri.shim);
>> > +   if (ret)
>> > +           return ret;
>> > +   I915_WRITE(SKL_PORT_HDCP_RPRIME(port), ri.reg);
>> > +
>> > +   // Wait for Ri prime match
>> > +   if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>> > +                (SKL_HDCP_STATUS_RI_MATCH | SKL_HDCP_STATUS_ENC), 1)) {
>> > +           DRM_ERROR("Timed out waiting for Ri prime match (%x)\n",
>> > +                     I915_READ(SKL_PORT_HDCP_STATUS(port)));
>> > +           return -ETIMEDOUT;
>> > +   }
>> > +
>> > +   // Wait for encryption confirmation
>> > +   if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>> > +                 SKL_HDCP_STATUS_ENC, 20)) {
>> > +           DRM_ERROR("Timed out waiting for encryption\n");
>> > +           return -ETIMEDOUT;
>> > +   }
>> > +
>> > +   /*
>> > +    * XXX: If we have MST-connected devices, we need to enable encryption
>> > +    * on those as well.
>> > +    */
>> > +
>> > +   return intel_hdcp_auth_downstream(intel_dig_port, shim);
>> > +}
>> > +
>> > +static
>> > +struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector)
>> > +{
>> > +   return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base);
>> > +}
>> > +
>> > +static int _intel_hdcp_disable(struct intel_connector *connector)
>> > +{
>> > +   struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>> > +   struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
>> > +   enum port port = intel_dig_port->port;
>> > +   int ret;
>> > +
>> > +   I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
>> > +   if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) == 0, 20)) {
>> > +           DRM_ERROR("Failed to disable HDCP, timeout clearing status\n");
>> > +           return -ETIMEDOUT;
>> > +   }
>> > +
>> > +   intel_hdcp_clear_keys(dev_priv);
>> > +
>> > +   ret = connector->hdcp_shim->toggle_signalling(intel_dig_port, false);
>> > +   if (ret) {
>> > +           DRM_ERROR("Failed to disable HDCP signalling\n");
>> > +           return ret;
>> > +   }
>> > +
>> > +   DRM_INFO("HDCP is disabled\n");
>> > +   return 0;
>> > +}
>> > +
>> > +static int _intel_hdcp_enable(struct intel_connector *connector)
>> > +{
>> > +   struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>> > +   int i, ret;
>> > +
>> > +   if (!(I915_READ(SKL_FUSE_STATUS) & SKL_FUSE_PG_DIST_STATUS(1))) {
>> > +           DRM_ERROR("PG1 is disabled, cannot load keys\n");
>> > +           return -ENXIO;
>> > +   }
>> > +
>> > +   for (i = 0; i < KEY_LOAD_TRIES; i++) {
>> > +           ret = intel_hdcp_load_keys(dev_priv);
>> > +           if (!ret)
>> > +                   break;
>> > +           intel_hdcp_clear_keys(dev_priv);
>> > +   }
>> > +   if (ret) {
>> > +           DRM_ERROR("Could not load HDCP keys, (%d)\n", ret);
>> > +           return ret;
>> > +   }
>> > +
>> > +   ret = intel_hdcp_auth(conn_to_dig_port(connector),
>> > +                         connector->hdcp_shim);
>> > +   if (ret) {
>> > +           DRM_ERROR("Failed to authenticate HDCP (%d)\n", ret);
>> > +           return ret;
>> > +   }
>> > +
>> > +   return 0;
>> > +}
>> > +
>> > +void intel_hdcp_work(struct work_struct *work)
>> > +{
>> > +   struct intel_connector *connector = container_of(to_delayed_work(work),
>> > +                                                    struct intel_connector,
>> > +                                                    hdcp_work);
>> > +   struct drm_device *dev = connector->base.dev;
>> > +   int ret;
>> > +
>> > +   drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>> > +
>> > +   ret = intel_hdcp_check_link(connector);
>> > +   if (!ret)
>> > +           schedule_delayed_work(&connector->hdcp_work,
>> > +                                 DRM_HDCP_CHECK_PERIOD_MS);
>> > +
>> > +   drm_modeset_unlock(&dev->mode_config.connection_mutex);
>> > +}
>> > +
>> > +int intel_hdcp_enable(struct intel_connector *connector)
>> > +{
>> > +   struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>> > +   struct drm_device *dev = &dev_priv->drm;
>> > +   struct drm_connector_state *state = connector->base.state;
>> > +   int ret;
>> > +
>> > +   WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>> > +
>> > +   if (!connector->hdcp_shim)
>> > +           return -ENOENT;
>> > +
>> > +   ret = _intel_hdcp_enable(connector);
>> > +   if (ret)
>> > +           return ret;
>> > +
>> > +   state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
>> > +
>> > +   schedule_delayed_work(&connector->hdcp_work, DRM_HDCP_CHECK_PERIOD_MS);
>> > +   return 0;
>> > +}
>> > +
>> > +int intel_hdcp_disable(struct intel_connector *connector)
>> > +{
>> > +   struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>> > +   struct drm_device *dev = &dev_priv->drm;
>> > +
>> > +   WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>> > +
>> > +   if (!connector->hdcp_shim)
>> > +           return -ENOENT;
>> > +
>> > +   cancel_delayed_work(&connector->hdcp_work);
>> > +
>> > +   return _intel_hdcp_disable(connector);
>> > +}
>> > +
>> > +/* Implements Part 3 of the HDCP authorization procedure */
>> > +int intel_hdcp_check_link(struct intel_connector *connector)
>> > +{
>> > +   struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>> > +   struct drm_device *dev = &dev_priv->drm;
>> > +   struct drm_connector_state *state = connector->base.state;
>> > +   struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
>> > +   enum port port = intel_dig_port->port;
>> > +   int ret;
>> > +
>> > +   WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>> > +
>> > +   if (state->content_protection != DRM_MODE_CONTENT_PROTECTION_ENABLED)
>> > +           return 0;
>> > +
>> > +   if (!connector->hdcp_shim)
>> > +           return -ENOENT;
>> > +
>> > +   if (!(I915_READ(SKL_PORT_HDCP_STATUS(port)) & SKL_HDCP_STATUS_ENC)) {
>> > +           DRM_ERROR("HDCP check failed: link is not encrypted, %x\n",
>> > +                      I915_READ(SKL_PORT_HDCP_STATUS(port)));
>> > +           ret = -ENXIO;
>> > +           goto fail;
>> > +   }
>> > +
>> > +   if (connector->hdcp_shim->check_link(intel_dig_port))
>> > +           return 0;
>> > +
>> > +   DRM_INFO("HDCP link failed, retrying authentication\n");
>> > +
>> > +   ret = _intel_hdcp_disable(connector);
>> > +   if (ret) {
>> > +           DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
>> > +           goto fail;
>> > +   }
>> > +
>> > +   ret = _intel_hdcp_enable(connector);
>> > +   if (ret) {
>> > +           DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
>> > +           goto fail;
>> > +   }
>> > +
>> > +   return 0;
>> > +
>> > +fail:
>> > +   state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
>> > +   return ret;
>> > +}
>>
>> _______________________________________________
>> Intel-gfx mailing list
>> Intel-gfx@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 3/6] drm/i915: Add HDCP framework + base implementation
@ 2017-12-01 14:16         ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-12-01 14:16 UTC (permalink / raw)
  To: Ramalingam C, Sean Paul, dri-devel, Intel Graphics Development,
	David Airlie, Linux Kernel Mailing List, Rodrigo Vivi

On Fri, Dec 1, 2017 at 2:36 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Fri, Dec 01, 2017 at 12:53:31PM +0530, Ramalingam C wrote:
>> Sean,
>>
>> IMHO, it will good if we can have all generic hdcp1.4 authentication flow in
>> drm helpers and all interested display drivers to use them.
>>
>> This Design will make the extending of hdcp easy for other display drivers
>> based on DRM.
>>
>> We can have the required drm_hdcp_shim type of implementation at drm
>> structure which will be called for platform specific operations (like
>> prepare an, send aksv, program bksv/repeater/r0 and verify sha1 etc)?
>
> I discussed this exact question with Sean Paul, and apparently the
> hardware designs are too diverse to make shared code much useful. Some hw
> has the entire hdcp flow in hw, some almost nothing (like i915 here), and
> then there's everything in between.
>
> Given that Sean has seen a lot more hdcp implementations than we have,
> that we right now have no other implementation than i915 in upstream and
> than wrong abstraction is much harder to fix than no abstraction I'm going
> with Sean's approach of "no generic abstraction" here. Personally I'm not
> even fully sold on the shim abstraction, but I think by&large that one is
> fine.
>

[html fail on the first response, resending in plain text]

I think there's some sharing potential between exynos and i915, but
the rockchip stuff is completely different. Even exynos differs in
that each step of the authentication process is interrupt driven
(iirc). I just don't see a pattern worth abstracting atm. We might be
able to share the enable/disable/check song & dance, but let's not
worry about abstraction until we have 2 implementations.


>> On Thursday 30 November 2017 08:38 AM, Sean Paul wrote:
>> > This patch adds the framework required to add HDCP support to intel
>> > connectors. It implements Aksv loading from fuse, and parts 1/2/3
>> > of the HDCP authentication scheme.
>> >
>> > Note that without shim implementations, this does not actually implement
>> > HDCP. That will come in subsequent patches.
>> >
>> > Signed-off-by: Sean Paul <seanpaul@chromium.org>
>> > ---
>> >   drivers/gpu/drm/i915/Makefile       |   1 +
>> >   drivers/gpu/drm/i915/i915_reg.h     |  83 +++++
>> >   drivers/gpu/drm/i915/intel_atomic.c |  26 +-
>> >   drivers/gpu/drm/i915/intel_ddi.c    |  14 +
>> >   drivers/gpu/drm/i915/intel_drv.h    |  53 +++
>> >   drivers/gpu/drm/i915/intel_hdcp.c   | 636 ++++++++++++++++++++++++++++++++++++
>> >   6 files changed, 811 insertions(+), 2 deletions(-)
>> >   create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c
>> >
>> > diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
>> > index 6c3b0481ef82..1e745508e437 100644
>> > --- a/drivers/gpu/drm/i915/Makefile
>> > +++ b/drivers/gpu/drm/i915/Makefile
>> > @@ -87,6 +87,7 @@ i915-y += intel_audio.o \
>> >       intel_fbc.o \
>> >       intel_fifo_underrun.o \
>> >       intel_frontbuffer.o \
>> > +     intel_hdcp.o \
>> >       intel_hotplug.o \
>> >       intel_modes.o \
>> >       intel_overlay.o \
>> > diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
>> > index 68a58cce6ab1..43128030171d 100644
>> > --- a/drivers/gpu/drm/i915/i915_reg.h
>> > +++ b/drivers/gpu/drm/i915/i915_reg.h
>> > @@ -7991,6 +7991,7 @@ enum {
>> >   #define     GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT      8
>> >   #define     GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT      16
>> >   #define     GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT      24
>> > +#define   SKL_PCODE_LOAD_HDCP_KEYS         0x5
>> >   #define   SKL_PCODE_CDCLK_CONTROL         0x7
>> >   #define     SKL_CDCLK_PREPARE_FOR_CHANGE  0x3
>> >   #define     SKL_CDCLK_READY_FOR_CHANGE            0x1
>> > @@ -8285,6 +8286,88 @@ enum skl_power_gate {
>> >   #define  SKL_PW_TO_PG(pw)                 ((pw) - SKL_DISP_PW_1 + SKL_PG1)
>> >   #define  SKL_FUSE_PG_DIST_STATUS(pg)              (1 << (27 - (pg)))
>> > +
>> > +/* HDCP Key Registers */
>> > +#define SKL_HDCP_KEY_CONF          _MMIO(0x66c00)
>> > +#define     SKL_HDCP_AKSV_SEND_TRIGGER     BIT(31)
>> > +#define  SKL_HDCP_CLEAR_KEYS_TRIGGER       BIT(30)
>> > +#define SKL_HDCP_KEY_STATUS                _MMIO(0x66c04)
>> > +#define  SKL_HDCP_FUSE_IN_PROGRESS BIT(7)
>> > +#define  SKL_HDCP_FUSE_ERROR               BIT(6)
>> > +#define  SKL_HDCP_FUSE_DONE                BIT(5)
>> > +#define  SKL_HDCP_KEY_LOAD_STATUS  BIT(1)
>> > +#define  SKL_HDCP_KEY_LOAD_DONE            BIT(0)
>> > +#define SKL_HDCP_AKSV_LO           _MMIO(0x66c10)
>> > +#define SKL_HDCP_AKSV_HI           _MMIO(0x66c14)
>> > +
>> > +/* HDCP Repeater Registers */
>> > +#define SKL_HDCP_REP_CTL           _MMIO(0x66d00)
>> > +#define  SKL_HDCP_DDIB_REP_PRESENT BIT(30)
>> > +#define  SKL_HDCP_DDIA_REP_PRESENT BIT(29)
>> > +#define  SKL_HDCP_DDIC_REP_PRESENT BIT(28)
>> > +#define  SKL_HDCP_DDID_REP_PRESENT BIT(27)
>> > +#define  SKL_HDCP_DDIF_REP_PRESENT BIT(26)
>> > +#define  SKL_HDCP_DDIE_REP_PRESENT BIT(25)
>> > +#define  SKL_HDCP_DDIB_SHA1_M0             (1 << 20)
>> > +#define  SKL_HDCP_DDIA_SHA1_M0             (2 << 20)
>> > +#define  SKL_HDCP_DDIC_SHA1_M0             (3 << 20)
>> > +#define  SKL_HDCP_DDID_SHA1_M0             (4 << 20)
>> > +#define  SKL_HDCP_DDIF_SHA1_M0             (5 << 20)
>> > +#define  SKL_HDCP_DDIE_SHA1_M0             (6 << 20) // Bspec says 5?
>> > +#define  SKL_HDCP_SHA1_BUSY                BIT(16)
>> > +#define  SKL_HDCP_SHA1_READY               BIT(17)
>> > +#define  SKL_HDCP_SHA1_COMPLETE            BIT(18)
>> > +#define  SKL_HDCP_SHA1_V_MATCH             BIT(19)
>> > +#define  SKL_HDCP_SHA1_TEXT_32             (1 << 1)
>> > +#define  SKL_HDCP_SHA1_COMPLETE_HASH       (2 << 1)
>> > +#define  SKL_HDCP_SHA1_TEXT_24             (4 << 1)
>> > +#define  SKL_HDCP_SHA1_TEXT_16             (5 << 1)
>> > +#define  SKL_HDCP_SHA1_TEXT_8              (6 << 1)
>> > +#define  SKL_HDCP_SHA1_TEXT_0              (7 << 1)
>> > +#define SKL_HDCP_SHA_V_PRIME_H0            _MMIO(0x66d04)
>> > +#define SKL_HDCP_SHA_V_PRIME_H1            _MMIO(0x66d08)
>> > +#define SKL_HDCP_SHA_V_PRIME_H2            _MMIO(0x66d0C)
>> > +#define SKL_HDCP_SHA_V_PRIME_H3            _MMIO(0x66d10)
>> > +#define SKL_HDCP_SHA_V_PRIME_H4            _MMIO(0x66d14)
>> > +#define SKL_HDCP_SHA_V_PRIME(h)            _MMIO((0x66d04 + h * 4))
>> > +#define SKL_HDCP_SHA_TEXT          _MMIO(0x66d18)
>> > +
>> > +/* HDCP Auth Registers */
>> > +#define _SKL_PORTA_HDCP_AUTHENC            0x66800
>> > +#define _SKL_PORTB_HDCP_AUTHENC            0x66500
>> > +#define _SKL_PORTC_HDCP_AUTHENC            0x66600
>> > +#define _SKL_PORTD_HDCP_AUTHENC            0x66700
>> > +#define _SKL_PORTE_HDCP_AUTHENC            0x66A00
>> > +#define _SKL_PORTF_HDCP_AUTHENC            0x66900
>> > +#define _SKL_PORT_HDCP_AUTHENC(port, x)    _MMIO(_PICK(port, \
>> > +                                     _SKL_PORTA_HDCP_AUTHENC, \
>> > +                                     _SKL_PORTB_HDCP_AUTHENC, \
>> > +                                     _SKL_PORTC_HDCP_AUTHENC, \
>> > +                                     _SKL_PORTD_HDCP_AUTHENC, \
>> > +                                     _SKL_PORTE_HDCP_AUTHENC, \
>> > +                                     _SKL_PORTF_HDCP_AUTHENC) + x)
>> > +#define SKL_PORT_HDCP_CONF(port)   _SKL_PORT_HDCP_AUTHENC(port, 0x0)
>> > +#define  SKL_HDCP_CONF_CAPTURE_AN  BIT(0)
>> > +#define  SKL_HDCP_CONF_AUTH_AND_ENC        (BIT(1) | BIT(0))
>> > +#define SKL_PORT_HDCP_ANINIT(port) _SKL_PORT_HDCP_AUTHENC(port, 0x4)
>> > +#define SKL_PORT_HDCP_ANLO(port)   _SKL_PORT_HDCP_AUTHENC(port, 0x8)
>> > +#define SKL_PORT_HDCP_ANHI(port)   _SKL_PORT_HDCP_AUTHENC(port, 0xC)
>> > +#define SKL_PORT_HDCP_BKSVLO(port) _SKL_PORT_HDCP_AUTHENC(port, 0x10)
>> > +#define SKL_PORT_HDCP_BKSVHI(port) _SKL_PORT_HDCP_AUTHENC(port, 0x14)
>> > +#define SKL_PORT_HDCP_RPRIME(port) _SKL_PORT_HDCP_AUTHENC(port, 0x18)
>> > +#define SKL_PORT_HDCP_STATUS(port) _SKL_PORT_HDCP_AUTHENC(port, 0x1C)
>> > +#define  SKL_HDCP_STATUS_STREAM_A_ENC      BIT(31)
>> > +#define  SKL_HDCP_STATUS_STREAM_B_ENC      BIT(30)
>> > +#define  SKL_HDCP_STATUS_STREAM_C_ENC      BIT(29)
>> > +#define  SKL_HDCP_STATUS_STREAM_D_ENC      BIT(28)
>> > +#define  SKL_HDCP_STATUS_AUTH              BIT(21)
>> > +#define  SKL_HDCP_STATUS_ENC               BIT(20)
>> > +#define  SKL_HDCP_STATUS_RI_MATCH  BIT(19)
>> > +#define  SKL_HDCP_STATUS_R0_READY  BIT(18)
>> > +#define  SKL_HDCP_STATUS_AN_READY  BIT(17)
>> > +#define  SKL_HDCP_STATUS_CIPHER            BIT(16)
>> > +#define  SKL_HDCP_STATUS_FRAME_CNT(x)      ((x >> 8) & 0xff)
>> > +
>> >   /* Per-pipe DDI Function Control */
>> >   #define _TRANS_DDI_FUNC_CTL_A             0x60400
>> >   #define _TRANS_DDI_FUNC_CTL_B             0x61400
>> > diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
>> > index 36d4e635e4ce..ddf08227d9cb 100644
>> > --- a/drivers/gpu/drm/i915/intel_atomic.c
>> > +++ b/drivers/gpu/drm/i915/intel_atomic.c
>> > @@ -109,12 +109,34 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
>> >     struct intel_digital_connector_state *old_conn_state =
>> >             to_intel_digital_connector_state(old_state);
>> >     struct drm_crtc_state *crtc_state;
>> > -
>> > -   if (!new_state->crtc)
>> > +   uint64_t old_cp = old_conn_state->base.content_protection;
>> > +   uint64_t new_cp = new_state->content_protection;
>> > +
>> > +   if (!new_state->crtc) {
>> > +           /*
>> > +            * If the connector is being disabled with CP enabled, mark it
>> > +            * desired so it's re-enabled when the connector is brought back
>> > +            */
>> > +           if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
>> > +                   new_state->content_protection =
>> > +                           DRM_MODE_CONTENT_PROTECTION_DESIRED;
>> >             return 0;
>> > +   }
>> >     crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc);
>> > +   if (new_cp != old_cp) {
>> > +           /* Only drivers can set content protection enabled */
>> > +           if (new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
>> > +                   new_state->content_protection =
>> > +                           DRM_MODE_CONTENT_PROTECTION_DESIRED;
>> > +
>> > +           /* Involve the encoder/connector to enable/disable CP */
>> > +           if (new_cp == DRM_MODE_CONTENT_PROTECTION_OFF ||
>> > +               old_cp == DRM_MODE_CONTENT_PROTECTION_OFF)
>> > +                   crtc_state->mode_changed = true;
>>
>> We need not perform the mode set for hdcp enable/disable.
>> Authentication and encryption can be started on active port.
>
> Was simpler to implement this way :-) We can fix this by pushing the hdcp
> enable/disable code into a post-modeset operation. Ville had written the
> infrastructure for that to fix a few fastboot corner cases. But that
> infrastructure hasn't landed yet, so probably better to do that in a
> follow-up.
>
> I also guess that CrOS simply set this to desired every time they enable
> an external screen, so it won't result in an unecessary modeset.
>

Actually, we enable only as needed, if there is no HD licensed content
on the screen, the link is unencrypted. The UX of doing it through
enable/disable is meh, but things are a lot easier to reason about.

>> > +   }
>> > +
>> >     /*
>> >      * These properties are handled by fastset, and might not end
>> >      * up in a modeset.
>> > diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
>> > index 933c18fd4258..0e69337f410d 100644
>> > --- a/drivers/gpu/drm/i915/intel_ddi.c
>> > +++ b/drivers/gpu/drm/i915/intel_ddi.c
>> > @@ -2432,10 +2432,17 @@ static void intel_enable_ddi(struct intel_encoder *encoder,
>> >                          const struct intel_crtc_state *crtc_state,
>> >                          const struct drm_connector_state *conn_state)
>> >   {
>> > +   struct drm_connector *connector = conn_state->connector;
>> > +   struct intel_connector *intel_connector = to_intel_connector(connector);
>> > +
>> >     if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
>> >             intel_enable_ddi_hdmi(encoder, crtc_state, conn_state);
>> >     else
>> >             intel_enable_ddi_dp(encoder, crtc_state, conn_state);
>> > +
>> > +   if (conn_state->content_protection ==
>> > +                   DRM_MODE_CONTENT_PROTECTION_DESIRED)
>> > +           intel_hdcp_enable(intel_connector);
>> >   }
>> >   static void intel_disable_ddi_dp(struct intel_encoder *encoder,
>> > @@ -2468,10 +2475,17 @@ static void intel_disable_ddi(struct intel_encoder *encoder,
>> >                           const struct intel_crtc_state *old_crtc_state,
>> >                           const struct drm_connector_state *old_conn_state)
>> >   {
>> > +   struct drm_connector *connector = old_conn_state->connector;
>> > +   struct intel_connector *intel_connector = to_intel_connector(connector);
>> > +
>> >     if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
>> >             intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state);
>> >     else
>> >             intel_disable_ddi_dp(encoder, old_crtc_state, old_conn_state);
>> > +
>> > +   if (old_conn_state->content_protection !=
>> > +                   DRM_MODE_CONTENT_PROTECTION_OFF)
>> > +           intel_hdcp_disable(intel_connector);
>> We might want to disable the hdcp before disabling the DDI. Actually we
>> could trigger hdcp disable at connector state change(due to hot-unplug)
>> also.
>
> Yeah this part needs to be reworked a bit, also because the locking
> doesn't work yet. I think hot-unplug is handled by the worker thread
> already, it does the mandatory regular polling.

Yeah, this should go above the disable. intel_hdcp_disable() only does
transmitter-side operations, but it's certainly better form to
wind/unwind in enable/disable.

Sean

> -Daniel
>
>>
>> Thanks,
>> --Ram
>> >   }
>> >   static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder,
>> > diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
>> > index 47d022d48718..8924004575b8 100644
>> > --- a/drivers/gpu/drm/i915/intel_drv.h
>> > +++ b/drivers/gpu/drm/i915/intel_drv.h
>> > @@ -299,6 +299,49 @@ struct intel_panel {
>> >     } backlight;
>> >   };
>> > +struct intel_hdcp_shim {
>> > +   /* Outputs the transmitter's An and Aksv values to the receiver. */
>> > +   int (*write_an_aksv)(struct intel_digital_port *intel_dig_port, u8 *an);
>> > +
>> > +   /* Reads the receiver's key selection vector */
>> > +   int (*read_bksv)(struct intel_digital_port *intel_dig_port, u8 *bksv);
>> > +
>> > +   /*
>> > +    * Reads BINFO from DP receivers and BSTATUS from HDMI receivers. The
>> > +    * definitions are the same in the respective specs, but the names are
>> > +    * different. Call it BSTATUS since that's the name the HDMI spec
>> > +    * uses and it was there first.
>> > +    */
>> > +   int (*read_bstatus)(struct intel_digital_port *intel_dig_port,
>> > +                       u8 *bstatus);
>> > +
>> > +   /* Determines whether a repeater is present downstream */
>> > +   int (*repeater_present)(struct intel_digital_port *intel_dig_port,
>> > +                           bool *repeater_present);
>> > +
>> > +   /* Reads the receiver's Ri' value */
>> > +   int (*read_ri_prime)(struct intel_digital_port *intel_dig_port, u8 *ri);
>> > +
>> > +   /* Determines if the receiver's KSV FIFO is ready for consumption */
>> > +   int (*read_ksv_ready)(struct intel_digital_port *intel_dig_port,
>> > +                         bool *ksv_ready);
>> > +
>> > +   /* Reads the ksv fifo for num_downstream devices */
>> > +   int (*read_ksv_fifo)(struct intel_digital_port *intel_dig_port,
>> > +                        int num_downstream, u8 *ksv_fifo);
>> > +
>> > +   /* Reads a 32-bit part of V' from the receiver */
>> > +   int (*read_v_prime_part)(struct intel_digital_port *intel_dig_port,
>> > +                            int i, u32 *part);
>> > +
>> > +   /* Enables HDCP signalling on the port */
>> > +   int (*toggle_signalling)(struct intel_digital_port *intel_dig_port,
>> > +                            bool enable);
>> > +
>> > +   /* Ensures the link is still protected */
>> > +   bool (*check_link)(struct intel_digital_port *intel_dig_port);
>> > +};
>> > +
>> >   struct intel_connector {
>> >     struct drm_connector base;
>> >     /*
>> > @@ -330,6 +373,9 @@ struct intel_connector {
>> >     /* Work struct to schedule a uevent on link train failure */
>> >     struct work_struct modeset_retry_work;
>> > +
>> > +   const struct intel_hdcp_shim *hdcp_shim;
>> > +   struct delayed_work hdcp_work;
>> >   };
>> >   struct intel_digital_connector_state {
>> > @@ -1295,6 +1341,8 @@ void intel_ddi_set_vc_payload_alloc(const struct intel_crtc_state *crtc_state,
>> >                                 bool state);
>> >   u32 bxt_signal_levels(struct intel_dp *intel_dp);
>> >   uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
>> > +int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder);
>> > +int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder);
>> >   u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
>> >   unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
>> > @@ -1746,6 +1794,11 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
>> >   }
>> >   #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
>> > +/* intel_hdcp.c */
>> > +int intel_hdcp_enable(struct intel_connector *connector);
>> > +int intel_hdcp_disable(struct intel_connector *connector);
>> > +int intel_hdcp_check_link(struct intel_connector *connector);
>> > +void intel_hdcp_work(struct work_struct *work);
>> >   /* intel_psr.c */
>> >   void intel_psr_enable(struct intel_dp *intel_dp,
>> > diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
>> > new file mode 100644
>> > index 000000000000..a2a575ed657e
>> > --- /dev/null
>> > +++ b/drivers/gpu/drm/i915/intel_hdcp.c
>> > @@ -0,0 +1,636 @@
>> > +/*
>> > + * Copyright (C) 2017 Google, Inc.
>> > + *
>> > + * This software is licensed under the terms of the GNU General Public
>> > + * License version 2, as published by the Free Software Foundation, and
>> > + * may be copied, distributed, and modified under those terms.
>> > + *
>> > + * This program is distributed in the hope that it will be useful,
>> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> > + * GNU General Public License for more details.
>> > + */
>> > +
>> > +#include <drm/drmP.h>
>> > +#include <drm/drm_hdcp.h>
>> > +#include <linux/i2c.h>
>> > +#include <linux/random.h>
>> > +
>> > +#include "intel_drv.h"
>> > +#include "i915_reg.h"
>> > +
>> > +#define KEY_LOAD_TRIES     5
>> > +
>> > +static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
>> > +                               const struct intel_hdcp_shim *shim)
>> > +{
>> > +   unsigned long timeout = jiffies + msecs_to_jiffies_timeout(500);
>> > +   int ret;
>> > +   bool ksv_ready;
>> > +
>> > +   while (true) {
>> > +           ret = shim->read_ksv_ready(intel_dig_port, &ksv_ready);
>> > +           if (ret)
>> > +                   return ret;
>> > +           if (ksv_ready)
>> > +                   break;
>> > +           if (time_after(jiffies, timeout))
>> > +                   return -ETIMEDOUT;
>> > +           msleep(100);
>> > +   }
>> > +   return 0;
>> > +}
>> > +
>> > +static void intel_hdcp_clear_keys(struct drm_i915_private *dev_priv)
>> > +{
>> > +   I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_CLEAR_KEYS_TRIGGER);
>> > +   I915_WRITE(SKL_HDCP_KEY_STATUS,
>> > +              SKL_HDCP_KEY_LOAD_DONE | SKL_HDCP_KEY_LOAD_STATUS |
>> > +              SKL_HDCP_FUSE_IN_PROGRESS | SKL_HDCP_FUSE_ERROR |
>> > +              SKL_HDCP_FUSE_DONE);
>> > +}
>> > +
>> > +static int intel_hdcp_load_keys(struct drm_i915_private *dev_priv)
>> > +{
>> > +   unsigned long timeout;
>> > +   int ret;
>> > +   u32 val;
>> > +
>> > +   // Initiate loading the HDCP key from fuses
>> > +   mutex_lock(&dev_priv->pcu_lock);
>> > +   ret = sandybridge_pcode_write(dev_priv, SKL_PCODE_LOAD_HDCP_KEYS, 1);
>> > +   mutex_unlock(&dev_priv->pcu_lock);
>> > +   if (ret) {
>> > +           DRM_ERROR("Failed to initiate HDCP key load (%d)\n", ret);
>> > +           return ret;
>> > +   }
>> > +
>> > +   // Wait for the keys to load (500us)
>> > +   timeout = jiffies + nsecs_to_jiffies_timeout(500 * 1000);
>> > +   while (true) {
>> > +           val = I915_READ(SKL_HDCP_KEY_STATUS);
>> > +           if (val & SKL_HDCP_KEY_LOAD_DONE)
>> > +                   break;
>> > +           if (time_after(jiffies, timeout))
>> > +                   return -ETIMEDOUT;
>> > +           usleep_range(50, 100);
>> > +   }
>> > +   if (!(val & SKL_HDCP_KEY_LOAD_STATUS))
>> > +           return -ENXIO;
>> > +
>> > +   // Send Aksv over to PCH display for use in authentication
>> > +   I915_WRITE(SKL_HDCP_KEY_CONF, SKL_HDCP_AKSV_SEND_TRIGGER);
>> > +
>> > +   return 0;
>> > +}
>> > +
>> > +/* Returns updated SHA-1 index */
>> > +static int intel_write_sha_text(struct drm_i915_private *dev_priv, u32 sha_text)
>> > +{
>> > +   I915_WRITE(SKL_HDCP_SHA_TEXT, sha_text);
>> > +   if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_READY, 1)) {
>> > +           DRM_ERROR("Timed out waiting for SHA1 ready\n");
>> > +           return -ETIMEDOUT;
>> > +   }
>> > +   return 0;
>> > +}
>> > +
>> > +static
>> > +u32 intel_hdcp_get_repeater_ctl(struct intel_digital_port *intel_dig_port)
>> > +{
>> > +   enum port port = intel_dig_port->port;
>> > +   switch(port) {
>> > +   case PORT_A:
>> > +           return SKL_HDCP_DDIA_REP_PRESENT | SKL_HDCP_DDIA_SHA1_M0;
>> > +   case PORT_B:
>> > +           return SKL_HDCP_DDIB_REP_PRESENT | SKL_HDCP_DDIB_SHA1_M0;
>> > +   case PORT_C:
>> > +           return SKL_HDCP_DDIC_REP_PRESENT | SKL_HDCP_DDIC_SHA1_M0;
>> > +   case PORT_D:
>> > +           return SKL_HDCP_DDID_REP_PRESENT | SKL_HDCP_DDID_SHA1_M0;
>> > +   case PORT_E:
>> > +           return SKL_HDCP_DDIE_REP_PRESENT | SKL_HDCP_DDIE_SHA1_M0;
>> > +   default:
>> > +           break;
>> > +   }
>> > +   DRM_ERROR("Unknown port %d\n", port);
>> > +   return -EINVAL;
>> > +}
>> > +
>> > +/* Implements Part 2 of the HDCP authorization procedure */
>> > +static
>> > +int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
>> > +                          const struct intel_hdcp_shim *shim)
>> > +{
>> > +   struct drm_i915_private *dev_priv;
>> > +   u32 vprime, sha_text, sha_leftovers, rep_ctl;
>> > +   u8 bstatus[2], num_downstream, *ksv_fifo;
>> > +   int ret, i, j, sha_idx;
>> > +
>> > +   dev_priv = intel_dig_port->base.base.dev->dev_private;
>> > +
>> > +   ret = shim->read_bstatus(intel_dig_port, bstatus);
>> > +   if (ret)
>> > +           return ret;
>> > +
>> > +   /* If there are no downstream devices, we're all done. */
>> > +   num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
>> > +   if (num_downstream == 0) {
>> > +           DRM_INFO("HDCP is enabled (no downstream devices)\n");
>> > +           return 0;
>> > +   }
>> > +
>> > +   // Poll for ksv list ready (spec says max time allowed is 5s)
>> > +   ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim);
>> > +   if (ret) {
>> > +           DRM_ERROR("KSV list failed to become ready (%d)\n", ret);
>> > +           return ret;
>> > +   }
>> > +
>> > +   ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL);
>> > +   if (!ksv_fifo)
>> > +           return -ENOMEM;
>> > +
>> > +   ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo);
>> > +   if (ret)
>> > +           return ret;
>> > +
>> > +   // Process V' values from the receiver
>> > +   for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
>> > +           ret = shim->read_v_prime_part(intel_dig_port, i, &vprime);
>> > +           if (ret)
>> > +                   return ret;
>> > +           I915_WRITE(SKL_HDCP_SHA_V_PRIME(i), vprime);
>> > +   }
>> > +
>> > +   /*
>> > +    * We need to write the concatenation of all device KSVs, BINFO (DP) ||
>> > +    * BSTATUS (HDMI), and M0 (which is added via HDCP_REP_CTL). This byte
>> > +    * stream is written via the HDCP_SHA_TEXT register in 32-bit
>> > +    * increments. Every 64 bytes, we need to write HDCP_REP_CTL again. This
>> > +    * index will keep track of our progress through the 64 bytes as well as
>> > +    * helping us work the 40-bit KSVs through our 32-bit register.
>> > +    *
>> > +    * NOTE: data passed via HDCP_SHA_TEXT should be big-endian
>> > +    */
>> > +   sha_idx = 0;
>> > +   sha_text = 0;
>> > +   sha_leftovers = 0;
>> > +   rep_ctl = intel_hdcp_get_repeater_ctl(intel_dig_port);
>> > +   I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>> > +   for (i = 0; i < num_downstream; i++) {
>> > +           unsigned sha_empty;
>> > +           u8 *ksv = &ksv_fifo[i * DRM_HDCP_KSV_LEN];
>> > +
>> > +           // Fill up the empty slots in sha_text and write it out
>> > +           sha_empty = sizeof(sha_text) - sha_leftovers;
>> > +           for (j = 0; j < sha_empty; j++)
>> > +                   sha_text |= ksv[j] << ((sizeof(sha_text) - j - 1) * 8);
>> > +
>> > +           ret = intel_write_sha_text(dev_priv, sha_text);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +
>> > +           // Programming guide writes this every 64 bytes
>> > +           sha_idx += sizeof(sha_text);
>> > +           if (!(sha_idx % 64))
>> > +                   I915_WRITE(SKL_HDCP_REP_CTL,
>> > +                              rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>> > +
>> > +           // Store the leftover bytes from the ksv in sha_text
>> > +           sha_leftovers = DRM_HDCP_KSV_LEN - sha_empty;
>> > +           sha_text = 0;
>> > +           for (j = 0; j < sha_leftovers; j++)
>> > +                   sha_text |= ksv[sha_empty + j] <<
>> > +                                   ((sizeof(sha_text) - j - 1) * 8);
>> > +
>> > +           /*
>> > +            * If we still have room in sha_text for more data, continue.
>> > +            * Otherwise, write it out immediately.
>> > +            */
>> > +           if (sizeof(sha_text) > sha_leftovers)
>> > +                   continue;
>> > +
>> > +           ret = intel_write_sha_text(dev_priv, sha_text);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_leftovers = 0;
>> > +           sha_text = 0;
>> > +           sha_idx += sizeof(sha_text);
>> > +   }
>> > +
>> > +   /*
>> > +    * We need to write BINFO/BSTATUS, and M0 now. Depending on how many
>> > +    * bytes are leftover from the last ksv, we might be able to fit them
>> > +    * all in sha_text (first 2 cases), or we might need to split them up
>> > +    * into 2 writes (last 2 cases).
>> > +    */
>> > +   if (sha_leftovers == 0) {
>> > +           // Write 16 bits of text, 16 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
>> > +           ret = intel_write_sha_text(dev_priv,
>> > +                                      bstatus[0] << 8 | bstatus[1]);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +           // Write 32 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>> > +           ret = intel_write_sha_text(dev_priv, 0);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +           // Write 16 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_16);
>> > +           ret = intel_write_sha_text(dev_priv, 0);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +   } else if (sha_leftovers == 1) {
>> > +           // Write 24 bits of text, 8 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
>> > +           sha_text |= bstatus[0] << 16 | bstatus[1] << 8;
>> > +           // Only 24-bits of data, must be in the LSB
>> > +           sha_text = (sha_text & 0xffffff00) >> 8;
>> > +           ret = intel_write_sha_text(dev_priv, sha_text);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +           // Write 32 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>> > +           ret = intel_write_sha_text(dev_priv, 0);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +           // Write 24 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
>> > +           ret = intel_write_sha_text(dev_priv, 0);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +   } else if (sha_leftovers == 2) {
>> > +           // Write 32 bits of text
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>> > +           sha_text |= bstatus[0] << 24 | bstatus[1] << 16;
>> > +           ret = intel_write_sha_text(dev_priv, sha_text);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +           // Write 64 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>> > +           for (i = 0; i < 2; i++) {
>> > +                   ret = intel_write_sha_text(dev_priv, 0);
>> > +                   if (ret < 0)
>> > +                           return ret;
>> > +                   sha_idx += sizeof(sha_text);
>> > +           }
>> > +   } else if (sha_leftovers == 3) {
>> > +           // Write 32 bits of text
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>> > +           sha_text |= bstatus[0] << 24;
>> > +           ret = intel_write_sha_text(dev_priv, sha_text);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +           // Write 8 bits of text, 24 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_8);
>> > +           ret = intel_write_sha_text(dev_priv, bstatus[1]);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +           // Write 32 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_0);
>> > +           ret = intel_write_sha_text(dev_priv, 0);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +
>> > +           // Write 8 bits of M0
>> > +           I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_24);
>> > +           ret = intel_write_sha_text(dev_priv, 0);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +   } else {
>> > +           DRM_ERROR("Invalid number of leftovers %d\n", sha_leftovers);
>> > +           return -EINVAL;
>> > +   }
>> > +
>> > +   I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_TEXT_32);
>> > +   // Fill up to 64 - 4 bytes with zeros (leave the last write for length)
>> > +   while ((sha_idx % 64) < (64 - sizeof(sha_text))) {
>> > +           ret = intel_write_sha_text(dev_priv, 0);
>> > +           if (ret < 0)
>> > +                   return ret;
>> > +           sha_idx += sizeof(sha_text);
>> > +   }
>> > +
>> > +   /*
>> > +    * Last write gets the length of the concatenation in bits. That is:
>> > +    *  - 5 bytes per device
>> > +    *  - 10 bytes for BINFO/BSTATUS(2), M0(8)
>> > +    */
>> > +   sha_text = (num_downstream * 5 + 10) * 8;
>> > +   ret = intel_write_sha_text(dev_priv, sha_text);
>> > +   if (ret < 0)
>> > +           return ret;
>> > +
>> > +   // Finally, tell the HW we're done with the hash and wait for it to ACK
>> > +   I915_WRITE(SKL_HDCP_REP_CTL, rep_ctl | SKL_HDCP_SHA1_COMPLETE_HASH);
>> > +   if (wait_for(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_COMPLETE, 1)) {
>> > +           DRM_ERROR("Timed out waiting for SHA1 complete\n");
>> > +           return -ETIMEDOUT;
>> > +   }
>> > +   if (!(I915_READ(SKL_HDCP_REP_CTL) & SKL_HDCP_SHA1_V_MATCH)) {
>> > +           DRM_ERROR("SHA-1 mismatch, HDCP failed\n");
>> > +           return -ENXIO;
>> > +   }
>> > +
>> > +   DRM_INFO("HDCP is enabled (%d downstream devices)\n", num_downstream);
>> > +   return 0;
>> > +}
>> > +
>> > +/* Implements Part 1 of the HDCP authorization procedure */
>> > +static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
>> > +                      const struct intel_hdcp_shim *shim)
>> > +{
>> > +   struct drm_i915_private *dev_priv;
>> > +   enum port port;
>> > +   unsigned long r0_prime_gen_start;
>> > +   int ret, i;
>> > +   union {
>> > +           u32 reg[2];
>> > +           u8 shim[DRM_HDCP_AN_LEN];
>> > +   } an;
>> > +   union {
>> > +           u32 reg[2];
>> > +           u8 shim[DRM_HDCP_KSV_LEN];
>> > +   } bksv;
>> > +   union {
>> > +           u32 reg;
>> > +           u8 shim[DRM_HDCP_RI_LEN];
>> > +   } ri;
>> > +   bool repeater_present;
>> > +
>> > +   dev_priv = intel_dig_port->base.base.dev->dev_private;
>> > +
>> > +   port = intel_dig_port->port;
>> > +
>> > +   // Initialize An with 2 random values and acquire it
>> > +   for (i = 0; i < 2; i++)
>> > +           I915_WRITE(SKL_PORT_HDCP_ANINIT(port), get_random_long());
>> > +   I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_CAPTURE_AN);
>> > +
>> > +   // Wait for An to be acquired
>> > +   if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>> > +                SKL_HDCP_STATUS_AN_READY, 1)) {
>> > +           DRM_ERROR("Timed out waiting for An\n");
>> > +           return -ETIMEDOUT;
>> > +   }
>> > +
>> > +   an.reg[0] = I915_READ(SKL_PORT_HDCP_ANLO(port));
>> > +   an.reg[1] = I915_READ(SKL_PORT_HDCP_ANHI(port));
>> > +   ret = shim->write_an_aksv(intel_dig_port, an.shim);
>> > +   if (ret)
>> > +           return ret;
>> > +
>> > +   r0_prime_gen_start = jiffies;
>> > +
>> > +   memset(&bksv, 0, sizeof(bksv));
>> > +   ret = shim->read_bksv(intel_dig_port, bksv.shim);
>> > +   if (ret)
>> > +           return ret;
>> > +
>> > +   I915_WRITE(SKL_PORT_HDCP_BKSVLO(port), bksv.reg[0]);
>> > +   I915_WRITE(SKL_PORT_HDCP_BKSVHI(port), bksv.reg[1]);
>> > +
>> > +   ret = shim->repeater_present(intel_dig_port, &repeater_present);
>> > +   if (ret)
>> > +           return ret;
>> > +   if (repeater_present)
>> > +           I915_WRITE(SKL_HDCP_REP_CTL,
>> > +                      intel_hdcp_get_repeater_ctl(intel_dig_port));
>> > +
>> > +   ret = shim->toggle_signalling(intel_dig_port, true);
>> > +   if (ret)
>> > +           return ret;
>> > +
>> > +   I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_AUTH_AND_ENC);
>> > +
>> > +   // Wait for R0 ready
>> > +   if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>> > +                (SKL_HDCP_STATUS_R0_READY | SKL_HDCP_STATUS_ENC), 1)) {
>> > +           DRM_ERROR("Timed out waiting for R0 ready\n");
>> > +           return -ETIMEDOUT;
>> > +   }
>> > +
>> > +   /*
>> > +    * Wait for R0' to become available, the spec says 100ms from Aksv
>> > +    * write. On DP, there's an R0_READY bit available but no such bit
>> > +    * exists on HDMI. Since the upper-bound is the same, we'll just do
>> > +    * the stupid thing instead of polling on one and not the other.
>> > +    */
>> > +   wait_remaining_ms_from_jiffies(r0_prime_gen_start, 100);
>> > +
>> > +   ri.reg = 0;
>> > +   ret = shim->read_ri_prime(intel_dig_port, ri.shim);
>> > +   if (ret)
>> > +           return ret;
>> > +   I915_WRITE(SKL_PORT_HDCP_RPRIME(port), ri.reg);
>> > +
>> > +   // Wait for Ri prime match
>> > +   if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>> > +                (SKL_HDCP_STATUS_RI_MATCH | SKL_HDCP_STATUS_ENC), 1)) {
>> > +           DRM_ERROR("Timed out waiting for Ri prime match (%x)\n",
>> > +                     I915_READ(SKL_PORT_HDCP_STATUS(port)));
>> > +           return -ETIMEDOUT;
>> > +   }
>> > +
>> > +   // Wait for encryption confirmation
>> > +   if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) &
>> > +                 SKL_HDCP_STATUS_ENC, 20)) {
>> > +           DRM_ERROR("Timed out waiting for encryption\n");
>> > +           return -ETIMEDOUT;
>> > +   }
>> > +
>> > +   /*
>> > +    * XXX: If we have MST-connected devices, we need to enable encryption
>> > +    * on those as well.
>> > +    */
>> > +
>> > +   return intel_hdcp_auth_downstream(intel_dig_port, shim);
>> > +}
>> > +
>> > +static
>> > +struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector)
>> > +{
>> > +   return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base);
>> > +}
>> > +
>> > +static int _intel_hdcp_disable(struct intel_connector *connector)
>> > +{
>> > +   struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>> > +   struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
>> > +   enum port port = intel_dig_port->port;
>> > +   int ret;
>> > +
>> > +   I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
>> > +   if (wait_for(I915_READ(SKL_PORT_HDCP_STATUS(port)) == 0, 20)) {
>> > +           DRM_ERROR("Failed to disable HDCP, timeout clearing status\n");
>> > +           return -ETIMEDOUT;
>> > +   }
>> > +
>> > +   intel_hdcp_clear_keys(dev_priv);
>> > +
>> > +   ret = connector->hdcp_shim->toggle_signalling(intel_dig_port, false);
>> > +   if (ret) {
>> > +           DRM_ERROR("Failed to disable HDCP signalling\n");
>> > +           return ret;
>> > +   }
>> > +
>> > +   DRM_INFO("HDCP is disabled\n");
>> > +   return 0;
>> > +}
>> > +
>> > +static int _intel_hdcp_enable(struct intel_connector *connector)
>> > +{
>> > +   struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>> > +   int i, ret;
>> > +
>> > +   if (!(I915_READ(SKL_FUSE_STATUS) & SKL_FUSE_PG_DIST_STATUS(1))) {
>> > +           DRM_ERROR("PG1 is disabled, cannot load keys\n");
>> > +           return -ENXIO;
>> > +   }
>> > +
>> > +   for (i = 0; i < KEY_LOAD_TRIES; i++) {
>> > +           ret = intel_hdcp_load_keys(dev_priv);
>> > +           if (!ret)
>> > +                   break;
>> > +           intel_hdcp_clear_keys(dev_priv);
>> > +   }
>> > +   if (ret) {
>> > +           DRM_ERROR("Could not load HDCP keys, (%d)\n", ret);
>> > +           return ret;
>> > +   }
>> > +
>> > +   ret = intel_hdcp_auth(conn_to_dig_port(connector),
>> > +                         connector->hdcp_shim);
>> > +   if (ret) {
>> > +           DRM_ERROR("Failed to authenticate HDCP (%d)\n", ret);
>> > +           return ret;
>> > +   }
>> > +
>> > +   return 0;
>> > +}
>> > +
>> > +void intel_hdcp_work(struct work_struct *work)
>> > +{
>> > +   struct intel_connector *connector = container_of(to_delayed_work(work),
>> > +                                                    struct intel_connector,
>> > +                                                    hdcp_work);
>> > +   struct drm_device *dev = connector->base.dev;
>> > +   int ret;
>> > +
>> > +   drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>> > +
>> > +   ret = intel_hdcp_check_link(connector);
>> > +   if (!ret)
>> > +           schedule_delayed_work(&connector->hdcp_work,
>> > +                                 DRM_HDCP_CHECK_PERIOD_MS);
>> > +
>> > +   drm_modeset_unlock(&dev->mode_config.connection_mutex);
>> > +}
>> > +
>> > +int intel_hdcp_enable(struct intel_connector *connector)
>> > +{
>> > +   struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>> > +   struct drm_device *dev = &dev_priv->drm;
>> > +   struct drm_connector_state *state = connector->base.state;
>> > +   int ret;
>> > +
>> > +   WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>> > +
>> > +   if (!connector->hdcp_shim)
>> > +           return -ENOENT;
>> > +
>> > +   ret = _intel_hdcp_enable(connector);
>> > +   if (ret)
>> > +           return ret;
>> > +
>> > +   state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
>> > +
>> > +   schedule_delayed_work(&connector->hdcp_work, DRM_HDCP_CHECK_PERIOD_MS);
>> > +   return 0;
>> > +}
>> > +
>> > +int intel_hdcp_disable(struct intel_connector *connector)
>> > +{
>> > +   struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>> > +   struct drm_device *dev = &dev_priv->drm;
>> > +
>> > +   WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>> > +
>> > +   if (!connector->hdcp_shim)
>> > +           return -ENOENT;
>> > +
>> > +   cancel_delayed_work(&connector->hdcp_work);
>> > +
>> > +   return _intel_hdcp_disable(connector);
>> > +}
>> > +
>> > +/* Implements Part 3 of the HDCP authorization procedure */
>> > +int intel_hdcp_check_link(struct intel_connector *connector)
>> > +{
>> > +   struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
>> > +   struct drm_device *dev = &dev_priv->drm;
>> > +   struct drm_connector_state *state = connector->base.state;
>> > +   struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
>> > +   enum port port = intel_dig_port->port;
>> > +   int ret;
>> > +
>> > +   WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>> > +
>> > +   if (state->content_protection != DRM_MODE_CONTENT_PROTECTION_ENABLED)
>> > +           return 0;
>> > +
>> > +   if (!connector->hdcp_shim)
>> > +           return -ENOENT;
>> > +
>> > +   if (!(I915_READ(SKL_PORT_HDCP_STATUS(port)) & SKL_HDCP_STATUS_ENC)) {
>> > +           DRM_ERROR("HDCP check failed: link is not encrypted, %x\n",
>> > +                      I915_READ(SKL_PORT_HDCP_STATUS(port)));
>> > +           ret = -ENXIO;
>> > +           goto fail;
>> > +   }
>> > +
>> > +   if (connector->hdcp_shim->check_link(intel_dig_port))
>> > +           return 0;
>> > +
>> > +   DRM_INFO("HDCP link failed, retrying authentication\n");
>> > +
>> > +   ret = _intel_hdcp_disable(connector);
>> > +   if (ret) {
>> > +           DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
>> > +           goto fail;
>> > +   }
>> > +
>> > +   ret = _intel_hdcp_enable(connector);
>> > +   if (ret) {
>> > +           DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
>> > +           goto fail;
>> > +   }
>> > +
>> > +   return 0;
>> > +
>> > +fail:
>> > +   state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
>> > +   return ret;
>> > +}
>>
>> _______________________________________________
>> Intel-gfx mailing list
>> Intel-gfx@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
  2017-11-30  3:08   ` Sean Paul
  (?)
@ 2017-12-05 10:28     ` Pavel Machek
  -1 siblings, 0 replies; 62+ messages in thread
From: Pavel Machek @ 2017-12-05 10:28 UTC (permalink / raw)
  To: Sean Paul
  Cc: dri-devel, intel-gfx, Daniel Vetter, Jani Nikula,
	Gustavo Padovan, David Airlie, linux-kernel, linux-arm-kernel,
	linux-mediatek

[-- Attachment #1: Type: text/plain, Size: 1155 bytes --]

On Wed 2017-11-29 22:08:56, Sean Paul wrote:
> This patch adds a new optional connector property to allow userspace to enable
> protection over the content it is displaying. This will typically be implemented
> by the driver using HDCP.
> 
> The property is a tri-state with the following values:
> - OFF: Self explanatory, no content protection
> - DESIRED: Userspace requests that the driver enable protection
> - ENABLED: Once the driver has authenticated the link, it sets this value
> 
> The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
> unprotected. The driver should also maintain the desiredness of protection
> across hotplug/dpms/suspend.

Why would user of the machine want this to be something else than
'OFF'?

If kernel implements this, will it mean hardware vendors will have to
prevent user from updating kernel on machines they own?

If this is merged, does it open kernel developers to DMCA threats if
they try to change it?

									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-05 10:28     ` Pavel Machek
  0 siblings, 0 replies; 62+ messages in thread
From: Pavel Machek @ 2017-12-05 10:28 UTC (permalink / raw)
  To: Sean Paul
  Cc: David Airlie, intel-gfx, linux-kernel, linux-mediatek, dri-devel,
	Daniel Vetter, linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 1155 bytes --]

On Wed 2017-11-29 22:08:56, Sean Paul wrote:
> This patch adds a new optional connector property to allow userspace to enable
> protection over the content it is displaying. This will typically be implemented
> by the driver using HDCP.
> 
> The property is a tri-state with the following values:
> - OFF: Self explanatory, no content protection
> - DESIRED: Userspace requests that the driver enable protection
> - ENABLED: Once the driver has authenticated the link, it sets this value
> 
> The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
> unprotected. The driver should also maintain the desiredness of protection
> across hotplug/dpms/suspend.

Why would user of the machine want this to be something else than
'OFF'?

If kernel implements this, will it mean hardware vendors will have to
prevent user from updating kernel on machines they own?

If this is merged, does it open kernel developers to DMCA threats if
they try to change it?

									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

[-- Attachment #2: Type: text/plain, Size: 160 bytes --]

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

^ permalink raw reply	[flat|nested] 62+ messages in thread

* [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-05 10:28     ` Pavel Machek
  0 siblings, 0 replies; 62+ messages in thread
From: Pavel Machek @ 2017-12-05 10:28 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed 2017-11-29 22:08:56, Sean Paul wrote:
> This patch adds a new optional connector property to allow userspace to enable
> protection over the content it is displaying. This will typically be implemented
> by the driver using HDCP.
> 
> The property is a tri-state with the following values:
> - OFF: Self explanatory, no content protection
> - DESIRED: Userspace requests that the driver enable protection
> - ENABLED: Once the driver has authenticated the link, it sets this value
> 
> The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
> unprotected. The driver should also maintain the desiredness of protection
> across hotplug/dpms/suspend.

Why would user of the machine want this to be something else than
'OFF'?

If kernel implements this, will it mean hardware vendors will have to
prevent user from updating kernel on machines they own?

If this is merged, does it open kernel developers to DMCA threats if
they try to change it?

									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20171205/db10c986/attachment.sig>

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
  2017-12-05 10:28     ` Pavel Machek
  (?)
@ 2017-12-05 10:45       ` Daniel Vetter
  -1 siblings, 0 replies; 62+ messages in thread
From: Daniel Vetter @ 2017-12-05 10:45 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Sean Paul, David Airlie, intel-gfx, linux-kernel, linux-mediatek,
	dri-devel, Daniel Vetter, linux-arm-kernel

On Tue, Dec 05, 2017 at 11:28:40AM +0100, Pavel Machek wrote:
> On Wed 2017-11-29 22:08:56, Sean Paul wrote:
> > This patch adds a new optional connector property to allow userspace to enable
> > protection over the content it is displaying. This will typically be implemented
> > by the driver using HDCP.
> > 
> > The property is a tri-state with the following values:
> > - OFF: Self explanatory, no content protection
> > - DESIRED: Userspace requests that the driver enable protection
> > - ENABLED: Once the driver has authenticated the link, it sets this value
> > 
> > The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
> > unprotected. The driver should also maintain the desiredness of protection
> > across hotplug/dpms/suspend.
> 
> Why would user of the machine want this to be something else than
> 'OFF'?
> 
> If kernel implements this, will it mean hardware vendors will have to
> prevent user from updating kernel on machines they own?
> 
> If this is merged, does it open kernel developers to DMCA threats if
> they try to change it?

Because this just implements one part of the content protection scheme.
This only gives you an option to enable HDCP (aka encryption, it's really
nothing else) on the cable. Just because it has Content Protection in the
name does _not_ mean it is (stand-alone) an effective nor complete content
protection scheme. It's simply encrypting data, that's all.

If you want to actually lock down a machine to implement content
protection, then you need secure boot without unlockable boot-loader and a
pile more bits in userspace.  If you do all that, only then do you have
full content protection. And yes, then you don't really own the machine
fully, and I think users who are concerned with being able to update their
kernels and be able to exercise their software freedoms already know to
avoid such locked down systems.

So yeah it would be better to call this the "HDMI/DP cable encryption
support", but well, it's not what it's called really.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-05 10:45       ` Daniel Vetter
  0 siblings, 0 replies; 62+ messages in thread
From: Daniel Vetter @ 2017-12-05 10:45 UTC (permalink / raw)
  To: Pavel Machek
  Cc: David Airlie, intel-gfx, linux-kernel, dri-devel, linux-mediatek,
	Daniel Vetter, linux-arm-kernel

On Tue, Dec 05, 2017 at 11:28:40AM +0100, Pavel Machek wrote:
> On Wed 2017-11-29 22:08:56, Sean Paul wrote:
> > This patch adds a new optional connector property to allow userspace to enable
> > protection over the content it is displaying. This will typically be implemented
> > by the driver using HDCP.
> > 
> > The property is a tri-state with the following values:
> > - OFF: Self explanatory, no content protection
> > - DESIRED: Userspace requests that the driver enable protection
> > - ENABLED: Once the driver has authenticated the link, it sets this value
> > 
> > The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
> > unprotected. The driver should also maintain the desiredness of protection
> > across hotplug/dpms/suspend.
> 
> Why would user of the machine want this to be something else than
> 'OFF'?
> 
> If kernel implements this, will it mean hardware vendors will have to
> prevent user from updating kernel on machines they own?
> 
> If this is merged, does it open kernel developers to DMCA threats if
> they try to change it?

Because this just implements one part of the content protection scheme.
This only gives you an option to enable HDCP (aka encryption, it's really
nothing else) on the cable. Just because it has Content Protection in the
name does _not_ mean it is (stand-alone) an effective nor complete content
protection scheme. It's simply encrypting data, that's all.

If you want to actually lock down a machine to implement content
protection, then you need secure boot without unlockable boot-loader and a
pile more bits in userspace.  If you do all that, only then do you have
full content protection. And yes, then you don't really own the machine
fully, and I think users who are concerned with being able to update their
kernels and be able to exercise their software freedoms already know to
avoid such locked down systems.

So yeah it would be better to call this the "HDMI/DP cable encryption
support", but well, it's not what it's called really.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 62+ messages in thread

* [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-05 10:45       ` Daniel Vetter
  0 siblings, 0 replies; 62+ messages in thread
From: Daniel Vetter @ 2017-12-05 10:45 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Dec 05, 2017 at 11:28:40AM +0100, Pavel Machek wrote:
> On Wed 2017-11-29 22:08:56, Sean Paul wrote:
> > This patch adds a new optional connector property to allow userspace to enable
> > protection over the content it is displaying. This will typically be implemented
> > by the driver using HDCP.
> > 
> > The property is a tri-state with the following values:
> > - OFF: Self explanatory, no content protection
> > - DESIRED: Userspace requests that the driver enable protection
> > - ENABLED: Once the driver has authenticated the link, it sets this value
> > 
> > The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
> > unprotected. The driver should also maintain the desiredness of protection
> > across hotplug/dpms/suspend.
> 
> Why would user of the machine want this to be something else than
> 'OFF'?
> 
> If kernel implements this, will it mean hardware vendors will have to
> prevent user from updating kernel on machines they own?
> 
> If this is merged, does it open kernel developers to DMCA threats if
> they try to change it?

Because this just implements one part of the content protection scheme.
This only gives you an option to enable HDCP (aka encryption, it's really
nothing else) on the cable. Just because it has Content Protection in the
name does _not_ mean it is (stand-alone) an effective nor complete content
protection scheme. It's simply encrypting data, that's all.

If you want to actually lock down a machine to implement content
protection, then you need secure boot without unlockable boot-loader and a
pile more bits in userspace.  If you do all that, only then do you have
full content protection. And yes, then you don't really own the machine
fully, and I think users who are concerned with being able to update their
kernels and be able to exercise their software freedoms already know to
avoid such locked down systems.

So yeah it would be better to call this the "HDMI/DP cable encryption
support", but well, it's not what it's called really.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Intel-gfx] [RFC PATCH 0/6] drm/i915: Implement HDCP
  2017-11-30  7:50 ` [Intel-gfx] [RFC PATCH 0/6] drm/i915: Implement HDCP Daniel Vetter
@ 2017-12-05 13:45   ` Ville Syrjälä
  2017-12-05 14:45     ` Sean Paul
  0 siblings, 1 reply; 62+ messages in thread
From: Ville Syrjälä @ 2017-12-05 13:45 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: intel-gfx, dri-devel

On Thu, Nov 30, 2017 at 08:50:38AM +0100, Daniel Vetter wrote:
> On Wed, Nov 29, 2017 at 10:08:55PM -0500, Sean Paul wrote:
> > Here's the RFC for my i915 HDCP patchset. The UABI is based on what we've been
> > using in Chrome for the past 3 years. I posted the property to the list back
> > then, but never had a mainline driver to implement it. I do now :-)
> > 
> > Things are mostly in place, danvet gave me some feedback that I will
> > incorporate in v1. However, in the interest of gaining more early feedback, I'm
> > posting this now.
> > 
> > TODO:
> > - Add kerneldoc for property
> 
> The big thing I'd like to see here is clear description of the flows
> between kernel and userspace (since there's no helpers on the kernel side
> to standardize this).
> 
> One thing we discussed in that context is the addition of an uevent (like
> we do for anything else that changes with connectors, link_status is one
> example). But since the hdcp spec explicitly demands that the video player
> must poll the status an event is moot and won't be used. And I'm no fan of
> speculative uapi :-)

Speaking of uapi... Do we have an open source userspace and igts for
this somewhere?

I'm also concerned about debugging this. It will be a real PITA
to do if we can't even test it easily.

> 
> > - Fix '//' comments
> > - Change to MIT license
> > - Rebase on Ville's gmbus fixes (thanks Ville)
> > - Improve documentation on drm_intel_hdcp_shim
> > - Fix async commit locking (ie: don't use connection_mutex)
> > - Don't change connector->state in enable, defer to worker
> 
> Same holds for the disable callback, you can't touch state in there.
> 
> With the link_status prop (which is somewhat similar) we only reset it in
> atomic_check (where we hold the state locks) and in the async worker (same
> applies).
> -Daniel
> 
> > - Add igt coverage for the feature.
> > 
> > Thanks!
> > 
> > Sean
> > 
> > 
> > Sean Paul (6):
> >   drm: Add Content Protection property
> >   drm: Add some HDCP related #defines
> >   drm/i915: Add HDCP framework + base implementation
> >   drm/i915: Add function to output Aksv over GMBUS
> >   drm/i915: Implement HDCP for HDMI
> >   drm/i915: Implement HDCP for DisplayPort
> > 
> >  drivers/gpu/drm/drm_atomic.c        |   8 +
> >  drivers/gpu/drm/drm_connector.c     |  43 +++
> >  drivers/gpu/drm/drm_sysfs.c         |  29 ++
> >  drivers/gpu/drm/i915/Makefile       |   1 +
> >  drivers/gpu/drm/i915/i915_drv.h     |   1 +
> >  drivers/gpu/drm/i915/i915_reg.h     |  85 +++++
> >  drivers/gpu/drm/i915/intel_atomic.c |  26 +-
> >  drivers/gpu/drm/i915/intel_ddi.c    |  64 ++++
> >  drivers/gpu/drm/i915/intel_dp.c     | 243 +++++++++++++-
> >  drivers/gpu/drm/i915/intel_drv.h    |  53 +++
> >  drivers/gpu/drm/i915/intel_hdcp.c   | 636 ++++++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/i915/intel_hdmi.c   | 253 ++++++++++++++
> >  drivers/gpu/drm/i915/intel_i2c.c    |  54 ++-
> >  include/drm/drm_connector.h         |  16 +
> >  include/drm/drm_dp_helper.h         |  17 +
> >  include/drm/drm_hdcp.h              |  44 +++
> >  include/uapi/drm/drm_mode.h         |   4 +
> >  17 files changed, 1560 insertions(+), 17 deletions(-)
> >  create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c
> >  create mode 100644 include/drm/drm_hdcp.h
> > 
> > -- 
> > 2.15.0.531.g2ccb3012c9-goog
> > 
> > _______________________________________________
> > Intel-gfx mailing list
> > Intel-gfx@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/intel-gfx
> 
> -- 
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Ville Syrjälä
Intel OTC
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 0/6] drm/i915: Implement HDCP
  2017-12-05 13:45   ` Ville Syrjälä
@ 2017-12-05 14:45     ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-12-05 14:45 UTC (permalink / raw)
  To: Ville Syrjälä; +Cc: Intel Graphics Development, dri-devel

On Tue, Dec 5, 2017 at 8:45 AM, Ville Syrjälä
<ville.syrjala@linux.intel.com> wrote:
> On Thu, Nov 30, 2017 at 08:50:38AM +0100, Daniel Vetter wrote:
>> On Wed, Nov 29, 2017 at 10:08:55PM -0500, Sean Paul wrote:
>> > Here's the RFC for my i915 HDCP patchset. The UABI is based on what we've been
>> > using in Chrome for the past 3 years. I posted the property to the list back
>> > then, but never had a mainline driver to implement it. I do now :-)
>> >
>> > Things are mostly in place, danvet gave me some feedback that I will
>> > incorporate in v1. However, in the interest of gaining more early feedback, I'm
>> > posting this now.
>> >
>> > TODO:
>> > - Add kerneldoc for property
>>
>> The big thing I'd like to see here is clear description of the flows
>> between kernel and userspace (since there's no helpers on the kernel side
>> to standardize this).
>>
>> One thing we discussed in that context is the addition of an uevent (like
>> we do for anything else that changes with connectors, link_status is one
>> example). But since the hdcp spec explicitly demands that the video player
>> must poll the status an event is moot and won't be used. And I'm no fan of
>> speculative uapi :-)
>
> Speaking of uapi... Do we have an open source userspace and igts for
> this somewhere?
>

The userspace is implemented in Chrome for Chrome OS:
https://cs.chromium.org/chromium/src/ui/ozone/platform/drm/gpu/drm_display.cc

I posted an igt test on the list earlier today.

Sean

> I'm also concerned about debugging this. It will be a real PITA
> to do if we can't even test it easily.
>
>>
>> > - Fix '//' comments
>> > - Change to MIT license
>> > - Rebase on Ville's gmbus fixes (thanks Ville)
>> > - Improve documentation on drm_intel_hdcp_shim
>> > - Fix async commit locking (ie: don't use connection_mutex)
>> > - Don't change connector->state in enable, defer to worker
>>
>> Same holds for the disable callback, you can't touch state in there.
>>
>> With the link_status prop (which is somewhat similar) we only reset it in
>> atomic_check (where we hold the state locks) and in the async worker (same
>> applies).
>> -Daniel
>>
>> > - Add igt coverage for the feature.
>> >
>> > Thanks!
>> >
>> > Sean
>> >
>> >
>> > Sean Paul (6):
>> >   drm: Add Content Protection property
>> >   drm: Add some HDCP related #defines
>> >   drm/i915: Add HDCP framework + base implementation
>> >   drm/i915: Add function to output Aksv over GMBUS
>> >   drm/i915: Implement HDCP for HDMI
>> >   drm/i915: Implement HDCP for DisplayPort
>> >
>> >  drivers/gpu/drm/drm_atomic.c        |   8 +
>> >  drivers/gpu/drm/drm_connector.c     |  43 +++
>> >  drivers/gpu/drm/drm_sysfs.c         |  29 ++
>> >  drivers/gpu/drm/i915/Makefile       |   1 +
>> >  drivers/gpu/drm/i915/i915_drv.h     |   1 +
>> >  drivers/gpu/drm/i915/i915_reg.h     |  85 +++++
>> >  drivers/gpu/drm/i915/intel_atomic.c |  26 +-
>> >  drivers/gpu/drm/i915/intel_ddi.c    |  64 ++++
>> >  drivers/gpu/drm/i915/intel_dp.c     | 243 +++++++++++++-
>> >  drivers/gpu/drm/i915/intel_drv.h    |  53 +++
>> >  drivers/gpu/drm/i915/intel_hdcp.c   | 636 ++++++++++++++++++++++++++++++++++++
>> >  drivers/gpu/drm/i915/intel_hdmi.c   | 253 ++++++++++++++
>> >  drivers/gpu/drm/i915/intel_i2c.c    |  54 ++-
>> >  include/drm/drm_connector.h         |  16 +
>> >  include/drm/drm_dp_helper.h         |  17 +
>> >  include/drm/drm_hdcp.h              |  44 +++
>> >  include/uapi/drm/drm_mode.h         |   4 +
>> >  17 files changed, 1560 insertions(+), 17 deletions(-)
>> >  create mode 100644 drivers/gpu/drm/i915/intel_hdcp.c
>> >  create mode 100644 include/drm/drm_hdcp.h
>> >
>> > --
>> > 2.15.0.531.g2ccb3012c9-goog
>> >
>> > _______________________________________________
>> > Intel-gfx mailing list
>> > Intel-gfx@lists.freedesktop.org
>> > https://lists.freedesktop.org/mailman/listinfo/intel-gfx
>>
>> --
>> Daniel Vetter
>> Software Engineer, Intel Corporation
>> http://blog.ffwll.ch
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
> --
> Ville Syrjälä
> Intel OTC
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
  2017-12-05 10:45       ` Daniel Vetter
  (?)
@ 2017-12-05 17:34         ` Pavel Machek
  -1 siblings, 0 replies; 62+ messages in thread
From: Pavel Machek @ 2017-12-05 17:34 UTC (permalink / raw)
  To: Sean Paul, David Airlie, intel-gfx, linux-kernel, linux-mediatek,
	dri-devel, Daniel Vetter, linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 2924 bytes --]

On Tue 2017-12-05 11:45:38, Daniel Vetter wrote:
> On Tue, Dec 05, 2017 at 11:28:40AM +0100, Pavel Machek wrote:
> > On Wed 2017-11-29 22:08:56, Sean Paul wrote:
> > > This patch adds a new optional connector property to allow userspace to enable
> > > protection over the content it is displaying. This will typically be implemented
> > > by the driver using HDCP.
> > > 
> > > The property is a tri-state with the following values:
> > > - OFF: Self explanatory, no content protection
> > > - DESIRED: Userspace requests that the driver enable protection
> > > - ENABLED: Once the driver has authenticated the link, it sets this value
> > > 
> > > The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
> > > unprotected. The driver should also maintain the desiredness of protection
> > > across hotplug/dpms/suspend.
> > 
> > Why would user of the machine want this to be something else than
> > 'OFF'?
> > 
> > If kernel implements this, will it mean hardware vendors will have to
> > prevent user from updating kernel on machines they own?
> > 
> > If this is merged, does it open kernel developers to DMCA threats if
> > they try to change it?
> 
> Because this just implements one part of the content protection scheme.
> This only gives you an option to enable HDCP (aka encryption, it's really
> nothing else) on the cable. Just because it has Content Protection in the
> name does _not_ mean it is (stand-alone) an effective nor complete content
> protection scheme. It's simply encrypting data, that's all.

Yep. So my first question was: why would user of the machine ever want
encryption "ENABLED" or "DESIRED"? Could you answer it?

> If you want to actually lock down a machine to implement content
> protection, then you need secure boot without unlockable boot-loader and a
> pile more bits in userspace.  If you do all that, only then do you have
> full content protection. And yes, then you don't really own the machine
> fully, and I think users who are concerned with being able to update
> their

Yes, so... This patch makes it more likely to see machines with locked
down kernels, preventing developers from working with systems their
own, running hardware. That is evil, and direct threat to Free
software movement.

Users compiling their own kernels get no benefit from it. Actually it
looks like this only benefits Intel and Disney. We don't want that.

> kernels and be able to exercise their software freedoms already know to
> avoid such locked down systems.
> 
> So yeah it would be better to call this the "HDMI/DP cable encryption
> support", but well, it's not what it's called really.

Well, it does not belong in kernel, no matter what is the name.

									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-05 17:34         ` Pavel Machek
  0 siblings, 0 replies; 62+ messages in thread
From: Pavel Machek @ 2017-12-05 17:34 UTC (permalink / raw)
  To: Sean Paul, David Airlie, intel-gfx, linux-kernel, linux-mediatek,
	dri-devel, Daniel Vetter, linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 2924 bytes --]

On Tue 2017-12-05 11:45:38, Daniel Vetter wrote:
> On Tue, Dec 05, 2017 at 11:28:40AM +0100, Pavel Machek wrote:
> > On Wed 2017-11-29 22:08:56, Sean Paul wrote:
> > > This patch adds a new optional connector property to allow userspace to enable
> > > protection over the content it is displaying. This will typically be implemented
> > > by the driver using HDCP.
> > > 
> > > The property is a tri-state with the following values:
> > > - OFF: Self explanatory, no content protection
> > > - DESIRED: Userspace requests that the driver enable protection
> > > - ENABLED: Once the driver has authenticated the link, it sets this value
> > > 
> > > The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
> > > unprotected. The driver should also maintain the desiredness of protection
> > > across hotplug/dpms/suspend.
> > 
> > Why would user of the machine want this to be something else than
> > 'OFF'?
> > 
> > If kernel implements this, will it mean hardware vendors will have to
> > prevent user from updating kernel on machines they own?
> > 
> > If this is merged, does it open kernel developers to DMCA threats if
> > they try to change it?
> 
> Because this just implements one part of the content protection scheme.
> This only gives you an option to enable HDCP (aka encryption, it's really
> nothing else) on the cable. Just because it has Content Protection in the
> name does _not_ mean it is (stand-alone) an effective nor complete content
> protection scheme. It's simply encrypting data, that's all.

Yep. So my first question was: why would user of the machine ever want
encryption "ENABLED" or "DESIRED"? Could you answer it?

> If you want to actually lock down a machine to implement content
> protection, then you need secure boot without unlockable boot-loader and a
> pile more bits in userspace.  If you do all that, only then do you have
> full content protection. And yes, then you don't really own the machine
> fully, and I think users who are concerned with being able to update
> their

Yes, so... This patch makes it more likely to see machines with locked
down kernels, preventing developers from working with systems their
own, running hardware. That is evil, and direct threat to Free
software movement.

Users compiling their own kernels get no benefit from it. Actually it
looks like this only benefits Intel and Disney. We don't want that.

> kernels and be able to exercise their software freedoms already know to
> avoid such locked down systems.
> 
> So yeah it would be better to call this the "HDMI/DP cable encryption
> support", but well, it's not what it's called really.

Well, it does not belong in kernel, no matter what is the name.

									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

[-- Attachment #2: Type: text/plain, Size: 160 bytes --]

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-05 17:34         ` Pavel Machek
  0 siblings, 0 replies; 62+ messages in thread
From: Pavel Machek @ 2017-12-05 17:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue 2017-12-05 11:45:38, Daniel Vetter wrote:
> On Tue, Dec 05, 2017 at 11:28:40AM +0100, Pavel Machek wrote:
> > On Wed 2017-11-29 22:08:56, Sean Paul wrote:
> > > This patch adds a new optional connector property to allow userspace to enable
> > > protection over the content it is displaying. This will typically be implemented
> > > by the driver using HDCP.
> > > 
> > > The property is a tri-state with the following values:
> > > - OFF: Self explanatory, no content protection
> > > - DESIRED: Userspace requests that the driver enable protection
> > > - ENABLED: Once the driver has authenticated the link, it sets this value
> > > 
> > > The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
> > > unprotected. The driver should also maintain the desiredness of protection
> > > across hotplug/dpms/suspend.
> > 
> > Why would user of the machine want this to be something else than
> > 'OFF'?
> > 
> > If kernel implements this, will it mean hardware vendors will have to
> > prevent user from updating kernel on machines they own?
> > 
> > If this is merged, does it open kernel developers to DMCA threats if
> > they try to change it?
> 
> Because this just implements one part of the content protection scheme.
> This only gives you an option to enable HDCP (aka encryption, it's really
> nothing else) on the cable. Just because it has Content Protection in the
> name does _not_ mean it is (stand-alone) an effective nor complete content
> protection scheme. It's simply encrypting data, that's all.

Yep. So my first question was: why would user of the machine ever want
encryption "ENABLED" or "DESIRED"? Could you answer it?

> If you want to actually lock down a machine to implement content
> protection, then you need secure boot without unlockable boot-loader and a
> pile more bits in userspace.  If you do all that, only then do you have
> full content protection. And yes, then you don't really own the machine
> fully, and I think users who are concerned with being able to update
> their

Yes, so... This patch makes it more likely to see machines with locked
down kernels, preventing developers from working with systems their
own, running hardware. That is evil, and direct threat to Free
software movement.

Users compiling their own kernels get no benefit from it. Actually it
looks like this only benefits Intel and Disney. We don't want that.

> kernels and be able to exercise their software freedoms already know to
> avoid such locked down systems.
> 
> So yeah it would be better to call this the "HDMI/DP cable encryption
> support", but well, it's not what it's called really.

Well, it does not belong in kernel, no matter what is the name.

									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20171205/45481bfd/attachment.sig>

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
  2017-12-05 17:34         ` Pavel Machek
  (?)
@ 2017-12-05 17:53           ` Alex Deucher
  -1 siblings, 0 replies; 62+ messages in thread
From: Alex Deucher @ 2017-12-05 17:53 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Sean Paul, David Airlie, Intel Graphics Development, LKML,
	linux-mediatek, Maling list - DRI developers, Daniel Vetter,
	linux-arm-kernel

On Tue, Dec 5, 2017 at 12:34 PM, Pavel Machek <pavel@ucw.cz> wrote:
> On Tue 2017-12-05 11:45:38, Daniel Vetter wrote:
>> On Tue, Dec 05, 2017 at 11:28:40AM +0100, Pavel Machek wrote:
>> > On Wed 2017-11-29 22:08:56, Sean Paul wrote:
>> > > This patch adds a new optional connector property to allow userspace to enable
>> > > protection over the content it is displaying. This will typically be implemented
>> > > by the driver using HDCP.
>> > >
>> > > The property is a tri-state with the following values:
>> > > - OFF: Self explanatory, no content protection
>> > > - DESIRED: Userspace requests that the driver enable protection
>> > > - ENABLED: Once the driver has authenticated the link, it sets this value
>> > >
>> > > The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
>> > > unprotected. The driver should also maintain the desiredness of protection
>> > > across hotplug/dpms/suspend.
>> >
>> > Why would user of the machine want this to be something else than
>> > 'OFF'?
>> >
>> > If kernel implements this, will it mean hardware vendors will have to
>> > prevent user from updating kernel on machines they own?
>> >
>> > If this is merged, does it open kernel developers to DMCA threats if
>> > they try to change it?
>>
>> Because this just implements one part of the content protection scheme.
>> This only gives you an option to enable HDCP (aka encryption, it's really
>> nothing else) on the cable. Just because it has Content Protection in the
>> name does _not_ mean it is (stand-alone) an effective nor complete content
>> protection scheme. It's simply encrypting data, that's all.
>
> Yep. So my first question was: why would user of the machine ever want
> encryption "ENABLED" or "DESIRED"? Could you answer it?

How about for sensitive video streams in government offices where you
want to avoid a spy potentially tapping the cable to see the video
stream?

>
>> If you want to actually lock down a machine to implement content
>> protection, then you need secure boot without unlockable boot-loader and a
>> pile more bits in userspace.  If you do all that, only then do you have
>> full content protection. And yes, then you don't really own the machine
>> fully, and I think users who are concerned with being able to update
>> their
>
> Yes, so... This patch makes it more likely to see machines with locked
> down kernels, preventing developers from working with systems their
> own, running hardware. That is evil, and direct threat to Free
> software movement.
>
> Users compiling their own kernels get no benefit from it. Actually it
> looks like this only benefits Intel and Disney. We don't want that.

And just about every SoC manufacturer and google and amazon and a ton
of other companies and organizations.  Who gets to decide who's
benefit gets taken into account?

>
>> kernels and be able to exercise their software freedoms already know to
>> avoid such locked down systems.
>>
>> So yeah it would be better to call this the "HDMI/DP cable encryption
>> support", but well, it's not what it's called really.
>
> Well, it does not belong in kernel, no matter what is the name.

Should we remove support for encrypted file systems and encrypted
virtual machines?  Just like them the option is there is you want to
use it.  If you don't want to, you don't have to.


Alex

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-05 17:53           ` Alex Deucher
  0 siblings, 0 replies; 62+ messages in thread
From: Alex Deucher @ 2017-12-05 17:53 UTC (permalink / raw)
  To: Pavel Machek
  Cc: David Airlie, Intel Graphics Development, LKML,
	Maling list - DRI developers, linux-mediatek, Daniel Vetter,
	linux-arm-kernel

On Tue, Dec 5, 2017 at 12:34 PM, Pavel Machek <pavel@ucw.cz> wrote:
> On Tue 2017-12-05 11:45:38, Daniel Vetter wrote:
>> On Tue, Dec 05, 2017 at 11:28:40AM +0100, Pavel Machek wrote:
>> > On Wed 2017-11-29 22:08:56, Sean Paul wrote:
>> > > This patch adds a new optional connector property to allow userspace to enable
>> > > protection over the content it is displaying. This will typically be implemented
>> > > by the driver using HDCP.
>> > >
>> > > The property is a tri-state with the following values:
>> > > - OFF: Self explanatory, no content protection
>> > > - DESIRED: Userspace requests that the driver enable protection
>> > > - ENABLED: Once the driver has authenticated the link, it sets this value
>> > >
>> > > The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
>> > > unprotected. The driver should also maintain the desiredness of protection
>> > > across hotplug/dpms/suspend.
>> >
>> > Why would user of the machine want this to be something else than
>> > 'OFF'?
>> >
>> > If kernel implements this, will it mean hardware vendors will have to
>> > prevent user from updating kernel on machines they own?
>> >
>> > If this is merged, does it open kernel developers to DMCA threats if
>> > they try to change it?
>>
>> Because this just implements one part of the content protection scheme.
>> This only gives you an option to enable HDCP (aka encryption, it's really
>> nothing else) on the cable. Just because it has Content Protection in the
>> name does _not_ mean it is (stand-alone) an effective nor complete content
>> protection scheme. It's simply encrypting data, that's all.
>
> Yep. So my first question was: why would user of the machine ever want
> encryption "ENABLED" or "DESIRED"? Could you answer it?

How about for sensitive video streams in government offices where you
want to avoid a spy potentially tapping the cable to see the video
stream?

>
>> If you want to actually lock down a machine to implement content
>> protection, then you need secure boot without unlockable boot-loader and a
>> pile more bits in userspace.  If you do all that, only then do you have
>> full content protection. And yes, then you don't really own the machine
>> fully, and I think users who are concerned with being able to update
>> their
>
> Yes, so... This patch makes it more likely to see machines with locked
> down kernels, preventing developers from working with systems their
> own, running hardware. That is evil, and direct threat to Free
> software movement.
>
> Users compiling their own kernels get no benefit from it. Actually it
> looks like this only benefits Intel and Disney. We don't want that.

And just about every SoC manufacturer and google and amazon and a ton
of other companies and organizations.  Who gets to decide who's
benefit gets taken into account?

>
>> kernels and be able to exercise their software freedoms already know to
>> avoid such locked down systems.
>>
>> So yeah it would be better to call this the "HDMI/DP cable encryption
>> support", but well, it's not what it's called really.
>
> Well, it does not belong in kernel, no matter what is the name.

Should we remove support for encrypted file systems and encrypted
virtual machines?  Just like them the option is there is you want to
use it.  If you don't want to, you don't have to.


Alex
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-05 17:53           ` Alex Deucher
  0 siblings, 0 replies; 62+ messages in thread
From: Alex Deucher @ 2017-12-05 17:53 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Dec 5, 2017 at 12:34 PM, Pavel Machek <pavel@ucw.cz> wrote:
> On Tue 2017-12-05 11:45:38, Daniel Vetter wrote:
>> On Tue, Dec 05, 2017 at 11:28:40AM +0100, Pavel Machek wrote:
>> > On Wed 2017-11-29 22:08:56, Sean Paul wrote:
>> > > This patch adds a new optional connector property to allow userspace to enable
>> > > protection over the content it is displaying. This will typically be implemented
>> > > by the driver using HDCP.
>> > >
>> > > The property is a tri-state with the following values:
>> > > - OFF: Self explanatory, no content protection
>> > > - DESIRED: Userspace requests that the driver enable protection
>> > > - ENABLED: Once the driver has authenticated the link, it sets this value
>> > >
>> > > The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
>> > > unprotected. The driver should also maintain the desiredness of protection
>> > > across hotplug/dpms/suspend.
>> >
>> > Why would user of the machine want this to be something else than
>> > 'OFF'?
>> >
>> > If kernel implements this, will it mean hardware vendors will have to
>> > prevent user from updating kernel on machines they own?
>> >
>> > If this is merged, does it open kernel developers to DMCA threats if
>> > they try to change it?
>>
>> Because this just implements one part of the content protection scheme.
>> This only gives you an option to enable HDCP (aka encryption, it's really
>> nothing else) on the cable. Just because it has Content Protection in the
>> name does _not_ mean it is (stand-alone) an effective nor complete content
>> protection scheme. It's simply encrypting data, that's all.
>
> Yep. So my first question was: why would user of the machine ever want
> encryption "ENABLED" or "DESIRED"? Could you answer it?

How about for sensitive video streams in government offices where you
want to avoid a spy potentially tapping the cable to see the video
stream?

>
>> If you want to actually lock down a machine to implement content
>> protection, then you need secure boot without unlockable boot-loader and a
>> pile more bits in userspace.  If you do all that, only then do you have
>> full content protection. And yes, then you don't really own the machine
>> fully, and I think users who are concerned with being able to update
>> their
>
> Yes, so... This patch makes it more likely to see machines with locked
> down kernels, preventing developers from working with systems their
> own, running hardware. That is evil, and direct threat to Free
> software movement.
>
> Users compiling their own kernels get no benefit from it. Actually it
> looks like this only benefits Intel and Disney. We don't want that.

And just about every SoC manufacturer and google and amazon and a ton
of other companies and organizations.  Who gets to decide who's
benefit gets taken into account?

>
>> kernels and be able to exercise their software freedoms already know to
>> avoid such locked down systems.
>>
>> So yeah it would be better to call this the "HDMI/DP cable encryption
>> support", but well, it's not what it's called really.
>
> Well, it does not belong in kernel, no matter what is the name.

Should we remove support for encrypted file systems and encrypted
virtual machines?  Just like them the option is there is you want to
use it.  If you don't want to, you don't have to.


Alex

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
  2017-12-05 17:53           ` Alex Deucher
  (?)
@ 2017-12-05 18:01             ` Pavel Machek
  -1 siblings, 0 replies; 62+ messages in thread
From: Pavel Machek @ 2017-12-05 18:01 UTC (permalink / raw)
  To: Alex Deucher
  Cc: Sean Paul, David Airlie, Intel Graphics Development, LKML,
	linux-mediatek, Maling list - DRI developers, Daniel Vetter,
	linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 2084 bytes --]

Hi!

> >> > Why would user of the machine want this to be something else than
> >> > 'OFF'?
> >> >
> >> > If kernel implements this, will it mean hardware vendors will have to
> >> > prevent user from updating kernel on machines they own?
> >> >
> >> > If this is merged, does it open kernel developers to DMCA threats if
> >> > they try to change it?
> >>
> >> Because this just implements one part of the content protection scheme.
> >> This only gives you an option to enable HDCP (aka encryption, it's really
> >> nothing else) on the cable. Just because it has Content Protection in the
> >> name does _not_ mean it is (stand-alone) an effective nor complete content
> >> protection scheme. It's simply encrypting data, that's all.
> >
> > Yep. So my first question was: why would user of the machine ever want
> > encryption "ENABLED" or "DESIRED"? Could you answer it?
> 
> How about for sensitive video streams in government offices where you
> want to avoid a spy potentially tapping the cable to see the video
> stream?

Except that spies already have the keys, as every monitor
manufacturer has them?

> >> kernels and be able to exercise their software freedoms already know to
> >> avoid such locked down systems.
> >>
> >> So yeah it would be better to call this the "HDMI/DP cable encryption
> >> support", but well, it's not what it's called really.
> >
> > Well, it does not belong in kernel, no matter what is the name.
> 
> Should we remove support for encrypted file systems and encrypted
> virtual machines?  Just like them the option is there is you want to
> use it.  If you don't want to, you don't have to.

Encrypted file systems benefit users. Encrypted video is designed to
work against users. In particular, users don't have encryption keys
for video they generate. I'd have nothing against feature that would
let users encrypt video with keys they control.
								Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-05 18:01             ` Pavel Machek
  0 siblings, 0 replies; 62+ messages in thread
From: Pavel Machek @ 2017-12-05 18:01 UTC (permalink / raw)
  To: Alex Deucher
  Cc: David Airlie, Intel Graphics Development, LKML,
	Maling list - DRI developers, linux-mediatek, Daniel Vetter,
	linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 2084 bytes --]

Hi!

> >> > Why would user of the machine want this to be something else than
> >> > 'OFF'?
> >> >
> >> > If kernel implements this, will it mean hardware vendors will have to
> >> > prevent user from updating kernel on machines they own?
> >> >
> >> > If this is merged, does it open kernel developers to DMCA threats if
> >> > they try to change it?
> >>
> >> Because this just implements one part of the content protection scheme.
> >> This only gives you an option to enable HDCP (aka encryption, it's really
> >> nothing else) on the cable. Just because it has Content Protection in the
> >> name does _not_ mean it is (stand-alone) an effective nor complete content
> >> protection scheme. It's simply encrypting data, that's all.
> >
> > Yep. So my first question was: why would user of the machine ever want
> > encryption "ENABLED" or "DESIRED"? Could you answer it?
> 
> How about for sensitive video streams in government offices where you
> want to avoid a spy potentially tapping the cable to see the video
> stream?

Except that spies already have the keys, as every monitor
manufacturer has them?

> >> kernels and be able to exercise their software freedoms already know to
> >> avoid such locked down systems.
> >>
> >> So yeah it would be better to call this the "HDMI/DP cable encryption
> >> support", but well, it's not what it's called really.
> >
> > Well, it does not belong in kernel, no matter what is the name.
> 
> Should we remove support for encrypted file systems and encrypted
> virtual machines?  Just like them the option is there is you want to
> use it.  If you don't want to, you don't have to.

Encrypted file systems benefit users. Encrypted video is designed to
work against users. In particular, users don't have encryption keys
for video they generate. I'd have nothing against feature that would
let users encrypt video with keys they control.
								Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

[-- Attachment #2: Type: text/plain, Size: 160 bytes --]

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

^ permalink raw reply	[flat|nested] 62+ messages in thread

* [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-05 18:01             ` Pavel Machek
  0 siblings, 0 replies; 62+ messages in thread
From: Pavel Machek @ 2017-12-05 18:01 UTC (permalink / raw)
  To: linux-arm-kernel

Hi!

> >> > Why would user of the machine want this to be something else than
> >> > 'OFF'?
> >> >
> >> > If kernel implements this, will it mean hardware vendors will have to
> >> > prevent user from updating kernel on machines they own?
> >> >
> >> > If this is merged, does it open kernel developers to DMCA threats if
> >> > they try to change it?
> >>
> >> Because this just implements one part of the content protection scheme.
> >> This only gives you an option to enable HDCP (aka encryption, it's really
> >> nothing else) on the cable. Just because it has Content Protection in the
> >> name does _not_ mean it is (stand-alone) an effective nor complete content
> >> protection scheme. It's simply encrypting data, that's all.
> >
> > Yep. So my first question was: why would user of the machine ever want
> > encryption "ENABLED" or "DESIRED"? Could you answer it?
> 
> How about for sensitive video streams in government offices where you
> want to avoid a spy potentially tapping the cable to see the video
> stream?

Except that spies already have the keys, as every monitor
manufacturer has them?

> >> kernels and be able to exercise their software freedoms already know to
> >> avoid such locked down systems.
> >>
> >> So yeah it would be better to call this the "HDMI/DP cable encryption
> >> support", but well, it's not what it's called really.
> >
> > Well, it does not belong in kernel, no matter what is the name.
> 
> Should we remove support for encrypted file systems and encrypted
> virtual machines?  Just like them the option is there is you want to
> use it.  If you don't want to, you don't have to.

Encrypted file systems benefit users. Encrypted video is designed to
work against users. In particular, users don't have encryption keys
for video they generate. I'd have nothing against feature that would
let users encrypt video with keys they control.
								Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20171205/179f88b1/attachment.sig>

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
  2017-12-05 17:34         ` Pavel Machek
  (?)
@ 2017-12-05 19:03           ` Sean Paul
  -1 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-12-05 19:03 UTC (permalink / raw)
  To: Pavel Machek
  Cc: David Airlie, Intel Graphics Development,
	Linux Kernel Mailing List, linux-mediatek, dri-devel,
	Daniel Vetter, Linux ARM Kernel

On Tue, Dec 5, 2017 at 12:34 PM, Pavel Machek <pavel@ucw.cz> wrote:
> On Tue 2017-12-05 11:45:38, Daniel Vetter wrote:
>> On Tue, Dec 05, 2017 at 11:28:40AM +0100, Pavel Machek wrote:
>> > On Wed 2017-11-29 22:08:56, Sean Paul wrote:
>> > > This patch adds a new optional connector property to allow userspace to enable
>> > > protection over the content it is displaying. This will typically be implemented
>> > > by the driver using HDCP.
>> > >
>> > > The property is a tri-state with the following values:
>> > > - OFF: Self explanatory, no content protection
>> > > - DESIRED: Userspace requests that the driver enable protection
>> > > - ENABLED: Once the driver has authenticated the link, it sets this value
>> > >
>> > > The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
>> > > unprotected. The driver should also maintain the desiredness of protection
>> > > across hotplug/dpms/suspend.
>> >
>> > Why would user of the machine want this to be something else than
>> > 'OFF'?
>> >
>> > If kernel implements this, will it mean hardware vendors will have to
>> > prevent user from updating kernel on machines they own?
>> >
>> > If this is merged, does it open kernel developers to DMCA threats if
>> > they try to change it?
>>
>> Because this just implements one part of the content protection scheme.
>> This only gives you an option to enable HDCP (aka encryption, it's really
>> nothing else) on the cable. Just because it has Content Protection in the
>> name does _not_ mean it is (stand-alone) an effective nor complete content
>> protection scheme. It's simply encrypting data, that's all.
>
> Yep. So my first question was: why would user of the machine ever want
> encryption "ENABLED" or "DESIRED"? Could you answer it?
>

Sure. We have a lot of Chrome OS users who would really like to enjoy
premium hd content on their tvs.


>> If you want to actually lock down a machine to implement content
>> protection, then you need secure boot without unlockable boot-loader and a
>> pile more bits in userspace.  If you do all that, only then do you have
>> full content protection. And yes, then you don't really own the machine
>> fully, and I think users who are concerned with being able to update
>> their
>
> Yes, so... This patch makes it more likely to see machines with locked
> down kernels, preventing developers from working with systems their
> own, running hardware. That is evil, and direct threat to Free
> software movement.
>
> Users compiling their own kernels get no benefit from it. Actually it
> looks like this only benefits Intel and Disney. We don't want that.
>

Major citation needed here. Did you actually read the code? If you
did, you would realize that the feature is already latent in your
computer. This patchset merely exposes how that hardware can be
enabled to encrypt your video link. Would you have the same problems
with a new whizzbang video encoding format, or is it just the "Content
Protection" name? Perhaps you'd prefer this feature was implemented in
Intel's firmware, or a userspace blob? It wouldn't change the fact
that those registers exist and _can_ be used for HDCP, it's just that
now you know about it.

Having all of the code in the open allows users to see what is
happening with their hardware, how is this a bad thing?

Sean


>> kernels and be able to exercise their software freedoms already know to
>> avoid such locked down systems.
>>
>> So yeah it would be better to call this the "HDMI/DP cable encryption
>> support", but well, it's not what it's called really.
>
> Well, it does not belong in kernel, no matter what is the name.
>
>                                                                         Pavel
> --
> (english) http://www.livejournal.com/~pavelmachek
> (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-05 19:03           ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-12-05 19:03 UTC (permalink / raw)
  To: Pavel Machek
  Cc: David Airlie, Intel Graphics Development,
	Linux Kernel Mailing List, dri-devel, linux-mediatek,
	Daniel Vetter, Linux ARM Kernel

On Tue, Dec 5, 2017 at 12:34 PM, Pavel Machek <pavel@ucw.cz> wrote:
> On Tue 2017-12-05 11:45:38, Daniel Vetter wrote:
>> On Tue, Dec 05, 2017 at 11:28:40AM +0100, Pavel Machek wrote:
>> > On Wed 2017-11-29 22:08:56, Sean Paul wrote:
>> > > This patch adds a new optional connector property to allow userspace to enable
>> > > protection over the content it is displaying. This will typically be implemented
>> > > by the driver using HDCP.
>> > >
>> > > The property is a tri-state with the following values:
>> > > - OFF: Self explanatory, no content protection
>> > > - DESIRED: Userspace requests that the driver enable protection
>> > > - ENABLED: Once the driver has authenticated the link, it sets this value
>> > >
>> > > The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
>> > > unprotected. The driver should also maintain the desiredness of protection
>> > > across hotplug/dpms/suspend.
>> >
>> > Why would user of the machine want this to be something else than
>> > 'OFF'?
>> >
>> > If kernel implements this, will it mean hardware vendors will have to
>> > prevent user from updating kernel on machines they own?
>> >
>> > If this is merged, does it open kernel developers to DMCA threats if
>> > they try to change it?
>>
>> Because this just implements one part of the content protection scheme.
>> This only gives you an option to enable HDCP (aka encryption, it's really
>> nothing else) on the cable. Just because it has Content Protection in the
>> name does _not_ mean it is (stand-alone) an effective nor complete content
>> protection scheme. It's simply encrypting data, that's all.
>
> Yep. So my first question was: why would user of the machine ever want
> encryption "ENABLED" or "DESIRED"? Could you answer it?
>

Sure. We have a lot of Chrome OS users who would really like to enjoy
premium hd content on their tvs.


>> If you want to actually lock down a machine to implement content
>> protection, then you need secure boot without unlockable boot-loader and a
>> pile more bits in userspace.  If you do all that, only then do you have
>> full content protection. And yes, then you don't really own the machine
>> fully, and I think users who are concerned with being able to update
>> their
>
> Yes, so... This patch makes it more likely to see machines with locked
> down kernels, preventing developers from working with systems their
> own, running hardware. That is evil, and direct threat to Free
> software movement.
>
> Users compiling their own kernels get no benefit from it. Actually it
> looks like this only benefits Intel and Disney. We don't want that.
>

Major citation needed here. Did you actually read the code? If you
did, you would realize that the feature is already latent in your
computer. This patchset merely exposes how that hardware can be
enabled to encrypt your video link. Would you have the same problems
with a new whizzbang video encoding format, or is it just the "Content
Protection" name? Perhaps you'd prefer this feature was implemented in
Intel's firmware, or a userspace blob? It wouldn't change the fact
that those registers exist and _can_ be used for HDCP, it's just that
now you know about it.

Having all of the code in the open allows users to see what is
happening with their hardware, how is this a bad thing?

Sean


>> kernels and be able to exercise their software freedoms already know to
>> avoid such locked down systems.
>>
>> So yeah it would be better to call this the "HDMI/DP cable encryption
>> support", but well, it's not what it's called really.
>
> Well, it does not belong in kernel, no matter what is the name.
>
>                                                                         Pavel
> --
> (english) http://www.livejournal.com/~pavelmachek
> (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-05 19:03           ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2017-12-05 19:03 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Dec 5, 2017 at 12:34 PM, Pavel Machek <pavel@ucw.cz> wrote:
> On Tue 2017-12-05 11:45:38, Daniel Vetter wrote:
>> On Tue, Dec 05, 2017 at 11:28:40AM +0100, Pavel Machek wrote:
>> > On Wed 2017-11-29 22:08:56, Sean Paul wrote:
>> > > This patch adds a new optional connector property to allow userspace to enable
>> > > protection over the content it is displaying. This will typically be implemented
>> > > by the driver using HDCP.
>> > >
>> > > The property is a tri-state with the following values:
>> > > - OFF: Self explanatory, no content protection
>> > > - DESIRED: Userspace requests that the driver enable protection
>> > > - ENABLED: Once the driver has authenticated the link, it sets this value
>> > >
>> > > The driver is responsible for downgrading ENABLED to DESIRED if the link becomes
>> > > unprotected. The driver should also maintain the desiredness of protection
>> > > across hotplug/dpms/suspend.
>> >
>> > Why would user of the machine want this to be something else than
>> > 'OFF'?
>> >
>> > If kernel implements this, will it mean hardware vendors will have to
>> > prevent user from updating kernel on machines they own?
>> >
>> > If this is merged, does it open kernel developers to DMCA threats if
>> > they try to change it?
>>
>> Because this just implements one part of the content protection scheme.
>> This only gives you an option to enable HDCP (aka encryption, it's really
>> nothing else) on the cable. Just because it has Content Protection in the
>> name does _not_ mean it is (stand-alone) an effective nor complete content
>> protection scheme. It's simply encrypting data, that's all.
>
> Yep. So my first question was: why would user of the machine ever want
> encryption "ENABLED" or "DESIRED"? Could you answer it?
>

Sure. We have a lot of Chrome OS users who would really like to enjoy
premium hd content on their tvs.


>> If you want to actually lock down a machine to implement content
>> protection, then you need secure boot without unlockable boot-loader and a
>> pile more bits in userspace.  If you do all that, only then do you have
>> full content protection. And yes, then you don't really own the machine
>> fully, and I think users who are concerned with being able to update
>> their
>
> Yes, so... This patch makes it more likely to see machines with locked
> down kernels, preventing developers from working with systems their
> own, running hardware. That is evil, and direct threat to Free
> software movement.
>
> Users compiling their own kernels get no benefit from it. Actually it
> looks like this only benefits Intel and Disney. We don't want that.
>

Major citation needed here. Did you actually read the code? If you
did, you would realize that the feature is already latent in your
computer. This patchset merely exposes how that hardware can be
enabled to encrypt your video link. Would you have the same problems
with a new whizzbang video encoding format, or is it just the "Content
Protection" name? Perhaps you'd prefer this feature was implemented in
Intel's firmware, or a userspace blob? It wouldn't change the fact
that those registers exist and _can_ be used for HDCP, it's just that
now you know about it.

Having all of the code in the open allows users to see what is
happening with their hardware, how is this a bad thing?

Sean


>> kernels and be able to exercise their software freedoms already know to
>> avoid such locked down systems.
>>
>> So yeah it would be better to call this the "HDMI/DP cable encryption
>> support", but well, it's not what it's called really.
>
> Well, it does not belong in kernel, no matter what is the name.
>
>                                                                         Pavel
> --
> (english) http://www.livejournal.com/~pavelmachek
> (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
  2017-12-05 17:34         ` Pavel Machek
  (?)
@ 2017-12-05 20:14           ` Daniel Stone
  -1 siblings, 0 replies; 62+ messages in thread
From: Daniel Stone @ 2017-12-05 20:14 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Sean Paul, David Airlie, intel-gfx, Linux Kernel Mailing List,
	linux-mediatek, dri-devel, Daniel Vetter, linux-arm-kernel

Hi Pavel,

On 5 December 2017 at 17:34, Pavel Machek <pavel@ucw.cz> wrote:
> Yes, so... This patch makes it more likely to see machines with locked
> down kernels, preventing developers from working with systems their
> own, running hardware. That is evil, and direct threat to Free
> software movement.
>
> Users compiling their own kernels get no benefit from it. Actually it
> looks like this only benefits Intel and Disney. We don't want that.

With all due respect, you can't claim to speak for the entire kernel
and FLOSS community of users and developers.

The feature is optional: it does not enforce additional constraints on
users, but exposes additional functionality already present in
hardware, for those who wish to opt in to it. Those who wish to avoid
it can do so, by simply not making active use of it.

Cheers,
Daniel

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-05 20:14           ` Daniel Stone
  0 siblings, 0 replies; 62+ messages in thread
From: Daniel Stone @ 2017-12-05 20:14 UTC (permalink / raw)
  To: Pavel Machek
  Cc: David Airlie, intel-gfx, Linux Kernel Mailing List, dri-devel,
	linux-mediatek, Daniel Vetter, linux-arm-kernel

Hi Pavel,

On 5 December 2017 at 17:34, Pavel Machek <pavel@ucw.cz> wrote:
> Yes, so... This patch makes it more likely to see machines with locked
> down kernels, preventing developers from working with systems their
> own, running hardware. That is evil, and direct threat to Free
> software movement.
>
> Users compiling their own kernels get no benefit from it. Actually it
> looks like this only benefits Intel and Disney. We don't want that.

With all due respect, you can't claim to speak for the entire kernel
and FLOSS community of users and developers.

The feature is optional: it does not enforce additional constraints on
users, but exposes additional functionality already present in
hardware, for those who wish to opt in to it. Those who wish to avoid
it can do so, by simply not making active use of it.

Cheers,
Daniel
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-05 20:14           ` Daniel Stone
  0 siblings, 0 replies; 62+ messages in thread
From: Daniel Stone @ 2017-12-05 20:14 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Pavel,

On 5 December 2017 at 17:34, Pavel Machek <pavel@ucw.cz> wrote:
> Yes, so... This patch makes it more likely to see machines with locked
> down kernels, preventing developers from working with systems their
> own, running hardware. That is evil, and direct threat to Free
> software movement.
>
> Users compiling their own kernels get no benefit from it. Actually it
> looks like this only benefits Intel and Disney. We don't want that.

With all due respect, you can't claim to speak for the entire kernel
and FLOSS community of users and developers.

The feature is optional: it does not enforce additional constraints on
users, but exposes additional functionality already present in
hardware, for those who wish to opt in to it. Those who wish to avoid
it can do so, by simply not making active use of it.

Cheers,
Daniel

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
  2017-12-05 10:45       ` Daniel Vetter
  (?)
@ 2017-12-07 14:30         ` Alan Cox
  -1 siblings, 0 replies; 62+ messages in thread
From: Alan Cox @ 2017-12-07 14:30 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: Pavel Machek, Sean Paul, David Airlie, intel-gfx, linux-kernel,
	linux-mediatek, dri-devel, Daniel Vetter, linux-arm-kernel

> If you want to actually lock down a machine to implement content
> protection, then you need secure boot without unlockable boot-loader and a
> pile more bits in userspace. 

So let me take my Intel hat off for a moment.

The upstream policy has always been that we don't merge things which
don't have an open usable user space. Is the HDCP encryption feature
useful on its own ? What do users get from it ?

If this is just an enabler for a lump of binary stuff in ChromeOS then I
don't think it belongs, if it is useful standalone then it seems it does
belong ?

Alan

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-07 14:30         ` Alan Cox
  0 siblings, 0 replies; 62+ messages in thread
From: Alan Cox @ 2017-12-07 14:30 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: David Airlie, intel-gfx, linux-kernel, dri-devel, linux-mediatek,
	Pavel Machek, Daniel Vetter, Sean Paul, linux-arm-kernel

> If you want to actually lock down a machine to implement content
> protection, then you need secure boot without unlockable boot-loader and a
> pile more bits in userspace. 

So let me take my Intel hat off for a moment.

The upstream policy has always been that we don't merge things which
don't have an open usable user space. Is the HDCP encryption feature
useful on its own ? What do users get from it ?

If this is just an enabler for a lump of binary stuff in ChromeOS then I
don't think it belongs, if it is useful standalone then it seems it does
belong ?

Alan

^ permalink raw reply	[flat|nested] 62+ messages in thread

* [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-07 14:30         ` Alan Cox
  0 siblings, 0 replies; 62+ messages in thread
From: Alan Cox @ 2017-12-07 14:30 UTC (permalink / raw)
  To: linux-arm-kernel

> If you want to actually lock down a machine to implement content
> protection, then you need secure boot without unlockable boot-loader and a
> pile more bits in userspace. 

So let me take my Intel hat off for a moment.

The upstream policy has always been that we don't merge things which
don't have an open usable user space. Is the HDCP encryption feature
useful on its own ? What do users get from it ?

If this is just an enabler for a lump of binary stuff in ChromeOS then I
don't think it belongs, if it is useful standalone then it seems it does
belong ?

Alan

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-07 14:32             ` Alan Cox
  0 siblings, 0 replies; 62+ messages in thread
From: Alan Cox @ 2017-12-07 14:32 UTC (permalink / raw)
  To: Alex Deucher
  Cc: Pavel Machek, Sean Paul, David Airlie,
	Intel Graphics Development, LKML, linux-mediatek,
	Maling list - DRI developers, Daniel Vetter,
	linux-arm-kernel@lists.infradead.org"
	<linux-arm-kernel@lists.infradead.org>

> How about for sensitive video streams in government offices where you
> want to avoid a spy potentially tapping the cable to see the video
> stream?

Last time I checked HDCP did not meet government security requirements -
which is hardly surprising since you can buy $10 boxes from China to
de-hdcp video streams 8)

Alan

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-07 14:32             ` Alan Cox
  0 siblings, 0 replies; 62+ messages in thread
From: Alan Cox @ 2017-12-07 14:32 UTC (permalink / raw)
  To: Alex Deucher
  Cc: David Airlie, Intel Graphics Development, LKML,
	Maling list - DRI developers,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Pavel Machek,
	Daniel Vetter,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org"
	       
	<linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org>,
	Sean Paul

> How about for sensitive video streams in government offices where you
> want to avoid a spy potentially tapping the cable to see the video
> stream?

Last time I checked HDCP did not meet government security requirements -
which is hardly surprising since you can buy $10 boxes from China to
de-hdcp video streams 8)

Alan

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
  2017-12-07 14:30         ` Alan Cox
  (?)
@ 2017-12-08  8:55           ` Daniel Vetter
  -1 siblings, 0 replies; 62+ messages in thread
From: Daniel Vetter @ 2017-12-08  8:55 UTC (permalink / raw)
  To: Alan Cox
  Cc: Daniel Vetter, Pavel Machek, Sean Paul, David Airlie, intel-gfx,
	linux-kernel, linux-mediatek, dri-devel, Daniel Vetter,
	linux-arm-kernel

On Thu, Dec 07, 2017 at 02:30:52PM +0000, Alan Cox wrote:
> > If you want to actually lock down a machine to implement content
> > protection, then you need secure boot without unlockable boot-loader and a
> > pile more bits in userspace. 
> 
> So let me take my Intel hat off for a moment.
> 
> The upstream policy has always been that we don't merge things which
> don't have an open usable user space. Is the HDCP encryption feature
> useful on its own ? What do users get from it ?
> 
> If this is just an enabler for a lump of binary stuff in ChromeOS then I
> don't think it belongs, if it is useful standalone then it seems it does
> belong ?

The cros side is ofc all open source. dri-devel is extremely strict with
not taking anything that doesn't fullfil this requirement, probably more
strict than anyone else. Sean has the link in the cover letter of his
patch series.

For more context, here's our documented expectations about the userspace
side of any uapi addition to drm:

https://dri.freedesktop.org/docs/drm/gpu/drm-uapi.html#open-source-userspace-requirements

Cheers, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-08  8:55           ` Daniel Vetter
  0 siblings, 0 replies; 62+ messages in thread
From: Daniel Vetter @ 2017-12-08  8:55 UTC (permalink / raw)
  To: Alan Cox
  Cc: David Airlie, intel-gfx, linux-kernel, dri-devel, Pavel Machek,
	Daniel Vetter, linux-mediatek, linux-arm-kernel

On Thu, Dec 07, 2017 at 02:30:52PM +0000, Alan Cox wrote:
> > If you want to actually lock down a machine to implement content
> > protection, then you need secure boot without unlockable boot-loader and a
> > pile more bits in userspace. 
> 
> So let me take my Intel hat off for a moment.
> 
> The upstream policy has always been that we don't merge things which
> don't have an open usable user space. Is the HDCP encryption feature
> useful on its own ? What do users get from it ?
> 
> If this is just an enabler for a lump of binary stuff in ChromeOS then I
> don't think it belongs, if it is useful standalone then it seems it does
> belong ?

The cros side is ofc all open source. dri-devel is extremely strict with
not taking anything that doesn't fullfil this requirement, probably more
strict than anyone else. Sean has the link in the cover letter of his
patch series.

For more context, here's our documented expectations about the userspace
side of any uapi addition to drm:

https://dri.freedesktop.org/docs/drm/gpu/drm-uapi.html#open-source-userspace-requirements

Cheers, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* [RFC PATCH 1/6] drm: Add Content Protection property
@ 2017-12-08  8:55           ` Daniel Vetter
  0 siblings, 0 replies; 62+ messages in thread
From: Daniel Vetter @ 2017-12-08  8:55 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Dec 07, 2017 at 02:30:52PM +0000, Alan Cox wrote:
> > If you want to actually lock down a machine to implement content
> > protection, then you need secure boot without unlockable boot-loader and a
> > pile more bits in userspace. 
> 
> So let me take my Intel hat off for a moment.
> 
> The upstream policy has always been that we don't merge things which
> don't have an open usable user space. Is the HDCP encryption feature
> useful on its own ? What do users get from it ?
> 
> If this is just an enabler for a lump of binary stuff in ChromeOS then I
> don't think it belongs, if it is useful standalone then it seems it does
> belong ?

The cros side is ofc all open source. dri-devel is extremely strict with
not taking anything that doesn't fullfil this requirement, probably more
strict than anyone else. Sean has the link in the cover letter of his
patch series.

For more context, here's our documented expectations about the userspace
side of any uapi addition to drm:

https://dri.freedesktop.org/docs/drm/gpu/drm-uapi.html#open-source-userspace-requirements

Cheers, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 62+ messages in thread

end of thread, other threads:[~2017-12-08  8:55 UTC | newest]

Thread overview: 62+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-11-30  3:08 [RFC PATCH 0/6] drm/i915: Implement HDCP Sean Paul
2017-11-30  3:08 ` [RFC PATCH 1/6] drm: Add Content Protection property Sean Paul
2017-11-30  3:08   ` Sean Paul
2017-11-30  3:08   ` Sean Paul
2017-12-05 10:28   ` Pavel Machek
2017-12-05 10:28     ` Pavel Machek
2017-12-05 10:28     ` Pavel Machek
2017-12-05 10:45     ` Daniel Vetter
2017-12-05 10:45       ` Daniel Vetter
2017-12-05 10:45       ` Daniel Vetter
2017-12-05 17:34       ` Pavel Machek
2017-12-05 17:34         ` Pavel Machek
2017-12-05 17:34         ` Pavel Machek
2017-12-05 17:53         ` Alex Deucher
2017-12-05 17:53           ` Alex Deucher
2017-12-05 17:53           ` Alex Deucher
2017-12-05 18:01           ` Pavel Machek
2017-12-05 18:01             ` Pavel Machek
2017-12-05 18:01             ` Pavel Machek
2017-12-07 14:32           ` Alan Cox
2017-12-07 14:32             ` Alan Cox
2017-12-05 19:03         ` Sean Paul
2017-12-05 19:03           ` Sean Paul
2017-12-05 19:03           ` Sean Paul
2017-12-05 20:14         ` Daniel Stone
2017-12-05 20:14           ` Daniel Stone
2017-12-05 20:14           ` Daniel Stone
2017-12-07 14:30       ` Alan Cox
2017-12-07 14:30         ` Alan Cox
2017-12-07 14:30         ` Alan Cox
2017-12-08  8:55         ` Daniel Vetter
2017-12-08  8:55           ` Daniel Vetter
2017-12-08  8:55           ` Daniel Vetter
2017-11-30  3:08 ` [RFC PATCH 2/6] drm: Add some HDCP related #defines Sean Paul
2017-11-30  3:08   ` Sean Paul
2017-11-30  3:08 ` [RFC PATCH 3/6] drm/i915: Add HDCP framework + base implementation Sean Paul
2017-11-30  3:08   ` Sean Paul
2017-11-30  9:12   ` [Intel-gfx] " Chris Wilson
2017-12-01  7:23   ` Ramalingam C
2017-12-01  7:23     ` Ramalingam C
2017-12-01  7:36     ` [Intel-gfx] " Daniel Vetter
2017-12-01  7:36       ` Daniel Vetter
2017-12-01  8:36       ` [Intel-gfx] " Ramalingam C
2017-12-01  8:36         ` Ramalingam C
2017-12-01 14:13         ` Sean Paul
2017-12-01 14:13           ` Sean Paul
2017-12-01 14:12       ` [Intel-gfx] " Sean Paul
2017-12-01 14:16       ` Sean Paul
2017-12-01 14:16         ` Sean Paul
2017-11-30  3:08 ` [RFC PATCH 4/6] drm/i915: Add function to output Aksv over GMBUS Sean Paul
2017-11-30  3:08   ` Sean Paul
2017-11-30  3:09 ` [RFC PATCH 5/6] drm/i915: Implement HDCP for HDMI Sean Paul
2017-11-30  3:09   ` Sean Paul
2017-12-01  7:31   ` Ramalingam C
2017-12-01  7:31     ` Ramalingam C
2017-11-30  3:09 ` [RFC PATCH 6/6] drm/i915: Implement HDCP for DisplayPort Sean Paul
2017-11-30  7:50 ` [Intel-gfx] [RFC PATCH 0/6] drm/i915: Implement HDCP Daniel Vetter
2017-12-05 13:45   ` Ville Syrjälä
2017-12-05 14:45     ` Sean Paul
2017-11-30  9:07 ` ✗ Fi.CI.BAT: failure for " Patchwork
2017-11-30 10:05 ` Patchwork
2017-11-30 15:15 ` Patchwork

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.