All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/9] drm/i915: Implement HDCP
@ 2017-12-05  5:14 ` Sean Paul
  0 siblings, 0 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:14 UTC (permalink / raw)
  To: dri-devel, intel-gfx; +Cc: linux-kernel, daniel.vetter, seanpaul, Sean Paul

Oh, hello there. Here's v3 of the HDCP patchset.

Highlights of v3 are:
- Add atomic_check/commit helpers to intel_hdcp to handle state transitions and
  call enable/disable at the right time.
- intel_hdcp_check_link() gets moved again to avoid being called with locks held
- Split out setting the property from the check worker into its own dedicated
  worker. This avoids deadlock when doing a synchronous cancel with mode_config
  lock(s) held.
- Add internal connector->hdcp_value to track actual state of hardware
- Add "indexed writes" to GMBUS driver
- Rebase on drm-tip to pick up new wait_for
- Tested with igt patch

Thanks!

Sean


Sean Paul (9):
  drm: Fix link-status kerneldoc line lengths
  drm/i915: Add more control to wait_for routines
  drm: Add Content Protection property
  drm: Add some HDCP related #defines
  drm/i915: Add HDCP framework + base implementation
  drm/i915: Make use of indexed write GMBUS feature
  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      |  80 +++-
 drivers/gpu/drm/drm_sysfs.c          |   1 +
 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  |   2 +
 drivers/gpu/drm/i915/intel_ddi.c     |  50 +++
 drivers/gpu/drm/i915/intel_display.c |  14 +
 drivers/gpu/drm/i915/intel_dp.c      | 248 +++++++++++-
 drivers/gpu/drm/i915/intel_drv.h     | 107 ++++-
 drivers/gpu/drm/i915/intel_hdcp.c    | 731 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_hdmi.c    | 257 ++++++++++++
 drivers/gpu/drm/i915/intel_i2c.c     |  86 ++++-
 drivers/gpu/drm/i915/intel_uncore.c  |  23 +-
 drivers/gpu/drm/i915/intel_uncore.h  |  14 +-
 include/drm/drm_connector.h          |  16 +
 include/drm/drm_dp_helper.h          |  17 +
 include/drm/drm_hdcp.h               |  56 +++
 include/uapi/drm/drm_mode.h          |   4 +
 20 files changed, 1767 insertions(+), 34 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

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

* [PATCH v3 0/9] drm/i915: Implement HDCP
@ 2017-12-05  5:14 ` Sean Paul
  0 siblings, 0 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:14 UTC (permalink / raw)
  To: dri-devel, intel-gfx; +Cc: daniel.vetter, linux-kernel, seanpaul

Oh, hello there. Here's v3 of the HDCP patchset.

Highlights of v3 are:
- Add atomic_check/commit helpers to intel_hdcp to handle state transitions and
  call enable/disable at the right time.
- intel_hdcp_check_link() gets moved again to avoid being called with locks held
- Split out setting the property from the check worker into its own dedicated
  worker. This avoids deadlock when doing a synchronous cancel with mode_config
  lock(s) held.
- Add internal connector->hdcp_value to track actual state of hardware
- Add "indexed writes" to GMBUS driver
- Rebase on drm-tip to pick up new wait_for
- Tested with igt patch

Thanks!

Sean


Sean Paul (9):
  drm: Fix link-status kerneldoc line lengths
  drm/i915: Add more control to wait_for routines
  drm: Add Content Protection property
  drm: Add some HDCP related #defines
  drm/i915: Add HDCP framework + base implementation
  drm/i915: Make use of indexed write GMBUS feature
  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      |  80 +++-
 drivers/gpu/drm/drm_sysfs.c          |   1 +
 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  |   2 +
 drivers/gpu/drm/i915/intel_ddi.c     |  50 +++
 drivers/gpu/drm/i915/intel_display.c |  14 +
 drivers/gpu/drm/i915/intel_dp.c      | 248 +++++++++++-
 drivers/gpu/drm/i915/intel_drv.h     | 107 ++++-
 drivers/gpu/drm/i915/intel_hdcp.c    | 731 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_hdmi.c    | 257 ++++++++++++
 drivers/gpu/drm/i915/intel_i2c.c     |  86 ++++-
 drivers/gpu/drm/i915/intel_uncore.c  |  23 +-
 drivers/gpu/drm/i915/intel_uncore.h  |  14 +-
 include/drm/drm_connector.h          |  16 +
 include/drm/drm_dp_helper.h          |  17 +
 include/drm/drm_hdcp.h               |  56 +++
 include/uapi/drm/drm_mode.h          |   4 +
 20 files changed, 1767 insertions(+), 34 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

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

* [PATCH v3 1/9] drm: Fix link-status kerneldoc line lengths
  2017-12-05  5:14 ` Sean Paul
@ 2017-12-05  5:15   ` Sean Paul
  -1 siblings, 0 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:15 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: linux-kernel, daniel.vetter, seanpaul, Sean Paul, Manasi Navare,
	Jani Nikula, David Airlie

I'm adding some stuff below it and it's killing my editor's vibe.

Changes in v2:
- Added to the series
Changes in v3:
- None

Cc: Manasi Navare <manasi.d.navare@intel.com>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Sean Paul <seanpaul@chromium.org>
---
 drivers/gpu/drm/drm_connector.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 704fc8934616..f14b48e6e839 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -759,10 +759,11 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
  * 	should update this value using drm_mode_connector_set_tile_property().
  * 	Userspace cannot change this property.
  * link-status:
- *      Connector link-status property to indicate the status of link. The default
- *      value of link-status is "GOOD". If something fails during or after modeset,
- *      the kernel driver may set this to "BAD" and issue a hotplug uevent. Drivers
- *      should update this value using drm_mode_connector_set_link_status_property().
+ *      Connector link-status property to indicate the status of link. The
+ *      default value of link-status is "GOOD". If something fails during or
+ *      after modeset, the kernel driver may set this to "BAD" and issue a
+ *      hotplug uevent. Drivers should update this value using
+ *      drm_mode_connector_set_link_status_property().
  *
  * Connectors also have one standardized atomic property:
  *
-- 
2.15.0.531.g2ccb3012c9-goog

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

* [PATCH v3 1/9] drm: Fix link-status kerneldoc line lengths
@ 2017-12-05  5:15   ` Sean Paul
  0 siblings, 0 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:15 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: seanpaul, David Airlie, linux-kernel, Manasi Navare, daniel.vetter

I'm adding some stuff below it and it's killing my editor's vibe.

Changes in v2:
- Added to the series
Changes in v3:
- None

Cc: Manasi Navare <manasi.d.navare@intel.com>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Sean Paul <seanpaul@chromium.org>
---
 drivers/gpu/drm/drm_connector.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 704fc8934616..f14b48e6e839 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -759,10 +759,11 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
  * 	should update this value using drm_mode_connector_set_tile_property().
  * 	Userspace cannot change this property.
  * link-status:
- *      Connector link-status property to indicate the status of link. The default
- *      value of link-status is "GOOD". If something fails during or after modeset,
- *      the kernel driver may set this to "BAD" and issue a hotplug uevent. Drivers
- *      should update this value using drm_mode_connector_set_link_status_property().
+ *      Connector link-status property to indicate the status of link. The
+ *      default value of link-status is "GOOD". If something fails during or
+ *      after modeset, the kernel driver may set this to "BAD" and issue a
+ *      hotplug uevent. Drivers should update this value using
+ *      drm_mode_connector_set_link_status_property().
  *
  * Connectors also have one standardized atomic property:
  *
-- 
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] 55+ messages in thread

* [PATCH v3 2/9] drm/i915: Add more control to wait_for routines
  2017-12-05  5:14 ` Sean Paul
@ 2017-12-05  5:15   ` Sean Paul
  -1 siblings, 0 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:15 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: linux-kernel, daniel.vetter, seanpaul, Sean Paul, Jani Nikula,
	Joonas Lahtinen, Rodrigo Vivi, David Airlie

This patch adds a little more control to a couple wait_for routines such
that we can avoid open-coding read/wait/timeout patterns which:
 - need the value of the register after the wait_for
 - run arbitrary operation for the read portion

This patch also chooses the correct sleep function (based on
timers-howto.txt) for the polling interval the caller specifies.

Changes in v2:
- Added to the series
Changes in v3:
- Rebased on drm-intel-next-queued and the new Wmin/max _wait_for
- Removed msleep option

Suggested-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Sean Paul <seanpaul@chromium.org>
---
 drivers/gpu/drm/i915/intel_drv.h    | 17 ++++++++++-------
 drivers/gpu/drm/i915/intel_uncore.c | 23 ++++++++++++++++-------
 drivers/gpu/drm/i915/intel_uncore.h | 14 +++++++++++++-
 3 files changed, 39 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 64426d3e078e..852b3d161754 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -41,20 +41,21 @@
 #include <drm/drm_atomic.h>
 
 /**
- * _wait_for - magic (register) wait macro
+ * __wait_for - magic wait macro
  *
- * Does the right thing for modeset paths when run under kdgb or similar atomic
- * contexts. Note that it's important that we check the condition again after
- * having timed out, since the timeout could be due to preemption or similar and
- * we've never had a chance to check the condition before the timeout.
+ * Macro to help avoid open coding check/wait/timeout patterns. Note that it's
+ * important that we check the condition again after having timed out, since the
+ * timeout could be due to preemption or similar and we've never had a chance to
+ * check the condition before the timeout.
  */
-#define _wait_for(COND, US, Wmin, Wmax) ({ \
+#define __wait_for(OP, COND, US, Wmin, Wmax) ({ \
 	unsigned long timeout__ = jiffies + usecs_to_jiffies(US) + 1;	\
 	long wait__ = (Wmin); /* recommended min for usleep is 10 us */	\
 	int ret__;							\
 	might_sleep();							\
 	for (;;) {							\
 		bool expired__ = time_after(jiffies, timeout__);	\
+		OP;							\
 		if (COND) {						\
 			ret__ = 0;					\
 			break;						\
@@ -70,7 +71,9 @@
 	ret__;								\
 })
 
-#define wait_for(COND, MS)	_wait_for((COND), (MS) * 1000, 10, 1000)
+#define _wait_for(COND, US, Wmin, Wmax)	__wait_for(;, (COND), (US), (Wmin), \
+						   (Wmax))
+#define wait_for(COND, MS)		_wait_for((COND), (MS) * 1000, 10, 1000)
 
 /* If CONFIG_PREEMPT_COUNT is disabled, in_atomic() always reports false. */
 #if defined(CONFIG_DRM_I915_DEBUG) && defined(CONFIG_PREEMPT_COUNT)
diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index b4621271e7a2..9c7d07151f16 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -1770,12 +1770,14 @@ int __intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
 }
 
 /**
- * intel_wait_for_register - wait until register matches expected state
+ * __intel_wait_for_register - wait until register matches expected state
  * @dev_priv: the i915 device
  * @reg: the register to read
  * @mask: mask to apply to register value
  * @value: expected value
- * @timeout_ms: timeout in millisecond
+ * @fast_timeout_us: fast timeout in microsecond for atomic/tight wait
+ * @slow_timeout_ms: slow timeout in millisecond
+ * @out_value: optional placeholder to hold registry value
  *
  * This routine waits until the target register @reg contains the expected
  * @value after applying the @mask, i.e. it waits until ::
@@ -1786,15 +1788,18 @@ int __intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
  *
  * Returns 0 if the register matches the desired condition, or -ETIMEOUT.
  */
-int intel_wait_for_register(struct drm_i915_private *dev_priv,
+int __intel_wait_for_register(struct drm_i915_private *dev_priv,
 			    i915_reg_t reg,
 			    u32 mask,
 			    u32 value,
-			    unsigned int timeout_ms)
+			    unsigned int fast_timeout_us,
+			    unsigned int slow_timeout_ms,
+			    u32 *out_value)
 {
 	unsigned fw =
 		intel_uncore_forcewake_for_reg(dev_priv, reg, FW_REG_READ);
 	int ret;
+	u32 reg_value;
 
 	might_sleep();
 
@@ -1803,14 +1808,18 @@ int intel_wait_for_register(struct drm_i915_private *dev_priv,
 
 	ret = __intel_wait_for_register_fw(dev_priv,
 					   reg, mask, value,
-					   2, 0, NULL);
+					   fast_timeout_us, 0, &reg_value);
 
 	intel_uncore_forcewake_put__locked(dev_priv, fw);
 	spin_unlock_irq(&dev_priv->uncore.lock);
 
 	if (ret)
-		ret = wait_for((I915_READ_NOTRACE(reg) & mask) == value,
-			       timeout_ms);
+		ret = __wait_for(reg_value = I915_READ_NOTRACE(reg),
+			         (reg_value & mask) == value,
+			         slow_timeout_ms * 1000, 10, 1000);
+
+	if (out_value)
+		*out_value = reg_value;
 
 	return ret;
 }
diff --git a/drivers/gpu/drm/i915/intel_uncore.h b/drivers/gpu/drm/i915/intel_uncore.h
index 9ce079b5dd0d..bed019ef000f 100644
--- a/drivers/gpu/drm/i915/intel_uncore.h
+++ b/drivers/gpu/drm/i915/intel_uncore.h
@@ -163,11 +163,23 @@ void intel_uncore_forcewake_put__locked(struct drm_i915_private *dev_priv,
 void intel_uncore_forcewake_user_get(struct drm_i915_private *dev_priv);
 void intel_uncore_forcewake_user_put(struct drm_i915_private *dev_priv);
 
+int __intel_wait_for_register(struct drm_i915_private *dev_priv,
+			      i915_reg_t reg,
+			      u32 mask,
+			      u32 value,
+			      unsigned int fast_timeout_us,
+			      unsigned int slow_timeout_ms,
+			      u32 *out_value);
+static inline
 int intel_wait_for_register(struct drm_i915_private *dev_priv,
 			    i915_reg_t reg,
 			    u32 mask,
 			    u32 value,
-			    unsigned int timeout_ms);
+			    unsigned int timeout_ms)
+{
+	return __intel_wait_for_register(dev_priv, reg, mask, value, 2,
+					 timeout_ms, NULL);
+}
 int __intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
 				 i915_reg_t reg,
 				 u32 mask,
-- 
2.15.0.531.g2ccb3012c9-goog

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

* [PATCH v3 2/9] drm/i915: Add more control to wait_for routines
@ 2017-12-05  5:15   ` Sean Paul
  0 siblings, 0 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:15 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: seanpaul, David Airlie, linux-kernel, Rodrigo Vivi, daniel.vetter

This patch adds a little more control to a couple wait_for routines such
that we can avoid open-coding read/wait/timeout patterns which:
 - need the value of the register after the wait_for
 - run arbitrary operation for the read portion

This patch also chooses the correct sleep function (based on
timers-howto.txt) for the polling interval the caller specifies.

Changes in v2:
- Added to the series
Changes in v3:
- Rebased on drm-intel-next-queued and the new Wmin/max _wait_for
- Removed msleep option

Suggested-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Sean Paul <seanpaul@chromium.org>
---
 drivers/gpu/drm/i915/intel_drv.h    | 17 ++++++++++-------
 drivers/gpu/drm/i915/intel_uncore.c | 23 ++++++++++++++++-------
 drivers/gpu/drm/i915/intel_uncore.h | 14 +++++++++++++-
 3 files changed, 39 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 64426d3e078e..852b3d161754 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -41,20 +41,21 @@
 #include <drm/drm_atomic.h>
 
 /**
- * _wait_for - magic (register) wait macro
+ * __wait_for - magic wait macro
  *
- * Does the right thing for modeset paths when run under kdgb or similar atomic
- * contexts. Note that it's important that we check the condition again after
- * having timed out, since the timeout could be due to preemption or similar and
- * we've never had a chance to check the condition before the timeout.
+ * Macro to help avoid open coding check/wait/timeout patterns. Note that it's
+ * important that we check the condition again after having timed out, since the
+ * timeout could be due to preemption or similar and we've never had a chance to
+ * check the condition before the timeout.
  */
-#define _wait_for(COND, US, Wmin, Wmax) ({ \
+#define __wait_for(OP, COND, US, Wmin, Wmax) ({ \
 	unsigned long timeout__ = jiffies + usecs_to_jiffies(US) + 1;	\
 	long wait__ = (Wmin); /* recommended min for usleep is 10 us */	\
 	int ret__;							\
 	might_sleep();							\
 	for (;;) {							\
 		bool expired__ = time_after(jiffies, timeout__);	\
+		OP;							\
 		if (COND) {						\
 			ret__ = 0;					\
 			break;						\
@@ -70,7 +71,9 @@
 	ret__;								\
 })
 
-#define wait_for(COND, MS)	_wait_for((COND), (MS) * 1000, 10, 1000)
+#define _wait_for(COND, US, Wmin, Wmax)	__wait_for(;, (COND), (US), (Wmin), \
+						   (Wmax))
+#define wait_for(COND, MS)		_wait_for((COND), (MS) * 1000, 10, 1000)
 
 /* If CONFIG_PREEMPT_COUNT is disabled, in_atomic() always reports false. */
 #if defined(CONFIG_DRM_I915_DEBUG) && defined(CONFIG_PREEMPT_COUNT)
diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index b4621271e7a2..9c7d07151f16 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -1770,12 +1770,14 @@ int __intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
 }
 
 /**
- * intel_wait_for_register - wait until register matches expected state
+ * __intel_wait_for_register - wait until register matches expected state
  * @dev_priv: the i915 device
  * @reg: the register to read
  * @mask: mask to apply to register value
  * @value: expected value
- * @timeout_ms: timeout in millisecond
+ * @fast_timeout_us: fast timeout in microsecond for atomic/tight wait
+ * @slow_timeout_ms: slow timeout in millisecond
+ * @out_value: optional placeholder to hold registry value
  *
  * This routine waits until the target register @reg contains the expected
  * @value after applying the @mask, i.e. it waits until ::
@@ -1786,15 +1788,18 @@ int __intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
  *
  * Returns 0 if the register matches the desired condition, or -ETIMEOUT.
  */
-int intel_wait_for_register(struct drm_i915_private *dev_priv,
+int __intel_wait_for_register(struct drm_i915_private *dev_priv,
 			    i915_reg_t reg,
 			    u32 mask,
 			    u32 value,
-			    unsigned int timeout_ms)
+			    unsigned int fast_timeout_us,
+			    unsigned int slow_timeout_ms,
+			    u32 *out_value)
 {
 	unsigned fw =
 		intel_uncore_forcewake_for_reg(dev_priv, reg, FW_REG_READ);
 	int ret;
+	u32 reg_value;
 
 	might_sleep();
 
@@ -1803,14 +1808,18 @@ int intel_wait_for_register(struct drm_i915_private *dev_priv,
 
 	ret = __intel_wait_for_register_fw(dev_priv,
 					   reg, mask, value,
-					   2, 0, NULL);
+					   fast_timeout_us, 0, &reg_value);
 
 	intel_uncore_forcewake_put__locked(dev_priv, fw);
 	spin_unlock_irq(&dev_priv->uncore.lock);
 
 	if (ret)
-		ret = wait_for((I915_READ_NOTRACE(reg) & mask) == value,
-			       timeout_ms);
+		ret = __wait_for(reg_value = I915_READ_NOTRACE(reg),
+			         (reg_value & mask) == value,
+			         slow_timeout_ms * 1000, 10, 1000);
+
+	if (out_value)
+		*out_value = reg_value;
 
 	return ret;
 }
diff --git a/drivers/gpu/drm/i915/intel_uncore.h b/drivers/gpu/drm/i915/intel_uncore.h
index 9ce079b5dd0d..bed019ef000f 100644
--- a/drivers/gpu/drm/i915/intel_uncore.h
+++ b/drivers/gpu/drm/i915/intel_uncore.h
@@ -163,11 +163,23 @@ void intel_uncore_forcewake_put__locked(struct drm_i915_private *dev_priv,
 void intel_uncore_forcewake_user_get(struct drm_i915_private *dev_priv);
 void intel_uncore_forcewake_user_put(struct drm_i915_private *dev_priv);
 
+int __intel_wait_for_register(struct drm_i915_private *dev_priv,
+			      i915_reg_t reg,
+			      u32 mask,
+			      u32 value,
+			      unsigned int fast_timeout_us,
+			      unsigned int slow_timeout_ms,
+			      u32 *out_value);
+static inline
 int intel_wait_for_register(struct drm_i915_private *dev_priv,
 			    i915_reg_t reg,
 			    u32 mask,
 			    u32 value,
-			    unsigned int timeout_ms);
+			    unsigned int timeout_ms)
+{
+	return __intel_wait_for_register(dev_priv, reg, mask, value, 2,
+					 timeout_ms, NULL);
+}
 int __intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
 				 i915_reg_t reg,
 				 u32 mask,
-- 
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] 55+ messages in thread

* [PATCH v3 3/9] drm: Add Content Protection property
  2017-12-05  5:14 ` Sean Paul
@ 2017-12-05  5:15   ` Sean Paul
  -1 siblings, 0 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:15 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: linux-kernel, daniel.vetter, seanpaul, Sean Paul, Jani Nikula,
	David Airlie

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.

Changes in v2:
 - Pimp kerneldoc for content_protection_property (Daniel)
 - Drop sysfs attribute
Changes in v3:
 - None

Cc: Daniel Vetter <daniel.vetter@intel.com>
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 | 71 +++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_sysfs.c     |  1 +
 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 c2da5585e201..676025d755b2 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1196,6 +1196,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);
@@ -1275,6 +1281,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 f14b48e6e839..8626aa8f485e 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
  *
@@ -764,6 +771,34 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
  *      after modeset, the kernel driver may set this to "BAD" and issue a
  *      hotplug uevent. Drivers should update this value using
  *      drm_mode_connector_set_link_status_property().
+ * Content Protection:
+ *	This property is used by userspace to request the kernel protect future
+ *	content communicated over the link. When requested, kernel will apply
+ *	the appropriate means of protection (most often HDCP), and use the
+ *	property to tell userspace the protection is active.
+ *
+ *	The value of this property can be one of the following:
+ *
+ *	- DRM_MODE_CONTENT_PROTECTION_OFF = 0
+ *		The link is not protected, content is transmitted in the clear.
+ *	- DRM_MODE_CONTENT_PROTECTION_DESIRED = 1
+ *		Userspace has requested content protection, but the link is not
+ *		currently protected. When in this state, kernel should enable
+ *		Content Protection as soon as possible.
+ *	- DRM_MODE_CONTENT_PROTECTION_ENABLED = 2
+ *		Userspace has requested content protection, and the link is
+ *		protected. Only the driver can set the property to this value.
+ *		If userspace attempts to set to ENABLED, kernel will return
+ *		-EINVAL.
+ *
+ *	A few guidelines:
+ *
+ *	- DESIRED state should be preserved until userspace de-asserts it by
+ *	  setting the property to OFF. This means ENABLED should only transition
+ *	  to OFF when the user explicitly requests it.
+ *	- If the state is DESIRED, kernel should attempt to re-authenticate the
+ *	  link whenever possible. This includes across disable/enable, dpms,
+ *	  hotplug, downstream device changes, link status failures, etc..
  *
  * Connectors also have one standardized atomic property:
  *
@@ -1047,6 +1082,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..2385c7e0bef5 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)
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 7a7140543012..828878addd03 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -370,6 +370,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;
 };
 
 /**
@@ -718,6 +724,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
@@ -808,6 +815,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:
 	 *
@@ -1002,6 +1015,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,
@@ -1010,6 +1024,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] 55+ messages in thread

* [PATCH v3 3/9] drm: Add Content Protection property
@ 2017-12-05  5:15   ` Sean Paul
  0 siblings, 0 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:15 UTC (permalink / raw)
  To: dri-devel, intel-gfx; +Cc: seanpaul, David Airlie, linux-kernel, daniel.vetter

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.

Changes in v2:
 - Pimp kerneldoc for content_protection_property (Daniel)
 - Drop sysfs attribute
Changes in v3:
 - None

Cc: Daniel Vetter <daniel.vetter@intel.com>
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 | 71 +++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_sysfs.c     |  1 +
 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 c2da5585e201..676025d755b2 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1196,6 +1196,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);
@@ -1275,6 +1281,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 f14b48e6e839..8626aa8f485e 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
  *
@@ -764,6 +771,34 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
  *      after modeset, the kernel driver may set this to "BAD" and issue a
  *      hotplug uevent. Drivers should update this value using
  *      drm_mode_connector_set_link_status_property().
+ * Content Protection:
+ *	This property is used by userspace to request the kernel protect future
+ *	content communicated over the link. When requested, kernel will apply
+ *	the appropriate means of protection (most often HDCP), and use the
+ *	property to tell userspace the protection is active.
+ *
+ *	The value of this property can be one of the following:
+ *
+ *	- DRM_MODE_CONTENT_PROTECTION_OFF = 0
+ *		The link is not protected, content is transmitted in the clear.
+ *	- DRM_MODE_CONTENT_PROTECTION_DESIRED = 1
+ *		Userspace has requested content protection, but the link is not
+ *		currently protected. When in this state, kernel should enable
+ *		Content Protection as soon as possible.
+ *	- DRM_MODE_CONTENT_PROTECTION_ENABLED = 2
+ *		Userspace has requested content protection, and the link is
+ *		protected. Only the driver can set the property to this value.
+ *		If userspace attempts to set to ENABLED, kernel will return
+ *		-EINVAL.
+ *
+ *	A few guidelines:
+ *
+ *	- DESIRED state should be preserved until userspace de-asserts it by
+ *	  setting the property to OFF. This means ENABLED should only transition
+ *	  to OFF when the user explicitly requests it.
+ *	- If the state is DESIRED, kernel should attempt to re-authenticate the
+ *	  link whenever possible. This includes across disable/enable, dpms,
+ *	  hotplug, downstream device changes, link status failures, etc..
  *
  * Connectors also have one standardized atomic property:
  *
@@ -1047,6 +1082,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..2385c7e0bef5 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)
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 7a7140543012..828878addd03 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -370,6 +370,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;
 };
 
 /**
@@ -718,6 +724,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
@@ -808,6 +815,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:
 	 *
@@ -1002,6 +1015,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,
@@ -1010,6 +1024,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

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

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

* [PATCH v3 4/9] drm: Add some HDCP related #defines
  2017-12-05  5:14 ` Sean Paul
@ 2017-12-05  5:15   ` Sean Paul
  -1 siblings, 0 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:15 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: linux-kernel, daniel.vetter, seanpaul, Sean Paul, Jani Nikula,
	David Airlie

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.

Changes in v2:
- drm_hdcp.h gets MIT license (Daniel)
Changes in v3:
 - None

Cc: Daniel Vetter <daniel.vetter@intel.com>
Signed-off-by: Sean Paul <seanpaul@chromium.org>
---
 include/drm/drm_dp_helper.h | 17 ++++++++++++++
 include/drm/drm_hdcp.h      | 56 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 73 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 8b9ac321c3bd..4b2640d54c70 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -815,6 +815,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..c9b2484240d4
--- /dev/null
+++ b/include/drm/drm_hdcp.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Sean Paul <seanpaul@chromium.org>
+ */
+
+#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] 55+ messages in thread

* [PATCH v3 4/9] drm: Add some HDCP related #defines
@ 2017-12-05  5:15   ` Sean Paul
  0 siblings, 0 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:15 UTC (permalink / raw)
  To: dri-devel, intel-gfx; +Cc: seanpaul, 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.

Changes in v2:
- drm_hdcp.h gets MIT license (Daniel)
Changes in v3:
 - None

Cc: Daniel Vetter <daniel.vetter@intel.com>
Signed-off-by: Sean Paul <seanpaul@chromium.org>
---
 include/drm/drm_dp_helper.h | 17 ++++++++++++++
 include/drm/drm_hdcp.h      | 56 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 73 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 8b9ac321c3bd..4b2640d54c70 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -815,6 +815,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..c9b2484240d4
--- /dev/null
+++ b/include/drm/drm_hdcp.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Sean Paul <seanpaul@chromium.org>
+ */
+
+#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

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

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

* [PATCH v3 5/9] drm/i915: Add HDCP framework + base implementation
  2017-12-05  5:14 ` Sean Paul
@ 2017-12-05  5:15   ` Sean Paul
  -1 siblings, 0 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:15 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: linux-kernel, daniel.vetter, seanpaul, Sean Paul, Chris Wilson,
	Ramalingam C, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
	David Airlie

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.

Changes in v2:
- Don't open code wait_fors (Chris)
- drm_hdcp.c under MIT license (Daniel)
- Move intel_hdcp_disable() call above ddi_disable (Ram)
- Fix // comments (I wore a cone of shame for 12 hours to atone) (Daniel)
- Justify intel_hdcp_shim with comments (Daniel)
- Fixed async locking issues by adding hdcp_mutex (Daniel)
- Don't alter connector_state in enable/disable (Daniel)
Changes in v3:
- Added hdcp_mutex/hdcp_value to make async reasonable
- Added hdcp_prop_work to separate link checking & property setting
- Added new helper for atomic_check state tracking (Daniel)
- Moved enable/disable into atomic_commit with matching helpers
- Moved intel_hdcp_check_link out of all locks when called from dp
- Bumped up ksv_fifo timeout (noticed failure on one of my dongles)

Cc: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Daniel Vetter <daniel.vetter@intel.com>
Cc: Ramalingam C <ramalingam.c@intel.com>
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  |   2 +
 drivers/gpu/drm/i915/intel_display.c |  14 +
 drivers/gpu/drm/i915/intel_drv.h     |  88 +++++
 drivers/gpu/drm/i915/intel_hdcp.c    | 731 +++++++++++++++++++++++++++++++++++
 6 files changed, 919 insertions(+)
 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 42bc8bd4ff06..3facea4eefdb 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -107,6 +107,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 09bf043c1c2e..2bd2cc8441d4 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -8034,6 +8034,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
@@ -8335,6 +8336,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..d452c327dc1d 100644
--- a/drivers/gpu/drm/i915/intel_atomic.c
+++ b/drivers/gpu/drm/i915/intel_atomic.c
@@ -110,6 +110,8 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
 		to_intel_digital_connector_state(old_state);
 	struct drm_crtc_state *crtc_state;
 
+	intel_hdcp_atomic_check(conn, old_state, new_state);
+
 	if (!new_state->crtc)
 		return 0;
 
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 601c23be8264..f45c468abf98 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -12319,6 +12319,8 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
 	struct drm_crtc *crtc;
+	struct drm_connector_state *old_conn_state, *new_conn_state;
+	struct drm_connector *connector;
 	struct intel_crtc_state *intel_cstate;
 	u64 put_domains[I915_MAX_PIPES] = {};
 	int i;
@@ -12408,9 +12410,17 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
 		}
 	}
 
+	for_each_oldnew_connector_in_state(state, connector, old_conn_state,
+					   new_conn_state, i)
+		intel_hdcp_atomic_pre_commit(connector, old_conn_state,
+					     new_conn_state);
+
 	/* Now enable the clocks, plane, pipe, and connectors that we set up. */
 	dev_priv->display.update_crtcs(state);
 
+	for_each_new_connector_in_state(state, connector, new_conn_state, i)
+		intel_hdcp_atomic_commit(connector, new_conn_state);
+
 	/* FIXME: We should call drm_atomic_helper_commit_hw_done() here
 	 * already, but still need the state for the delayed optimization. To
 	 * fix this:
@@ -15322,6 +15332,10 @@ static void intel_hpd_poll_fini(struct drm_device *dev)
 	for_each_intel_connector_iter(connector, &conn_iter) {
 		if (connector->modeset_retry_work.func)
 			cancel_work_sync(&connector->modeset_retry_work);
+		if (connector->hdcp_shim) {
+			cancel_delayed_work_sync(&connector->hdcp_check_work);
+			cancel_work_sync(&connector->hdcp_prop_work);
+		}
 	}
 	drm_connector_list_iter_end(&conn_iter);
 }
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 852b3d161754..6f47a4227f5f 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -301,6 +301,76 @@ struct intel_panel {
 	} backlight;
 };
 
+/*
+ * This structure serves as a translation layer between the generic HDCP code
+ * and the bus-specific code. What that means is that HDCP over HDMI differs
+ * from HDCP over DP, so to account for these differences, we need to
+ * communicate with the receiver through this shim.
+ *
+ * For completeness, the 2 buses differ in the following ways:
+ *	- DP AUX vs. DDC
+ *		HDCP registers on the receiver are set via DP AUX for DP, and
+ *		they are set via DDC for HDMI.
+ *	- Receiver register offsets
+ *		The offsets of the registers are different for DP vs. HDMI
+ *	- Receiver register masks/offsets
+ *		For instance, the ready bit for the KSV fifo is in a different
+ *		place on DP vs HDMI
+ *	- Receiver register names
+ *		Seriously. In the DP spec, the 16-bit register containing
+ *		downstream information is called BINFO, on HDMI it's called
+ *		BSTATUS. To confuse matters further, DP has a BSTATUS register
+ *		with a completely different definition.
+ *	- KSV FIFO
+ *		On HDMI, the ksv fifo is read all at once, whereas on DP it must
+ *		be read 3 keys at a time
+ *	- Aksv output
+ *		Since Aksv is hidden in hardware, there's different procedures
+ *		to send it over DP AUX vs DDC
+ */
+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;
 	/*
@@ -332,6 +402,12 @@ 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 mutex hdcp_mutex;
+	uint64_t hdcp_value; /* protected by hdcp_mutex */
+	struct delayed_work hdcp_check_work;
+	struct work_struct hdcp_prop_work;
 };
 
 struct intel_digital_connector_state {
@@ -1763,6 +1839,18 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
 }
 #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
 
+/* intel_hdcp.c */
+void intel_hdcp_atomic_check(struct drm_connector *connector,
+			     struct drm_connector_state *old_state,
+			     struct drm_connector_state *new_state);
+void intel_hdcp_atomic_pre_commit(struct drm_connector *connector,
+			          struct drm_connector_state *old_state,
+			          struct drm_connector_state *new_state);
+void intel_hdcp_atomic_commit(struct drm_connector *connector,
+			      struct drm_connector_state *new_state);
+int intel_hdcp_check_link(struct intel_connector *connector);
+void intel_hdcp_check_work(struct work_struct *work);
+void intel_hdcp_prop_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..4eef9505410f
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -0,0 +1,731 @@
+/*
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Sean Paul <seanpaul@chromium.org>
+ */
+
+#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)
+{
+	int ret, read_ret;
+	bool ksv_ready;
+
+	ret = __wait_for(read_ret = shim->read_ksv_ready(intel_dig_port,
+							 &ksv_ready),
+			 read_ret || ksv_ready, 1500 * 1000, 1000, 100 * 1000);
+	if (ret)
+		return ret;
+	if (read_ret)
+		return read_ret;
+	if (!ksv_ready)
+		return -ETIMEDOUT;
+
+	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)
+{
+	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) */
+	ret = __intel_wait_for_register(dev_priv, SKL_HDCP_KEY_STATUS,
+					SKL_HDCP_KEY_LOAD_DONE,
+					SKL_HDCP_KEY_LOAD_DONE,
+					10, 1, &val);
+	if (ret)
+		return ret;
+	else 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 (intel_wait_for_register(dev_priv, SKL_HDCP_REP_CTL,
+				    SKL_HDCP_SHA1_READY,
+				    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->base.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;
+
+	/* 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 (intel_wait_for_register(dev_priv, SKL_HDCP_REP_CTL,
+				    SKL_HDCP_SHA1_COMPLETE,
+				    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->base.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 (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port),
+				    SKL_HDCP_STATUS_AN_READY,
+				    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 (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port),
+				    SKL_HDCP_STATUS_ENC,
+				    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->base.port;
+	int ret;
+
+	I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
+	if (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port), ~0, 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_check_work(struct work_struct *work)
+{
+	struct intel_connector *connector = container_of(to_delayed_work(work),
+							 struct intel_connector,
+						         hdcp_check_work);
+	if (!intel_hdcp_check_link(connector))
+		schedule_delayed_work(&connector->hdcp_check_work,
+				      DRM_HDCP_CHECK_PERIOD_MS);
+}
+
+void intel_hdcp_prop_work(struct work_struct *work)
+{
+	struct intel_connector *connector = container_of(work,
+							 struct intel_connector,
+						         hdcp_prop_work);
+	struct drm_device *dev = connector->base.dev;
+	struct drm_connector_state *state;
+
+	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+	mutex_lock(&connector->hdcp_mutex);
+
+	/*
+	 * This worker is only used to flip between ENABLED/DESIRED. Either of
+	 * those to OFF is handled by core. If hdcp_value == OFF, we're running
+	 * just after hdcp has been disabled, so just exit
+	 */
+	if (connector->hdcp_value != DRM_MODE_CONTENT_PROTECTION_OFF) {
+		state = connector->base.state;
+		state->content_protection = connector->hdcp_value;
+	}
+
+	mutex_unlock(&connector->hdcp_mutex);
+	drm_modeset_unlock(&dev->mode_config.connection_mutex);
+	mutex_unlock(&dev->mode_config.mutex);
+}
+
+static int intel_hdcp_enable(struct intel_connector *connector)
+{
+	int ret;
+
+	if (!connector->hdcp_shim)
+		return -ENOENT;
+
+	mutex_lock(&connector->hdcp_mutex);
+
+	ret = _intel_hdcp_enable(connector);
+	if (ret)
+		goto out;
+
+	connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+	schedule_work(&connector->hdcp_prop_work);
+	schedule_delayed_work(&connector->hdcp_check_work,
+			      DRM_HDCP_CHECK_PERIOD_MS);
+out:
+	mutex_unlock(&connector->hdcp_mutex);
+	return ret;
+}
+
+static int intel_hdcp_disable(struct intel_connector *connector)
+{
+	int ret;
+
+	if (!connector->hdcp_shim)
+		return -ENOENT;
+
+	mutex_lock(&connector->hdcp_mutex);
+
+	connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_OFF;
+	ret = _intel_hdcp_disable(connector);
+
+	mutex_unlock(&connector->hdcp_mutex);
+	cancel_delayed_work_sync(&connector->hdcp_check_work);
+	return ret;
+}
+
+void intel_hdcp_atomic_check(struct drm_connector *connector,
+			     struct drm_connector_state *old_state,
+			     struct drm_connector_state *new_state)
+{
+	uint64_t old_cp = old_state->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;
+	}
+
+	/* Only drivers can set content protection enabled */
+	if (old_cp != DRM_MODE_CONTENT_PROTECTION_ENABLED &&
+	    new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
+		new_state->content_protection =
+			DRM_MODE_CONTENT_PROTECTION_DESIRED;
+}
+
+void intel_hdcp_atomic_pre_commit(struct drm_connector *connector,
+			          struct drm_connector_state *old_state,
+			          struct drm_connector_state *new_state)
+{
+	uint64_t old_cp = old_state->content_protection;
+	uint64_t new_cp = new_state->content_protection;
+
+	/*
+	 * Disable HDCP if the connector is becoming disabled, or if requested
+	 * via the property.
+	 */
+	if ((!new_state->crtc && old_cp != DRM_MODE_CONTENT_PROTECTION_OFF) ||
+	    (new_state->crtc && old_cp != DRM_MODE_CONTENT_PROTECTION_OFF &&
+	     new_cp == DRM_MODE_CONTENT_PROTECTION_OFF))
+		intel_hdcp_disable(to_intel_connector(connector));
+}
+
+void intel_hdcp_atomic_commit(struct drm_connector *connector,
+			      struct drm_connector_state *new_state)
+{
+	uint64_t new_cp = new_state->content_protection;
+
+	/* Enable hdcp if it's desired */
+	if (new_state->crtc && new_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED)
+		intel_hdcp_enable(to_intel_connector(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 intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+	enum port port = intel_dig_port->base.port;
+	int ret = 0;
+
+	if (!connector->hdcp_shim)
+		return -ENOENT;
+
+	mutex_lock(&connector->hdcp_mutex);
+
+	if (connector->hdcp_value == DRM_MODE_CONTENT_PROTECTION_OFF)
+		goto out;
+
+	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;
+		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+		schedule_work(&connector->hdcp_prop_work);
+		goto out;
+	}
+
+	if (connector->hdcp_shim->check_link(intel_dig_port)) {
+		if (connector->hdcp_value != DRM_MODE_CONTENT_PROTECTION_OFF) {
+			connector->hdcp_value =
+				DRM_MODE_CONTENT_PROTECTION_ENABLED;
+			schedule_work(&connector->hdcp_prop_work);
+		}
+		goto out;
+	}
+
+	DRM_INFO("HDCP link failed, retrying authentication\n");
+
+	ret = _intel_hdcp_disable(connector);
+	if (ret) {
+		DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
+		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+		schedule_work(&connector->hdcp_prop_work);
+		goto out;
+	}
+
+	ret = _intel_hdcp_enable(connector);
+	if (ret) {
+		DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
+		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+		schedule_work(&connector->hdcp_prop_work);
+		goto out;
+	}
+
+out:
+	mutex_unlock(&connector->hdcp_mutex);
+	return ret;
+}
-- 
2.15.0.531.g2ccb3012c9-goog

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

* [PATCH v3 5/9] drm/i915: Add HDCP framework + base implementation
@ 2017-12-05  5:15   ` Sean Paul
  0 siblings, 0 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:15 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: seanpaul, David Airlie, linux-kernel, Rodrigo Vivi, daniel.vetter

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.

Changes in v2:
- Don't open code wait_fors (Chris)
- drm_hdcp.c under MIT license (Daniel)
- Move intel_hdcp_disable() call above ddi_disable (Ram)
- Fix // comments (I wore a cone of shame for 12 hours to atone) (Daniel)
- Justify intel_hdcp_shim with comments (Daniel)
- Fixed async locking issues by adding hdcp_mutex (Daniel)
- Don't alter connector_state in enable/disable (Daniel)
Changes in v3:
- Added hdcp_mutex/hdcp_value to make async reasonable
- Added hdcp_prop_work to separate link checking & property setting
- Added new helper for atomic_check state tracking (Daniel)
- Moved enable/disable into atomic_commit with matching helpers
- Moved intel_hdcp_check_link out of all locks when called from dp
- Bumped up ksv_fifo timeout (noticed failure on one of my dongles)

Cc: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Daniel Vetter <daniel.vetter@intel.com>
Cc: Ramalingam C <ramalingam.c@intel.com>
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  |   2 +
 drivers/gpu/drm/i915/intel_display.c |  14 +
 drivers/gpu/drm/i915/intel_drv.h     |  88 +++++
 drivers/gpu/drm/i915/intel_hdcp.c    | 731 +++++++++++++++++++++++++++++++++++
 6 files changed, 919 insertions(+)
 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 42bc8bd4ff06..3facea4eefdb 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -107,6 +107,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 09bf043c1c2e..2bd2cc8441d4 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -8034,6 +8034,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
@@ -8335,6 +8336,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..d452c327dc1d 100644
--- a/drivers/gpu/drm/i915/intel_atomic.c
+++ b/drivers/gpu/drm/i915/intel_atomic.c
@@ -110,6 +110,8 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
 		to_intel_digital_connector_state(old_state);
 	struct drm_crtc_state *crtc_state;
 
+	intel_hdcp_atomic_check(conn, old_state, new_state);
+
 	if (!new_state->crtc)
 		return 0;
 
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 601c23be8264..f45c468abf98 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -12319,6 +12319,8 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
 	struct drm_crtc *crtc;
+	struct drm_connector_state *old_conn_state, *new_conn_state;
+	struct drm_connector *connector;
 	struct intel_crtc_state *intel_cstate;
 	u64 put_domains[I915_MAX_PIPES] = {};
 	int i;
@@ -12408,9 +12410,17 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
 		}
 	}
 
+	for_each_oldnew_connector_in_state(state, connector, old_conn_state,
+					   new_conn_state, i)
+		intel_hdcp_atomic_pre_commit(connector, old_conn_state,
+					     new_conn_state);
+
 	/* Now enable the clocks, plane, pipe, and connectors that we set up. */
 	dev_priv->display.update_crtcs(state);
 
+	for_each_new_connector_in_state(state, connector, new_conn_state, i)
+		intel_hdcp_atomic_commit(connector, new_conn_state);
+
 	/* FIXME: We should call drm_atomic_helper_commit_hw_done() here
 	 * already, but still need the state for the delayed optimization. To
 	 * fix this:
@@ -15322,6 +15332,10 @@ static void intel_hpd_poll_fini(struct drm_device *dev)
 	for_each_intel_connector_iter(connector, &conn_iter) {
 		if (connector->modeset_retry_work.func)
 			cancel_work_sync(&connector->modeset_retry_work);
+		if (connector->hdcp_shim) {
+			cancel_delayed_work_sync(&connector->hdcp_check_work);
+			cancel_work_sync(&connector->hdcp_prop_work);
+		}
 	}
 	drm_connector_list_iter_end(&conn_iter);
 }
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 852b3d161754..6f47a4227f5f 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -301,6 +301,76 @@ struct intel_panel {
 	} backlight;
 };
 
+/*
+ * This structure serves as a translation layer between the generic HDCP code
+ * and the bus-specific code. What that means is that HDCP over HDMI differs
+ * from HDCP over DP, so to account for these differences, we need to
+ * communicate with the receiver through this shim.
+ *
+ * For completeness, the 2 buses differ in the following ways:
+ *	- DP AUX vs. DDC
+ *		HDCP registers on the receiver are set via DP AUX for DP, and
+ *		they are set via DDC for HDMI.
+ *	- Receiver register offsets
+ *		The offsets of the registers are different for DP vs. HDMI
+ *	- Receiver register masks/offsets
+ *		For instance, the ready bit for the KSV fifo is in a different
+ *		place on DP vs HDMI
+ *	- Receiver register names
+ *		Seriously. In the DP spec, the 16-bit register containing
+ *		downstream information is called BINFO, on HDMI it's called
+ *		BSTATUS. To confuse matters further, DP has a BSTATUS register
+ *		with a completely different definition.
+ *	- KSV FIFO
+ *		On HDMI, the ksv fifo is read all at once, whereas on DP it must
+ *		be read 3 keys at a time
+ *	- Aksv output
+ *		Since Aksv is hidden in hardware, there's different procedures
+ *		to send it over DP AUX vs DDC
+ */
+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;
 	/*
@@ -332,6 +402,12 @@ 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 mutex hdcp_mutex;
+	uint64_t hdcp_value; /* protected by hdcp_mutex */
+	struct delayed_work hdcp_check_work;
+	struct work_struct hdcp_prop_work;
 };
 
 struct intel_digital_connector_state {
@@ -1763,6 +1839,18 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
 }
 #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
 
+/* intel_hdcp.c */
+void intel_hdcp_atomic_check(struct drm_connector *connector,
+			     struct drm_connector_state *old_state,
+			     struct drm_connector_state *new_state);
+void intel_hdcp_atomic_pre_commit(struct drm_connector *connector,
+			          struct drm_connector_state *old_state,
+			          struct drm_connector_state *new_state);
+void intel_hdcp_atomic_commit(struct drm_connector *connector,
+			      struct drm_connector_state *new_state);
+int intel_hdcp_check_link(struct intel_connector *connector);
+void intel_hdcp_check_work(struct work_struct *work);
+void intel_hdcp_prop_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..4eef9505410f
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -0,0 +1,731 @@
+/*
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Sean Paul <seanpaul@chromium.org>
+ */
+
+#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)
+{
+	int ret, read_ret;
+	bool ksv_ready;
+
+	ret = __wait_for(read_ret = shim->read_ksv_ready(intel_dig_port,
+							 &ksv_ready),
+			 read_ret || ksv_ready, 1500 * 1000, 1000, 100 * 1000);
+	if (ret)
+		return ret;
+	if (read_ret)
+		return read_ret;
+	if (!ksv_ready)
+		return -ETIMEDOUT;
+
+	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)
+{
+	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) */
+	ret = __intel_wait_for_register(dev_priv, SKL_HDCP_KEY_STATUS,
+					SKL_HDCP_KEY_LOAD_DONE,
+					SKL_HDCP_KEY_LOAD_DONE,
+					10, 1, &val);
+	if (ret)
+		return ret;
+	else 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 (intel_wait_for_register(dev_priv, SKL_HDCP_REP_CTL,
+				    SKL_HDCP_SHA1_READY,
+				    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->base.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;
+
+	/* 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 (intel_wait_for_register(dev_priv, SKL_HDCP_REP_CTL,
+				    SKL_HDCP_SHA1_COMPLETE,
+				    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->base.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 (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port),
+				    SKL_HDCP_STATUS_AN_READY,
+				    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 (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port),
+				    SKL_HDCP_STATUS_ENC,
+				    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->base.port;
+	int ret;
+
+	I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
+	if (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port), ~0, 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_check_work(struct work_struct *work)
+{
+	struct intel_connector *connector = container_of(to_delayed_work(work),
+							 struct intel_connector,
+						         hdcp_check_work);
+	if (!intel_hdcp_check_link(connector))
+		schedule_delayed_work(&connector->hdcp_check_work,
+				      DRM_HDCP_CHECK_PERIOD_MS);
+}
+
+void intel_hdcp_prop_work(struct work_struct *work)
+{
+	struct intel_connector *connector = container_of(work,
+							 struct intel_connector,
+						         hdcp_prop_work);
+	struct drm_device *dev = connector->base.dev;
+	struct drm_connector_state *state;
+
+	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+	mutex_lock(&connector->hdcp_mutex);
+
+	/*
+	 * This worker is only used to flip between ENABLED/DESIRED. Either of
+	 * those to OFF is handled by core. If hdcp_value == OFF, we're running
+	 * just after hdcp has been disabled, so just exit
+	 */
+	if (connector->hdcp_value != DRM_MODE_CONTENT_PROTECTION_OFF) {
+		state = connector->base.state;
+		state->content_protection = connector->hdcp_value;
+	}
+
+	mutex_unlock(&connector->hdcp_mutex);
+	drm_modeset_unlock(&dev->mode_config.connection_mutex);
+	mutex_unlock(&dev->mode_config.mutex);
+}
+
+static int intel_hdcp_enable(struct intel_connector *connector)
+{
+	int ret;
+
+	if (!connector->hdcp_shim)
+		return -ENOENT;
+
+	mutex_lock(&connector->hdcp_mutex);
+
+	ret = _intel_hdcp_enable(connector);
+	if (ret)
+		goto out;
+
+	connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+	schedule_work(&connector->hdcp_prop_work);
+	schedule_delayed_work(&connector->hdcp_check_work,
+			      DRM_HDCP_CHECK_PERIOD_MS);
+out:
+	mutex_unlock(&connector->hdcp_mutex);
+	return ret;
+}
+
+static int intel_hdcp_disable(struct intel_connector *connector)
+{
+	int ret;
+
+	if (!connector->hdcp_shim)
+		return -ENOENT;
+
+	mutex_lock(&connector->hdcp_mutex);
+
+	connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_OFF;
+	ret = _intel_hdcp_disable(connector);
+
+	mutex_unlock(&connector->hdcp_mutex);
+	cancel_delayed_work_sync(&connector->hdcp_check_work);
+	return ret;
+}
+
+void intel_hdcp_atomic_check(struct drm_connector *connector,
+			     struct drm_connector_state *old_state,
+			     struct drm_connector_state *new_state)
+{
+	uint64_t old_cp = old_state->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;
+	}
+
+	/* Only drivers can set content protection enabled */
+	if (old_cp != DRM_MODE_CONTENT_PROTECTION_ENABLED &&
+	    new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
+		new_state->content_protection =
+			DRM_MODE_CONTENT_PROTECTION_DESIRED;
+}
+
+void intel_hdcp_atomic_pre_commit(struct drm_connector *connector,
+			          struct drm_connector_state *old_state,
+			          struct drm_connector_state *new_state)
+{
+	uint64_t old_cp = old_state->content_protection;
+	uint64_t new_cp = new_state->content_protection;
+
+	/*
+	 * Disable HDCP if the connector is becoming disabled, or if requested
+	 * via the property.
+	 */
+	if ((!new_state->crtc && old_cp != DRM_MODE_CONTENT_PROTECTION_OFF) ||
+	    (new_state->crtc && old_cp != DRM_MODE_CONTENT_PROTECTION_OFF &&
+	     new_cp == DRM_MODE_CONTENT_PROTECTION_OFF))
+		intel_hdcp_disable(to_intel_connector(connector));
+}
+
+void intel_hdcp_atomic_commit(struct drm_connector *connector,
+			      struct drm_connector_state *new_state)
+{
+	uint64_t new_cp = new_state->content_protection;
+
+	/* Enable hdcp if it's desired */
+	if (new_state->crtc && new_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED)
+		intel_hdcp_enable(to_intel_connector(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 intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+	enum port port = intel_dig_port->base.port;
+	int ret = 0;
+
+	if (!connector->hdcp_shim)
+		return -ENOENT;
+
+	mutex_lock(&connector->hdcp_mutex);
+
+	if (connector->hdcp_value == DRM_MODE_CONTENT_PROTECTION_OFF)
+		goto out;
+
+	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;
+		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+		schedule_work(&connector->hdcp_prop_work);
+		goto out;
+	}
+
+	if (connector->hdcp_shim->check_link(intel_dig_port)) {
+		if (connector->hdcp_value != DRM_MODE_CONTENT_PROTECTION_OFF) {
+			connector->hdcp_value =
+				DRM_MODE_CONTENT_PROTECTION_ENABLED;
+			schedule_work(&connector->hdcp_prop_work);
+		}
+		goto out;
+	}
+
+	DRM_INFO("HDCP link failed, retrying authentication\n");
+
+	ret = _intel_hdcp_disable(connector);
+	if (ret) {
+		DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
+		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+		schedule_work(&connector->hdcp_prop_work);
+		goto out;
+	}
+
+	ret = _intel_hdcp_enable(connector);
+	if (ret) {
+		DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
+		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+		schedule_work(&connector->hdcp_prop_work);
+		goto out;
+	}
+
+out:
+	mutex_unlock(&connector->hdcp_mutex);
+	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] 55+ messages in thread

* [PATCH v3 6/9] drm/i915: Make use of indexed write GMBUS feature
  2017-12-05  5:14 ` Sean Paul
@ 2017-12-05  5:15   ` Sean Paul
  -1 siblings, 0 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:15 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: linux-kernel, daniel.vetter, seanpaul, Sean Paul, Jani Nikula,
	Joonas Lahtinen, Rodrigo Vivi, David Airlie

This patch enables the indexed write feature of the GMBUS to concatenate
2 consecutive messages into one. The criteria for an indexed write is
that both messages are writes, the first is length == 1, and the second
is length > 0. The first message is sent out by the GMBUS as the slave
command, and the second one is sent via the GMBUS FIFO as usual.

Changes in v3:
- Added to series

Suggested-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Sean Paul <seanpaul@chromium.org>
---
 drivers/gpu/drm/i915/intel_i2c.c | 39 ++++++++++++++++++++++++++++++++++-----
 1 file changed, 34 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index 49fdf09f9919..7399009aee0a 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -373,7 +373,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 +387,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 +410,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 +421,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;
 
@@ -430,6 +433,14 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
 	return 0;
 }
 
+static int
+gmbus_xfer_index_write(struct drm_i915_private *dev_priv, u8 cmd,
+		       struct i2c_msg *msg)
+{
+	u8 gmbus1_index = GMBUS_CYCLE_INDEX | (cmd << GMBUS_SLAVE_INDEX_SHIFT);
+	return gmbus_xfer_write(dev_priv, msg, gmbus1_index);
+}
+
 /*
  * The gmbus controller can combine a 1 or 2 byte write with a read that
  * immediately follows it by using an "INDEX" cycle.
@@ -444,6 +455,20 @@ gmbus_is_index_read(struct i2c_msg *msgs, int i, int num)
 		(msgs[i + 1].flags & I2C_M_RD));
 }
 
+/*
+ * The gmbus controller can combine a 2-msg write into a single write that
+ * immediately follows it by using an "INDEX" cycle.
+ */
+static bool
+gmbus_is_index_write(struct i2c_msg *msgs, int i, int num)
+{
+	return (i + 1 < num &&
+		msgs[i].addr == msgs[i + 1].addr &&
+		!(msgs[i].flags & I2C_M_RD) &&
+		!(msgs[i + 1].flags & I2C_M_RD) &&
+		(msgs[i].len == 1 || msgs[i + 1].len > 0));
+}
+
 static int
 gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
 {
@@ -489,10 +514,14 @@ do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
 		if (gmbus_is_index_read(msgs, i, num)) {
 			ret = gmbus_xfer_index_read(dev_priv, &msgs[i]);
 			inc = 2; /* an index read is two msgs */
+		} else if (gmbus_is_index_write(msgs, i, num)) {
+			ret = gmbus_xfer_index_write(dev_priv, msgs[i].buf[0],
+					&msgs[i + 1]);
+			inc = 2; /* an index write is two msgs */
 		} 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], 0);
 		}
 
 		if (!ret)
-- 
2.15.0.531.g2ccb3012c9-goog

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

* [PATCH v3 6/9] drm/i915: Make use of indexed write GMBUS feature
@ 2017-12-05  5:15   ` Sean Paul
  0 siblings, 0 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:15 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: seanpaul, David Airlie, linux-kernel, Rodrigo Vivi, daniel.vetter

This patch enables the indexed write feature of the GMBUS to concatenate
2 consecutive messages into one. The criteria for an indexed write is
that both messages are writes, the first is length == 1, and the second
is length > 0. The first message is sent out by the GMBUS as the slave
command, and the second one is sent via the GMBUS FIFO as usual.

Changes in v3:
- Added to series

Suggested-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Sean Paul <seanpaul@chromium.org>
---
 drivers/gpu/drm/i915/intel_i2c.c | 39 ++++++++++++++++++++++++++++++++++-----
 1 file changed, 34 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index 49fdf09f9919..7399009aee0a 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -373,7 +373,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 +387,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 +410,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 +421,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;
 
@@ -430,6 +433,14 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
 	return 0;
 }
 
+static int
+gmbus_xfer_index_write(struct drm_i915_private *dev_priv, u8 cmd,
+		       struct i2c_msg *msg)
+{
+	u8 gmbus1_index = GMBUS_CYCLE_INDEX | (cmd << GMBUS_SLAVE_INDEX_SHIFT);
+	return gmbus_xfer_write(dev_priv, msg, gmbus1_index);
+}
+
 /*
  * The gmbus controller can combine a 1 or 2 byte write with a read that
  * immediately follows it by using an "INDEX" cycle.
@@ -444,6 +455,20 @@ gmbus_is_index_read(struct i2c_msg *msgs, int i, int num)
 		(msgs[i + 1].flags & I2C_M_RD));
 }
 
+/*
+ * The gmbus controller can combine a 2-msg write into a single write that
+ * immediately follows it by using an "INDEX" cycle.
+ */
+static bool
+gmbus_is_index_write(struct i2c_msg *msgs, int i, int num)
+{
+	return (i + 1 < num &&
+		msgs[i].addr == msgs[i + 1].addr &&
+		!(msgs[i].flags & I2C_M_RD) &&
+		!(msgs[i + 1].flags & I2C_M_RD) &&
+		(msgs[i].len == 1 || msgs[i + 1].len > 0));
+}
+
 static int
 gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
 {
@@ -489,10 +514,14 @@ do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
 		if (gmbus_is_index_read(msgs, i, num)) {
 			ret = gmbus_xfer_index_read(dev_priv, &msgs[i]);
 			inc = 2; /* an index read is two msgs */
+		} else if (gmbus_is_index_write(msgs, i, num)) {
+			ret = gmbus_xfer_index_write(dev_priv, msgs[i].buf[0],
+					&msgs[i + 1]);
+			inc = 2; /* an index write is two msgs */
 		} 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], 0);
 		}
 
 		if (!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] 55+ messages in thread

* [PATCH v3 7/9] drm/i915: Add function to output Aksv over GMBUS
  2017-12-05  5:14 ` Sean Paul
@ 2017-12-05  5:15   ` Sean Paul
  -1 siblings, 0 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:15 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: linux-kernel, daniel.vetter, seanpaul, Sean Paul,
	Ville Syrjälä,
	Jani Nikula, Joonas Lahtinen, Rodrigo Vivi, David Airlie

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.

Changes in v2:
- None
Changes in v3:
- Uses new index write feature (Ville)

Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
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 | 47 +++++++++++++++++++++++++++++++++++++---
 3 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index bddd65839f60..6b39081c5e53 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -4049,6 +4049,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 2bd2cc8441d4..107e16392710 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -3043,6 +3043,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 7399009aee0a..0a4c7486fc7b 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"
@@ -497,7 +498,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)
 {
 	struct intel_gmbus *bus = container_of(adapter,
 					       struct intel_gmbus,
@@ -507,7 +509,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;
@@ -629,7 +631,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);
 		if (ret == -EAGAIN)
 			bus->force_bit |= GMBUS_FORCE_BIT_RETRY;
 	}
@@ -639,6 +641,45 @@ 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 cmd = DRM_HDCP_DDC_AKSV ;
+	u8 buf[DRM_HDCP_KSV_LEN] = { 0 };
+	struct i2c_msg msgs[] = {
+		{
+			.addr = DRM_HDCP_DDC_ADDR,
+			.flags = 0,
+			.len = sizeof(cmd),
+			.buf = &cmd,
+		},
+		{
+			.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, msgs, ARRAY_SIZE(msgs), GMBUS_AKSV_SELECT);
+
+	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] 55+ messages in thread

* [PATCH v3 7/9] drm/i915: Add function to output Aksv over GMBUS
@ 2017-12-05  5:15   ` Sean Paul
  0 siblings, 0 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:15 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: seanpaul, David Airlie, linux-kernel, Rodrigo Vivi, daniel.vetter

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.

Changes in v2:
- None
Changes in v3:
- Uses new index write feature (Ville)

Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
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 | 47 +++++++++++++++++++++++++++++++++++++---
 3 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index bddd65839f60..6b39081c5e53 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -4049,6 +4049,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 2bd2cc8441d4..107e16392710 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -3043,6 +3043,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 7399009aee0a..0a4c7486fc7b 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"
@@ -497,7 +498,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)
 {
 	struct intel_gmbus *bus = container_of(adapter,
 					       struct intel_gmbus,
@@ -507,7 +509,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;
@@ -629,7 +631,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);
 		if (ret == -EAGAIN)
 			bus->force_bit |= GMBUS_FORCE_BIT_RETRY;
 	}
@@ -639,6 +641,45 @@ 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 cmd = DRM_HDCP_DDC_AKSV ;
+	u8 buf[DRM_HDCP_KSV_LEN] = { 0 };
+	struct i2c_msg msgs[] = {
+		{
+			.addr = DRM_HDCP_DDC_ADDR,
+			.flags = 0,
+			.len = sizeof(cmd),
+			.buf = &cmd,
+		},
+		{
+			.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, msgs, ARRAY_SIZE(msgs), GMBUS_AKSV_SELECT);
+
+	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] 55+ messages in thread

* [PATCH v3 8/9] drm/i915: Implement HDCP for HDMI
  2017-12-05  5:14 ` Sean Paul
                   ` (7 preceding siblings ...)
  (?)
@ 2017-12-05  5:15 ` Sean Paul
  2017-12-05 17:06     ` Daniel Vetter
  -1 siblings, 1 reply; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:15 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: linux-kernel, daniel.vetter, seanpaul, Sean Paul, Jani Nikula,
	Joonas Lahtinen, Rodrigo Vivi, David Airlie

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

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

Changes in v2:
- Rebased on drm-intel-next
Changes in v3:
- Initialize new worker

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_drv.h  |   2 +
 drivers/gpu/drm/i915/intel_hdmi.c | 257 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 310 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 107e16392710..79944ab4218a 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -8450,6 +8450,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 eff3b51872eb..a179fd9968a5 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -1615,6 +1615,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_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 6f47a4227f5f..0b4405f3e988 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -1379,6 +1379,8 @@ void intel_ddi_compute_min_voltage_level(struct drm_i915_private *dev_priv,
 u32 bxt_signal_levels(struct intel_dp *intel_dp);
 uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
 u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
+int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder);
+int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder);
 
 unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
 				   int plane, unsigned int height);
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 9d5e72728475..17a525b9fcf9 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>
@@ -873,6 +874,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->base.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)
 {
@@ -2050,6 +2297,16 @@ 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;
+		mutex_init(&intel_connector->hdcp_mutex);
+		INIT_DELAYED_WORK(&intel_connector->hdcp_check_work,
+				  intel_hdcp_check_work);
+		INIT_WORK(&intel_connector->hdcp_prop_work,
+			  intel_hdcp_prop_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] 55+ messages in thread

* [PATCH v3 9/9] drm/i915: Implement HDCP for DisplayPort
  2017-12-05  5:14 ` Sean Paul
                   ` (8 preceding siblings ...)
  (?)
@ 2017-12-05  5:15 ` Sean Paul
  2017-12-05 14:30     ` Ramalingam C
  2017-12-05 17:12     ` Daniel Vetter
  -1 siblings, 2 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05  5:15 UTC (permalink / raw)
  To: dri-devel, intel-gfx
  Cc: linux-kernel, daniel.vetter, seanpaul, Sean Paul, Jani Nikula,
	Joonas Lahtinen, Rodrigo Vivi, David Airlie

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.

Changes in v2:
- Moved intel_hdcp_check_link out of intel_dp_check_link and only call
  it on short pulse. Since intel_hdcp_check_link does its own locking,
  this ensures we don't deadlock when intel_dp_check_link is called
  holding connection_mutex.
- Rebased on drm-intel-next
Changes in v3:
- Initialize new worker
- Move intel_hdcp_check_link further out to avoid calling it while
  holding _any_ locks

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

diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index c603d4c903e1..dc303e18c1dd 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"
@@ -1025,10 +1027,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 =
@@ -1088,10 +1109,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++) {
@@ -1228,7 +1250,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;
 
@@ -1250,7 +1273,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;
 			/*
@@ -4981,6 +5005,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 drm_i915_private *dev_priv = to_i915(intel_dp_to_dev(intel_dp));
@@ -5146,6 +5367,9 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd)
 		drm_modeset_acquire_fini(&ctx);
 		WARN(iret, "Acquiring modeset locks failed with %i\n", iret);
 
+		/* Short pulse can signify loss of hdcp authentication */
+		intel_hdcp_check_link(intel_dp->attached_connector);
+
 		if (!handled) {
 			intel_dp->detect_done = false;
 			goto put_power;
@@ -6121,6 +6345,16 @@ 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;
+		mutex_init(&intel_connector->hdcp_mutex);
+		INIT_DELAYED_WORK(&intel_connector->hdcp_check_work,
+				  intel_hdcp_check_work);
+		INIT_WORK(&intel_connector->hdcp_prop_work,
+			  intel_hdcp_prop_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] 55+ messages in thread

* ✗ Fi.CI.BAT: failure for drm/i915: Implement HDCP (rev3)
  2017-12-05  5:14 ` Sean Paul
                   ` (9 preceding siblings ...)
  (?)
@ 2017-12-05  5:21 ` Patchwork
  2017-12-05 15:30   ` Daniel Vetter
  -1 siblings, 1 reply; 55+ messages in thread
From: Patchwork @ 2017-12-05  5:21 UTC (permalink / raw)
  To: Sean Paul; +Cc: intel-gfx

== Series Details ==

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

== Summary ==

Applying: drm: Fix link-status kerneldoc line lengths
error: Failed to merge in the changes.
Using index info to reconstruct a base tree...
M	drivers/gpu/drm/drm_connector.c
Falling back to patching base and 3-way merge...
Auto-merging drivers/gpu/drm/drm_connector.c
CONFLICT (content): Merge conflict in drivers/gpu/drm/drm_connector.c
Patch failed at 0001 drm: Fix link-status kerneldoc line lengths
The copy of the patch that failed is found in: .git/rebase-apply/patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

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

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

* Re: [PATCH v3 3/9] drm: Add Content Protection property
  2017-12-05  5:15   ` Sean Paul
@ 2017-12-05  8:07     ` Hans Verkuil
  -1 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2017-12-05  8:07 UTC (permalink / raw)
  To: Sean Paul, dri-devel, intel-gfx
  Cc: seanpaul, David Airlie, linux-kernel, daniel.vetter

On 12/05/2017 06:15 AM, 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.
> 
> 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.
> 
> Changes in v2:
>  - Pimp kerneldoc for content_protection_property (Daniel)
>  - Drop sysfs attribute
> Changes in v3:
>  - None
> 
> Cc: Daniel Vetter <daniel.vetter@intel.com>
> 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 | 71 +++++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/drm_sysfs.c     |  1 +
>  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 c2da5585e201..676025d755b2 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -1196,6 +1196,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);
> @@ -1275,6 +1281,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 f14b48e6e839..8626aa8f485e 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
>   *
> @@ -764,6 +771,34 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
>   *      after modeset, the kernel driver may set this to "BAD" and issue a
>   *      hotplug uevent. Drivers should update this value using
>   *      drm_mode_connector_set_link_status_property().
> + * Content Protection:
> + *	This property is used by userspace to request the kernel protect future
> + *	content communicated over the link. When requested, kernel will apply
> + *	the appropriate means of protection (most often HDCP), and use the
> + *	property to tell userspace the protection is active.
> + *
> + *	The value of this property can be one of the following:
> + *
> + *	- DRM_MODE_CONTENT_PROTECTION_OFF = 0
> + *		The link is not protected, content is transmitted in the clear.
> + *	- DRM_MODE_CONTENT_PROTECTION_DESIRED = 1
> + *		Userspace has requested content protection, but the link is not
> + *		currently protected. When in this state, kernel should enable
> + *		Content Protection as soon as possible.
> + *	- DRM_MODE_CONTENT_PROTECTION_ENABLED = 2
> + *		Userspace has requested content protection, and the link is
> + *		protected. Only the driver can set the property to this value.
> + *		If userspace attempts to set to ENABLED, kernel will return
> + *		-EINVAL.
> + *
> + *	A few guidelines:
> + *
> + *	- DESIRED state should be preserved until userspace de-asserts it by
> + *	  setting the property to OFF. This means ENABLED should only transition
> + *	  to OFF when the user explicitly requests it.
> + *	- If the state is DESIRED, kernel should attempt to re-authenticate the
> + *	  link whenever possible. This includes across disable/enable, dpms,
> + *	  hotplug, downstream device changes, link status failures, etc..
>   *
>   * Connectors also have one standardized atomic property:
>   *
> @@ -1047,6 +1082,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..2385c7e0bef5 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)
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 7a7140543012..828878addd03 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -370,6 +370,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;
>  };
>  
>  /**
> @@ -718,6 +724,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
> @@ -808,6 +815,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:
>  	 *
> @@ -1002,6 +1015,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,
> @@ -1010,6 +1024,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

What about HDCP 1.4 and HDCP 2.2? Userspace would need to know which version
was negotiated since content protected 4k videos require HDCP 2.2. Perhaps
provide a property with the HDCP version?

I'm also missing a method for userspace to read the BKSV from the transmitter.

Regards,

	Hans

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

* Re: [PATCH v3 3/9] drm: Add Content Protection property
@ 2017-12-05  8:07     ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2017-12-05  8:07 UTC (permalink / raw)
  To: Sean Paul, dri-devel, intel-gfx
  Cc: David Airlie, daniel.vetter, linux-kernel, seanpaul

On 12/05/2017 06:15 AM, 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.
> 
> 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.
> 
> Changes in v2:
>  - Pimp kerneldoc for content_protection_property (Daniel)
>  - Drop sysfs attribute
> Changes in v3:
>  - None
> 
> Cc: Daniel Vetter <daniel.vetter@intel.com>
> 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 | 71 +++++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/drm_sysfs.c     |  1 +
>  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 c2da5585e201..676025d755b2 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -1196,6 +1196,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);
> @@ -1275,6 +1281,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 f14b48e6e839..8626aa8f485e 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
>   *
> @@ -764,6 +771,34 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
>   *      after modeset, the kernel driver may set this to "BAD" and issue a
>   *      hotplug uevent. Drivers should update this value using
>   *      drm_mode_connector_set_link_status_property().
> + * Content Protection:
> + *	This property is used by userspace to request the kernel protect future
> + *	content communicated over the link. When requested, kernel will apply
> + *	the appropriate means of protection (most often HDCP), and use the
> + *	property to tell userspace the protection is active.
> + *
> + *	The value of this property can be one of the following:
> + *
> + *	- DRM_MODE_CONTENT_PROTECTION_OFF = 0
> + *		The link is not protected, content is transmitted in the clear.
> + *	- DRM_MODE_CONTENT_PROTECTION_DESIRED = 1
> + *		Userspace has requested content protection, but the link is not
> + *		currently protected. When in this state, kernel should enable
> + *		Content Protection as soon as possible.
> + *	- DRM_MODE_CONTENT_PROTECTION_ENABLED = 2
> + *		Userspace has requested content protection, and the link is
> + *		protected. Only the driver can set the property to this value.
> + *		If userspace attempts to set to ENABLED, kernel will return
> + *		-EINVAL.
> + *
> + *	A few guidelines:
> + *
> + *	- DESIRED state should be preserved until userspace de-asserts it by
> + *	  setting the property to OFF. This means ENABLED should only transition
> + *	  to OFF when the user explicitly requests it.
> + *	- If the state is DESIRED, kernel should attempt to re-authenticate the
> + *	  link whenever possible. This includes across disable/enable, dpms,
> + *	  hotplug, downstream device changes, link status failures, etc..
>   *
>   * Connectors also have one standardized atomic property:
>   *
> @@ -1047,6 +1082,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..2385c7e0bef5 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)
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 7a7140543012..828878addd03 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -370,6 +370,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;
>  };
>  
>  /**
> @@ -718,6 +724,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
> @@ -808,6 +815,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:
>  	 *
> @@ -1002,6 +1015,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,
> @@ -1010,6 +1024,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

What about HDCP 1.4 and HDCP 2.2? Userspace would need to know which version
was negotiated since content protected 4k videos require HDCP 2.2. Perhaps
provide a property with the HDCP version?

I'm also missing a method for userspace to read the BKSV from the transmitter.

Regards,

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

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

* Re: [PATCH v3 5/9] drm/i915: Add HDCP framework + base implementation
  2017-12-05  5:15   ` Sean Paul
@ 2017-12-05  9:06     ` Ramalingam C
  -1 siblings, 0 replies; 55+ messages in thread
From: Ramalingam C @ 2017-12-05  9:06 UTC (permalink / raw)
  To: Sean Paul, dri-devel, intel-gfx
  Cc: linux-kernel, daniel.vetter, seanpaul, Chris Wilson, Jani Nikula,
	Joonas Lahtinen, Rodrigo Vivi, David Airlie



On Tuesday 05 December 2017 10:45 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.
>
> Changes in v2:
> - Don't open code wait_fors (Chris)
> - drm_hdcp.c under MIT license (Daniel)
> - Move intel_hdcp_disable() call above ddi_disable (Ram)
> - Fix // comments (I wore a cone of shame for 12 hours to atone) (Daniel)
> - Justify intel_hdcp_shim with comments (Daniel)
> - Fixed async locking issues by adding hdcp_mutex (Daniel)
> - Don't alter connector_state in enable/disable (Daniel)
> Changes in v3:
> - Added hdcp_mutex/hdcp_value to make async reasonable
> - Added hdcp_prop_work to separate link checking & property setting
> - Added new helper for atomic_check state tracking (Daniel)
> - Moved enable/disable into atomic_commit with matching helpers
> - Moved intel_hdcp_check_link out of all locks when called from dp
> - Bumped up ksv_fifo timeout (noticed failure on one of my dongles)
>
> Cc: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Daniel Vetter <daniel.vetter@intel.com>
> Cc: Ramalingam C <ramalingam.c@intel.com>
> 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  |   2 +
>   drivers/gpu/drm/i915/intel_display.c |  14 +
>   drivers/gpu/drm/i915/intel_drv.h     |  88 +++++
>   drivers/gpu/drm/i915/intel_hdcp.c    | 731 +++++++++++++++++++++++++++++++++++
>   6 files changed, 919 insertions(+)
>   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 42bc8bd4ff06..3facea4eefdb 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -107,6 +107,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 09bf043c1c2e..2bd2cc8441d4 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -8034,6 +8034,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
> @@ -8335,6 +8336,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..d452c327dc1d 100644
> --- a/drivers/gpu/drm/i915/intel_atomic.c
> +++ b/drivers/gpu/drm/i915/intel_atomic.c
> @@ -110,6 +110,8 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
>   		to_intel_digital_connector_state(old_state);
>   	struct drm_crtc_state *crtc_state;
>   
> +	intel_hdcp_atomic_check(conn, old_state, new_state);
> +
>   	if (!new_state->crtc)
>   		return 0;
>   
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index 601c23be8264..f45c468abf98 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -12319,6 +12319,8 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>   	struct drm_i915_private *dev_priv = to_i915(dev);
>   	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
>   	struct drm_crtc *crtc;
> +	struct drm_connector_state *old_conn_state, *new_conn_state;
> +	struct drm_connector *connector;
>   	struct intel_crtc_state *intel_cstate;
>   	u64 put_domains[I915_MAX_PIPES] = {};
>   	int i;
> @@ -12408,9 +12410,17 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>   		}
>   	}
>   
> +	for_each_oldnew_connector_in_state(state, connector, old_conn_state,
> +					   new_conn_state, i)
> +		intel_hdcp_atomic_pre_commit(connector, old_conn_state,
> +					     new_conn_state);
> +
>   	/* Now enable the clocks, plane, pipe, and connectors that we set up. */
>   	dev_priv->display.update_crtcs(state);
>   
> +	for_each_new_connector_in_state(state, connector, new_conn_state, i)
> +		intel_hdcp_atomic_commit(connector, new_conn_state);
> +
>   	/* FIXME: We should call drm_atomic_helper_commit_hw_done() here
>   	 * already, but still need the state for the delayed optimization. To
>   	 * fix this:
> @@ -15322,6 +15332,10 @@ static void intel_hpd_poll_fini(struct drm_device *dev)
>   	for_each_intel_connector_iter(connector, &conn_iter) {
>   		if (connector->modeset_retry_work.func)
>   			cancel_work_sync(&connector->modeset_retry_work);
> +		if (connector->hdcp_shim) {
> +			cancel_delayed_work_sync(&connector->hdcp_check_work);
> +			cancel_work_sync(&connector->hdcp_prop_work);
> +		}
>   	}
>   	drm_connector_list_iter_end(&conn_iter);
>   }
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 852b3d161754..6f47a4227f5f 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -301,6 +301,76 @@ struct intel_panel {
>   	} backlight;
>   };
>   
> +/*
> + * This structure serves as a translation layer between the generic HDCP code
> + * and the bus-specific code. What that means is that HDCP over HDMI differs
> + * from HDCP over DP, so to account for these differences, we need to
> + * communicate with the receiver through this shim.
> + *
> + * For completeness, the 2 buses differ in the following ways:
> + *	- DP AUX vs. DDC
> + *		HDCP registers on the receiver are set via DP AUX for DP, and
> + *		they are set via DDC for HDMI.
> + *	- Receiver register offsets
> + *		The offsets of the registers are different for DP vs. HDMI
> + *	- Receiver register masks/offsets
> + *		For instance, the ready bit for the KSV fifo is in a different
> + *		place on DP vs HDMI
> + *	- Receiver register names
> + *		Seriously. In the DP spec, the 16-bit register containing
> + *		downstream information is called BINFO, on HDMI it's called
> + *		BSTATUS. To confuse matters further, DP has a BSTATUS register
> + *		with a completely different definition.
> + *	- KSV FIFO
> + *		On HDMI, the ksv fifo is read all at once, whereas on DP it must
> + *		be read 3 keys at a time
> + *	- Aksv output
> + *		Since Aksv is hidden in hardware, there's different procedures
> + *		to send it over DP AUX vs DDC
> + */
> +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;
>   	/*
> @@ -332,6 +402,12 @@ 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 mutex hdcp_mutex;
> +	uint64_t hdcp_value; /* protected by hdcp_mutex */
> +	struct delayed_work hdcp_check_work;
> +	struct work_struct hdcp_prop_work;
>   };
>   
>   struct intel_digital_connector_state {
> @@ -1763,6 +1839,18 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
>   }
>   #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
>   
> +/* intel_hdcp.c */
> +void intel_hdcp_atomic_check(struct drm_connector *connector,
> +			     struct drm_connector_state *old_state,
> +			     struct drm_connector_state *new_state);
> +void intel_hdcp_atomic_pre_commit(struct drm_connector *connector,
> +			          struct drm_connector_state *old_state,
> +			          struct drm_connector_state *new_state);
> +void intel_hdcp_atomic_commit(struct drm_connector *connector,
> +			      struct drm_connector_state *new_state);
> +int intel_hdcp_check_link(struct intel_connector *connector);
> +void intel_hdcp_check_work(struct work_struct *work);
> +void intel_hdcp_prop_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..4eef9505410f
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_hdcp.c
> @@ -0,0 +1,731 @@
> +/*
> + * Copyright (C) 2017 Google, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + * Authors:
> + * Sean Paul <seanpaul@chromium.org>
> + */
> +
> +#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)
> +{
> +	int ret, read_ret;
> +	bool ksv_ready;
> +
> +	ret = __wait_for(read_ret = shim->read_ksv_ready(intel_dig_port,
> +							 &ksv_ready),
> +			 read_ret || ksv_ready, 1500 * 1000, 1000, 100 * 1000);
> +	if (ret)
> +		return ret;
> +	if (read_ret)
> +		return read_ret;
> +	if (!ksv_ready)
> +		return -ETIMEDOUT;
> +
> +	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)
> +{
> +	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) */
> +	ret = __intel_wait_for_register(dev_priv, SKL_HDCP_KEY_STATUS,
> +					SKL_HDCP_KEY_LOAD_DONE,
> +					SKL_HDCP_KEY_LOAD_DONE,
> +					10, 1, &val);
> +	if (ret)
> +		return ret;
> +	else 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 (intel_wait_for_register(dev_priv, SKL_HDCP_REP_CTL,
> +				    SKL_HDCP_SHA1_READY,
> +				    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->base.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;
> +
> +	/* 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 (intel_wait_for_register(dev_priv, SKL_HDCP_REP_CTL,
> +				    SKL_HDCP_SHA1_COMPLETE,
> +				    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->base.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 (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port),
> +				    SKL_HDCP_STATUS_AN_READY,
> +				    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 (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port),
> +				    SKL_HDCP_STATUS_ENC,
> +				    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->base.port;
> +	int ret;
> +
> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
> +	if (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port), ~0, 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_check_work(struct work_struct *work)
> +{
> +	struct intel_connector *connector = container_of(to_delayed_work(work),
> +							 struct intel_connector,
> +						         hdcp_check_work);
> +	if (!intel_hdcp_check_link(connector))
> +		schedule_delayed_work(&connector->hdcp_check_work,
> +				      DRM_HDCP_CHECK_PERIOD_MS);
> +}
> +
> +void intel_hdcp_prop_work(struct work_struct *work)
> +{
> +	struct intel_connector *connector = container_of(work,
> +							 struct intel_connector,
> +						         hdcp_prop_work);
> +	struct drm_device *dev = connector->base.dev;
> +	struct drm_connector_state *state;
> +
> +	mutex_lock(&dev->mode_config.mutex);
> +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> +	mutex_lock(&connector->hdcp_mutex);
> +
> +	/*
> +	 * This worker is only used to flip between ENABLED/DESIRED. Either of
> +	 * those to OFF is handled by core. If hdcp_value == OFF, we're running
> +	 * just after hdcp has been disabled, so just exit
> +	 */
> +	if (connector->hdcp_value != DRM_MODE_CONTENT_PROTECTION_OFF) {
> +		state = connector->base.state;
> +		state->content_protection = connector->hdcp_value;
> +	}
> +
> +	mutex_unlock(&connector->hdcp_mutex);
> +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
> +	mutex_unlock(&dev->mode_config.mutex);
> +}
> +
> +static int intel_hdcp_enable(struct intel_connector *connector)
> +{
> +	int ret;
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	mutex_lock(&connector->hdcp_mutex);
> +
> +	ret = _intel_hdcp_enable(connector);
> +	if (ret)
> +		goto out;
> +
> +	connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
> +	schedule_work(&connector->hdcp_prop_work);
> +	schedule_delayed_work(&connector->hdcp_check_work,
> +			      DRM_HDCP_CHECK_PERIOD_MS);
> +out:
> +	mutex_unlock(&connector->hdcp_mutex);
> +	return ret;
> +}
> +
> +static int intel_hdcp_disable(struct intel_connector *connector)
> +{
> +	int ret;
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	mutex_lock(&connector->hdcp_mutex);
> +
> +	connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_OFF;
> +	ret = _intel_hdcp_disable(connector);
> +
> +	mutex_unlock(&connector->hdcp_mutex);
> +	cancel_delayed_work_sync(&connector->hdcp_check_work);
> +	return ret;
> +}
> +
> +void intel_hdcp_atomic_check(struct drm_connector *connector,
> +			     struct drm_connector_state *old_state,
> +			     struct drm_connector_state *new_state)
> +{
> +	uint64_t old_cp = old_state->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;
> +	}
> +
> +	/* Only drivers can set content protection enabled */
> +	if (old_cp != DRM_MODE_CONTENT_PROTECTION_ENABLED &&
> +	    new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
> +		new_state->content_protection =
> +			DRM_MODE_CONTENT_PROTECTION_DESIRED;
As drm_atomic_connector_set_property is rejecting the userspace request 
for DRM_MODE_CONTENT_PROTECTION_ENABLED,
We dont need this check here.

--Ram
> +}
> +
> +void intel_hdcp_atomic_pre_commit(struct drm_connector *connector,
> +			          struct drm_connector_state *old_state,
> +			          struct drm_connector_state *new_state)
> +{
> +	uint64_t old_cp = old_state->content_protection;
> +	uint64_t new_cp = new_state->content_protection;
> +
> +	/*
> +	 * Disable HDCP if the connector is becoming disabled, or if requested
> +	 * via the property.
> +	 */
> +	if ((!new_state->crtc && old_cp != DRM_MODE_CONTENT_PROTECTION_OFF) ||
> +	    (new_state->crtc && old_cp != DRM_MODE_CONTENT_PROTECTION_OFF &&
> +	     new_cp == DRM_MODE_CONTENT_PROTECTION_OFF))
> +		intel_hdcp_disable(to_intel_connector(connector));
> +}
> +
> +void intel_hdcp_atomic_commit(struct drm_connector *connector,
> +			      struct drm_connector_state *new_state)
> +{
> +	uint64_t new_cp = new_state->content_protection;
> +
> +	/* Enable hdcp if it's desired */
> +	if (new_state->crtc && new_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED)
> +		intel_hdcp_enable(to_intel_connector(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 intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
> +	enum port port = intel_dig_port->base.port;
> +	int ret = 0;
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	mutex_lock(&connector->hdcp_mutex);
> +
> +	if (connector->hdcp_value == DRM_MODE_CONTENT_PROTECTION_OFF)
> +		goto out;
> +
> +	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;
> +		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +		schedule_work(&connector->hdcp_prop_work);
> +		goto out;
> +	}
> +
> +	if (connector->hdcp_shim->check_link(intel_dig_port)) {
> +		if (connector->hdcp_value != DRM_MODE_CONTENT_PROTECTION_OFF) {
> +			connector->hdcp_value =
> +				DRM_MODE_CONTENT_PROTECTION_ENABLED;
> +			schedule_work(&connector->hdcp_prop_work);
> +		}
> +		goto out;
> +	}
> +
> +	DRM_INFO("HDCP link failed, retrying authentication\n");
> +
> +	ret = _intel_hdcp_disable(connector);
> +	if (ret) {
> +		DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
> +		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +		schedule_work(&connector->hdcp_prop_work);
> +		goto out;
> +	}
> +
> +	ret = _intel_hdcp_enable(connector);
> +	if (ret) {
> +		DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
> +		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +		schedule_work(&connector->hdcp_prop_work);
> +		goto out;
> +	}
> +
> +out:
> +	mutex_unlock(&connector->hdcp_mutex);
> +	return ret;
> +}

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

* Re: [PATCH v3 5/9] drm/i915: Add HDCP framework + base implementation
@ 2017-12-05  9:06     ` Ramalingam C
  0 siblings, 0 replies; 55+ messages in thread
From: Ramalingam C @ 2017-12-05  9:06 UTC (permalink / raw)
  To: Sean Paul, dri-devel, intel-gfx
  Cc: seanpaul, David Airlie, Joonas Lahtinen, linux-kernel,
	Rodrigo Vivi, daniel.vetter



On Tuesday 05 December 2017 10:45 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.
>
> Changes in v2:
> - Don't open code wait_fors (Chris)
> - drm_hdcp.c under MIT license (Daniel)
> - Move intel_hdcp_disable() call above ddi_disable (Ram)
> - Fix // comments (I wore a cone of shame for 12 hours to atone) (Daniel)
> - Justify intel_hdcp_shim with comments (Daniel)
> - Fixed async locking issues by adding hdcp_mutex (Daniel)
> - Don't alter connector_state in enable/disable (Daniel)
> Changes in v3:
> - Added hdcp_mutex/hdcp_value to make async reasonable
> - Added hdcp_prop_work to separate link checking & property setting
> - Added new helper for atomic_check state tracking (Daniel)
> - Moved enable/disable into atomic_commit with matching helpers
> - Moved intel_hdcp_check_link out of all locks when called from dp
> - Bumped up ksv_fifo timeout (noticed failure on one of my dongles)
>
> Cc: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Daniel Vetter <daniel.vetter@intel.com>
> Cc: Ramalingam C <ramalingam.c@intel.com>
> 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  |   2 +
>   drivers/gpu/drm/i915/intel_display.c |  14 +
>   drivers/gpu/drm/i915/intel_drv.h     |  88 +++++
>   drivers/gpu/drm/i915/intel_hdcp.c    | 731 +++++++++++++++++++++++++++++++++++
>   6 files changed, 919 insertions(+)
>   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 42bc8bd4ff06..3facea4eefdb 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -107,6 +107,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 09bf043c1c2e..2bd2cc8441d4 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -8034,6 +8034,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
> @@ -8335,6 +8336,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..d452c327dc1d 100644
> --- a/drivers/gpu/drm/i915/intel_atomic.c
> +++ b/drivers/gpu/drm/i915/intel_atomic.c
> @@ -110,6 +110,8 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
>   		to_intel_digital_connector_state(old_state);
>   	struct drm_crtc_state *crtc_state;
>   
> +	intel_hdcp_atomic_check(conn, old_state, new_state);
> +
>   	if (!new_state->crtc)
>   		return 0;
>   
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index 601c23be8264..f45c468abf98 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -12319,6 +12319,8 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>   	struct drm_i915_private *dev_priv = to_i915(dev);
>   	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
>   	struct drm_crtc *crtc;
> +	struct drm_connector_state *old_conn_state, *new_conn_state;
> +	struct drm_connector *connector;
>   	struct intel_crtc_state *intel_cstate;
>   	u64 put_domains[I915_MAX_PIPES] = {};
>   	int i;
> @@ -12408,9 +12410,17 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>   		}
>   	}
>   
> +	for_each_oldnew_connector_in_state(state, connector, old_conn_state,
> +					   new_conn_state, i)
> +		intel_hdcp_atomic_pre_commit(connector, old_conn_state,
> +					     new_conn_state);
> +
>   	/* Now enable the clocks, plane, pipe, and connectors that we set up. */
>   	dev_priv->display.update_crtcs(state);
>   
> +	for_each_new_connector_in_state(state, connector, new_conn_state, i)
> +		intel_hdcp_atomic_commit(connector, new_conn_state);
> +
>   	/* FIXME: We should call drm_atomic_helper_commit_hw_done() here
>   	 * already, but still need the state for the delayed optimization. To
>   	 * fix this:
> @@ -15322,6 +15332,10 @@ static void intel_hpd_poll_fini(struct drm_device *dev)
>   	for_each_intel_connector_iter(connector, &conn_iter) {
>   		if (connector->modeset_retry_work.func)
>   			cancel_work_sync(&connector->modeset_retry_work);
> +		if (connector->hdcp_shim) {
> +			cancel_delayed_work_sync(&connector->hdcp_check_work);
> +			cancel_work_sync(&connector->hdcp_prop_work);
> +		}
>   	}
>   	drm_connector_list_iter_end(&conn_iter);
>   }
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 852b3d161754..6f47a4227f5f 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -301,6 +301,76 @@ struct intel_panel {
>   	} backlight;
>   };
>   
> +/*
> + * This structure serves as a translation layer between the generic HDCP code
> + * and the bus-specific code. What that means is that HDCP over HDMI differs
> + * from HDCP over DP, so to account for these differences, we need to
> + * communicate with the receiver through this shim.
> + *
> + * For completeness, the 2 buses differ in the following ways:
> + *	- DP AUX vs. DDC
> + *		HDCP registers on the receiver are set via DP AUX for DP, and
> + *		they are set via DDC for HDMI.
> + *	- Receiver register offsets
> + *		The offsets of the registers are different for DP vs. HDMI
> + *	- Receiver register masks/offsets
> + *		For instance, the ready bit for the KSV fifo is in a different
> + *		place on DP vs HDMI
> + *	- Receiver register names
> + *		Seriously. In the DP spec, the 16-bit register containing
> + *		downstream information is called BINFO, on HDMI it's called
> + *		BSTATUS. To confuse matters further, DP has a BSTATUS register
> + *		with a completely different definition.
> + *	- KSV FIFO
> + *		On HDMI, the ksv fifo is read all at once, whereas on DP it must
> + *		be read 3 keys at a time
> + *	- Aksv output
> + *		Since Aksv is hidden in hardware, there's different procedures
> + *		to send it over DP AUX vs DDC
> + */
> +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;
>   	/*
> @@ -332,6 +402,12 @@ 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 mutex hdcp_mutex;
> +	uint64_t hdcp_value; /* protected by hdcp_mutex */
> +	struct delayed_work hdcp_check_work;
> +	struct work_struct hdcp_prop_work;
>   };
>   
>   struct intel_digital_connector_state {
> @@ -1763,6 +1839,18 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
>   }
>   #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
>   
> +/* intel_hdcp.c */
> +void intel_hdcp_atomic_check(struct drm_connector *connector,
> +			     struct drm_connector_state *old_state,
> +			     struct drm_connector_state *new_state);
> +void intel_hdcp_atomic_pre_commit(struct drm_connector *connector,
> +			          struct drm_connector_state *old_state,
> +			          struct drm_connector_state *new_state);
> +void intel_hdcp_atomic_commit(struct drm_connector *connector,
> +			      struct drm_connector_state *new_state);
> +int intel_hdcp_check_link(struct intel_connector *connector);
> +void intel_hdcp_check_work(struct work_struct *work);
> +void intel_hdcp_prop_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..4eef9505410f
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_hdcp.c
> @@ -0,0 +1,731 @@
> +/*
> + * Copyright (C) 2017 Google, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + * Authors:
> + * Sean Paul <seanpaul@chromium.org>
> + */
> +
> +#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)
> +{
> +	int ret, read_ret;
> +	bool ksv_ready;
> +
> +	ret = __wait_for(read_ret = shim->read_ksv_ready(intel_dig_port,
> +							 &ksv_ready),
> +			 read_ret || ksv_ready, 1500 * 1000, 1000, 100 * 1000);
> +	if (ret)
> +		return ret;
> +	if (read_ret)
> +		return read_ret;
> +	if (!ksv_ready)
> +		return -ETIMEDOUT;
> +
> +	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)
> +{
> +	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) */
> +	ret = __intel_wait_for_register(dev_priv, SKL_HDCP_KEY_STATUS,
> +					SKL_HDCP_KEY_LOAD_DONE,
> +					SKL_HDCP_KEY_LOAD_DONE,
> +					10, 1, &val);
> +	if (ret)
> +		return ret;
> +	else 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 (intel_wait_for_register(dev_priv, SKL_HDCP_REP_CTL,
> +				    SKL_HDCP_SHA1_READY,
> +				    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->base.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;
> +
> +	/* 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 (intel_wait_for_register(dev_priv, SKL_HDCP_REP_CTL,
> +				    SKL_HDCP_SHA1_COMPLETE,
> +				    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->base.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 (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port),
> +				    SKL_HDCP_STATUS_AN_READY,
> +				    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 (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port),
> +				    SKL_HDCP_STATUS_ENC,
> +				    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->base.port;
> +	int ret;
> +
> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
> +	if (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port), ~0, 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_check_work(struct work_struct *work)
> +{
> +	struct intel_connector *connector = container_of(to_delayed_work(work),
> +							 struct intel_connector,
> +						         hdcp_check_work);
> +	if (!intel_hdcp_check_link(connector))
> +		schedule_delayed_work(&connector->hdcp_check_work,
> +				      DRM_HDCP_CHECK_PERIOD_MS);
> +}
> +
> +void intel_hdcp_prop_work(struct work_struct *work)
> +{
> +	struct intel_connector *connector = container_of(work,
> +							 struct intel_connector,
> +						         hdcp_prop_work);
> +	struct drm_device *dev = connector->base.dev;
> +	struct drm_connector_state *state;
> +
> +	mutex_lock(&dev->mode_config.mutex);
> +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> +	mutex_lock(&connector->hdcp_mutex);
> +
> +	/*
> +	 * This worker is only used to flip between ENABLED/DESIRED. Either of
> +	 * those to OFF is handled by core. If hdcp_value == OFF, we're running
> +	 * just after hdcp has been disabled, so just exit
> +	 */
> +	if (connector->hdcp_value != DRM_MODE_CONTENT_PROTECTION_OFF) {
> +		state = connector->base.state;
> +		state->content_protection = connector->hdcp_value;
> +	}
> +
> +	mutex_unlock(&connector->hdcp_mutex);
> +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
> +	mutex_unlock(&dev->mode_config.mutex);
> +}
> +
> +static int intel_hdcp_enable(struct intel_connector *connector)
> +{
> +	int ret;
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	mutex_lock(&connector->hdcp_mutex);
> +
> +	ret = _intel_hdcp_enable(connector);
> +	if (ret)
> +		goto out;
> +
> +	connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
> +	schedule_work(&connector->hdcp_prop_work);
> +	schedule_delayed_work(&connector->hdcp_check_work,
> +			      DRM_HDCP_CHECK_PERIOD_MS);
> +out:
> +	mutex_unlock(&connector->hdcp_mutex);
> +	return ret;
> +}
> +
> +static int intel_hdcp_disable(struct intel_connector *connector)
> +{
> +	int ret;
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	mutex_lock(&connector->hdcp_mutex);
> +
> +	connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_OFF;
> +	ret = _intel_hdcp_disable(connector);
> +
> +	mutex_unlock(&connector->hdcp_mutex);
> +	cancel_delayed_work_sync(&connector->hdcp_check_work);
> +	return ret;
> +}
> +
> +void intel_hdcp_atomic_check(struct drm_connector *connector,
> +			     struct drm_connector_state *old_state,
> +			     struct drm_connector_state *new_state)
> +{
> +	uint64_t old_cp = old_state->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;
> +	}
> +
> +	/* Only drivers can set content protection enabled */
> +	if (old_cp != DRM_MODE_CONTENT_PROTECTION_ENABLED &&
> +	    new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
> +		new_state->content_protection =
> +			DRM_MODE_CONTENT_PROTECTION_DESIRED;
As drm_atomic_connector_set_property is rejecting the userspace request 
for DRM_MODE_CONTENT_PROTECTION_ENABLED,
We dont need this check here.

--Ram
> +}
> +
> +void intel_hdcp_atomic_pre_commit(struct drm_connector *connector,
> +			          struct drm_connector_state *old_state,
> +			          struct drm_connector_state *new_state)
> +{
> +	uint64_t old_cp = old_state->content_protection;
> +	uint64_t new_cp = new_state->content_protection;
> +
> +	/*
> +	 * Disable HDCP if the connector is becoming disabled, or if requested
> +	 * via the property.
> +	 */
> +	if ((!new_state->crtc && old_cp != DRM_MODE_CONTENT_PROTECTION_OFF) ||
> +	    (new_state->crtc && old_cp != DRM_MODE_CONTENT_PROTECTION_OFF &&
> +	     new_cp == DRM_MODE_CONTENT_PROTECTION_OFF))
> +		intel_hdcp_disable(to_intel_connector(connector));
> +}
> +
> +void intel_hdcp_atomic_commit(struct drm_connector *connector,
> +			      struct drm_connector_state *new_state)
> +{
> +	uint64_t new_cp = new_state->content_protection;
> +
> +	/* Enable hdcp if it's desired */
> +	if (new_state->crtc && new_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED)
> +		intel_hdcp_enable(to_intel_connector(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 intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
> +	enum port port = intel_dig_port->base.port;
> +	int ret = 0;
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	mutex_lock(&connector->hdcp_mutex);
> +
> +	if (connector->hdcp_value == DRM_MODE_CONTENT_PROTECTION_OFF)
> +		goto out;
> +
> +	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;
> +		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +		schedule_work(&connector->hdcp_prop_work);
> +		goto out;
> +	}
> +
> +	if (connector->hdcp_shim->check_link(intel_dig_port)) {
> +		if (connector->hdcp_value != DRM_MODE_CONTENT_PROTECTION_OFF) {
> +			connector->hdcp_value =
> +				DRM_MODE_CONTENT_PROTECTION_ENABLED;
> +			schedule_work(&connector->hdcp_prop_work);
> +		}
> +		goto out;
> +	}
> +
> +	DRM_INFO("HDCP link failed, retrying authentication\n");
> +
> +	ret = _intel_hdcp_disable(connector);
> +	if (ret) {
> +		DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
> +		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +		schedule_work(&connector->hdcp_prop_work);
> +		goto out;
> +	}
> +
> +	ret = _intel_hdcp_enable(connector);
> +	if (ret) {
> +		DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
> +		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +		schedule_work(&connector->hdcp_prop_work);
> +		goto out;
> +	}
> +
> +out:
> +	mutex_unlock(&connector->hdcp_mutex);
> +	return ret;
> +}

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

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

* Re: [PATCH v3 3/9] drm: Add Content Protection property
  2017-12-05  8:07     ` Hans Verkuil
  (?)
@ 2017-12-05 14:04     ` Ramalingam C
  2017-12-05 14:36       ` Sean Paul
  -1 siblings, 1 reply; 55+ messages in thread
From: Ramalingam C @ 2017-12-05 14:04 UTC (permalink / raw)
  To: dri-devel


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



On Tuesday 05 December 2017 01:37 PM, Hans Verkuil wrote:
> On 12/05/2017 06:15 AM, 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.
>>
>> 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.
>>
>> Changes in v2:
>>   - Pimp kerneldoc for content_protection_property (Daniel)
>>   - Drop sysfs attribute
>> Changes in v3:
>>   - None
>>
>> Cc: Daniel Vetter <daniel.vetter@intel.com>
>> 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 | 71 +++++++++++++++++++++++++++++++++++++++++
>>   drivers/gpu/drm/drm_sysfs.c     |  1 +
>>   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 c2da5585e201..676025d755b2 100644
>> --- a/drivers/gpu/drm/drm_atomic.c
>> +++ b/drivers/gpu/drm/drm_atomic.c
>> @@ -1196,6 +1196,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);
>> @@ -1275,6 +1281,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 f14b48e6e839..8626aa8f485e 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
>>    *
>> @@ -764,6 +771,34 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
>>    *      after modeset, the kernel driver may set this to "BAD" and issue a
>>    *      hotplug uevent. Drivers should update this value using
>>    *      drm_mode_connector_set_link_status_property().
>> + * Content Protection:
>> + *	This property is used by userspace to request the kernel protect future
>> + *	content communicated over the link. When requested, kernel will apply
>> + *	the appropriate means of protection (most often HDCP), and use the
>> + *	property to tell userspace the protection is active.
>> + *
>> + *	The value of this property can be one of the following:
>> + *
>> + *	- DRM_MODE_CONTENT_PROTECTION_OFF = 0
>> + *		The link is not protected, content is transmitted in the clear.
>> + *	- DRM_MODE_CONTENT_PROTECTION_DESIRED = 1
>> + *		Userspace has requested content protection, but the link is not
>> + *		currently protected. When in this state, kernel should enable
>> + *		Content Protection as soon as possible.
>> + *	- DRM_MODE_CONTENT_PROTECTION_ENABLED = 2
>> + *		Userspace has requested content protection, and the link is
>> + *		protected. Only the driver can set the property to this value.
>> + *		If userspace attempts to set to ENABLED, kernel will return
>> + *		-EINVAL.
>> + *
>> + *	A few guidelines:
>> + *
>> + *	- DESIRED state should be preserved until userspace de-asserts it by
>> + *	  setting the property to OFF. This means ENABLED should only transition
>> + *	  to OFF when the user explicitly requests it.
>> + *	- If the state is DESIRED, kernel should attempt to re-authenticate the
>> + *	  link whenever possible. This includes across disable/enable, dpms,
>> + *	  hotplug, downstream device changes, link status failures, etc..
>>    *
>>    * Connectors also have one standardized atomic property:
>>    *
>> @@ -1047,6 +1082,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..2385c7e0bef5 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)
>> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
>> index 7a7140543012..828878addd03 100644
>> --- a/include/drm/drm_connector.h
>> +++ b/include/drm/drm_connector.h
>> @@ -370,6 +370,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;
>>   };
>>   
>>   /**
>> @@ -718,6 +724,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
>> @@ -808,6 +815,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:
>>   	 *
>> @@ -1002,6 +1015,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,
>> @@ -1010,6 +1024,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
> What about HDCP 1.4 and HDCP 2.2? Userspace would need to know which version
> was negotiated since content protected 4k videos require HDCP 2.2. Perhaps
> provide a property with the HDCP version?
>
> I'm also missing a method for userspace to read the BKSV from the transmitter.
Hans,

I guess you are asking about the use case explained at 
http://www.spinics.net/lists/intel-gfx/msg134813.html

Sean,
As this solution is only for 1.4, this version requirement might not 
matter here.
But as a community could we at least ack such a need, so that inevitable 
extension of this uAPI will be well thought about?

As we discussed before Enum values required are

    -OFF           : Disable any content protection
    -DESIRED       : For any possible HDCP protection (v1.4/2.2)
    -DESIRED_TYPE1 : For HDCP2.2 only
    -ENABLED       : Highest HDCP protection is enabled (Could be v1.4/2.2)
    -ENABLED_TYPE1 : HDCP2.2 enabled

And another gap i am seeing with this uAPI is that there is no 
communication back about the
HDCP authentication failure, as DESIRED will remain without 
transitioning into ENABLED.
So userspace has to identify the failure of the HDCP req with polling 
and Timeout.
Timeout also will vary with system to system, as
with big downstream topology(worst case 127 devices with 7depth) 
authentication could take 5+ Sec.
     with only receiver < few 100 mSec(~200mSec?)


So could it help userspace if we could indicate the authentication failure.
Agreed that runtime link integrity lost is indicated by the 
ENABLED->DESIRED transition.

--Ram

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


[-- Attachment #1.2: Type: text/html, Size: 13287 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] 55+ messages in thread

* Re: [PATCH v3 9/9] drm/i915: Implement HDCP for DisplayPort
  2017-12-05  5:15 ` [PATCH v3 9/9] drm/i915: Implement HDCP for DisplayPort Sean Paul
@ 2017-12-05 14:30     ` Ramalingam C
  2017-12-05 17:12     ` Daniel Vetter
  1 sibling, 0 replies; 55+ messages in thread
From: Ramalingam C @ 2017-12-05 14:30 UTC (permalink / raw)
  To: Sean Paul, dri-devel, intel-gfx
  Cc: seanpaul, David Airlie, Joonas Lahtinen, linux-kernel,
	Rodrigo Vivi, daniel.vetter



On Tuesday 05 December 2017 10:45 AM, Sean Paul wrote:
> +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);
> +}
as per link integrity check should cover other indication from the 
repeater like

REAUTHENTICATION_REQUEST - request for reauth due to unauthenticated 
state at downstream for some unknown reason

-Ram

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

* Re: [PATCH v3 9/9] drm/i915: Implement HDCP for DisplayPort
@ 2017-12-05 14:30     ` Ramalingam C
  0 siblings, 0 replies; 55+ messages in thread
From: Ramalingam C @ 2017-12-05 14:30 UTC (permalink / raw)
  To: Sean Paul, dri-devel, intel-gfx
  Cc: seanpaul, David Airlie, linux-kernel, Rodrigo Vivi, daniel.vetter



On Tuesday 05 December 2017 10:45 AM, Sean Paul wrote:
> +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);
> +}
as per link integrity check should cover other indication from the 
repeater like

REAUTHENTICATION_REQUEST - request for reauth due to unauthenticated 
state at downstream for some unknown reason

-Ram

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

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

* Re: [PATCH v3 3/9] drm: Add Content Protection property
  2017-12-05 14:04     ` Ramalingam C
@ 2017-12-05 14:36       ` Sean Paul
  2017-12-05 16:10         ` Hans Verkuil
  2017-12-05 17:11         ` C, Ramalingam
  0 siblings, 2 replies; 55+ messages in thread
From: Sean Paul @ 2017-12-05 14:36 UTC (permalink / raw)
  To: Ramalingam C; +Cc: Hans Verkuil, dri-devel

On Tue, Dec 5, 2017 at 9:04 AM, Ramalingam C <ramalingam.c@intel.com> wrote:
>
>
> On Tuesday 05 December 2017 01:37 PM, Hans Verkuil wrote:
>
> On 12/05/2017 06:15 AM, 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.
>
> 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.
>
> Changes in v2:
>  - Pimp kerneldoc for content_protection_property (Daniel)
>  - Drop sysfs attribute
> Changes in v3:
>  - None
>
> Cc: Daniel Vetter <daniel.vetter@intel.com>
> 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 | 71
> +++++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/drm_sysfs.c     |  1 +
>  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 c2da5585e201..676025d755b2 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -1196,6 +1196,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);
> @@ -1275,6 +1281,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 f14b48e6e839..8626aa8f485e 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
>   *
> @@ -764,6 +771,34 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
>   *      after modeset, the kernel driver may set this to "BAD" and issue a
>   *      hotplug uevent. Drivers should update this value using
>   *      drm_mode_connector_set_link_status_property().
> + * Content Protection:
> + * This property is used by userspace to request the kernel protect future
> + * content communicated over the link. When requested, kernel will apply
> + * the appropriate means of protection (most often HDCP), and use the
> + * property to tell userspace the protection is active.
> + *
> + * The value of this property can be one of the following:
> + *
> + * - DRM_MODE_CONTENT_PROTECTION_OFF = 0
> + * The link is not protected, content is transmitted in the clear.
> + * - DRM_MODE_CONTENT_PROTECTION_DESIRED = 1
> + * Userspace has requested content protection, but the link is not
> + * currently protected. When in this state, kernel should enable
> + * Content Protection as soon as possible.
> + * - DRM_MODE_CONTENT_PROTECTION_ENABLED = 2
> + * Userspace has requested content protection, and the link is
> + * protected. Only the driver can set the property to this value.
> + * If userspace attempts to set to ENABLED, kernel will return
> + * -EINVAL.
> + *
> + * A few guidelines:
> + *
> + * - DESIRED state should be preserved until userspace de-asserts it by
> + *  setting the property to OFF. This means ENABLED should only transition
> + *  to OFF when the user explicitly requests it.
> + * - If the state is DESIRED, kernel should attempt to re-authenticate the
> + *  link whenever possible. This includes across disable/enable, dpms,
> + *  hotplug, downstream device changes, link status failures, etc..
>   *
>   * Connectors also have one standardized atomic property:
>   *
> @@ -1047,6 +1082,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..2385c7e0bef5 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)
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 7a7140543012..828878addd03 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -370,6 +370,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;
>  };
>
>  /**
> @@ -718,6 +724,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
> @@ -808,6 +815,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:
>   *
> @@ -1002,6 +1015,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,
> @@ -1010,6 +1024,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
>
> What about HDCP 1.4 and HDCP 2.2? Userspace would need to know which version
> was negotiated since content protected 4k videos require HDCP 2.2. Perhaps
> provide a property with the HDCP version?
>
> I'm also missing a method for userspace to read the BKSV from the
> transmitter.
>
> Hans,
>
> I guess you are asking about the use case explained at
> http://www.spinics.net/lists/intel-gfx/msg134813.html
>
> Sean,
> As this solution is only for 1.4, this version requirement might not matter
> here.
> But as a community could we at least ack such a need, so that inevitable
> extension of this uAPI will be well thought about?
>
> As we discussed before Enum values required are
>
> -OFF           : Disable any content protection
> -DESIRED       : For any possible HDCP protection (v1.4/2.2)
> -DESIRED_TYPE1 : For HDCP2.2 only
> -ENABLED       : Highest HDCP protection is enabled (Could be v1.4/2.2)
> -ENABLED_TYPE1 : HDCP2.2 enabled
>

I'd rather keep the property as-is and expose an HDCP version property
alongside it (or perhaps something more elaborate that includes bksv
and the downstream bksvs). The reason I prefer that is it will also
cover the 1.2 vs 1.4 difference that is a more immediate need.

This property is intentionally vague about the underlying encryption,
and tying it to HDCP (and specific versions, at that) is not
consistent with the design.


> And another gap i am seeing with this uAPI is that there is no communication
> back about the
> HDCP authentication failure, as DESIRED will remain without transitioning
> into ENABLED.
> So userspace has to identify the failure of the HDCP req with polling and
> Timeout.
> Timeout also will vary with system to system, as
>     with big downstream topology(worst case 127 devices with 7depth)
> authentication could take 5+ Sec.
>     with only receiver < few 100 mSec(~200mSec?)
>

It's worked on CrOS for a number of years now, why change what isn't broken?

Sean

>
> So could it help userspace if we could indicate the authentication failure.
> Agreed that runtime link integrity lost is indicated by the ENABLED->DESIRED
> transition.
>
> --Ram
>
>
> Regards,
>
> Hans
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
>
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [Intel-gfx] [PATCH v3 3/9] drm: Add Content Protection property
  2017-12-05  8:07     ` Hans Verkuil
@ 2017-12-05 15:27       ` Daniel Vetter
  -1 siblings, 0 replies; 55+ messages in thread
From: Daniel Vetter @ 2017-12-05 15:27 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Sean Paul, dri-devel, intel-gfx, David Airlie, daniel.vetter,
	linux-kernel, seanpaul

On Tue, Dec 05, 2017 at 09:07:58AM +0100, Hans Verkuil wrote:
> On 12/05/2017 06:15 AM, 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.
> > 
> > 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.
> > 
> > Changes in v2:
> >  - Pimp kerneldoc for content_protection_property (Daniel)
> >  - Drop sysfs attribute
> > Changes in v3:
> >  - None
> > 
> > Cc: Daniel Vetter <daniel.vetter@intel.com>
> > 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 | 71 +++++++++++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/drm_sysfs.c     |  1 +
> >  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 c2da5585e201..676025d755b2 100644
> > --- a/drivers/gpu/drm/drm_atomic.c
> > +++ b/drivers/gpu/drm/drm_atomic.c
> > @@ -1196,6 +1196,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);
> > @@ -1275,6 +1281,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 f14b48e6e839..8626aa8f485e 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
> >   *
> > @@ -764,6 +771,34 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
> >   *      after modeset, the kernel driver may set this to "BAD" and issue a
> >   *      hotplug uevent. Drivers should update this value using
> >   *      drm_mode_connector_set_link_status_property().
> > + * Content Protection:
> > + *	This property is used by userspace to request the kernel protect future
> > + *	content communicated over the link. When requested, kernel will apply
> > + *	the appropriate means of protection (most often HDCP), and use the
> > + *	property to tell userspace the protection is active.
> > + *
> > + *	The value of this property can be one of the following:
> > + *
> > + *	- DRM_MODE_CONTENT_PROTECTION_OFF = 0
> > + *		The link is not protected, content is transmitted in the clear.
> > + *	- DRM_MODE_CONTENT_PROTECTION_DESIRED = 1
> > + *		Userspace has requested content protection, but the link is not
> > + *		currently protected. When in this state, kernel should enable
> > + *		Content Protection as soon as possible.
> > + *	- DRM_MODE_CONTENT_PROTECTION_ENABLED = 2
> > + *		Userspace has requested content protection, and the link is
> > + *		protected. Only the driver can set the property to this value.
> > + *		If userspace attempts to set to ENABLED, kernel will return
> > + *		-EINVAL.
> > + *
> > + *	A few guidelines:
> > + *
> > + *	- DESIRED state should be preserved until userspace de-asserts it by
> > + *	  setting the property to OFF. This means ENABLED should only transition
> > + *	  to OFF when the user explicitly requests it.
> > + *	- If the state is DESIRED, kernel should attempt to re-authenticate the
> > + *	  link whenever possible. This includes across disable/enable, dpms,
> > + *	  hotplug, downstream device changes, link status failures, etc..
> >   *
> >   * Connectors also have one standardized atomic property:
> >   *
> > @@ -1047,6 +1082,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..2385c7e0bef5 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)
> > diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> > index 7a7140543012..828878addd03 100644
> > --- a/include/drm/drm_connector.h
> > +++ b/include/drm/drm_connector.h
> > @@ -370,6 +370,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;
> >  };
> >  
> >  /**
> > @@ -718,6 +724,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
> > @@ -808,6 +815,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:
> >  	 *
> > @@ -1002,6 +1015,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,
> > @@ -1010,6 +1024,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
> 
> What about HDCP 1.4 and HDCP 2.2? Userspace would need to know which version
> was negotiated since content protected 4k videos require HDCP 2.2. Perhaps
> provide a property with the HDCP version?
> 
> I'm also missing a method for userspace to read the BKSV from the transmitter.

Atm the only open source userspace we have is chrome os, which doesn't
care about either. That's why these two pieces have been left out, which
just need open source userspace for this stuff.

The rough idea for hdcp2 was to add a desired_type2 and enabled_type2,
which will guarantee hdcp2. _desired alone could give you either. Or we
try the most we can automatically, and publish the type1 vs. type2 stuff
in a 2nd property.

Either way, not having that from the start doesn't prevent us from adding
it later on in a backwards compatible way. Properties are rather nice this
way :-)
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

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

* Re: [PATCH v3 3/9] drm: Add Content Protection property
@ 2017-12-05 15:27       ` Daniel Vetter
  0 siblings, 0 replies; 55+ messages in thread
From: Daniel Vetter @ 2017-12-05 15:27 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: seanpaul, David Airlie, intel-gfx, linux-kernel, dri-devel,
	daniel.vetter

On Tue, Dec 05, 2017 at 09:07:58AM +0100, Hans Verkuil wrote:
> On 12/05/2017 06:15 AM, 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.
> > 
> > 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.
> > 
> > Changes in v2:
> >  - Pimp kerneldoc for content_protection_property (Daniel)
> >  - Drop sysfs attribute
> > Changes in v3:
> >  - None
> > 
> > Cc: Daniel Vetter <daniel.vetter@intel.com>
> > 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 | 71 +++++++++++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/drm_sysfs.c     |  1 +
> >  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 c2da5585e201..676025d755b2 100644
> > --- a/drivers/gpu/drm/drm_atomic.c
> > +++ b/drivers/gpu/drm/drm_atomic.c
> > @@ -1196,6 +1196,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);
> > @@ -1275,6 +1281,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 f14b48e6e839..8626aa8f485e 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
> >   *
> > @@ -764,6 +771,34 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
> >   *      after modeset, the kernel driver may set this to "BAD" and issue a
> >   *      hotplug uevent. Drivers should update this value using
> >   *      drm_mode_connector_set_link_status_property().
> > + * Content Protection:
> > + *	This property is used by userspace to request the kernel protect future
> > + *	content communicated over the link. When requested, kernel will apply
> > + *	the appropriate means of protection (most often HDCP), and use the
> > + *	property to tell userspace the protection is active.
> > + *
> > + *	The value of this property can be one of the following:
> > + *
> > + *	- DRM_MODE_CONTENT_PROTECTION_OFF = 0
> > + *		The link is not protected, content is transmitted in the clear.
> > + *	- DRM_MODE_CONTENT_PROTECTION_DESIRED = 1
> > + *		Userspace has requested content protection, but the link is not
> > + *		currently protected. When in this state, kernel should enable
> > + *		Content Protection as soon as possible.
> > + *	- DRM_MODE_CONTENT_PROTECTION_ENABLED = 2
> > + *		Userspace has requested content protection, and the link is
> > + *		protected. Only the driver can set the property to this value.
> > + *		If userspace attempts to set to ENABLED, kernel will return
> > + *		-EINVAL.
> > + *
> > + *	A few guidelines:
> > + *
> > + *	- DESIRED state should be preserved until userspace de-asserts it by
> > + *	  setting the property to OFF. This means ENABLED should only transition
> > + *	  to OFF when the user explicitly requests it.
> > + *	- If the state is DESIRED, kernel should attempt to re-authenticate the
> > + *	  link whenever possible. This includes across disable/enable, dpms,
> > + *	  hotplug, downstream device changes, link status failures, etc..
> >   *
> >   * Connectors also have one standardized atomic property:
> >   *
> > @@ -1047,6 +1082,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..2385c7e0bef5 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)
> > diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> > index 7a7140543012..828878addd03 100644
> > --- a/include/drm/drm_connector.h
> > +++ b/include/drm/drm_connector.h
> > @@ -370,6 +370,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;
> >  };
> >  
> >  /**
> > @@ -718,6 +724,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
> > @@ -808,6 +815,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:
> >  	 *
> > @@ -1002,6 +1015,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,
> > @@ -1010,6 +1024,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
> 
> What about HDCP 1.4 and HDCP 2.2? Userspace would need to know which version
> was negotiated since content protected 4k videos require HDCP 2.2. Perhaps
> provide a property with the HDCP version?
> 
> I'm also missing a method for userspace to read the BKSV from the transmitter.

Atm the only open source userspace we have is chrome os, which doesn't
care about either. That's why these two pieces have been left out, which
just need open source userspace for this stuff.

The rough idea for hdcp2 was to add a desired_type2 and enabled_type2,
which will guarantee hdcp2. _desired alone could give you either. Or we
try the most we can automatically, and publish the type1 vs. type2 stuff
in a 2nd property.

Either way, not having that from the start doesn't prevent us from adding
it later on in a backwards compatible way. Properties are rather nice this
way :-)
-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] 55+ messages in thread

* Re: ✗ Fi.CI.BAT:  failure for drm/i915: Implement HDCP (rev3)
  2017-12-05  5:21 ` ✗ Fi.CI.BAT: failure for drm/i915: Implement HDCP (rev3) Patchwork
@ 2017-12-05 15:30   ` Daniel Vetter
  0 siblings, 0 replies; 55+ messages in thread
From: Daniel Vetter @ 2017-12-05 15:30 UTC (permalink / raw)
  To: intel-gfx

On Tue, Dec 05, 2017 at 05:21:25AM -0000, Patchwork wrote:
> == Series Details ==
> 
> Series: drm/i915: Implement HDCP (rev3)
> URL   : https://patchwork.freedesktop.org/series/34671/
> State : failure
> 
> == Summary ==
> 
> Applying: drm: Fix link-status kerneldoc line lengths
> error: Failed to merge in the changes.
> Using index info to reconstruct a base tree...
> M	drivers/gpu/drm/drm_connector.c
> Falling back to patching base and 3-way merge...
> Auto-merging drivers/gpu/drm/drm_connector.c
> CONFLICT (content): Merge conflict in drivers/gpu/drm/drm_connector.c
> Patch failed at 0001 drm: Fix link-status kerneldoc line lengths
> The copy of the patch that failed is found in: .git/rebase-apply/patch
> When you have resolved this problem, run "git am --continue".
> If you prefer to skip this patch, run "git am --skip" instead.
> To restore the original branch and stop patching, run "git am --abort".

You need to rebase onto drm-tip to appease CI.
-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] 55+ messages in thread

* Re: [PATCH v3 3/9] drm: Add Content Protection property
  2017-12-05  5:15   ` Sean Paul
@ 2017-12-05 15:34     ` Daniel Vetter
  -1 siblings, 0 replies; 55+ messages in thread
From: Daniel Vetter @ 2017-12-05 15:34 UTC (permalink / raw)
  To: Sean Paul
  Cc: dri-devel, intel-gfx, seanpaul, David Airlie, linux-kernel,
	daniel.vetter

On Tue, Dec 05, 2017 at 12:15:02AM -0500, 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.
> 
> 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.
> 
> Changes in v2:
>  - Pimp kerneldoc for content_protection_property (Daniel)
>  - Drop sysfs attribute
> Changes in v3:
>  - None
> 
> Cc: Daniel Vetter <daniel.vetter@intel.com>
> 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 | 71 +++++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/drm_sysfs.c     |  1 +
>  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 c2da5585e201..676025d755b2 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -1196,6 +1196,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);
> @@ -1275,6 +1281,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 f14b48e6e839..8626aa8f485e 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
>   *
> @@ -764,6 +771,34 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
>   *      after modeset, the kernel driver may set this to "BAD" and issue a
>   *      hotplug uevent. Drivers should update this value using
>   *      drm_mode_connector_set_link_status_property().
> + * Content Protection:
> + *	This property is used by userspace to request the kernel protect future
> + *	content communicated over the link. When requested, kernel will apply
> + *	the appropriate means of protection (most often HDCP), and use the
> + *	property to tell userspace the protection is active.
> + *
> + *	The value of this property can be one of the following:
> + *
> + *	- DRM_MODE_CONTENT_PROTECTION_OFF = 0
> + *		The link is not protected, content is transmitted in the clear.
> + *	- DRM_MODE_CONTENT_PROTECTION_DESIRED = 1
> + *		Userspace has requested content protection, but the link is not
> + *		currently protected. When in this state, kernel should enable
> + *		Content Protection as soon as possible.
> + *	- DRM_MODE_CONTENT_PROTECTION_ENABLED = 2
> + *		Userspace has requested content protection, and the link is
> + *		protected. Only the driver can set the property to this value.
> + *		If userspace attempts to set to ENABLED, kernel will return
> + *		-EINVAL.
> + *
> + *	A few guidelines:
> + *
> + *	- DESIRED state should be preserved until userspace de-asserts it by
> + *	  setting the property to OFF. This means ENABLED should only transition
> + *	  to OFF when the user explicitly requests it.
> + *	- If the state is DESIRED, kernel should attempt to re-authenticate the
> + *	  link whenever possible. This includes across disable/enable, dpms,
> + *	  hotplug, downstream device changes, link status failures, etc..

Two bits missing imo:
- Should explain that userspace should poll this property to detect a
  change from ENABLED to DESIRED (and take adequate actions and e.g. stop
  the stream). No uevent will be sent out because the HDCP specs require
  polling anyway.
- One sentence to explain that drivers set this up using
  drm_connector_attach_content_protection_property().

Also, pls double-check the html output looks decent, you have quite some
fancy formatting in the above section :-)

With that all addressed:

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>


>   *
>   * Connectors also have one standardized atomic property:
>   *
> @@ -1047,6 +1082,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..2385c7e0bef5 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)
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 7a7140543012..828878addd03 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -370,6 +370,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;
>  };
>  
>  /**
> @@ -718,6 +724,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
> @@ -808,6 +815,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:
>  	 *
> @@ -1002,6 +1015,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,
> @@ -1010,6 +1024,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

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

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

* Re: [PATCH v3 3/9] drm: Add Content Protection property
@ 2017-12-05 15:34     ` Daniel Vetter
  0 siblings, 0 replies; 55+ messages in thread
From: Daniel Vetter @ 2017-12-05 15:34 UTC (permalink / raw)
  To: Sean Paul
  Cc: seanpaul, David Airlie, intel-gfx, linux-kernel, dri-devel,
	daniel.vetter

On Tue, Dec 05, 2017 at 12:15:02AM -0500, 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.
> 
> 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.
> 
> Changes in v2:
>  - Pimp kerneldoc for content_protection_property (Daniel)
>  - Drop sysfs attribute
> Changes in v3:
>  - None
> 
> Cc: Daniel Vetter <daniel.vetter@intel.com>
> 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 | 71 +++++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/drm_sysfs.c     |  1 +
>  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 c2da5585e201..676025d755b2 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -1196,6 +1196,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);
> @@ -1275,6 +1281,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 f14b48e6e839..8626aa8f485e 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
>   *
> @@ -764,6 +771,34 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
>   *      after modeset, the kernel driver may set this to "BAD" and issue a
>   *      hotplug uevent. Drivers should update this value using
>   *      drm_mode_connector_set_link_status_property().
> + * Content Protection:
> + *	This property is used by userspace to request the kernel protect future
> + *	content communicated over the link. When requested, kernel will apply
> + *	the appropriate means of protection (most often HDCP), and use the
> + *	property to tell userspace the protection is active.
> + *
> + *	The value of this property can be one of the following:
> + *
> + *	- DRM_MODE_CONTENT_PROTECTION_OFF = 0
> + *		The link is not protected, content is transmitted in the clear.
> + *	- DRM_MODE_CONTENT_PROTECTION_DESIRED = 1
> + *		Userspace has requested content protection, but the link is not
> + *		currently protected. When in this state, kernel should enable
> + *		Content Protection as soon as possible.
> + *	- DRM_MODE_CONTENT_PROTECTION_ENABLED = 2
> + *		Userspace has requested content protection, and the link is
> + *		protected. Only the driver can set the property to this value.
> + *		If userspace attempts to set to ENABLED, kernel will return
> + *		-EINVAL.
> + *
> + *	A few guidelines:
> + *
> + *	- DESIRED state should be preserved until userspace de-asserts it by
> + *	  setting the property to OFF. This means ENABLED should only transition
> + *	  to OFF when the user explicitly requests it.
> + *	- If the state is DESIRED, kernel should attempt to re-authenticate the
> + *	  link whenever possible. This includes across disable/enable, dpms,
> + *	  hotplug, downstream device changes, link status failures, etc..

Two bits missing imo:
- Should explain that userspace should poll this property to detect a
  change from ENABLED to DESIRED (and take adequate actions and e.g. stop
  the stream). No uevent will be sent out because the HDCP specs require
  polling anyway.
- One sentence to explain that drivers set this up using
  drm_connector_attach_content_protection_property().

Also, pls double-check the html output looks decent, you have quite some
fancy formatting in the above section :-)

With that all addressed:

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>


>   *
>   * Connectors also have one standardized atomic property:
>   *
> @@ -1047,6 +1082,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..2385c7e0bef5 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)
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 7a7140543012..828878addd03 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -370,6 +370,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;
>  };
>  
>  /**
> @@ -718,6 +724,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
> @@ -808,6 +815,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:
>  	 *
> @@ -1002,6 +1015,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,
> @@ -1010,6 +1024,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

-- 
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] 55+ messages in thread

* Re: [Intel-gfx] [PATCH v3 3/9] drm: Add Content Protection property
  2017-12-05 15:34     ` Daniel Vetter
@ 2017-12-05 15:40       ` Chris Wilson
  -1 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2017-12-05 15:40 UTC (permalink / raw)
  To: Daniel Vetter, Sean Paul
  Cc: seanpaul, David Airlie, intel-gfx, linux-kernel, dri-devel,
	daniel.vetter

Quoting Daniel Vetter (2017-12-05 15:34:36)
> Two bits missing imo:
> - Should explain that userspace should poll this property to detect a
>   change from ENABLED to DESIRED (and take adequate actions and e.g. stop
>   the stream). No uevent will be sent out because the HDCP specs require
>   polling anyway.

What's more polling than poll()? :-p
-Chris

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

* Re: [Intel-gfx] [PATCH v3 3/9] drm: Add Content Protection property
@ 2017-12-05 15:40       ` Chris Wilson
  0 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2017-12-05 15:40 UTC (permalink / raw)
  To: Daniel Vetter, Sean Paul
  Cc: seanpaul, David Airlie, intel-gfx, linux-kernel, dri-devel,
	daniel.vetter

Quoting Daniel Vetter (2017-12-05 15:34:36)
> Two bits missing imo:
> - Should explain that userspace should poll this property to detect a
>   change from ENABLED to DESIRED (and take adequate actions and e.g. stop
>   the stream). No uevent will be sent out because the HDCP specs require
>   polling anyway.

What's more polling than poll()? :-p
-Chris
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH v3 3/9] drm: Add Content Protection property
  2017-12-05 14:36       ` Sean Paul
@ 2017-12-05 16:10         ` Hans Verkuil
  2017-12-05 17:11         ` C, Ramalingam
  1 sibling, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2017-12-05 16:10 UTC (permalink / raw)
  To: Sean Paul, Ramalingam C; +Cc: dri-devel

On 12/05/17 15:36, Sean Paul wrote:
> On Tue, Dec 5, 2017 at 9:04 AM, Ramalingam C <ramalingam.c@intel.com> wrote:
>>
>>
>> On Tuesday 05 December 2017 01:37 PM, Hans Verkuil wrote:
>>
>> On 12/05/2017 06:15 AM, 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.
>>
>> 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.
>>
>> Changes in v2:
>>  - Pimp kerneldoc for content_protection_property (Daniel)
>>  - Drop sysfs attribute
>> Changes in v3:
>>  - None
>>
>> Cc: Daniel Vetter <daniel.vetter@intel.com>
>> 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 | 71
>> +++++++++++++++++++++++++++++++++++++++++
>>  drivers/gpu/drm/drm_sysfs.c     |  1 +
>>  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 c2da5585e201..676025d755b2 100644
>> --- a/drivers/gpu/drm/drm_atomic.c
>> +++ b/drivers/gpu/drm/drm_atomic.c
>> @@ -1196,6 +1196,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);
>> @@ -1275,6 +1281,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 f14b48e6e839..8626aa8f485e 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
>>   *
>> @@ -764,6 +771,34 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
>>   *      after modeset, the kernel driver may set this to "BAD" and issue a
>>   *      hotplug uevent. Drivers should update this value using
>>   *      drm_mode_connector_set_link_status_property().
>> + * Content Protection:
>> + * This property is used by userspace to request the kernel protect future
>> + * content communicated over the link. When requested, kernel will apply
>> + * the appropriate means of protection (most often HDCP), and use the
>> + * property to tell userspace the protection is active.
>> + *
>> + * The value of this property can be one of the following:
>> + *
>> + * - DRM_MODE_CONTENT_PROTECTION_OFF = 0
>> + * The link is not protected, content is transmitted in the clear.
>> + * - DRM_MODE_CONTENT_PROTECTION_DESIRED = 1
>> + * Userspace has requested content protection, but the link is not
>> + * currently protected. When in this state, kernel should enable
>> + * Content Protection as soon as possible.
>> + * - DRM_MODE_CONTENT_PROTECTION_ENABLED = 2
>> + * Userspace has requested content protection, and the link is
>> + * protected. Only the driver can set the property to this value.
>> + * If userspace attempts to set to ENABLED, kernel will return
>> + * -EINVAL.
>> + *
>> + * A few guidelines:
>> + *
>> + * - DESIRED state should be preserved until userspace de-asserts it by
>> + *  setting the property to OFF. This means ENABLED should only transition
>> + *  to OFF when the user explicitly requests it.
>> + * - If the state is DESIRED, kernel should attempt to re-authenticate the
>> + *  link whenever possible. This includes across disable/enable, dpms,
>> + *  hotplug, downstream device changes, link status failures, etc..
>>   *
>>   * Connectors also have one standardized atomic property:
>>   *
>> @@ -1047,6 +1082,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..2385c7e0bef5 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)
>> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
>> index 7a7140543012..828878addd03 100644
>> --- a/include/drm/drm_connector.h
>> +++ b/include/drm/drm_connector.h
>> @@ -370,6 +370,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;
>>  };
>>
>>  /**
>> @@ -718,6 +724,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
>> @@ -808,6 +815,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:
>>   *
>> @@ -1002,6 +1015,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,
>> @@ -1010,6 +1024,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
>>
>> What about HDCP 1.4 and HDCP 2.2? Userspace would need to know which version
>> was negotiated since content protected 4k videos require HDCP 2.2. Perhaps
>> provide a property with the HDCP version?
>>
>> I'm also missing a method for userspace to read the BKSV from the
>> transmitter.
>>
>> Hans,
>>
>> I guess you are asking about the use case explained at
>> http://www.spinics.net/lists/intel-gfx/msg134813.html
>>
>> Sean,
>> As this solution is only for 1.4, this version requirement might not matter
>> here.
>> But as a community could we at least ack such a need, so that inevitable
>> extension of this uAPI will be well thought about?
>>
>> As we discussed before Enum values required are
>>
>> -OFF           : Disable any content protection
>> -DESIRED       : For any possible HDCP protection (v1.4/2.2)
>> -DESIRED_TYPE1 : For HDCP2.2 only
>> -ENABLED       : Highest HDCP protection is enabled (Could be v1.4/2.2)
>> -ENABLED_TYPE1 : HDCP2.2 enabled
>>
> 
> I'd rather keep the property as-is and expose an HDCP version property
> alongside it (or perhaps something more elaborate that includes bksv
> and the downstream bksvs). The reason I prefer that is it will also
> cover the 1.2 vs 1.4 difference that is a more immediate need.

I agree with that. 'DESIRED' negotiates the 'highest' level it can, and all
that's needed is to report what that level is back to userspace.

I think it makes sense to add the version property now, otherwise applications
would have to add logic like: 'if property HDCP_VERSION exists, then use it,
otherwise assume HDCP 1.4', which is rather awkward.

Exporting the BKSV(s) can be postponed. If it is not there, then you simply
cannot perform certain functions. It would be nice if it is documented as a
TODO somewhere.

> This property is intentionally vague about the underlying encryption,
> and tying it to HDCP (and specific versions, at that) is not
> consistent with the design.

Indeed.

Regards,

	Hans

> 
> 
>> And another gap i am seeing with this uAPI is that there is no communication
>> back about the
>> HDCP authentication failure, as DESIRED will remain without transitioning
>> into ENABLED.
>> So userspace has to identify the failure of the HDCP req with polling and
>> Timeout.
>> Timeout also will vary with system to system, as
>>     with big downstream topology(worst case 127 devices with 7depth)
>> authentication could take 5+ Sec.
>>     with only receiver < few 100 mSec(~200mSec?)
>>
> 
> It's worked on CrOS for a number of years now, why change what isn't broken?
> 
> Sean
> 
>>
>> So could it help userspace if we could indicate the authentication failure.
>> Agreed that runtime link integrity lost is indicated by the ENABLED->DESIRED
>> transition.
>>
>> --Ram
>>
>>
>> Regards,
>>
>> Hans
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>
>>
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>

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

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

* Re: [PATCH v3 5/9] drm/i915: Add HDCP framework + base implementation
  2017-12-05  5:15   ` Sean Paul
@ 2017-12-05 17:00     ` Daniel Vetter
  -1 siblings, 0 replies; 55+ messages in thread
From: Daniel Vetter @ 2017-12-05 17:00 UTC (permalink / raw)
  To: Sean Paul
  Cc: dri-devel, intel-gfx, seanpaul, David Airlie, Joonas Lahtinen,
	linux-kernel, Rodrigo Vivi, daniel.vetter

On Tue, Dec 05, 2017 at 12:15:04AM -0500, 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.
> 
> Changes in v2:
> - Don't open code wait_fors (Chris)
> - drm_hdcp.c under MIT license (Daniel)
> - Move intel_hdcp_disable() call above ddi_disable (Ram)
> - Fix // comments (I wore a cone of shame for 12 hours to atone) (Daniel)
> - Justify intel_hdcp_shim with comments (Daniel)
> - Fixed async locking issues by adding hdcp_mutex (Daniel)
> - Don't alter connector_state in enable/disable (Daniel)
> Changes in v3:
> - Added hdcp_mutex/hdcp_value to make async reasonable
> - Added hdcp_prop_work to separate link checking & property setting
> - Added new helper for atomic_check state tracking (Daniel)
> - Moved enable/disable into atomic_commit with matching helpers
> - Moved intel_hdcp_check_link out of all locks when called from dp
> - Bumped up ksv_fifo timeout (noticed failure on one of my dongles)
> 
> Cc: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Daniel Vetter <daniel.vetter@intel.com>
> Cc: Ramalingam C <ramalingam.c@intel.com>
> 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  |   2 +
>  drivers/gpu/drm/i915/intel_display.c |  14 +
>  drivers/gpu/drm/i915/intel_drv.h     |  88 +++++
>  drivers/gpu/drm/i915/intel_hdcp.c    | 731 +++++++++++++++++++++++++++++++++++
>  6 files changed, 919 insertions(+)
>  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 42bc8bd4ff06..3facea4eefdb 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -107,6 +107,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 09bf043c1c2e..2bd2cc8441d4 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -8034,6 +8034,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

SKL_ prefix feels right here, since this is for skl, kbl, ... only, and
doesn't apply to bxt. So not gen9 stuff.

>  #define   SKL_PCODE_CDCLK_CONTROL		0x7
>  #define     SKL_CDCLK_PREPARE_FOR_CHANGE	0x3
>  #define     SKL_CDCLK_READY_FOR_CHANGE		0x1
> @@ -8335,6 +8336,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_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? */

Yeah that's one good wtf :-)

> +#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)

Except for the SKL+ specific GT mailbox bit all the above registers are
generic (except the 3 bits in KEY_STATUS I highlighted). Can you pls sed
the entire patch and drop the SKL_ prefix from all of them?

I know it's a bit busywork, but given how the bits move around between
platforms in funny ways it's good prep for enabling hdcp on more platforms
I think.

If it's too messy feel free to apply the bikeshed in a follow-up patch.

Either way all the above looks correct.

> +
>  /* 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..d452c327dc1d 100644
> --- a/drivers/gpu/drm/i915/intel_atomic.c
> +++ b/drivers/gpu/drm/i915/intel_atomic.c
> @@ -110,6 +110,8 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
>  		to_intel_digital_connector_state(old_state);
>  	struct drm_crtc_state *crtc_state;
>  
> +	intel_hdcp_atomic_check(conn, old_state, new_state);
> +
>  	if (!new_state->crtc)
>  		return 0;
>  
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index 601c23be8264..f45c468abf98 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -12319,6 +12319,8 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>  	struct drm_i915_private *dev_priv = to_i915(dev);
>  	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
>  	struct drm_crtc *crtc;
> +	struct drm_connector_state *old_conn_state, *new_conn_state;
> +	struct drm_connector *connector;
>  	struct intel_crtc_state *intel_cstate;
>  	u64 put_domains[I915_MAX_PIPES] = {};
>  	int i;
> @@ -12408,9 +12410,17 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>  		}
>  	}
>  
> +	for_each_oldnew_connector_in_state(state, connector, old_conn_state,
> +					   new_conn_state, i)
> +		intel_hdcp_atomic_pre_commit(connector, old_conn_state,
> +					     new_conn_state);
> +
>  	/* Now enable the clocks, plane, pipe, and connectors that we set up. */
>  	dev_priv->display.update_crtcs(state);
>  
> +	for_each_new_connector_in_state(state, connector, new_conn_state, i)
> +		intel_hdcp_atomic_commit(connector, new_conn_state);

Hm, I liked the old place where this was called from ddi enable/disable
hooks much better. HDCP very much is a connector thing, leaking that
through the entire layer-cake that is atomic to the highest level feels a
bit wrong.

Why?

Filling in after an irc chat with Sean: It's leftovers from an attempt at
trying to implement HDCP state changes without a full modeset. Imo that
should be undone again to the old version, for a few reasons:

- Atomic is damn hard to understand already, leaking a connector thing
  through all layers makes it worse. And we're probably already over the
  limit of complexity that can be understood, so making abstractions more
  leaky needs some real good reasons.

- Ville is working on a slightly different approach to allow
  connectors/encoders to fix up stuff if no modeset happened (new separate
  callback). This here doesn't align with that plan.

> +
>  	/* FIXME: We should call drm_atomic_helper_commit_hw_done() here
>  	 * already, but still need the state for the delayed optimization. To
>  	 * fix this:
> @@ -15322,6 +15332,10 @@ static void intel_hpd_poll_fini(struct drm_device *dev)
>  	for_each_intel_connector_iter(connector, &conn_iter) {
>  		if (connector->modeset_retry_work.func)
>  			cancel_work_sync(&connector->modeset_retry_work);
> +		if (connector->hdcp_shim) {
> +			cancel_delayed_work_sync(&connector->hdcp_check_work);
> +			cancel_work_sync(&connector->hdcp_prop_work);
> +		}
>  	}
>  	drm_connector_list_iter_end(&conn_iter);
>  }
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 852b3d161754..6f47a4227f5f 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -301,6 +301,76 @@ struct intel_panel {
>  	} backlight;
>  };
>  
> +/*
> + * This structure serves as a translation layer between the generic HDCP code
> + * and the bus-specific code. What that means is that HDCP over HDMI differs
> + * from HDCP over DP, so to account for these differences, we need to
> + * communicate with the receiver through this shim.
> + *
> + * For completeness, the 2 buses differ in the following ways:
> + *	- DP AUX vs. DDC
> + *		HDCP registers on the receiver are set via DP AUX for DP, and
> + *		they are set via DDC for HDMI.
> + *	- Receiver register offsets
> + *		The offsets of the registers are different for DP vs. HDMI
> + *	- Receiver register masks/offsets
> + *		For instance, the ready bit for the KSV fifo is in a different
> + *		place on DP vs HDMI
> + *	- Receiver register names
> + *		Seriously. In the DP spec, the 16-bit register containing
> + *		downstream information is called BINFO, on HDMI it's called
> + *		BSTATUS. To confuse matters further, DP has a BSTATUS register
> + *		with a completely different definition.
> + *	- KSV FIFO
> + *		On HDMI, the ksv fifo is read all at once, whereas on DP it must
> + *		be read 3 keys at a time
> + *	- Aksv output
> + *		Since Aksv is hidden in hardware, there's different procedures
> + *		to send it over DP AUX vs DDC
> + */
> +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;
>  	/*
> @@ -332,6 +402,12 @@ 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 mutex hdcp_mutex;
> +	uint64_t hdcp_value; /* protected by hdcp_mutex */
> +	struct delayed_work hdcp_check_work;
> +	struct work_struct hdcp_prop_work;
>  };
>  
>  struct intel_digital_connector_state {
> @@ -1763,6 +1839,18 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
>  }
>  #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
>  
> +/* intel_hdcp.c */
> +void intel_hdcp_atomic_check(struct drm_connector *connector,
> +			     struct drm_connector_state *old_state,
> +			     struct drm_connector_state *new_state);
> +void intel_hdcp_atomic_pre_commit(struct drm_connector *connector,
> +			          struct drm_connector_state *old_state,
> +			          struct drm_connector_state *new_state);
> +void intel_hdcp_atomic_commit(struct drm_connector *connector,
> +			      struct drm_connector_state *new_state);
> +int intel_hdcp_check_link(struct intel_connector *connector);
> +void intel_hdcp_check_work(struct work_struct *work);
> +void intel_hdcp_prop_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..4eef9505410f
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_hdcp.c
> @@ -0,0 +1,731 @@
> +/*
> + * Copyright (C) 2017 Google, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + * Authors:
> + * Sean Paul <seanpaul@chromium.org>
> + */
> +
> +#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)
> +{
> +	int ret, read_ret;
> +	bool ksv_ready;
> +
> +	ret = __wait_for(read_ret = shim->read_ksv_ready(intel_dig_port,
> +							 &ksv_ready),
> +			 read_ret || ksv_ready, 1500 * 1000, 1000, 100 * 1000);
> +	if (ret)
> +		return ret;
> +	if (read_ret)
> +		return read_ret;
> +	if (!ksv_ready)
> +		return -ETIMEDOUT;
> +
> +	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)
> +{
> +	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) */
> +	ret = __intel_wait_for_register(dev_priv, SKL_HDCP_KEY_STATUS,
> +					SKL_HDCP_KEY_LOAD_DONE,
> +					SKL_HDCP_KEY_LOAD_DONE,
> +					10, 1, &val);
> +	if (ret)
> +		return ret;
> +	else 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 (intel_wait_for_register(dev_priv, SKL_HDCP_REP_CTL,
> +				    SKL_HDCP_SHA1_READY,
> +				    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->base.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;
> +	}

I'd have extracted a little helper to write teh sha_text bytewise which
internally accumulates it and pushes it out every 32 bytes. Would neatly
separate what we write from how the sha machinery works.

But this a bit more verbose style works too I guess, so just an idea in
case we ever spot a bug in here (I didn't).

> +
> +	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;
> +
> +	/* 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 (intel_wait_for_register(dev_priv, SKL_HDCP_REP_CTL,
> +				    SKL_HDCP_SHA1_COMPLETE,
> +				    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->base.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());

nit: get_random_u32 instad of get_random_long?

> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_CAPTURE_AN);
> +
> +	/* Wait for An to be acquired */
> +	if (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port),
> +				    SKL_HDCP_STATUS_AN_READY,
> +				    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 (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port),
> +				    SKL_HDCP_STATUS_ENC,
> +				    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.
> +	 */

Just an aside: I think we can stuff the DDI stream specific stuff into
the toggle_signalling callback. But not entirely sure.

> +
> +	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->base.port;
> +	int ret;
> +
> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
> +	if (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port), ~0, 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_check_work(struct work_struct *work)
> +{
> +	struct intel_connector *connector = container_of(to_delayed_work(work),
> +							 struct intel_connector,
> +						         hdcp_check_work);
> +	if (!intel_hdcp_check_link(connector))
> +		schedule_delayed_work(&connector->hdcp_check_work,
> +				      DRM_HDCP_CHECK_PERIOD_MS);
> +}
> +
> +void intel_hdcp_prop_work(struct work_struct *work)
> +{
> +	struct intel_connector *connector = container_of(work,
> +							 struct intel_connector,
> +						         hdcp_prop_work);
> +	struct drm_device *dev = connector->base.dev;
> +	struct drm_connector_state *state;
> +
> +	mutex_lock(&dev->mode_config.mutex);

You don't need this lock here. Only connection_mutex is needed to protect
connector->status.

> +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> +	mutex_lock(&connector->hdcp_mutex);
> +
> +	/*
> +	 * This worker is only used to flip between ENABLED/DESIRED. Either of
> +	 * those to OFF is handled by core. If hdcp_value == OFF, we're running
> +	 * just after hdcp has been disabled, so just exit
> +	 */
> +	if (connector->hdcp_value != DRM_MODE_CONTENT_PROTECTION_OFF) {
> +		state = connector->base.state;
> +		state->content_protection = connector->hdcp_value;
> +	}
> +
> +	mutex_unlock(&connector->hdcp_mutex);
> +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
> +	mutex_unlock(&dev->mode_config.mutex);
> +}
> +
> +static int intel_hdcp_enable(struct intel_connector *connector)
> +{
> +	int ret;
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	mutex_lock(&connector->hdcp_mutex);
> +
> +	ret = _intel_hdcp_enable(connector);
> +	if (ret)
> +		goto out;
> +
> +	connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
> +	schedule_work(&connector->hdcp_prop_work);
> +	schedule_delayed_work(&connector->hdcp_check_work,
> +			      DRM_HDCP_CHECK_PERIOD_MS);
> +out:
> +	mutex_unlock(&connector->hdcp_mutex);
> +	return ret;
> +}
> +
> +static int intel_hdcp_disable(struct intel_connector *connector)
> +{
> +	int ret;
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	mutex_lock(&connector->hdcp_mutex);
> +
> +	connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_OFF;
> +	ret = _intel_hdcp_disable(connector);
> +
> +	mutex_unlock(&connector->hdcp_mutex);
> +	cancel_delayed_work_sync(&connector->hdcp_check_work);
> +	return ret;
> +}
> +
> +void intel_hdcp_atomic_check(struct drm_connector *connector,
> +			     struct drm_connector_state *old_state,
> +			     struct drm_connector_state *new_state)
> +{
> +	uint64_t old_cp = old_state->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;
> +	}
> +
> +	/* Only drivers can set content protection enabled */
> +	if (old_cp != DRM_MODE_CONTENT_PROTECTION_ENABLED &&
> +	    new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
> +		new_state->content_protection =
> +			DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +}
> +
> +void intel_hdcp_atomic_pre_commit(struct drm_connector *connector,
> +			          struct drm_connector_state *old_state,
> +			          struct drm_connector_state *new_state)
> +{
> +	uint64_t old_cp = old_state->content_protection;
> +	uint64_t new_cp = new_state->content_protection;
> +
> +	/*
> +	 * Disable HDCP if the connector is becoming disabled, or if requested
> +	 * via the property.
> +	 */
> +	if ((!new_state->crtc && old_cp != DRM_MODE_CONTENT_PROTECTION_OFF) ||
> +	    (new_state->crtc && old_cp != DRM_MODE_CONTENT_PROTECTION_OFF &&
> +	     new_cp == DRM_MODE_CONTENT_PROTECTION_OFF))
> +		intel_hdcp_disable(to_intel_connector(connector));
> +}
> +
> +void intel_hdcp_atomic_commit(struct drm_connector *connector,
> +			      struct drm_connector_state *new_state)
> +{
> +	uint64_t new_cp = new_state->content_protection;
> +
> +	/* Enable hdcp if it's desired */
> +	if (new_state->crtc && new_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED)
> +		intel_hdcp_enable(to_intel_connector(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 intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
> +	enum port port = intel_dig_port->base.port;
> +	int ret = 0;
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	mutex_lock(&connector->hdcp_mutex);
> +
> +	if (connector->hdcp_value == DRM_MODE_CONTENT_PROTECTION_OFF)
> +		goto out;
> +
> +	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;
> +		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +		schedule_work(&connector->hdcp_prop_work);
> +		goto out;
> +	}
> +
> +	if (connector->hdcp_shim->check_link(intel_dig_port)) {
> +		if (connector->hdcp_value != DRM_MODE_CONTENT_PROTECTION_OFF) {
> +			connector->hdcp_value =
> +				DRM_MODE_CONTENT_PROTECTION_ENABLED;
> +			schedule_work(&connector->hdcp_prop_work);
> +		}
> +		goto out;
> +	}
> +
> +	DRM_INFO("HDCP link failed, retrying authentication\n");
> +
> +	ret = _intel_hdcp_disable(connector);
> +	if (ret) {
> +		DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
> +		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +		schedule_work(&connector->hdcp_prop_work);
> +		goto out;
> +	}
> +
> +	ret = _intel_hdcp_enable(connector);
> +	if (ret) {
> +		DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
> +		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +		schedule_work(&connector->hdcp_prop_work);
> +		goto out;
> +	}
> +
> +out:
> +	mutex_unlock(&connector->hdcp_mutex);
> +	return ret;
> +}

With the comments addressed:

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

Also counts for the reg renaming patch if you want to do that as a
follow-up in this series :-)
-Daniel

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

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

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

* Re: [PATCH v3 5/9] drm/i915: Add HDCP framework + base implementation
@ 2017-12-05 17:00     ` Daniel Vetter
  0 siblings, 0 replies; 55+ messages in thread
From: Daniel Vetter @ 2017-12-05 17:00 UTC (permalink / raw)
  To: Sean Paul
  Cc: seanpaul, David Airlie, intel-gfx, Joonas Lahtinen, linux-kernel,
	dri-devel, Rodrigo Vivi, daniel.vetter

On Tue, Dec 05, 2017 at 12:15:04AM -0500, 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.
> 
> Changes in v2:
> - Don't open code wait_fors (Chris)
> - drm_hdcp.c under MIT license (Daniel)
> - Move intel_hdcp_disable() call above ddi_disable (Ram)
> - Fix // comments (I wore a cone of shame for 12 hours to atone) (Daniel)
> - Justify intel_hdcp_shim with comments (Daniel)
> - Fixed async locking issues by adding hdcp_mutex (Daniel)
> - Don't alter connector_state in enable/disable (Daniel)
> Changes in v3:
> - Added hdcp_mutex/hdcp_value to make async reasonable
> - Added hdcp_prop_work to separate link checking & property setting
> - Added new helper for atomic_check state tracking (Daniel)
> - Moved enable/disable into atomic_commit with matching helpers
> - Moved intel_hdcp_check_link out of all locks when called from dp
> - Bumped up ksv_fifo timeout (noticed failure on one of my dongles)
> 
> Cc: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Daniel Vetter <daniel.vetter@intel.com>
> Cc: Ramalingam C <ramalingam.c@intel.com>
> 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  |   2 +
>  drivers/gpu/drm/i915/intel_display.c |  14 +
>  drivers/gpu/drm/i915/intel_drv.h     |  88 +++++
>  drivers/gpu/drm/i915/intel_hdcp.c    | 731 +++++++++++++++++++++++++++++++++++
>  6 files changed, 919 insertions(+)
>  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 42bc8bd4ff06..3facea4eefdb 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -107,6 +107,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 09bf043c1c2e..2bd2cc8441d4 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -8034,6 +8034,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

SKL_ prefix feels right here, since this is for skl, kbl, ... only, and
doesn't apply to bxt. So not gen9 stuff.

>  #define   SKL_PCODE_CDCLK_CONTROL		0x7
>  #define     SKL_CDCLK_PREPARE_FOR_CHANGE	0x3
>  #define     SKL_CDCLK_READY_FOR_CHANGE		0x1
> @@ -8335,6 +8336,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_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? */

Yeah that's one good wtf :-)

> +#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)

Except for the SKL+ specific GT mailbox bit all the above registers are
generic (except the 3 bits in KEY_STATUS I highlighted). Can you pls sed
the entire patch and drop the SKL_ prefix from all of them?

I know it's a bit busywork, but given how the bits move around between
platforms in funny ways it's good prep for enabling hdcp on more platforms
I think.

If it's too messy feel free to apply the bikeshed in a follow-up patch.

Either way all the above looks correct.

> +
>  /* 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..d452c327dc1d 100644
> --- a/drivers/gpu/drm/i915/intel_atomic.c
> +++ b/drivers/gpu/drm/i915/intel_atomic.c
> @@ -110,6 +110,8 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
>  		to_intel_digital_connector_state(old_state);
>  	struct drm_crtc_state *crtc_state;
>  
> +	intel_hdcp_atomic_check(conn, old_state, new_state);
> +
>  	if (!new_state->crtc)
>  		return 0;
>  
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index 601c23be8264..f45c468abf98 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -12319,6 +12319,8 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>  	struct drm_i915_private *dev_priv = to_i915(dev);
>  	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
>  	struct drm_crtc *crtc;
> +	struct drm_connector_state *old_conn_state, *new_conn_state;
> +	struct drm_connector *connector;
>  	struct intel_crtc_state *intel_cstate;
>  	u64 put_domains[I915_MAX_PIPES] = {};
>  	int i;
> @@ -12408,9 +12410,17 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>  		}
>  	}
>  
> +	for_each_oldnew_connector_in_state(state, connector, old_conn_state,
> +					   new_conn_state, i)
> +		intel_hdcp_atomic_pre_commit(connector, old_conn_state,
> +					     new_conn_state);
> +
>  	/* Now enable the clocks, plane, pipe, and connectors that we set up. */
>  	dev_priv->display.update_crtcs(state);
>  
> +	for_each_new_connector_in_state(state, connector, new_conn_state, i)
> +		intel_hdcp_atomic_commit(connector, new_conn_state);

Hm, I liked the old place where this was called from ddi enable/disable
hooks much better. HDCP very much is a connector thing, leaking that
through the entire layer-cake that is atomic to the highest level feels a
bit wrong.

Why?

Filling in after an irc chat with Sean: It's leftovers from an attempt at
trying to implement HDCP state changes without a full modeset. Imo that
should be undone again to the old version, for a few reasons:

- Atomic is damn hard to understand already, leaking a connector thing
  through all layers makes it worse. And we're probably already over the
  limit of complexity that can be understood, so making abstractions more
  leaky needs some real good reasons.

- Ville is working on a slightly different approach to allow
  connectors/encoders to fix up stuff if no modeset happened (new separate
  callback). This here doesn't align with that plan.

> +
>  	/* FIXME: We should call drm_atomic_helper_commit_hw_done() here
>  	 * already, but still need the state for the delayed optimization. To
>  	 * fix this:
> @@ -15322,6 +15332,10 @@ static void intel_hpd_poll_fini(struct drm_device *dev)
>  	for_each_intel_connector_iter(connector, &conn_iter) {
>  		if (connector->modeset_retry_work.func)
>  			cancel_work_sync(&connector->modeset_retry_work);
> +		if (connector->hdcp_shim) {
> +			cancel_delayed_work_sync(&connector->hdcp_check_work);
> +			cancel_work_sync(&connector->hdcp_prop_work);
> +		}
>  	}
>  	drm_connector_list_iter_end(&conn_iter);
>  }
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 852b3d161754..6f47a4227f5f 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -301,6 +301,76 @@ struct intel_panel {
>  	} backlight;
>  };
>  
> +/*
> + * This structure serves as a translation layer between the generic HDCP code
> + * and the bus-specific code. What that means is that HDCP over HDMI differs
> + * from HDCP over DP, so to account for these differences, we need to
> + * communicate with the receiver through this shim.
> + *
> + * For completeness, the 2 buses differ in the following ways:
> + *	- DP AUX vs. DDC
> + *		HDCP registers on the receiver are set via DP AUX for DP, and
> + *		they are set via DDC for HDMI.
> + *	- Receiver register offsets
> + *		The offsets of the registers are different for DP vs. HDMI
> + *	- Receiver register masks/offsets
> + *		For instance, the ready bit for the KSV fifo is in a different
> + *		place on DP vs HDMI
> + *	- Receiver register names
> + *		Seriously. In the DP spec, the 16-bit register containing
> + *		downstream information is called BINFO, on HDMI it's called
> + *		BSTATUS. To confuse matters further, DP has a BSTATUS register
> + *		with a completely different definition.
> + *	- KSV FIFO
> + *		On HDMI, the ksv fifo is read all at once, whereas on DP it must
> + *		be read 3 keys at a time
> + *	- Aksv output
> + *		Since Aksv is hidden in hardware, there's different procedures
> + *		to send it over DP AUX vs DDC
> + */
> +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;
>  	/*
> @@ -332,6 +402,12 @@ 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 mutex hdcp_mutex;
> +	uint64_t hdcp_value; /* protected by hdcp_mutex */
> +	struct delayed_work hdcp_check_work;
> +	struct work_struct hdcp_prop_work;
>  };
>  
>  struct intel_digital_connector_state {
> @@ -1763,6 +1839,18 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
>  }
>  #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
>  
> +/* intel_hdcp.c */
> +void intel_hdcp_atomic_check(struct drm_connector *connector,
> +			     struct drm_connector_state *old_state,
> +			     struct drm_connector_state *new_state);
> +void intel_hdcp_atomic_pre_commit(struct drm_connector *connector,
> +			          struct drm_connector_state *old_state,
> +			          struct drm_connector_state *new_state);
> +void intel_hdcp_atomic_commit(struct drm_connector *connector,
> +			      struct drm_connector_state *new_state);
> +int intel_hdcp_check_link(struct intel_connector *connector);
> +void intel_hdcp_check_work(struct work_struct *work);
> +void intel_hdcp_prop_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..4eef9505410f
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_hdcp.c
> @@ -0,0 +1,731 @@
> +/*
> + * Copyright (C) 2017 Google, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + * Authors:
> + * Sean Paul <seanpaul@chromium.org>
> + */
> +
> +#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)
> +{
> +	int ret, read_ret;
> +	bool ksv_ready;
> +
> +	ret = __wait_for(read_ret = shim->read_ksv_ready(intel_dig_port,
> +							 &ksv_ready),
> +			 read_ret || ksv_ready, 1500 * 1000, 1000, 100 * 1000);
> +	if (ret)
> +		return ret;
> +	if (read_ret)
> +		return read_ret;
> +	if (!ksv_ready)
> +		return -ETIMEDOUT;
> +
> +	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)
> +{
> +	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) */
> +	ret = __intel_wait_for_register(dev_priv, SKL_HDCP_KEY_STATUS,
> +					SKL_HDCP_KEY_LOAD_DONE,
> +					SKL_HDCP_KEY_LOAD_DONE,
> +					10, 1, &val);
> +	if (ret)
> +		return ret;
> +	else 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 (intel_wait_for_register(dev_priv, SKL_HDCP_REP_CTL,
> +				    SKL_HDCP_SHA1_READY,
> +				    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->base.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;
> +	}

I'd have extracted a little helper to write teh sha_text bytewise which
internally accumulates it and pushes it out every 32 bytes. Would neatly
separate what we write from how the sha machinery works.

But this a bit more verbose style works too I guess, so just an idea in
case we ever spot a bug in here (I didn't).

> +
> +	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;
> +
> +	/* 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 (intel_wait_for_register(dev_priv, SKL_HDCP_REP_CTL,
> +				    SKL_HDCP_SHA1_COMPLETE,
> +				    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->base.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());

nit: get_random_u32 instad of get_random_long?

> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), SKL_HDCP_CONF_CAPTURE_AN);
> +
> +	/* Wait for An to be acquired */
> +	if (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port),
> +				    SKL_HDCP_STATUS_AN_READY,
> +				    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 (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port),
> +				    SKL_HDCP_STATUS_ENC,
> +				    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.
> +	 */

Just an aside: I think we can stuff the DDI stream specific stuff into
the toggle_signalling callback. But not entirely sure.

> +
> +	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->base.port;
> +	int ret;
> +
> +	I915_WRITE(SKL_PORT_HDCP_CONF(port), 0);
> +	if (intel_wait_for_register(dev_priv, SKL_PORT_HDCP_STATUS(port), ~0, 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_check_work(struct work_struct *work)
> +{
> +	struct intel_connector *connector = container_of(to_delayed_work(work),
> +							 struct intel_connector,
> +						         hdcp_check_work);
> +	if (!intel_hdcp_check_link(connector))
> +		schedule_delayed_work(&connector->hdcp_check_work,
> +				      DRM_HDCP_CHECK_PERIOD_MS);
> +}
> +
> +void intel_hdcp_prop_work(struct work_struct *work)
> +{
> +	struct intel_connector *connector = container_of(work,
> +							 struct intel_connector,
> +						         hdcp_prop_work);
> +	struct drm_device *dev = connector->base.dev;
> +	struct drm_connector_state *state;
> +
> +	mutex_lock(&dev->mode_config.mutex);

You don't need this lock here. Only connection_mutex is needed to protect
connector->status.

> +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> +	mutex_lock(&connector->hdcp_mutex);
> +
> +	/*
> +	 * This worker is only used to flip between ENABLED/DESIRED. Either of
> +	 * those to OFF is handled by core. If hdcp_value == OFF, we're running
> +	 * just after hdcp has been disabled, so just exit
> +	 */
> +	if (connector->hdcp_value != DRM_MODE_CONTENT_PROTECTION_OFF) {
> +		state = connector->base.state;
> +		state->content_protection = connector->hdcp_value;
> +	}
> +
> +	mutex_unlock(&connector->hdcp_mutex);
> +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
> +	mutex_unlock(&dev->mode_config.mutex);
> +}
> +
> +static int intel_hdcp_enable(struct intel_connector *connector)
> +{
> +	int ret;
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	mutex_lock(&connector->hdcp_mutex);
> +
> +	ret = _intel_hdcp_enable(connector);
> +	if (ret)
> +		goto out;
> +
> +	connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
> +	schedule_work(&connector->hdcp_prop_work);
> +	schedule_delayed_work(&connector->hdcp_check_work,
> +			      DRM_HDCP_CHECK_PERIOD_MS);
> +out:
> +	mutex_unlock(&connector->hdcp_mutex);
> +	return ret;
> +}
> +
> +static int intel_hdcp_disable(struct intel_connector *connector)
> +{
> +	int ret;
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	mutex_lock(&connector->hdcp_mutex);
> +
> +	connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_OFF;
> +	ret = _intel_hdcp_disable(connector);
> +
> +	mutex_unlock(&connector->hdcp_mutex);
> +	cancel_delayed_work_sync(&connector->hdcp_check_work);
> +	return ret;
> +}
> +
> +void intel_hdcp_atomic_check(struct drm_connector *connector,
> +			     struct drm_connector_state *old_state,
> +			     struct drm_connector_state *new_state)
> +{
> +	uint64_t old_cp = old_state->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;
> +	}
> +
> +	/* Only drivers can set content protection enabled */
> +	if (old_cp != DRM_MODE_CONTENT_PROTECTION_ENABLED &&
> +	    new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
> +		new_state->content_protection =
> +			DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +}
> +
> +void intel_hdcp_atomic_pre_commit(struct drm_connector *connector,
> +			          struct drm_connector_state *old_state,
> +			          struct drm_connector_state *new_state)
> +{
> +	uint64_t old_cp = old_state->content_protection;
> +	uint64_t new_cp = new_state->content_protection;
> +
> +	/*
> +	 * Disable HDCP if the connector is becoming disabled, or if requested
> +	 * via the property.
> +	 */
> +	if ((!new_state->crtc && old_cp != DRM_MODE_CONTENT_PROTECTION_OFF) ||
> +	    (new_state->crtc && old_cp != DRM_MODE_CONTENT_PROTECTION_OFF &&
> +	     new_cp == DRM_MODE_CONTENT_PROTECTION_OFF))
> +		intel_hdcp_disable(to_intel_connector(connector));
> +}
> +
> +void intel_hdcp_atomic_commit(struct drm_connector *connector,
> +			      struct drm_connector_state *new_state)
> +{
> +	uint64_t new_cp = new_state->content_protection;
> +
> +	/* Enable hdcp if it's desired */
> +	if (new_state->crtc && new_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED)
> +		intel_hdcp_enable(to_intel_connector(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 intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
> +	enum port port = intel_dig_port->base.port;
> +	int ret = 0;
> +
> +	if (!connector->hdcp_shim)
> +		return -ENOENT;
> +
> +	mutex_lock(&connector->hdcp_mutex);
> +
> +	if (connector->hdcp_value == DRM_MODE_CONTENT_PROTECTION_OFF)
> +		goto out;
> +
> +	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;
> +		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +		schedule_work(&connector->hdcp_prop_work);
> +		goto out;
> +	}
> +
> +	if (connector->hdcp_shim->check_link(intel_dig_port)) {
> +		if (connector->hdcp_value != DRM_MODE_CONTENT_PROTECTION_OFF) {
> +			connector->hdcp_value =
> +				DRM_MODE_CONTENT_PROTECTION_ENABLED;
> +			schedule_work(&connector->hdcp_prop_work);
> +		}
> +		goto out;
> +	}
> +
> +	DRM_INFO("HDCP link failed, retrying authentication\n");
> +
> +	ret = _intel_hdcp_disable(connector);
> +	if (ret) {
> +		DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
> +		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +		schedule_work(&connector->hdcp_prop_work);
> +		goto out;
> +	}
> +
> +	ret = _intel_hdcp_enable(connector);
> +	if (ret) {
> +		DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
> +		connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
> +		schedule_work(&connector->hdcp_prop_work);
> +		goto out;
> +	}
> +
> +out:
> +	mutex_unlock(&connector->hdcp_mutex);
> +	return ret;
> +}

With the comments addressed:

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

Also counts for the reg renaming patch if you want to do that as a
follow-up in this series :-)
-Daniel

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

-- 
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] 55+ messages in thread

* Re: [Intel-gfx] [PATCH v3 6/9] drm/i915: Make use of indexed write GMBUS feature
  2017-12-05  5:15   ` Sean Paul
  (?)
@ 2017-12-05 17:01   ` Daniel Vetter
  -1 siblings, 0 replies; 55+ messages in thread
From: Daniel Vetter @ 2017-12-05 17:01 UTC (permalink / raw)
  To: Sean Paul
  Cc: dri-devel, intel-gfx, seanpaul, David Airlie, linux-kernel,
	Rodrigo Vivi, daniel.vetter

On Tue, Dec 05, 2017 at 12:15:05AM -0500, Sean Paul wrote:
> This patch enables the indexed write feature of the GMBUS to concatenate
> 2 consecutive messages into one. The criteria for an indexed write is
> that both messages are writes, the first is length == 1, and the second
> is length > 0. The first message is sent out by the GMBUS as the slave
> command, and the second one is sent via the GMBUS FIFO as usual.
> 
> Changes in v3:
> - Added to series
> 
> Suggested-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Signed-off-by: Sean Paul <seanpaul@chromium.org>

lgtm. Will probably never see a user except the aksv write, but oh well
:-)

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> ---
>  drivers/gpu/drm/i915/intel_i2c.c | 39 ++++++++++++++++++++++++++++++++++-----
>  1 file changed, 34 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
> index 49fdf09f9919..7399009aee0a 100644
> --- a/drivers/gpu/drm/i915/intel_i2c.c
> +++ b/drivers/gpu/drm/i915/intel_i2c.c
> @@ -373,7 +373,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 +387,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 +410,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 +421,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;
>  
> @@ -430,6 +433,14 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
>  	return 0;
>  }
>  
> +static int
> +gmbus_xfer_index_write(struct drm_i915_private *dev_priv, u8 cmd,
> +		       struct i2c_msg *msg)
> +{
> +	u8 gmbus1_index = GMBUS_CYCLE_INDEX | (cmd << GMBUS_SLAVE_INDEX_SHIFT);
> +	return gmbus_xfer_write(dev_priv, msg, gmbus1_index);
> +}
> +
>  /*
>   * The gmbus controller can combine a 1 or 2 byte write with a read that
>   * immediately follows it by using an "INDEX" cycle.
> @@ -444,6 +455,20 @@ gmbus_is_index_read(struct i2c_msg *msgs, int i, int num)
>  		(msgs[i + 1].flags & I2C_M_RD));
>  }
>  
> +/*
> + * The gmbus controller can combine a 2-msg write into a single write that
> + * immediately follows it by using an "INDEX" cycle.
> + */
> +static bool
> +gmbus_is_index_write(struct i2c_msg *msgs, int i, int num)
> +{
> +	return (i + 1 < num &&
> +		msgs[i].addr == msgs[i + 1].addr &&
> +		!(msgs[i].flags & I2C_M_RD) &&
> +		!(msgs[i + 1].flags & I2C_M_RD) &&
> +		(msgs[i].len == 1 || msgs[i + 1].len > 0));
> +}
> +
>  static int
>  gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
>  {
> @@ -489,10 +514,14 @@ do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
>  		if (gmbus_is_index_read(msgs, i, num)) {
>  			ret = gmbus_xfer_index_read(dev_priv, &msgs[i]);
>  			inc = 2; /* an index read is two msgs */
> +		} else if (gmbus_is_index_write(msgs, i, num)) {
> +			ret = gmbus_xfer_index_write(dev_priv, msgs[i].buf[0],
> +					&msgs[i + 1]);
> +			inc = 2; /* an index write is two msgs */
>  		} 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], 0);
>  		}
>  
>  		if (!ret)
> -- 
> 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

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

* Re: [Intel-gfx] [PATCH v3 7/9] drm/i915: Add function to output Aksv over GMBUS
  2017-12-05  5:15   ` Sean Paul
@ 2017-12-05 17:02     ` Daniel Vetter
  -1 siblings, 0 replies; 55+ messages in thread
From: Daniel Vetter @ 2017-12-05 17:02 UTC (permalink / raw)
  To: Sean Paul
  Cc: dri-devel, intel-gfx, seanpaul, David Airlie, linux-kernel,
	Rodrigo Vivi, daniel.vetter

On Tue, Dec 05, 2017 at 12:15:06AM -0500, Sean Paul wrote:
> 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.
> 
> Changes in v2:
> - None
> Changes in v3:
> - Uses new index write feature (Ville)
> 
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Signed-off-by: Sean Paul <seanpaul@chromium.org>

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> ---
>  drivers/gpu/drm/i915/i915_drv.h  |  1 +
>  drivers/gpu/drm/i915/i915_reg.h  |  1 +
>  drivers/gpu/drm/i915/intel_i2c.c | 47 +++++++++++++++++++++++++++++++++++++---
>  3 files changed, 46 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index bddd65839f60..6b39081c5e53 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -4049,6 +4049,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 2bd2cc8441d4..107e16392710 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -3043,6 +3043,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 7399009aee0a..0a4c7486fc7b 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"
> @@ -497,7 +498,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)
>  {
>  	struct intel_gmbus *bus = container_of(adapter,
>  					       struct intel_gmbus,
> @@ -507,7 +509,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;
> @@ -629,7 +631,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);
>  		if (ret == -EAGAIN)
>  			bus->force_bit |= GMBUS_FORCE_BIT_RETRY;
>  	}
> @@ -639,6 +641,45 @@ 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 cmd = DRM_HDCP_DDC_AKSV ;
> +	u8 buf[DRM_HDCP_KSV_LEN] = { 0 };
> +	struct i2c_msg msgs[] = {
> +		{
> +			.addr = DRM_HDCP_DDC_ADDR,
> +			.flags = 0,
> +			.len = sizeof(cmd),
> +			.buf = &cmd,
> +		},
> +		{
> +			.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, msgs, ARRAY_SIZE(msgs), GMBUS_AKSV_SELECT);
> +
> +	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

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

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

* Re: [PATCH v3 7/9] drm/i915: Add function to output Aksv over GMBUS
@ 2017-12-05 17:02     ` Daniel Vetter
  0 siblings, 0 replies; 55+ messages in thread
From: Daniel Vetter @ 2017-12-05 17:02 UTC (permalink / raw)
  To: Sean Paul
  Cc: seanpaul, David Airlie, intel-gfx, linux-kernel, dri-devel,
	Rodrigo Vivi, daniel.vetter

On Tue, Dec 05, 2017 at 12:15:06AM -0500, Sean Paul wrote:
> 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.
> 
> Changes in v2:
> - None
> Changes in v3:
> - Uses new index write feature (Ville)
> 
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Signed-off-by: Sean Paul <seanpaul@chromium.org>

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> ---
>  drivers/gpu/drm/i915/i915_drv.h  |  1 +
>  drivers/gpu/drm/i915/i915_reg.h  |  1 +
>  drivers/gpu/drm/i915/intel_i2c.c | 47 +++++++++++++++++++++++++++++++++++++---
>  3 files changed, 46 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index bddd65839f60..6b39081c5e53 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -4049,6 +4049,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 2bd2cc8441d4..107e16392710 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -3043,6 +3043,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 7399009aee0a..0a4c7486fc7b 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"
> @@ -497,7 +498,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)
>  {
>  	struct intel_gmbus *bus = container_of(adapter,
>  					       struct intel_gmbus,
> @@ -507,7 +509,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;
> @@ -629,7 +631,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);
>  		if (ret == -EAGAIN)
>  			bus->force_bit |= GMBUS_FORCE_BIT_RETRY;
>  	}
> @@ -639,6 +641,45 @@ 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 cmd = DRM_HDCP_DDC_AKSV ;
> +	u8 buf[DRM_HDCP_KSV_LEN] = { 0 };
> +	struct i2c_msg msgs[] = {
> +		{
> +			.addr = DRM_HDCP_DDC_ADDR,
> +			.flags = 0,
> +			.len = sizeof(cmd),
> +			.buf = &cmd,
> +		},
> +		{
> +			.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, msgs, ARRAY_SIZE(msgs), GMBUS_AKSV_SELECT);
> +
> +	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

-- 
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] 55+ messages in thread

* Re: [PATCH v3 8/9] drm/i915: Implement HDCP for HDMI
  2017-12-05  5:15 ` [PATCH v3 8/9] drm/i915: Implement HDCP for HDMI Sean Paul
@ 2017-12-05 17:06     ` Daniel Vetter
  0 siblings, 0 replies; 55+ messages in thread
From: Daniel Vetter @ 2017-12-05 17:06 UTC (permalink / raw)
  To: Sean Paul
  Cc: dri-devel, intel-gfx, seanpaul, David Airlie, Joonas Lahtinen,
	linux-kernel, Rodrigo Vivi, daniel.vetter

On Tue, Dec 05, 2017 at 12:15:07AM -0500, 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.
> 
> Changes in v2:
> - Rebased on drm-intel-next
> Changes in v3:
> - Initialize new worker
> 
> 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_drv.h  |   2 +
>  drivers/gpu/drm/i915/intel_hdmi.c | 257 ++++++++++++++++++++++++++++++++++++++
>  4 files changed, 310 insertions(+)
> 
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index 107e16392710..79944ab4218a 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -8450,6 +8450,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 eff3b51872eb..a179fd9968a5 100644
> --- a/drivers/gpu/drm/i915/intel_ddi.c
> +++ b/drivers/gpu/drm/i915/intel_ddi.c
> @@ -1615,6 +1615,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;
> +	}

Hm, do we really need these checks here? With the new worker design I
think they'd indicate a synchronization bug (misplaced
cancel_delayed_work_sync probably).

If you want to keep them for safetey please wrap in a WARN_ON. Same for
the one below.

Otherwise looks all good to me.

> +
> +	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_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 6f47a4227f5f..0b4405f3e988 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -1379,6 +1379,8 @@ void intel_ddi_compute_min_voltage_level(struct drm_i915_private *dev_priv,
>  u32 bxt_signal_levels(struct intel_dp *intel_dp);
>  uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
>  u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
> +int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder);
> +int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder);
>  
>  unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
>  				   int plane, unsigned int height);
> diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
> index 9d5e72728475..17a525b9fcf9 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>
> @@ -873,6 +874,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->base.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)
>  {
> @@ -2050,6 +2297,16 @@ 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;
> +		mutex_init(&intel_connector->hdcp_mutex);
> +		INIT_DELAYED_WORK(&intel_connector->hdcp_check_work,
> +				  intel_hdcp_check_work);
> +		INIT_WORK(&intel_connector->hdcp_prop_work,
> +			  intel_hdcp_prop_work);

One bikeshed nit, maybe as a follow-up: Extract this into the
intel_hdcp.c library, as an intel_hdcp_init(connector, &shim); function
call?

I just like to keep all the mutex/work/whatever init tidy. Also has the
benefit of teaching lockdep a few more things (bigger locking classes).

Either way:

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> +	}
> +
>  	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

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

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

* Re: [PATCH v3 8/9] drm/i915: Implement HDCP for HDMI
@ 2017-12-05 17:06     ` Daniel Vetter
  0 siblings, 0 replies; 55+ messages in thread
From: Daniel Vetter @ 2017-12-05 17:06 UTC (permalink / raw)
  To: Sean Paul
  Cc: seanpaul, David Airlie, intel-gfx, Joonas Lahtinen, linux-kernel,
	dri-devel, Rodrigo Vivi, daniel.vetter

On Tue, Dec 05, 2017 at 12:15:07AM -0500, 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.
> 
> Changes in v2:
> - Rebased on drm-intel-next
> Changes in v3:
> - Initialize new worker
> 
> 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_drv.h  |   2 +
>  drivers/gpu/drm/i915/intel_hdmi.c | 257 ++++++++++++++++++++++++++++++++++++++
>  4 files changed, 310 insertions(+)
> 
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index 107e16392710..79944ab4218a 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -8450,6 +8450,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 eff3b51872eb..a179fd9968a5 100644
> --- a/drivers/gpu/drm/i915/intel_ddi.c
> +++ b/drivers/gpu/drm/i915/intel_ddi.c
> @@ -1615,6 +1615,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;
> +	}

Hm, do we really need these checks here? With the new worker design I
think they'd indicate a synchronization bug (misplaced
cancel_delayed_work_sync probably).

If you want to keep them for safetey please wrap in a WARN_ON. Same for
the one below.

Otherwise looks all good to me.

> +
> +	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_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 6f47a4227f5f..0b4405f3e988 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -1379,6 +1379,8 @@ void intel_ddi_compute_min_voltage_level(struct drm_i915_private *dev_priv,
>  u32 bxt_signal_levels(struct intel_dp *intel_dp);
>  uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
>  u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
> +int intel_ddi_enable_hdcp_signalling(struct intel_encoder *intel_encoder);
> +int intel_ddi_disable_hdcp_signalling(struct intel_encoder *intel_encoder);
>  
>  unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
>  				   int plane, unsigned int height);
> diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
> index 9d5e72728475..17a525b9fcf9 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>
> @@ -873,6 +874,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->base.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)
>  {
> @@ -2050,6 +2297,16 @@ 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;
> +		mutex_init(&intel_connector->hdcp_mutex);
> +		INIT_DELAYED_WORK(&intel_connector->hdcp_check_work,
> +				  intel_hdcp_check_work);
> +		INIT_WORK(&intel_connector->hdcp_prop_work,
> +			  intel_hdcp_prop_work);

One bikeshed nit, maybe as a follow-up: Extract this into the
intel_hdcp.c library, as an intel_hdcp_init(connector, &shim); function
call?

I just like to keep all the mutex/work/whatever init tidy. Also has the
benefit of teaching lockdep a few more things (bigger locking classes).

Either way:

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> +	}
> +
>  	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

-- 
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] 55+ messages in thread

* RE: [PATCH v3 3/9] drm: Add Content Protection property
  2017-12-05 14:36       ` Sean Paul
  2017-12-05 16:10         ` Hans Verkuil
@ 2017-12-05 17:11         ` C, Ramalingam
  2017-12-06 16:26           ` Sean Paul
  1 sibling, 1 reply; 55+ messages in thread
From: C, Ramalingam @ 2017-12-05 17:11 UTC (permalink / raw)
  To: Sean Paul; +Cc: Hans Verkuil, dri-devel




Best Regards,
Ramalingam C

> -----Original Message-----
> From: Sean Paul [mailto:seanpaul@chromium.org]
> Sent: Tuesday, December 5, 2017 8:07 PM
> To: C, Ramalingam <ramalingam.c@intel.com>
> Cc: dri-devel <dri-devel@lists.freedesktop.org>; Hans Verkuil
> <hverkuil@xs4all.nl>
> Subject: Re: [PATCH v3 3/9] drm: Add Content Protection property
> 
> On Tue, Dec 5, 2017 at 9:04 AM, Ramalingam C <ramalingam.c@intel.com>
> wrote:
> >
> >
> > On Tuesday 05 December 2017 01:37 PM, Hans Verkuil wrote:
> >
> > On 12/05/2017 06:15 AM, 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.
> >
> > 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.
> >
> > Changes in v2:
> >  - Pimp kerneldoc for content_protection_property (Daniel)
> >  - Drop sysfs attribute
> > Changes in v3:
> >  - None
> >
> > Cc: Daniel Vetter <daniel.vetter@intel.com>
> > 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 | 71
> > +++++++++++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/drm_sysfs.c     |  1 +
> >  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 c2da5585e201..676025d755b2 100644
> > --- a/drivers/gpu/drm/drm_atomic.c
> > +++ b/drivers/gpu/drm/drm_atomic.c
> > @@ -1196,6 +1196,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);
> > @@ -1275,6 +1281,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 f14b48e6e839..8626aa8f485e
> > 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
> >   *
> > @@ -764,6 +771,34 @@
> DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
> >   *      after modeset, the kernel driver may set this to "BAD" and issue a
> >   *      hotplug uevent. Drivers should update this value using
> >   *      drm_mode_connector_set_link_status_property().
> > + * Content Protection:
> > + * This property is used by userspace to request the kernel protect
> > + future
> > + * content communicated over the link. When requested, kernel will
> > + apply
> > + * the appropriate means of protection (most often HDCP), and use the
> > + * property to tell userspace the protection is active.
> > + *
> > + * The value of this property can be one of the following:
> > + *
> > + * - DRM_MODE_CONTENT_PROTECTION_OFF = 0
> > + * The link is not protected, content is transmitted in the clear.
> > + * - DRM_MODE_CONTENT_PROTECTION_DESIRED = 1
> > + * Userspace has requested content protection, but the link is not
> > + * currently protected. When in this state, kernel should enable
> > + * Content Protection as soon as possible.
> > + * - DRM_MODE_CONTENT_PROTECTION_ENABLED = 2
> > + * Userspace has requested content protection, and the link is
> > + * protected. Only the driver can set the property to this value.
> > + * If userspace attempts to set to ENABLED, kernel will return
> > + * -EINVAL.
> > + *
> > + * A few guidelines:
> > + *
> > + * - DESIRED state should be preserved until userspace de-asserts it
> > + by
> > + *  setting the property to OFF. This means ENABLED should only
> > + transition
> > + *  to OFF when the user explicitly requests it.
> > + * - If the state is DESIRED, kernel should attempt to
> > + re-authenticate the
> > + *  link whenever possible. This includes across disable/enable,
> > + dpms,
> > + *  hotplug, downstream device changes, link status failures, etc..
> >   *
> >   * Connectors also have one standardized atomic property:
> >   *
> > @@ -1047,6 +1082,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..2385c7e0bef5 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) diff --git
> > a/include/drm/drm_connector.h b/include/drm/drm_connector.h index
> > 7a7140543012..828878addd03 100644
> > --- a/include/drm/drm_connector.h
> > +++ b/include/drm/drm_connector.h
> > @@ -370,6 +370,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;
> >  };
> >
> >  /**
> > @@ -718,6 +724,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 @@ -808,6 +815,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:
> >   *
> > @@ -1002,6 +1015,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, @@ -1010,6
> > +1024,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
> >
> > What about HDCP 1.4 and HDCP 2.2? Userspace would need to know which
> > version was negotiated since content protected 4k videos require HDCP
> > 2.2. Perhaps provide a property with the HDCP version?
> >
> > I'm also missing a method for userspace to read the BKSV from the
> > transmitter.
> >
> > Hans,
> >
> > I guess you are asking about the use case explained at
> > http://www.spinics.net/lists/intel-gfx/msg134813.html
> >
> > Sean,
> > As this solution is only for 1.4, this version requirement might not
> > matter here.
> > But as a community could we at least ack such a need, so that
> > inevitable extension of this uAPI will be well thought about?
> >
> > As we discussed before Enum values required are
> >
> > -OFF           : Disable any content protection
> > -DESIRED       : For any possible HDCP protection (v1.4/2.2)
> > -DESIRED_TYPE1 : For HDCP2.2 only
> > -ENABLED       : Highest HDCP protection is enabled (Could be v1.4/2.2)
> > -ENABLED_TYPE1 : HDCP2.2 enabled
> >
> 
> I'd rather keep the property as-is and expose an HDCP version property
> alongside it (or perhaps something more elaborate that includes bksv and the
> downstream bksvs). The reason I prefer that is it will also cover the 1.2 vs 1.4
> difference that is a more immediate need.

HDCP specification wants to differentiate 2.2 vs <2.2 as 2.2 is not a backward compatible.
Why do we need to differentiate 1.2 and 1.4? Any use case?

> 
> This property is intentionally vague about the underlying encryption, and tying it
> to HDCP (and specific versions, at that) is not consistent with the design.

To differentiate non 2.2(mostly 1.4?) vs 2.2,  we could add another enum for content_type with states
	Type 0 - Indicating protected content for any HDCP protection
	Type 1 - Indicating protected content for HDCP2.2 only

And with respect to passing downstream topology information(bksv list, device count and depth) to userspace, we might need blob property!?
Anyway we need to worry about this only when we have a userspace consumer for such need.

> 
> 
> > And another gap i am seeing with this uAPI is that there is no
> > communication back about the HDCP authentication failure, as DESIRED
> > will remain without transitioning into ENABLED.
> > So userspace has to identify the failure of the HDCP req with polling
> > and Timeout.
> > Timeout also will vary with system to system, as
> >     with big downstream topology(worst case 127 devices with 7depth)
> > authentication could take 5+ Sec.
> >     with only receiver < few 100 mSec(~200mSec?)
> >
> 
> It's worked on CrOS for a number of years now, why change what isn't broken?

I am confident it will work as intact :) I am just curious if we can do it better
for all possible consumer here. So just mentioning the gaps here for discussion.

Main concern is that as far as I understand if new uAPI is accepted we are not allowed to modify easily, if at all.
So IMHO we must think through such that immediate required
usecases can be covered by just extending this uAPI or by simple additions.

--Ram

> 
> Sean
> 
> >
> > So could it help userspace if we could indicate the authentication failure.
> > Agreed that runtime link integrity lost is indicated by the
> > ENABLED->DESIRED transition.
> >
> > --Ram
> >
> >
> > Regards,
> >
> > Hans
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel
> >
> >
> >
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel
> >
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH v3 9/9] drm/i915: Implement HDCP for DisplayPort
  2017-12-05  5:15 ` [PATCH v3 9/9] drm/i915: Implement HDCP for DisplayPort Sean Paul
@ 2017-12-05 17:12     ` Daniel Vetter
  2017-12-05 17:12     ` Daniel Vetter
  1 sibling, 0 replies; 55+ messages in thread
From: Daniel Vetter @ 2017-12-05 17:12 UTC (permalink / raw)
  To: Sean Paul
  Cc: dri-devel, intel-gfx, seanpaul, David Airlie, Joonas Lahtinen,
	linux-kernel, Rodrigo Vivi, daniel.vetter

On Tue, Dec 05, 2017 at 12:15:08AM -0500, Sean Paul wrote:
> 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.
> 
> Changes in v2:
> - Moved intel_hdcp_check_link out of intel_dp_check_link and only call
>   it on short pulse. Since intel_hdcp_check_link does its own locking,
>   this ensures we don't deadlock when intel_dp_check_link is called
>   holding connection_mutex.
> - Rebased on drm-intel-next
> Changes in v3:
> - Initialize new worker
> - Move intel_hdcp_check_link further out to avoid calling it while
>   holding _any_ locks
> 
> Signed-off-by: Sean Paul <seanpaul@chromium.org>
> ---
>  drivers/gpu/drm/i915/intel_dp.c | 248 ++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 241 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
> index c603d4c903e1..dc303e18c1dd 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"
> @@ -1025,10 +1027,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 =
> @@ -1088,10 +1109,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++) {
> @@ -1228,7 +1250,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;
>  
> @@ -1250,7 +1273,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;
>  			/*
> @@ -4981,6 +5005,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)

Spotted the one (I hope, maybe I didn't check stuff perfectly) C++ style
comment!

> +	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;

Fun. I didn't really spot this behaviour difference compared to earlier
paltforms, where you had to (iirc) toggle bit 11 in the DP register.

> +}
> +
> +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 drm_i915_private *dev_priv = to_i915(intel_dp_to_dev(intel_dp));
> @@ -5146,6 +5367,9 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd)
>  		drm_modeset_acquire_fini(&ctx);
>  		WARN(iret, "Acquiring modeset locks failed with %i\n", iret);
>  
> +		/* Short pulse can signify loss of hdcp authentication */
> +		intel_hdcp_check_link(intel_dp->attached_connector);
> +
>  		if (!handled) {
>  			intel_dp->detect_done = false;
>  			goto put_power;
> @@ -6121,6 +6345,16 @@ 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;
> +		mutex_init(&intel_connector->hdcp_mutex);
> +		INIT_DELAYED_WORK(&intel_connector->hdcp_check_work,
> +				  intel_hdcp_check_work);
> +		INIT_WORK(&intel_connector->hdcp_prop_work,
> +			  intel_hdcp_prop_work);
> +	}

Same refactor request as for the hdmi patch here.

Either way:

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> +
>  	/* 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
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

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

* Re: [PATCH v3 9/9] drm/i915: Implement HDCP for DisplayPort
@ 2017-12-05 17:12     ` Daniel Vetter
  0 siblings, 0 replies; 55+ messages in thread
From: Daniel Vetter @ 2017-12-05 17:12 UTC (permalink / raw)
  To: Sean Paul
  Cc: seanpaul, David Airlie, intel-gfx, Joonas Lahtinen, linux-kernel,
	dri-devel, Rodrigo Vivi, daniel.vetter

On Tue, Dec 05, 2017 at 12:15:08AM -0500, Sean Paul wrote:
> 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.
> 
> Changes in v2:
> - Moved intel_hdcp_check_link out of intel_dp_check_link and only call
>   it on short pulse. Since intel_hdcp_check_link does its own locking,
>   this ensures we don't deadlock when intel_dp_check_link is called
>   holding connection_mutex.
> - Rebased on drm-intel-next
> Changes in v3:
> - Initialize new worker
> - Move intel_hdcp_check_link further out to avoid calling it while
>   holding _any_ locks
> 
> Signed-off-by: Sean Paul <seanpaul@chromium.org>
> ---
>  drivers/gpu/drm/i915/intel_dp.c | 248 ++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 241 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
> index c603d4c903e1..dc303e18c1dd 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"
> @@ -1025,10 +1027,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 =
> @@ -1088,10 +1109,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++) {
> @@ -1228,7 +1250,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;
>  
> @@ -1250,7 +1273,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;
>  			/*
> @@ -4981,6 +5005,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)

Spotted the one (I hope, maybe I didn't check stuff perfectly) C++ style
comment!

> +	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;

Fun. I didn't really spot this behaviour difference compared to earlier
paltforms, where you had to (iirc) toggle bit 11 in the DP register.

> +}
> +
> +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 drm_i915_private *dev_priv = to_i915(intel_dp_to_dev(intel_dp));
> @@ -5146,6 +5367,9 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd)
>  		drm_modeset_acquire_fini(&ctx);
>  		WARN(iret, "Acquiring modeset locks failed with %i\n", iret);
>  
> +		/* Short pulse can signify loss of hdcp authentication */
> +		intel_hdcp_check_link(intel_dp->attached_connector);
> +
>  		if (!handled) {
>  			intel_dp->detect_done = false;
>  			goto put_power;
> @@ -6121,6 +6345,16 @@ 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;
> +		mutex_init(&intel_connector->hdcp_mutex);
> +		INIT_DELAYED_WORK(&intel_connector->hdcp_check_work,
> +				  intel_hdcp_check_work);
> +		INIT_WORK(&intel_connector->hdcp_prop_work,
> +			  intel_hdcp_prop_work);
> +	}

Same refactor request as for the hdmi patch here.

Either way:

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> +
>  	/* 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
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
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] 55+ messages in thread

* Re: [PATCH v3 2/9] drm/i915: Add more control to wait_for routines
  2017-12-05  5:15   ` Sean Paul
@ 2017-12-05 17:13     ` Daniel Vetter
  -1 siblings, 0 replies; 55+ messages in thread
From: Daniel Vetter @ 2017-12-05 17:13 UTC (permalink / raw)
  To: Sean Paul
  Cc: dri-devel, intel-gfx, seanpaul, David Airlie, Joonas Lahtinen,
	linux-kernel, Rodrigo Vivi, daniel.vetter

On Tue, Dec 05, 2017 at 12:15:01AM -0500, Sean Paul wrote:
> This patch adds a little more control to a couple wait_for routines such
> that we can avoid open-coding read/wait/timeout patterns which:
>  - need the value of the register after the wait_for
>  - run arbitrary operation for the read portion
> 
> This patch also chooses the correct sleep function (based on
> timers-howto.txt) for the polling interval the caller specifies.
> 
> Changes in v2:
> - Added to the series
> Changes in v3:
> - Rebased on drm-intel-next-queued and the new Wmin/max _wait_for
> - Removed msleep option
> 
> Suggested-by: Chris Wilson <chris@chris-wilson.co.uk>
> Signed-off-by: Sean Paul <seanpaul@chromium.org>

Patches 1&2:

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

I can't find the dang docs for patch 3 ... needs a bit of digging or a
different victim.
-Daniel

> ---
>  drivers/gpu/drm/i915/intel_drv.h    | 17 ++++++++++-------
>  drivers/gpu/drm/i915/intel_uncore.c | 23 ++++++++++++++++-------
>  drivers/gpu/drm/i915/intel_uncore.h | 14 +++++++++++++-
>  3 files changed, 39 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 64426d3e078e..852b3d161754 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -41,20 +41,21 @@
>  #include <drm/drm_atomic.h>
>  
>  /**
> - * _wait_for - magic (register) wait macro
> + * __wait_for - magic wait macro
>   *
> - * Does the right thing for modeset paths when run under kdgb or similar atomic
> - * contexts. Note that it's important that we check the condition again after
> - * having timed out, since the timeout could be due to preemption or similar and
> - * we've never had a chance to check the condition before the timeout.
> + * Macro to help avoid open coding check/wait/timeout patterns. Note that it's
> + * important that we check the condition again after having timed out, since the
> + * timeout could be due to preemption or similar and we've never had a chance to
> + * check the condition before the timeout.
>   */
> -#define _wait_for(COND, US, Wmin, Wmax) ({ \
> +#define __wait_for(OP, COND, US, Wmin, Wmax) ({ \
>  	unsigned long timeout__ = jiffies + usecs_to_jiffies(US) + 1;	\
>  	long wait__ = (Wmin); /* recommended min for usleep is 10 us */	\
>  	int ret__;							\
>  	might_sleep();							\
>  	for (;;) {							\
>  		bool expired__ = time_after(jiffies, timeout__);	\
> +		OP;							\
>  		if (COND) {						\
>  			ret__ = 0;					\
>  			break;						\
> @@ -70,7 +71,9 @@
>  	ret__;								\
>  })
>  
> -#define wait_for(COND, MS)	_wait_for((COND), (MS) * 1000, 10, 1000)
> +#define _wait_for(COND, US, Wmin, Wmax)	__wait_for(;, (COND), (US), (Wmin), \
> +						   (Wmax))
> +#define wait_for(COND, MS)		_wait_for((COND), (MS) * 1000, 10, 1000)
>  
>  /* If CONFIG_PREEMPT_COUNT is disabled, in_atomic() always reports false. */
>  #if defined(CONFIG_DRM_I915_DEBUG) && defined(CONFIG_PREEMPT_COUNT)
> diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
> index b4621271e7a2..9c7d07151f16 100644
> --- a/drivers/gpu/drm/i915/intel_uncore.c
> +++ b/drivers/gpu/drm/i915/intel_uncore.c
> @@ -1770,12 +1770,14 @@ int __intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
>  }
>  
>  /**
> - * intel_wait_for_register - wait until register matches expected state
> + * __intel_wait_for_register - wait until register matches expected state
>   * @dev_priv: the i915 device
>   * @reg: the register to read
>   * @mask: mask to apply to register value
>   * @value: expected value
> - * @timeout_ms: timeout in millisecond
> + * @fast_timeout_us: fast timeout in microsecond for atomic/tight wait
> + * @slow_timeout_ms: slow timeout in millisecond
> + * @out_value: optional placeholder to hold registry value
>   *
>   * This routine waits until the target register @reg contains the expected
>   * @value after applying the @mask, i.e. it waits until ::
> @@ -1786,15 +1788,18 @@ int __intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
>   *
>   * Returns 0 if the register matches the desired condition, or -ETIMEOUT.
>   */
> -int intel_wait_for_register(struct drm_i915_private *dev_priv,
> +int __intel_wait_for_register(struct drm_i915_private *dev_priv,
>  			    i915_reg_t reg,
>  			    u32 mask,
>  			    u32 value,
> -			    unsigned int timeout_ms)
> +			    unsigned int fast_timeout_us,
> +			    unsigned int slow_timeout_ms,
> +			    u32 *out_value)
>  {
>  	unsigned fw =
>  		intel_uncore_forcewake_for_reg(dev_priv, reg, FW_REG_READ);
>  	int ret;
> +	u32 reg_value;
>  
>  	might_sleep();
>  
> @@ -1803,14 +1808,18 @@ int intel_wait_for_register(struct drm_i915_private *dev_priv,
>  
>  	ret = __intel_wait_for_register_fw(dev_priv,
>  					   reg, mask, value,
> -					   2, 0, NULL);
> +					   fast_timeout_us, 0, &reg_value);
>  
>  	intel_uncore_forcewake_put__locked(dev_priv, fw);
>  	spin_unlock_irq(&dev_priv->uncore.lock);
>  
>  	if (ret)
> -		ret = wait_for((I915_READ_NOTRACE(reg) & mask) == value,
> -			       timeout_ms);
> +		ret = __wait_for(reg_value = I915_READ_NOTRACE(reg),
> +			         (reg_value & mask) == value,
> +			         slow_timeout_ms * 1000, 10, 1000);
> +
> +	if (out_value)
> +		*out_value = reg_value;
>  
>  	return ret;
>  }
> diff --git a/drivers/gpu/drm/i915/intel_uncore.h b/drivers/gpu/drm/i915/intel_uncore.h
> index 9ce079b5dd0d..bed019ef000f 100644
> --- a/drivers/gpu/drm/i915/intel_uncore.h
> +++ b/drivers/gpu/drm/i915/intel_uncore.h
> @@ -163,11 +163,23 @@ void intel_uncore_forcewake_put__locked(struct drm_i915_private *dev_priv,
>  void intel_uncore_forcewake_user_get(struct drm_i915_private *dev_priv);
>  void intel_uncore_forcewake_user_put(struct drm_i915_private *dev_priv);
>  
> +int __intel_wait_for_register(struct drm_i915_private *dev_priv,
> +			      i915_reg_t reg,
> +			      u32 mask,
> +			      u32 value,
> +			      unsigned int fast_timeout_us,
> +			      unsigned int slow_timeout_ms,
> +			      u32 *out_value);
> +static inline
>  int intel_wait_for_register(struct drm_i915_private *dev_priv,
>  			    i915_reg_t reg,
>  			    u32 mask,
>  			    u32 value,
> -			    unsigned int timeout_ms);
> +			    unsigned int timeout_ms)
> +{
> +	return __intel_wait_for_register(dev_priv, reg, mask, value, 2,
> +					 timeout_ms, NULL);
> +}
>  int __intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
>  				 i915_reg_t reg,
>  				 u32 mask,
> -- 
> 2.15.0.531.g2ccb3012c9-goog
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

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

* Re: [PATCH v3 2/9] drm/i915: Add more control to wait_for routines
@ 2017-12-05 17:13     ` Daniel Vetter
  0 siblings, 0 replies; 55+ messages in thread
From: Daniel Vetter @ 2017-12-05 17:13 UTC (permalink / raw)
  To: Sean Paul
  Cc: seanpaul, David Airlie, intel-gfx, linux-kernel, dri-devel,
	Rodrigo Vivi, daniel.vetter

On Tue, Dec 05, 2017 at 12:15:01AM -0500, Sean Paul wrote:
> This patch adds a little more control to a couple wait_for routines such
> that we can avoid open-coding read/wait/timeout patterns which:
>  - need the value of the register after the wait_for
>  - run arbitrary operation for the read portion
> 
> This patch also chooses the correct sleep function (based on
> timers-howto.txt) for the polling interval the caller specifies.
> 
> Changes in v2:
> - Added to the series
> Changes in v3:
> - Rebased on drm-intel-next-queued and the new Wmin/max _wait_for
> - Removed msleep option
> 
> Suggested-by: Chris Wilson <chris@chris-wilson.co.uk>
> Signed-off-by: Sean Paul <seanpaul@chromium.org>

Patches 1&2:

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

I can't find the dang docs for patch 3 ... needs a bit of digging or a
different victim.
-Daniel

> ---
>  drivers/gpu/drm/i915/intel_drv.h    | 17 ++++++++++-------
>  drivers/gpu/drm/i915/intel_uncore.c | 23 ++++++++++++++++-------
>  drivers/gpu/drm/i915/intel_uncore.h | 14 +++++++++++++-
>  3 files changed, 39 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 64426d3e078e..852b3d161754 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -41,20 +41,21 @@
>  #include <drm/drm_atomic.h>
>  
>  /**
> - * _wait_for - magic (register) wait macro
> + * __wait_for - magic wait macro
>   *
> - * Does the right thing for modeset paths when run under kdgb or similar atomic
> - * contexts. Note that it's important that we check the condition again after
> - * having timed out, since the timeout could be due to preemption or similar and
> - * we've never had a chance to check the condition before the timeout.
> + * Macro to help avoid open coding check/wait/timeout patterns. Note that it's
> + * important that we check the condition again after having timed out, since the
> + * timeout could be due to preemption or similar and we've never had a chance to
> + * check the condition before the timeout.
>   */
> -#define _wait_for(COND, US, Wmin, Wmax) ({ \
> +#define __wait_for(OP, COND, US, Wmin, Wmax) ({ \
>  	unsigned long timeout__ = jiffies + usecs_to_jiffies(US) + 1;	\
>  	long wait__ = (Wmin); /* recommended min for usleep is 10 us */	\
>  	int ret__;							\
>  	might_sleep();							\
>  	for (;;) {							\
>  		bool expired__ = time_after(jiffies, timeout__);	\
> +		OP;							\
>  		if (COND) {						\
>  			ret__ = 0;					\
>  			break;						\
> @@ -70,7 +71,9 @@
>  	ret__;								\
>  })
>  
> -#define wait_for(COND, MS)	_wait_for((COND), (MS) * 1000, 10, 1000)
> +#define _wait_for(COND, US, Wmin, Wmax)	__wait_for(;, (COND), (US), (Wmin), \
> +						   (Wmax))
> +#define wait_for(COND, MS)		_wait_for((COND), (MS) * 1000, 10, 1000)
>  
>  /* If CONFIG_PREEMPT_COUNT is disabled, in_atomic() always reports false. */
>  #if defined(CONFIG_DRM_I915_DEBUG) && defined(CONFIG_PREEMPT_COUNT)
> diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
> index b4621271e7a2..9c7d07151f16 100644
> --- a/drivers/gpu/drm/i915/intel_uncore.c
> +++ b/drivers/gpu/drm/i915/intel_uncore.c
> @@ -1770,12 +1770,14 @@ int __intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
>  }
>  
>  /**
> - * intel_wait_for_register - wait until register matches expected state
> + * __intel_wait_for_register - wait until register matches expected state
>   * @dev_priv: the i915 device
>   * @reg: the register to read
>   * @mask: mask to apply to register value
>   * @value: expected value
> - * @timeout_ms: timeout in millisecond
> + * @fast_timeout_us: fast timeout in microsecond for atomic/tight wait
> + * @slow_timeout_ms: slow timeout in millisecond
> + * @out_value: optional placeholder to hold registry value
>   *
>   * This routine waits until the target register @reg contains the expected
>   * @value after applying the @mask, i.e. it waits until ::
> @@ -1786,15 +1788,18 @@ int __intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
>   *
>   * Returns 0 if the register matches the desired condition, or -ETIMEOUT.
>   */
> -int intel_wait_for_register(struct drm_i915_private *dev_priv,
> +int __intel_wait_for_register(struct drm_i915_private *dev_priv,
>  			    i915_reg_t reg,
>  			    u32 mask,
>  			    u32 value,
> -			    unsigned int timeout_ms)
> +			    unsigned int fast_timeout_us,
> +			    unsigned int slow_timeout_ms,
> +			    u32 *out_value)
>  {
>  	unsigned fw =
>  		intel_uncore_forcewake_for_reg(dev_priv, reg, FW_REG_READ);
>  	int ret;
> +	u32 reg_value;
>  
>  	might_sleep();
>  
> @@ -1803,14 +1808,18 @@ int intel_wait_for_register(struct drm_i915_private *dev_priv,
>  
>  	ret = __intel_wait_for_register_fw(dev_priv,
>  					   reg, mask, value,
> -					   2, 0, NULL);
> +					   fast_timeout_us, 0, &reg_value);
>  
>  	intel_uncore_forcewake_put__locked(dev_priv, fw);
>  	spin_unlock_irq(&dev_priv->uncore.lock);
>  
>  	if (ret)
> -		ret = wait_for((I915_READ_NOTRACE(reg) & mask) == value,
> -			       timeout_ms);
> +		ret = __wait_for(reg_value = I915_READ_NOTRACE(reg),
> +			         (reg_value & mask) == value,
> +			         slow_timeout_ms * 1000, 10, 1000);
> +
> +	if (out_value)
> +		*out_value = reg_value;
>  
>  	return ret;
>  }
> diff --git a/drivers/gpu/drm/i915/intel_uncore.h b/drivers/gpu/drm/i915/intel_uncore.h
> index 9ce079b5dd0d..bed019ef000f 100644
> --- a/drivers/gpu/drm/i915/intel_uncore.h
> +++ b/drivers/gpu/drm/i915/intel_uncore.h
> @@ -163,11 +163,23 @@ void intel_uncore_forcewake_put__locked(struct drm_i915_private *dev_priv,
>  void intel_uncore_forcewake_user_get(struct drm_i915_private *dev_priv);
>  void intel_uncore_forcewake_user_put(struct drm_i915_private *dev_priv);
>  
> +int __intel_wait_for_register(struct drm_i915_private *dev_priv,
> +			      i915_reg_t reg,
> +			      u32 mask,
> +			      u32 value,
> +			      unsigned int fast_timeout_us,
> +			      unsigned int slow_timeout_ms,
> +			      u32 *out_value);
> +static inline
>  int intel_wait_for_register(struct drm_i915_private *dev_priv,
>  			    i915_reg_t reg,
>  			    u32 mask,
>  			    u32 value,
> -			    unsigned int timeout_ms);
> +			    unsigned int timeout_ms)
> +{
> +	return __intel_wait_for_register(dev_priv, reg, mask, value, 2,
> +					 timeout_ms, NULL);
> +}
>  int __intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
>  				 i915_reg_t reg,
>  				 u32 mask,
> -- 
> 2.15.0.531.g2ccb3012c9-goog
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
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] 55+ messages in thread

* Re: [Intel-gfx] [PATCH v3 6/9] drm/i915: Make use of indexed write GMBUS feature
  2017-12-05  5:15   ` Sean Paul
@ 2017-12-05 17:33     ` Ville Syrjälä
  -1 siblings, 0 replies; 55+ messages in thread
From: Ville Syrjälä @ 2017-12-05 17:33 UTC (permalink / raw)
  To: Sean Paul
  Cc: dri-devel, intel-gfx, seanpaul, David Airlie, linux-kernel,
	Rodrigo Vivi, daniel.vetter

On Tue, Dec 05, 2017 at 12:15:05AM -0500, Sean Paul wrote:
> This patch enables the indexed write feature of the GMBUS to concatenate
> 2 consecutive messages into one. The criteria for an indexed write is
> that both messages are writes, the first is length == 1, and the second
> is length > 0. The first message is sent out by the GMBUS as the slave
> command, and the second one is sent via the GMBUS FIFO as usual.
> 
> Changes in v3:
> - Added to series
> 
> Suggested-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Signed-off-by: Sean Paul <seanpaul@chromium.org>
> ---
>  drivers/gpu/drm/i915/intel_i2c.c | 39 ++++++++++++++++++++++++++++++++++-----
>  1 file changed, 34 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
> index 49fdf09f9919..7399009aee0a 100644
> --- a/drivers/gpu/drm/i915/intel_i2c.c
> +++ b/drivers/gpu/drm/i915/intel_i2c.c
> @@ -373,7 +373,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 +387,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 +410,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 +421,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;
>  
> @@ -430,6 +433,14 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
>  	return 0;
>  }
>  
> +static int
> +gmbus_xfer_index_write(struct drm_i915_private *dev_priv, u8 cmd,
> +		       struct i2c_msg *msg)
> +{
> +	u8 gmbus1_index = GMBUS_CYCLE_INDEX | (cmd << GMBUS_SLAVE_INDEX_SHIFT);
> +	return gmbus_xfer_write(dev_priv, msg, gmbus1_index);
> +}

Instead of a duplicating the entire thing I'd just

- gmbus_xfer_index_read
+ gmbus_xfer_index
  {
  ...
+ 	if (msgs[1].flags & I2C_M_RD)
		gmbus_xfer_read()
+ 	else
+ 		gmbus_xfer_write()
  ...
  }

Matches the current pattern better (no 'cmd' passed in), and
will give us the 2 byte index for free as well.

> +
>  /*
>   * The gmbus controller can combine a 1 or 2 byte write with a read that
>   * immediately follows it by using an "INDEX" cycle.
> @@ -444,6 +455,20 @@ gmbus_is_index_read(struct i2c_msg *msgs, int i, int num)
>  		(msgs[i + 1].flags & I2C_M_RD));
>  }
>  
> +/*
> + * The gmbus controller can combine a 2-msg write into a single write that
> + * immediately follows it by using an "INDEX" cycle.
> + */
> +static bool
> +gmbus_is_index_write(struct i2c_msg *msgs, int i, int num)
> +{
> +	return (i + 1 < num &&
> +		msgs[i].addr == msgs[i + 1].addr &&
> +		!(msgs[i].flags & I2C_M_RD) &&
> +		!(msgs[i + 1].flags & I2C_M_RD) &&
> +		(msgs[i].len == 1 || msgs[i + 1].len > 0));

Hmm. We don't have the len check for the second msg for reads. I wonder
if gmbus can actually do a zero length "read/write"?

> +}
> +
>  static int
>  gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
>  {
> @@ -489,10 +514,14 @@ do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
>  		if (gmbus_is_index_read(msgs, i, num)) {
>  			ret = gmbus_xfer_index_read(dev_priv, &msgs[i]);
>  			inc = 2; /* an index read is two msgs */
> +		} else if (gmbus_is_index_write(msgs, i, num)) {
> +			ret = gmbus_xfer_index_write(dev_priv, msgs[i].buf[0],
> +					&msgs[i + 1]);
> +			inc = 2; /* an index write is two msgs */
>  		} 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], 0);
>  		}
>  
>  		if (!ret)
> -- 
> 2.15.0.531.g2ccb3012c9-goog
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx

-- 
Ville Syrjälä
Intel OTC

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

* Re: [PATCH v3 6/9] drm/i915: Make use of indexed write GMBUS feature
@ 2017-12-05 17:33     ` Ville Syrjälä
  0 siblings, 0 replies; 55+ messages in thread
From: Ville Syrjälä @ 2017-12-05 17:33 UTC (permalink / raw)
  To: Sean Paul
  Cc: seanpaul, David Airlie, intel-gfx, linux-kernel, dri-devel,
	Rodrigo Vivi, daniel.vetter

On Tue, Dec 05, 2017 at 12:15:05AM -0500, Sean Paul wrote:
> This patch enables the indexed write feature of the GMBUS to concatenate
> 2 consecutive messages into one. The criteria for an indexed write is
> that both messages are writes, the first is length == 1, and the second
> is length > 0. The first message is sent out by the GMBUS as the slave
> command, and the second one is sent via the GMBUS FIFO as usual.
> 
> Changes in v3:
> - Added to series
> 
> Suggested-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Signed-off-by: Sean Paul <seanpaul@chromium.org>
> ---
>  drivers/gpu/drm/i915/intel_i2c.c | 39 ++++++++++++++++++++++++++++++++++-----
>  1 file changed, 34 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
> index 49fdf09f9919..7399009aee0a 100644
> --- a/drivers/gpu/drm/i915/intel_i2c.c
> +++ b/drivers/gpu/drm/i915/intel_i2c.c
> @@ -373,7 +373,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 +387,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 +410,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 +421,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;
>  
> @@ -430,6 +433,14 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
>  	return 0;
>  }
>  
> +static int
> +gmbus_xfer_index_write(struct drm_i915_private *dev_priv, u8 cmd,
> +		       struct i2c_msg *msg)
> +{
> +	u8 gmbus1_index = GMBUS_CYCLE_INDEX | (cmd << GMBUS_SLAVE_INDEX_SHIFT);
> +	return gmbus_xfer_write(dev_priv, msg, gmbus1_index);
> +}

Instead of a duplicating the entire thing I'd just

- gmbus_xfer_index_read
+ gmbus_xfer_index
  {
  ...
+ 	if (msgs[1].flags & I2C_M_RD)
		gmbus_xfer_read()
+ 	else
+ 		gmbus_xfer_write()
  ...
  }

Matches the current pattern better (no 'cmd' passed in), and
will give us the 2 byte index for free as well.

> +
>  /*
>   * The gmbus controller can combine a 1 or 2 byte write with a read that
>   * immediately follows it by using an "INDEX" cycle.
> @@ -444,6 +455,20 @@ gmbus_is_index_read(struct i2c_msg *msgs, int i, int num)
>  		(msgs[i + 1].flags & I2C_M_RD));
>  }
>  
> +/*
> + * The gmbus controller can combine a 2-msg write into a single write that
> + * immediately follows it by using an "INDEX" cycle.
> + */
> +static bool
> +gmbus_is_index_write(struct i2c_msg *msgs, int i, int num)
> +{
> +	return (i + 1 < num &&
> +		msgs[i].addr == msgs[i + 1].addr &&
> +		!(msgs[i].flags & I2C_M_RD) &&
> +		!(msgs[i + 1].flags & I2C_M_RD) &&
> +		(msgs[i].len == 1 || msgs[i + 1].len > 0));

Hmm. We don't have the len check for the second msg for reads. I wonder
if gmbus can actually do a zero length "read/write"?

> +}
> +
>  static int
>  gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
>  {
> @@ -489,10 +514,14 @@ do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
>  		if (gmbus_is_index_read(msgs, i, num)) {
>  			ret = gmbus_xfer_index_read(dev_priv, &msgs[i]);
>  			inc = 2; /* an index read is two msgs */
> +		} else if (gmbus_is_index_write(msgs, i, num)) {
> +			ret = gmbus_xfer_index_write(dev_priv, msgs[i].buf[0],
> +					&msgs[i + 1]);
> +			inc = 2; /* an index write is two msgs */
>  		} 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], 0);
>  		}
>  
>  		if (!ret)
> -- 
> 2.15.0.531.g2ccb3012c9-goog
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx

-- 
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] 55+ messages in thread

* Re: [Intel-gfx] [PATCH v3 2/9] drm/i915: Add more control to wait_for routines
  2017-12-05  5:15   ` Sean Paul
  (?)
  (?)
@ 2017-12-05 23:09   ` Chris Wilson
  -1 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2017-12-05 23:09 UTC (permalink / raw)
  To: Sean Paul, dri-devel, intel-gfx
  Cc: seanpaul, David Airlie, linux-kernel, Rodrigo Vivi, daniel.vetter

Quoting Sean Paul (2017-12-05 05:15:01)
> This patch adds a little more control to a couple wait_for routines such
> that we can avoid open-coding read/wait/timeout patterns which:
>  - need the value of the register after the wait_for
>  - run arbitrary operation for the read portion
> 
> This patch also chooses the correct sleep function (based on
> timers-howto.txt) for the polling interval the caller specifies.
> 
> Changes in v2:
> - Added to the series
> Changes in v3:
> - Rebased on drm-intel-next-queued and the new Wmin/max _wait_for
> - Removed msleep option
> 
> Suggested-by: Chris Wilson <chris@chris-wilson.co.uk>
> Signed-off-by: Sean Paul <seanpaul@chromium.org>
> ---
>  drivers/gpu/drm/i915/intel_drv.h    | 17 ++++++++++-------
>  drivers/gpu/drm/i915/intel_uncore.c | 23 ++++++++++++++++-------
>  drivers/gpu/drm/i915/intel_uncore.h | 14 +++++++++++++-
>  3 files changed, 39 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 64426d3e078e..852b3d161754 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -41,20 +41,21 @@
>  #include <drm/drm_atomic.h>
>  
>  /**
> - * _wait_for - magic (register) wait macro
> + * __wait_for - magic wait macro
>   *
> - * Does the right thing for modeset paths when run under kdgb or similar atomic
> - * contexts. Note that it's important that we check the condition again after
> - * having timed out, since the timeout could be due to preemption or similar and
> - * we've never had a chance to check the condition before the timeout.
> + * Macro to help avoid open coding check/wait/timeout patterns. Note that it's
> + * important that we check the condition again after having timed out, since the
> + * timeout could be due to preemption or similar and we've never had a chance to
> + * check the condition before the timeout.
>   */
> -#define _wait_for(COND, US, Wmin, Wmax) ({ \
> +#define __wait_for(OP, COND, US, Wmin, Wmax) ({ \
>         unsigned long timeout__ = jiffies + usecs_to_jiffies(US) + 1;   \
>         long wait__ = (Wmin); /* recommended min for usleep is 10 us */ \
>         int ret__;                                                      \
>         might_sleep();                                                  \
>         for (;;) {                                                      \
>                 bool expired__ = time_after(jiffies, timeout__);        \
> +               OP;                                                     \
>                 if (COND) {                                             \
>                         ret__ = 0;                                      \
>                         break;                                          \
> @@ -70,7 +71,9 @@
>         ret__;                                                          \
>  })
>  
> -#define wait_for(COND, MS)     _wait_for((COND), (MS) * 1000, 10, 1000)
> +#define _wait_for(COND, US, Wmin, Wmax)        __wait_for(;, (COND), (US), (Wmin), \
> +                                                  (Wmax))

Hmm, doesn't an empty OP (__wait_for(, ...)) work?

> +int __intel_wait_for_register(struct drm_i915_private *dev_priv,
>                             i915_reg_t reg,
>                             u32 mask,
>                             u32 value,
> -                           unsigned int timeout_ms)
> +                           unsigned int fast_timeout_us,
> +                           unsigned int slow_timeout_ms,
> +                           u32 *out_value)
>  {
>         unsigned fw =
>                 intel_uncore_forcewake_for_reg(dev_priv, reg, FW_REG_READ);
>         int ret;
> +       u32 reg_value;

Before int ret; Try to avoid building a Christmas tree if possible.

Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
-Chris

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

* Re: [Intel-gfx] [PATCH v3 4/9] drm: Add some HDCP related #defines
  2017-12-05  5:15   ` Sean Paul
  (?)
@ 2017-12-05 23:12   ` Chris Wilson
  2017-12-06 15:01       ` Alex Deucher
  -1 siblings, 1 reply; 55+ messages in thread
From: Chris Wilson @ 2017-12-05 23:12 UTC (permalink / raw)
  To: Sean Paul, dri-devel, intel-gfx
  Cc: seanpaul, David Airlie, linux-kernel, daniel.vetter

Quoting Sean Paul (2017-12-05 05:15:03)
> 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.
> 
> Changes in v2:
> - drm_hdcp.h gets MIT license (Daniel)

Speaking of licences, what's the right spdx for drm files?
SPDX-License-Identifier: (GPL-2.0+ OR MIT) ?
It looks like we've already grown quite a few BSD-unfriendly files...
-Chris

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

* Re: [Intel-gfx] [PATCH v3 4/9] drm: Add some HDCP related #defines
  2017-12-05 23:12   ` [Intel-gfx] " Chris Wilson
@ 2017-12-06 15:01       ` Alex Deucher
  0 siblings, 0 replies; 55+ messages in thread
From: Alex Deucher @ 2017-12-06 15:01 UTC (permalink / raw)
  To: Chris Wilson
  Cc: Sean Paul, Maling list - DRI developers,
	Intel Graphics Development, David Airlie, Daniel Vetter, LKML,
	seanpaul

On Tue, Dec 5, 2017 at 6:12 PM, Chris Wilson <chris@chris-wilson.co.uk> wrote:
> Quoting Sean Paul (2017-12-05 05:15:03)
>> 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.
>>
>> Changes in v2:
>> - drm_hdcp.h gets MIT license (Daniel)
>
> Speaking of licences, what's the right spdx for drm files?
> SPDX-License-Identifier: (GPL-2.0+ OR MIT) ?
> It looks like we've already grown quite a few BSD-unfriendly files...

Should be MIT.

Alex

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

* Re: [Intel-gfx] [PATCH v3 4/9] drm: Add some HDCP related #defines
@ 2017-12-06 15:01       ` Alex Deucher
  0 siblings, 0 replies; 55+ messages in thread
From: Alex Deucher @ 2017-12-06 15:01 UTC (permalink / raw)
  To: Chris Wilson
  Cc: seanpaul, David Airlie, Intel Graphics Development, LKML,
	Maling list - DRI developers, Daniel Vetter

On Tue, Dec 5, 2017 at 6:12 PM, Chris Wilson <chris@chris-wilson.co.uk> wrote:
> Quoting Sean Paul (2017-12-05 05:15:03)
>> 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.
>>
>> Changes in v2:
>> - drm_hdcp.h gets MIT license (Daniel)
>
> Speaking of licences, what's the right spdx for drm files?
> SPDX-License-Identifier: (GPL-2.0+ OR MIT) ?
> It looks like we've already grown quite a few BSD-unfriendly files...

Should be MIT.

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

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

* Re: [PATCH v3 3/9] drm: Add Content Protection property
  2017-12-05 17:11         ` C, Ramalingam
@ 2017-12-06 16:26           ` Sean Paul
  2017-12-07  4:34             ` Ramalingam C
  0 siblings, 1 reply; 55+ messages in thread
From: Sean Paul @ 2017-12-06 16:26 UTC (permalink / raw)
  To: C, Ramalingam; +Cc: Hans Verkuil, dri-devel

On Tue, Dec 5, 2017 at 12:11 PM, C, Ramalingam <ramalingam.c@intel.com> wrote:
>
>
>
> Best Regards,
> Ramalingam C
>
>> -----Original Message-----
>> From: Sean Paul [mailto:seanpaul@chromium.org]
>> Sent: Tuesday, December 5, 2017 8:07 PM
>> To: C, Ramalingam <ramalingam.c@intel.com>
>> Cc: dri-devel <dri-devel@lists.freedesktop.org>; Hans Verkuil
>> <hverkuil@xs4all.nl>
>> Subject: Re: [PATCH v3 3/9] drm: Add Content Protection property
>>
>> On Tue, Dec 5, 2017 at 9:04 AM, Ramalingam C <ramalingam.c@intel.com>
>> wrote:
>> >
>> >
>> > On Tuesday 05 December 2017 01:37 PM, Hans Verkuil wrote:
>> >
>> > On 12/05/2017 06:15 AM, 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.
>> >
>> > 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.
>> >
>> > Changes in v2:
>> >  - Pimp kerneldoc for content_protection_property (Daniel)
>> >  - Drop sysfs attribute
>> > Changes in v3:
>> >  - None
>> >
>> > Cc: Daniel Vetter <daniel.vetter@intel.com>
>> > 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 | 71
>> > +++++++++++++++++++++++++++++++++++++++++
>> >  drivers/gpu/drm/drm_sysfs.c     |  1 +
>> >  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 c2da5585e201..676025d755b2 100644
>> > --- a/drivers/gpu/drm/drm_atomic.c
>> > +++ b/drivers/gpu/drm/drm_atomic.c
>> > @@ -1196,6 +1196,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);
>> > @@ -1275,6 +1281,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 f14b48e6e839..8626aa8f485e
>> > 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
>> >   *
>> > @@ -764,6 +771,34 @@
>> DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
>> >   *      after modeset, the kernel driver may set this to "BAD" and issue a
>> >   *      hotplug uevent. Drivers should update this value using
>> >   *      drm_mode_connector_set_link_status_property().
>> > + * Content Protection:
>> > + * This property is used by userspace to request the kernel protect
>> > + future
>> > + * content communicated over the link. When requested, kernel will
>> > + apply
>> > + * the appropriate means of protection (most often HDCP), and use the
>> > + * property to tell userspace the protection is active.
>> > + *
>> > + * The value of this property can be one of the following:
>> > + *
>> > + * - DRM_MODE_CONTENT_PROTECTION_OFF = 0
>> > + * The link is not protected, content is transmitted in the clear.
>> > + * - DRM_MODE_CONTENT_PROTECTION_DESIRED = 1
>> > + * Userspace has requested content protection, but the link is not
>> > + * currently protected. When in this state, kernel should enable
>> > + * Content Protection as soon as possible.
>> > + * - DRM_MODE_CONTENT_PROTECTION_ENABLED = 2
>> > + * Userspace has requested content protection, and the link is
>> > + * protected. Only the driver can set the property to this value.
>> > + * If userspace attempts to set to ENABLED, kernel will return
>> > + * -EINVAL.
>> > + *
>> > + * A few guidelines:
>> > + *
>> > + * - DESIRED state should be preserved until userspace de-asserts it
>> > + by
>> > + *  setting the property to OFF. This means ENABLED should only
>> > + transition
>> > + *  to OFF when the user explicitly requests it.
>> > + * - If the state is DESIRED, kernel should attempt to
>> > + re-authenticate the
>> > + *  link whenever possible. This includes across disable/enable,
>> > + dpms,
>> > + *  hotplug, downstream device changes, link status failures, etc..
>> >   *
>> >   * Connectors also have one standardized atomic property:
>> >   *
>> > @@ -1047,6 +1082,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..2385c7e0bef5 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) diff --git
>> > a/include/drm/drm_connector.h b/include/drm/drm_connector.h index
>> > 7a7140543012..828878addd03 100644
>> > --- a/include/drm/drm_connector.h
>> > +++ b/include/drm/drm_connector.h
>> > @@ -370,6 +370,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;
>> >  };
>> >
>> >  /**
>> > @@ -718,6 +724,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 @@ -808,6 +815,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:
>> >   *
>> > @@ -1002,6 +1015,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, @@ -1010,6
>> > +1024,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
>> >
>> > What about HDCP 1.4 and HDCP 2.2? Userspace would need to know which
>> > version was negotiated since content protected 4k videos require HDCP
>> > 2.2. Perhaps provide a property with the HDCP version?
>> >
>> > I'm also missing a method for userspace to read the BKSV from the
>> > transmitter.
>> >
>> > Hans,
>> >
>> > I guess you are asking about the use case explained at
>> > http://www.spinics.net/lists/intel-gfx/msg134813.html
>> >
>> > Sean,
>> > As this solution is only for 1.4, this version requirement might not
>> > matter here.
>> > But as a community could we at least ack such a need, so that
>> > inevitable extension of this uAPI will be well thought about?
>> >
>> > As we discussed before Enum values required are
>> >
>> > -OFF           : Disable any content protection
>> > -DESIRED       : For any possible HDCP protection (v1.4/2.2)
>> > -DESIRED_TYPE1 : For HDCP2.2 only
>> > -ENABLED       : Highest HDCP protection is enabled (Could be v1.4/2.2)
>> > -ENABLED_TYPE1 : HDCP2.2 enabled
>> >
>>
>> I'd rather keep the property as-is and expose an HDCP version property
>> alongside it (or perhaps something more elaborate that includes bksv and the
>> downstream bksvs). The reason I prefer that is it will also cover the 1.2 vs 1.4
>> difference that is a more immediate need.
>
> HDCP specification wants to differentiate 2.2 vs <2.2 as 2.2 is not a backward compatible.
> Why do we need to differentiate 1.2 and 1.4? Any use case?
>

Someone on the Chrome side asked me to surface the version, apparently
they care about 1.2 vs 1.4.

>>
>> This property is intentionally vague about the underlying encryption, and tying it
>> to HDCP (and specific versions, at that) is not consistent with the design.
>
> To differentiate non 2.2(mostly 1.4?) vs 2.2,  we could add another enum for content_type with states
>         Type 0 - Indicating protected content for any HDCP protection
>         Type 1 - Indicating protected content for HDCP2.2 only
>
> And with respect to passing downstream topology information(bksv list, device count and depth) to userspace, we might need blob property!?
> Anyway we need to worry about this only when we have a userspace consumer for such need.
>

Yeah, so if we're already adding a blob, perhaps version fits better
there. At any rate, I don't want to speculate on what uapi we'll need
in the future if I don't have to. So until I dig into exposing
version, I'd rather defer this decision.

>>
>>
>> > And another gap i am seeing with this uAPI is that there is no
>> > communication back about the HDCP authentication failure, as DESIRED
>> > will remain without transitioning into ENABLED.
>> > So userspace has to identify the failure of the HDCP req with polling
>> > and Timeout.
>> > Timeout also will vary with system to system, as
>> >     with big downstream topology(worst case 127 devices with 7depth)
>> > authentication could take 5+ Sec.
>> >     with only receiver < few 100 mSec(~200mSec?)
>> >
>>
>> It's worked on CrOS for a number of years now, why change what isn't broken?
>
> I am confident it will work as intact :) I am just curious if we can do it better
> for all possible consumer here. So just mentioning the gaps here for discussion.
>
> Main concern is that as far as I understand if new uAPI is accepted we are not allowed to modify easily, if at all.

It can be modified as long as it's backwards compatible. At the
moment, the only userspace is Chrome, and afaik, everything is happy
there.

Sean

> So IMHO we must think through such that immediate required
> usecases can be covered by just extending this uAPI or by simple additions.
>
> --Ram
>
>>
>> Sean
>>
>> >
>> > So could it help userspace if we could indicate the authentication failure.
>> > Agreed that runtime link integrity lost is indicated by the
>> > ENABLED->DESIRED transition.
>> >
>> > --Ram
>> >
>> >
>> > Regards,
>> >
>> > Hans
>> > _______________________________________________
>> > dri-devel mailing list
>> > dri-devel@lists.freedesktop.org
>> > https://lists.freedesktop.org/mailman/listinfo/dri-devel
>> >
>> >
>> >
>> > _______________________________________________
>> > dri-devel mailing list
>> > dri-devel@lists.freedesktop.org
>> > https://lists.freedesktop.org/mailman/listinfo/dri-devel
>> >
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH v3 3/9] drm: Add Content Protection property
  2017-12-06 16:26           ` Sean Paul
@ 2017-12-07  4:34             ` Ramalingam C
  0 siblings, 0 replies; 55+ messages in thread
From: Ramalingam C @ 2017-12-07  4:34 UTC (permalink / raw)
  To: Sean Paul; +Cc: Hans Verkuil, dri-devel


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



On Wednesday 06 December 2017 09:56 PM, Sean Paul wrote:
>>> I'd rather keep the property as-is and expose an HDCP version property
>>> alongside it (or perhaps something more elaborate that includes bksv and the
>>> downstream bksvs). The reason I prefer that is it will also cover the 1.2 vs 1.4
>>> difference that is a more immediate need.
>> HDCP specification wants to differentiate 2.2 vs <2.2 as 2.2 is not a backward compatible.
>> Why do we need to differentiate 1.2 and 1.4? Any use case?
>>
> Someone on the Chrome side asked me to surface the version, apparently
> they care about 1.2 vs 1.4.
>
Sean, if you can get the reason for differentiating v1.2 and v1.4 at 
chrome, it will be educative info. - Thanks

-Ram

[-- Attachment #1.2: Type: text/html, Size: 1372 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] 55+ messages in thread

end of thread, other threads:[~2017-12-07  4:38 UTC | newest]

Thread overview: 55+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-12-05  5:14 [PATCH v3 0/9] drm/i915: Implement HDCP Sean Paul
2017-12-05  5:14 ` Sean Paul
2017-12-05  5:15 ` [PATCH v3 1/9] drm: Fix link-status kerneldoc line lengths Sean Paul
2017-12-05  5:15   ` Sean Paul
2017-12-05  5:15 ` [PATCH v3 2/9] drm/i915: Add more control to wait_for routines Sean Paul
2017-12-05  5:15   ` Sean Paul
2017-12-05 17:13   ` Daniel Vetter
2017-12-05 17:13     ` Daniel Vetter
2017-12-05 23:09   ` [Intel-gfx] " Chris Wilson
2017-12-05  5:15 ` [PATCH v3 3/9] drm: Add Content Protection property Sean Paul
2017-12-05  5:15   ` Sean Paul
2017-12-05  8:07   ` Hans Verkuil
2017-12-05  8:07     ` Hans Verkuil
2017-12-05 14:04     ` Ramalingam C
2017-12-05 14:36       ` Sean Paul
2017-12-05 16:10         ` Hans Verkuil
2017-12-05 17:11         ` C, Ramalingam
2017-12-06 16:26           ` Sean Paul
2017-12-07  4:34             ` Ramalingam C
2017-12-05 15:27     ` [Intel-gfx] " Daniel Vetter
2017-12-05 15:27       ` Daniel Vetter
2017-12-05 15:34   ` Daniel Vetter
2017-12-05 15:34     ` Daniel Vetter
2017-12-05 15:40     ` [Intel-gfx] " Chris Wilson
2017-12-05 15:40       ` Chris Wilson
2017-12-05  5:15 ` [PATCH v3 4/9] drm: Add some HDCP related #defines Sean Paul
2017-12-05  5:15   ` Sean Paul
2017-12-05 23:12   ` [Intel-gfx] " Chris Wilson
2017-12-06 15:01     ` Alex Deucher
2017-12-06 15:01       ` Alex Deucher
2017-12-05  5:15 ` [PATCH v3 5/9] drm/i915: Add HDCP framework + base implementation Sean Paul
2017-12-05  5:15   ` Sean Paul
2017-12-05  9:06   ` Ramalingam C
2017-12-05  9:06     ` Ramalingam C
2017-12-05 17:00   ` Daniel Vetter
2017-12-05 17:00     ` Daniel Vetter
2017-12-05  5:15 ` [PATCH v3 6/9] drm/i915: Make use of indexed write GMBUS feature Sean Paul
2017-12-05  5:15   ` Sean Paul
2017-12-05 17:01   ` [Intel-gfx] " Daniel Vetter
2017-12-05 17:33   ` Ville Syrjälä
2017-12-05 17:33     ` Ville Syrjälä
2017-12-05  5:15 ` [PATCH v3 7/9] drm/i915: Add function to output Aksv over GMBUS Sean Paul
2017-12-05  5:15   ` Sean Paul
2017-12-05 17:02   ` [Intel-gfx] " Daniel Vetter
2017-12-05 17:02     ` Daniel Vetter
2017-12-05  5:15 ` [PATCH v3 8/9] drm/i915: Implement HDCP for HDMI Sean Paul
2017-12-05 17:06   ` Daniel Vetter
2017-12-05 17:06     ` Daniel Vetter
2017-12-05  5:15 ` [PATCH v3 9/9] drm/i915: Implement HDCP for DisplayPort Sean Paul
2017-12-05 14:30   ` Ramalingam C
2017-12-05 14:30     ` Ramalingam C
2017-12-05 17:12   ` Daniel Vetter
2017-12-05 17:12     ` Daniel Vetter
2017-12-05  5:21 ` ✗ Fi.CI.BAT: failure for drm/i915: Implement HDCP (rev3) Patchwork
2017-12-05 15:30   ` Daniel Vetter

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.