[PATCH 35/43] drm/i915: Enable HDCP version that is best capable

Ramalingam C
2018-02-14T14:21:09Z

hdcp_enable_work is extended to choose the better hdcp version based
on the system and panel capability.

And intel_hdcp_disable will find the version that is inforce, if any.
And corresponding version specific disable function alone called.

Signed-off-by: Ramalingam C <ramalingam.c@intel.com>
---
drivers/gpu/drm/i915/intel_hdcp.c | 112 ++++++++++++++++++++++++++++++++++----
1 file changed, 100 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 69ae47eaff49..2fe73f7eb6dd 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -27,6 +27,43 @@ static int _intel_hdcp2_disable(struct intel_hdcp *hdcp);
static void intel_hdcp2_check_work(struct work_struct *work);
static int intel_hdcp2_check_link(struct intel_hdcp *hdcp);

+static inline bool intel_hdcp1_supported(struct intel_hdcp *hdcp)
+{
+ return (hdcp->plat_cap & HDCP_CAPABILITY &&
+ hdcp->panel_cap & HDCP_CAPABILITY);
+}
+
+static inline bool intel_hdcp2_supported(struct intel_hdcp *hdcp)
+{
+ return (hdcp->plat_cap & HDCP2_CAPABILITY &&
+ hdcp->panel_cap & HDCP2_CAPABILITY);
+}
+
+static inline bool intel_hdcp_in_force(struct intel_hdcp *hdcp)
+{
+ struct drm_i915_private *dev_priv = to_i915(hdcp->connector->base.dev);
+ enum port port = hdcp->connector->encoder->port;
+ u32 reg;
+
+ reg = I915_READ(PORT_HDCP_STATUS(port));
+ if (reg & HDCP_STATUS_AUTH || reg & HDCP_STATUS_ENC)
+ return true;
+ return false;
+
+}
+
+static inline bool intel_hdcp2_in_force(struct intel_hdcp *hdcp)
+{
+ struct drm_i915_private *dev_priv = to_i915(hdcp->connector->base.dev);
+ enum port port = hdcp->connector->encoder->port;
+ u32 reg;
+
+ reg = I915_READ(HDCP2_STATUS_DDI(port));
+ if (reg & LINK_ENCRYPTION_STATUS || reg & LINK_AUTH_STATUS)
+ return true;
+ return false;
+}
+
static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
const struct intel_hdcp_shim *shim)
{
@@ -650,7 +687,11 @@ static void intel_hdcp_enable_work(struct work_struct *work)
hdcp_enable_work);

mutex_lock(&hdcp->hdcp_mutex);
- _intel_hdcp_enable(hdcp);
+ if (intel_hdcp2_supported(hdcp))
+ _intel_hdcp2_enable(hdcp);
+ else if (intel_hdcp1_supported(hdcp))
+ _intel_hdcp_enable(hdcp);
+
mutex_unlock(&hdcp->hdcp_mutex);
}

@@ -727,13 +768,48 @@ int intel_hdcp_init(struct intel_connector *connector,
return ret;
}

+static void intel_hdcp_probe_panel_cap(struct intel_hdcp *hdcp)
+{
+ struct intel_digital_port *intel_dig_port =
+ conn_to_dig_port(hdcp->connector);
+ bool capable = false;
+ u8 bksv[5];
+
+ hdcp->panel_cap = 0;
+
+ if (hdcp->plat_cap & HDCP_CAPABILITY) {
+ if (hdcp->hdcp_shim->hdcp_capable) {
+ hdcp->hdcp_shim->hdcp_capable(intel_dig_port, &capable);
+ } else {
+ if (!intel_hdcp_read_valid_bksv(intel_dig_port,
+ hdcp->hdcp_shim, bksv))
+ capable = true;
+ }
+ if (capable)
+ hdcp->panel_cap |= HDCP_CAPABILITY;
+ }
+
+ if (hdcp->plat_cap & HDCP2_CAPABILITY) {
+ hdcp->hdcp2_shim->hdcp_capable(intel_dig_port, &capable);
+ if (capable)
+ hdcp->panel_cap |= HDCP2_CAPABILITY;
+ }
+ DRM_DEBUG_KMS("plat_cap: 0x%X, panel_cap: 0x%X\n",
+ hdcp->plat_cap, hdcp->panel_cap);
+}
+
int intel_hdcp_enable(struct intel_connector *connector)
{
struct intel_hdcp *hdcp = connector->hdcp;

- if (!hdcp || !hdcp->hdcp_shim)
+ if (!hdcp)
return -ENOENT;

+ intel_hdcp_probe_panel_cap(hdcp);
+
+ if (!intel_hdcp1_supported(hdcp) && !intel_hdcp2_supported(hdcp))
+ return -EINVAL;
+
mutex_lock(&hdcp->hdcp_mutex);
schedule_work(&hdcp->hdcp_enable_work);
mutex_unlock(&hdcp->hdcp_mutex);
@@ -744,21 +820,33 @@ int intel_hdcp_enable(struct intel_connector *connector)
int intel_hdcp_disable(struct intel_connector *connector)
{
struct intel_hdcp *hdcp = connector->hdcp;
- int ret = 0;

- if (!hdcp || !hdcp->hdcp_shim)
+ if (!hdcp)
return -ENOENT;

- mutex_lock(&hdcp->hdcp_mutex);
+ if (hdcp->hdcp_value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ return 0;

- if (hdcp->hdcp_value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
- hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
- ret = _intel_hdcp_disable(hdcp);
- }
+ /*
+ * Setting it out of mutex, so that other on going process like check
+ * link and auth can check for this state change and terminate themself.
+ */
+ hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
+ cancel_work_sync(&hdcp->hdcp_enable_work);

- mutex_unlock(&hdcp->hdcp_mutex);
cancel_delayed_work_sync(&hdcp->hdcp_check_work);
- return ret;
+ cancel_delayed_work_sync(&hdcp->hdcp2_check_work);
+
+ mutex_lock(&hdcp->hdcp_mutex);
+ if (hdcp->plat_cap & HDCP_CAPABILITY)
+ if (intel_hdcp_in_force(hdcp))
+ _intel_hdcp_disable(hdcp);
+ if (hdcp->plat_cap & HDCP2_CAPABILITY)
+ if (intel_hdcp2_in_force(hdcp))
+ _intel_hdcp2_disable(hdcp);
+ mutex_unlock(&hdcp->hdcp_mutex);
+
+ return 0;
}

void intel_hdcp_atomic_check(struct drm_connector *connector,
@@ -1437,7 +1525,7 @@ static int _intel_hdcp2_disable(struct intel_hdcp *hdcp)
{
int ret;

- DRM_ERROR("[%s:%d] HDCP2.2 is being Disabled\n",
+ DRM_INFO("[%s:%d] HDCP2.2 is being Disabled\n",
hdcp->connector->base.name, hdcp->connector->base.base.id);

ret = hdcp2_disable_encryption(hdcp);